SpyBara
Go Premium

Documentation 2026-10-28 23:00 UTC to 2026-10-29 23:59 UTC

30 files changed +1,987 −151. View all changes and history on the product overview
2026
Sat 31 23:01 Fri 30 23:59 Thu 29 23:59 Wed 28 23:00 Tue 27 23:58 Mon 26 23:58 Sat 24 23:02 Fri 23 23:58 Thu 22 22:59 Wed 21 23:02 Tue 20 23:02 Mon 19 23:00 Sun 18 23:00 Sat 17 22:57 Fri 16 23:02 Thu 15 23:00 Wed 14 23:59 Tue 13 23:00 Sun 11 23:59 Sat 10 23:58 Fri 9 22:57 Thu 8 23:01 Wed 7 23:57 Tue 6 22:58 Mon 5 22:59 Sun 4 22:58 Fri 2 23:58 Thu 1 22:59
Details

873conversations_by_session = {}873conversations_by_session = {}

874 874 

875handle_message = lambda do |session_id:, content:|875handle_message = lambda do |session_id:, content:|

876 unless session_id.is_a?(String) && !session_id.strip.empty? && content.is_a?(String) && !content.strip.empty?

877 raise ArgumentError, "session_id and content are required"

878 end

879 

876 conversation_id = conversations_by_session[session_id]880 conversation_id = conversations_by_session[session_id]

877 unless conversation_id881 unless conversation_id

878 conversation_id = client.conversations.create.id882 conversation_id = client.conversations.create.id

Details

266 notification_channel: {266 notification_channel: {

267 recipients: ["billing@example.com"],267 recipients: ["billing@example.com"],

268 type: :email,268 type: :email,

269 subject_prefix: "[OpenAI spend]"269 subject_prefix: "OpenAI spend"

270 },270 },

271 threshold_amount: 50_000271 threshold_amount: 50_000

272)272)

Details

407```407```

408 408 

409```ruby409```ruby

410# Replace the illustrative IDs and URLs below with your own resource values.410# Replace the illustrative IDs below with values from your command item.

411require "openai"411require "openai"

412 412 

413client = OpenAI::Client.new413# Discover the owning agent before retrieving the turn.

414result = client.beta.agents.sessions.turns.list(414def find_turn(client, session_id, turn_id)

415 "sess_123",415 turns = client.beta.agents.sessions.turns.list(session_id, limit: 100)

416 limit: 20,416 turns.auto_paging_each do |turn|

417 order: "desc"417 if turn.id == turn_id

418)418 return client.beta.agents.sessions.turns.retrieve(turn_id, session_id: session_id)

419puts result.data419 end

420turn = client.beta.agents.sessions.turns.retrieve(420 end

421 "turn_123",421 

422 session_id: "sess_123"422 subagents = client.beta.agents.sessions.subagents.list(session_id, limit: 100)

423)423 subagents.auto_paging_each do |subagent|

424puts turn.subagent_id424 turns = client.beta.agents.sessions.subagents.turns.list(

425 subagent.id, session_id: session_id, limit: 100

426 )

427 turns.auto_paging_each do |turn|

428 if turn.id == turn_id

429 return client.beta.agents.sessions.subagents.turns.retrieve(

430 turn_id, session_id: session_id, subagent_id: subagent.id

431 )

432 end

433 end

434 end

435 raise "Turn #{turn_id} not found in session #{session_id}"

436end

437 

438turn = find_turn(OpenAI::Client.new, "sess_123", "turn_123")

439puts(turn.subagent_id || "main agent")

425```440```

426 441 

427```bash442```bash

Details

443 voice: :alloy,443 voice: :alloy,

444 format: :wav444 format: :wav

445 },445 },

446 store: true446 store: false

447)447)

448 448 

449puts(completion.choices.fetch(0).message.content)449message = completion.choices.fetch(0).message

450puts(message.audio&.transcript || message.content)

450```451```

451 452 

452```bash453```bash

Details

303end303end

304 304 

305puts("Final status: #{response.status}")305puts("Final status: #{response.status}")

306puts("Output:")

306puts(response.output_text)307puts(response.output_text)

307```308```

308 309 


687```ruby688```ruby

688require "openai"689require "openai"

689 690 

690client = OpenAI::Client.new691# An uncapped background novel can run beyond the SDK's default ten minutes.

692client = OpenAI::Client.new(timeout: 30 * 60)

691stream = client.responses.stream(693stream = client.responses.stream(

692 model: "gpt-6-astra",694 model: "gpt-6-astra",

693 input: "Write a very long novel about otters in space.",695 input: "Write a very long novel about otters in space.",


696 698 

697last_sequence_number = -1699last_sequence_number = -1

698response_id = ""700response_id = ""

699stream.each do |event|701begin

702 stream.each do |event|

700 puts(event.type)703 puts(event.type)

704 # The SDK filters transport keepalives before yielding stored events.

701 last_sequence_number = event.sequence_number || last_sequence_number705 last_sequence_number = event.sequence_number || last_sequence_number

702 if event.is_a?(OpenAI::Models::Responses::ResponseCreatedEvent)706 if event.is_a?(OpenAI::Models::Responses::ResponseCreatedEvent)

703 response_id = event.response.id707 response_id = event.response.id

704 end708 end

709 end

710ensure

711 stream.close

705end712end

706 713 

707puts("Response #{response_id}; last sequence number #{last_sequence_number}")714puts("response #{response_id} last cursor #{last_sequence_number}")

708 715 

709# If the connection drops, resume from the last sequence number:716# If the connection drops, resume from the last sequence number:

710# client.responses.stream(response_id: response_id, starting_after: last_sequence_number).each do |event|717# client.responses.stream(response_id: response_id, starting_after: last_sequence_number).each do |event|

Details

245 }245 }

246]246]

247 247 

248response = client.responses.create(248loop do

249 response = client.responses.create(

249 model: "gpt-5.3-codex",250 model: "gpt-5.3-codex",

250 input: conversation,251 input: conversation,

251 store: false,252 store: false,


255 compact_threshold: 200_000256 compact_threshold: 200_000

256 }257 }

257 ]258 ]

258)259 )

259conversation.concat(response.output)260 conversation.concat(response.output)

260conversation << {261 puts(response.output_text)

262 line = $stdin.gets

263 break unless line

264 

265 conversation << {

261 type: :message,266 type: :message,

262 role: :user,267 role: :user,

263 content: "Now implement the next step."268 content: line.chomp

264}

265next_response = client.responses.create(

266 model: "gpt-5.3-codex",

267 input: conversation,

268 store: false,

269 context_management: [

270 {

271 type: :compaction,

272 compact_threshold: 200_000

273 }269 }

274 ]270end

275)

276puts(next_response.output_text)

277```271```

278 272 

279 273 

Details

1800The JavaScript sample uses `npm install openai@^7.10.0 ws`.1800The JavaScript sample uses `npm install openai@^7.10.0 ws`.

1801The Ruby sample uses `gem install openai async-websocket`.1801The Ruby sample uses `gem install openai async-websocket`.

1802 1802 

1803For Go, run `go get github.com/openai/openai-go/v3@v3.73.0`.1803For Go, run `go get github.com/openai/openai-go/v3@v3.85.1`.

1804For Java, add the Maven dependency `com.openai:openai-java:4.79.0`.1804For Java, add the Maven dependency `com.openai:openai-java:4.90.0`.

1805These Go and Java SDK versions provide native Responses WebSocket support.1805These Go and Java SDK versions provide native Responses WebSocket support.

1806 1806 

1807Start a Responses API WebSocket session1807Start a Responses API WebSocket session

guides/evals.md +2 −1

Details

335 name: "Support answer quality",335 name: "Support answer quality",

336 data_source_config: {336 data_source_config: {

337 type: :custom,337 type: :custom,

338 include_sample_schema: true,

338 item_schema: {339 item_schema: {

339 type: :object,340 type: :object,

340 properties: { input: { type: :string } },341 properties: { input: { type: :string } },


346 type: :string_check,347 type: :string_check,

347 name: "mentions_refund",348 name: "mentions_refund",

348 input: "{{sample.output_text}}",349 input: "{{sample.output_text}}",

349 operation: :contains,350 operation: :like,

350 reference: "refund"351 reference: "refund"

351 }352 }

352 ]353 ]

Details

1608 item = event.item1608 item = event.item

1609 next unless item.is_a?(OpenAI::Models::Responses::ResponseFunctionToolCall)1609 next unless item.is_a?(OpenAI::Models::Responses::ResponseFunctionToolCall)

1610 1610 

1611 final_tool_calls[event.output_index] = {1611 final_tool_calls[event.output_index] = item.to_h.merge(arguments: item.arguments.dup)

1612 id: item.id,

1613 call_id: item.call_id,

1614 name: item.name,

1615 type: item.type,

1616 arguments: item.arguments.dup

1617 }

1618 when OpenAI::Models::Responses::ResponseFunctionCallArgumentsDeltaEvent1612 when OpenAI::Models::Responses::ResponseFunctionCallArgumentsDeltaEvent

1619 tool_call = final_tool_calls[event.output_index]1613 tool_call = final_tool_calls[event.output_index]

1620 tool_call[:arguments] << event.delta if tool_call1614 tool_call[:arguments] << event.delta if tool_call

Details

252 }252 }

253 ],253 ],

254 "pass_threshold" => 0.5,254 "pass_threshold" => 0.5,

255 "model" => "o4-mini-2025-04-16",255 "model" => "gpt-4.1-2025-04-14",

256 "range" => [0, 1],256 "range" => [0, 1],

257 "sampling_params" => {257 "sampling_params" => {

258 "max_completions_tokens" => 32768,258 "max_completions_tokens" => 32768,

259 "top_p" => 1,259 "top_p" => 1

260 "reasoning_effort" => "medium"

261 }260 }

262}261}

263item = { reference_answer: 1.0 }262item = { reference_answer: 1.0 }

264model_sample = "0.9"263model_sample = "0.9"

265 264 

266pp(client.fine_tuning.alpha.graders.validate(grader: grader))265puts(client.fine_tuning.alpha.graders.validate(grader: grader).to_json)

267pp(client.fine_tuning.alpha.graders.run(grader: grader, item: item, model_sample: model_sample))266puts(client.fine_tuning.alpha.graders.run(grader: grader, item: item, model_sample: model_sample).to_json)

268```267```

269 268 

270 269 


436item = { reference_answer: "fuzzy wuzzy had no hair" }435item = { reference_answer: "fuzzy wuzzy had no hair" }

437model_sample = "fuzzy wuzzy was a bear"436model_sample = "fuzzy wuzzy was a bear"

438 437 

439pp(client.fine_tuning.alpha.graders.validate(grader: grader))438puts(client.fine_tuning.alpha.graders.validate(grader: grader).to_json)

440pp(client.fine_tuning.alpha.graders.run(grader: grader, item: item, model_sample: model_sample))439puts(client.fine_tuning.alpha.graders.run(grader: grader, item: item, model_sample: model_sample).to_json)

441```440```

442 441 

443 442 

Details

2466 2466 

2467client = OpenAI::Client.new2467client = OpenAI::Client.new

2468images = %w[body-lotion.png bath-bomb.png incense-kit.png soap.png].map do |path|2468images = %w[body-lotion.png bath-bomb.png incense-kit.png soap.png].map do |path|

2469 Pathname(path)2469 OpenAI::FilePart.new(Pathname(path), content_type: "image/png")

2470end2470end

