Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions agents/build/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@

`conversation.timezone` is the default IANA timezone (like `Asia/Shanghai`) the agent uses for dates and times in conversation. Leave it empty for **automatic** — each session follows the caller's device or phone number, falling back to UTC. Set one when your agent serves a single region regardless of who calls. A per-session `timezone` on the [session request](/agents/build/time-timezone) overrides this. See [Time & timezone](/agents/build/time-timezone) for the full resolution order.

## Autosave and publishing

Check warning on line 71 in agents/build/configuration.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

agents/build/configuration.mdx#L71

Did you really mean 'Autosave'?

There is no Save button. Each change is written to the agent's draft moments after you stop editing, and the **Saving… / Saved** indicator at the bottom-left of the page shows the current state. If a save fails, the Builder tells you and keeps your pending edits so nothing is lost.

Expand Down Expand Up @@ -136,6 +136,7 @@
| `knowledge_base` | Attached knowledge sources | [Knowledge base](/agents/build/knowledge-base) |
| `analysis` | Post-call summary, data fields, and success criteria | [Post-call analysis](/agents/monitor/post-call-analysis) |
| `webhooks` | Post-call webhook delivery | [Webhooks](/agents/monitor/webhooks) |
| `llm` | Conversation model settings, including a custom LLM endpoint | [Custom LLM](/agents/build/custom-llm) |

## Going further

Expand Down
65 changes: 65 additions & 0 deletions agents/build/custom-llm.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
title: "Custom LLM"
description: "Serve your agent's replies from your own OpenAI-compatible model endpoint"
icon: "brain-circuit"
---

By default your agent generates replies with a Fish Audio platform model. With a custom LLM, every reply comes from an endpoint you host instead, whether that is your own fine-tuned model, your own memory and conversation logic, or a proxy inside your VPC. Fish Audio keeps running the rest of the call, including speech recognition, voice synthesis, interruption handling, [tool](/agents/build/tools) execution, recordings, and billing.

## How it works

