@livekit/agents-plugin-openai 1.0.50 → 1.0.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +5 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/llm.test.cjs +31 -16
- package/dist/llm.test.cjs.map +1 -1
- package/dist/llm.test.js +32 -17
- package/dist/llm.test.js.map +1 -1
- package/dist/responses/llm.cjs +71 -16
- package/dist/responses/llm.cjs.map +1 -1
- package/dist/responses/llm.d.cts +10 -25
- package/dist/responses/llm.d.ts +10 -25
- package/dist/responses/llm.d.ts.map +1 -1
- package/dist/responses/llm.js +71 -14
- package/dist/responses/llm.js.map +1 -1
- package/dist/responses/llm.test.cjs +32 -17
- package/dist/responses/llm.test.cjs.map +1 -1
- package/dist/responses/llm.test.js +33 -18
- package/dist/responses/llm.test.js.map +1 -1
- package/dist/stt.cjs +7 -3
- package/dist/stt.cjs.map +1 -1
- package/dist/stt.d.ts.map +1 -1
- package/dist/stt.js +8 -4
- package/dist/stt.js.map +1 -1
- package/dist/stt.test.cjs +11 -3
- package/dist/stt.test.cjs.map +1 -1
- package/dist/stt.test.js +12 -4
- package/dist/stt.test.js.map +1 -1
- package/dist/tts.test.cjs +11 -3
- package/dist/tts.test.cjs.map +1 -1
- package/dist/tts.test.js +12 -4
- package/dist/tts.test.js.map +1 -1
- package/dist/ws/index.cjs +29 -0
- package/dist/ws/index.cjs.map +1 -0
- package/dist/ws/index.d.cts +3 -0
- package/dist/ws/index.d.ts +3 -0
- package/dist/ws/index.d.ts.map +1 -0
- package/dist/ws/index.js +5 -0
- package/dist/ws/index.js.map +1 -0
- package/dist/ws/llm.cjs +502 -0
- package/dist/ws/llm.cjs.map +1 -0
- package/dist/ws/llm.d.cts +74 -0
- package/dist/ws/llm.d.ts +74 -0
- package/dist/ws/llm.d.ts.map +1 -0
- package/dist/ws/llm.js +485 -0
- package/dist/ws/llm.js.map +1 -0
- package/dist/ws/llm.test.cjs +26 -0
- package/dist/ws/llm.test.cjs.map +1 -0
- package/dist/ws/llm.test.d.cts +2 -0
- package/dist/ws/llm.test.d.ts +2 -0
- package/dist/ws/llm.test.d.ts.map +1 -0
- package/dist/ws/llm.test.js +25 -0
- package/dist/ws/llm.test.js.map +1 -0
- package/dist/ws/types.cjs +128 -0
- package/dist/ws/types.cjs.map +1 -0
- package/dist/ws/types.d.cts +167 -0
- package/dist/ws/types.d.ts +167 -0
- package/dist/ws/types.d.ts.map +1 -0
- package/dist/ws/types.js +95 -0
- package/dist/ws/types.js.map +1 -0
- package/package.json +6 -5
- package/src/index.ts +1 -0
- package/src/llm.test.ts +31 -17
- package/src/responses/llm.test.ts +32 -18
- package/src/responses/llm.ts +105 -19
- package/src/stt.test.ts +12 -4
- package/src/stt.ts +8 -4
- package/src/tts.test.ts +12 -4
- package/src/ws/index.ts +17 -0
- package/src/ws/llm.test.ts +30 -0
- package/src/ws/llm.ts +665 -0
- package/src/ws/types.ts +131 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var ws_exports = {};
|
|
20
|
+
__export(ws_exports, {
|
|
21
|
+
ResponsesWebSocket: () => import_llm.ResponsesWebSocket
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(ws_exports);
|
|
24
|
+
var import_llm = require("./llm.cjs");
|
|
25
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
26
|
+
0 && (module.exports = {
|
|
27
|
+
ResponsesWebSocket
|
|
28
|
+
});
|
|
29
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/ws/index.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n\nexport { ResponsesWebSocket } from './llm.js';\nexport type {\n WsErrorEvent,\n WsFunctionCallItem,\n WsOutputItem,\n WsOutputItemDoneEvent,\n WsOutputTextDeltaEvent,\n WsResponseCompletedEvent,\n WsResponseCreateEvent,\n WsResponseCreatedEvent,\n WsResponseFailedEvent,\n WsServerEvent,\n} from './types.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,iBAAmC;","names":[]}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { ResponsesWebSocket } from './llm.js';
|
|
2
|
+
export type { WsErrorEvent, WsFunctionCallItem, WsOutputItem, WsOutputItemDoneEvent, WsOutputTextDeltaEvent, WsResponseCompletedEvent, WsResponseCreateEvent, WsResponseCreatedEvent, WsResponseFailedEvent, WsServerEvent, } from './types.js';
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { ResponsesWebSocket } from './llm.js';
|
|
2
|
+
export type { WsErrorEvent, WsFunctionCallItem, WsOutputItem, WsOutputItemDoneEvent, WsOutputTextDeltaEvent, WsResponseCompletedEvent, WsResponseCreateEvent, WsResponseCreatedEvent, WsResponseFailedEvent, WsServerEvent, } from './types.js';
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ws/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,YAAY,EACZ,qBAAqB,EACrB,sBAAsB,EACtB,wBAAwB,EACxB,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,aAAa,GACd,MAAM,YAAY,CAAC"}
|
package/dist/ws/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/ws/index.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n\nexport { ResponsesWebSocket } from './llm.js';\nexport type {\n WsErrorEvent,\n WsFunctionCallItem,\n WsOutputItem,\n WsOutputItemDoneEvent,\n WsOutputTextDeltaEvent,\n WsResponseCompletedEvent,\n WsResponseCreateEvent,\n WsResponseCreatedEvent,\n WsResponseFailedEvent,\n WsServerEvent,\n} from './types.js';\n"],"mappings":"AAIA,SAAS,0BAA0B;","names":[]}
|
package/dist/ws/llm.cjs
ADDED
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var llm_exports = {};
|
|
20
|
+
__export(llm_exports, {
|
|
21
|
+
ResponsesWebSocket: () => ResponsesWebSocket,
|
|
22
|
+
WSLLM: () => WSLLM,
|
|
23
|
+
WSLLMStream: () => WSLLMStream
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(llm_exports);
|
|
26
|
+
var import_agents = require("@livekit/agents");
|
|
27
|
+
var import_ws = require("ws");
|
|
28
|
+
var import_types = require("./types.cjs");
|
|
29
|
+
const OPENAI_RESPONSES_WS_URL = "wss://api.openai.com/v1/responses";
|
|
30
|
+
const WS_MAX_SESSION_DURATION = 36e5;
|
|
31
|
+
class ResponsesWebSocket {
|
|
32
|
+
#ws;
|
|
33
|
+
// FIFO queue: the front entry receives validated WsServerEvents for the in-flight response.
|
|
34
|
+
#outputQueue = [];
|
|
35
|
+
constructor(ws) {
|
|
36
|
+
this.#ws = ws;
|
|
37
|
+
ws.on("message", (data) => {
|
|
38
|
+
const current = this.#outputQueue[0];
|
|
39
|
+
if (!current) return;
|
|
40
|
+
let raw;
|
|
41
|
+
try {
|
|
42
|
+
raw = JSON.parse(data.toString());
|
|
43
|
+
} catch {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const parsed = import_types.wsServerEventSchema.safeParse(raw);
|
|
47
|
+
if (!parsed.success) return;
|
|
48
|
+
const event = parsed.data;
|
|
49
|
+
void current.write(event);
|
|
50
|
+
if (event.type === "response.completed" || event.type === "response.failed" || event.type === "error") {
|
|
51
|
+
void current.close();
|
|
52
|
+
this.#outputQueue.shift();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
ws.on("close", () => {
|
|
56
|
+
for (const current of this.#outputQueue) {
|
|
57
|
+
if (!current.closed) {
|
|
58
|
+
const closeError = {
|
|
59
|
+
type: "error",
|
|
60
|
+
error: {
|
|
61
|
+
code: "websocket_closed",
|
|
62
|
+
message: "OpenAI Responses WebSocket closed unexpectedly"
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
void current.write(closeError).finally(() => current.close());
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
this.#outputQueue = [];
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Send a response.create event. Returns a typed `StreamChannel<WsServerEvent>`
|
|
73
|
+
* that yields validated server events until the response terminates.
|
|
74
|
+
*/
|
|
75
|
+
sendRequest(payload) {
|
|
76
|
+
if (this.#ws.readyState !== import_ws.WebSocket.OPEN) {
|
|
77
|
+
throw new import_agents.APIConnectionError({
|
|
78
|
+
message: `OpenAI Responses WebSocket is not open (state ${getWebSocketStateLabel(this.#ws.readyState)})`,
|
|
79
|
+
options: { retryable: true }
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const channel = import_agents.stream.createStreamChannel();
|
|
83
|
+
this.#outputQueue.push(channel);
|
|
84
|
+
this.#ws.send(JSON.stringify(payload));
|
|
85
|
+
return channel;
|
|
86
|
+
}
|
|
87
|
+
close() {
|
|
88
|
+
for (const ch of this.#outputQueue) {
|
|
89
|
+
void ch.close();
|
|
90
|
+
}
|
|
91
|
+
this.#outputQueue = [];
|
|
92
|
+
this.#ws.close();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const defaultLLMOptions = {
|
|
96
|
+
model: "gpt-4.1",
|
|
97
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
98
|
+
strictToolSchema: true
|
|
99
|
+
};
|
|
100
|
+
class WSLLM extends import_agents.llm.LLM {
|
|
101
|
+
#opts;
|
|
102
|
+
#pool;
|
|
103
|
+
#prevResponseId = "";
|
|
104
|
+
#prevChatCtx = null;
|
|
105
|
+
#pendingToolCalls = /* @__PURE__ */ new Set();
|
|
106
|
+
/**
|
|
107
|
+
* Create a new instance of the OpenAI Responses API WebSocket LLM.
|
|
108
|
+
*
|
|
109
|
+
* @remarks
|
|
110
|
+
* `apiKey` must be set to your OpenAI API key, either using the argument or
|
|
111
|
+
* by setting the `OPENAI_API_KEY` environment variable.
|
|
112
|
+
*
|
|
113
|
+
* A persistent WebSocket connection to `/v1/responses` is maintained and
|
|
114
|
+
* reused across turns, reducing per-turn continuation overhead for
|
|
115
|
+
* tool-call-heavy workflows.
|
|
116
|
+
*/
|
|
117
|
+
constructor(opts = defaultLLMOptions) {
|
|
118
|
+
super();
|
|
119
|
+
this.#opts = { ...defaultLLMOptions, ...opts };
|
|
120
|
+
if (!this.#opts.apiKey) {
|
|
121
|
+
throw new Error("OpenAI API key is required, whether as an argument or as $OPENAI_API_KEY");
|
|
122
|
+
}
|
|
123
|
+
this.#pool = new import_agents.ConnectionPool({
|
|
124
|
+
maxSessionDuration: WS_MAX_SESSION_DURATION,
|
|
125
|
+
connectCb: async (timeoutMs) => {
|
|
126
|
+
const wsUrl = this.#opts.baseURL ? `${this.#opts.baseURL.replace(/^https?/, "wss").replace(/\/+$/, "")}/responses` : OPENAI_RESPONSES_WS_URL;
|
|
127
|
+
const ws = await connectWs(wsUrl, this.#opts.apiKey, timeoutMs);
|
|
128
|
+
return new ResponsesWebSocket(ws);
|
|
129
|
+
},
|
|
130
|
+
closeCb: async (conn) => {
|
|
131
|
+
conn.close();
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
label() {
|
|
136
|
+
return "openai.ws.LLM";
|
|
137
|
+
}
|
|
138
|
+
get model() {
|
|
139
|
+
return this.#opts.model;
|
|
140
|
+
}
|
|
141
|
+
prewarm() {
|
|
142
|
+
this.#pool.prewarm();
|
|
143
|
+
}
|
|
144
|
+
async close() {
|
|
145
|
+
await this.#pool.close();
|
|
146
|
+
}
|
|
147
|
+
async aclose() {
|
|
148
|
+
await this.close();
|
|
149
|
+
}
|
|
150
|
+
/** Called by LLMStream once response.created fires to atomically persist both the
|
|
151
|
+
* response ID and its corresponding chat context for the next turn's diff. */
|
|
152
|
+
_onResponseCreated(responseId, chatCtx) {
|
|
153
|
+
this.#prevResponseId = responseId;
|
|
154
|
+
this.#prevChatCtx = chatCtx;
|
|
155
|
+
}
|
|
156
|
+
_setPendingToolCalls(callIds) {
|
|
157
|
+
this.#pendingToolCalls = callIds;
|
|
158
|
+
}
|
|
159
|
+
chat({
|
|
160
|
+
chatCtx,
|
|
161
|
+
toolCtx,
|
|
162
|
+
connOptions = import_agents.DEFAULT_API_CONNECT_OPTIONS,
|
|
163
|
+
parallelToolCalls,
|
|
164
|
+
toolChoice,
|
|
165
|
+
extraKwargs
|
|
166
|
+
}) {
|
|
167
|
+
var _a;
|
|
168
|
+
const modelOptions = { ...extraKwargs ?? {} };
|
|
169
|
+
parallelToolCalls = parallelToolCalls !== void 0 ? parallelToolCalls : this.#opts.parallelToolCalls;
|
|
170
|
+
if (toolCtx && Object.keys(toolCtx).length > 0 && parallelToolCalls !== void 0) {
|
|
171
|
+
modelOptions.parallel_tool_calls = parallelToolCalls;
|
|
172
|
+
}
|
|
173
|
+
toolChoice = toolChoice !== void 0 ? toolChoice : this.#opts.toolChoice;
|
|
174
|
+
if (toolChoice) {
|
|
175
|
+
modelOptions.tool_choice = toolChoice;
|
|
176
|
+
}
|
|
177
|
+
if (this.#opts.temperature !== void 0) {
|
|
178
|
+
modelOptions.temperature = this.#opts.temperature;
|
|
179
|
+
}
|
|
180
|
+
if (this.#opts.store !== void 0) {
|
|
181
|
+
modelOptions.store = this.#opts.store;
|
|
182
|
+
}
|
|
183
|
+
if (this.#opts.metadata) {
|
|
184
|
+
modelOptions.metadata = this.#opts.metadata;
|
|
185
|
+
}
|
|
186
|
+
let inputChatCtx = chatCtx;
|
|
187
|
+
let prevResponseId;
|
|
188
|
+
const canUseStoredResponse = modelOptions.store !== false;
|
|
189
|
+
if (canUseStoredResponse && this.#prevChatCtx && this.#prevResponseId) {
|
|
190
|
+
const diff = import_agents.llm.computeChatCtxDiff(this.#prevChatCtx, chatCtx);
|
|
191
|
+
const lastPrevItemId = ((_a = this.#prevChatCtx.items.at(-1)) == null ? void 0 : _a.id) ?? null;
|
|
192
|
+
if (diff.toRemove.length === 0 && diff.toCreate.length > 0 && diff.toCreate[0][0] === lastPrevItemId) {
|
|
193
|
+
const newItemIds = new Set(diff.toCreate.map(([, id]) => id));
|
|
194
|
+
const newItems = chatCtx.items.filter((item) => newItemIds.has(item.id));
|
|
195
|
+
const pendingToolCallsCompleted = this.#pendingToolCallsCompleted(newItems);
|
|
196
|
+
if (pendingToolCallsCompleted) {
|
|
197
|
+
inputChatCtx = new import_agents.llm.ChatContext(newItems);
|
|
198
|
+
prevResponseId = this.#prevResponseId;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return new WSLLMStream(this, {
|
|
203
|
+
pool: this.#pool,
|
|
204
|
+
model: this.#opts.model,
|
|
205
|
+
chatCtx: inputChatCtx,
|
|
206
|
+
fullChatCtx: chatCtx,
|
|
207
|
+
toolCtx,
|
|
208
|
+
connOptions,
|
|
209
|
+
modelOptions,
|
|
210
|
+
prevResponseId,
|
|
211
|
+
strictToolSchema: this.#opts.strictToolSchema ?? true
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
#pendingToolCallsCompleted(items) {
|
|
215
|
+
if (this.#pendingToolCalls.size === 0) return true;
|
|
216
|
+
const completedCallIds = new Set(
|
|
217
|
+
items.filter((item) => item.type === "function_call_output").map((item) => item.callId)
|
|
218
|
+
);
|
|
219
|
+
return [...this.#pendingToolCalls].every((callId) => completedCallIds.has(callId));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
class WSLLMStream extends import_agents.llm.LLMStream {
|
|
223
|
+
#llm;
|
|
224
|
+
#pool;
|
|
225
|
+
#model;
|
|
226
|
+
#modelOptions;
|
|
227
|
+
#strictToolSchema;
|
|
228
|
+
#prevResponseId;
|
|
229
|
+
/** Full chat context — used as fallback when previous_response_id is stale. */
|
|
230
|
+
#fullChatCtx;
|
|
231
|
+
#responseId = "";
|
|
232
|
+
#pendingToolCalls = /* @__PURE__ */ new Set();
|
|
233
|
+
constructor(llm2, {
|
|
234
|
+
pool,
|
|
235
|
+
model,
|
|
236
|
+
chatCtx,
|
|
237
|
+
fullChatCtx,
|
|
238
|
+
toolCtx,
|
|
239
|
+
connOptions,
|
|
240
|
+
modelOptions,
|
|
241
|
+
prevResponseId,
|
|
242
|
+
strictToolSchema
|
|
243
|
+
}) {
|
|
244
|
+
super(llm2, { chatCtx, toolCtx, connOptions });
|
|
245
|
+
this.#llm = llm2;
|
|
246
|
+
this.#pool = pool;
|
|
247
|
+
this.#model = model;
|
|
248
|
+
this.#modelOptions = modelOptions;
|
|
249
|
+
this.#strictToolSchema = strictToolSchema;
|
|
250
|
+
this.#prevResponseId = prevResponseId;
|
|
251
|
+
this.#fullChatCtx = fullChatCtx;
|
|
252
|
+
}
|
|
253
|
+
async run() {
|
|
254
|
+
let retryable = true;
|
|
255
|
+
try {
|
|
256
|
+
await this.#pool.withConnection(async (conn) => {
|
|
257
|
+
const needsRetry = await this.#runWithConn(conn, this.chatCtx, this.#prevResponseId);
|
|
258
|
+
if (needsRetry) {
|
|
259
|
+
retryable = true;
|
|
260
|
+
await this.#runWithConn(conn, this.#fullChatCtx, void 0);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
} catch (error) {
|
|
264
|
+
if (error instanceof import_agents.APIStatusError || error instanceof import_agents.APITimeoutError || error instanceof import_agents.APIConnectionError) {
|
|
265
|
+
throw error;
|
|
266
|
+
}
|
|
267
|
+
throw new import_agents.APIConnectionError({
|
|
268
|
+
message: (0, import_agents.toError)(error).message,
|
|
269
|
+
options: { retryable }
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Execute a single response.create round-trip on the given connection.
|
|
275
|
+
* Returns `true` when the caller should retry with the full chat context
|
|
276
|
+
* (i.e. `previous_response_not_found`), `false` otherwise.
|
|
277
|
+
*/
|
|
278
|
+
async #runWithConn(conn, chatCtx, prevResponseId) {
|
|
279
|
+
const messages = await chatCtx.toProviderFormat(
|
|
280
|
+
"openai.responses"
|
|
281
|
+
);
|
|
282
|
+
const tools = this.toolCtx ? Object.entries(this.toolCtx).map(([name, func]) => {
|
|
283
|
+
const oaiParams = {
|
|
284
|
+
type: "function",
|
|
285
|
+
name,
|
|
286
|
+
description: func.description,
|
|
287
|
+
parameters: import_agents.llm.toJsonSchema(
|
|
288
|
+
func.parameters,
|
|
289
|
+
true,
|
|
290
|
+
this.#strictToolSchema
|
|
291
|
+
)
|
|
292
|
+
};
|
|
293
|
+
if (this.#strictToolSchema) {
|
|
294
|
+
oaiParams.strict = true;
|
|
295
|
+
}
|
|
296
|
+
return oaiParams;
|
|
297
|
+
}) : void 0;
|
|
298
|
+
const requestOptions = { ...this.#modelOptions };
|
|
299
|
+
if (!tools) {
|
|
300
|
+
delete requestOptions.tool_choice;
|
|
301
|
+
}
|
|
302
|
+
const payload = {
|
|
303
|
+
type: "response.create",
|
|
304
|
+
model: this.#model,
|
|
305
|
+
input: messages,
|
|
306
|
+
tools: tools ?? [],
|
|
307
|
+
...prevResponseId ? { previous_response_id: prevResponseId } : {},
|
|
308
|
+
...requestOptions
|
|
309
|
+
};
|
|
310
|
+
let channel;
|
|
311
|
+
try {
|
|
312
|
+
channel = conn.sendRequest(payload);
|
|
313
|
+
} catch (error) {
|
|
314
|
+
if (error instanceof import_agents.APIConnectionError) {
|
|
315
|
+
conn.close();
|
|
316
|
+
this.#pool.invalidate();
|
|
317
|
+
}
|
|
318
|
+
throw error;
|
|
319
|
+
}
|
|
320
|
+
const reader = channel.stream().getReader();
|
|
321
|
+
try {
|
|
322
|
+
while (true) {
|
|
323
|
+
const { done, value: event } = await reader.read();
|
|
324
|
+
if (done) break;
|
|
325
|
+
let chunk;
|
|
326
|
+
switch (event.type) {
|
|
327
|
+
case "error": {
|
|
328
|
+
const retry = this.#handleError(event, conn);
|
|
329
|
+
if (retry) return true;
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
case "response.created":
|
|
333
|
+
this.#handleResponseCreated(event);
|
|
334
|
+
break;
|
|
335
|
+
case "response.output_item.done":
|
|
336
|
+
chunk = this.#handleOutputItemDone(event);
|
|
337
|
+
break;
|
|
338
|
+
case "response.output_text.delta":
|
|
339
|
+
chunk = this.#handleOutputTextDelta(event);
|
|
340
|
+
break;
|
|
341
|
+
case "response.completed":
|
|
342
|
+
chunk = this.#handleResponseCompleted(event);
|
|
343
|
+
break;
|
|
344
|
+
case "response.failed":
|
|
345
|
+
this.#handleResponseFailed(event);
|
|
346
|
+
break;
|
|
347
|
+
default:
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
if (chunk) {
|
|
351
|
+
this.queue.put(chunk);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
} finally {
|
|
355
|
+
reader.releaseLock();
|
|
356
|
+
}
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Returns `true` when the caller should retry with full context
|
|
361
|
+
* (`previous_response_not_found`), throws for all other errors.
|
|
362
|
+
*/
|
|
363
|
+
#handleError(event, conn) {
|
|
364
|
+
var _a, _b, _c;
|
|
365
|
+
const code = (_a = event.error) == null ? void 0 : _a.code;
|
|
366
|
+
if (code === "previous_response_not_found") {
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
if (code === "websocket_connection_limit_reached" || code === "websocket_closed") {
|
|
370
|
+
conn.close();
|
|
371
|
+
this.#pool.invalidate();
|
|
372
|
+
throw new import_agents.APIConnectionError({
|
|
373
|
+
message: ((_b = event.error) == null ? void 0 : _b.message) ?? `WebSocket closed (${code})`,
|
|
374
|
+
options: { retryable: true }
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
throw new import_agents.APIStatusError({
|
|
378
|
+
message: ((_c = event.error) == null ? void 0 : _c.message) ?? event.message ?? "Unknown error from OpenAI Responses WS",
|
|
379
|
+
options: {
|
|
380
|
+
statusCode: event.status ?? -1,
|
|
381
|
+
retryable: false
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
#handleResponseCreated(event) {
|
|
386
|
+
this.#responseId = event.response.id;
|
|
387
|
+
this.#llm._onResponseCreated(event.response.id, this.#fullChatCtx);
|
|
388
|
+
}
|
|
389
|
+
#handleOutputItemDone(event) {
|
|
390
|
+
if (event.item.type === "function_call") {
|
|
391
|
+
this.#pendingToolCalls.add(event.item.call_id);
|
|
392
|
+
return {
|
|
393
|
+
id: this.#responseId,
|
|
394
|
+
delta: {
|
|
395
|
+
role: "assistant",
|
|
396
|
+
content: void 0,
|
|
397
|
+
toolCalls: [
|
|
398
|
+
import_agents.llm.FunctionCall.create({
|
|
399
|
+
callId: event.item.call_id,
|
|
400
|
+
name: event.item.name,
|
|
401
|
+
args: event.item.arguments
|
|
402
|
+
})
|
|
403
|
+
]
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
return void 0;
|
|
408
|
+
}
|
|
409
|
+
#handleOutputTextDelta(event) {
|
|
410
|
+
return {
|
|
411
|
+
id: this.#responseId,
|
|
412
|
+
delta: {
|
|
413
|
+
role: "assistant",
|
|
414
|
+
content: event.delta
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
#handleResponseCompleted(event) {
|
|
419
|
+
this.#llm._setPendingToolCalls(this.#pendingToolCalls);
|
|
420
|
+
if (event.response.usage) {
|
|
421
|
+
return {
|
|
422
|
+
id: this.#responseId,
|
|
423
|
+
usage: {
|
|
424
|
+
completionTokens: event.response.usage.output_tokens,
|
|
425
|
+
promptTokens: event.response.usage.input_tokens,
|
|
426
|
+
promptCachedTokens: event.response.usage.input_tokens_details.cached_tokens,
|
|
427
|
+
totalTokens: event.response.usage.total_tokens
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
return void 0;
|
|
432
|
+
}
|
|
433
|
+
#handleResponseFailed(event) {
|
|
434
|
+
var _a, _b;
|
|
435
|
+
throw new import_agents.APIStatusError({
|
|
436
|
+
message: ((_b = (_a = event.response) == null ? void 0 : _a.error) == null ? void 0 : _b.message) ?? "Response failed",
|
|
437
|
+
options: { statusCode: -1, retryable: false }
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
async function connectWs(url, apiKey, timeoutMs) {
|
|
442
|
+
return new Promise((resolve, reject) => {
|
|
443
|
+
const ws = new import_ws.WebSocket(url, {
|
|
444
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
445
|
+
});
|
|
446
|
+
let settled = false;
|
|
447
|
+
const timer = setTimeout(() => {
|
|
448
|
+
settled = true;
|
|
449
|
+
ws.close();
|
|
450
|
+
reject(
|
|
451
|
+
new import_agents.APIConnectionError({ message: "Timeout connecting to OpenAI Responses WebSocket" })
|
|
452
|
+
);
|
|
453
|
+
}, timeoutMs);
|
|
454
|
+
ws.once("open", () => {
|
|
455
|
+
if (settled) return;
|
|
456
|
+
settled = true;
|
|
457
|
+
clearTimeout(timer);
|
|
458
|
+
resolve(ws);
|
|
459
|
+
});
|
|
460
|
+
ws.once("error", (err) => {
|
|
461
|
+
if (settled) return;
|
|
462
|
+
settled = true;
|
|
463
|
+
clearTimeout(timer);
|
|
464
|
+
reject(
|
|
465
|
+
new import_agents.APIConnectionError({
|
|
466
|
+
message: `Error connecting to OpenAI Responses WebSocket: ${err.message}`
|
|
467
|
+
})
|
|
468
|
+
);
|
|
469
|
+
});
|
|
470
|
+
ws.once("close", (code) => {
|
|
471
|
+
if (settled) return;
|
|
472
|
+
settled = true;
|
|
473
|
+
clearTimeout(timer);
|
|
474
|
+
reject(
|
|
475
|
+
new import_agents.APIConnectionError({
|
|
476
|
+
message: `OpenAI Responses WebSocket closed unexpectedly during connect (code ${code})`
|
|
477
|
+
})
|
|
478
|
+
);
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
function getWebSocketStateLabel(readyState) {
|
|
483
|
+
switch (readyState) {
|
|
484
|
+
case import_ws.WebSocket.CONNECTING:
|
|
485
|
+
return "CONNECTING";
|
|
486
|
+
case import_ws.WebSocket.OPEN:
|
|
487
|
+
return "OPEN";
|
|
488
|
+
case import_ws.WebSocket.CLOSING:
|
|
489
|
+
return "CLOSING";
|
|
490
|
+
case import_ws.WebSocket.CLOSED:
|
|
491
|
+
return "CLOSED";
|
|
492
|
+
default:
|
|
493
|
+
return `UNKNOWN:${readyState}`;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
497
|
+
0 && (module.exports = {
|
|
498
|
+
ResponsesWebSocket,
|
|
499
|
+
WSLLM,
|
|
500
|
+
WSLLMStream
|
|
501
|
+
});
|
|
502
|
+
//# sourceMappingURL=llm.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/ws/llm.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { APIConnectOptions } from '@livekit/agents';\nimport {\n APIConnectionError,\n APIStatusError,\n APITimeoutError,\n ConnectionPool,\n DEFAULT_API_CONNECT_OPTIONS,\n llm,\n stream,\n toError,\n} from '@livekit/agents';\nimport type OpenAI from 'openai';\nimport { WebSocket } from 'ws';\nimport type { ChatModels } from '../models.js';\nimport type {\n WsOutputItemDoneEvent,\n WsOutputTextDeltaEvent,\n WsResponseCompletedEvent,\n WsResponseCreateEvent,\n WsResponseCreatedEvent,\n WsResponseFailedEvent,\n WsServerEvent,\n} from './types.js';\nimport { wsServerEventSchema } from './types.js';\n\nconst OPENAI_RESPONSES_WS_URL = 'wss://api.openai.com/v1/responses';\n\n// OpenAI enforces a 60-minute maximum duration on Responses WebSocket connections.\nconst WS_MAX_SESSION_DURATION = 3_600_000;\n\n// ============================================================================\n// Internal: ResponsesWebSocket\n//\n// Wraps a single raw WebSocket connection. Maintains a FIFO queue of\n// StreamChannels — one per outstanding response.create request — and\n// dispatches every incoming server-event to the front of the queue.\n// A response is terminated (and its channel closed) when the service sends\n// response.completed, response.failed, or error.\n//\n// ============================================================================\n\nexport class ResponsesWebSocket {\n #ws: WebSocket;\n // FIFO queue: the front entry receives validated WsServerEvents for the in-flight response.\n #outputQueue: stream.StreamChannel<WsServerEvent>[] = [];\n\n constructor(ws: WebSocket) {\n this.#ws = ws;\n\n ws.on('message', (data: Buffer) => {\n const current = this.#outputQueue[0];\n if (!current) return;\n\n let raw: unknown;\n try {\n raw = JSON.parse(data.toString());\n } catch {\n return;\n }\n\n // Validate and type-narrow with Zod at write time so readers always\n // receive a fully-typed WsServerEvent.\n const parsed = wsServerEventSchema.safeParse(raw);\n if (!parsed.success) return;\n\n const event = parsed.data;\n void current.write(event);\n\n // Close and dequeue on any terminal event.\n if (\n event.type === 'response.completed' ||\n event.type === 'response.failed' ||\n event.type === 'error'\n ) {\n void current.close();\n this.#outputQueue.shift();\n }\n });\n\n ws.on('close', () => {\n // If the WebSocket closes while requests are still in flight, synthesise\n // a typed error event so all readers can handle it cleanly.\n for (const current of this.#outputQueue) {\n if (!current.closed) {\n const closeError: WsServerEvent = {\n type: 'error',\n error: {\n code: 'websocket_closed',\n message: 'OpenAI Responses WebSocket closed unexpectedly',\n },\n };\n void current.write(closeError).finally(() => current.close());\n }\n }\n this.#outputQueue = [];\n });\n }\n\n /**\n * Send a response.create event. Returns a typed `StreamChannel<WsServerEvent>`\n * that yields validated server events until the response terminates.\n */\n sendRequest(payload: WsResponseCreateEvent): stream.StreamChannel<WsServerEvent> {\n if (this.#ws.readyState !== WebSocket.OPEN) {\n throw new APIConnectionError({\n message: `OpenAI Responses WebSocket is not open (state ${getWebSocketStateLabel(this.#ws.readyState)})`,\n options: { retryable: true },\n });\n }\n\n const channel = stream.createStreamChannel<WsServerEvent>();\n this.#outputQueue.push(channel);\n this.#ws.send(JSON.stringify(payload));\n return channel;\n }\n\n close(): void {\n // Drain pending channels before closing the socket.\n for (const ch of this.#outputQueue) {\n void ch.close();\n }\n this.#outputQueue = [];\n this.#ws.close();\n }\n}\n\n// ============================================================================\n// LLMOptions\n// ============================================================================\n\nexport interface WSLLMOptions {\n model: string | ChatModels;\n apiKey?: string;\n baseURL?: string;\n temperature?: number;\n parallelToolCalls?: boolean;\n toolChoice?: llm.ToolChoice;\n store?: boolean;\n metadata?: Record<string, string>;\n strictToolSchema?: boolean;\n}\n\nconst defaultLLMOptions: WSLLMOptions = {\n model: 'gpt-4.1',\n apiKey: process.env.OPENAI_API_KEY,\n strictToolSchema: true,\n};\n\n// ============================================================================\n// LLM\n// ============================================================================\n\nexport class WSLLM extends llm.LLM {\n #opts: WSLLMOptions;\n #pool: ConnectionPool<ResponsesWebSocket>;\n #prevResponseId = '';\n #prevChatCtx: llm.ChatContext | null = null;\n #pendingToolCalls = new Set<string>();\n\n /**\n * Create a new instance of the OpenAI Responses API WebSocket LLM.\n *\n * @remarks\n * `apiKey` must be set to your OpenAI API key, either using the argument or\n * by setting the `OPENAI_API_KEY` environment variable.\n *\n * A persistent WebSocket connection to `/v1/responses` is maintained and\n * reused across turns, reducing per-turn continuation overhead for\n * tool-call-heavy workflows.\n */\n constructor(opts: Partial<WSLLMOptions> = defaultLLMOptions) {\n super();\n\n this.#opts = { ...defaultLLMOptions, ...opts };\n if (!this.#opts.apiKey) {\n throw new Error('OpenAI API key is required, whether as an argument or as $OPENAI_API_KEY');\n }\n\n this.#pool = new ConnectionPool<ResponsesWebSocket>({\n maxSessionDuration: WS_MAX_SESSION_DURATION,\n connectCb: async (timeoutMs: number) => {\n const wsUrl = this.#opts.baseURL\n ? `${this.#opts.baseURL.replace(/^https?/, 'wss').replace(/\\/+$/, '')}/responses`\n : OPENAI_RESPONSES_WS_URL;\n const ws = await connectWs(wsUrl, this.#opts.apiKey!, timeoutMs);\n return new ResponsesWebSocket(ws);\n },\n closeCb: async (conn: ResponsesWebSocket) => {\n conn.close();\n },\n });\n }\n\n label(): string {\n return 'openai.ws.LLM';\n }\n\n get model(): string {\n return this.#opts.model;\n }\n\n prewarm(): void {\n this.#pool.prewarm();\n }\n\n async close(): Promise<void> {\n await this.#pool.close();\n }\n\n override async aclose(): Promise<void> {\n await this.close();\n }\n\n /** Called by LLMStream once response.created fires to atomically persist both the\n * response ID and its corresponding chat context for the next turn's diff. */\n _onResponseCreated(responseId: string, chatCtx: llm.ChatContext): void {\n this.#prevResponseId = responseId;\n this.#prevChatCtx = chatCtx;\n }\n\n _setPendingToolCalls(callIds: Set<string>): void {\n this.#pendingToolCalls = callIds;\n }\n\n chat({\n chatCtx,\n toolCtx,\n connOptions = DEFAULT_API_CONNECT_OPTIONS,\n parallelToolCalls,\n toolChoice,\n extraKwargs,\n }: {\n chatCtx: llm.ChatContext;\n toolCtx?: llm.ToolContext;\n connOptions?: APIConnectOptions;\n parallelToolCalls?: boolean;\n toolChoice?: llm.ToolChoice;\n extraKwargs?: Record<string, unknown>;\n }): WSLLMStream {\n const modelOptions: Record<string, unknown> = { ...(extraKwargs ?? {}) };\n\n parallelToolCalls =\n parallelToolCalls !== undefined ? parallelToolCalls : this.#opts.parallelToolCalls;\n if (toolCtx && Object.keys(toolCtx).length > 0 && parallelToolCalls !== undefined) {\n modelOptions.parallel_tool_calls = parallelToolCalls;\n }\n\n toolChoice =\n toolChoice !== undefined ? toolChoice : (this.#opts.toolChoice as llm.ToolChoice | undefined);\n if (toolChoice) {\n modelOptions.tool_choice = toolChoice;\n }\n\n if (this.#opts.temperature !== undefined) {\n modelOptions.temperature = this.#opts.temperature;\n }\n\n if (this.#opts.store !== undefined) {\n modelOptions.store = this.#opts.store;\n }\n\n if (this.#opts.metadata) {\n modelOptions.metadata = this.#opts.metadata;\n }\n\n let inputChatCtx = chatCtx;\n let prevResponseId: string | undefined;\n const canUseStoredResponse = modelOptions.store !== false;\n\n if (canUseStoredResponse && this.#prevChatCtx && this.#prevResponseId) {\n const diff = llm.computeChatCtxDiff(this.#prevChatCtx, chatCtx);\n const lastPrevItemId = this.#prevChatCtx.items.at(-1)?.id ?? null;\n\n if (\n diff.toRemove.length === 0 &&\n diff.toCreate.length > 0 &&\n diff.toCreate[0]![0] === lastPrevItemId\n ) {\n // All new items are appended after the tail of the previous context —\n // safe to send only the incremental input with previous_response_id,\n // but only if all pending tool calls from the previous response have\n // their corresponding function_call_output in the new items.\n const newItemIds = new Set(diff.toCreate.map(([, id]) => id));\n const newItems = chatCtx.items.filter((item: llm.ChatItem) => newItemIds.has(item.id));\n const pendingToolCallsCompleted = this.#pendingToolCallsCompleted(newItems);\n if (pendingToolCallsCompleted) {\n inputChatCtx = new llm.ChatContext(newItems);\n prevResponseId = this.#prevResponseId;\n }\n }\n // Otherwise: items were removed or inserted mid-history — fall back to\n // sending the full context with no previous_response_id.\n }\n\n return new WSLLMStream(this, {\n pool: this.#pool,\n model: this.#opts.model,\n chatCtx: inputChatCtx,\n fullChatCtx: chatCtx,\n toolCtx,\n connOptions,\n modelOptions,\n prevResponseId,\n strictToolSchema: this.#opts.strictToolSchema ?? true,\n });\n }\n\n #pendingToolCallsCompleted(items: llm.ChatItem[]): boolean {\n if (this.#pendingToolCalls.size === 0) return true;\n const completedCallIds = new Set(\n items\n .filter((item): item is llm.FunctionCallOutput => item.type === 'function_call_output')\n .map((item) => item.callId),\n );\n return [...this.#pendingToolCalls].every((callId) => completedCallIds.has(callId));\n }\n}\n\n// ============================================================================\n// WsLLMStream\n// ============================================================================\n\nexport class WSLLMStream extends llm.LLMStream {\n #llm: WSLLM;\n #pool: ConnectionPool<ResponsesWebSocket>;\n #model: string | ChatModels;\n #modelOptions: Record<string, unknown>;\n #strictToolSchema: boolean;\n #prevResponseId?: string;\n /** Full chat context — used as fallback when previous_response_id is stale. */\n #fullChatCtx: llm.ChatContext;\n #responseId = '';\n #pendingToolCalls = new Set<string>();\n\n constructor(\n llm: WSLLM,\n {\n pool,\n model,\n chatCtx,\n fullChatCtx,\n toolCtx,\n connOptions,\n modelOptions,\n prevResponseId,\n strictToolSchema,\n }: {\n pool: ConnectionPool<ResponsesWebSocket>;\n model: string | ChatModels;\n chatCtx: llm.ChatContext;\n fullChatCtx: llm.ChatContext;\n toolCtx?: llm.ToolContext;\n connOptions: APIConnectOptions;\n modelOptions: Record<string, unknown>;\n prevResponseId?: string;\n strictToolSchema: boolean;\n },\n ) {\n super(llm, { chatCtx, toolCtx, connOptions });\n this.#llm = llm;\n this.#pool = pool;\n this.#model = model;\n this.#modelOptions = modelOptions;\n this.#strictToolSchema = strictToolSchema;\n this.#prevResponseId = prevResponseId;\n this.#fullChatCtx = fullChatCtx;\n }\n\n protected async run(): Promise<void> {\n let retryable = true;\n\n try {\n await this.#pool.withConnection(async (conn: ResponsesWebSocket) => {\n const needsRetry = await this.#runWithConn(conn, this.chatCtx, this.#prevResponseId);\n\n if (needsRetry) {\n // previous_response_id was evicted from the server-side cache.\n // Retry once on the same connection with the full context and no ID.\n retryable = true;\n await this.#runWithConn(conn, this.#fullChatCtx, undefined);\n }\n });\n } catch (error) {\n if (\n error instanceof APIStatusError ||\n error instanceof APITimeoutError ||\n error instanceof APIConnectionError\n ) {\n throw error;\n }\n throw new APIConnectionError({\n message: toError(error).message,\n options: { retryable },\n });\n }\n }\n\n /**\n * Execute a single response.create round-trip on the given connection.\n * Returns `true` when the caller should retry with the full chat context\n * (i.e. `previous_response_not_found`), `false` otherwise.\n */\n async #runWithConn(\n conn: ResponsesWebSocket,\n chatCtx: llm.ChatContext,\n prevResponseId: string | undefined,\n ): Promise<boolean> {\n const messages = (await chatCtx.toProviderFormat(\n 'openai.responses',\n )) as OpenAI.Responses.ResponseInputItem[];\n\n const tools = this.toolCtx\n ? Object.entries(this.toolCtx).map(([name, func]) => {\n const oaiParams = {\n type: 'function' as const,\n name,\n description: func.description,\n parameters: llm.toJsonSchema(\n func.parameters,\n true,\n this.#strictToolSchema,\n ) as unknown as OpenAI.Responses.FunctionTool['parameters'],\n } as OpenAI.Responses.FunctionTool;\n\n if (this.#strictToolSchema) {\n oaiParams.strict = true;\n }\n\n return oaiParams;\n })\n : undefined;\n\n const requestOptions: Record<string, unknown> = { ...this.#modelOptions };\n if (!tools) {\n delete requestOptions.tool_choice;\n }\n\n const payload: WsResponseCreateEvent = {\n type: 'response.create',\n model: this.#model as string,\n input: messages as unknown[],\n tools: (tools ?? []) as unknown[],\n ...(prevResponseId ? { previous_response_id: prevResponseId } : {}),\n ...requestOptions,\n };\n\n let channel: stream.StreamChannel<WsServerEvent>;\n try {\n channel = conn.sendRequest(payload);\n } catch (error) {\n if (error instanceof APIConnectionError) {\n conn.close();\n this.#pool.invalidate();\n }\n throw error;\n }\n const reader = channel.stream().getReader();\n\n // Events are already Zod-validated by ResponsesWebSocket before being\n // written to the channel, so no re-parsing is needed here.\n try {\n while (true) {\n const { done, value: event } = await reader.read();\n if (done) break;\n\n let chunk: llm.ChatChunk | undefined;\n\n switch (event.type) {\n case 'error': {\n const retry = this.#handleError(event, conn);\n if (retry) return true;\n break;\n }\n case 'response.created':\n this.#handleResponseCreated(event);\n break;\n case 'response.output_item.done':\n chunk = this.#handleOutputItemDone(event);\n break;\n case 'response.output_text.delta':\n chunk = this.#handleOutputTextDelta(event);\n break;\n case 'response.completed':\n chunk = this.#handleResponseCompleted(event);\n break;\n case 'response.failed':\n this.#handleResponseFailed(event);\n break;\n default:\n break;\n }\n\n if (chunk) {\n this.queue.put(chunk);\n }\n }\n } finally {\n reader.releaseLock();\n }\n\n return false;\n }\n\n /**\n * Returns `true` when the caller should retry with full context\n * (`previous_response_not_found`), throws for all other errors.\n */\n #handleError(event: WsServerEvent & { type: 'error' }, conn: ResponsesWebSocket): boolean {\n const code = event.error?.code;\n\n if (code === 'previous_response_not_found') {\n // The server-side in-memory cache was evicted (e.g. after a failed turn\n // or reconnect). Signal the caller to retry with the full context.\n return true;\n }\n\n if (code === 'websocket_connection_limit_reached' || code === 'websocket_closed') {\n // Transient connection issue (timeout, network drop, or 60-min limit).\n // Evict this connection so the pool opens a fresh one on retry.\n conn.close();\n this.#pool.invalidate();\n throw new APIConnectionError({\n message: event.error?.message ?? `WebSocket closed (${code})`,\n options: { retryable: true },\n });\n }\n\n throw new APIStatusError({\n message: event.error?.message ?? event.message ?? 'Unknown error from OpenAI Responses WS',\n options: {\n statusCode: event.status ?? -1,\n retryable: false,\n },\n });\n }\n\n #handleResponseCreated(event: WsResponseCreatedEvent): void {\n this.#responseId = event.response.id;\n this.#llm._onResponseCreated(event.response.id, this.#fullChatCtx);\n }\n\n #handleOutputItemDone(event: WsOutputItemDoneEvent): llm.ChatChunk | undefined {\n if (event.item.type === 'function_call') {\n this.#pendingToolCalls.add(event.item.call_id);\n return {\n id: this.#responseId,\n delta: {\n role: 'assistant',\n content: undefined,\n toolCalls: [\n llm.FunctionCall.create({\n callId: event.item.call_id,\n name: event.item.name,\n args: event.item.arguments,\n }),\n ],\n },\n };\n }\n return undefined;\n }\n\n #handleOutputTextDelta(event: WsOutputTextDeltaEvent): llm.ChatChunk {\n return {\n id: this.#responseId,\n delta: {\n role: 'assistant',\n content: event.delta,\n },\n };\n }\n\n #handleResponseCompleted(event: WsResponseCompletedEvent): llm.ChatChunk | undefined {\n this.#llm._setPendingToolCalls(this.#pendingToolCalls);\n\n if (event.response.usage) {\n return {\n id: this.#responseId,\n usage: {\n completionTokens: event.response.usage.output_tokens,\n promptTokens: event.response.usage.input_tokens,\n promptCachedTokens: event.response.usage.input_tokens_details.cached_tokens,\n totalTokens: event.response.usage.total_tokens,\n },\n };\n }\n return undefined;\n }\n\n #handleResponseFailed(event: WsResponseFailedEvent): void {\n throw new APIStatusError({\n message: event.response?.error?.message ?? 'Response failed',\n options: { statusCode: -1, retryable: false },\n });\n }\n}\n\n// ============================================================================\n// Internal helpers\n// ============================================================================\n\nasync function connectWs(url: string, apiKey: string, timeoutMs: number): Promise<WebSocket> {\n return new Promise<WebSocket>((resolve, reject) => {\n const ws = new WebSocket(url, {\n headers: { Authorization: `Bearer ${apiKey}` },\n });\n\n let settled = false;\n\n const timer = setTimeout(() => {\n settled = true;\n ws.close();\n reject(\n new APIConnectionError({ message: 'Timeout connecting to OpenAI Responses WebSocket' }),\n );\n }, timeoutMs);\n\n ws.once('open', () => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve(ws);\n });\n\n ws.once('error', (err) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n reject(\n new APIConnectionError({\n message: `Error connecting to OpenAI Responses WebSocket: ${err.message}`,\n }),\n );\n });\n\n ws.once('close', (code) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n reject(\n new APIConnectionError({\n message: `OpenAI Responses WebSocket closed unexpectedly during connect (code ${code})`,\n }),\n );\n });\n });\n}\n\nfunction getWebSocketStateLabel(readyState: number): string {\n switch (readyState) {\n case WebSocket.CONNECTING:\n return 'CONNECTING';\n case WebSocket.OPEN:\n return 'OPEN';\n case WebSocket.CLOSING:\n return 'CLOSING';\n case WebSocket.CLOSED:\n return 'CLOSED';\n default:\n return `UNKNOWN:${readyState}`;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,oBASO;AAEP,gBAA0B;AAW1B,mBAAoC;AAEpC,MAAM,0BAA0B;AAGhC,MAAM,0BAA0B;AAazB,MAAM,mBAAmB;AAAA,EAC9B;AAAA;AAAA,EAEA,eAAsD,CAAC;AAAA,EAEvD,YAAY,IAAe;AACzB,SAAK,MAAM;AAEX,OAAG,GAAG,WAAW,CAAC,SAAiB;AACjC,YAAM,UAAU,KAAK,aAAa,CAAC;AACnC,UAAI,CAAC,QAAS;AAEd,UAAI;AACJ,UAAI;AACF,cAAM,KAAK,MAAM,KAAK,SAAS,CAAC;AAAA,MAClC,QAAQ;AACN;AAAA,MACF;AAIA,YAAM,SAAS,iCAAoB,UAAU,GAAG;AAChD,UAAI,CAAC,OAAO,QAAS;AAErB,YAAM,QAAQ,OAAO;AACrB,WAAK,QAAQ,MAAM,KAAK;AAGxB,UACE,MAAM,SAAS,wBACf,MAAM,SAAS,qBACf,MAAM,SAAS,SACf;AACA,aAAK,QAAQ,MAAM;AACnB,aAAK,aAAa,MAAM;AAAA,MAC1B;AAAA,IACF,CAAC;AAED,OAAG,GAAG,SAAS,MAAM;AAGnB,iBAAW,WAAW,KAAK,cAAc;AACvC,YAAI,CAAC,QAAQ,QAAQ;AACnB,gBAAM,aAA4B;AAAA,YAChC,MAAM;AAAA,YACN,OAAO;AAAA,cACL,MAAM;AAAA,cACN,SAAS;AAAA,YACX;AAAA,UACF;AACA,eAAK,QAAQ,MAAM,UAAU,EAAE,QAAQ,MAAM,QAAQ,MAAM,CAAC;AAAA,QAC9D;AAAA,MACF;AACA,WAAK,eAAe,CAAC;AAAA,IACvB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,YAAY,SAAqE;AAC/E,QAAI,KAAK,IAAI,eAAe,oBAAU,MAAM;AAC1C,YAAM,IAAI,iCAAmB;AAAA,QAC3B,SAAS,iDAAiD,uBAAuB,KAAK,IAAI,UAAU,CAAC;AAAA,QACrG,SAAS,EAAE,WAAW,KAAK;AAAA,MAC7B,CAAC;AAAA,IACH;AAEA,UAAM,UAAU,qBAAO,oBAAmC;AAC1D,SAAK,aAAa,KAAK,OAAO;AAC9B,SAAK,IAAI,KAAK,KAAK,UAAU,OAAO,CAAC;AACrC,WAAO;AAAA,EACT;AAAA,EAEA,QAAc;AAEZ,eAAW,MAAM,KAAK,cAAc;AAClC,WAAK,GAAG,MAAM;AAAA,IAChB;AACA,SAAK,eAAe,CAAC;AACrB,SAAK,IAAI,MAAM;AAAA,EACjB;AACF;AAkBA,MAAM,oBAAkC;AAAA,EACtC,OAAO;AAAA,EACP,QAAQ,QAAQ,IAAI;AAAA,EACpB,kBAAkB;AACpB;AAMO,MAAM,cAAc,kBAAI,IAAI;AAAA,EACjC;AAAA,EACA;AAAA,EACA,kBAAkB;AAAA,EAClB,eAAuC;AAAA,EACvC,oBAAoB,oBAAI,IAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAapC,YAAY,OAA8B,mBAAmB;AAC3D,UAAM;AAEN,SAAK,QAAQ,EAAE,GAAG,mBAAmB,GAAG,KAAK;AAC7C,QAAI,CAAC,KAAK,MAAM,QAAQ;AACtB,YAAM,IAAI,MAAM,0EAA0E;AAAA,IAC5F;AAEA,SAAK,QAAQ,IAAI,6BAAmC;AAAA,MAClD,oBAAoB;AAAA,MACpB,WAAW,OAAO,cAAsB;AACtC,cAAM,QAAQ,KAAK,MAAM,UACrB,GAAG,KAAK,MAAM,QAAQ,QAAQ,WAAW,KAAK,EAAE,QAAQ,QAAQ,EAAE,CAAC,eACnE;AACJ,cAAM,KAAK,MAAM,UAAU,OAAO,KAAK,MAAM,QAAS,SAAS;AAC/D,eAAO,IAAI,mBAAmB,EAAE;AAAA,MAClC;AAAA,MACA,SAAS,OAAO,SAA6B;AAC3C,aAAK,MAAM;AAAA,MACb;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,QAAgB;AACd,WAAO;AAAA,EACT;AAAA,EAEA,IAAI,QAAgB;AAClB,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA,EAEA,UAAgB;AACd,SAAK,MAAM,QAAQ;AAAA,EACrB;AAAA,EAEA,MAAM,QAAuB;AAC3B,UAAM,KAAK,MAAM,MAAM;AAAA,EACzB;AAAA,EAEA,MAAe,SAAwB;AACrC,UAAM,KAAK,MAAM;AAAA,EACnB;AAAA;AAAA;AAAA,EAIA,mBAAmB,YAAoB,SAAgC;AACrE,SAAK,kBAAkB;AACvB,SAAK,eAAe;AAAA,EACtB;AAAA,EAEA,qBAAqB,SAA4B;AAC/C,SAAK,oBAAoB;AAAA,EAC3B;AAAA,EAEA,KAAK;AAAA,IACH;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAOgB;AAjPlB;AAkPI,UAAM,eAAwC,EAAE,GAAI,eAAe,CAAC,EAAG;AAEvE,wBACE,sBAAsB,SAAY,oBAAoB,KAAK,MAAM;AACnE,QAAI,WAAW,OAAO,KAAK,OAAO,EAAE,SAAS,KAAK,sBAAsB,QAAW;AACjF,mBAAa,sBAAsB;AAAA,IACrC;AAEA,iBACE,eAAe,SAAY,aAAc,KAAK,MAAM;AACtD,QAAI,YAAY;AACd,mBAAa,cAAc;AAAA,IAC7B;AAEA,QAAI,KAAK,MAAM,gBAAgB,QAAW;AACxC,mBAAa,cAAc,KAAK,MAAM;AAAA,IACxC;AAEA,QAAI,KAAK,MAAM,UAAU,QAAW;AAClC,mBAAa,QAAQ,KAAK,MAAM;AAAA,IAClC;AAEA,QAAI,KAAK,MAAM,UAAU;AACvB,mBAAa,WAAW,KAAK,MAAM;AAAA,IACrC;AAEA,QAAI,eAAe;AACnB,QAAI;AACJ,UAAM,uBAAuB,aAAa,UAAU;AAEpD,QAAI,wBAAwB,KAAK,gBAAgB,KAAK,iBAAiB;AACrE,YAAM,OAAO,kBAAI,mBAAmB,KAAK,cAAc,OAAO;AAC9D,YAAM,mBAAiB,UAAK,aAAa,MAAM,GAAG,EAAE,MAA7B,mBAAgC,OAAM;AAE7D,UACE,KAAK,SAAS,WAAW,KACzB,KAAK,SAAS,SAAS,KACvB,KAAK,SAAS,CAAC,EAAG,CAAC,MAAM,gBACzB;AAKA,cAAM,aAAa,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC;AAC5D,cAAM,WAAW,QAAQ,MAAM,OAAO,CAAC,SAAuB,WAAW,IAAI,KAAK,EAAE,CAAC;AACrF,cAAM,4BAA4B,KAAK,2BAA2B,QAAQ;AAC1E,YAAI,2BAA2B;AAC7B,yBAAe,IAAI,kBAAI,YAAY,QAAQ;AAC3C,2BAAiB,KAAK;AAAA,QACxB;AAAA,MACF;AAAA,IAGF;AAEA,WAAO,IAAI,YAAY,MAAM;AAAA,MAC3B,MAAM,KAAK;AAAA,MACX,OAAO,KAAK,MAAM;AAAA,MAClB,SAAS;AAAA,MACT,aAAa;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,kBAAkB,KAAK,MAAM,oBAAoB;AAAA,IACnD,CAAC;AAAA,EACH;AAAA,EAEA,2BAA2B,OAAgC;AACzD,QAAI,KAAK,kBAAkB,SAAS,EAAG,QAAO;AAC9C,UAAM,mBAAmB,IAAI;AAAA,MAC3B,MACG,OAAO,CAAC,SAAyC,KAAK,SAAS,sBAAsB,EACrF,IAAI,CAAC,SAAS,KAAK,MAAM;AAAA,IAC9B;AACA,WAAO,CAAC,GAAG,KAAK,iBAAiB,EAAE,MAAM,CAAC,WAAW,iBAAiB,IAAI,MAAM,CAAC;AAAA,EACnF;AACF;AAMO,MAAM,oBAAoB,kBAAI,UAAU;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA,cAAc;AAAA,EACd,oBAAoB,oBAAI,IAAY;AAAA,EAEpC,YACEA,MACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAWA;AACA,UAAMA,MAAK,EAAE,SAAS,SAAS,YAAY,CAAC;AAC5C,SAAK,OAAOA;AACZ,SAAK,QAAQ;AACb,SAAK,SAAS;AACd,SAAK,gBAAgB;AACrB,SAAK,oBAAoB;AACzB,SAAK,kBAAkB;AACvB,SAAK,eAAe;AAAA,EACtB;AAAA,EAEA,MAAgB,MAAqB;AACnC,QAAI,YAAY;AAEhB,QAAI;AACF,YAAM,KAAK,MAAM,eAAe,OAAO,SAA6B;AAClE,cAAM,aAAa,MAAM,KAAK,aAAa,MAAM,KAAK,SAAS,KAAK,eAAe;AAEnF,YAAI,YAAY;AAGd,sBAAY;AACZ,gBAAM,KAAK,aAAa,MAAM,KAAK,cAAc,MAAS;AAAA,QAC5D;AAAA,MACF,CAAC;AAAA,IACH,SAAS,OAAO;AACd,UACE,iBAAiB,gCACjB,iBAAiB,iCACjB,iBAAiB,kCACjB;AACA,cAAM;AAAA,MACR;AACA,YAAM,IAAI,iCAAmB;AAAA,QAC3B,aAAS,uBAAQ,KAAK,EAAE;AAAA,QACxB,SAAS,EAAE,UAAU;AAAA,MACvB,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,aACJ,MACA,SACA,gBACkB;AAClB,UAAM,WAAY,MAAM,QAAQ;AAAA,MAC9B;AAAA,IACF;AAEA,UAAM,QAAQ,KAAK,UACf,OAAO,QAAQ,KAAK,OAAO,EAAE,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM;AACjD,YAAM,YAAY;AAAA,QAChB,MAAM;AAAA,QACN;AAAA,QACA,aAAa,KAAK;AAAA,QAClB,YAAY,kBAAI;AAAA,UACd,KAAK;AAAA,UACL;AAAA,UACA,KAAK;AAAA,QACP;AAAA,MACF;AAEA,UAAI,KAAK,mBAAmB;AAC1B,kBAAU,SAAS;AAAA,MACrB;AAEA,aAAO;AAAA,IACT,CAAC,IACD;AAEJ,UAAM,iBAA0C,EAAE,GAAG,KAAK,cAAc;AACxE,QAAI,CAAC,OAAO;AACV,aAAO,eAAe;AAAA,IACxB;AAEA,UAAM,UAAiC;AAAA,MACrC,MAAM;AAAA,MACN,OAAO,KAAK;AAAA,MACZ,OAAO;AAAA,MACP,OAAQ,SAAS,CAAC;AAAA,MAClB,GAAI,iBAAiB,EAAE,sBAAsB,eAAe,IAAI,CAAC;AAAA,MACjE,GAAG;AAAA,IACL;AAEA,QAAI;AACJ,QAAI;AACF,gBAAU,KAAK,YAAY,OAAO;AAAA,IACpC,SAAS,OAAO;AACd,UAAI,iBAAiB,kCAAoB;AACvC,aAAK,MAAM;AACX,aAAK,MAAM,WAAW;AAAA,MACxB;AACA,YAAM;AAAA,IACR;AACA,UAAM,SAAS,QAAQ,OAAO,EAAE,UAAU;AAI1C,QAAI;AACF,aAAO,MAAM;AACX,cAAM,EAAE,MAAM,OAAO,MAAM,IAAI,MAAM,OAAO,KAAK;AACjD,YAAI,KAAM;AAEV,YAAI;AAEJ,gBAAQ,MAAM,MAAM;AAAA,UAClB,KAAK,SAAS;AACZ,kBAAM,QAAQ,KAAK,aAAa,OAAO,IAAI;AAC3C,gBAAI,MAAO,QAAO;AAClB;AAAA,UACF;AAAA,UACA,KAAK;AACH,iBAAK,uBAAuB,KAAK;AACjC;AAAA,UACF,KAAK;AACH,oBAAQ,KAAK,sBAAsB,KAAK;AACxC;AAAA,UACF,KAAK;AACH,oBAAQ,KAAK,uBAAuB,KAAK;AACzC;AAAA,UACF,KAAK;AACH,oBAAQ,KAAK,yBAAyB,KAAK;AAC3C;AAAA,UACF,KAAK;AACH,iBAAK,sBAAsB,KAAK;AAChC;AAAA,UACF;AACE;AAAA,QACJ;AAEA,YAAI,OAAO;AACT,eAAK,MAAM,IAAI,KAAK;AAAA,QACtB;AAAA,MACF;AAAA,IACF,UAAE;AACA,aAAO,YAAY;AAAA,IACrB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,OAA0C,MAAmC;AA9f5F;AA+fI,UAAM,QAAO,WAAM,UAAN,mBAAa;AAE1B,QAAI,SAAS,+BAA+B;AAG1C,aAAO;AAAA,IACT;AAEA,QAAI,SAAS,wCAAwC,SAAS,oBAAoB;AAGhF,WAAK,MAAM;AACX,WAAK,MAAM,WAAW;AACtB,YAAM,IAAI,iCAAmB;AAAA,QAC3B,WAAS,WAAM,UAAN,mBAAa,YAAW,qBAAqB,IAAI;AAAA,QAC1D,SAAS,EAAE,WAAW,KAAK;AAAA,MAC7B,CAAC;AAAA,IACH;AAEA,UAAM,IAAI,6BAAe;AAAA,MACvB,WAAS,WAAM,UAAN,mBAAa,YAAW,MAAM,WAAW;AAAA,MAClD,SAAS;AAAA,QACP,YAAY,MAAM,UAAU;AAAA,QAC5B,WAAW;AAAA,MACb;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,uBAAuB,OAAqC;AAC1D,SAAK,cAAc,MAAM,SAAS;AAClC,SAAK,KAAK,mBAAmB,MAAM,SAAS,IAAI,KAAK,YAAY;AAAA,EACnE;AAAA,EAEA,sBAAsB,OAAyD;AAC7E,QAAI,MAAM,KAAK,SAAS,iBAAiB;AACvC,WAAK,kBAAkB,IAAI,MAAM,KAAK,OAAO;AAC7C,aAAO;AAAA,QACL,IAAI,KAAK;AAAA,QACT,OAAO;AAAA,UACL,MAAM;AAAA,UACN,SAAS;AAAA,UACT,WAAW;AAAA,YACT,kBAAI,aAAa,OAAO;AAAA,cACtB,QAAQ,MAAM,KAAK;AAAA,cACnB,MAAM,MAAM,KAAK;AAAA,cACjB,MAAM,MAAM,KAAK;AAAA,YACnB,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,uBAAuB,OAA8C;AACnE,WAAO;AAAA,MACL,IAAI,KAAK;AAAA,MACT,OAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS,MAAM;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,yBAAyB,OAA4D;AACnF,SAAK,KAAK,qBAAqB,KAAK,iBAAiB;AAErD,QAAI,MAAM,SAAS,OAAO;AACxB,aAAO;AAAA,QACL,IAAI,KAAK;AAAA,QACT,OAAO;AAAA,UACL,kBAAkB,MAAM,SAAS,MAAM;AAAA,UACvC,cAAc,MAAM,SAAS,MAAM;AAAA,UACnC,oBAAoB,MAAM,SAAS,MAAM,qBAAqB;AAAA,UAC9D,aAAa,MAAM,SAAS,MAAM;AAAA,QACpC;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,sBAAsB,OAAoC;AAhlB5D;AAilBI,UAAM,IAAI,6BAAe;AAAA,MACvB,WAAS,iBAAM,aAAN,mBAAgB,UAAhB,mBAAuB,YAAW;AAAA,MAC3C,SAAS,EAAE,YAAY,IAAI,WAAW,MAAM;AAAA,IAC9C,CAAC;AAAA,EACH;AACF;AAMA,eAAe,UAAU,KAAa,QAAgB,WAAuC;AAC3F,SAAO,IAAI,QAAmB,CAAC,SAAS,WAAW;AACjD,UAAM,KAAK,IAAI,oBAAU,KAAK;AAAA,MAC5B,SAAS,EAAE,eAAe,UAAU,MAAM,GAAG;AAAA,IAC/C,CAAC;AAED,QAAI,UAAU;AAEd,UAAM,QAAQ,WAAW,MAAM;AAC7B,gBAAU;AACV,SAAG,MAAM;AACT;AAAA,QACE,IAAI,iCAAmB,EAAE,SAAS,mDAAmD,CAAC;AAAA,MACxF;AAAA,IACF,GAAG,SAAS;AAEZ,OAAG,KAAK,QAAQ,MAAM;AACpB,UAAI,QAAS;AACb,gBAAU;AACV,mBAAa,KAAK;AAClB,cAAQ,EAAE;AAAA,IACZ,CAAC;AAED,OAAG,KAAK,SAAS,CAAC,QAAQ;AACxB,UAAI,QAAS;AACb,gBAAU;AACV,mBAAa,KAAK;AAClB;AAAA,QACE,IAAI,iCAAmB;AAAA,UACrB,SAAS,mDAAmD,IAAI,OAAO;AAAA,QACzE,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAED,OAAG,KAAK,SAAS,CAAC,SAAS;AACzB,UAAI,QAAS;AACb,gBAAU;AACV,mBAAa,KAAK;AAClB;AAAA,QACE,IAAI,iCAAmB;AAAA,UACrB,SAAS,uEAAuE,IAAI;AAAA,QACtF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAEA,SAAS,uBAAuB,YAA4B;AAC1D,UAAQ,YAAY;AAAA,IAClB,KAAK,oBAAU;AACb,aAAO;AAAA,IACT,KAAK,oBAAU;AACb,aAAO;AAAA,IACT,KAAK,oBAAU;AACb,aAAO;AAAA,IACT,KAAK,oBAAU;AACb,aAAO;AAAA,IACT;AACE,aAAO,WAAW,UAAU;AAAA,EAChC;AACF;","names":["llm"]}
|