2471result = client.images.edit(2471result = client.images.edit(

2472 image: images,2472 image: images,


2966require "base64"2966require "base64"

2967 2967 

2968client = OpenAI::Client.new2968client = OpenAI::Client.new

2969image = Pathname("sunlit_lounge.png")2969image = OpenAI::FilePart.new(Pathname("sunlit_lounge.png"), content_type: "image/png")

2970mask = Pathname("mask.png")2970mask = OpenAI::FilePart.new(Pathname("mask.png"), content_type: "image/png")

2971result = client.images.edit(2971result = client.images.edit(

2972 image: image,2972 image: image,

2973 mask: mask,2973 mask: mask,


3378 raise unless error.code == "moderation_blocked"3378 raise unless error.code == "moderation_blocked"

3379 3379 

3380 body = Hash.try_convert(error.body) || {}3380 body = Hash.try_convert(error.body) || {}

3381 body = Hash.try_convert(body[:error] || body["error"]) || body

3381 moderation_details = body[:moderation_details] || body["moderation_details"] || {}3382 moderation_details = body[:moderation_details] || body["moderation_details"] || {}

3382 categories = moderation_details[:categories] || moderation_details["categories"] || []3383 categories = moderation_details[:categories] || moderation_details["categories"] || []

3383 stage = moderation_details[:moderation_stage] || moderation_details["moderation_stage"]3384 stage = moderation_details[:moderation_stage] || moderation_details["moderation_stage"]

Details

1396 1396 

1397client = OpenAI::Client.new1397client = OpenAI::Client.new

1398result = client.images.generate(1398result = client.images.generate(

1399 model: "gpt-image-2",1399 model: "gpt-image-2.5-sunburst",

1400 prompt: "Create an original logo for Field & Flour, a local bakery. Use warm, simple shapes on a fully transparent background, with clean alpha edges and no shadow or checkerboard.",1400 prompt: "Create an original logo for Field & Flour, a local bakery. Use warm, simple shapes on a fully transparent background, with clean alpha edges and no shadow or checkerboard.",

1401 size: "1024x1536", quality: :medium, background: :transparent, output_format: :png, n: 41401 size: "1024x1536", quality: :medium, background: :transparent, output_format: :png, n: 4

1402)1402)

1403Array(result.data).each_with_index do |item, index|1403Array(result.data).each_with_index do |item, index|

1404 File.binwrite("logo-generation-#{index + 1}-gpt-image-2.png", Base64.strict_decode64(item.b64_json || raise("No PNG returned")))1404 File.binwrite("logo-generation-#{index + 1}-gpt-image-2.5-sunburst.png", Base64.strict_decode64(item.b64_json || raise("No PNG returned")))

1405end1405end

1406result = client.images.edit(1406result = client.images.edit(

1407 model: "gpt-image-2", image: OpenAI::FilePart.new(Pathname("input_images/shampoo.webp"), content_type: "image/webp"),1407 model: "gpt-image-2.5-sunburst", image: OpenAI::FilePart.new(Pathname("input_images/shampoo.webp"), content_type: "image/webp"),

1408 prompt: "Extract the product onto a fully transparent background. Preserve its geometry and label, with clean edges and no shadow or restyling.",1408 prompt: "Extract the product onto a fully transparent background. Preserve its geometry and label, with clean edges and no shadow or restyling.",

1409 size: "1024x1536", quality: :medium, background: :transparent, output_format: :png1409 size: "1024x1536", quality: :medium, background: :transparent, output_format: :png

1410)1410)

1411File.binwrite("extract-product-gpt-image-2.png", Base64.strict_decode64(Array(result.data).fetch(0).b64_json || raise("No PNG returned")))1411File.binwrite("extract-product-gpt-image-2.5-sunburst.png", Base64.strict_decode64(Array(result.data).fetch(0).b64_json || raise("No PNG returned")))

1412```1412```

1413 1413 

1414 1414 

Details

127}127}

128```128```

129 129 

130```go

131import (

132 "github.com/openai/openai-go/v3/live"

133)

134 

135var session = live.SessionConfigParam{Model: "gpt-live-1", Input: []live.InitialItemUnionParam{

136 {OfUser: &live.InitialItemUserParam{Content: []live.InitialItemUserContentParam{{Text: "I need help with my recent order."}}}},

137 {OfAssistant: &live.InitialItemAssistantParam{Content: []live.InitialItemAssistantContentUnionParam{{OfOutputText: &live.InitialItemAssistantContentOutputTextParam{Text: "What is the order number?"}}}}},

138}}

139```

140 

141```java

142import com.openai.models.live.*;

143 

144var session =

145 SessionConfig.builder()

146 .model("gpt-live-1")

147 .addInput(

148 InitialItem.User.builder()

149 .addContent(

150 InitialItem.User.Content.builder()

151 .text("I need help with my recent order.")

152 .build())

153 .build())

154 .addInput(

155 InitialItem.Assistant.builder()

156 .addContent(

157 InitialItem.Assistant.Content.ofOutputText(

158 InitialItem.Assistant.Content.OutputText.builder()

159 .text("What is the order number?")

160 .build()))

161 .build())

162 .build();

163```

164 

165```ruby

166require "openai"

167session = OpenAI::Models::Live::SessionConfig.new(

168 model: "gpt-live-1", input: [

169 {

170 type: :message,

171 role: :user,

172 content: [

173 {

174 type: :input_text,

175 text: "I need help with my recent order."

176 }

177 ]

178 }, {

179 type: :message,

180 role: :assistant,

181 content: [

182 {

183 type: :output_text,

184 text: "What is the order number?"

185 }

186 ]

187 }

188 ]

189)

190```

191 

130 192 

131The list accepts up to 128 messages and 8,192 combined tokens. Each message has one text part and one of these roles: `developer`, `user`, or `assistant`. Developer and user messages use `input_text`; assistant messages use `text` or `output_text`. Put trusted application instructions in `instructions` or a developer message.193The list accepts up to 128 messages and 8,192 combined tokens. Each message has one text part and one of these roles: `developer`, `user`, or `assistant`. Developer and user messages use `input_text`; assistant messages use `text` or `output_text`. Put trusted application instructions in `instructions` or a developer message.

132 194 


182 )244 )

183```245```

184 246 

247```go

248import (

249 "context"

250 "github.com/openai/openai-go/v3"

251 "github.com/openai/openai-go/v3/live"

252 "github.com/openai/openai-go/v3/packages/param"

253)

254 

255// SendUpdate requires a started Live session.

256func SendUpdate(ctx context.Context, connection *live.LiveConnection) error {

257 return connection.Send(ctx, live.ClientEventUnionParam{OfSessionThinkingAppend: &live.ThinkingAppendEventParam{

258 EventID: openai.String("context_1"),

259 DelegationID: param.Null[string](),

260 Content: "The user has already accepted the terms. The account lookup is still running.",

261 }})

262}

263```

264 

265```java

266import com.openai.core.JsonField;

267import com.openai.helpers.LiveConnection;

268import com.openai.models.live.*;

269 

270public static void sendUpdate(LiveConnection connection) {

271 connection.send(

272 ClientEvent.ofSessionThinkingAppend(

273 ThinkingAppendEvent.builder()

274 .eventId("context_1")

275 .delegationId(JsonField.<String>ofNullable(null))

276 .content(

277 "The user has already accepted the terms. The account lookup is still running.")

278 .build()));

279}

280```

281 

282```ruby

283def send_update(connection)

284 connection.send_event(type: "session.thinking.append", event_id: "context_1", delegation_id: nil, content: "The user has already accepted the terms. The account lookup is still running.")

285end

286```

287 

185 288 

186Handle `session.thinking.appended` with `client_event_id: "context_1"`, or the corresponding error, to track this update. See [Understand when context reaches the model](#understand-when-context-reaches-the-model) for acknowledgment timing.289Handle `session.thinking.appended` with `client_event_id: "context_1"`, or the corresponding error, to track this update. See [Understand when context reaches the model](#understand-when-context-reaches-the-model) for acknowledgment timing.

187 290 


272 )375 )

273```376```

274 377 

378```go

379import (

380 "context"

381 "github.com/openai/openai-go/v3"

382 "github.com/openai/openai-go/v3/live"

383)

384 

385// SendUpdate requires a started Live session.

386func SendUpdate(ctx context.Context, connection *live.LiveConnection) error {

387 return connection.Send(ctx, live.ClientEventUnionParam{OfSessionInputAudioMute: &live.InputAudioMuteEventParam{

388 EventID: openai.String("mute_1"),

389 }})

390}

391```

392 

393```java

394import com.openai.helpers.LiveConnection;

395import com.openai.models.live.*;

396 

397public static void sendUpdate(LiveConnection connection) {

398 connection.send(

399 ClientEvent.ofSessionInputAudioMute(

400 InputAudioMuteEvent.builder().eventId("mute_1").build()));

401}

402```

403 

404```ruby

405def send_update(connection)

406 connection.send_event(type: "session.input_audio.mute", event_id: "mute_1")

407end

408```

409 

275 410 

276Wait for `session.input_audio.muted` with `client_event_id: "mute_1"` before treating the command as accepted. To resume input, send `session.input_audio.unmute` and wait for `session.input_audio.unmuted`. Handle errors for either command.411Wait for `session.input_audio.muted` with `client_event_id: "mute_1"` before treating the command as accepted. To resume input, send `session.input_audio.unmute` and wait for `session.input_audio.unmuted`. Handle errors for either command.

277 412 


337 )472 )

338```473```

339 474 

475```go

476import (

477 "context"

478 "github.com/openai/openai-go/v3"

479 "github.com/openai/openai-go/v3/live"

480 "github.com/openai/openai-go/v3/packages/param"

481)

482 

483// SendUpdate requires a started Live session.

484func SendUpdate(ctx context.Context, connection *live.LiveConnection) error {

485 return connection.Send(ctx, live.ClientEventUnionParam{OfSessionInstructionsAppend: &live.InstructionsAppendEventParam{

486 EventID: openai.String("disclosure_1"),

487 DelegationID: param.Null[string](),

488 Content: "Immediately say the following disclosure exactly and in full before responding to the caller: This call may be recorded for quality and training purposes.",

489 }})

490}

491```

492 

493```java

494import com.openai.core.JsonField;

495import com.openai.helpers.LiveConnection;

496import com.openai.models.live.*;

497 

498public static void sendUpdate(LiveConnection connection) {

499 connection.send(

500 ClientEvent.ofSessionInstructionsAppend(

501 InstructionsAppendEvent.builder()

502 .eventId("disclosure_1")

503 .delegationId(JsonField.<String>ofNullable(null))

504 .content(

505 "Immediately say the following disclosure exactly and in full before responding to the caller: This call may be recorded for quality and training purposes.")

506 .build()));

507}

508```

509 

510```ruby

511def send_update(connection)

512 connection.send_event(type: "session.instructions.append", event_id: "disclosure_1", delegation_id: nil, content: "Immediately say the following disclosure exactly and in full before responding to the caller: This call may be recorded for quality and training purposes.")

513end

514```

515 

340 516 

341Keep input audio running, as in [Greet before the caller speaks](#greet-before-the-caller-speaks). An instruction sent during the conversation can interrupt speech in progress.517Keep input audio running, as in [Greet before the caller speaks](#greet-before-the-caller-speaks). An instruction sent during the conversation can interrupt speech in progress.

342 518 


482 raise RuntimeError("Connection closed before session.closed")658 raise RuntimeError("Connection closed before session.closed")

483```659```

484 660 

661```go

662import (

663 "context"

664 "fmt"

665 "github.com/openai/openai-go/v3"

666 "github.com/openai/openai-go/v3/live"

667 "os"

668 "time"

669)

670 

671func forkSession(ctx context.Context, sourceSessionID string) error {

672 client := openai.NewClient()

673 connection, err := client.Live.Forks.Connect(ctx, sourceSessionID, live.ForkConnectionOptions{})

674 if err != nil {

675 return err

676 }

677 defer connection.Abort()

678 if err := connection.Send(ctx, live.ForkClientEventUnionParam{OfSessionStart: &live.ForkSessionStartEventParam{Session: live.ForkSessionConfigParam{}}}); err != nil {

679 return err

680 }

681 started := false

682 for {

683 event, err := connection.Recv(ctx)

684 if err != nil {

685 return err

686 }

687 switch event.Type {

688 case "error":

689 return fmt.Errorf("Live fork error: %s", event.RawJSON())

690 case "session.started":

691 started = true

692 if err := connection.Send(ctx, live.ForkClientEventUnionParam{OfSessionClose: &live.SessionCloseEventParam{}}); err != nil {

693 return err

694 }

695 case "session.closed":

696 if !started {

697 return fmt.Errorf("fork closed before session.started")

698 }

699 fmt.Fprintln(os.Stderr, "Final session usage", event.OfSessionClosedEvent.Usage)

700 return nil

701 }

702 }

703}

704```

705 

706```java

707import com.openai.client.okhttp.OkHttpClient;

708import com.openai.core.ClientOptions;

709import com.openai.helpers.LiveForkConnection;

710import com.openai.models.live.*;

711import com.openai.models.live.forks.*;

712import java.util.concurrent.TimeUnit;

713 

714public static void forkSession(String sourceSessionId) throws Exception {

715 try (var http = OkHttpClient.builder().build();

716 var connection =

717 LiveForkConnection.connect(

718 ClientOptions.builder().fromEnv().httpClient(http).build(), sourceSessionId)) {

719 connection.send(

720 ForkClientEvent.ofSessionStart(

721 ForkSessionStartEvent.builder()

722 .session(ForkSessionConfig.builder().build())

723 .build()));

724 boolean started = false;

725 long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(30);

726 while (true) {

727 long remaining = deadline - System.nanoTime();

728 if (remaining <= 0) throw new IllegalStateException("Incomplete fork finalization");

729 var event = connection.receiveAsync().get(remaining, TimeUnit.NANOSECONDS);

730 if (event.isErrorEvent()) throw new IllegalStateException(event.toString());

731 if (event.isSessionStarted()) {

732 started = true;

733 connection.send(ForkClientEvent.ofSessionClose(SessionCloseEvent.builder().build()));

734 } else if (event.isSessionClosed()) {

735 if (!started) throw new IllegalStateException("Fork closed before session.started");

736 System.err.println("Final session usage " + event.asSessionClosed().usage());

737 return;

738 }

739 }

740 }

741}

742```

743 

744```ruby

745require "openai"

746require "timeout"

747def fork_websocket_session(source_session_id)

748 client = OpenAI::Client.new

749 Timeout.timeout(30) do

750 client.live.forks.connect(source_session_id) do |connection|

751 connection.send_event(type: :"session.start", session: {})

752 state = {

753 started: false,

754 finalized: false

755 }

756 connection.each do |event|

757 case event

758 when OpenAI::Models::Live::ErrorEvent

759 raise "Live fork error: #{event.to_json}"

760 when OpenAI::Models::Live::SessionStartedEvent

761 state[:started] = true

762 connection.send_event(type: :"session.close")

763 when OpenAI::Models::Live::SessionClosedEvent

764 raise "Fork closed before session.started" unless state[:started]

765 

766 warn "Final session usage #{event.usage}"

767 state[:finalized] = true

768 break

769 end

770 end

771 raise "Connection closed without final session usage" unless state[:finalized]

772 end

773 end

774end

775```

776 

485 777 

486### Start a WebRTC fork778### Start a WebRTC fork

487 779 


512 print(fork.model_dump_json())804 print(fork.model_dump_json())

513```805```

514 806 

807```go

808import (

809 "context"

810 "encoding/json"

811 "github.com/openai/openai-go/v3"

812 "github.com/openai/openai-go/v3/live"

813 "github.com/openai/openai-go/v3/option"

814 "os"

815)

816 

817func forkSession(ctx context.Context, sourceSessionID, offerSDP string) (*live.SessionForkResponse, error) {

818 client := openai.NewClient(option.WithMaxRetries(0))

819 return client.Live.Sessions.Fork(ctx, sourceSessionID, live.SessionForkParams{Transport: live.SessionForkParamsTransport{Sdp: offerSDP}})

820}

821```

822 

823```java

824import com.openai.client.okhttp.OpenAIOkHttpClient;

825import com.openai.models.live.sessions.SessionForkParams;

826import com.openai.models.live.sessions.SessionForkResponse;

827 

828public static SessionForkResponse forkSession(String sourceSessionId, String offerSdp) {

829 var client = OpenAIOkHttpClient.builder().fromEnv().maxRetries(0).build();

830 try {

831 return client

832 .live()

833 .sessions()

834 .fork(

835 SessionForkParams.builder()

836 .sessionId(sourceSessionId)

837 .transport(SessionForkParams.Transport.builder().sdp(offerSdp).build())

838 .build());

839 } finally {

840 client.close();

841 }

842}

843```

844 

845```ruby

846require "openai"

847def fork_session(source_session_id, offer_sdp)

848 client = OpenAI::Client.new(max_retries: 0)

849 client.live.sessions.fork(

850 source_session_id, transport: {

851 type: :webrtc,

852 sdp: offer_sdp

853 }

854 )

855end

856```

857 

515 858 

516Return the response to your frontend, apply `transport.sdp` as the new peer connection's answer, and retain the new `session.id`. Keep the API key on your backend.859Return the response to your frontend, apply `transport.sdp` as the new peer connection's answer, and retain the new `session.id`. Keep the API key on your backend.

517 860 


546 response.stream_to_file("recording.wav")889 response.stream_to_file("recording.wav")

547```890```

548 891 

892```go

893import (

894 "context"

895 "github.com/openai/openai-go/v3"

896 "io"

897 "os"

898)

899 

900func downloadRecording(ctx context.Context, sessionID string) error {

901 client := openai.NewClient()

902 response, err := client.Live.Sessions.DownloadRecording(ctx, sessionID)

903 if err != nil {

904 return err

905 }

906 defer response.Body.Close()

907 file, err := os.Create("recording.wav")

908 if err != nil {

909 return err

910 }

911 defer file.Close()

912 _, err = io.Copy(file, response.Body)

913 return err

914}

915```

916 

917```java

918import com.openai.client.okhttp.OpenAIOkHttpClient;

919import com.openai.models.live.sessions.SessionDownloadRecordingParams;

920import java.nio.file.Files;

921import java.nio.file.Path;

922import java.nio.file.StandardCopyOption;

923 

924public static void downloadRecording(String sessionId) throws Exception {

925 var client = OpenAIOkHttpClient.fromEnv();

926 try (var response =

927 client

928 .live()

929 .sessions()

930 .downloadRecording(

931 SessionDownloadRecordingParams.builder().sessionId(sessionId).build());

932 var body = response.body()) {

933 Files.copy(body, Path.of("recording.wav"), StandardCopyOption.REPLACE_EXISTING);

934 } finally {

935 client.close();

936 }

937}

938```

939 

940```ruby

941require "openai"

942def download_recording(session_id)

943 client = OpenAI::Client.new

944 response = client.live.sessions.download_recording(session_id)

945 File.open("recording.wav", "wb") do |file|

946 IO.copy_stream(response, file)

947 end

948ensure

949 response&.close

950end

951```

952 

549 953 

550## Close idle sessions and resume954## Close idle sessions and resume

551 955 

Details

74}74}

