@pipedog/openai 0.0.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,8 @@
1
+ // Copyright (c) 2024-2026, Daily
2
+ // Copyright (c) 2026, pipedog contributors
3
+ // SPDX-License-Identifier: BSD-2-Clause
4
+ //
5
+ // Ported from pipecat: src/pipecat/services/openai/live/__init__.py
6
+ // Upstream's package __init__ is empty: import the submodules directly.
7
+ export {};
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,566 @@
1
+ /**
2
+ * OpenAI Live LLM service: full-duplex speech-to-speech over WebSocket.
3
+ *
4
+ * Porting notes:
5
+ * - The WebSocket is opened with `websocketConnect` from core, which sends
6
+ * the `Authorization` header on Bun, Node and Deno. Runtimes whose
7
+ * `WebSocket` can't send headers (Cloudflare Workers) pass a
8
+ * `webSocketFactory` (an addition to the upstream options).
9
+ * - `parse_server_event` raises only `ValueError`, which the receive loop
10
+ * skips; here any (non-cancellation) error from `parseServerEvent` is
11
+ * treated as that.
12
+ * - `OpenAIResponsesReasoningConfig` is sent as its set fields (`effort`,
13
+ * `summary`, `mode`), which is what upstream's `model_dump(exclude_none=True)`
14
+ * produces.
15
+ */
16
+ import { Event, Lock, type Task } from "@pipedog/asyncio";
17
+ import type { BaseLLMAdapter } from "@pipedog/core/adapters/base_llm_adapter";
18
+ import { type CancelFrame, type EndFrame, type Frame, FunctionCallCancelFrame, FunctionCallResultFrame, InputAudioRawFrame, LLMServiceMetadataFrame } from "@pipedog/core/frames/frames";
19
+ import type { LLMContext, LLMStandardMessage } from "@pipedog/core/processors/aggregators/llm_context";
20
+ import { FrameDirection, type FrameProcessorSetup } from "@pipedog/core/processors/frame_processor";
21
+ import { LLMService, type LLMServiceOptions, type LLMWithBackendRole } from "@pipedog/core/services/llm_service";
22
+ import { LLMSettings, type LLMSettingsOptions } from "@pipedog/core/services/settings";
23
+ import { type WebsocketClientConnection, type WebsocketConnectOptions } from "@pipedog/core/services/websocket_service";
24
+ import { type NotGiven } from "@pipedog/core/utils/types";
25
+ import { _BackendSession, BackendError, BackendIdle, BackendLLMWorker, BackendOutput, BackendToolCall } from "@pipedog/core/workers/llm/backend_llm_worker";
26
+ import { OpenAILiveLLMAdapter, type OpenAILiveLLMInvocationParams } from "../open_ai_live_adapter.ts";
27
+ import { type OpenAIResponsesLLMSettings } from "../responses/llm.ts";
28
+ import * as events from "./events.ts";
29
+ export declare const DEFAULT_MODEL = "gpt-live-1";
30
+ export declare const TURN_GAP_SECS = 0.8;
31
+ export declare const SESSION_CLOSE_TIMEOUT_SECS = 10;
32
+ /** How long a delegation waits for the session with the backend to open. */
33
+ export declare const BACKEND_ATTACH_TIMEOUT_SECS = 5;
34
+ export declare const MAX_CONTEXT_APPEND_TOKENS = 450;
35
+ export interface OpenAILiveLLMSettingsOptions extends LLMSettingsOptions {
36
+ /**
37
+ * Output voice name (for example `marin` or `cedar`). `null` leaves the
38
+ * choice to the API. Cannot be changed once the session has started.
39
+ */
40
+ voice?: string | null | NotGiven;
41
+ /**
42
+ * Whether the API keeps the session, making it available for 30 days as a
43
+ * stereo recording from `GET /v1/live/sessions/{id}/content` and as a
44
+ * source a later session can fork from. `false` by default at the API, and
45
+ * requires a project whose data policy permits persistence. Cannot be
46
+ * changed once the session has started.
47
+ */
48
+ store?: boolean | null | NotGiven;
49
+ }
50
+ /**
51
+ * Settings for OpenAILiveLLMService.
52
+ *
53
+ * - `voice`: output voice name (for example `marin` or `cedar`). `null`
54
+ * leaves the choice to the API. Cannot be changed once the session has
55
+ * started.
56
+ * - `store`: whether the API keeps the session, making it available for 30
57
+ * days as a stereo recording from `GET /v1/live/sessions/{id}/content` and
58
+ * as a source a later session can fork from. `false` by default at the API,
59
+ * and requires a project whose data policy permits persistence. Cannot be
60
+ * changed once the session has started.
61
+ */
62
+ export declare class OpenAILiveLLMSettings extends LLMSettings {
63
+ voice: string | null | NotGiven;
64
+ store: boolean | null | NotGiven;
65
+ constructor(options?: OpenAILiveLLMSettingsOptions);
66
+ }
67
+ /**
68
+ * One speaker's in-progress turn, assembled from timed transcript fragments.
69
+ *
70
+ * - `gapSecs`: quiet time that ends the turn.
71
+ * - `text`: fragments accumulated so far.
72
+ * - `open`: whether a turn is currently open.
73
+ * - `timer`: the task that closes the turn once `gapSecs` elapses.
74
+ * - `lock`: held while the turn opens or closes. The timer runs in its own
75
+ * task, so this is what keeps one turn's end frames from landing among the
76
+ * next turn's start frames.
77
+ */
78
+ export declare class _TurnGrouper {
79
+ gapSecs: number;
80
+ text: string;
81
+ open: boolean;
82
+ timer: Task<unknown> | null;
83
+ lock: Lock;
84
+ constructor(options: {
85
+ gapSecs: number;
86
+ });
87
+ }
88
+ /**
89
+ * A client delegation in progress: what to correlate the backend's outputs with.
90
+ *
91
+ * - `id`: the delegation's id, which the appends carry.
92
+ * - `answered`: whether the backend has sent a spoken output for it yet; its
93
+ * notes do not count.
94
+ * - `timeoutTask`: the task that abandons it if the backend takes too long.
95
+ */
96
+ export declare class _LiveDelegation {
97
+ id: string;
98
+ answered: boolean;
99
+ timeoutTask: Task<unknown> | null;
100
+ constructor(options: {
101
+ id: string;
102
+ });
103
+ }
104
+ export interface ResponsesDelegationOptions {
105
+ /**
106
+ * Request settings for the backend model — the same object
107
+ * `OpenAIResponsesLLMService` takes. `model` is required.
108
+ */
109
+ settings: OpenAIResponsesLLMSettings;
110
+ /**
111
+ * Responses API service tier for delegated requests (`auto`, `default`,
112
+ * `flex` or `priority`).
113
+ */
114
+ serviceTier?: string | null;
115
+ }
116
+ /**
117
+ * Responses delegation: OpenAI hosts the backend model the live model delegates to.
118
+ *
119
+ * Hosted tools run server-side; function calls are executed by this service
120
+ * with the handlers registered for the tools in the pipeline's `LLMContext`
121
+ * (which is also where the backend model's `tools` and `tool_choice` come
122
+ * from).
123
+ *
124
+ * - `settings`: request settings for the backend model — the same object
125
+ * `OpenAIResponsesLLMService` takes. `model` is required.
126
+ * `systemInstruction` becomes the backend's `instructions`,
127
+ * `maxCompletionTokens` its `max_output_tokens`, `reasoning` is sent when
128
+ * configured (the server default applies otherwise) and `extra` is merged
129
+ * into the delegation configuration. Fields the Live API doesn't accept for
130
+ * a delegated model (`temperature`, `topP`, the penalties, `seed`, `topK`,
131
+ * `maxTokens`, and the user-turn-completion fields) are dropped with a
132
+ * warning.
133
+ * - `serviceTier`: Responses API service tier for delegated requests
134
+ * (`auto`, `default`, `flex` or `priority`).
135
+ */
136
+ export declare class ResponsesDelegation {
137
+ settings: OpenAIResponsesLLMSettings;
138
+ serviceTier: string | null;
139
+ constructor(options: ResponsesDelegationOptions);
140
+ }
141
+ export interface ClientDelegationOptions {
142
+ /**
143
+ * The worker that runs delegated work — normally a `BackendLLMWorker`
144
+ * wrapping any LLM service, or the name of one registered elsewhere.
145
+ */
146
+ backend: BackendLLMWorker | string;
147
+ /** How long a delegation may take before it is abandoned. Defaults to 120. */
148
+ timeoutSecs?: number;
149
+ }
150
+ /**
151
+ * Client delegation: a Pipecat worker is the backend the live model delegates to.
152
+ *
153
+ * A delegation names no task: the live model signals only that it is handing
154
+ * work over. Each one is put to the backend as a message carrying the
155
+ * transcript fragments since the previous delegation, and the backend works
156
+ * out the request from them. The service attaches to the backend for its
157
+ * life and takes delegations one at a time: the next waits until the backend
158
+ * reports it has nothing left to do, since they share one backend
159
+ * conversation.
160
+ *
161
+ * What comes back is appended to the live session according to each output's
162
+ * `prefersSpoken` flag: what it wants heard goes as commentary, for the model
163
+ * to relay in its own words, and the rest as thinking — silent context it can
164
+ * draw on if the conversation turns that way.
165
+ *
166
+ * - `backend`: the worker that runs delegated work — normally a
167
+ * `BackendLLMWorker` wrapping any LLM service, or the name of one
168
+ * registered elsewhere (in the app, or in another process on a shared
169
+ * bus). A worker given here is registered as a child of the pipeline worker
170
+ * at setup, so the pipeline must run under a `WorkerRunner`.
171
+ * - `timeoutSecs`: how long a delegation may take before it is abandoned.
172
+ */
173
+ export declare class ClientDelegation {
174
+ backend: BackendLLMWorker | string;
175
+ timeoutSecs: number;
176
+ constructor(options: ClientDelegationOptions);
177
+ }
178
+ /**
179
+ * A delegated Responses run whose function calls we are collecting.
180
+ *
181
+ * - `callIds`: calls still awaiting an output.
182
+ * - `hadCalls`: whether the response asked for any function call at all.
183
+ * - `finished`: whether the response emitted all of its output items.
184
+ */
185
+ export declare class _PendingResponse {
186
+ callIds: Set<string>;
187
+ hadCalls: boolean;
188
+ finished: boolean;
189
+ }
190
+ export interface OpenAILiveLLMServiceOptions extends LLMServiceOptions {
191
+ /** OpenAI project API key. */
192
+ apiKey: string;
193
+ /** WebSocket base URL of the Live API. */
194
+ baseUrl?: string;
195
+ /**
196
+ * Runtime-updatable settings. `model`, `voice`, `store` and
197
+ * `systemInstruction` are fixed once the session has started.
198
+ */
199
+ settings?: OpenAILiveLLMSettings | null;
200
+ /**
201
+ * Where the model's delegated work runs. `null` selects client delegation
202
+ * with no backend configured.
203
+ */
204
+ delegation?: ResponsesDelegation | ClientDelegation | null;
205
+ /**
206
+ * Creates the underlying `WebSocket` (see `WebsocketConnectOptions`), for
207
+ * runtimes whose `WebSocket` can't send the `Authorization` header.
208
+ */
209
+ webSocketFactory?: WebsocketConnectOptions["webSocketFactory"];
210
+ }
211
+ /**
212
+ * OpenAI Live LLM service: full-duplex speech-to-speech over WebSocket.
213
+ *
214
+ * The live model (`gpt-live-1`) listens and speaks at the same time. It
215
+ * decides on its own when to answer, when to stop talking when the user
216
+ * speaks over it, and when to *delegate* work — search, reasoning, tool use —
217
+ * to a backend text model while the conversation continues. In the
218
+ * frontend/backend terms used throughout, the live model is the *frontend*
219
+ * (the conversational model) and the delegated-to model is the *backend*.
220
+ * There is no client-side turn detection or response triggering: the
221
+ * pipeline streams audio in and plays audio out.
222
+ *
223
+ * Delegation modes, selected with `delegation`:
224
+ *
225
+ * - `ResponsesDelegation` — OpenAI hosts the backend (Responses API) model.
226
+ * Function calls it makes are executed here with the handlers registered
227
+ * for the pipeline context's tools; results go back to the API as soon as
228
+ * they are available.
229
+ * - `ClientDelegation` — a `BackendLLMWorker` running any Pipecat LLM service
230
+ * is the backend. The model does not share its conversation with the
231
+ * backend, so the service ships the transcript turns since the previous
232
+ * delegation along with each request.
233
+ * - `null` — client delegation mode with no backend configured; delegated
234
+ * requests are declined.
235
+ *
236
+ * Turn frames: the service proposes user turn boundaries from the API's
237
+ * projected transcript turns, and the recommended
238
+ * `new ExternalUserTurnStrategies({ enableInterruptions: false })` resolve
239
+ * them into `UserStartedSpeakingFrame` / `UserStoppedSpeakingFrame` **without
240
+ * broadcasting interruptions**: the model handles being talked over itself,
241
+ * and a delegation keeps running when that happens (the model is expected to
242
+ * disregard results the conversation has moved past). As a consequence every
243
+ * tool behaves as `cancelOnInterruption: false`; use `cancellableByLlm: true`
244
+ * for tools the model should be able to cancel on request.
245
+ *
246
+ * The context aggregators record both sides of the conversation from the
247
+ * transcript frames. Unlike the Realtime services, this one is not flagged as
248
+ * a realtime service for the aggregators: the service closes a user turn
249
+ * itself once the speaker falls quiet and pushes its final transcript then,
250
+ * so the user aggregator writes each user turn to the context as it ends —
251
+ * before any tool calls it triggers — rather than when the assistant starts
252
+ * to respond.
253
+ *
254
+ * The session starts on the first `LLMContextFrame` (typically queued as an
255
+ * `LLMRunFrame`): the context's leading system message (or
256
+ * `Settings.systemInstruction`) becomes the model's instructions and the
257
+ * remaining text messages seed the session as prior conversation.
258
+ *
259
+ * Event handlers available:
260
+ *
261
+ * - on_session_started: called with the session resource once the session is
262
+ * ready for audio.
263
+ * - on_delegation_created: called with the delegation item when the model
264
+ * delegates work.
265
+ *
266
+ * @example
267
+ * const llm = new OpenAILiveLLMService({
268
+ * apiKey: process.env.OPENAI_API_KEY!,
269
+ * settings: new OpenAILiveLLMService.Settings({ systemInstruction: "..." }),
270
+ * delegation: new OpenAILiveLLMService.ResponsesDelegation({
271
+ * settings: new OpenAIResponsesLLMService.Settings({ model: "gpt-5.4-mini" }),
272
+ * }),
273
+ * });
274
+ */
275
+ export declare class OpenAILiveLLMService extends LLMService<OpenAILiveLLMAdapter & BaseLLMAdapter> {
276
+ static Settings: typeof OpenAILiveLLMSettings;
277
+ static ResponsesDelegation: typeof ResponsesDelegation;
278
+ static ClientDelegation: typeof ClientDelegation;
279
+ protected _settings: OpenAILiveLLMSettings;
280
+ static adapterClass: typeof OpenAILiveLLMAdapter;
281
+ apiKey: string;
282
+ baseUrl: string;
283
+ protected _sessionModel: string;
284
+ protected _delegation: ResponsesDelegation | ClientDelegation | null;
285
+ protected _webSocketFactory: WebsocketConnectOptions["webSocketFactory"];
286
+ protected _websocket: WebsocketClientConnection | null;
287
+ protected _receiveTask: Task<unknown> | null;
288
+ protected _disconnecting: boolean;
289
+ protected _context: LLMContext | null;
290
+ protected _needsSessionConfig: boolean;
291
+ protected _sessionStarted: boolean;
292
+ protected _sessionClosedEvent: Event;
293
+ protected _sentToolsSnapshot: string | null;
294
+ protected _sessionStartedOnConnection: boolean;
295
+ protected _resampler: import("@pipedog/core/audio/resamplers/base_audio_resampler").BaseAudioResampler;
296
+ protected _warnedAudioDropped: boolean;
297
+ protected _userTurn: _TurnGrouper;
298
+ protected _assistantTurn: _TurnGrouper;
299
+ protected _openingInstruction: string | null;
300
+ protected _openFunctionCalls: Map<string, string>;
301
+ protected _pendingResponses: Map<string, _PendingResponse>;
302
+ protected _transcriptFragments: {
303
+ role: string;
304
+ content: string;
305
+ }[];
306
+ protected _delegatedBefore: boolean;
307
+ protected _backendSession: _BackendSession | null;
308
+ protected _backendSessionTask: Task<unknown> | null;
309
+ protected _backendAttached: Event;
310
+ protected _currentDelegation: _LiveDelegation | null;
311
+ protected _queuedDelegations: [events.DelegationMetadata, string][];
312
+ /**
313
+ * Initialize the OpenAI Live LLM service.
314
+ *
315
+ * @param options.apiKey OpenAI project API key.
316
+ * @param options.baseUrl WebSocket base URL of the Live API.
317
+ * @param options.settings Runtime-updatable settings. `model`, `voice`,
318
+ * `store` and `systemInstruction` are fixed once the session has started.
319
+ * @param options.delegation Where the model's delegated work runs. `null`
320
+ * selects client delegation with no backend configured.
321
+ * Other options are passed to the parent LLMService.
322
+ */
323
+ constructor(options: OpenAILiveLLMServiceOptions);
324
+ /**
325
+ * Check if the service can generate usage metrics.
326
+ *
327
+ * @returns True if metrics generation is supported.
328
+ */
329
+ canGenerateMetrics(): boolean;
330
+ /** The Live API takes one output per function call, so intermediate results are dropped. */
331
+ get acceptsIntermediateFunctionCallResults(): boolean;
332
+ /**
333
+ * Decline both roles: this service is a frontend with a backend of its own.
334
+ *
335
+ * @param role The role the service is being asked to take.
336
+ * @returns The reason, naming what to use instead.
337
+ */
338
+ llmWithBackendRoleObjection(role: LLMWithBackendRole): string | null;
339
+ /**
340
+ * Recommend external turn strategies, resolved from the API's projected turns.
341
+ *
342
+ * `isRealtimeService` is left off on purpose: the aggregators' realtime
343
+ * mode defers the user-message write until the assistant responds, to
344
+ * absorb late transcripts, and here that would place a delegation's tool
345
+ * calls ahead of the user message that caused them.
346
+ */
347
+ serviceMetadataFrame(): LLMServiceMetadataFrame;
348
+ /**
349
+ * Set up the service, register the client-delegation backend, and connect.
350
+ *
351
+ * @param setup Configuration object containing setup parameters.
352
+ */
353
+ setup(setup: FrameProcessorSetup): Promise<void>;
354
+ /** Release resources at teardown. */
355
+ cleanup(): Promise<void>;
356
+ /**
357
+ * Close the session gracefully and disconnect.
358
+ *
359
+ * @param frame The end frame triggering service shutdown.
360
+ */
361
+ stop(frame: EndFrame): Promise<void>;
362
+ /**
363
+ * Disconnect immediately.
364
+ *
365
+ * @param frame The cancel frame triggering service cancellation.
366
+ */
367
+ cancel(frame: CancelFrame): Promise<void>;
368
+ /**
369
+ * Apply a settings delta.
370
+ *
371
+ * The API fixes the session's settings when it starts, so a change is
372
+ * stored and reaches the API on the next session (see `resetConversation`).
373
+ */
374
+ protected _updateSettings(delta: LLMSettings): Promise<Record<string, unknown>>;
375
+ /**
376
+ * Start a new session seeded from the current context.
377
+ *
378
+ * The Live API only takes conversation history at session start, so
379
+ * replacing the context (for example to restore a saved conversation) means
380
+ * dropping the session and opening a new one configured from the context as
381
+ * it is now. The old session is abandoned rather than closed gracefully: a
382
+ * graceful close waits for its in-flight delegations to drain, and their
383
+ * results belong to the conversation being replaced. Must not be called
384
+ * from the receive task.
385
+ */
386
+ resetConversation(): Promise<void>;
387
+ /**
388
+ * Process incoming frames from the pipeline.
389
+ *
390
+ * @param frame The frame to process.
391
+ * @param direction The direction of frame flow in the pipeline.
392
+ */
393
+ processFrame(frame: Frame, direction: FrameDirection): Promise<void>;
394
+ /**
395
+ * Push a frame, delivering function call outcomes to the API on the way.
396
+ *
397
+ * Function call results and cancellations are broadcast by the base
398
+ * service; the downstream copy is observed here and answered to the backend
399
+ * model immediately. The frames continue to the assistant aggregator, which
400
+ * records them in the context.
401
+ *
402
+ * @param frame The frame to push.
403
+ * @param direction The direction of frame pushing.
404
+ */
405
+ pushFrame(frame: Frame, direction?: FrameDirection): Promise<void>;
406
+ /**
407
+ * Take the context, configuring the session from the first one.
408
+ *
409
+ * A later context carries messages the aggregators recorded from this
410
+ * session's own transcripts, tool results (delivered to the API as they are
411
+ * produced, see `pushFrame`), or messages the app appended. None of it
412
+ * reaches the API: user and assistant messages can't be told apart from the
413
+ * transcript recordings. Newly appended system/developer messages can only
414
+ * come from the app (the aggregators add user, assistant and tool messages,
415
+ * and async-tool envelopes that `async_tool_messages.parseMessage`
416
+ * identifies), so a future diff could forward those as
417
+ * `session.context.append(channel="developer")` unambiguously.
418
+ */
419
+ protected _handleContext(context: LLMContext): Promise<void>;
420
+ protected _invocationParams(): Promise<OpenAILiveLLMInvocationParams>;
421
+ /** Send `session.start`, the first message on the socket. */
422
+ protected _sendSessionConfig(): Promise<void>;
423
+ protected _delegationConfig(tools: Record<string, unknown>[], toolChoice: unknown): events.ClientDelegationConfig | events.ResponsesDelegationConfig;
424
+ /** Send a sparse `session.update` when the backend model's tools changed. */
425
+ protected _maybeSendToolsUpdate(): Promise<void>;
426
+ protected static _toolsSnapshot(params: OpenAILiveLLMInvocationParams): string;
427
+ /**
428
+ * Send a client event to the Live API.
429
+ *
430
+ * @param event The client event to send.
431
+ */
432
+ sendClientEvent(event: events.ClientEvent): Promise<void>;
433
+ protected _connect(): Promise<void>;
434
+ protected _disconnect(): Promise<void>;
435
+ /** Ask the server to shut down gracefully and wait for `session.closed`. */
436
+ protected _closeSession(): Promise<void>;
437
+ protected _wsSend(message: Record<string, unknown>): Promise<void>;
438
+ protected _receiveTaskHandler(): Promise<void>;
439
+ protected _handleServerEvent(evt: events.ServerEvent): Promise<void>;
440
+ protected _handleEvtSessionStarted(evt: events.SessionStartedEvent): Promise<void>;
441
+ protected _handleEvtSessionClosed(evt: events.SessionClosedEvent): Promise<void>;
442
+ protected _handleEvtError(evt: events.ErrorEvent): Promise<void>;
443
+ protected _sendUserAudio(frame: InputAudioRawFrame): Promise<void>;
444
+ protected _handleEvtAudioDelta(evt: events.OutputAudioDeltaEvent): Promise<void>;
445
+ protected _handleEvtTranscriptDelta(evt: events.TranscriptDeltaEvent): Promise<void>;
446
+ protected _restartTurnTimer(role: string, turn: _TurnGrouper): Promise<void>;
447
+ protected _closeTurnAfterGap(role: string, turn: _TurnGrouper): Promise<void>;
448
+ /** Close an open turn, if any, emitting its end frames. */
449
+ protected _closeTurn(role: string): Promise<void>;
450
+ /** Emit an open turn's end frames, with the turn's lock held. */
451
+ protected _endTurn(role: string): Promise<void>;
452
+ /** Close both directions, so response frames stay balanced across sessions. */
453
+ protected _closeOpenTurns(): Promise<void>;
454
+ protected _openTurn(role: string): Promise<void>;
455
+ protected _appendTurn(role: string, delta: string, accumulated: string, evt: events.ServerEvent): Promise<void>;
456
+ /**
457
+ * Keep a transcript fragment for the next client delegation.
458
+ *
459
+ * Fragments land on frame boundaries, so consecutive ones from the same
460
+ * speaker are joined back up as they arrive, giving the backend whole
461
+ * utterances to read. Deltas carry their own leading spaces, so they
462
+ * concatenate into the original text unaltered.
463
+ */
464
+ protected _rememberFragment(role: "user" | "assistant", delta: string): void;
465
+ /** Take the conversation recorded since the previous delegation. */
466
+ protected _takeTranscript(): LLMStandardMessage[];
467
+ protected _pushAssistantText(text: string): Promise<void>;
468
+ protected _pushInterimTranscription(transcript: string, evt: events.ServerEvent): Promise<void>;
469
+ protected _handleEvtDelegationCreated(evt: events.SessionDelegationCreatedEvent): Promise<void>;
470
+ protected _handleClientDelegation(delegation: events.DelegationMetadata): Promise<void>;
471
+ /** Hold the session with the backend, appending what it produces to the live session. */
472
+ protected _runBackendSession(): Promise<void>;
473
+ protected _onBackendEvent(event: BackendOutput | BackendToolCall | BackendIdle | BackendError): Promise<void>;
474
+ protected _startDelegation(delegation: events.DelegationMetadata, request: string): Promise<void>;
475
+ /** Close the delegation in progress and start the next one waiting, if any. */
476
+ protected _finishDelegation(): Promise<void>;
477
+ protected _delegationTimeout(current: _LiveDelegation, timeoutSecs: number): Promise<void>;
478
+ /**
479
+ * Drop the delegation in progress and the ones waiting: the session they belong to is over.
480
+ *
481
+ * The backend is detached, which stops its work; the next connection
482
+ * attaches again.
483
+ */
484
+ protected _abandonDelegations(): Promise<void>;
485
+ /** Open the session with the backend, when client delegation is configured and none is open. */
486
+ protected _attachBackend(): Promise<void>;
487
+ /** Close the session with the backend, which stops its work. */
488
+ protected _detachBackend(): Promise<void>;
489
+ /**
490
+ * Append text to the live session, for it to speak or to keep to itself.
491
+ *
492
+ * The API's two channels are named for what the live model does with the
493
+ * text: commentary is paraphrased aloud, thinking joins its private
494
+ * reasoning. That vantage is the model's own, which is why what arrives
495
+ * from a backend is flagged `prefersSpoken` instead — ordinary prose can go
496
+ * unspoken without being anything like a thought.
497
+ */
498
+ protected _sendContextAppend(delegationId: string | null, text: string, options: {
499
+ spoken: boolean;
500
+ }): Promise<void>;
501
+ /** Dispatch a wrapped Responses lifecycle event by its own type. */
502
+ protected _handleEvtResponse(evt: events.ResponseEventEnvelope): Promise<void>;
503
+ /**
504
+ * Remember a delegated response so its function calls can be collected.
505
+ *
506
+ * @returns The response's correlation key.
507
+ */
508
+ protected _trackResponse(evt: events.ResponseEventEnvelope): string;
509
+ protected _handleResponseOutputItemDone(evt: events.ResponseEventEnvelope): Promise<void>;
510
+ /**
511
+ * Note that a response emitted all of its output items.
512
+ *
513
+ * The lifecycle snapshot's `output` is empty even here, so the calls
514
+ * collected from the individual item events are what has to be answered.
515
+ */
516
+ protected _handleResponseFinished(evt: events.ResponseEventEnvelope): Promise<void>;
517
+ /** Continue a response once every function call it made has been answered. */
518
+ protected _maybeContinueResponse(key: string): Promise<void>;
519
+ protected _handleFunctionCallResult(frame: FunctionCallResultFrame): Promise<void>;
520
+ protected _handleFunctionCallCancel(frame: FunctionCallCancelFrame): Promise<void>;
521
+ /** Queue one function result, and continue the response once all are in. */
522
+ protected _sendFunctionCallOutput(callId: string, output: string): Promise<void>;
523
+ /**
524
+ * Log the session's cumulative live audio duration.
525
+ *
526
+ * The live model's usage is reported as seconds rather than tokens. Token
527
+ * metrics come from the backend model instead, on the nested Responses
528
+ * completion events.
529
+ */
530
+ protected _reportUsage(usage: events.Usage): Promise<void>;
531
+ /** Report the backend model's token usage from a completed delegated response. */
532
+ protected _reportBackendUsage(response: Record<string, unknown>): Promise<void>;
533
+ }
534
+ /** Return the text of a trailing developer message, if the history ends with one. */
535
+ export declare function _trailingDeveloperText(items: readonly events.InputItem[]): string | null;
536
+ /**
537
+ * Build `delegation.responses` from the nested Responses settings.
538
+ *
539
+ * Mirrors how `OpenAIResponsesLLMService` builds a request from the same
540
+ * settings, minus what the Live API owns for a delegated model.
541
+ */
542
+ export declare function _responsesDelegationConfig(delegation: ResponsesDelegation): Record<string, unknown>;
543
+ /**
544
+ * Estimate how many tokens a piece of text costs.
545
+ *
546
+ * A byte-pair encoding packs about four characters of ASCII into a token.
547
+ * Other scripts cost more per character — CJK runs close to one token each —
548
+ * so anything outside ASCII is counted whole.
549
+ */
550
+ export declare function _estimatedTokens(text: string): number;
551
+ /** Return a text's cost in quarter-tokens: one for an ASCII character, four for any other. */
552
+ export declare function _tokenQuarters(text: string): number;
553
+ /**
554
+ * Split off the longest prefix of `text` that fits `tokenLimit`.
555
+ *
556
+ * The cut lands on the last space within the limit, so words stay whole.
557
+ */
558
+ export declare function _splitAtTokenLimit(text: string, tokenLimit: number): [string, string];
559
+ /**
560
+ * Split text into chunks of at most `tokenLimit` estimated tokens.
561
+ *
562
+ * Chunks break on sentence boundaries; a sentence longer than the limit is
563
+ * split at a space.
564
+ */
565
+ export declare function _chunkText(text: string, tokenLimit: number): string[];
566
+ //# sourceMappingURL=llm.d.ts.map