Supafone Labs ยท Documentation

Provider Injection Examples#

Supafone keeps three choices independent:

ChoiceSDK fieldWhat it controls
Speaking runtimeproviderThe agent receiving caller audio and producing speech
Supervisor modelllm and oracle_modelThe independent model that evaluates call state and writes a bounded directive
Injector adapterinject_via or the speaking providerThe native control or developer-owned context that receives the directive

The supervisor model is sometimes informally called the injector model. It does not write directly to a provider socket. It produces the instruction; Supafone's injector adapter compiles that instruction into the correct provider action. This separation lets Claude supervise an OpenAI Realtime agent, OpenAI supervise Grok, or a Supafone-managed Oracle supervise Ultravox without changing the caller-facing model.

Choose the supervisor model#

Supafone-managed, with no additional model key:

python
from supafone_labs import SupafoneLabs

supervisor = SupafoneLabs(
    provider="gpt_realtime",
    llm="hosted",
    oracle_model="supafone-labs-oracle",
    mode="return",
)

Bring Anthropic, OpenAI, or xAI:

python
claude_supervisor = SupafoneLabs(
    provider="gpt_realtime",
    llm="anthropic",
    oracle_model="claude-haiku-4-5-20251001",
    mode="return",
)

openai_supervisor = SupafoneLabs(
    provider="grok",
    llm="openai",
    oracle_model="gpt-4.1-mini",
    mode="return",
)

grok_supervisor = SupafoneLabs(
    provider="ultravox",
    llm="xai",
    oracle_model="grok-4-fast",
    mode="return",
)

Credentials stay in SUPAFONE_LABS_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, or XAI_API_KEY. They never belong in browser code, prompts, or provider events.

One delivery loop#

Every integration follows the same sequence:

python
result = await supervisor.observe(raw_provider_event)

for action in result.actions:
    await deliver_to_active_call(action.payload)

action.payload is already the provider-native wire object. Do not turn it back into a generic prompt patch.

OpenAI Realtime#

OpenAI Realtime accepts a system-role conversation.item.create. Send the compiled payload unchanged over the active Realtime WebSocket:

python
supervisor = SupafoneLabs(
    provider="gpt_realtime",
    llm="anthropic",
    oracle_model="claude-haiku-4-5-20251001",
    mode="return",
)

async for raw in openai_socket:
    result = await supervisor.observe(json.loads(raw))
    for action in result.actions:
        if action.kind == "conversation_item_create":
            await openai_socket.send(json.dumps(action.payload))

The accepted action adds silent system context and does not issue a separate response.create. See the complete @@LABSTOKEN1@@.

Grok Voice Agent#

Grok accepts per-response instructions on response.create. The instruction applies to the next response and then expires:

python
supervisor = SupafoneLabs(
    provider="grok",
    llm="openai",
    oracle_model="gpt-4.1-mini",
    mode="return",
)

async for raw in grok_socket:
    result = await supervisor.observe(json.loads(raw))
    for action in result.actions:
        if action.kind == "response_create":
            await grok_socket.send(json.dumps(action.payload))

The wire payload is response.create.instructions, not an OpenAI-style standing session mutation. See @@LABSTOKEN1@@.

Gemini Developer Live#

Gemini Live's initial systemInstruction is session configuration. Its mid-session clientContent and realtime text messages are ordinary conversation content, not a documented hidden instruction channel. The Gemini native socket does not receive a hidden action from the adapter:

python
observer = SupafoneLabs(
    provider="gemini_live",
    llm="hosted",
    oracle_model="supafone-labs-oracle",
    mode="return",
)

result = await observer.observe(raw_gemini_event)
if result.directive:
    record_for_live_qa(result.directive)

assert result.actions == []

For live Gemini steering, run Gemini inside a developer-owned framework such as LiveKit or Pipecat and select that host as the injector adapter:

python
supervisor = SupafoneLabs(
    provider="gemini_live",
    inject_via="livekit",
    llm="hosted",
    oracle_model="supafone-labs-oracle",
    mode="return",
)

result = await supervisor.observe(raw_gemini_event)
for action in result.actions:
    if action.kind == "chat_context_append":
        await append_to_livekit_context(action.payload)

The host-owned context is the injector; Supafone does not disguise a visible Gemini user turn as a system directive. See @@LABSTOKEN0@@ and @@LABSTOKEN1@@.

Ultravox#

Ultravox receives a deferred user_text_message with urgency="later":

python
supervisor = SupafoneLabs(
    provider="ultravox",
    llm="hosted",
    oracle_model="supafone-labs-oracle",
    mode="return",
)

result = await supervisor.observe(raw_ultravox_event)
for action in result.actions:
    if action.kind == "inject_message":
        await ultravox_socket.send(json.dumps(action.payload))

See @@LABSTOKEN0@@.

Vapi#

Enable monitorPlan.controlEnabled so Vapi includes the live call's controlUrl. Post Supafone's add-message payload to that URL:

python
supervisor = SupafoneLabs(provider="vapi", llm="hosted", mode="return")
result = await supervisor.observe(vapi_webhook)

for action in result.actions:
    if action.kind == "control_add_message":
        response = await http.post(control_url, json=action.payload)
        response.raise_for_status()

See @@LABSTOKEN0@@.

LiveKit and Pipecat#

These frameworks run in your process, so your application owns the model context. LiveKit actions append a system message to ChatContext; Pipecat actions push an LLMMessagesAppendFrame with run_llm=false:

python
livekit_supervisor = SupafoneLabs(provider="livekit", llm="hosted", mode="return")
pipecat_supervisor = SupafoneLabs(provider="pipecat", llm="hosted", mode="return")

Use the complete @@LABSTOKEN0@@ and @@LABSTOKEN1@@ for the host SDK calls that apply each action.

Failure behavior#

The supervisor remains off the audio path. A timeout, low-confidence directive, unsupported injector, or provider error produces no action; the speaking agent continues normally. Check the Framework Coverage matrix before treating event normalization as proof of live injection support.

View raw Markdown