75```75```

76 76 

77```go

78import (

79 "github.com/openai/openai-go/v3"

80 "github.com/openai/openai-go/v3/live"

81)

82 

83var session = live.SessionConfigParam{Model: "gpt-live-1", Delegation: live.SessionConfigDelegationUnionParam{OfResponses: &live.SessionConfigDelegationResponsesParam{Responses: live.ResponsesDelegationConfigParam{Model: "gpt-6-luna", Instructions: openai.String("[Your backend prompt]")}}}}

84```

85 

86```java

87import com.openai.models.live.*;

88 

89var session =

90 SessionConfig.builder()

91 .model("gpt-live-1")

92 .responsesDelegation(

93 ResponsesDelegationConfig.builder()

94 .model("gpt-6-luna")

95 .instructions("[Your backend prompt]")

96 .build())

97 .build();

98```

99 

100```ruby

101require "openai"

102session = OpenAI::Models::Live::SessionConfig.new(

103 model: "gpt-live-1", delegation: {

104 type: :responses,

105 responses: {

106 model: "gpt-6-luna",

107 instructions: "[Your backend prompt]"

108 }

109 }

110)

111```

112 

77 113 

78Start with [`gpt-6-luna`](https://developers.openai.com/api/docs/models/gpt-6-luna), or try [`gpt-6-sol`](https://developers.openai.com/api/docs/models/gpt-6-sol) for more complex backend tasks. Compare answer quality and latency on your tasks before choosing a backend model.114Start with [`gpt-6-luna`](https://developers.openai.com/api/docs/models/gpt-6-luna), or try [`gpt-6-sol`](https://developers.openai.com/api/docs/models/gpt-6-sol) for more complex backend tasks. Compare answer quality and latency on your tasks before choosing a backend model.

79 115 


210 )246 )

211```247```

212 248 

249```go

250import (

251 "context"

252 "github.com/openai/openai-go/v3"

253 "github.com/openai/openai-go/v3/live"

254 "github.com/openai/openai-go/v3/responses"

255)

256 

257// SendUpdate requires a started Live session.

258func SendUpdate(ctx context.Context, connection *live.LiveConnection) error {

259 return connection.Send(ctx, live.ClientEventUnionParam{OfResponseItemCreate: &live.ResponseItemCreateEventParam{

260 EventID: openai.String("tool_result_1"),

261 Item: responses.ResponseInputItemUnionParam{OfFunctionCallOutput: &responses.ResponseInputItemFunctionCallOutputParam{CallID: openai.String("call_123"), Output: responses.ResponseInputItemFunctionCallOutputOutputUnionParam{OfString: openai.String("{\"status\":\"confirmed\",\"order_id\":\"order_123\"}")}}},

262 }})

263}

264```

265 

266```java

267import com.openai.helpers.LiveConnection;

268import com.openai.models.live.*;

269import com.openai.models.responses.*;

270 

271public static void sendUpdate(LiveConnection connection) {

272 connection.send(

273 ClientEvent.ofResponseItemCreate(

274 ResponseItemCreateEvent.builder()

275 .eventId("tool_result_1")

276 .item(

277 ResponseInputItem.FunctionCallOutput.builder()

278 .callId("call_123")

279 .output("{\"status\":\"confirmed\",\"order_id\":\"order_123\"}")

280 .build())

281 .build()));

282}

283```

284 

285```ruby

286def send_update(connection)

287 connection.send_event(

288 type: "response.item.create", event_id: "tool_result_1", item: {

289 type: "function_call_output",

290 call_id: "call_123",

291 output: "{\"status\":\"confirmed\",\"order_id\":\"order_123\"}"

292 }

293 )

294end

295```

296 

213 297 

214Then explicitly continue the response:298Then explicitly continue the response:

215 299 


235 )319 )

236```320```

237 321 

322```go

323import (

324 "context"

325 "github.com/openai/openai-go/v3"

326 "github.com/openai/openai-go/v3/live"

327)

328 

329// SendUpdate requires a started Live session.

330func SendUpdate(ctx context.Context, connection *live.LiveConnection) error {

331 return connection.Send(ctx, live.ClientEventUnionParam{OfResponseCreate: &live.ResponseCreateEventParam{

332 EventID: openai.String("continue_1"),

333 }})

334}

335```

336 

337```java

338import com.openai.helpers.LiveConnection;

339import com.openai.models.live.*;

340 

341public static void sendUpdate(LiveConnection connection) {

342 connection.send(

343 ClientEvent.ofResponseCreate(ResponseCreateEvent.builder().eventId("continue_1").build()));

344}

345```

346 

347```ruby

348def send_update(connection)

349 connection.send_event(type: "response.create", event_id: "continue_1")

350end

351```

352 

238 353 

239Send a `response.item.create` result for every pending function call, then send `response.create` to continue the backend response. `response.item.create` has no separate success acknowledgment; keep processing errors and nested Responses lifecycle events.354Send a `response.item.create` result for every pending function call, then send `response.create` to continue the backend response. `response.item.create` has no separate success acknowledgment; keep processing errors and nested Responses lifecycle events.

240 355 


264session: SessionConfigParam = {"model": "gpt-live-1", "delegation": {"type": "client"}}379session: SessionConfigParam = {"model": "gpt-live-1", "delegation": {"type": "client"}}

265```380```

266 381 

382```go

383import (

384 "github.com/openai/openai-go/v3/live"

385)

386 

387var session = live.SessionConfigParam{Model: "gpt-live-1", Delegation: live.SessionConfigDelegationUnionParam{OfClient: &live.ClientDelegationParam{}}}

388```

389 

390```java

391import com.openai.models.live.*;

392 

393var session =

394 SessionConfig.builder()

395 .model("gpt-live-1")

396 .delegation(ClientDelegation.builder().build())

397 .build();

398```

399 

400```ruby

401require "openai"

402session = OpenAI::Models::Live::SessionConfig.new(model: "gpt-live-1", delegation: { type: :client })

403```

404 

267 405 

268Your application configures and runs the backend: choose its model or service, instructions, tools, and routing. If the backend uses the Responses API, set its model and tools in your application's Responses requests.406Your application configures and runs the backend: choose its model or service, instructions, tools, and routing. If the backend uses the Responses API, set its model and tools in your application's Responses requests.

269 407 


326 )464 )

327```465```

328 466 

467```go

468import (

469 "context"

470 "github.com/openai/openai-go/v3"

471 "github.com/openai/openai-go/v3/live"

472)

473 

474// SendUpdate requires a started Live session.

475func SendUpdate(ctx context.Context, connection *live.LiveConnection) error {

476 return connection.Send(ctx, live.ClientEventUnionParam{OfSessionCommentaryAppend: &live.CommentaryAppendEventParam{

477 EventID: openai.String("result_123"),

478 DelegationID: openai.String("item_9tA2bF3h7K9m2P5q8R1s4"),

479 Content: "The order shipped today and should arrive tomorrow.",

480 }})

481}

482```

483 

484```java

485import com.openai.helpers.LiveConnection;

486import com.openai.models.live.*;

487 

488public static void sendUpdate(LiveConnection connection) {

489 connection.send(

490 ClientEvent.ofSessionCommentaryAppend(

491 CommentaryAppendEvent.builder()

492 .eventId("result_123")

493 .delegationId("item_9tA2bF3h7K9m2P5q8R1s4")

494 .content("The order shipped today and should arrive tomorrow.")

495 .build()));

496}

497```

498 

499```ruby

500def send_update(connection)

501 connection.send_event(type: "session.commentary.append", event_id: "result_123", delegation_id: "item_9tA2bF3h7K9m2P5q8R1s4", content: "The order shipped today and should arrive tomorrow.")

502end

503```

504 

329 505 

330Use `session.commentary.append` for results GPT-Live should say aloud; it is trained to paraphrase the text. Use `session.thinking.append` for facts or progress that it can use in later replies without saying them when they arrive. You can send multiple updates with the same client delegation ID.506Use `session.commentary.append` for results GPT-Live should say aloud; it is trained to paraphrase the text. Use `session.thinking.append` for facts or progress that it can use in later replies without saying them when they arrive. You can send multiple updates with the same client delegation ID.

331 507 


405 )581 )

406```582```

407 583 

584```go

585import (

586 "context"

587 "github.com/openai/openai-go/v3"

588 "github.com/openai/openai-go/v3/live"

589)

590 

591// SendUpdate requires a started Live session.

592func SendUpdate(ctx context.Context, connection *live.LiveConnection) error {

593 return connection.Send(ctx, live.ClientEventUnionParam{OfSessionThinkingAppend: &live.ThinkingAppendEventParam{

594 EventID: openai.String("availability_progress"),

595 DelegationID: openai.String("item_123"),

596 Content: "Checking Thursday availability. No appointment has been booked.",

597 }})

598}

599```

600 

601```java

602import com.openai.helpers.LiveConnection;

603import com.openai.models.live.*;

604 

605public static void sendUpdate(LiveConnection connection) {

606 connection.send(

607 ClientEvent.ofSessionThinkingAppend(

608 ThinkingAppendEvent.builder()

609 .eventId("availability_progress")

610 .delegationId("item_123")

611 .content("Checking Thursday availability. No appointment has been booked.")

612 .build()));

613}

614```

615 

616```ruby

617def send_update(connection)

618 connection.send_event(type: "session.thinking.append", event_id: "availability_progress", delegation_id: "item_123", content: "Checking Thursday availability. No appointment has been booked.")

619end

620```

621 

408 622 

409For a confirmed booking, send the result the user should hear:623For a confirmed booking, send the result the user should hear:

410 624 


434 )648 )

435```649```

436 650 

651```go

652import (

653 "context"

654 "github.com/openai/openai-go/v3"

655 "github.com/openai/openai-go/v3/live"

656)

657 

658// SendUpdate requires a started Live session.

659func SendUpdate(ctx context.Context, connection *live.LiveConnection) error {

660 return connection.Send(ctx, live.ClientEventUnionParam{OfSessionCommentaryAppend: &live.CommentaryAppendEventParam{

661 EventID: openai.String("appointment_result"),

662 DelegationID: openai.String("item_123"),

663 Content: "Your appointment is confirmed for Thursday at 2:00 PM",

664 }})

665}

666```

667 

668```java

669import com.openai.helpers.LiveConnection;

670import com.openai.models.live.*;

671 

672public static void sendUpdate(LiveConnection connection) {

673 connection.send(

674 ClientEvent.ofSessionCommentaryAppend(

675 CommentaryAppendEvent.builder()

676 .eventId("appointment_result")

677 .delegationId("item_123")

678 .content("Your appointment is confirmed for Thursday at 2:00 PM")

679 .build()));

680}

681```

682 

683```ruby

684def send_update(connection)

685 connection.send_event(type: "session.commentary.append", event_id: "appointment_result", delegation_id: "item_123", content: "Your appointment is confirmed for Thursday at 2:00 PM")

686end

687```

688 

437 689 

438Only send that result after the booking has actually succeeded. For a session-wide instruction, use `session.instructions.append` with `delegation_id: null`.690Only send that result after the booking has actually succeeded. For a session-wide instruction, use `session.instructions.append` with `delegation_id: null`.

439 691 


469 )721 )

470```722```

471 723 

724```go

725import (

726 "context"

727 "github.com/openai/openai-go/v3"

728 "github.com/openai/openai-go/v3/live"

729 "github.com/openai/openai-go/v3/packages/param"

730)

731 

732// SendUpdate requires a started Live session.

733func SendUpdate(ctx context.Context, connection *live.LiveConnection) error {

734 return connection.Send(ctx, live.ClientEventUnionParam{OfSessionInstructionsAppend: &live.InstructionsAppendEventParam{

735 EventID: openai.String("guardrail_block_17"),

736 DelegationID: param.Null[string](),

737 Content: "Stop speaking about that request. Briefly explain that you cannot help with it, then wait for the user.",

738 }})

739}

740```

741 

742```java

743import com.openai.core.JsonField;

744import com.openai.helpers.LiveConnection;

745import com.openai.models.live.*;

746 

747public static void sendUpdate(LiveConnection connection) {

748 connection.send(

749 ClientEvent.ofSessionInstructionsAppend(

750 InstructionsAppendEvent.builder()

751 .eventId("guardrail_block_17")

752 .delegationId(JsonField.<String>ofNullable(null))

753 .content(

754 "Stop speaking about that request. Briefly explain that you cannot help with it, then wait for the user.")

755 .build()));

756}

757```

