@jambonz/schema 0.4.6 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -35,7 +35,7 @@ The verb schemas and JSON structure are identical in both modes. The difference
35
35
  - **Webhook**: Simple IVR, call routing, voicemail, basic gather-and-respond patterns.
36
36
  - **WebSocket**: LLM-powered voice agents, real-time audio streaming, complex conversational flows, anything requiring bidirectional communication, or asynchronous logic, or streaming tts.
37
37
 
38
- **IMPORTANT**: Any application that uses a speech-to-speech verb (`openai_s2s`, `google_s2s`, `deepgram_s2s`, `ultravox_s2s`, `elevenlabs_s2s`, `s2s`, or `agent`) MUST use WebSocket transport, not webhooks. These verbs require persistent bidirectional communication for real-time audio and events.
38
+ **IMPORTANT**: Any application that uses a speech-to-speech verb (`openai_s2s`, `gptlive_s2s`, `google_s2s`, `deepgram_s2s`, `ultravox_s2s`, `qwen_s2s`, `elevenlabs_s2s`, `s2s`, or `agent`) MUST use WebSocket transport, not webhooks. These verbs require persistent bidirectional communication for real-time audio and events.
39
39
 
40
40
  ## Schema
41
41
 
@@ -62,7 +62,8 @@ Two tools are available:
62
62
  - **gather** — Collect speech (STT) and/or DTMF input. The workhorse for interactive menus and voice input.
63
63
 
64
64
  ### AI & Real-time
65
- - **openai_s2s** / **google_s2s** / **deepgram_s2s** / **ultravox_s2s** — Connect the caller to a vendor-specific LLM for real-time voice conversation. These are the **preferred** verbs when the vendor is known. Each handles the full STT→LLM→TTS flow with the vendor pre-set.
65
+ - **openai_s2s** / **google_s2s** / **deepgram_s2s** / **ultravox_s2s** / **qwen_s2s** — Connect the caller to a vendor-specific LLM for real-time voice conversation. These are the **preferred** verbs when the vendor is known. Each handles the full STT→LLM→TTS flow with the vendor pre-set.
66
+ - **gptlive_s2s** — Connect the caller to OpenAI's GPT Live API (limited-access alpha). A different wire protocol from `openai_s2s` (Realtime): `llmOptions` carries only a `session_update`, there is no `response_create`, and tool calls arrive via a Responses-targeted *delegation*. See [GPT Live S2S specifics](#gpt-live-s2s-specifics) below.
66
67
  - **elevenlabs_s2s** — Connect the caller to an ElevenLabs Conversational AI agent. **Unlike other s2s vendors**, ElevenLabs requires a pre-configured `agent_id` (created in the ElevenLabs dashboard) rather than a model and messages. See [ElevenLabs S2S specifics](#elevenlabs-s2s-specifics) below.
67
68
  - **s2s** — Generic LLM voice conversation verb. Use only when the vendor is determined at runtime (e.g. from an env var). Requires `vendor` to be specified.
68
69
  - **agent** — Higher-level voice AI agent with integrated turn detection. Mix-and-match STT, LLM, and TTS vendors.
@@ -98,7 +99,7 @@ Two tools are available:
98
99
  **IMPORTANT — Code generation rules:**
99
100
 
100
101
  1. **Always use `stream`, never `listen`** — they are synonyms; `stream` is the preferred name.
101
- 2. **Always use the vendor-specific shortcut when the LLM vendor is known** — use `openai_s2s`, `google_s2s`, `elevenlabs_s2s`, `deepgram_s2s`, or `ultravox_s2s`. Do NOT use `llm` or `s2s` with a `vendor` property when a shortcut exists.
102
+ 2. **Always use the vendor-specific shortcut when the LLM vendor is known** — use `openai_s2s`, `gptlive_s2s`, `google_s2s`, `elevenlabs_s2s`, `deepgram_s2s`, `qwen_s2s`, or `ultravox_s2s`. Do NOT use `llm` or `s2s` with a `vendor` property when a shortcut exists.
102
103
  3. **Use `s2s` (not `llm`) when the vendor is dynamic** — e.g. the vendor comes from an env var or runtime config. Both `s2s` and `llm` are synonyms, but prefer `s2s`.
103
104
  4. **Never use `llm` in generated code** — it is a legacy name. Use either a vendor shortcut or `s2s`.
104
105
 
@@ -161,6 +162,51 @@ ElevenLabs works differently from other s2s vendors. Instead of passing a model
161
162
  ]