Your endpoint speaks the standard OpenAI [chat completions](https://platform.openai.com/docs/api-reference/chat) protocol. The platform sends `POST {base_url}/chat/completions` with `stream: true` and reads the reply as server-sent events, so if your prototype already runs against another voice-agent platform through a custom LLM, the same server works here unchanged.

Each conversation turn, your endpoint receives the same fully assembled context a platform model would see. The `messages` array carries the system prompt with [dynamic variables](/agents/build/dynamic-variables) and [session overrides](/agents/deploy/authenticated-sessions#overrides) applied, the complete conversation history, and any retrieved [knowledge](/agents/build/knowledge-base), and the agent's tools are included in OpenAI function format. When your model returns `tool_calls`, the platform executes the tool and calls you again with the result. Two extra fields ride each request body so your server can look up its own state:

| Field | Content |
|---|---|
| `session_id` | The Fish Audio session id. |
| `user_id` | The `end_user_id` you passed when [creating the session](/agents/deploy/authenticated-sessions). Omitted when the session has none. |

Requests authenticate with `Authorization: Bearer <your API key>`. The endpoint must use `https` on a publicly reachable host, and must support function calling if the agent has tools configured.

## Point the agent at your endpoint

Set the `llm.custom` section on the agent's [configuration](/agents/build/configuration#configure-through-the-api):

```bash
curl --request PATCH "https://api.fish.audio/v1/agent/agents/$AGENT_ID/config" \
--header "Authorization: Bearer $FISH_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"llm": {
"custom": {
"base_url": "https://llm.example.com/v1",
"model": "persona-70b",
"api_key": "sk-your-endpoint-key"
}
}
}'
```

| Field | Rules |
|---|---|
| `base_url` | `https` only, publicly reachable host, no query string or fragment. A trailing `/chat/completions` is stripped. |
| `model` | Sent to your endpoint verbatim as the request's `model` field. |
| `api_key` | Sent to your endpoint as the `Authorization` bearer token. The field is write-only, so config and version reads return it as `null`. Omit it (or send `null`) on later patches to keep the stored key. |

Once [published](/agents/deploy/versions-publishing), every session of the agent generates on your endpoint, whether it starts from the web, a phone call, or a [preview call](/agents/test/preview-calls) in the Builder. Rotating the key works the same way as any other config change. Patch a new `api_key` into the draft, then publish; sessions started after the publish use the new key. Send `{ "llm": { "custom": null } }` to switch back to platform models.

## Failure behavior

A custom LLM never silently falls back to a platform model, because a platform model answering with the wrong persona and no memory would be worse than a failed turn.

- Each request gets one retry and a 10 second response cap.
- When a generation still fails, the agent speaks a brief hold line and stays on the call.
- After three consecutive failed generations the agent apologizes, hangs up, and the session records `ended_reason: llm_endpoint_failure`. Each failure also emits an `llm.endpoint_error` event on the session timeline.

<Tip>
Voice conversations are latency sensitive, so aim for a time-to-first-token under 800 ms. Turn latency is attributed per session in [conversation history](/agents/monitor/conversation-history), which lets you tell endpoint time from platform time.
</Tip>

## Limitations

- [Agent tests](/agents/test/agent-tests) are not supported. A scripted test run refuses to execute rather than substitute a platform model for yours.
- Configuration is API-only for now. A console UI comes later.
- The prompt-level safety guardrails still ride the assembled context, but your model decides whether to honor them.
2 changes: 1 addition & 1 deletion agents/deploy/authenticated-sessions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<Note>
No backend, and anyone may talk to the agent? A [public
agent](/agents/deploy/public-agents) lets the SDK create sessions with just an
`agentId` — no token involved, gated by an origin allowlist and rate limits.

Check warning on line 21 in agents/deploy/authenticated-sessions.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

agents/deploy/authenticated-sessions.mdx#L21

Did you really mean 'allowlist'?
</Note>

## Create a token on your backend
Expand Down Expand Up @@ -109,7 +109,7 @@

<Note>
`overrides`, `dynamic_variables`, `tool_events`, `timezone`, and
`world_context` belong in your backend's creation request — the SDK forwards

Check warning on line 112 in agents/deploy/authenticated-sessions.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

agents/deploy/authenticated-sessions.mdx#L112

Did you really mean 'backend's'?
these options (and its `language` shorthand for `overrides.language`) only in
[public agent](/agents/deploy/public-agents) mode.
</Note>
Expand All @@ -119,12 +119,12 @@
| Field | Type | Description |
| ------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `agent_id` | string, required | The agent to talk to. It must have a [published version](/agents/deploy/versions-publishing). |
| `name` | string, optional | Display name for this session in the console's Conversations list, up to 128 characters. Omit it to show the session's start time instead. API-key requests only — keyless (public) creation rejects it with `400`. |

Check warning on line 122 in agents/deploy/authenticated-sessions.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

agents/deploy/authenticated-sessions.mdx#L122

Did you really mean 'keyless'?
| `overrides` | object, optional | Replace parts of the published configuration for this session — see [Overrides](#overrides). |
| `dynamic_variables` | object, optional | Up to 50 entries of string, number, or boolean values, substituted into `{{placeholders}}`. See [Dynamic variables](/agents/build/dynamic-variables). |
| `tool_events` | boolean, optional | Stream tool lifecycle events (`toolCallStarted` / `toolCallCompleted` / `toolCallFailed`) to the client. Default `true`; set `false` to keep tool inputs and outputs off the client. |
| `end_user_id` | string, optional | Your identifier for the end user, up to 256 characters. Stored on the session and echoed in [webhook](/agents/monitor/webhooks) payloads for attribution. |
| `end_user_id` | string, optional | Your identifier for the end user, up to 256 characters. Stored on the session and echoed in [webhook](/agents/monitor/webhooks) payloads and [custom LLM](/agents/build/custom-llm) requests. |
| `metadata` | object, optional | Your own key-value namespace. Stored and returned verbatim on session queries and webhooks — never read or interpreted by the platform. |

Check warning on line 127 in agents/deploy/authenticated-sessions.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

agents/deploy/authenticated-sessions.mdx#L127

Did you really mean 'namespace'?
| `record_audio` | boolean, optional | Whether to record this session's audio. Overrides the agent's [recording setting](/agents/monitor/conversation-history#what-gets-stored) for this session only — it never changes the agent; omit it to use the agent's configuration. |
| `timezone` | string, optional | IANA timezone (like `Asia/Shanghai`) for the agent's sense of local time. Invalid names are rejected with `422`. See [Time & timezone](/agents/build/time-timezone). |
| `client_timezone` | string, optional | The end user's browser timezone, filled automatically by the SDK in public-agent mode. A hint, not a demand: it applies only when neither `timezone` nor the agent's configured timezone is set, and invalid values are ignored. See the [resolution order](/agents/build/time-timezone). |
Expand All @@ -149,7 +149,7 @@
"agent_id": "YOUR_AGENT_ID",
"overrides": {
"first_message": "Welcome back, {{name}} — picking up where we left off.",
"voice_id": "802e3bc2b27e49c2995d23ef70e6ac89",

Check warning on line 152 in agents/deploy/authenticated-sessions.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

agents/deploy/authenticated-sessions.mdx#L152

Did you really mean 'voice_id'?
"language": "ja"
}
}
Expand Down Expand Up @@ -214,7 +214,7 @@

| Status | Meaning |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `400` | A keyless (public-agent) request sent an override [public sessions don't accept](#overrides), or a `name`. |

Check warning on line 217 in agents/deploy/authenticated-sessions.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

agents/deploy/authenticated-sessions.mdx#L217

Did you really mean 'keyless'?
| `401` | Invalid API key. A request with no `Authorization` header at all is treated as a public-agent request instead. |
| `402` | Quota exceeded. |
| `403` | Public-agent request rejected: the agent is not public, or the page's `Origin` is not on the allow-list. |
Expand Down
4 changes: 3 additions & 1 deletion agents/deploy/versions-publishing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
</Step>
<Step title="Publish">
Click **Publish**. Any pending edits are saved first, then the draft is
snapshotted as the next version. A confirmation shows the new version

Check warning on line 63 in agents/deploy/versions-publishing.mdx

View check run for this annotation

Mintlify / Mintlify Validation (hanabiaiinc) - vale-spellcheck

agents/deploy/versions-publishing.mdx#L63

Did you really mean 'snapshotted'?
number, and the agent shows as **Live** in your agents list.
</Step>
</Steps>
Expand Down Expand Up @@ -118,7 +118,9 @@
<Warning>
Version snapshots never echo credential secrets. Write-only fields — such as
webhook secrets and tool authorization headers — return a `has_secret: true`
marker instead of the value, in every version including historical ones.
marker instead of the value, and the custom LLM
[`api_key`](/agents/build/custom-llm) reads back as `null`, in every version
including historical ones.
</Warning>

## Restore a previous version
Expand Down
3 changes: 2 additions & 1 deletion docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@
"agents/build/system-tools"
]
},
"agents/build/dynamic-variables"
"agents/build/dynamic-variables",
"agents/build/custom-llm"
]
},
{
Expand Down
1 change: 1 addition & 0 deletions llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
- [Client Tools](https://docs.fish.audio/agents/build/client-tools.md): Tools your app executes in the browser through the SDK.
- [System Tools](https://docs.fish.audio/agents/build/system-tools.md): Built-in capabilities like hanging up the call.
- [Dynamic Variables](https://docs.fish.audio/agents/build/dynamic-variables.md): Personalize each session with template variables supplied at creation time.
- [Custom LLM](https://docs.fish.audio/agents/build/custom-llm.md): Serve your agent's replies from your own OpenAI-compatible model endpoint.
- [Versions & Publishing](https://docs.fish.audio/agents/deploy/versions-publishing.md): Draft, publish, clone, and restore agent configurations.
- [Deployment Overview](https://docs.fish.audio/agents/deploy/overview.md): Pick a deployment channel — widget, public agents, authenticated sessions, or a phone number.
- [Authentication & Session Tokens](https://docs.fish.audio/agents/deploy/authentication.md): Public agent IDs vs server-minted session tokens; POST /v1/agent/sessions.
Expand Down
Loading