758 

759```ruby

760def send_update(connection)

761 connection.send_event(type: "session.instructions.append", event_id: "guardrail_block_17", delegation_id: nil, content: "Stop speaking about that request. Briefly explain that you cannot help with it, then wait for the user.")

762end

763```

764 

472 765 

473The instruction does not cancel backend work. [Block the affected action and handle any work already running](https://developers.openai.com/api/docs/guides/voice-server-controls?api=live#apply-conversation-guardrails) in your application.766The instruction does not cancel backend work. [Block the affected action and handle any work already running](https://developers.openai.com/api/docs/guides/voice-server-controls?api=live#apply-conversation-guardrails) in your application.

474 767 


588 )881 )

589```882```

590 883 

884```go

885import (

886 "context"

887 "github.com/openai/openai-go/v3"

888 "github.com/openai/openai-go/v3/live"

889 "github.com/openai/openai-go/v3/responses"

890)

891 

892// SendUpdate requires a started Live session.

893func SendUpdate(ctx context.Context, connection *live.LiveConnection) error {

894 return connection.Send(ctx, live.ClientEventUnionParam{OfResponseItemCreate: &live.ResponseItemCreateEventParam{

895 EventID: openai.String("typed_order_number"),

896 Item: responses.ResponseInputItemUnionParam{OfMessage: &responses.EasyInputMessageParam{Role: "user", Content: responses.EasyInputMessageContentUnionParam{OfInputItemContentList: responses.ResponseInputMessageContentListParam{responses.ResponseInputContentParamOfInputText("My order number is A0042.")}}}},

897 }})

898}

899```

900 

901```java

902import com.openai.helpers.LiveConnection;

903import com.openai.models.live.*;

904import com.openai.models.responses.*;

905 

906public static void sendUpdate(LiveConnection connection) {

907 connection.send(

908 ClientEvent.ofResponseItemCreate(

909 ResponseItemCreateEvent.builder()

910 .eventId("typed_order_number")

911 .item(

912 EasyInputMessage.builder()

913 .role(EasyInputMessage.Role.USER)

914 .contentOfResponseInputMessageContentList(

915 java.util.List.of(

916 ResponseInputContent.ofInputText(

917 ResponseInputText.builder()

918 .text("My order number is A0042.")

919 .build())))

920 .build())

921 .build()));

922}

923```

924 

925```ruby

926def send_update(connection)

927 connection.send_event(

928 type: "response.item.create", event_id: "typed_order_number", item: {

929 type: "message",

930 role: "user",

931 content: [

932 {

933 type: "input_text",

934 text: "My order number is A0042."

935 }

936 ]

937 }

938 )

939end

940```

941 

591 942 

592Send `response.create` when ready to run or continue the backend. If it is waiting for function results, return all required results first. Queuing text does not itself cancel work already running.943Send `response.create` when ready to run or continue the backend. If it is waiting for function results, return all required results first. Queuing text does not itself cancel work already running.

593 944 

Details

287 )287 )

288```288```

289 289 

290```go

291import (

292 "context"

293 "github.com/openai/openai-go/v3"

294 "github.com/openai/openai-go/v3/live"

295 "github.com/openai/openai-go/v3/responses"

296)

297 

298// SendUpdate requires a started Live session.

299func SendUpdate(ctx context.Context, connection *live.LiveConnection) error {

300 return connection.Send(ctx, live.ClientEventUnionParam{OfResponseItemCreate: &live.ResponseItemCreateEventParam{

301 EventID: openai.String("availability_result_1"),

302 Item: responses.ResponseInputItemUnionParam{OfFunctionCallOutput: &responses.ResponseInputItemFunctionCallOutputParam{CallID: openai.String("call_availability"), Output: responses.ResponseInputItemFunctionCallOutputOutputUnionParam{OfString: openai.String("{\"available\":true,\"slot_id\":\"slot_friday_14\",\"booked\":false}")}}},

303 }})

304}

305```

306 

307```java

308import com.openai.helpers.LiveConnection;

309import com.openai.models.live.*;

310import com.openai.models.responses.*;

311 

312public static void sendUpdate(LiveConnection connection) {

313 connection.send(

314 ClientEvent.ofResponseItemCreate(

315 ResponseItemCreateEvent.builder()

316 .eventId("availability_result_1")

317 .item(

318 ResponseInputItem.FunctionCallOutput.builder()

319 .callId("call_availability")

320 .output(

321 "{\"available\":true,\"slot_id\":\"slot_friday_14\",\"booked\":false}")

322 .build())

323 .build()));

324}

325```

326 

327```ruby

328def send_update(connection)

329 connection.send_event(

330 type: "response.item.create", event_id: "availability_result_1", item: {

331 type: "function_call_output",

332 call_id: "call_availability",

333 output: "{\"available\":true,\"slot_id\":\"slot_friday_14\",\"booked\":false}"

334 }

335 )

336end

337```

338 

290 339 

291After submitting every required function result, continue the backend:340After submitting every required function result, continue the backend:

292 341 


312 )361 )

313```362```

314 363 

364```go

365import (

366 "context"

367 "github.com/openai/openai-go/v3"

368 "github.com/openai/openai-go/v3/live"

369)

370 

371// SendUpdate requires a started Live session.

372func SendUpdate(ctx context.Context, connection *live.LiveConnection) error {

373 return connection.Send(ctx, live.ClientEventUnionParam{OfResponseCreate: &live.ResponseCreateEventParam{

374 EventID: openai.String("continue_availability_1"),

375 }})

376}

377```

378 

379```java

380import com.openai.helpers.LiveConnection;

381import com.openai.models.live.*;

382 

383public static void sendUpdate(LiveConnection connection) {

384 connection.send(

385 ClientEvent.ofResponseCreate(

386 ResponseCreateEvent.builder().eventId("continue_availability_1").build()));

387}

388```

389 

390```ruby

391def send_update(connection)

392 connection.send_event(type: "response.create", event_id: "continue_availability_1")

393end

394```

395 

315 396 

316For the initial migration, set `parallel_tool_calls` to `false` to handle one tool call at a time. Collect each function call from the inner `response.output_item.done` event and keep its name, arguments, and `call_id`. Keep that record even if a later completion event contains `output: []`. Wait for the completed item before running the handler; the arguments-done event alone lacks the function name and `call_id`. Follow the complete [function-result procedure](https://developers.openai.com/api/docs/guides/live-delegation#complete-a-client-actionable-function-call) for collection, output submission, and errors.397For the initial migration, set `parallel_tool_calls` to `false` to handle one tool call at a time. Collect each function call from the inner `response.output_item.done` event and keep its name, arguments, and `call_id`. Keep that record even if a later completion event contains `output: []`. Wait for the completed item before running the handler; the arguments-done event alone lacks the function name and `call_id`. Follow the complete [function-result procedure](https://developers.openai.com/api/docs/guides/live-delegation#complete-a-client-actionable-function-call) for collection, output submission, and errors.

317 398 


466 )547 )

467```548```

468 549 

550```go

551import (

552 "context"

553 "crypto/rand"

554 "github.com/openai/openai-go/v3"

555 "github.com/openai/openai-go/v3/live"

556)

557 

558type Context struct {

559 Revision int

560 RecentConversation string

561 Task string

562}

563 

564func handleDelegation(ctx context.Context, event live.ServerEventUnion, connection *live.LiveConnection,

565 readContext func() *Context, runAgent func(context.Context, Context) (string, error), currentRevision func() int) error {

566 if event.Type != "session.delegation.created" || event.AsDelegationCreatedEvent().Delegation.Target != "client" {

567 return nil

568 }

569 snapshot := readContext()

570 if snapshot == nil {

571 return nil

572 } // Retain the notice; resolve the request before acting.

573 saved := *snapshot

574 summary, err := runAgent(ctx, saved)

575 if err != nil {

576 return err

577 }

578 if currentRevision() != saved.Revision {

579 return nil

580 }

581 return connection.Send(ctx, live.ClientEventUnionParam{OfSessionCommentaryAppend: &live.CommentaryAppendEventParam{

582 EventID: openai.String(rand.Text()), DelegationID: openai.String(event.AsDelegationCreatedEvent().Delegation.ID), Content: summary,

583 }})

584}

585```

586 

587```java

588import com.openai.helpers.LiveConnection;

589import com.openai.models.live.*;

590import java.util.UUID;

591import java.util.concurrent.CompletionStage;

592import java.util.function.*;

593 

594public record Context(int revision, String recentConversation, String task) {}

595 

596public static CompletionStage<Void> handleDelegation(

597 ServerEvent event,

598 LiveConnection connection,

599 Supplier<Context> readContext,

600 Function<Context, CompletionStage<String>> runAgent,

601 IntSupplier currentRevision) {

602 if (!event.isSessionDelegationCreated()

603 || !event.asSessionDelegationCreated().delegation().target().toString().equals("client"))

604 return java.util.concurrent.CompletableFuture.completedFuture(null);

605 var snapshot = readContext.get();

606 // Retain the notice; resolve the request before acting.

607 if (snapshot == null) return java.util.concurrent.CompletableFuture.completedFuture(null);

608 return runAgent

609 .apply(snapshot)

610 .thenAccept(

611 summary -> {

612 if (currentRevision.getAsInt() != snapshot.revision()) return;

613 connection.send(

614 ClientEvent.ofSessionCommentaryAppend(

615 CommentaryAppendEvent.builder()

616 .eventId(UUID.randomUUID().toString())

617 .delegationId(event.asSessionDelegationCreated().delegation().id())

618 .content(summary)

619 .build()));

620 });

621}

622```

623 

624```ruby

625require "openai"

626require "securerandom"

627Context = Data.define(:revision, :recent_conversation, :task)

628 

629def handle_delegation(event, connection, read_context:, run_agent:, current_revision:)

630 return unless event.type == :"session.delegation.created" && event.delegation.target == :client

631 

632 context = read_context.call

633 return if context.nil? # Retain the notice; resolve the request before acting.

634 

635 summary = run_agent.call(context)

636 return unless current_revision.call == context.revision

637 

638 connection.send_event(

639 type: :"session.commentary.append", event_id: SecureRandom.uuid,

640 delegation_id: event.delegation.id, content: summary

641 )

642end

643```

644 

469 645 

470Implement the context and agent callbacks in your application. The context callback returns recent conversation and the current task, or no value while the request is still unclear. The agent callback runs your existing backend and returns a verified summary of at most 500 tokens. In JavaScript, the application-provided `send` callback sends the JSON event on your Live connection. In Python, the adapter sends the update through the SDK `connection` directly.646Implement the context and agent callbacks in your application. The context callback returns recent conversation and the current task, or no value while the request is still unclear. The agent callback runs your existing backend and returns a verified summary of at most 500 tokens. In JavaScript, the application-provided `send` callback sends the JSON event on your Live connection. In Python, the adapter sends the update through the SDK `connection` directly.

471 647 

Details

3355 "schema" => {3355 "schema" => {

3356 "type" => "object",3356 "type" => "object",

3357 "properties" => {3357 "properties" => {

3358 "description" => { "type" => "string" },

3358 "name" => {3359 "name" => {

3359 "type" => "string",3360 "type" => "string",

3360 "description" => "The name of the schema"3361 "description" => "The name of the schema"


3407 "schema_definition" => {3408 "schema_definition" => {

3408 "type" => "object",3409 "type" => "object",

3409 "properties" => {3410 "properties" => {

3411 "description" => { "type" => "string" },

3410 "type" => {3412 "type" => {

3411 "type" => "string",3413 "type" => "string",

3412 "enum" => ["object", "array", "string", "number", "boolean", "null"]3414 "enum" => ["object", "array", "string", "number", "boolean", "null"]


4824 "schema_definition" => {4826 "schema_definition" => {

4825 "type" => "object",4827 "type" => "object",

4826 "properties" => {4828 "properties" => {

4829 "description" => { "type" => "string" },

4827 "type" => {4830 "type" => {

4828 "type" => "string",4831 "type" => "string",

4829 "enum" => ["object", "array", "string", "number", "boolean", "null"]4832 "enum" => ["object", "array", "string", "number", "boolean", "null"]

Details

247 247 

248Another python library that provides function decorators for backoff and retry is [backoff](https://pypi.org/project/backoff/):248Another python library that provides function decorators for backoff and retry is [backoff](https://pypi.org/project/backoff/):

249 249 

250Using the Tenacity library250Using the backoff library

251 251 

252```python252```python

253import backoff253import backoff

Details

247};247};

248 248 

249// WebRTC data channel and WebSocket both have .send()249// WebRTC data channel and WebSocket both have .send()

250dataChannel.send(JSON.stringify(event));250ws.send(JSON.stringify(event));

251```251```

252 252 

253```python253```python


309 }309 }

310 ]310 ]

311)311)

312 

313receipt = connection.find do |event|

314 raise event.error.message if event.is_a?(OpenAI::Realtime::RealtimeErrorEvent)

315 next unless event.is_a?(OpenAI::Realtime::ConversationItemDone)

316 

317 event.item.is_a?(OpenAI::Realtime::RealtimeConversationItemUserMessage)

318end

319raise "Connection closed before the conversation item was acknowledged" unless receipt

312```320```

313 321 

314 322 


325};333};

326 334 

327// WebRTC data channel and WebSocket both have .send()335// WebRTC data channel and WebSocket both have .send()

328dataChannel.send(JSON.stringify(event));336ws.send(JSON.stringify(event));

329```337```

330 338 

331```python339```python


372 output_modalities: [:text],380 output_modalities: [:text],

373 instructions: "Respond with a concise text message."381 instructions: "Respond with a concise text message."

374)382)

383 

384response_done = connection.find do |event|

385 raise event.error.message if event.is_a?(OpenAI::Realtime::RealtimeErrorEvent)

386 next unless event.is_a?(OpenAI::Realtime::ResponseDoneEvent)

387 

388 raise "Response failed: #{event.response.status}" unless event.response.status == :completed

389 

390 true

391end

392raise "Connection closed before the response completed" unless response_done

375```393```

376 394 

377 395 


389}407}

390 408 

391// Listen for server messages (WebRTC)409// Listen for server messages (WebRTC)

392dataChannel.addEventListener("message", handleEvent);410// dataChannel.addEventListener("message", handleEvent);

393 411 

394// Listen for server messages (WebSocket)412// Listen for server messages (WebSocket)

395// ws.on("message", handleEvent);413ws.on("message", handleEvent);

396```414```

397 415 

398```python416```python


946 }964 }

947 ]965 ]

948)966)