162
163
  ```
163
164
 
165
+ ### GPT Live S2S Specifics
166
+
167
+ OpenAI's GPT Live API (limited-access alpha) is a different wire protocol from the OpenAI Realtime API behind `openai_s2s`, so it has its own verb and its own `llmOptions` shape.
168
+
169
+ **Key differences from `openai_s2s`:**
170
+ - `llmOptions` takes only `session_update` — there is **no** `response_create`. The model starts and drives the conversation itself.
171
+ - Do NOT put `model` inside `session_update`; the model travels in the connection URL (set the verb's `model` property instead, default `gpt-live-1-boulder-alpha`).
172
+ - Work the model wants done arrives as a **delegation**, selected by `session_update.delegation.type`:
173
+ - `client` — the model asks your app for free-form text context. Answer with an `llm:update` carrying `delegation.context.append` (max 500 tokens, exactly one `input_text` part).
174
+ - `responses` — the model runs a Responses API turn whose `response.*` events stream inline. Function calls are answered with `delegation.function_call_output.create`.
175
+ - A `responses` delegation carries a **required nested object** whose `model` is **mandatory**, and tools go INSIDE it:
176
+ ```json
177
+ "delegation": {
178
+ "type": "responses",
179
+ "responses": {
180
+ "model": "gpt-5.5",
181
+ "tools": [{ "type": "function", "name": "get_weather", "description": "...", "parameters": {} }]
182
+ }
183
+ }
184
+ ```
185
+ Tools placed at `delegation.tools` are rejected by the server. `delegation.responses.model` is the Responses-side model, separate from the GPT Live voice model in the verb's `model` property.
186
+ - Tools — `mcpServers`, `handoff` and `hangup` — only work with `delegation.type: "responses"` **and** `delegation.responses.model` set; with a `client` delegation there is no function-calling protocol to declare them on, and the verb is rejected.
187
+ - Mid-call `llm:update` accepts only `session.update`, `session.context.append`, `delegation.context.append`, `delegation.function_call_output.create` and `session.close`. Realtime events (`response.create`, `response.cancel`, `input_audio_buffer.*`, `conversation.item.create`) are rejected.
188
+ - A later `session.update` is sparse (omitted fields keep their values), but replacing `delegation` requires the complete delegation object.
189
+
190
+ ```json
191
+ [
192
+ {
193
+ "verb": "gptlive_s2s",
194
+ "model": "gpt-live-1-boulder-alpha",
195
+ "auth": { "apiKey": "sk-..." },
196
+ "llmOptions": {
197
+ "session_update": {
198
+ "instructions": "You are a helpful voice assistant. Be concise.",
199
+ "audio": { "output": { "voice": "marin" } },
200
+ "delegation": { "type": "client" }
201
+ }
202
+ },
203
+ "actionHook": "/s2s-complete",
204
+ "eventHook": "/event",
205
+ "events": ["all"]
206
+ }
207
+ ]
208
+ ```
209
+
164
210
  ### Dial with Fallback
165
211
  ```json
