provider-sarvam
v0.1.1Sarvam AI provider worker; implements provider::sarvam::stream for the Sarvam chat models plus provider::sarvam::transcribe (Saaras) and provider::sarvam::speak (Bulbul) behind llm-router.
Install
iii trigger compose::add worker=provider-sarvamRun in your project with the iii engine running. Exact versions are immutable and digest-pinned.
Full markdown
/registry/workers/provider-sarvam.md. Paste it into an LLM prompt or pipe it through curl from a worker.Install
iii trigger compose::add worker=provider-sarvam@0.1.1Dependencies
Readme
provider-sarvam
Sarvam AI behind llm-router, as one worker for every Sarvam model family:
- Chat:
provider::sarvam::streamrunssarvam-105b,sarvam-30bandsarvam-mon Sarvam's OpenAI-compatible Chat Completions API (SSE streaming, tool calling,reasoning_effort), sorouter::chatand the harness use them like any other provider's models. - Speech to text:
provider::sarvam::transcriberuns Saaras (saaras:v3default,saaras:v4,saarika:v2.5) behindrouter::transcribe: 22 Indian languages and English, detected when no hint is given, word timings folded into sentence segments. - Text to speech:
provider::sarvam::speakruns Bulbul (bulbul:v3default,bulbul:v2) behindrouter::speak: 11 languages, 35+ speakers, mp3, wav, pcm16 or opus back. provider::sarvam::count_tokensestimates prompt tokens locally with tiktoken (Sarvam publishes no tokenizer for the 105B and 30B models).
The voice worker reaches the speech half with stt.backend: router /
tts.backend: router and a sarvam::… model.
Install
iii trigger compose::add worker=provider-sarvamPut the key where the router reads it: SARVAM_API_KEY in the router's
environment, or paste it into the sarvam block of the llm-router entry in
the console's Settings. The catalog fills the moment a key lands.
Behavior
- Auth: the same key goes out as
Authorization: Bearer(what the chat endpoint documents) and asapi-subscription-key(what every other endpoint takes), so a gateway in front of either shape is satisfied. - Endpoints: chat at the configured
api_url(defaulthttps://api.sarvam.ai/v1/chat/completions); the speech endpoints at that URL's origin (/speech-to-text,/text-to-speech), so a residency host override moves all three. - Reasoning: every Sarvam chat model reasons by default; the router's
thinking_levelfolds ontoreasoning_effortlow | medium | high.response_formatmaps tojson_objectwithout schema validation and the catalog says so (supports_structured_output: false). - Languages: hints such as
hi,hi-INoren-USbecome Sarvam'sxx-INcodes; Odia's ISO codeorbecomesod-IN. Unknown hints let Saaras detect the language and make Bulbul speak English. - Speakers:
shubhis the Bulbul v3 default (anushkafor v2). Pass any speaker name Sarvam lists; v2 voices do not work on v3 and vice versa. - Limits: Bulbul takes up to 2500 characters per request; the REST speech-to-text endpoint is for recordings, not hour-long files (use Sarvam's batch API for those).
Surfaces
| Function | Request | Response |
|---|---|---|
provider::sarvam::stream |
router chat stream contract | AssistantMessageEvent frames |
provider::sarvam::transcribe |
{model?, audio_base64, mime?, language?} |
{model, text, segments[{text, start_secs, end_secs}], language, duration_secs} |
provider::sarvam::speak |
{model?, text, voice?, format?, language?, speed?} |
{model, audio_base64, mime, voice} |
provider::sarvam::count_tokens |
{model, system_prompt?, tools?, messages} |
{model, tokens, estimator} |
provider::sarvam::refresh_models |
{} |
{ok, count} |
Errors carry stable prefixes: provider/not_configured,
provider/auth_expired, provider/quota_exceeded, provider/rate_limited,
provider/invalid_input, provider/upstream_transient; chat failures use
the shared ErrorKind taxonomy on their error frames.
Configuration
Nothing in this worker's own config file. The credential, api_url and
max_tokens resolve per request through router::provider::resolve.
Tests
cargo test # unit tests + wire-schema goldens (+ engine-backed suite when `iii` is on PATH)
UPDATE_GOLDENS=1 cargo test # regenerate tests/golden after a surface changeRunning
cargo run -- --url ws://127.0.0.1:49134API reference (JSON)
{
"functions": [
{
"description": "Cancel the in-flight upstream stream for a request_id (router::abort fan-out), stopping billed generation immediately.",
"metadata": {
"internal": true
},
"name": "provider::sarvam::abort",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Input of a provider's `provider::<id>::abort`: actively cancel the in-flight upstream stream for `request_id` (the router's `request_id`, delivered to the provider as `resolution_key`) so billed generation stops immediately instead of waiting for the provider to notice the closed channel on its next write.",
"properties": {
"request_id": {
"type": "string"
}
},
"required": [
"request_id"
],
"title": "ProviderAbortRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Output of `provider::<id>::abort`. `aborted: false` means the request was unknown — already finished, never started, or aborted before (idempotent).",
"properties": {
"aborted": {
"type": "boolean"
}
},
"required": [
"aborted"
],
"title": "ProviderAbortResponse",
"type": "object"
}
},
{
"description": "Estimate prompt tokens for {model, system_prompt?, tools?, messages} locally with tiktoken; never runs the model and costs nothing.",
"metadata": {
"internal": true
},
"name": "provider::sarvam::count_tokens",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"AgentFunction": {
"description": "Function invocation schema — what a provider sees as a `tools` array entry (README § Function invocation schema; adapter boundary). These describe iii functions exposed to the model, not provider-native tools.",
"properties": {
"description": {
"type": "string"
},
"execution_mode": {
"type": [
"string",
"null"
]
},
"label": {
"type": [
"string",
"null"
]
},
"name": {
"type": "string"
},
"parameters": true
},
"required": [
"description",
"name",
"parameters"
],
"type": "object"
},
"AgentMessage": {
"anyOf": [
{
"$ref": "#/definitions/AssistantMessage"
},
{
"$ref": "#/definitions/FunctionResultMessage"
},
{
"$ref": "#/definitions/CustomMessage"
},
{
"$ref": "#/definitions/UserMessage"
}
],
"description": "The canonical transcript message union. Untagged: the single-variant role tags disambiguate deserialization."
},
"AssistantMessage": {
"properties": {
"content": {
"items": {
"$ref": "#/definitions/ContentBlock"
},
"type": "array"
},
"error_kind": {
"anyOf": [
{
"$ref": "#/definitions/ErrorKind"
},
{
"type": "null"
}
]
},
"error_message": {
"type": [
"string",
"null"
]
},
"model": {
"type": "string"
},
"native_stop_reason": {
"type": [
"string",
"null"
]
},
"provider": {
"type": "string"
},
"role": {
"$ref": "#/definitions/AssistantRoleTag"
},
"stop_reason": {
"$ref": "#/definitions/StopReason"
},
"timestamp": {
"format": "int64",
"type": "integer"
},
"usage": {
"anyOf": [
{
"$ref": "#/definitions/Usage"
},
{
"type": "null"
}
]
},
"warnings": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
}
},
"required": [
"content",
"model",
"provider",
"role",
"stop_reason",
"timestamp"
],
"type": "object"
},
"AssistantRoleTag": {
"enum": [
"assistant"
],
"type": "string"
},
"ContentBlock": {
"description": "Content blocks — the atomic units of message content (README § Content blocks).",
"oneOf": [
{
"properties": {
"text": {
"type": "string"
},
"type": {
"enum": [
"text"
],
"type": "string"
}
},
"required": [
"text",
"type"
],
"type": "object"
},
{
"properties": {
"attachment_id": {
"description": "Optional link to the stored original in the session-manager attachment store. Transcript bookkeeping for lazy readers only: the harness clears it before `router::chat`, and no provider mapping reads it. Tolerated here so a block that still carries it deserializes instead of failing the request; omitted on the wire when absent.",
"type": [
"string",
"null"
]
},
"data": {
"type": "string"
},
"mime": {
"type": "string"
},
"type": {
"enum": [
"image"
],
"type": "string"
}
},
"required": [
"data",
"mime",
"type"
],
"type": "object"
},
{
"description": "Reference to an original upload in the session-manager attachment store. A REFERENCE ONLY (never inline bytes); the harness strips it before `router::chat`. Tolerated here so a stray block deserializes instead of failing the request; every counter/mapping emits nothing for it.",
"properties": {
"attachment_id": {
"type": "string"
},
"mime": {
"type": "string"
},
"name": {
"type": "string"
},
"size": {
"format": "uint64",
"minimum": 0,
"type": "integer"
},
"type": {
"enum": [
"file"
],
"type": "string"
}
},
"required": [
"attachment_id",
"mime",
"name",
"size",
"type"
],
"type": "object"
},
{
"properties": {
"signature": {
"type": [
"string",
"null"
]
},
"text": {
"type": "string"
},
"type": {
"enum": [
"thinking"
],
"type": "string"
}
},
"required": [
"text",
"type"
],
"type": "object"
},
{
"description": "Opaque redacted thinking payload — replayed verbatim on the Anthropic wire.",
"properties": {
"data": {
"type": "string"
},
"type": {
"enum": [
"redacted_thinking"
],
"type": "string"
}
},
"required": [
"data",
"type"
],
"type": "object"
},
{
"properties": {
"arguments": true,
"function_id": {
"type": "string"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"function_call"
],
"type": "string"
}
},
"required": [
"arguments",
"function_id",
"id",
"type"
],
"type": "object"
},
{
"properties": {
"content": {
"items": {
"$ref": "#/definitions/ContentBlock"
},
"type": "array"
},
"function_call_id": {
"type": "string"
},
"is_error": {
"type": [
"boolean",
"null"
]
},
"type": {
"enum": [
"function_result"
],
"type": "string"
}
},
"required": [
"content",
"function_call_id",
"type"
],
"type": "object"
}
]
},
"CustomMessage": {
"properties": {
"content": {
"items": {
"$ref": "#/definitions/ContentBlock"
},
"type": "array"
},
"custom_type": {
"type": "string"
},
"details": true,
"display": {
"type": [
"string",
"null"
]
},
"role": {
"$ref": "#/definitions/CustomRoleTag"
},
"timestamp": {
"format": "int64",
"type": "integer"
}
},
"required": [
"content",
"custom_type",
"role",
"timestamp"
],
"type": "object"
},
"CustomRoleTag": {
"enum": [
"custom"
],
"type": "string"
},
"ErrorKind": {
"enum": [
"auth_expired",
"rate_limited",
"context_overflow",
"transient",
"permanent"
],
"type": "string"
},
"FunctionResultMessage": {
"properties": {
"content": {
"items": {
"$ref": "#/definitions/ContentBlock"
},
"type": "array"
},
"details": true,
"function_call_id": {
"type": "string"
},
"function_id": {
"type": "string"
},
"is_error": {
"type": "boolean"
},
"role": {
"$ref": "#/definitions/FunctionResultRoleTag"
},
"timestamp": {
"format": "int64",
"type": "integer"
}
},
"required": [
"content",
"details",
"function_call_id",
"function_id",
"is_error",
"role",
"timestamp"
],
"type": "object"
},
"FunctionResultRoleTag": {
"enum": [
"function_result"
],
"type": "string"
},
"StopReason": {
"enum": [
"end",
"length",
"function_call",
"aborted",
"error"
],
"type": "string"
},
"Usage": {
"properties": {
"cache_read": {
"format": "uint64",
"minimum": 0,
"type": [
"integer",
"null"
]
},
"cache_write": {
"format": "uint64",
"minimum": 0,
"type": [
"integer",
"null"
]
},
"cost_usd": {
"format": "double",
"type": [
"number",
"null"
]
},
"input": {
"format": "uint64",
"minimum": 0,
"type": [
"integer",
"null"
]
},
"output": {
"format": "uint64",
"minimum": 0,
"type": [
"integer",
"null"
]
},
"reasoning": {
"format": "uint64",
"minimum": 0,
"type": [
"integer",
"null"
]
}
},
"type": "object"
},
"UserMessage": {
"properties": {
"content": {
"items": {
"$ref": "#/definitions/ContentBlock"
},
"type": "array"
},
"role": {
"$ref": "#/definitions/UserRoleTag"
},
"timestamp": {
"format": "int64",
"type": "integer"
}
},
"required": [
"content",
"role",
"timestamp"
],
"type": "object"
},
"UserRoleTag": {
"description": "Single-variant role tags: exact-match on deserialize, correct wire string on serialize, and they let `AgentMessage` be an untagged union.",
"enum": [
"user"
],
"type": "string"
}
},
"properties": {
"messages": {
"description": "Wire agent messages, the same shape `provider::sarvam::stream` accepts. Must be non-empty.",
"items": {
"$ref": "#/definitions/AgentMessage"
},
"type": "array"
},
"model": {
"description": "Model id the prompt targets; reported back unchanged.",
"type": "string"
},
"system_prompt": {
"default": null,
"description": "System prompt counted as its own wire message when present.",
"type": [
"string",
"null"
]
},
"tools": {
"default": null,
"description": "Function invocation schemas; each serialized schema counts toward the total.",
"items": {
"$ref": "#/definitions/AgentFunction"
},
"type": [
"array",
"null"
]
}
},
"required": [
"messages",
"model"
],
"title": "CountTokensRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"estimator": {
"description": "Always `tiktoken`: a local tokenizer produced the estimate.",
"type": "string"
},
"model": {
"type": "string"
},
"tokens": {
"description": "Estimated prompt tokens for the assembled request.",
"format": "uint64",
"minimum": 0,
"type": "integer"
}
},
"required": [
"estimator",
"model",
"tokens"
],
"title": "CountTokensResponse",
"type": "object"
}
},
{
"description": "Internal: router::ready subscriber that re-declares this provider and refreshes its catalog.",
"metadata": {
"internal": true
},
"name": "provider::sarvam::on_router_ready",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Event delivered to a provider's `provider::<id>::on_router_ready` (the `router::ready` trigger payload, currently `{}`). Unknown fields are ignored.",
"title": "RouterReadyEvent",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Ack returned by a provider's `provider::<id>::on_router_ready`.",
"properties": {
"ok": {
"type": "boolean"
}
},
"required": [
"ok"
],
"title": "ProviderReadyAck",
"type": "object"
}
},
{
"description": "Reconcile the curated Sarvam catalog slice, chat and speech models, through the router (Sarvam has no models-listing endpoint); returns the count written.",
"metadata": {
"internal": true
},
"name": "provider::sarvam::refresh_models",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Input of a provider's `provider::<id>::refresh_models` — takes no arguments. A struct (not `Value`) keeps the request schema concrete; unknown fields (e.g. the engine-injected `_caller_worker_id`) are ignored.",
"title": "RefreshModelsRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Output of `provider::<id>::refresh_models`.",
"properties": {
"count": {
"format": "uint",
"minimum": 0,
"type": "integer"
},
"ok": {
"type": "boolean"
}
},
"required": [
"count",
"ok"
],
"title": "RefreshModelsResponse",
"type": "object"
}
},
{
"description": "Text to speech on Sarvam Bulbul behind router::speak: text, a speaker name and a language in, base64 audio out (mp3, wav, pcm16, opus).",
"metadata": {
"internal": true
},
"name": "provider::sarvam::speak",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"format": {
"default": null,
"description": "`mp3` (default), `wav`, `pcm16`, `opus`.",
"type": [
"string",
"null"
]
},
"language": {
"default": null,
"description": "Language of the text (`hi`, `hi-IN`, ...); English when omitted.",
"type": [
"string",
"null"
]
},
"model": {
"default": null,
"description": "Text-to-speech model id; `bulbul:v3` when omitted.",
"type": [
"string",
"null"
]
},
"speed": {
"default": null,
"description": "Speaking-rate multiplier, 0.5 to 2.0; 1.0 is the voice's own pace.",
"format": "double",
"type": [
"number",
"null"
]
},
"text": {
"description": "Text to speak, up to 2500 characters. Write Indic words in their own script for natural speech.",
"type": "string"
},
"voice": {
"default": null,
"description": "Speaker name (`shubh`, `shreya`, `manan`, `ishita`, ...; the v2 voices `anushka`, `abhilash`, ... only with `bulbul:v2`).",
"type": [
"string",
"null"
]
}
},
"required": [
"text"
],
"title": "SpeakRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"audio_base64": {
"type": "string"
},
"mime": {
"type": "string"
},
"model": {
"type": "string"
},
"voice": {
"description": "The speaker used.",
"type": "string"
}
},
"required": [
"audio_base64",
"mime",
"model",
"voice"
],
"title": "SpeakResponse",
"type": "object"
}
},
{
"description": "Stream a Sarvam chat completion: resolve credentials, call the upstream Chat Completions API, and relay AssistantMessageEvent frames to writer_ref.",
"metadata": {
"internal": true
},
"name": "provider::sarvam::stream",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"AgentFunction": {
"description": "Function invocation schema — what a provider sees as a `tools` array entry (README § Function invocation schema; adapter boundary). These describe iii functions exposed to the model, not provider-native tools.",
"properties": {
"description": {
"type": "string"
},
"execution_mode": {
"type": [
"string",
"null"
]
},
"label": {
"type": [
"string",
"null"
]
},
"name": {
"type": "string"
},
"parameters": true
},
"required": [
"description",
"name",
"parameters"
],
"type": "object"
},
"AgentMessage": {
"anyOf": [
{
"$ref": "#/definitions/AssistantMessage"
},
{
"$ref": "#/definitions/FunctionResultMessage"
},
{
"$ref": "#/definitions/CustomMessage"
},
{
"$ref": "#/definitions/UserMessage"
}
],
"description": "The canonical transcript message union. Untagged: the single-variant role tags disambiguate deserialization."
},
"AssistantMessage": {
"properties": {
"content": {
"items": {
"$ref": "#/definitions/ContentBlock"
},
"type": "array"
},
"error_kind": {
"anyOf": [
{
"$ref": "#/definitions/ErrorKind"
},
{
"type": "null"
}
]
},
"error_message": {
"type": [
"string",
"null"
]
},
"model": {
"type": "string"
},
"native_stop_reason": {
"type": [
"string",
"null"
]
},
"provider": {
"type": "string"
},
"role": {
"$ref": "#/definitions/AssistantRoleTag"
},
"stop_reason": {
"$ref": "#/definitions/StopReason"
},
"timestamp": {
"format": "int64",
"type": "integer"
},
"usage": {
"anyOf": [
{
"$ref": "#/definitions/Usage"
},
{
"type": "null"
}
]
},
"warnings": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
}
},
"required": [
"content",
"model",
"provider",
"role",
"stop_reason",
"timestamp"
],
"type": "object"
},
"AssistantRoleTag": {
"enum": [
"assistant"
],
"type": "string"
},
"ChannelDirection": {
"enum": [
"read",
"write"
],
"type": "string"
},
"ContentBlock": {
"description": "Content blocks — the atomic units of message content (README § Content blocks).",
"oneOf": [
{
"properties": {
"text": {
"type": "string"
},
"type": {
"enum": [
"text"
],
"type": "string"
}
},
"required": [
"text",
"type"
],
"type": "object"
},
{
"properties": {
"attachment_id": {
"description": "Optional link to the stored original in the session-manager attachment store. Transcript bookkeeping for lazy readers only: the harness clears it before `router::chat`, and no provider mapping reads it. Tolerated here so a block that still carries it deserializes instead of failing the request; omitted on the wire when absent.",
"type": [
"string",
"null"
]
},
"data": {
"type": "string"
},
"mime": {
"type": "string"
},
"type": {
"enum": [
"image"
],
"type": "string"
}
},
"required": [
"data",
"mime",
"type"
],
"type": "object"
},
{
"description": "Reference to an original upload in the session-manager attachment store. A REFERENCE ONLY (never inline bytes); the harness strips it before `router::chat`. Tolerated here so a stray block deserializes instead of failing the request; every counter/mapping emits nothing for it.",
"properties": {
"attachment_id": {
"type": "string"
},
"mime": {
"type": "string"
},
"name": {
"type": "string"
},
"size": {
"format": "uint64",
"minimum": 0,
"type": "integer"
},
"type": {
"enum": [
"file"
],
"type": "string"
}
},
"required": [
"attachment_id",
"mime",
"name",
"size",
"type"
],
"type": "object"
},
{
"properties": {
"signature": {
"type": [
"string",
"null"
]
},
"text": {
"type": "string"
},
"type": {
"enum": [
"thinking"
],
"type": "string"
}
},
"required": [
"text",
"type"
],
"type": "object"
},
{
"description": "Opaque redacted thinking payload — replayed verbatim on the Anthropic wire.",
"properties": {
"data": {
"type": "string"
},
"type": {
"enum": [
"redacted_thinking"
],
"type": "string"
}
},
"required": [
"data",
"type"
],
"type": "object"
},
{
"properties": {
"arguments": true,
"function_id": {
"type": "string"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"function_call"
],
"type": "string"
}
},
"required": [
"arguments",
"function_id",
"id",
"type"
],
"type": "object"
},
{
"properties": {
"content": {
"items": {
"$ref": "#/definitions/ContentBlock"
},
"type": "array"
},
"function_call_id": {
"type": "string"
},
"is_error": {
"type": [
"boolean",
"null"
]
},
"type": {
"enum": [
"function_result"
],
"type": "string"
}
},
"required": [
"content",
"function_call_id",
"type"
],
"type": "object"
}
]
},
"CustomMessage": {
"properties": {
"content": {
"items": {
"$ref": "#/definitions/ContentBlock"
},
"type": "array"
},
"custom_type": {
"type": "string"
},
"details": true,
"display": {
"type": [
"string",
"null"
]
},
"role": {
"$ref": "#/definitions/CustomRoleTag"
},
"timestamp": {
"format": "int64",
"type": "integer"
}
},
"required": [
"content",
"custom_type",
"role",
"timestamp"
],
"type": "object"
},
"CustomRoleTag": {
"enum": [
"custom"
],
"type": "string"
},
"ErrorKind": {
"enum": [
"auth_expired",
"rate_limited",
"context_overflow",
"transient",
"permanent"
],
"type": "string"
},
"FunctionResultMessage": {
"properties": {
"content": {
"items": {
"$ref": "#/definitions/ContentBlock"
},
"type": "array"
},
"details": true,
"function_call_id": {
"type": "string"
},
"function_id": {
"type": "string"
},
"is_error": {
"type": "boolean"
},
"role": {
"$ref": "#/definitions/FunctionResultRoleTag"
},
"timestamp": {
"format": "int64",
"type": "integer"
}
},
"required": [
"content",
"details",
"function_call_id",
"function_id",
"is_error",
"role",
"timestamp"
],
"type": "object"
},
"FunctionResultRoleTag": {
"enum": [
"function_result"
],
"type": "string"
},
"Model": {
"description": "The capability record (README § Model descriptor).",
"properties": {
"context_window": {
"format": "uint64",
"minimum": 0,
"type": "integer"
},
"display_name": {
"type": [
"string",
"null"
]
},
"id": {
"type": "string"
},
"input_limit": {
"format": "uint64",
"minimum": 0,
"type": [
"integer",
"null"
]
},
"max_output_tokens": {
"format": "uint64",
"minimum": 0,
"type": "integer"
},
"pricing": {
"anyOf": [
{
"$ref": "#/definitions/Pricing"
},
{
"type": "null"
}
]
},
"provider": {
"type": "string"
},
"reasoning_efforts": {
"items": {
"$ref": "#/definitions/ReasoningEffort"
},
"type": [
"array",
"null"
]
},
"speech": {
"anyOf": [
{
"$ref": "#/definitions/SpeechModel"
},
{
"type": "null"
}
],
"description": "Set on speech models only; see [`SpeechModel`]."
},
"supports_cache": {
"type": [
"boolean",
"null"
]
},
"supports_structured_output": {
"type": [
"boolean",
"null"
]
},
"supports_thinking": {
"type": [
"boolean",
"null"
]
},
"supports_thinking_off": {
"description": "Whether `thinking_level: off` can switch the model's reasoning off. `Some(true)`: a native off switch exists. `Some(false)`: the model always reasons, or the provider has no switch. `None`: the provider does not know; consoles show the choice disabled.",
"type": [
"boolean",
"null"
]
},
"supports_tools": {
"type": [
"boolean",
"null"
]
},
"supports_vision": {
"type": [
"boolean",
"null"
]
},
"supports_xhigh": {
"type": [
"boolean",
"null"
]
},
"thinking_budgets": {
"additionalProperties": {
"format": "uint64",
"minimum": 0,
"type": "integer"
},
"type": [
"object",
"null"
]
}
},
"required": [
"context_window",
"id",
"max_output_tokens",
"provider"
],
"type": "object"
},
"Pricing": {
"properties": {
"cache_read": {
"format": "double",
"type": [
"number",
"null"
]
},
"cache_write": {
"format": "double",
"type": [
"number",
"null"
]
},
"input": {
"format": "double",
"type": [
"number",
"null"
]
},
"output": {
"format": "double",
"type": [
"number",
"null"
]
}
},
"type": "object"
},
"PromptCacheIntent": {
"description": "Why a request's stable prefix is shareable across sessions: `surface_digest` is the caller's content identity for the text before the boundary (`sha256:<hex>`). A routing/accounting hint only — never a provider cache handle, and never evidence of a cache hit.",
"properties": {
"surface_digest": {
"type": "string"
}
},
"required": [
"surface_digest"
],
"type": "object"
},
"PromptSection": {
"description": "One ordered system-prompt section. `cache_boundary` marks the end of a cumulative prefix worth its own provider cache entry (the frozen agent-profile surface). Adapters without a boundary concept read the sections joined with \"\\n\\n\", which must equal `system_prompt` when both forms are sent.",
"properties": {
"cache_boundary": {
"default": false,
"type": "boolean"
},
"text": {
"type": "string"
}
},
"required": [
"text"
],
"type": "object"
},
"ReasoningEffort": {
"description": "One provider-native reasoning effort advertised for a specific model.\n\nValues intentionally remain strings: provider catalogs can add efforts without requiring a router-wide enum release first.",
"properties": {
"description": {
"type": [
"string",
"null"
]
},
"effort": {
"type": "string"
}
},
"required": [
"effort"
],
"type": "object"
},
"ResponseFormat": {
"properties": {
"schema": true,
"type": {
"type": "string"
}
},
"required": [
"type"
],
"type": "object"
},
"SpeechModality": {
"description": "What a speech model does. Chat models carry no `speech` block.",
"oneOf": [
{
"description": "Speech to text, served through `router::transcribe`.",
"enum": [
"stt"
],
"type": "string"
},
{
"description": "Text to speech, served through `router::speak`.",
"enum": [
"tts"
],
"type": "string"
}
]
},
"SpeechModel": {
"description": "Facts about a speech model. Present only on models served through `router::transcribe` / `router::speak`; such models report `context_window` and `max_output_tokens` as 0.",
"properties": {
"languages": {
"description": "BCP-47 tags of the languages the model handles; empty when the provider does not say.",
"items": {
"type": "string"
},
"type": "array"
},
"modality": {
"$ref": "#/definitions/SpeechModality"
},
"streaming": {
"default": false,
"description": "Realtime input (stt) or streamed audio output (tts) is available.",
"type": "boolean"
}
},
"required": [
"modality"
],
"type": "object"
},
"StopReason": {
"enum": [
"end",
"length",
"function_call",
"aborted",
"error"
],
"type": "string"
},
"StreamChannelRef": {
"properties": {
"access_key": {
"type": "string"
},
"channel_id": {
"type": "string"
},
"direction": {
"$ref": "#/definitions/ChannelDirection"
}
},
"required": [
"access_key",
"channel_id",
"direction"
],
"type": "object"
},
"ThinkingLevel": {
"description": "\"off\" asks the provider not to reason at all and is honoured only where the model advertises `Model::supports_thinking_off` (elsewhere the provider falls back to its own lowest setting, or omits the parameter, and warns). \"minimal\" requests the lowest reasoning effort and needs only `thinking` support; levels map to provider-native knobs via `Model::thinking_budgets`.",
"enum": [
"off",
"minimal",
"low",
"medium",
"high",
"xhigh"
],
"type": "string"
},
"Usage": {
"properties": {
"cache_read": {
"format": "uint64",
"minimum": 0,
"type": [
"integer",
"null"
]
},
"cache_write": {
"format": "uint64",
"minimum": 0,
"type": [
"integer",
"null"
]
},
"cost_usd": {
"format": "double",
"type": [
"number",
"null"
]
},
"input": {
"format": "uint64",
"minimum": 0,
"type": [
"integer",
"null"
]
},
"output": {
"format": "uint64",
"minimum": 0,
"type": [
"integer",
"null"
]
},
"reasoning": {
"format": "uint64",
"minimum": 0,
"type": [
"integer",
"null"
]
}
},
"type": "object"
},
"UserMessage": {
"properties": {
"content": {
"items": {
"$ref": "#/definitions/ContentBlock"
},
"type": "array"
},
"role": {
"$ref": "#/definitions/UserRoleTag"
},
"timestamp": {
"format": "int64",
"type": "integer"
}
},
"required": [
"content",
"role",
"timestamp"
],
"type": "object"
},
"UserRoleTag": {
"description": "Single-variant role tags: exact-match on deserialize, correct wire string on serialize, and they let `AgentMessage` be an untagged union.",
"enum": [
"user"
],
"type": "string"
}
},
"description": "Input of a provider worker's `provider::<id>::stream` iii function — what the router forwards per attempt. (No `PartialEq`: `iii_sdk::StreamChannelRef` doesn't implement it.)",
"properties": {
"cache_intent": {
"anyOf": [
{
"$ref": "#/definitions/PromptCacheIntent"
},
{
"type": "null"
}
]
},
"max_output_tokens": {
"format": "uint64",
"minimum": 0,
"type": [
"integer",
"null"
]
},
"messages": {
"items": {
"$ref": "#/definitions/AgentMessage"
},
"type": "array"
},
"model": {
"type": "string"
},
"model_meta": {
"anyOf": [
{
"$ref": "#/definitions/Model"
},
{
"type": "null"
}
]
},
"provider_options": true,
"resolution_key": {
"type": [
"string",
"null"
]
},
"response_format": {
"anyOf": [
{
"$ref": "#/definitions/ResponseFormat"
},
{
"type": "null"
}
]
},
"session_id": {
"description": "Stable conversation identity for provider cache affinity.",
"type": [
"string",
"null"
]
},
"system_prompt": {
"type": [
"string",
"null"
]
},
"system_sections": {
"description": "Ordered sections behind `system_prompt` (always forwarded alongside the flat string, which equals the sections joined with \"\\n\\n\").",
"items": {
"$ref": "#/definitions/PromptSection"
},
"type": [
"array",
"null"
]
},
"thinking_level": {
"anyOf": [
{
"$ref": "#/definitions/ThinkingLevel"
},
{
"type": "null"
}
]
},
"tools": {
"items": {
"$ref": "#/definitions/AgentFunction"
},
"type": [
"array",
"null"
]
},
"writer_ref": {
"$ref": "#/definitions/StreamChannelRef"
}
},
"required": [
"messages",
"model",
"writer_ref"
],
"title": "ProviderStreamInput",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Output of a provider's `provider::<id>::stream` (spec § stream contract): the function streams frames to `writer_ref` and returns this ack.",
"properties": {
"ok": {
"type": "boolean"
}
},
"required": [
"ok"
],
"title": "ProviderStreamOutput",
"type": "object"
}
},
{
"description": "Speech to text on Sarvam Saaras behind router::transcribe: base64 audio in, text with timed segments and the detected language out; 22 Indian languages and English.",
"metadata": {
"internal": true
},
"name": "provider::sarvam::transcribe",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"audio_base64": {
"description": "The audio, base64. WAV, MP3, AAC, OGG, OPUS, FLAC, M4A, WebM.",
"type": "string"
},
"language": {
"default": null,
"description": "Language hint (`hi`, `hi-IN`, ...); detected when omitted.",
"type": [
"string",
"null"
]
},
"mime": {
"default": null,
"description": "MIME type of the audio; `audio/wav` when omitted.",
"type": [
"string",
"null"
]
},
"model": {
"default": null,
"description": "Speech-to-text model id; `saaras:v3` when omitted.",
"type": [
"string",
"null"
]
},
"prompt": {
"default": null,
"description": "Accepted for the shared contract; Saaras takes no prompt.",
"type": [
"string",
"null"
]
}
},
"required": [
"audio_base64"
],
"title": "TranscribeRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"TranscriptSegment": {
"properties": {
"end_secs": {
"format": "double",
"type": [
"number",
"null"
]
},
"start_secs": {
"format": "double",
"type": [
"number",
"null"
]
},
"text": {
"type": "string"
}
},
"required": [
"text"
],
"type": "object"
}
},
"properties": {
"duration_secs": {
"format": "double",
"type": [
"number",
"null"
]
},
"language": {
"description": "Language Saaras detected or was told, as `hi-IN`.",
"type": [
"string",
"null"
]
},
"model": {
"type": "string"
},
"segments": {
"description": "Sentences, cut at punctuation or a pause, with word timings.",
"items": {
"$ref": "#/definitions/TranscriptSegment"
},
"type": "array"
},
"text": {
"type": "string"
}
},
"required": [
"model",
"text"
],
"title": "TranscribeResponse",
"type": "object"
}
}
],
"triggers": []
}