967 

968receipt = connection.find do |event|

969 raise event.error.message if event.is_a?(OpenAI::Realtime::RealtimeErrorEvent)

970 

971 event.is_a?(OpenAI::Realtime::ConversationItemDone) &&

972 event.item.is_a?(OpenAI::Realtime::RealtimeConversationItemUserMessage)

973end

974raise "Connection closed before the audio item was acknowledged" unless receipt

949```975```

950 976 

951 977 


968function handleEvent(message) {994function handleEvent(message) {

969 const serverEvent = JSON.parse(message.toString());995 const serverEvent = JSON.parse(message.toString());

970 if (serverEvent.type === "response.output_audio.delta") {996 if (serverEvent.type === "response.output_audio.delta") {

971 // Access Base64-encoded audio chunks997 const audio = Buffer.from(serverEvent.delta, "base64");

972 // console.log(serverEvent.delta);998 console.log(`Received ${audio.length} audio bytes`);

973 }999 }

974}1000}

975 1001 


1010```1036```

1011 1037 

1012```ruby1038```ruby

1013connection.each do |event|1039response_done = connection.find do |event|

1014 case event1040 case event

1015 when OpenAI::Realtime::ResponseAudioDeltaEvent1041 when OpenAI::Realtime::ResponseAudioDeltaEvent

1016 audio_bytes = Base64.strict_decode64(event.delta)1042 audio_bytes = Base64.strict_decode64(event.delta)

1017 puts("Received #{audio_bytes.bytesize} audio bytes")1043 puts("Received #{audio_bytes.bytesize} audio bytes")

1044 when OpenAI::Realtime::RealtimeErrorEvent

1045 raise event.error.message

1018 when OpenAI::Realtime::ResponseDoneEvent1046 when OpenAI::Realtime::ResponseDoneEvent

1019 break1047 raise "Response failed: #{event.response.status}" unless event.response.status == :completed

1048 

1049 true

1020 end1050 end

1021end1051end

1052raise "Connection closed before the response completed" unless response_done

1022```1053```

1023 1054 

1024 1055 


1067 ]1098 ]

1068)1099)

1069connection.response.create(output_modalities: [:text])1100connection.response.create(output_modalities: [:text])

1101 

1102completion = connection.find do |event|

1103 raise event.error.message if event.is_a?(OpenAI::Realtime::RealtimeErrorEvent)

1104 

1105 event.is_a?(OpenAI::Realtime::ResponseDoneEvent)

1106end

1107unless completion.is_a?(OpenAI::Realtime::ResponseDoneEvent) && completion.response.status == :completed

1108 raise "The Realtime response did not complete"

1109end

1070```1110```

1071 1111 

1072 1112 


1108"support". If it is related to sales, output "sales".1148"support". If it is related to sales, output "sales".

1109`;1149`;

1110 1150 

1111const event = {1151 const event = {

1112 type: "response.create",1152 type: "response.create",

1113 response: {1153 response: {

1114 // Setting to "none" indicates the response is out of band1154 // Setting to "none" indicates the response is out of band


1122 output_modalities: ["text"],1162 output_modalities: ["text"],

1123 instructions: prompt,1163 instructions: prompt,

1124 },1164 },

1125};1165 };

1126 1166 

1127// WebRTC data channel and WebSocket both have .send()1167 // WebRTC data channel and WebSocket both have .send()

1128dataChannel.send(JSON.stringify(event));1168 ws.send(JSON.stringify(event));

1129```1169```

1130 1170 

1131```python1171```python


1206 output_modalities: [:text],1246 output_modalities: [:text],

1207 instructions: "Classify the conversation as support or sales."1247 instructions: "Classify the conversation as support or sales."

1208)1248)

1249 

1250response_done = connection.find do |event|

1251 raise event.error.message if event.is_a?(OpenAI::Realtime::RealtimeErrorEvent)

1252 next unless event.is_a?(OpenAI::Realtime::ResponseDoneEvent)

1253 

1254 raise "Response failed: #{event.response.status}" unless event.response.status == :completed

1255 

1256 true

1257end

1258raise "Connection closed before the response completed" unless response_done

1209```1259```

1210 1260 

1211 1261 


1232}1282}

1233 1283 

1234// Listen for server messages (WebRTC)1284// Listen for server messages (WebRTC)

1235dataChannel.addEventListener("message", handleEvent);1285// dataChannel.addEventListener("message", handleEvent);

1236 1286 

1237// Listen for server messages (WebSocket)1287// Listen for server messages (WebSocket)

1238// ws.on("message", handleEvent);1288ws.on("message", handleEvent);

1239```1289```

1240 1290 

1241```python1291```python


1335 // potentially include existing conversation items:1385 // potentially include existing conversation items:

1336 {1386 {

1337 type: "item_reference",1387 type: "item_reference",

1338 id: "some_conversation_item_id",1388 id: existingItemId,

1339 },1389 },

1340 {1390 {

1341 type: "message",1391 type: "message",


1352};1402};

1353 1403 

1354// WebRTC data channel and WebSocket both have .send()1404// WebRTC data channel and WebSocket both have .send()

1355dataChannel.send(JSON.stringify(event));1405ws.send(JSON.stringify(event));

1356```1406```

1357 1407 

1358```python1408```python


1423This is my handle, this is my spout!1473This is my handle, this is my spout!

1424`;1474`;

1425 1475 

1426const event = {1476 const event = {

1427 type: "response.create",1477 type: "response.create",

1428 response: {1478 response: {

1429 // An empty input array removes existing context1479 // An empty input array removes existing context

1430 input: [],1480 input: [],

1481 output_modalities: ["text"],

1431 instructions: prompt,1482 instructions: prompt,

1432 },1483 },

1433};1484 };

1434 1485 

1435// WebRTC data channel and WebSocket both have .send()1486 // WebRTC data channel and WebSocket both have .send()

1436dataChannel.send(JSON.stringify(event));1487 ws.send(JSON.stringify(event));

1437```1488```

1438 1489 

1439```python1490```python


1500 output_modalities: [:text],1551 output_modalities: [:text],

1501 instructions: "Generate a concise greeting without conversation context."1552 instructions: "Generate a concise greeting without conversation context."

1502)1553)

1554 

1555response_done = connection.find do |event|

1556 raise event.error.message if event.is_a?(OpenAI::Realtime::RealtimeErrorEvent)

1557 next unless event.is_a?(OpenAI::Realtime::ResponseDoneEvent)

1558 

1559 raise "Response failed: #{event.response.status}" unless event.response.status == :completed

1560 

1561 true

1562end

1563raise "Connection closed before the response completed" unless response_done

1503```1564```

1504 1565 

1505 1566 

Details

170 ],170 ],

171 tool_choice: :auto171 tool_choice: :auto

172)172)

173 

174updated = connection.find do |event|

175 raise event.error.message if event.is_a?(OpenAI::Realtime::RealtimeErrorEvent)

176 

177 event.is_a?(OpenAI::Realtime::SessionUpdatedEvent)

178end

179raise "Connection closed before the session update was acknowledged" unless updated

173```180```

174 181 

175 182 


191};198};

192 199 

193ws.send(JSON.stringify(event));200ws.send(JSON.stringify(event));

194ws.send(JSON.stringify({ type: "response.create" }));201ws.send(

202 JSON.stringify({

203 type: "response.create",

204 response: { output_modalities: ["text"], tool_choice: "none" },

205 })

206);

195```207```

196 208 

197```python209```python


294 call_id: call_id,306 call_id: call_id,

295 output: JSON.generate(status: "shipped", delivery_date: "2026-05-09")307 output: JSON.generate(status: "shipped", delivery_date: "2026-05-09")

296)308)

297connection.response.create(tool_choice: :none)309connection.response.create(output_modalities: [:text], tool_choice: :none)

298```310```

299 311 

300 312 


765```777```

766 778 

767```ruby779```ruby

768connection.each do |event|780completed_events = {}

781expected_calls = []

782completed_calls = {}

783complete = connection.find do |event|

769 case event784 case event

770 when OpenAI::Realtime::McpListToolsInProgress785 when OpenAI::Realtime::McpListToolsInProgress

771 puts("Listing MCP tools for item: #{event.item_id}")786 puts("Listing MCP tools for item: #{event.item_id}")

772 when OpenAI::Realtime::McpListToolsFailed787 when OpenAI::Realtime::McpListToolsFailed

773 warn("MCP tool listing failed for item: #{event.item_id}")788 raise "MCP tool listing failed for item: #{event.item_id}"

774 break

775 when OpenAI::Realtime::McpListToolsCompleted789 when OpenAI::Realtime::McpListToolsCompleted

776 puts("MCP tools ready for item: #{event.item_id}")790 puts("MCP tools ready for item: #{event.item_id}")

777 connection.response.create(791 connection.response.create(


788 ]802 ]

789 }803 }

790 ],804 ],

791 tool_choice: :required805 tool_choice: :required,

806 parallel_tool_calls: false

792 )807 )

793 when OpenAI::Realtime::ConversationItemDone808 when OpenAI::Realtime::ConversationItemDone

794 item = event.item809 item = event.item


806 when OpenAI::Realtime::ResponseMcpCallCompleted821 when OpenAI::Realtime::ResponseMcpCallCompleted

807 puts("MCP tool call completed: #{event.item_id}")822 puts("MCP tool call completed: #{event.item_id}")

808 when OpenAI::Realtime::ResponseMcpCallFailed823 when OpenAI::Realtime::ResponseMcpCallFailed

809 warn("MCP tool call failed: #{event.item_id}")824 raise "MCP tool call failed: #{event.item_id}"

810 break

811 when OpenAI::Realtime::ResponseOutputItemDoneEvent825 when OpenAI::Realtime::ResponseOutputItemDoneEvent

812 item = event.item826 item = event.item

813 case item827 case item

814 when OpenAI::Realtime::RealtimeMcpToolCall828 when OpenAI::Realtime::RealtimeMcpToolCall

829 raise "MCP tool call failed" if item.error || item.output.nil?

830 

831 completed_calls[item.id] = true

815 puts("MCP output from #{item.server_label}.#{item.name}: #{item.output}")832 puts("MCP output from #{item.server_label}.#{item.name}: #{item.output}")

816 when OpenAI::Realtime::RealtimeConversationItemAssistantMessage833 when OpenAI::Realtime::RealtimeConversationItemAssistantMessage

817 text = item.content.filter_map do |content|834 text = item.content.filter_map do |content|


820 puts("Assistant: #{text}")837 puts("Assistant: #{text}")

821 end838 end

822 when OpenAI::Realtime::RealtimeErrorEvent839 when OpenAI::Realtime::RealtimeErrorEvent

823 warn("Realtime API error: #{event.error.message}")840 raise "Realtime API error: #{event.error.message}"

824 break

825 when OpenAI::Realtime::ResponseDoneEvent841 when OpenAI::Realtime::ResponseDoneEvent

842 raise "Realtime response did not complete" unless event.response.status == :completed

843 

844 expected_calls.replace(Array(event.response.output).grep(OpenAI::Realtime::RealtimeMcpToolCall).map(&:id))

845 raise "The response did not call an MCP tool" if expected_calls.empty?

846 

847 completed_events[:response] = true

848 end

849 if completed_events[:response] && expected_calls.all? { |id| completed_calls[id] }

826 puts("Realtime turn complete.")850 puts("Realtime turn complete.")

827 break851 true

852 else

853 false

828 end854 end

829end855end

856raise "Connection closed before the MCP tool result arrived" unless complete

830```857```

831 858 

832 859 


939 966 

940connection.conversation.items.create(967connection.conversation.items.create(

941 type: :mcp_approval_response,968 type: :mcp_approval_response,

942 id: "mcp_approval_#{approval_request_id}",969 id: "approval_#{SecureRandom.hex(8)}",

943 approval_request_id: approval_request_id,970 approval_request_id: approval_request_id,

944 approve: true971 approve: true

945)972)


1031 ResponseCreateEvent.builder()1058 ResponseCreateEvent.builder()