166
212
  [
@@ -8,7 +8,7 @@
8
8
  "vendor": {
9
9
  "type": "string",
10
10
  "description": "The TTS vendor to use. Must match a vendor configured in the jambonz platform.",
11
- "examples": ["google", "aws", "microsoft", "elevenlabs", "cartesia", "gradium", "nineninesix", "deepgram", "ibm", "nvidia", "wellsaid", "whisper", "custom"]
11
+ "examples": ["google", "aws", "microsoft", "elevenlabs", "cartesia", "gradium", "nineninesix", "deepgram", "deepgramflux", "ibm", "nvidia", "wellsaid", "whisper", "custom"]
12
12
  },
13
13
  "label": {
14
14
  "type": "string",
@@ -5,6 +5,7 @@ Refer to vendor documentation for supported models, voices, and vendor-specific
5
5
  ### Deepgram
6
6
 
7
7
  - [TTS Models](https://developers.deepgram.com/docs/tts-models)
8
+ - [Flux TTS](https://developers.deepgram.com/docs/flux-tts/overview) (vendor `deepgramflux`)
8
9
 
9
10
  ### Google
10
11
 
@@ -28,6 +28,7 @@
28
28
  { "$ref": "verbs/deepgram_s2s" },
29
29
  { "$ref": "verbs/ultravox_s2s" },
30
30
  { "$ref": "verbs/qwen_s2s" },
31
+ { "$ref": "verbs/gptlive_s2s" },
31
32
  { "$ref": "verbs/dialogflow" },
32
33
  { "$ref": "verbs/agent" },
33
34
  { "$ref": "verbs/transfer" },
package/lib/normalize.js CHANGED
@@ -15,6 +15,7 @@ const verbTransforms = new Map([
15
15
  ['voiceagent_s2s', {verb: 'llm', properties: {vendor: 'voiceagent'}}],
16
16
  ['ultravox_s2s', {verb: 'llm', properties: {vendor: 'ultravox'}}],
17
17
  ['qwen_s2s', {verb: 'llm', properties: {vendor: 'qwen'}}],
18
+ ['gptlive_s2s', {verb: 'llm', properties: {vendor: 'gptlive'}}],
18
19
  ]);
19
20
 
20
21
  function applyVerbTransform(name, data) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jambonz/schema",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "description": "JSON Schema definitions and validation for jambonz verb applications",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,240 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://jambonz.org/schema/verbs/gptlive_s2s",
4
+ "minVersion": "11.1.0",
5
+ "title": "GPT Live S2S",
6
+ "description": "Shortcut for 'llm' with vendor automatically set to 'gptlive'. Connects the caller to OpenAI's GPT Live API (limited-access alpha, e.g. model gpt-live-1-boulder-alpha) for real-time speech-to-speech voice conversation. This is a different wire protocol from the OpenAI Realtime API used by 'openai_s2s': llmOptions takes only a session_update (there is no response_create \u2014 the model starts and drives the conversation itself), and work the model wants done arrives as a delegation. Set session_update.delegation.type to 'client' to be asked for free-form text context (answer with a delegation.context.append via llm:update), or to 'responses' to run a Responses API turn that can make function calls (answer with delegation.function_call_output.create). A 'responses' delegation carries a required nested object, delegation.responses, whose 'model' is mandatory and whose 'tools' take the flat Responses function shape. Tool support \u2014 mcpServers, handoff and hangup \u2014 requires delegation.type 'responses' with delegation.responses.model set. auth takes the OpenAI apiKey; the model is carried in the connection URL, so do not set session_update.model.",
7
+ "type": "object",
8
+ "allOf": [
9
+ {
10
+ "$ref": "../components/llm-base"
11
+ },
12
+ {
13
+ "$comment": "handoff, hangup and mcpServers need a function-calling channel, which only a Responses delegation provides. The feature-server throws when the verb executes otherwise, dropping the call \u2014 catch it at validation instead.",
14
+ "if": {
15
+ "anyOf": [
16
+ {
17
+ "required": [
18
+ "handoff"
19
+ ]
20
+ },
21
+ {
22
+ "required": [
23
+ "hangup"
24
+ ]
25
+ },
26
+ {
27
+ "required": [
28
+ "mcpServers"
29
+ ]
30
+ }
31
+ ]
32
+ },
33
+ "then": {
34
+ "properties": {
35
+ "llmOptions": {
36
+ "properties": {
37
+ "session_update": {
38
+ "properties": {
39
+ "delegation": {
40
+ "properties": {
41
+ "type": {
42
+ "const": "responses"
43
+ }
44
+ },
45
+ "required": [
46
+ "type",
47
+ "responses"
48
+ ]
49
+ }
50
+ },
51
+ "required": [
52
+ "delegation"
53
+ ]
54
+ }
55
+ }
56
+ }
57
+ }
58
+ }
59
+ }
60
+ ],
61
+ "properties": {
62
+ "verb": {
63
+ "const": "gptlive_s2s",
64
+ "description": "The verb name."
65
+ },
66
+ "vendor": {
67
+ "type": "string",
68
+ "const": "gptlive",
69
+ "description": "The LLM vendor (always 'gptlive' for this shortcut)."
70
+ },
71
+ "auth": {
72
+ "type": "object",
73
+ "description": "OpenAI credentials.",
74
+ "properties": {
75
+ "apiKey": {
76
+ "type": "string",
77
+ "description": "The OpenAI API key. Required \u2014 the session cannot be authenticated without it.",
78
+ "minLength": 1
79
+ }
80
+ },
81
+ "required": [
82
+ "apiKey"
83
+ ],
84
+ "additionalProperties": true
85
+ },
86
+ "llmOptions": {
87
+ "type": "object",
88
+ "properties": {
89
+ "session_update": {
90
+ "type": "object",
91
+ "description": "Startup configuration, sent as the first session.update client event. Required: the server withholds session.started \u2014 and therefore caller audio \u2014 until it arrives.",
92
+ "properties": {
93
+ "model": {
94
+ "not": {},
95
+ "description": "Not allowed: the model travels in the connection URL. Set the verb's top-level 'model' property instead."
96
+ },
97
+ "delegation": {
98
+ "type": "object",
99
+ "description": "How the model delegates work it cannot do itself. Replacing this mid-call requires the complete object.",
100
+ "properties": {
101
+ "type": {
102
+ "type": "string",
103
+ "enum": [
104
+ "client",
105
+ "responses"
106
+ ],
107
+ "description": "'client' asks the application for free-form text context; 'responses' runs a Responses API turn that can make function calls. Tools \u2014 mcpServers, handoff, hangup \u2014 require 'responses'."
108
+ },
109
+ "responses": {
110
+ "type": "object",
111
+ "description": "Configuration for a Responses-targeted delegation. Required when type is 'responses'.",
112
+ "properties": {
113
+ "model": {
114
+ "type": "string",
115
+ "description": "The Responses-side model the delegated turns run on (e.g. gpt-5.5). Required by the server, and distinct from the GPT Live voice model in the connection URL.",
116
+ "examples": [
117
+ "gpt-5.5"
118
+ ]
119
+ },
120
+ "tools": {
121
+ "type": "array",
122
+ "items": {
123
+ "type": "object"
124
+ },
125
+ "description": "Function tools in the flat Responses shape: {type:'function', name, description, parameters}. jambonz prepends the injected handoff/hangup/MCP tools here."
126
+ },
127
+ "instructions": {
128
+ "type": "string"
129
+ }
130
+ },
131
+ "required": [
132
+ "model"
133
+ ],
134
+ "additionalProperties": true
135
+ },
136
+ "tools": {
137
+ "not": {},
138
+ "description": "Not allowed here: a Responses delegation's tools belong at delegation.responses.tools. Tools placed at this level are rejected by the server at session start."
139
+ }
140
+ },
141
+ "additionalProperties": true,
142
+ "if": {
143
+ "properties": {
144
+ "type": {
145
+ "const": "responses"
146
+ }
147
+ },
148
+ "required": [
149
+ "type"
150
+ ]
151
+ },
152
+ "then": {
153
+ "required": [
154
+ "responses"
155
+ ]
156
+ }
157
+ }
158
+ },
159
+ "additionalProperties": true
160
+ }
161
+ },
162
+ "required": [
163
+ "session_update"
164
+ ],
165
+ "additionalProperties": true
166
+ }
167
+ },
168
+ "required": [
169
+ "auth",
170
+ "llmOptions"
171
+ ],
172
+ "examples": [
173
+ {
174
+ "verb": "gptlive_s2s",
175
+ "model": "gpt-live-1-boulder-alpha",
176
+ "auth": {
177
+ "apiKey": "sk-..."
178
+ },
179
+ "llmOptions": {
180
+ "session_update": {
181
+ "instructions": "You are a helpful voice assistant. Be concise.",
182
+ "audio": {
183
+ "output": {
184
+ "voice": "marin"
185
+ }
186
+ },
187
+ "delegation": {
188
+ "type": "client"
189
+ }
190
+ }
191
+ },
192
+ "eventHook": "/s2s-event",
193
+ "actionHook": "/s2s-complete"
194
+ },
195
+ {
196
+ "verb": "gptlive_s2s",
197
+ "model": "gpt-live-1-boulder-alpha",
198
+ "auth": {
199
+ "apiKey": "sk-..."
200
+ },
201
+ "llmOptions": {
202
+ "session_update": {
203
+ "instructions": "You are a helpful voice assistant. Be concise.",
204
+ "audio": {
205
+ "output": {
206
+ "voice": "marin"
207
+ }
208
+ },
209
+ "delegation": {
210
+ "type": "responses",
211
+ "responses": {
212
+ "model": "gpt-5.5",
213
+ "tools": [
214
+ {
215
+ "type": "function",
216
+ "name": "get_weather",
217
+ "description": "Get the current weather for a city",
218
+ "parameters": {
219
+ "type": "object",
220
+ "properties": {
221
+ "city": {
222
+ "type": "string"
223
+ }
224
+ },
225
+ "required": [
226
+ "city"
227
+ ]
228
+ }
229
+ }
230
+ ]
231
+ }
232
+ }
233
+ }
234
+ },
235
+ "toolHook": "/s2s-tool-call",
236
+ "eventHook": "/s2s-event",
237
+ "actionHook": "/s2s-complete"
238
+ }
239
+ ]
240
+ }