# Provider Injection Examples

Supafone keeps three choices independent:

| Choice | SDK field | What it controls |
| --- | --- | --- |
| Speaking runtime | `provider` | The agent receiving caller audio and producing speech |
| Supervisor model | `llm` and `oracle_model` | The independent model that evaluates call state and writes a bounded directive |
| Injector adapter | `inject_via` or the speaking `provider` | The 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
[`examples/gpt_realtime.py`](https://github.com/samthedataman/supafone-labs/blob/main/examples/gpt_realtime.py).

## 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
[`examples/grok_voice_agent.py`](https://github.com/samthedataman/supafone-labs/blob/main/examples/grok_voice_agent.py).

## 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
[`examples/gemini_live_observer.py`](https://github.com/samthedataman/supafone-labs/blob/main/examples/gemini_live_observer.py)
and [`examples/livekit_agent.py`](https://github.com/samthedataman/supafone-labs/blob/main/examples/livekit_agent.py).

## 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 [`examples/ultravox_end_to_end.py`](https://github.com/samthedataman/supafone-labs/blob/main/examples/ultravox_end_to_end.py).

## 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 [`examples/vapi_webhook_server.py`](https://github.com/samthedataman/supafone-labs/blob/main/examples/vapi_webhook_server.py).

## 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
[`examples/livekit_agent.py`](https://github.com/samthedataman/supafone-labs/blob/main/examples/livekit_agent.py)
and [`examples/pipecat_pipeline.py`](https://github.com/samthedataman/supafone-labs/blob/main/examples/pipecat_pipeline.py)
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](framework-support.md) matrix
before treating event normalization as proof of live injection support.