1032 .response(1059 .response(

1033 RealtimeResponseCreateParams.builder()1060 RealtimeResponseCreateParams.builder()

1061 .toolChoice(com.openai.models.responses.ToolChoiceOptions.REQUIRED)

1062 .parallelToolCalls(false)

1034 .metadata(1063 .metadata(

1035 RealtimeResponseCreateParams.Metadata.builder()1064 RealtimeResponseCreateParams.Metadata.builder()

1036 .putAdditionalProperty(1065 .putAdditionalProperty(


1102```ruby1131```ruby

1103connection.response.create(1132connection.response.create(

1104 output_modalities: [:text],1133 output_modalities: [:text],

1134 tool_choice: :required,

1135 parallel_tool_calls: false,

1105 input: [1136 input: [

1106 {1137 {

1107 type: :message,1138 type: :message,

Details

921require "openai"921require "openai"

922 922 

923client = OpenAI::Client.new923client = OpenAI::Client.new

924file = client.vector_stores.files.create("vs_123", file_id: "file_123")924file = client.vector_stores.files.create_and_poll("vs_123", file_id: "file_123")

925unless file.status == OpenAI::VectorStores::VectorStoreFile::Status::COMPLETED

926 raise "File indexing did not complete: #{file.last_error&.message || file.status}"

927end

928 

925puts(file.id)929puts(file.id)

926```930```

927 931 

Details

690)690)

691raise "Expected a JSON transcript response" if transcript.is_a?(StringIO)691raise "Expected a JSON transcript response" if transcript.is_a?(StringIO)

692 692 

693pp(transcript[:words])693pp(transcript.deep_to_h.fetch(:words))

694```694```

695 695 

696```bash696```bash

Details

338 task.with_timeout(120) do338 task.with_timeout(120) do

339 client.responses.connect(request_options: { timeout: 10 }) do |connection|339 client.responses.connect(request_options: { timeout: 10 }) do |connection|

340 connection.response.create(340 connection.response.create(

341 model: "gpt-6-astra", reasoning: { effort: "medium" },341 model: "gpt-6-astra", store: false, reasoning: { effort: "medium" },

342 input: "Draft a project plan for building a task-tracking app."342 input: "Draft a project plan for building a task-tracking app."

343 )343 )

344 state = {}344 state = {}

Details

161require "openai"161require "openai"

162 162 

163client = OpenAI::Client.new163client = OpenAI::Client.new

164files = {

165 "lib/fib.py" => "def fib(n):\n if n <= 1:\n return n\n return fib(n-1) + fib(n-2)\n",

166 "run.py" => "from lib.fib import fib\n\ndef main():\n print(fib(42))\n"

167}

168context = files.map { |path, content| "===== #{path}\n#{content}" }.join

164response = client.responses.create(169response = client.responses.create(

165 model: "gpt-6-astra",170 model: "gpt-6-astra",

166 input: "Rename fib() to fibonacci() in lib/fib.py and update run.py to use the new name.",171 input: "The user has the following files:\n<BEGIN_FILES>\n#{context}<END_FILES>\nHelp me rename fib() to fibonacci() in both existing files.",

167 tools: [{ type: :apply_patch }]172 tools: [{ type: :apply_patch }]

168)173)

169 

170patch_calls = response.output.select { |item| item.type == :apply_patch_call }

171puts(patch_calls)

172```174```

173 175 

174 176 


330```332```

331 333 

332```ruby334```ruby

333require "openai"3359.times do |turn|

334 336 raise "Response ended with status #{response.status}" unless response.status == OpenAI::Responses::ResponseStatus::COMPLETED

335client = OpenAI::Client.new337 

336response_id = ENV.fetch("OPENAI_RESPONSE_ID")338 patch_calls = response.output.grep(OpenAI::Models::Responses::ResponseApplyPatchToolCall)

337patch_call_id = ENV.fetch("OPENAI_APPLY_PATCH_CALL_ID")339 if patch_calls.empty?

338response = client.responses.create(340 puts(response.output_text)

339 model: "gpt-6-astra",341 break

340 previous_response_id: response_id,342 end

341 input: [343 raise "Patch workflow exceeded eight tool turns" if turn == 8

344 

345 # apply_operation is application-owned; see "Implementing the patch harness" in the guide.

346 results = patch_calls.map do |call|

347 success, output = apply_operation(files, call.operation)

342 {348 {

343 type: :apply_patch_call_output,349 type: :apply_patch_call_output,

344 call_id: patch_call_id,350 call_id: call.call_id,

345 status: :completed,351 status: success ? :completed : :failed,

346 output: "Patch applied successfully."352 output: output

347 }353 }

348 ],354 end

355 response = client.responses.create(

356 model: "gpt-6-astra",

357 previous_response_id: response.id,

358 input: results,

349 tools: [{ type: :apply_patch }]359 tools: [{ type: :apply_patch }]

350)360 )

351 361end

352puts(response.output_text)362files.each { |path, content| print("===== #{path}\n#{content}") }

353```363```

354 364 

355 365 

Details

1250 1250 

1251client = OpenAI::Client.new1251client = OpenAI::Client.new

1252client.containers.delete("container_id")1252client.containers.delete("container_id")

1253puts("Deleted container_id")1253puts("Container deleted")

1254```1254```

1255 1255 

1256 1256 

Details

415```go415```go

416// The surrounding program prepares an isolated sandbox with the skill files.416// The surrounding program prepares an isolated sandbox with the skill files.

417client := openai.NewClient()417client := openai.NewClient()

418tool := responses.ToolUnionParam{OfShell: &responses.FunctionShellToolParam{Environment: responses.FunctionShellToolEnvironmentUnionParam{OfLocal: &responses.LocalEnvironmentParam{Skills: []responses.LocalSkillParam{{Name: "csv-insights", Description: "Summarize CSV files and produce a markdown report.", Path: "/workspace/skills/csv-insights"}}}}}}418tool := responses.ToolUnionParam{OfShell: &responses.FunctionShellToolParam{

419params := responses.ResponseNewParams{Model: "gpt-6-astra", Tools: []responses.ToolUnionParam{tool}, Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Use the csv-insights skill and run locally to summarize /workspace/reports.csv. Read the skill instructions, write /workspace/report.md, and include the total in your final answer.")}}419 Environment: responses.FunctionShellToolEnvironmentUnionParam{

420 OfLocal: &responses.LocalEnvironmentParam{

421 Skills: []responses.LocalSkillParam{{

422 Name: "csv-insights",

423 Description: "Summarize CSV files and produce a markdown report.",

424 Path: "/workspace/skills/csv-insights",

425 }},

426 },

427 },

428}}

429params := responses.ResponseNewParams{

430 Model: "gpt-6-astra",

431 Tools: []responses.ToolUnionParam{tool},

432 Input: responses.ResponseNewParamsInputUnion{OfString: openai.String(

433 "Use the csv-insights skill and run locally to summarize /workspace/reports.csv. Read the skill instructions, write /workspace/report.md, and include the total in your final answer.")},

434}

420for turn := 0; turn <= 8; turn++ {435for turn := 0; turn <= 8; turn++ {

421 response, err := client.Responses.New(ctx, params)436 response, err := client.Responses.New(ctx, params)

422 if err != nil {437 if err != nil {


498 513 

499```ruby514```ruby

500require "openai"515require "openai"

501 516require "open3"

502client = OpenAI::Client.new517require "securerandom"

503response = client.responses.create(518 

519# LocalSkillSandbox is the application-owned Docker executor defined in this

520# complete example. It prepares the skill files, bounds execution, and cleans up.

521def run_local_skill(name)

522 sandbox = LocalSkillSandbox.new(name)

523 begin

524 # Reserve teardown before creation, even if Docker creation is interrupted.

525 sandbox.prepare

526 client = OpenAI::Client.new(max_retries: 0)

527 params = {

504 model: "gpt-6-astra",528 model: "gpt-6-astra",

505 input: "Use the csv-insights skill to summarize today's CSV reports.",529 input: "Use the csv-insights skill and run locally to summarize /workspace/reports.csv. Read the skill instructions, write /workspace/report.md, and include the total in your final answer.",

506 tools: [530 tools: [

507 {531 {

508 type: :shell,532 type: :shell,


512 {536 {

513 name: "csv-insights",537 name: "csv-insights",

514 description: "Summarize CSV files and produce a Markdown report.",538 description: "Summarize CSV files and produce a Markdown report.",

515 path: "<path-to-skill-folder>"539 path: "/workspace/skills/csv-insights"

516 }540 }

517 ]541 ]

518 }542 }

519 }543 }

520 ]544 ]

521)545 }

522 546 9.times do |turn|

523puts(response.output_text)547 response = client.responses.create(**params, request_options: { timeout: sandbox.remaining })

548 raise "Response did not complete" unless response.status == OpenAI::Responses::ResponseStatus::COMPLETED

549 

550 calls = response.output.grep(OpenAI::Models::Responses::ResponseFunctionShellToolCall)

551 if calls.empty?

552 puts(response.output_text)

553 puts("Report:\n#{sandbox.report}")

554 return response

555 end

556 raise "Shell workflow exceeded eight tool turns" if turn == 8

557 

558 params[:input] = calls.map { |call| sandbox.execute(call) }

559 params[:previous_response_id] = response.id

560 end

561 ensure

562 sandbox.close

563 end

564end

565 

566run_local_skill("ruby-skill-#{SecureRandom.hex(8)}")

524```567```

525 568 

526 569 

Details

123 )123 )

124```124```

125 125 

126```go

127import (

128 "context"

129 "github.com/openai/openai-go/v3"

130 "github.com/openai/openai-go/v3/live"

131 "github.com/openai/openai-go/v3/packages/param"

132)

133 

134// SendSidebandUpdate uses the server connection attached to an existing session.

135func SendSidebandUpdate(ctx context.Context, connection *live.SidebandConnection) error {

136 return connection.Send(ctx, live.ConnectClientEventUnionParam{OfSessionInstructionsAppend: &live.InstructionsAppendEventParam{

137 EventID: openai.String("guardrail_block_17"),

138 DelegationID: param.Null[string](),

139 Content: "Stop speaking immediately. Do not continue or act on the last request. Refuse briefly, then wait.",

140 }})

141}

142 

143// SendUpdate requires a started Live session.

144func SendUpdate(ctx context.Context, connection *live.LiveConnection) error {

145 return connection.Send(ctx, live.ClientEventUnionParam{OfSessionInstructionsAppend: &live.InstructionsAppendEventParam{

146 EventID: openai.String("guardrail_block_17"),

147 DelegationID: param.Null[string](),

148 Content: "Stop speaking immediately. Do not continue or act on the last request. Refuse briefly, then wait.",

149 }})

150}

151```

152 

153```java

154import com.openai.core.JsonField;

155import com.openai.helpers.LiveConnection;

156import com.openai.helpers.LiveSidebandConnection;

157import com.openai.models.live.*;

158 

159// Use the server connection attached to an existing session.

160public static void sendUpdate(LiveSidebandConnection connection) {

161 connection.send(

162 InstructionsAppendEvent.builder()

163 .eventId("guardrail_block_17")

164 .delegationId(JsonField.<String>ofNullable(null))

165 .content(

166 "Stop speaking immediately. Do not continue or act on the last request. Refuse briefly, then wait.")

167 .build());

168}

169 

170// Use a primary connection after its session has started.

171public static void sendUpdate(LiveConnection connection) {

172 connection.send(

173 ClientEvent.ofSessionInstructionsAppend(

174 InstructionsAppendEvent.builder()

175 .eventId("guardrail_block_17")

176 .delegationId(JsonField.<String>ofNullable(null))

177 .content(

178 "Stop speaking immediately. Do not continue or act on the last request. Refuse briefly, then wait.")

179 .build()));

180}

181```

182 

183```ruby

184def send_update(connection)

185 connection.send_event(type: "session.instructions.append", event_id: "guardrail_block_17", delegation_id: nil, content: "Stop speaking immediately. Do not continue or act on the last request. Refuse briefly, then wait.")

186end

187```

188 

126 189 

127Keep the instruction application-authored. Do not copy untrusted user text into it as an instruction. Use `delegation_id: null` for this session-wide correction, and keep `content` within 500 tokens.190Keep the instruction application-authored. Do not copy untrusted user text into it as an instruction. Use `delegation_id: null` for this session-wide correction, and keep `content` within 500 tokens.

128 191 

Details

38 38 

39Save the server example in a new directory and set `OPENAI_API_KEY` in its environment. For Node.js, use `server.mjs` and install `openai` and `express` with `npm install openai express`. For Python, install `openai`. This example binds to `127.0.0.1`, accepts session requests from `http://localhost:3000`, and serves `index.html` from the directory where you run it.39Save the server example in a new directory and set `OPENAI_API_KEY` in its environment. For Node.js, use `server.mjs` and install `openai` and `express` with `npm install openai express`. For Python, install `openai`. This example binds to `127.0.0.1`, accepts session requests from `http://localhost:3000`, and serves `index.html` from the directory where you run it.

40 40 

41For Go, save the example as `main.go`, initialize a module with `go mod init live-demo`, and install `github.com/openai/openai-go/v3@v3.85.1` with `go get`. Run `go run .` with Go 1.26 or later. For Java, use Java 17 or later, add the Maven dependency `com.openai:openai-java:4.90.0`, and run the `LiveConnectionWebrtcExample` class. For Ruby 3.3 or later, install `openai` 0.110.0 and `webrick` with `gem install openai -v 0.110.0` and `gem install webrick`, save the example as `server.rb`, and run `ruby server.rb`.

42 

41Choose a server language below; each variant serves `index.html` and the same `/api/session` endpoint on port 3000. Use an SDK version with Live support. Run only one variant at a time.43Choose a server language below; each variant serves `index.html` and the same `/api/session` endpoint on port 3000. Use an SDK version with Live support. Run only one variant at a time.

42 44 

43```javascript45```javascript


149 self.reply(403, b'{"error":"Unexpected request origin"}')151 self.reply(403, b'{"error":"Unexpected request origin"}')

150 return152 return

151 length = int(self.headers.get("Content-Length", "0"))153 length = int(self.headers.get("Content-Length", "0"))

152 if not 0 < length <= 65536:154 if length > 65536:

155 self.reply(413, b'{"error":"SDP offer is too large"}')

156 return

157 if length <= 0:

153 self.reply(400, b'{"error":"An SDP offer is required"}')158 self.reply(400, b'{"error":"An SDP offer is required"}')

154 return159 return

155 try:160 try:


192 ThreadingHTTPServer(("127.0.0.1", 3000), SessionHandler).serve_forever()197 ThreadingHTTPServer(("127.0.0.1", 3000), SessionHandler).serve_forever()

193```198```

194 199 

200```go

201package main

202 

203import (

204 "encoding/json"

205 "fmt"

206 "github.com/openai/openai-go/v3"

207 "github.com/openai/openai-go/v3/live"

208 "github.com/openai/openai-go/v3/option"

209 "io"

210 "log"

211 "net/http"

212 "os"

213 "strings"

214 "time"

215)

216 

217func main() {

218 client := openai.NewClient(option.WithMaxRetries(0))

219 mux := http.NewServeMux()

220 mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

221 if r.URL.Path != "/" || r.Method != http.MethodGet {

222 http.NotFound(w, r)

223 return

224 }

225 http.ServeFile(w, r, "index.html")

226 })

227 mux.HandleFunc("/api/session", func(w http.ResponseWriter, r *http.Request) {

228 if r.Method != http.MethodPost {

229 w.WriteHeader(http.StatusMethodNotAllowed)

230 return

231 }

232 if r.Header.Get("Origin") != "http://localhost:3000" {

233 http.Error(w, "Forbidden", 403)

234 return

235 }

236 if os.Getenv("OPENAI_API_KEY") == "" {

237 http.Error(w, "Server API key is not configured", 503)

238 return

239 }

240 body, err := io.ReadAll(http.MaxBytesReader(w, r.Body, 64*1024))

241 if err != nil {

242 http.Error(w, "Invalid SDP offer", 413)

243 return

244 }

245 var offer struct {

246 SDP string `json:"sdp"`

247 }

248 if err := json.Unmarshal(body, &offer); err != nil || strings.TrimSpace(offer.SDP) == "" {

249 http.Error(w, "SDP offer is required", 400)

250 return

251 }

252 result, err := client.Live.New(r.Context(), live.LiveNewParams{

253 Session: live.MediaSessionConfigParam{

254 Model: "gpt-live-1",

255 Instructions: openai.String("Be concise. Delegate requests needing current information to the backend, which can search the web."),

256 Delegation: live.MediaSessionConfigDelegationUnionParam{OfResponses: &live.MediaSessionConfigDelegationResponsesParam{Responses: live.ResponsesDelegationConfigParam{

257 Model: "gpt-5.6-terra",

258 Instructions: openai.String("Use web search when current facts are needed. Return concise, grounded results for a spoken conversation."),

259 Tools: []live.ResponsesDelegationConfigToolUnionParam{{OfWebSearch: &live.ResponsesDelegationConfigToolWebSearchParam{}}},

260 ToolChoice: live.ResponsesDelegationConfigToolChoiceUnionParam{OfLiveToolChoiceEnum: openai.String("auto")},

261 }}},

262 },

263 Transport: live.LiveNewParamsTransport{Sdp: offer.SDP},

264 })

265 if err != nil {

266 log.Printf("Live session creation failed: %T", err)

267 http.Error(w, "Session creation failed", 502)

268 return

269 }

270 w.Header().Set("Content-Type", "application/json")

271 w.WriteHeader(http.StatusCreated)

272 if err := json.NewEncoder(w).Encode(result); err != nil {

273 log.Print("Could not write session response")

274 }

275 })

276 server := &http.Server{Addr: "127.0.0.1:3000", Handler: mux, ReadHeaderTimeout: 5 * time.Second}

277 fmt.Println("Open http://localhost:3000")

278 log.Fatal(server.ListenAndServe())

279}

280```

281 

282```java

283import com.openai.client.okhttp.OpenAIOkHttpClient;

284import com.openai.models.live.*;

285import com.sun.net.httpserver.HttpServer;

286import java.net.InetSocketAddress;

287import java.nio.charset.StandardCharsets;

288import java.nio.file.*;

289 

290public final class LiveConnectionWebrtcExample {

291 public static void main(String[] args) throws Exception {

292 var client = OpenAIOkHttpClient.builder().fromEnv().maxRetries(0).build();

293 var server = HttpServer.create(new InetSocketAddress("127.0.0.1", 3000), 0);

294 server.createContext(

295 "/",

296 exchange -> {

297 try (exchange) {

298 var path = exchange.getRequestURI().getPath();

299 int status;

300 byte[] body;

301 String contentType = "text/plain; charset=utf-8";

302 if (path.equals("/") && exchange.getRequestMethod().equals("GET")) {

303 status = 200;

304 body = Files.readAllBytes(Path.of("index.html"));

305 contentType = "text/html; charset=utf-8";

306 } else if (!path.equals("/api/session")) {

307 status = 404;

308 body = "Not found".getBytes(StandardCharsets.UTF_8);

309 } else if (!exchange.getRequestMethod().equals("POST")) {

310 status = 405;

311 body = "Method not allowed".getBytes(StandardCharsets.UTF_8);

312 } else if (!"http://localhost:3000"

313 .equals(exchange.getRequestHeaders().getFirst("Origin"))) {

314 status = 403;

315 body = "Forbidden".getBytes(StandardCharsets.UTF_8);

316 } else if (System.getenv("OPENAI_API_KEY") == null

317 || System.getenv("OPENAI_API_KEY").isBlank()) {

318 status = 503;

319 body = "Server API key is not configured".getBytes(StandardCharsets.UTF_8);

320 } else {

321 var offer = exchange.getRequestBody().readNBytes(64 * 1024 + 1);

322 String sdp = "";

323 if (offer.length <= 64 * 1024) {

324 try {

325 var request = com.openai.core.ObjectMappers.jsonMapper().readTree(offer);

326 if (request != null && request.path("sdp").isTextual()) {

327 sdp = request.path("sdp").asText();

328 }

329 } catch (com.fasterxml.jackson.core.JsonProcessingException error) {

330 // Invalid browser JSON is rejected below without creating a session.

331 }

332 }

333 if (offer.length > 64 * 1024 || sdp.isBlank()) {

334 status = offer.length > 64 * 1024 ? 413 : 400;

335 body = "Invalid SDP offer".getBytes(StandardCharsets.UTF_8);

336 } else {

337 try {

338 var result =

339 client

340 .live()

341 .create(

342 LiveCreateParams.builder()

343 .session(

344 MediaSessionConfig.builder()

345 .model("gpt-live-1")

346 .instructions(

347 "Be concise. Delegate requests needing current information to the backend, which can search the web.")

348 .responsesDelegation(

349 ResponsesDelegationConfig.builder()

350 .model("gpt-5.6-terra")

351 .instructions(

352 "Use web search when current facts are needed. Return concise, grounded results for a spoken conversation.")

353 .addToolWebSearch()

354 .toolChoice(

355 ResponsesDelegationConfig.ToolChoice

356 .LiveToolChoiceEnum.AUTO)

357 .build())

358 .build())

359 .transport(LiveCreateParams.Transport.builder().sdp(sdp).build())

360 .build());

361 body = com.openai.core.ObjectMappers.jsonMapper().writeValueAsBytes(result);

362 status = 201;

363 contentType = "application/json";

364 } catch (RuntimeException error) {

365 System.err.println(

366 "Live session creation failed: " + error.getClass().getSimpleName());

367 status = 502;

368 body = "Session creation failed".getBytes(StandardCharsets.UTF_8);

369 }

370 }

371 }

372 exchange.getResponseHeaders().set("Content-Type", contentType);

373 exchange.sendResponseHeaders(status, body.length);

374 exchange.getResponseBody().write(body);

375 }

376 });

377 Runtime.getRuntime()

378 .addShutdownHook(

379 new Thread(

380 () -> {

381 server.stop(0);

382 client.close();

383 }));

384 server.start();

385 System.out.println("Open http://localhost:3000");

386 }

387}

388```

389 

390```ruby

391require "openai"

392require "webrick"

393require "json"

394client = OpenAI::Client.new(api_key: ENV["OPENAI_API_KEY"] || "", max_retries: 0)

395server = WEBrick::HTTPServer.new(BindAddress: "127.0.0.1", Port: 3000)

396server.mount_proc "/" do |request, response|

397 response["Content-Type"] = "text/plain; charset=utf-8"

398 if request.path == "/" && request.request_method == "GET"

399 response["Content-Type"] = "text/html; charset=utf-8"

400 response.body = File.read("index.html")

401 elsif request.path != "/api/session"

402 response.status = 404

403 response.body = "Not found"

404 elsif request.request_method != "POST"

405 response.status = 405

406 response.body = "Method not allowed"

407 elsif request["origin"] != "http://localhost:3000"

408 response.status = 403

409 response.body = "Forbidden"

410 elsif ENV["OPENAI_API_KEY"].to_s.empty?

411 response.status = 503

412 response.body = "Server API key is not configured"

413 else

414 raise WEBrick::HTTPStatus::LengthRequired if request.content_length.nil?

415 raise WEBrick::HTTPStatus::BadRequest if request["transfer-encoding"]

416 raise WEBrick::HTTPStatus::RequestEntityTooLarge if request.content_length.to_i > 64 * 1024

417 

418 offer = request.body.to_s

419 raise WEBrick::HTTPStatus::RequestEntityTooLarge if offer.bytesize > 64 * 1024

420 

421 begin

422 payload = JSON.parse(offer)

423 rescue JSON::ParserError

424 raise WEBrick::HTTPStatus::BadRequest

425 end

426 sdp = payload.is_a?(Hash) ? payload["sdp"] : nil

427 raise WEBrick::HTTPStatus::BadRequest unless sdp.is_a?(String) && !sdp.strip.empty?

428 

429 begin

430 result = client.live.create(

431 session: {

432 model: "gpt-live-1",

433 instructions: "Be concise. Delegate requests needing current information to the backend, which can search the web.",

434 delegation: {

435 type: :responses,

436 

437 responses: {

438 model: "gpt-5.6-terra",

439 instructions: "Use web search when current facts are needed. Return concise, grounded results for a spoken conversation.",

440 tools: [{ type: :web_search }],

441 

442 tool_choice: :auto

443 }

444 }

445 },

446 transport: {

447 type: :webrtc,

448 sdp: sdp

449 }

450 )

451 response.status = 201

452 response["Content-Type"] = "application/json"

453 response.body = result.to_json

454 rescue OpenAI::Errors::Error => error

455 warn "Live session creation failed: #{error.class}"

456 response.status = 502

457 response.body = "Session creation failed"

458 end

459 end

460end

461trap("INT") { server.shutdown }

462puts "Open http://localhost:3000"

463server.start

464```

465 

195 466 

196Before making the server accessible to other users, protect `/api/session` with your application's authentication, authorization, request limits, and HTTPS. The origin check in this local example does not authenticate users.467Before making the server accessible to other users, protect `/api/session` with your application's authentication, authorization, request limits, and HTTPS. The origin check in this local example does not authenticate users.

197 468 

Details

26 26 

27For Node.js, install `openai` and `ws` with `npm install openai ws` and save the JavaScript example as `client.mjs`. For Python on macOS or Linux, install `openai[realtime]` and save the Python example as `client.py`. Set `OPENAI_API_KEY` in the server environment. These examples require an SDK version with Live support. The example reads raw, mono PCM16 audio at 24 kHz from standard input and writes returned audio in the same format to standard output. Connect these streams to your application's audio capture and playback. Logs and transcript events go to standard error so they do not corrupt the audio stream.27For Node.js, install `openai` and `ws` with `npm install openai ws` and save the JavaScript example as `client.mjs`. For Python on macOS or Linux, install `openai[realtime]` and save the Python example as `client.py`. Set `OPENAI_API_KEY` in the server environment. These examples require an SDK version with Live support. The example reads raw, mono PCM16 audio at 24 kHz from standard input and writes returned audio in the same format to standard output. Connect these streams to your application's audio capture and playback. Logs and transcript events go to standard error so they do not corrupt the audio stream.

28 28 

29For Go, use Go 1.26 or later, save the example as `main.go`, initialize a module with `go mod init live-demo`, and install `github.com/openai/openai-go/v3@v3.85.1` with `go get`. For Java, use Java 17 or later, add the Maven dependency `com.openai:openai-java:4.90.0`, and use the `LiveConnectionWebsocketExample` class. For Ruby 3.3 or later, install the SDK with `gem install openai -v 0.110.0`, install its WebSocket dependency with `gem install async-websocket`, and save the example as `client.rb`.

30 

29```javascript31```javascript

30import OpenAI from "openai";32import OpenAI from "openai";

31import { LiveWS } from "openai/resources/live/ws";33import { LiveWS } from "openai/resources/live/ws";


237 asyncio.run(main())239 asyncio.run(main())

238```240```

239 241 

242```go

243package main

244 

245import (

246 "context"

247 "encoding/base64"

248 "fmt"

249 "github.com/openai/openai-go/v3"

250 "github.com/openai/openai-go/v3/live"

251 "io"

252 "os"

253 "os/signal"

254 "time"

255)

256 

257func run() error {

258 ctx, cancel := context.WithCancel(context.Background())

259 defer cancel()

260 client := openai.NewClient()

261 connection, err := client.Live.Connect(ctx, live.LiveConnectionOptions{})

262 if err != nil {

263 return err

264 }

265 defer connection.Abort()

266 session := live.SessionConfigParam{

267 Model: "gpt-live-1",

268 Instructions: openai.String("Be concise. Delegate requests needing current information to the backend, which can search the web."),

269 Audio: live.SessionConfigAudioParam{Format: live.AudioFormatParamOfAudioPCM(24000), Output: live.SessionConfigAudioOutputParam{Voice: live.SessionConfigAudioOutputVoiceUnionParam{OfString: openai.String("marin")}}},

270 Delegation: live.SessionConfigDelegationUnionParam{OfResponses: &live.SessionConfigDelegationResponsesParam{Responses: live.ResponsesDelegationConfigParam{

271 Model: "gpt-5.6-luna", Tools: []live.ResponsesDelegationConfigToolUnionParam{{OfWebSearch: &live.ResponsesDelegationConfigToolWebSearchParam{}}},

272 ToolChoice: live.ResponsesDelegationConfigToolChoiceUnionParam{OfLiveToolChoiceEnum: openai.String("auto")},

273 }}},

274 }

275 if err := connection.Send(ctx, live.ClientEventUnionParam{OfSessionStart: &live.SessionStartEventParam{Session: session, EventID: openai.String("event_start")}}); err != nil {

276 return err

277 }

278 events := make(chan live.ServerEventUnion)

279 failures := make(chan error, 2)

280 go func() {

281 for {

282 event, err := connection.Recv(ctx)

283 if err != nil {

284 select {

285 case failures <- err:

286 case <-ctx.Done():

287 }

288 return

289 }

290 select {

291 case events <- event:

292 case <-ctx.Done():

293 return

294 }

295 }

296 }()

297 audio := make(chan []byte)

298 interrupts := make(chan os.Signal, 1)

299 signal.Notify(interrupts, os.Interrupt)

300 defer signal.Stop(interrupts)

301 timer := time.NewTimer(30 * time.Second)

302 defer timer.Stop()

303 started, closing := false, false

304 for {

305 select {

306 case <-timer.C:

307 return fmt.Errorf("session startup or finalization timed out")

308 case err := <-failures:

309 return err

310 case <-interrupts:

311 if !started {

312 return fmt.Errorf("interrupted before session.started")

313 }

314 if closing {

315 continue

316 }

317 closing = true

318 timer.Reset(15 * time.Second)

319 if err := connection.Send(ctx, live.ClientEventUnionParam{OfSessionClose: &live.SessionCloseEventParam{}}); err != nil {

320 return err

321 }

322 case chunk := <-audio:

323 if closing {

324 continue

325 }

326 if err := connection.Send(ctx, live.ClientEventUnionParam{OfSessionInputAudioAppend: &live.InputAudioAppendEventParam{Audio: base64.StdEncoding.EncodeToString(chunk)}}); err != nil {

327 return err

328 }

329 case event := <-events:

330 switch event.Type {

331 case "error":

332 return fmt.Errorf("Live error: %s", event.RawJSON())

333 case "session.started":

334 if started {

335 continue

336 }

337 started = true

338 timer.Stop()

339 fmt.Fprintln(os.Stderr, "Session ready", event.AsSessionStartedEvent().Session.ID)

340 // stdin/stdout carry raw mono PCM16 at 24 kHz, not WAV files.

341 go func() {

342 pending := []byte{}

343 for {

344 buffer := make([]byte, 4800)

345 n, err := os.Stdin.Read(buffer)

346 pending = append(pending, buffer[:n]...)

347 complete := len(pending) - len(pending)%2

348 if complete > 0 {

349 chunk := append([]byte(nil), pending[:complete]...)

350 pending = pending[complete:]

351 select {

352 case audio <- chunk:

353 case <-ctx.Done():

354 return

355 }

356 }

357 if err != nil {

358 if err != io.EOF {

359 select {

360 case failures <- err:

361 case <-ctx.Done():

362 }

363 }

364 // EOF does not close the session. Press Ctrl+C to finalize it.

365 return

366 }

367 }

368 }()

369 case "session.output_audio.delta":

370 chunk, err := base64.StdEncoding.DecodeString(event.AsOutputAudioDeltaEvent().Delta)

371 if err != nil {

372 return err

373 }

374 if _, err = os.Stdout.Write(chunk); err != nil {

375 return err

376 }

377 case "session.closed":

378 if !started {

379 return fmt.Errorf("session closed before startup")

380 }

381 fmt.Fprintln(os.Stderr, "Final session usage", event.AsSessionClosedEvent().Usage)

382 return nil

383 default:

384 fmt.Fprintln(os.Stderr, event.RawJSON())

385 }

386 }

387 }

388}

389func main() {

390 if err := run(); err != nil {

391 fmt.Fprintln(os.Stderr, err)

392 os.Exit(1)

393 }

394}

395```

396 

397```java

398import com.openai.client.okhttp.OkHttpClient;

399import com.openai.core.ClientOptions;

400import com.openai.helpers.LiveConnection;

401import com.openai.models.live.*;

402import java.util.*;

403import java.util.concurrent.*;

404import java.util.concurrent.atomic.AtomicBoolean;

405 

406public final class LiveConnectionWebsocketExample {

407 public static void main(String[] args) throws Exception {

408 var session =

409 SessionConfig.builder()

410 .model("gpt-live-1")

411 .instructions(

412 "Be concise. Delegate requests needing current information to the backend, which can search the web.")

413 .audio(

414 SessionConfig.Audio.builder()

415 .audioPcmFormat(AudioFormat.AudioPcm.Rate.of(24000L))

416 .output(SessionConfig.Audio.Output.builder().voice("marin").build())

417 .build())

418 .responsesDelegation(

419 ResponsesDelegationConfig.builder()

420 .model("gpt-5.6-luna")

421 .addToolWebSearch()

422 .toolChoice(ResponsesDelegationConfig.ToolChoice.LiveToolChoiceEnum.AUTO)

423 .build())

424 .build();

425 var closing = new AtomicBoolean();

426 var finished = new CountDownLatch(1);

427 var audio = new ArrayBlockingQueue<byte[]>(8);

428 var failures = new ArrayBlockingQueue<Exception>(1);

429 try (var http = OkHttpClient.builder().build();

430 var connection =

431 LiveConnection.connect(ClientOptions.builder().fromEnv().httpClient(http).build())) {

432 Thread shutdown =

433 new Thread(

434 () -> {

435 closing.set(true);

436 try {

437 if (!finished.await(15, TimeUnit.SECONDS)) {

438 System.err.println("Incomplete finalization: no session.closed event");

439 connection.close();

440 }

441 } catch (InterruptedException interrupted) {

442 Thread.currentThread().interrupt();

443 }

444 });

445 Runtime.getRuntime().addShutdownHook(shutdown);

446 try {

447 connection.send(

448 ClientEvent.ofSessionStart(

449 SessionStartEvent.builder().session(session).eventId("event_start").build()));

450 boolean started = false, closeSent = false;

451 long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(30);

452 var incoming = connection.receiveAsync();

453 while (true) {

454 if (System.nanoTime() > deadline)

455 throw new IllegalStateException("Session startup or finalization timed out");

456 if (!failures.isEmpty()) throw failures.take();

457 if (closing.get() && started && !closeSent) {

458 closeSent = true;

459 deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(15);

460 connection.send(ClientEvent.ofSessionClose(SessionCloseEvent.builder().build()));

461 }

462 var chunk = audio.poll();

463 if (chunk != null && !closing.get())

464 connection.send(

465 ClientEvent.ofSessionInputAudioAppend(

466 InputAudioAppendEvent.builder()

467 .audio(Base64.getEncoder().encodeToString(chunk))

468 .build()));

469 ServerEvent event;

470 try {

471 event = incoming.get(20, TimeUnit.MILLISECONDS);

472 } catch (TimeoutException waiting) {

473 continue;

474 }

475 if (event.isErrorEvent()) throw new IllegalStateException(event.toString());

476 if (event.isSessionStarted() && !started) {

477 started = true;

478 deadline = Long.MAX_VALUE;

479 System.err.println("Session ready " + event.asSessionStarted().session().id());

480 // stdin/stdout carry raw mono PCM16 at 24 kHz, not WAV files.

481 Thread reader =

482 new Thread(

483 () -> {

484 try {

485 int pending = -1;

486 while (!closing.get()) {

487 byte[] buffer = new byte[4800];

488 int offset = pending < 0 ? 0 : 1;

489 if (offset == 1) buffer[0] = (byte) pending;

490 int count = System.in.read(buffer, offset, buffer.length - offset);

491 // EOF does not close the session. Press Ctrl+C to finalize it.

492 if (count < 0) return;

493 count += offset;

494 pending = count % 2 == 0 ? -1 : Byte.toUnsignedInt(buffer[count - 1]);

495 int complete = count - count % 2;

496 if (complete > 0) audio.put(Arrays.copyOf(buffer, complete));

497 }

498 } catch (Exception error) {

499 failures.offer(error);

500 }

501 });

502 reader.setDaemon(true);

503 reader.start();

504 } else if (event.isSessionOutputAudioDelta()) {

505 System.out.write(Base64.getDecoder().decode(event.asSessionOutputAudioDelta().delta()));

506 System.out.flush();

507 } else if (event.isSessionClosed()) {

508 if (!started) throw new IllegalStateException("Session closed before startup");

509 System.err.println("Final session usage " + event.asSessionClosed().usage());

510 return;

511 } else {

512 System.err.println(event);

513 }

514 incoming = connection.receiveAsync();

515 }

516 } finally {

517 closing.set(true);

518 finished.countDown();

519 try {

520 Runtime.getRuntime().removeShutdownHook(shutdown);

521 } catch (IllegalStateException shuttingDown) {

522 /* The hook is already draining. */

523 }

524 }

525 }

526 }

527}

528```

529 

530```ruby

531require "openai"

532require "base64"

533$stdin.binmode

534$stdout.binmode

535session = {

536 model: "gpt-live-1",

537 instructions: "Be concise. Delegate requests needing current information to the backend, which can search the web.",

538 audio: {

539 format: {

540 type: :"audio/pcm",

541 rate: 24_000

542 },

543 output: { voice: :marin }

544 },

545 delegation: {

546 type: :responses,

547 responses: {

548 model: "gpt-5.6-luna",

549 tools: [{ type: :web_search }],

550 tool_choice: :auto

551 }

552 }

553}

554client = OpenAI::Client.new

555client.live.connect do |connection|

556 state = {

557 closing: false,

558 finalized: false,

559 started: false

560 }

561 threads = {}

562 mutex = Mutex.new

563 previous = trap("INT") do

564 threads[:closer] ||= Thread.new do

565 mutex.synchronize do

566 state[:closing] = true

567 if state[:started]

568 connection.send_event(type: :"session.close")

569 else

570 connection.close

571 end

572 end

573 sleep 15

574 unless state[:finalized]

575 warn "Incomplete finalization: no session.closed event"

576 connection.close

577 end

578 end

579 end

580 begin

581 threads[:startup] = Thread.new do

582 sleep 30

583 unless state[:started]

584 warn "Session startup timed out"

585 connection.close

586 end

587 end

588 connection.send_event(type: :"session.start", session: session, event_id: "event_start")

589 connection.each do |event|

590 case event

591 when OpenAI::Models::Live::ErrorEvent

592 raise "Live error: #{event.to_json}"

593 when OpenAI::Models::Live::SessionStartedEvent

594 next if state[:started]

595 

596 state[:started] = true

597 warn "Session ready #{event.session.id}"

598 # stdin/stdout carry raw mono PCM16 at 24 kHz, not WAV files.

599 threads[:sender] = Thread.new do

600 pending = +"".b

601 loop do

602 pending << $stdin.readpartial(4800)

603 complete = pending.bytesize - pending.bytesize % 2

604 chunk = pending.slice!(0, complete).to_s

605 mutex.synchronize do

606 break if state[:closing]

607 

608 connection.send_event(type: :"session.input_audio.append", audio: Base64.strict_encode64(chunk)) unless chunk.empty?

609 end

610 end

611 rescue EOFError

612 # EOF does not close the session. Press Ctrl+C to finalize it.

613 nil

614 rescue => error

615 warn "Audio input failed: #{error.class}"

616 connection.close

617 end

618 when OpenAI::Models::Live::OutputAudioDeltaEvent

619 $stdout.write(Base64.strict_decode64(event.delta))

620 $stdout.flush

621 when OpenAI::Models::Live::SessionClosedEvent

622 raise "Session closed before startup" unless state[:started]

623 

624 warn "Final session usage #{event.usage}"

625 state[:finalized] = true

626 break

627 else

628 warn event.to_json

629 end

630 end

631 raise "Connection closed without final session usage" unless state[:finalized]

632 ensure

633 trap("INT", previous)

634 threads[:startup]&.kill

635 threads[:startup]&.join

636 threads[:sender]&.kill

637 threads[:sender]&.join

638 threads[:closer]&.kill

639 threads[:closer]&.join

640 end

641end

642```

240 643 

241Run `node client.mjs` or `python client.py` with your audio source and player attached. After `Session ready` appears, supply a continuous microphone stream paced at its recorded sample rate. Piping an entire file at once does not simulate a live microphone. EOF on the audio source does not end the conversation. Send `SIGINT` to the process to request a graceful close.644 

645Run `node client.mjs`, `python client.py`, `go run .`, `ruby client.rb`, or your Java application with your audio source and player attached. After `Session ready` appears, supply a continuous microphone stream paced at its recorded sample rate. Piping an entire file at once does not simulate a live microphone. EOF on the audio source does not end the conversation. Send `SIGINT` to the process to request a graceful close.

242 646 

243The example connects the audio streams; your application handles capture, buffering, playback, and resampling when needed. Test these parts with your devices and network before evaluating model behavior.647The example connects the audio streams; your application handles capture, buffering, playback, and resampling when needed. Test these parts with your devices and network before evaluating model behavior.

244 648 


328 },732 },

329});733});

330 734 

331let sessionCreated = false;735let connected = false;

332 

333ws.on("message", function incoming(message) {736ws.on("message", function incoming(message) {

334 const event = JSON.parse(message.toString());737 const event = JSON.parse(message.toString());

738 if (event.type === "error") throw new Error(event.error.message);

335 if (event.type === "session.created") {739 if (event.type === "session.created") {

336 sessionCreated = true;740 connected = true;

337 console.log("Connected to the Realtime API.", event);741 console.log(`Connected to the Realtime API: ${event.session.id}`);

338 ws.close();742 ws.close(1000);

339 } else if (event.type === "error") {

340 console.error(event);

341 ws.close();

342 }743 }

343});744});

344ws.on("error", function handleError(error) {745ws.on("close", () => {

345 console.error(error);746 if (!connected)

346 ws.close();747 throw new Error("Connection closed before the session was created");

347});

348ws.on("close", function closed() {

349 if (!sessionCreated) {

350 console.error("Connection closed before the Realtime session was created.");

351 process.exitCode = 1;

352 }

353});748});

354```749```

355 750 


428)823)

429 824 

430client.realtime.connect(model: "gpt-realtime-2.1") do |connection|825client.realtime.connect(model: "gpt-realtime-2.1") do |connection|

431 puts("Connected to the Realtime API: #{connection.url.host}")826 created = connection.find do |event|

432 connection.each { |event| puts("Received event: #{event.type}") }827 raise event.error.message if event.is_a?(OpenAI::Realtime::RealtimeErrorEvent)

828 

829 next unless event.is_a?(OpenAI::Realtime::SessionCreatedEvent)

830 

831 puts("Connected to the Realtime API: #{event.session[:id]}")

832 true

833 end

834 raise "Connection closed before the session was created" unless created

433end835end

434```836```

435 837 

Details

16 16 

17Install the WebSocket dependencies with `pip install "openai[realtime]>=3.8.0"` for Python, `npm install openai@^7.10.0 ws` for JavaScript, or `gem install openai async-websocket` for Ruby.17Install the WebSocket dependencies with `pip install "openai[realtime]>=3.8.0"` for Python, `npm install openai@^7.10.0 ws` for JavaScript, or `gem install openai async-websocket` for Ruby.

18 18 

19For Go, run `go get github.com/openai/openai-go/v3@v3.73.0`.19For Go, run `go get github.com/openai/openai-go/v3@v3.85.1`.

20For Java, add the Maven dependency `com.openai:openai-java:4.79.0` and use Java 17 or later for these examples.20For Java, add the Maven dependency `com.openai:openai-java:4.90.0` and use Java 17 or later for these examples.

21These Go and Java SDK versions provide native Responses WebSocket support.21These Go and Java SDK versions provide native Responses WebSocket support.

22 22 

23In WebSocket mode, start each turn by sending a `response.create` event from the client. The payload mirrors the normal [Responses create body](https://developers.openai.com/api/reference/resources/responses/methods/create), except that transport-specific fields like `stream` and `background` are not used.23In WebSocket mode, start each turn by sending a `response.create` event from the client. The payload mirrors the normal [Responses create body](https://developers.openai.com/api/reference/resources/responses/methods/create), except that transport-specific fields like `stream` and `background` are not used.

Details

252)252)

253puts(response.output_text)253puts(response.output_text)

254 254 

255response = client.with_options(data_residency: :eu).responses.create(255# EU processing requires a key from an EU-region project.

256response = client.with_options(data_residency: :eu, api_key: ENV.fetch("OPENAI_EU_API_KEY"), organization: nil, project: nil).responses.create(

256 model: "gpt-5.6-terra",257 model: "gpt-5.6-terra",

257 input: "Reply with OK."258 input: "Reply with OK."

258)259)