@kubuild/ai 0.3.0 → 0.4.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.
- package/dist/{chunk-27AY2KBI.js → chunk-3DRAVYU3.js} +173 -46
- package/dist/chunk-3DRAVYU3.js.map +1 -0
- package/dist/chunk-ILLNRABB.js +24 -0
- package/dist/chunk-ILLNRABB.js.map +1 -0
- package/dist/{chunk-JY4NHD3Q.js → chunk-T72YWBJS.js} +62 -1
- package/dist/chunk-T72YWBJS.js.map +1 -0
- package/dist/{chunk-UH24LHRY.js → chunk-YETTTWHI.js} +226 -1
- package/dist/chunk-YETTTWHI.js.map +1 -0
- package/dist/client/ai-client.d.ts +20 -1
- package/dist/client/ai-client.d.ts.map +1 -1
- package/dist/client/index.cjs +61 -0
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/core/document-outline.d.ts +60 -0
- package/dist/core/document-outline.d.ts.map +1 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/messages.d.ts +18 -0
- package/dist/core/messages.d.ts.map +1 -0
- package/dist/core/prompt-compiler.d.ts +29 -0
- package/dist/core/prompt-compiler.d.ts.map +1 -1
- package/dist/index.cjs +261 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +31 -3
- package/dist/react/index.cjs +173 -0
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +112 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/use-ai-agent.d.ts +47 -0
- package/dist/react/use-ai-agent.d.ts.map +1 -0
- package/dist/server/adapters/anthropic.d.ts.map +1 -1
- package/dist/server/adapters/custom.d.ts.map +1 -1
- package/dist/server/adapters/gemini.d.ts.map +1 -1
- package/dist/server/adapters/index.cjs +180 -45
- package/dist/server/adapters/index.cjs.map +1 -1
- package/dist/server/adapters/index.js +2 -1
- package/dist/server/adapters/openai.d.ts +25 -0
- package/dist/server/adapters/openai.d.ts.map +1 -1
- package/dist/server/agent.d.ts +59 -0
- package/dist/server/agent.d.ts.map +1 -0
- package/dist/server/engine.d.ts.map +1 -1
- package/dist/server/handler.d.ts +10 -1
- package/dist/server/handler.d.ts.map +1 -1
- package/dist/server/index.cjs +1734 -260
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1229 -94
- package/dist/server/index.js.map +1 -1
- package/dist/server/tools/helpers.d.ts +66 -0
- package/dist/server/tools/helpers.d.ts.map +1 -0
- package/dist/server/tools/index.d.ts +25 -0
- package/dist/server/tools/index.d.ts.map +1 -0
- package/dist/server/tools/read-tools.d.ts +3 -0
- package/dist/server/tools/read-tools.d.ts.map +1 -0
- package/dist/server/tools/types.d.ts +51 -0
- package/dist/server/tools/types.d.ts.map +1 -0
- package/dist/server/tools/write-tools.d.ts +3 -0
- package/dist/server/tools/write-tools.d.ts.map +1 -0
- package/dist/types.d.ts +193 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -5
- package/dist/chunk-27AY2KBI.js.map +0 -1
- package/dist/chunk-JY4NHD3Q.js.map +0 -1
- package/dist/chunk-UH24LHRY.js.map +0 -1
|
@@ -56,6 +56,20 @@ async function* readSseDataLines(body, signal) {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
// src/core/messages.ts
|
|
60
|
+
function getMessageText(message) {
|
|
61
|
+
if (typeof message.content === "string") return message.content;
|
|
62
|
+
return message.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
63
|
+
}
|
|
64
|
+
function getToolUseBlocks(message) {
|
|
65
|
+
if (typeof message.content === "string") return [];
|
|
66
|
+
return message.content.filter((b) => b.type === "tool_use");
|
|
67
|
+
}
|
|
68
|
+
function getToolResultBlocks(message) {
|
|
69
|
+
if (typeof message.content === "string") return [];
|
|
70
|
+
return message.content.filter((b) => b.type === "tool_result");
|
|
71
|
+
}
|
|
72
|
+
|
|
59
73
|
// src/server/adapters/gemini.ts
|
|
60
74
|
var GeminiAdapter = class {
|
|
61
75
|
name = "gemini";
|
|
@@ -76,7 +90,7 @@ var GeminiAdapter = class {
|
|
|
76
90
|
const { userPrompt, messages: chatMessages } = params;
|
|
77
91
|
return chatMessages && chatMessages.length > 0 ? chatMessages.map((m) => ({
|
|
78
92
|
role: m.role === "assistant" ? "model" : "user",
|
|
79
|
-
parts: [{ text: m
|
|
93
|
+
parts: [{ text: getMessageText(m) }]
|
|
80
94
|
})) : [
|
|
81
95
|
{
|
|
82
96
|
role: "user",
|
|
@@ -188,8 +202,47 @@ var GeminiAdapter = class {
|
|
|
188
202
|
};
|
|
189
203
|
|
|
190
204
|
// src/server/adapters/openai.ts
|
|
205
|
+
function mapStopReason(finishReason) {
|
|
206
|
+
switch (finishReason) {
|
|
207
|
+
case "stop":
|
|
208
|
+
return "stop";
|
|
209
|
+
case "tool_calls":
|
|
210
|
+
case "function_call":
|
|
211
|
+
return "tool_calls";
|
|
212
|
+
case "length":
|
|
213
|
+
return "length";
|
|
214
|
+
case "content_filter":
|
|
215
|
+
return "content_filter";
|
|
216
|
+
default:
|
|
217
|
+
return "unknown";
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function parseToolCalls(raw) {
|
|
221
|
+
if (!Array.isArray(raw) || raw.length === 0) return [];
|
|
222
|
+
return raw.filter((call) => call?.function?.name).map((call, index) => {
|
|
223
|
+
let input = {};
|
|
224
|
+
const argsText = call.function?.arguments;
|
|
225
|
+
if (argsText && argsText.trim()) {
|
|
226
|
+
try {
|
|
227
|
+
const parsed = JSON.parse(argsText);
|
|
228
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
229
|
+
input = parsed;
|
|
230
|
+
}
|
|
231
|
+
} catch {
|
|
232
|
+
input = {};
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
type: "tool_use",
|
|
237
|
+
id: call.id || `call_${index}`,
|
|
238
|
+
name: call.function.name,
|
|
239
|
+
input
|
|
240
|
+
};
|
|
241
|
+
});
|
|
242
|
+
}
|
|
191
243
|
var OpenAiAdapter = class {
|
|
192
244
|
name = "openai";
|
|
245
|
+
supportsTools = true;
|
|
193
246
|
apiKey;
|
|
194
247
|
model;
|
|
195
248
|
temperature;
|
|
@@ -205,30 +258,95 @@ var OpenAiAdapter = class {
|
|
|
205
258
|
this.baseUrl = (options.baseUrl || "https://api.openai.com/v1").replace(/\/+$/, "");
|
|
206
259
|
this.customHeaders = options.headers;
|
|
207
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* Expands one `AiChatMessage` into the one-or-more OpenAI messages it corresponds to
|
|
263
|
+
* (STORA-530).
|
|
264
|
+
*
|
|
265
|
+
* The block model is Anthropic-shaped — tool results ride along inside a `user` message —
|
|
266
|
+
* while OpenAI wants each result as its own `role: 'tool'` message keyed by
|
|
267
|
+
* `tool_call_id`. That fan-out happens here so the rest of the stack stays
|
|
268
|
+
* provider-agnostic.
|
|
269
|
+
*/
|
|
270
|
+
mapMessage(message) {
|
|
271
|
+
const toolUses = getToolUseBlocks(message);
|
|
272
|
+
const toolResults = getToolResultBlocks(message);
|
|
273
|
+
const text = getMessageText(message);
|
|
274
|
+
if (toolResults.length > 0) {
|
|
275
|
+
const results = toolResults.map((block) => ({
|
|
276
|
+
role: "tool",
|
|
277
|
+
content: block.content,
|
|
278
|
+
tool_call_id: block.toolUseId
|
|
279
|
+
}));
|
|
280
|
+
if (text.trim()) {
|
|
281
|
+
results.push({ role: "user", content: text });
|
|
282
|
+
}
|
|
283
|
+
return results;
|
|
284
|
+
}
|
|
285
|
+
if (toolUses.length > 0) {
|
|
286
|
+
return [
|
|
287
|
+
{
|
|
288
|
+
role: "assistant",
|
|
289
|
+
content: text.trim() ? text : null,
|
|
290
|
+
tool_calls: toolUses.map((block) => ({
|
|
291
|
+
id: block.id,
|
|
292
|
+
type: "function",
|
|
293
|
+
function: {
|
|
294
|
+
name: block.name,
|
|
295
|
+
arguments: JSON.stringify(block.input ?? {})
|
|
296
|
+
}
|
|
297
|
+
}))
|
|
298
|
+
}
|
|
299
|
+
];
|
|
300
|
+
}
|
|
301
|
+
return [{ role: message.role, content: text }];
|
|
302
|
+
}
|
|
208
303
|
buildMessages(params) {
|
|
209
304
|
const { systemPrompt, userPrompt, messages: chatMessages } = params;
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
305
|
+
if (chatMessages && chatMessages.length > 0) {
|
|
306
|
+
return [
|
|
307
|
+
{ role: "system", content: systemPrompt },
|
|
308
|
+
...chatMessages.flatMap((m) => this.mapMessage(m))
|
|
309
|
+
];
|
|
310
|
+
}
|
|
311
|
+
return [
|
|
214
312
|
{ role: "system", content: systemPrompt },
|
|
215
313
|
{ role: "user", content: userPrompt }
|
|
216
314
|
];
|
|
217
315
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
316
|
+
/**
|
|
317
|
+
* Shared request body builder for `generate`/`generateStream` — notably the
|
|
318
|
+
* `response_format` decision, which must never fire in tool mode: OpenAI rejects
|
|
319
|
+
* `json_object` combined with `tools`, and a tool-calling turn's text output is a
|
|
320
|
+
* natural-language summary, not JSON.
|
|
321
|
+
*/
|
|
322
|
+
buildBody(params) {
|
|
323
|
+
const { systemPrompt, jsonSchema, messages: chatMessages, tools, toolChoice } = params;
|
|
223
324
|
const body = {
|
|
224
325
|
model: this.model,
|
|
225
|
-
temperature: this.temperature,
|
|
226
|
-
messages
|
|
326
|
+
temperature: params.temperature ?? this.temperature,
|
|
327
|
+
messages: this.buildMessages(params)
|
|
227
328
|
};
|
|
228
|
-
if (
|
|
329
|
+
if (tools && tools.length > 0) {
|
|
330
|
+
body.tools = tools.map((tool) => ({
|
|
331
|
+
type: "function",
|
|
332
|
+
function: {
|
|
333
|
+
name: tool.name,
|
|
334
|
+
description: tool.description,
|
|
335
|
+
parameters: tool.inputSchema
|
|
336
|
+
}
|
|
337
|
+
}));
|
|
338
|
+
if (toolChoice) {
|
|
339
|
+
body.tool_choice = typeof toolChoice === "string" ? toolChoice : { type: "function", function: { name: toolChoice.name } };
|
|
340
|
+
}
|
|
341
|
+
return body;
|
|
342
|
+
}
|
|
343
|
+
if (jsonSchema || !chatMessages && (systemPrompt.includes("JSON") || systemPrompt.includes("json"))) {
|
|
229
344
|
body.response_format = { type: "json_object" };
|
|
230
345
|
}
|
|
231
|
-
|
|
346
|
+
return body;
|
|
347
|
+
}
|
|
348
|
+
async post(body, signal) {
|
|
349
|
+
const res = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
232
350
|
method: "POST",
|
|
233
351
|
headers: {
|
|
234
352
|
"Content-Type": "application/json",
|
|
@@ -244,13 +362,21 @@ var OpenAiAdapter = class {
|
|
|
244
362
|
`OpenAI API error [${res.status} ${res.statusText}]: ${errText || "Unknown error"}`
|
|
245
363
|
);
|
|
246
364
|
}
|
|
365
|
+
return res;
|
|
366
|
+
}
|
|
367
|
+
async generate(params) {
|
|
368
|
+
const res = await this.post(this.buildBody(params), params.signal);
|
|
247
369
|
const data = await res.json();
|
|
248
|
-
const
|
|
249
|
-
|
|
370
|
+
const choice = data.choices?.[0];
|
|
371
|
+
const content = choice?.message?.content;
|
|
372
|
+
const toolCalls = parseToolCalls(choice?.message?.tool_calls);
|
|
373
|
+
if (!content && toolCalls.length === 0) {
|
|
250
374
|
throw new Error("OpenAI returned an empty message content");
|
|
251
375
|
}
|
|
252
376
|
return {
|
|
253
|
-
text: content,
|
|
377
|
+
text: content || "",
|
|
378
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
379
|
+
stopReason: mapStopReason(choice?.finish_reason),
|
|
254
380
|
usage: data.usage ? {
|
|
255
381
|
promptTokens: data.usage.prompt_tokens,
|
|
256
382
|
completionTokens: data.usage.completion_tokens
|
|
@@ -261,38 +387,23 @@ var OpenAiAdapter = class {
|
|
|
261
387
|
* Token-level streaming via OpenAI's `stream: true` chat completions mode (STORA-515).
|
|
262
388
|
* Yields each `delta.content` fragment as it arrives and returns the fully assembled
|
|
263
389
|
* text + usage (when the final chunk includes it) once the stream ends.
|
|
390
|
+
*
|
|
391
|
+
* Tool calls arrive fragmented too (STORA-530): `delta.tool_calls[].function.arguments`
|
|
392
|
+
* is delivered as a character stream across many chunks, keyed by array `index`. They're
|
|
393
|
+
* accumulated per index and parsed only once the stream ends — parsing a partial
|
|
394
|
+
* arguments string would always fail. Tool call deltas are never `yield`ed as text,
|
|
395
|
+
* since they aren't part of the assistant's visible message.
|
|
264
396
|
*/
|
|
265
397
|
async *generateStream(params) {
|
|
266
398
|
const { signal } = params;
|
|
267
|
-
const
|
|
268
|
-
const messages = this.buildMessages(params);
|
|
269
|
-
const body = {
|
|
270
|
-
model: this.model,
|
|
271
|
-
temperature: this.temperature,
|
|
272
|
-
messages,
|
|
273
|
-
stream: true
|
|
274
|
-
};
|
|
275
|
-
const res = await fetch(url, {
|
|
276
|
-
method: "POST",
|
|
277
|
-
headers: {
|
|
278
|
-
"Content-Type": "application/json",
|
|
279
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
280
|
-
...this.customHeaders
|
|
281
|
-
},
|
|
282
|
-
body: JSON.stringify(body),
|
|
283
|
-
signal
|
|
284
|
-
});
|
|
285
|
-
if (!res.ok) {
|
|
286
|
-
const errText = await res.text().catch(() => "");
|
|
287
|
-
throw new Error(
|
|
288
|
-
`OpenAI API error [${res.status} ${res.statusText}]: ${errText || "Unknown error"}`
|
|
289
|
-
);
|
|
290
|
-
}
|
|
399
|
+
const res = await this.post({ ...this.buildBody(params), stream: true }, signal);
|
|
291
400
|
if (!res.body) {
|
|
292
401
|
throw new Error("OpenAI streaming response has no readable body");
|
|
293
402
|
}
|
|
294
403
|
let text = "";
|
|
295
404
|
let usage;
|
|
405
|
+
let finishReason;
|
|
406
|
+
const toolCallAccumulator = /* @__PURE__ */ new Map();
|
|
296
407
|
for await (const raw of readSseDataLines(res.body, signal)) {
|
|
297
408
|
if (raw === "[DONE]") break;
|
|
298
409
|
let parsed;
|
|
@@ -301,11 +412,23 @@ var OpenAiAdapter = class {
|
|
|
301
412
|
} catch {
|
|
302
413
|
continue;
|
|
303
414
|
}
|
|
304
|
-
const
|
|
415
|
+
const choice = parsed.choices?.[0];
|
|
416
|
+
const delta = choice?.delta?.content;
|
|
305
417
|
if (typeof delta === "string" && delta.length > 0) {
|
|
306
418
|
text += delta;
|
|
307
419
|
yield delta;
|
|
308
420
|
}
|
|
421
|
+
for (const call of choice?.delta?.tool_calls ?? []) {
|
|
422
|
+
const index = typeof call.index === "number" ? call.index : 0;
|
|
423
|
+
const entry = toolCallAccumulator.get(index) ?? { id: "", name: "", arguments: "" };
|
|
424
|
+
if (call.id) entry.id = call.id;
|
|
425
|
+
if (call.function?.name) entry.name = call.function.name;
|
|
426
|
+
if (call.function?.arguments) entry.arguments += call.function.arguments;
|
|
427
|
+
toolCallAccumulator.set(index, entry);
|
|
428
|
+
}
|
|
429
|
+
if (choice?.finish_reason) {
|
|
430
|
+
finishReason = choice.finish_reason;
|
|
431
|
+
}
|
|
309
432
|
if (parsed.usage) {
|
|
310
433
|
usage = {
|
|
311
434
|
promptTokens: parsed.usage.prompt_tokens,
|
|
@@ -313,7 +436,19 @@ var OpenAiAdapter = class {
|
|
|
313
436
|
};
|
|
314
437
|
}
|
|
315
438
|
}
|
|
316
|
-
|
|
439
|
+
const toolCalls = parseToolCalls(
|
|
440
|
+
Array.from(toolCallAccumulator.entries()).sort(([a], [b]) => a - b).map(([, entry]) => ({
|
|
441
|
+
id: entry.id,
|
|
442
|
+
type: "function",
|
|
443
|
+
function: { name: entry.name, arguments: entry.arguments }
|
|
444
|
+
}))
|
|
445
|
+
);
|
|
446
|
+
return {
|
|
447
|
+
text,
|
|
448
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
449
|
+
stopReason: mapStopReason(finishReason),
|
|
450
|
+
usage
|
|
451
|
+
};
|
|
317
452
|
}
|
|
318
453
|
};
|
|
319
454
|
|
|
@@ -341,7 +476,7 @@ var AnthropicAdapter = class {
|
|
|
341
476
|
}
|
|
342
477
|
buildMessages(params) {
|
|
343
478
|
const { userPrompt, messages: chatMessages } = params;
|
|
344
|
-
return chatMessages && chatMessages.length > 0 ? chatMessages.filter((m) => m.role === "user" || m.role === "assistant").map((m) => ({ role: m.role, content: m
|
|
479
|
+
return chatMessages && chatMessages.length > 0 ? chatMessages.filter((m) => m.role === "user" || m.role === "assistant").map((m) => ({ role: m.role, content: getMessageText(m) })) : [
|
|
345
480
|
{
|
|
346
481
|
role: "user",
|
|
347
482
|
content: userPrompt
|
|
@@ -473,7 +608,7 @@ var CustomHttpAdapter = class {
|
|
|
473
608
|
this.formatRequestBody = options.formatRequestBody || ((params) => ({
|
|
474
609
|
messages: params.messages && params.messages.length > 0 ? [
|
|
475
610
|
{ role: "system", content: params.systemPrompt },
|
|
476
|
-
...params.messages.map((m) => ({ role: m.role, content: m
|
|
611
|
+
...params.messages.map((m) => ({ role: m.role, content: getMessageText(m) }))
|
|
477
612
|
] : [
|
|
478
613
|
{ role: "system", content: params.systemPrompt },
|
|
479
614
|
{ role: "user", content: params.userPrompt }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/server/adapters/index.ts","../../../src/server/adapters/stream-utils.ts","../../../src/server/adapters/gemini.ts","../../../src/server/adapters/openai.ts","../../../src/server/adapters/anthropic.ts","../../../src/server/adapters/custom.ts"],"sourcesContent":["export * from './base';\nexport * from './gemini';\nexport * from './openai';\nexport * from './anthropic';\nexport * from './custom';\n","/**\n * Shared SSE (server-sent events) frame reader for provider adapters that support\n * token-level streaming (STORA-515) — OpenAI, Anthropic and Gemini all expose their\n * streaming APIs as `data: <json>\\n\\n` frames, so every adapter reuses this single\n * low-level reader instead of re-implementing buffering/decoding three times.\n *\n * Yields the raw string payload of each `data:` line (already trimmed), in arrival\n * order. Callers are responsible for `JSON.parse`-ing (or ignoring sentinel values\n * like OpenAI's `[DONE]`) and for interpreting provider-specific event shapes.\n */\nexport async function* readSseDataLines(\n body: ReadableStream<Uint8Array>,\n signal?: AbortSignal,\n): AsyncGenerator<string, void, void> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n if (signal?.aborted) {\n throw new Error('Streaming aborted by client');\n }\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const frames = buffer.split('\\n\\n');\n buffer = frames.pop() || '';\n\n for (const frame of frames) {\n for (const line of frame.split('\\n')) {\n const trimmed = line.trim();\n if (trimmed.startsWith('data:')) {\n yield trimmed.slice(5).trim();\n }\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n","import type {\n AiProviderAdapter,\n AiProviderGenerateParams,\n AiProviderGenerateResult,\n} from '../../types';\nimport { readSseDataLines } from './stream-utils';\n\nexport interface GeminiAdapterOptions {\n apiKey: string;\n model?: string;\n temperature?: number;\n baseUrl?: string;\n}\n\nexport class GeminiAdapter implements AiProviderAdapter {\n readonly name = 'gemini';\n private apiKey: string;\n private model: string;\n private temperature: number;\n private baseUrl: string;\n\n constructor(options: GeminiAdapterOptions) {\n if (!options.apiKey) {\n throw new Error('GeminiAdapter requires an apiKey');\n }\n this.apiKey = options.apiKey;\n this.model = options.model || 'gemini-2.5-flash';\n this.temperature = options.temperature ?? 0.4;\n this.baseUrl =\n options.baseUrl || 'https://generativelanguage.googleapis.com/v1beta';\n }\n\n private buildContents(\n params: AiProviderGenerateParams,\n ): Array<{ role: string; parts: Array<{ text: string }> }> {\n const { userPrompt, messages: chatMessages } = params;\n return chatMessages && chatMessages.length > 0\n ? chatMessages.map((m) => ({\n role: m.role === 'assistant' ? 'model' : 'user',\n parts: [{ text: m.content }],\n }))\n : [\n {\n role: 'user',\n parts: [{ text: userPrompt }],\n },\n ];\n }\n\n async generate(params: AiProviderGenerateParams): Promise<AiProviderGenerateResult> {\n const { systemPrompt, messages: chatMessages, signal } = params;\n\n const url = `${this.baseUrl}/models/${encodeURIComponent(this.model)}:generateContent?key=${encodeURIComponent(this.apiKey)}`;\n const contents = this.buildContents(params);\n\n const generationConfig: Record<string, unknown> = {\n temperature: this.temperature,\n };\n\n if (params.jsonSchema || (!chatMessages && (systemPrompt.includes('JSON') || systemPrompt.includes('json')))) {\n generationConfig.responseMimeType = 'application/json';\n }\n\n const body: Record<string, unknown> = {\n system_instruction: {\n parts: [{ text: systemPrompt }],\n },\n contents,\n generationConfig,\n };\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `Gemini API error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n const data = (await res.json()) as {\n candidates?: Array<{\n content?: {\n parts?: Array<{ text?: string }>;\n };\n }>;\n usageMetadata?: {\n promptTokenCount?: number;\n candidatesTokenCount?: number;\n };\n };\n\n const candidateText = data.candidates?.[0]?.content?.parts?.[0]?.text;\n if (!candidateText) {\n throw new Error('Gemini returned an empty or invalid candidate response');\n }\n\n return {\n text: candidateText,\n usage: data.usageMetadata\n ? {\n promptTokens: data.usageMetadata.promptTokenCount,\n completionTokens: data.usageMetadata.candidatesTokenCount,\n }\n : undefined,\n };\n }\n\n /**\n * Token-level streaming via Gemini's `streamGenerateContent` SSE endpoint (STORA-515).\n */\n async *generateStream(\n params: AiProviderGenerateParams,\n ): AsyncGenerator<string, AiProviderGenerateResult, void> {\n const { systemPrompt, signal } = params;\n const url = `${this.baseUrl}/models/${encodeURIComponent(this.model)}:streamGenerateContent?alt=sse&key=${encodeURIComponent(this.apiKey)}`;\n const contents = this.buildContents(params);\n\n const generationConfig: Record<string, unknown> = {\n temperature: this.temperature,\n };\n\n const body: Record<string, unknown> = {\n system_instruction: {\n parts: [{ text: systemPrompt }],\n },\n contents,\n generationConfig,\n };\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `Gemini API error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n if (!res.body) {\n throw new Error('Gemini streaming response has no readable body');\n }\n\n let text = '';\n let usage: AiProviderGenerateResult['usage'];\n\n for await (const raw of readSseDataLines(res.body, signal)) {\n let parsed: {\n candidates?: Array<{ content?: { parts?: Array<{ text?: string }> } }>;\n usageMetadata?: { promptTokenCount?: number; candidatesTokenCount?: number };\n };\n try {\n parsed = JSON.parse(raw);\n } catch {\n continue;\n }\n\n const delta = parsed.candidates?.[0]?.content?.parts?.[0]?.text;\n if (typeof delta === 'string' && delta.length > 0) {\n text += delta;\n yield delta;\n }\n\n if (parsed.usageMetadata) {\n usage = {\n promptTokens: parsed.usageMetadata.promptTokenCount,\n completionTokens: parsed.usageMetadata.candidatesTokenCount,\n };\n }\n }\n\n return { text, usage };\n }\n}\n","import type {\n AiProviderAdapter,\n AiProviderGenerateParams,\n AiProviderGenerateResult,\n} from '../../types';\nimport { readSseDataLines } from './stream-utils';\n\nexport interface OpenAiAdapterOptions {\n apiKey: string;\n model?: string;\n temperature?: number;\n baseUrl?: string;\n headers?: Record<string, string>;\n}\n\nexport class OpenAiAdapter implements AiProviderAdapter {\n readonly name = 'openai';\n private apiKey: string;\n private model: string;\n private temperature: number;\n private baseUrl: string;\n private customHeaders?: Record<string, string>;\n\n constructor(options: OpenAiAdapterOptions) {\n if (!options.apiKey) {\n throw new Error('OpenAiAdapter requires an apiKey');\n }\n this.apiKey = options.apiKey;\n this.model = options.model || 'gpt-4o';\n this.temperature = options.temperature ?? 0.4;\n this.baseUrl = (options.baseUrl || 'https://api.openai.com/v1').replace(/\\/+$/, '');\n this.customHeaders = options.headers;\n }\n\n private buildMessages(params: AiProviderGenerateParams): Array<{ role: string; content: string }> {\n const { systemPrompt, userPrompt, messages: chatMessages } = params;\n return chatMessages && chatMessages.length > 0\n ? [\n { role: 'system', content: systemPrompt },\n ...chatMessages.map((m) => ({ role: m.role, content: m.content })),\n ]\n : [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt },\n ];\n }\n\n async generate(params: AiProviderGenerateParams): Promise<AiProviderGenerateResult> {\n const { systemPrompt, signal } = params;\n\n const url = `${this.baseUrl}/chat/completions`;\n const messages = this.buildMessages(params);\n const chatMessages = params.messages;\n\n const body: Record<string, unknown> = {\n model: this.model,\n temperature: this.temperature,\n messages,\n };\n\n // Only force json_object when JSON schema is provided or specifically requested,\n // allowing natural conversation text in chat mode.\n if (params.jsonSchema || (!chatMessages && (systemPrompt.includes('JSON') || systemPrompt.includes('json')))) {\n body.response_format = { type: 'json_object' };\n }\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n ...this.customHeaders,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `OpenAI API error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n const data = (await res.json()) as {\n choices?: Array<{\n message?: {\n content?: string;\n };\n }>;\n usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n };\n };\n\n const content = data.choices?.[0]?.message?.content;\n if (!content) {\n throw new Error('OpenAI returned an empty message content');\n }\n\n return {\n text: content,\n usage: data.usage\n ? {\n promptTokens: data.usage.prompt_tokens,\n completionTokens: data.usage.completion_tokens,\n }\n : undefined,\n };\n }\n\n /**\n * Token-level streaming via OpenAI's `stream: true` chat completions mode (STORA-515).\n * Yields each `delta.content` fragment as it arrives and returns the fully assembled\n * text + usage (when the final chunk includes it) once the stream ends.\n */\n async *generateStream(\n params: AiProviderGenerateParams,\n ): AsyncGenerator<string, AiProviderGenerateResult, void> {\n const { signal } = params;\n const url = `${this.baseUrl}/chat/completions`;\n const messages = this.buildMessages(params);\n\n const body: Record<string, unknown> = {\n model: this.model,\n temperature: this.temperature,\n messages,\n stream: true,\n };\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n ...this.customHeaders,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `OpenAI API error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n if (!res.body) {\n throw new Error('OpenAI streaming response has no readable body');\n }\n\n let text = '';\n let usage: AiProviderGenerateResult['usage'];\n\n for await (const raw of readSseDataLines(res.body, signal)) {\n if (raw === '[DONE]') break;\n\n let parsed: {\n choices?: Array<{ delta?: { content?: string } }>;\n usage?: { prompt_tokens?: number; completion_tokens?: number };\n };\n try {\n parsed = JSON.parse(raw);\n } catch {\n continue;\n }\n\n const delta = parsed.choices?.[0]?.delta?.content;\n if (typeof delta === 'string' && delta.length > 0) {\n text += delta;\n yield delta;\n }\n\n if (parsed.usage) {\n usage = {\n promptTokens: parsed.usage.prompt_tokens,\n completionTokens: parsed.usage.completion_tokens,\n };\n }\n }\n\n return { text, usage };\n }\n}\n","import type {\n AiProviderAdapter,\n AiProviderGenerateParams,\n AiProviderGenerateResult,\n} from '../../types';\nimport { readSseDataLines } from './stream-utils';\n\nexport interface AnthropicAdapterOptions {\n apiKey: string;\n model?: string;\n maxTokens?: number;\n temperature?: number;\n baseUrl?: string;\n anthropicVersion?: string;\n headers?: Record<string, string>;\n}\n\nexport class AnthropicAdapter implements AiProviderAdapter {\n readonly name = 'anthropic';\n private apiKey: string;\n private model: string;\n private maxTokens: number;\n private temperature: number;\n private baseUrl: string;\n private anthropicVersion: string;\n private customHeaders?: Record<string, string>;\n\n constructor(options: AnthropicAdapterOptions) {\n if (!options.apiKey) {\n throw new Error('AnthropicAdapter requires an apiKey');\n }\n this.apiKey = options.apiKey;\n this.model = options.model || 'claude-3-5-sonnet-20241022';\n this.maxTokens = options.maxTokens ?? 4096;\n this.temperature = options.temperature ?? 0.4;\n this.baseUrl = (options.baseUrl || 'https://api.anthropic.com/v1').replace(/\\/+$/, '');\n this.anthropicVersion = options.anthropicVersion || '2023-06-01';\n this.customHeaders = options.headers;\n }\n\n private buildMessages(\n params: AiProviderGenerateParams,\n ): Array<{ role: 'user' | 'assistant'; content: string }> {\n const { userPrompt, messages: chatMessages } = params;\n return chatMessages && chatMessages.length > 0\n ? chatMessages\n .filter((m) => m.role === 'user' || m.role === 'assistant')\n .map((m) => ({ role: m.role as 'user' | 'assistant', content: m.content }))\n : [\n {\n role: 'user' as const,\n content: userPrompt,\n },\n ];\n }\n\n async generate(params: AiProviderGenerateParams): Promise<AiProviderGenerateResult> {\n const { systemPrompt, signal } = params;\n\n const url = `${this.baseUrl}/messages`;\n const messages = this.buildMessages(params);\n\n const body: Record<string, unknown> = {\n model: this.model,\n max_tokens: this.maxTokens,\n temperature: this.temperature,\n system: systemPrompt,\n messages,\n };\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': this.anthropicVersion,\n ...this.customHeaders,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `Anthropic API error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n const data = (await res.json()) as {\n content?: Array<{\n type?: string;\n text?: string;\n }>;\n usage?: {\n input_tokens?: number;\n output_tokens?: number;\n };\n };\n\n const textPart = data.content?.find((c) => c.type === 'text')?.text;\n if (!textPart) {\n throw new Error('Anthropic returned an empty text content');\n }\n\n return {\n text: textPart,\n usage: data.usage\n ? {\n promptTokens: data.usage.input_tokens,\n completionTokens: data.usage.output_tokens,\n }\n : undefined,\n };\n }\n\n /**\n * Token-level streaming via Anthropic's `stream: true` Messages API (STORA-515).\n * Parses the `content_block_delta` (`text_delta`) events for incremental text and the\n * `message_start`/`message_delta` events for usage accounting.\n */\n async *generateStream(\n params: AiProviderGenerateParams,\n ): AsyncGenerator<string, AiProviderGenerateResult, void> {\n const { systemPrompt, signal } = params;\n const url = `${this.baseUrl}/messages`;\n const messages = this.buildMessages(params);\n\n const body: Record<string, unknown> = {\n model: this.model,\n max_tokens: this.maxTokens,\n temperature: this.temperature,\n system: systemPrompt,\n messages,\n stream: true,\n };\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': this.anthropicVersion,\n ...this.customHeaders,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `Anthropic API error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n if (!res.body) {\n throw new Error('Anthropic streaming response has no readable body');\n }\n\n let text = '';\n let promptTokens: number | undefined;\n let completionTokens: number | undefined;\n\n for await (const raw of readSseDataLines(res.body, signal)) {\n if (!raw) continue;\n\n let evt: {\n type?: string;\n delta?: { type?: string; text?: string };\n usage?: { output_tokens?: number };\n message?: { usage?: { input_tokens?: number } };\n };\n try {\n evt = JSON.parse(raw);\n } catch {\n continue;\n }\n\n if (evt.type === 'content_block_delta' && evt.delta?.type === 'text_delta') {\n const delta = evt.delta.text;\n if (typeof delta === 'string' && delta.length > 0) {\n text += delta;\n yield delta;\n }\n } else if (evt.type === 'message_start' && evt.message?.usage?.input_tokens !== undefined) {\n promptTokens = evt.message.usage.input_tokens;\n } else if (evt.type === 'message_delta' && evt.usage?.output_tokens !== undefined) {\n completionTokens = evt.usage.output_tokens;\n }\n }\n\n return {\n text,\n usage:\n promptTokens !== undefined || completionTokens !== undefined\n ? { promptTokens, completionTokens }\n : undefined,\n };\n }\n}\n","import type {\n AiProviderAdapter,\n AiProviderGenerateParams,\n AiProviderGenerateResult,\n} from '../../types';\n\nexport interface CustomHttpAdapterOptions {\n url: string;\n name?: string;\n headers?: Record<string, string>;\n formatRequestBody?: (params: AiProviderGenerateParams) => unknown;\n parseResponse?: (json: unknown) => string;\n}\n\nexport class CustomHttpAdapter implements AiProviderAdapter {\n readonly name: string;\n private url: string;\n private headers?: Record<string, string>;\n private formatRequestBody: (params: AiProviderGenerateParams) => unknown;\n private parseResponse: (json: unknown) => string;\n\n constructor(options: CustomHttpAdapterOptions) {\n if (!options.url) {\n throw new Error('CustomHttpAdapter requires a url');\n }\n this.url = options.url;\n this.name = options.name || 'custom-http';\n this.headers = options.headers;\n\n // Default body formatter (OpenAI compatible or Ollama chat compatible)\n this.formatRequestBody =\n options.formatRequestBody ||\n ((params) => ({\n messages:\n params.messages && params.messages.length > 0\n ? [\n { role: 'system', content: params.systemPrompt },\n ...params.messages.map((m) => ({ role: m.role, content: m.content })),\n ]\n : [\n { role: 'system', content: params.systemPrompt },\n { role: 'user', content: params.userPrompt },\n ],\n }));\n\n // Default response parser\n this.parseResponse =\n options.parseResponse ||\n ((json: unknown) => {\n if (!json || typeof json !== 'object') {\n throw new Error('Response is not a valid JSON object');\n }\n const obj = json as Record<string, unknown>;\n if (typeof obj.response === 'string') return obj.response; // Ollama generate\n if (typeof obj.text === 'string') return obj.text;\n const choices = obj.choices as Array<{ message?: { content?: string } }>;\n if (Array.isArray(choices) && choices[0]?.message?.content) {\n return choices[0].message.content; // OpenAI format\n }\n const message = obj.message as { content?: string };\n if (message && typeof message.content === 'string') {\n return message.content; // Ollama chat format\n }\n throw new Error('Unable to extract generated text from custom HTTP response');\n });\n }\n\n async generate(params: AiProviderGenerateParams): Promise<AiProviderGenerateResult> {\n const body = this.formatRequestBody(params);\n\n const res = await fetch(this.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...this.headers,\n },\n body: JSON.stringify(body),\n signal: params.signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `Custom HTTP adapter error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n const json = await res.json();\n const text = this.parseResponse(json);\n\n return { text };\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/server/adapters/index.ts","../../../src/server/adapters/stream-utils.ts","../../../src/core/messages.ts","../../../src/server/adapters/gemini.ts","../../../src/server/adapters/openai.ts","../../../src/server/adapters/anthropic.ts","../../../src/server/adapters/custom.ts"],"sourcesContent":["export * from './base';\nexport * from './gemini';\nexport * from './openai';\nexport * from './anthropic';\nexport * from './custom';\n","/**\n * Shared SSE (server-sent events) frame reader for provider adapters that support\n * token-level streaming (STORA-515) — OpenAI, Anthropic and Gemini all expose their\n * streaming APIs as `data: <json>\\n\\n` frames, so every adapter reuses this single\n * low-level reader instead of re-implementing buffering/decoding three times.\n *\n * Yields the raw string payload of each `data:` line (already trimmed), in arrival\n * order. Callers are responsible for `JSON.parse`-ing (or ignoring sentinel values\n * like OpenAI's `[DONE]`) and for interpreting provider-specific event shapes.\n */\nexport async function* readSseDataLines(\n body: ReadableStream<Uint8Array>,\n signal?: AbortSignal,\n): AsyncGenerator<string, void, void> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n if (signal?.aborted) {\n throw new Error('Streaming aborted by client');\n }\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const frames = buffer.split('\\n\\n');\n buffer = frames.pop() || '';\n\n for (const frame of frames) {\n for (const line of frame.split('\\n')) {\n const trimmed = line.trim();\n if (trimmed.startsWith('data:')) {\n yield trimmed.slice(5).trim();\n }\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n","import type { AiChatMessage, AiContentBlock, AiToolUseBlock, AiToolResultBlock } from '../types';\n\n/**\n * Flattens a message's content to plain text (STORA-530).\n *\n * `AiChatMessage.content` became `string | AiContentBlock[]` when tool calling landed;\n * every place that previously assumed a bare string (adapter message mapping, the chat\n * prompt's \"last user message\" lookup, chat UIs) goes through this helper instead of\n * re-implementing the narrowing. Tool-use/tool-result blocks contribute nothing to the\n * text rendering — they're transported separately by the adapters.\n */\nexport function getMessageText(message: Pick<AiChatMessage, 'content'>): string {\n if (typeof message.content === 'string') return message.content;\n return message.content\n .filter((block): block is Extract<AiContentBlock, { type: 'text' }> => block.type === 'text')\n .map((block) => block.text)\n .join('\\n');\n}\n\n/** All `tool_use` blocks carried by a message, in order. Empty for plain-text messages. */\nexport function getToolUseBlocks(message: Pick<AiChatMessage, 'content'>): AiToolUseBlock[] {\n if (typeof message.content === 'string') return [];\n return message.content.filter((b): b is AiToolUseBlock => b.type === 'tool_use');\n}\n\n/** All `tool_result` blocks carried by a message, in order. */\nexport function getToolResultBlocks(message: Pick<AiChatMessage, 'content'>): AiToolResultBlock[] {\n if (typeof message.content === 'string') return [];\n return message.content.filter((b): b is AiToolResultBlock => b.type === 'tool_result');\n}\n\n/** True when the message carries any non-text block — i.e. adapters must map it structurally. */\nexport function hasStructuredContent(message: Pick<AiChatMessage, 'content'>): boolean {\n return typeof message.content !== 'string' && message.content.some((b) => b.type !== 'text');\n}\n","import type {\n AiProviderAdapter,\n AiProviderGenerateParams,\n AiProviderGenerateResult,\n} from '../../types';\nimport { readSseDataLines } from './stream-utils';\nimport { getMessageText } from '../../core/messages';\n\nexport interface GeminiAdapterOptions {\n apiKey: string;\n model?: string;\n temperature?: number;\n baseUrl?: string;\n}\n\nexport class GeminiAdapter implements AiProviderAdapter {\n readonly name = 'gemini';\n private apiKey: string;\n private model: string;\n private temperature: number;\n private baseUrl: string;\n\n constructor(options: GeminiAdapterOptions) {\n if (!options.apiKey) {\n throw new Error('GeminiAdapter requires an apiKey');\n }\n this.apiKey = options.apiKey;\n this.model = options.model || 'gemini-2.5-flash';\n this.temperature = options.temperature ?? 0.4;\n this.baseUrl =\n options.baseUrl || 'https://generativelanguage.googleapis.com/v1beta';\n }\n\n private buildContents(\n params: AiProviderGenerateParams,\n ): Array<{ role: string; parts: Array<{ text: string }> }> {\n const { userPrompt, messages: chatMessages } = params;\n return chatMessages && chatMessages.length > 0\n ? chatMessages.map((m) => ({\n role: m.role === 'assistant' ? 'model' : 'user',\n parts: [{ text: getMessageText(m) }],\n }))\n : [\n {\n role: 'user',\n parts: [{ text: userPrompt }],\n },\n ];\n }\n\n async generate(params: AiProviderGenerateParams): Promise<AiProviderGenerateResult> {\n const { systemPrompt, messages: chatMessages, signal } = params;\n\n const url = `${this.baseUrl}/models/${encodeURIComponent(this.model)}:generateContent?key=${encodeURIComponent(this.apiKey)}`;\n const contents = this.buildContents(params);\n\n const generationConfig: Record<string, unknown> = {\n temperature: this.temperature,\n };\n\n if (params.jsonSchema || (!chatMessages && (systemPrompt.includes('JSON') || systemPrompt.includes('json')))) {\n generationConfig.responseMimeType = 'application/json';\n }\n\n const body: Record<string, unknown> = {\n system_instruction: {\n parts: [{ text: systemPrompt }],\n },\n contents,\n generationConfig,\n };\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `Gemini API error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n const data = (await res.json()) as {\n candidates?: Array<{\n content?: {\n parts?: Array<{ text?: string }>;\n };\n }>;\n usageMetadata?: {\n promptTokenCount?: number;\n candidatesTokenCount?: number;\n };\n };\n\n const candidateText = data.candidates?.[0]?.content?.parts?.[0]?.text;\n if (!candidateText) {\n throw new Error('Gemini returned an empty or invalid candidate response');\n }\n\n return {\n text: candidateText,\n usage: data.usageMetadata\n ? {\n promptTokens: data.usageMetadata.promptTokenCount,\n completionTokens: data.usageMetadata.candidatesTokenCount,\n }\n : undefined,\n };\n }\n\n /**\n * Token-level streaming via Gemini's `streamGenerateContent` SSE endpoint (STORA-515).\n */\n async *generateStream(\n params: AiProviderGenerateParams,\n ): AsyncGenerator<string, AiProviderGenerateResult, void> {\n const { systemPrompt, signal } = params;\n const url = `${this.baseUrl}/models/${encodeURIComponent(this.model)}:streamGenerateContent?alt=sse&key=${encodeURIComponent(this.apiKey)}`;\n const contents = this.buildContents(params);\n\n const generationConfig: Record<string, unknown> = {\n temperature: this.temperature,\n };\n\n const body: Record<string, unknown> = {\n system_instruction: {\n parts: [{ text: systemPrompt }],\n },\n contents,\n generationConfig,\n };\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `Gemini API error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n if (!res.body) {\n throw new Error('Gemini streaming response has no readable body');\n }\n\n let text = '';\n let usage: AiProviderGenerateResult['usage'];\n\n for await (const raw of readSseDataLines(res.body, signal)) {\n let parsed: {\n candidates?: Array<{ content?: { parts?: Array<{ text?: string }> } }>;\n usageMetadata?: { promptTokenCount?: number; candidatesTokenCount?: number };\n };\n try {\n parsed = JSON.parse(raw);\n } catch {\n continue;\n }\n\n const delta = parsed.candidates?.[0]?.content?.parts?.[0]?.text;\n if (typeof delta === 'string' && delta.length > 0) {\n text += delta;\n yield delta;\n }\n\n if (parsed.usageMetadata) {\n usage = {\n promptTokens: parsed.usageMetadata.promptTokenCount,\n completionTokens: parsed.usageMetadata.candidatesTokenCount,\n };\n }\n }\n\n return { text, usage };\n }\n}\n","import type {\n AiChatMessage,\n AiProviderAdapter,\n AiProviderGenerateParams,\n AiProviderGenerateResult,\n AiStopReason,\n AiToolUseBlock,\n} from '../../types';\nimport { getMessageText, getToolResultBlocks, getToolUseBlocks } from '../../core/messages';\nimport { readSseDataLines } from './stream-utils';\n\nexport interface OpenAiAdapterOptions {\n apiKey: string;\n model?: string;\n temperature?: number;\n baseUrl?: string;\n headers?: Record<string, string>;\n}\n\n/** OpenAI Chat Completions message shape — `tool_calls`/`role: 'tool'` included. */\ninterface OpenAiMessage {\n role: 'system' | 'user' | 'assistant' | 'tool';\n content: string | null;\n tool_calls?: Array<{\n id: string;\n type: 'function';\n function: { name: string; arguments: string };\n }>;\n tool_call_id?: string;\n}\n\ninterface OpenAiToolCallPayload {\n id?: string;\n type?: string;\n function?: { name?: string; arguments?: string };\n}\n\n/**\n * Maps OpenAI's `finish_reason` onto the provider-agnostic `AiStopReason` the agent loop\n * branches on (STORA-530).\n */\nfunction mapStopReason(finishReason: string | undefined | null): AiStopReason {\n switch (finishReason) {\n case 'stop':\n return 'stop';\n case 'tool_calls':\n case 'function_call':\n return 'tool_calls';\n case 'length':\n return 'length';\n case 'content_filter':\n return 'content_filter';\n default:\n return 'unknown';\n }\n}\n\n/**\n * Parses OpenAI `tool_calls` into `AiToolUseBlock`s. A call whose `arguments` isn't valid\n * JSON is surfaced with an empty `input` rather than throwing: the agent's tool executor\n * then rejects it with a `tool_result` error and the model self-corrects on the next step,\n * which is far better than killing the whole run over one malformed argument blob.\n */\nfunction parseToolCalls(raw: OpenAiToolCallPayload[] | undefined): AiToolUseBlock[] {\n if (!Array.isArray(raw) || raw.length === 0) return [];\n return raw\n .filter((call) => call?.function?.name)\n .map((call, index) => {\n let input: Record<string, unknown> = {};\n const argsText = call.function?.arguments;\n if (argsText && argsText.trim()) {\n try {\n const parsed = JSON.parse(argsText);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n input = parsed as Record<string, unknown>;\n }\n } catch {\n input = {};\n }\n }\n return {\n type: 'tool_use' as const,\n id: call.id || `call_${index}`,\n name: call.function!.name!,\n input,\n };\n });\n}\n\nexport class OpenAiAdapter implements AiProviderAdapter {\n readonly name = 'openai';\n readonly supportsTools = true;\n private apiKey: string;\n private model: string;\n private temperature: number;\n private baseUrl: string;\n private customHeaders?: Record<string, string>;\n\n constructor(options: OpenAiAdapterOptions) {\n if (!options.apiKey) {\n throw new Error('OpenAiAdapter requires an apiKey');\n }\n this.apiKey = options.apiKey;\n this.model = options.model || 'gpt-4o';\n this.temperature = options.temperature ?? 0.4;\n this.baseUrl = (options.baseUrl || 'https://api.openai.com/v1').replace(/\\/+$/, '');\n this.customHeaders = options.headers;\n }\n\n /**\n * Expands one `AiChatMessage` into the one-or-more OpenAI messages it corresponds to\n * (STORA-530).\n *\n * The block model is Anthropic-shaped — tool results ride along inside a `user` message —\n * while OpenAI wants each result as its own `role: 'tool'` message keyed by\n * `tool_call_id`. That fan-out happens here so the rest of the stack stays\n * provider-agnostic.\n */\n private mapMessage(message: AiChatMessage): OpenAiMessage[] {\n const toolUses = getToolUseBlocks(message);\n const toolResults = getToolResultBlocks(message);\n const text = getMessageText(message);\n\n if (toolResults.length > 0) {\n const results: OpenAiMessage[] = toolResults.map((block) => ({\n role: 'tool',\n content: block.content,\n tool_call_id: block.toolUseId,\n }));\n // Any free text riding alongside tool results becomes a separate user turn — OpenAI\n // rejects a `role: 'tool'` message that carries anything but the result payload.\n if (text.trim()) {\n results.push({ role: 'user', content: text });\n }\n return results;\n }\n\n if (toolUses.length > 0) {\n return [\n {\n role: 'assistant',\n content: text.trim() ? text : null,\n tool_calls: toolUses.map((block) => ({\n id: block.id,\n type: 'function' as const,\n function: {\n name: block.name,\n arguments: JSON.stringify(block.input ?? {}),\n },\n })),\n },\n ];\n }\n\n return [{ role: message.role, content: text }];\n }\n\n private buildMessages(params: AiProviderGenerateParams): OpenAiMessage[] {\n const { systemPrompt, userPrompt, messages: chatMessages } = params;\n if (chatMessages && chatMessages.length > 0) {\n return [\n { role: 'system', content: systemPrompt },\n ...chatMessages.flatMap((m) => this.mapMessage(m)),\n ];\n }\n return [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt },\n ];\n }\n\n /**\n * Shared request body builder for `generate`/`generateStream` — notably the\n * `response_format` decision, which must never fire in tool mode: OpenAI rejects\n * `json_object` combined with `tools`, and a tool-calling turn's text output is a\n * natural-language summary, not JSON.\n */\n private buildBody(params: AiProviderGenerateParams): Record<string, unknown> {\n const { systemPrompt, jsonSchema, messages: chatMessages, tools, toolChoice } = params;\n\n const body: Record<string, unknown> = {\n model: this.model,\n temperature: params.temperature ?? this.temperature,\n messages: this.buildMessages(params),\n };\n\n if (tools && tools.length > 0) {\n body.tools = tools.map((tool) => ({\n type: 'function',\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.inputSchema,\n },\n }));\n if (toolChoice) {\n body.tool_choice =\n typeof toolChoice === 'string'\n ? toolChoice\n : { type: 'function', function: { name: toolChoice.name } };\n }\n return body;\n }\n\n // Only force json_object when JSON schema is provided or specifically requested,\n // allowing natural conversation text in chat mode.\n if (jsonSchema || (!chatMessages && (systemPrompt.includes('JSON') || systemPrompt.includes('json')))) {\n body.response_format = { type: 'json_object' };\n }\n\n return body;\n }\n\n private async post(body: Record<string, unknown>, signal?: AbortSignal): Promise<Response> {\n const res = await fetch(`${this.baseUrl}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n ...this.customHeaders,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `OpenAI API error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n return res;\n }\n\n async generate(params: AiProviderGenerateParams): Promise<AiProviderGenerateResult> {\n const res = await this.post(this.buildBody(params), params.signal);\n\n const data = (await res.json()) as {\n choices?: Array<{\n finish_reason?: string;\n message?: {\n content?: string;\n tool_calls?: OpenAiToolCallPayload[];\n };\n }>;\n usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n };\n };\n\n const choice = data.choices?.[0];\n const content = choice?.message?.content;\n const toolCalls = parseToolCalls(choice?.message?.tool_calls);\n\n // A tool-calling turn legitimately has null content — only a turn with neither text\n // nor tool calls is an empty response worth throwing over.\n if (!content && toolCalls.length === 0) {\n throw new Error('OpenAI returned an empty message content');\n }\n\n return {\n text: content || '',\n toolCalls: toolCalls.length > 0 ? toolCalls : undefined,\n stopReason: mapStopReason(choice?.finish_reason),\n usage: data.usage\n ? {\n promptTokens: data.usage.prompt_tokens,\n completionTokens: data.usage.completion_tokens,\n }\n : undefined,\n };\n }\n\n /**\n * Token-level streaming via OpenAI's `stream: true` chat completions mode (STORA-515).\n * Yields each `delta.content` fragment as it arrives and returns the fully assembled\n * text + usage (when the final chunk includes it) once the stream ends.\n *\n * Tool calls arrive fragmented too (STORA-530): `delta.tool_calls[].function.arguments`\n * is delivered as a character stream across many chunks, keyed by array `index`. They're\n * accumulated per index and parsed only once the stream ends — parsing a partial\n * arguments string would always fail. Tool call deltas are never `yield`ed as text,\n * since they aren't part of the assistant's visible message.\n */\n async *generateStream(\n params: AiProviderGenerateParams,\n ): AsyncGenerator<string, AiProviderGenerateResult, void> {\n const { signal } = params;\n const res = await this.post({ ...this.buildBody(params), stream: true }, signal);\n\n if (!res.body) {\n throw new Error('OpenAI streaming response has no readable body');\n }\n\n let text = '';\n let usage: AiProviderGenerateResult['usage'];\n let finishReason: string | undefined;\n const toolCallAccumulator = new Map<number, { id: string; name: string; arguments: string }>();\n\n for await (const raw of readSseDataLines(res.body, signal)) {\n if (raw === '[DONE]') break;\n\n let parsed: {\n choices?: Array<{\n finish_reason?: string;\n delta?: {\n content?: string;\n tool_calls?: Array<OpenAiToolCallPayload & { index?: number }>;\n };\n }>;\n usage?: { prompt_tokens?: number; completion_tokens?: number };\n };\n try {\n parsed = JSON.parse(raw);\n } catch {\n continue;\n }\n\n const choice = parsed.choices?.[0];\n\n const delta = choice?.delta?.content;\n if (typeof delta === 'string' && delta.length > 0) {\n text += delta;\n yield delta;\n }\n\n for (const call of choice?.delta?.tool_calls ?? []) {\n const index = typeof call.index === 'number' ? call.index : 0;\n const entry = toolCallAccumulator.get(index) ?? { id: '', name: '', arguments: '' };\n if (call.id) entry.id = call.id;\n if (call.function?.name) entry.name = call.function.name;\n if (call.function?.arguments) entry.arguments += call.function.arguments;\n toolCallAccumulator.set(index, entry);\n }\n\n if (choice?.finish_reason) {\n finishReason = choice.finish_reason;\n }\n\n if (parsed.usage) {\n usage = {\n promptTokens: parsed.usage.prompt_tokens,\n completionTokens: parsed.usage.completion_tokens,\n };\n }\n }\n\n const toolCalls = parseToolCalls(\n Array.from(toolCallAccumulator.entries())\n .sort(([a], [b]) => a - b)\n .map(([, entry]) => ({\n id: entry.id,\n type: 'function',\n function: { name: entry.name, arguments: entry.arguments },\n })),\n );\n\n return {\n text,\n toolCalls: toolCalls.length > 0 ? toolCalls : undefined,\n stopReason: mapStopReason(finishReason),\n usage,\n };\n }\n}\n","import type {\n AiProviderAdapter,\n AiProviderGenerateParams,\n AiProviderGenerateResult,\n} from '../../types';\nimport { readSseDataLines } from './stream-utils';\nimport { getMessageText } from '../../core/messages';\n\nexport interface AnthropicAdapterOptions {\n apiKey: string;\n model?: string;\n maxTokens?: number;\n temperature?: number;\n baseUrl?: string;\n anthropicVersion?: string;\n headers?: Record<string, string>;\n}\n\nexport class AnthropicAdapter implements AiProviderAdapter {\n readonly name = 'anthropic';\n private apiKey: string;\n private model: string;\n private maxTokens: number;\n private temperature: number;\n private baseUrl: string;\n private anthropicVersion: string;\n private customHeaders?: Record<string, string>;\n\n constructor(options: AnthropicAdapterOptions) {\n if (!options.apiKey) {\n throw new Error('AnthropicAdapter requires an apiKey');\n }\n this.apiKey = options.apiKey;\n this.model = options.model || 'claude-3-5-sonnet-20241022';\n this.maxTokens = options.maxTokens ?? 4096;\n this.temperature = options.temperature ?? 0.4;\n this.baseUrl = (options.baseUrl || 'https://api.anthropic.com/v1').replace(/\\/+$/, '');\n this.anthropicVersion = options.anthropicVersion || '2023-06-01';\n this.customHeaders = options.headers;\n }\n\n private buildMessages(\n params: AiProviderGenerateParams,\n ): Array<{ role: 'user' | 'assistant'; content: string }> {\n const { userPrompt, messages: chatMessages } = params;\n return chatMessages && chatMessages.length > 0\n ? chatMessages\n .filter((m) => m.role === 'user' || m.role === 'assistant')\n .map((m) => ({ role: m.role as 'user' | 'assistant', content: getMessageText(m) }))\n : [\n {\n role: 'user' as const,\n content: userPrompt,\n },\n ];\n }\n\n async generate(params: AiProviderGenerateParams): Promise<AiProviderGenerateResult> {\n const { systemPrompt, signal } = params;\n\n const url = `${this.baseUrl}/messages`;\n const messages = this.buildMessages(params);\n\n const body: Record<string, unknown> = {\n model: this.model,\n max_tokens: this.maxTokens,\n temperature: this.temperature,\n system: systemPrompt,\n messages,\n };\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': this.anthropicVersion,\n ...this.customHeaders,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `Anthropic API error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n const data = (await res.json()) as {\n content?: Array<{\n type?: string;\n text?: string;\n }>;\n usage?: {\n input_tokens?: number;\n output_tokens?: number;\n };\n };\n\n const textPart = data.content?.find((c) => c.type === 'text')?.text;\n if (!textPart) {\n throw new Error('Anthropic returned an empty text content');\n }\n\n return {\n text: textPart,\n usage: data.usage\n ? {\n promptTokens: data.usage.input_tokens,\n completionTokens: data.usage.output_tokens,\n }\n : undefined,\n };\n }\n\n /**\n * Token-level streaming via Anthropic's `stream: true` Messages API (STORA-515).\n * Parses the `content_block_delta` (`text_delta`) events for incremental text and the\n * `message_start`/`message_delta` events for usage accounting.\n */\n async *generateStream(\n params: AiProviderGenerateParams,\n ): AsyncGenerator<string, AiProviderGenerateResult, void> {\n const { systemPrompt, signal } = params;\n const url = `${this.baseUrl}/messages`;\n const messages = this.buildMessages(params);\n\n const body: Record<string, unknown> = {\n model: this.model,\n max_tokens: this.maxTokens,\n temperature: this.temperature,\n system: systemPrompt,\n messages,\n stream: true,\n };\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': this.anthropicVersion,\n ...this.customHeaders,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `Anthropic API error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n if (!res.body) {\n throw new Error('Anthropic streaming response has no readable body');\n }\n\n let text = '';\n let promptTokens: number | undefined;\n let completionTokens: number | undefined;\n\n for await (const raw of readSseDataLines(res.body, signal)) {\n if (!raw) continue;\n\n let evt: {\n type?: string;\n delta?: { type?: string; text?: string };\n usage?: { output_tokens?: number };\n message?: { usage?: { input_tokens?: number } };\n };\n try {\n evt = JSON.parse(raw);\n } catch {\n continue;\n }\n\n if (evt.type === 'content_block_delta' && evt.delta?.type === 'text_delta') {\n const delta = evt.delta.text;\n if (typeof delta === 'string' && delta.length > 0) {\n text += delta;\n yield delta;\n }\n } else if (evt.type === 'message_start' && evt.message?.usage?.input_tokens !== undefined) {\n promptTokens = evt.message.usage.input_tokens;\n } else if (evt.type === 'message_delta' && evt.usage?.output_tokens !== undefined) {\n completionTokens = evt.usage.output_tokens;\n }\n }\n\n return {\n text,\n usage:\n promptTokens !== undefined || completionTokens !== undefined\n ? { promptTokens, completionTokens }\n : undefined,\n };\n }\n}\n","import type {\n AiProviderAdapter,\n AiProviderGenerateParams,\n AiProviderGenerateResult,\n} from '../../types';\nimport { getMessageText } from '../../core/messages';\n\nexport interface CustomHttpAdapterOptions {\n url: string;\n name?: string;\n headers?: Record<string, string>;\n formatRequestBody?: (params: AiProviderGenerateParams) => unknown;\n parseResponse?: (json: unknown) => string;\n}\n\nexport class CustomHttpAdapter implements AiProviderAdapter {\n readonly name: string;\n private url: string;\n private headers?: Record<string, string>;\n private formatRequestBody: (params: AiProviderGenerateParams) => unknown;\n private parseResponse: (json: unknown) => string;\n\n constructor(options: CustomHttpAdapterOptions) {\n if (!options.url) {\n throw new Error('CustomHttpAdapter requires a url');\n }\n this.url = options.url;\n this.name = options.name || 'custom-http';\n this.headers = options.headers;\n\n // Default body formatter (OpenAI compatible or Ollama chat compatible)\n this.formatRequestBody =\n options.formatRequestBody ||\n ((params) => ({\n messages:\n params.messages && params.messages.length > 0\n ? [\n { role: 'system', content: params.systemPrompt },\n ...params.messages.map((m) => ({ role: m.role, content: getMessageText(m) })),\n ]\n : [\n { role: 'system', content: params.systemPrompt },\n { role: 'user', content: params.userPrompt },\n ],\n }));\n\n // Default response parser\n this.parseResponse =\n options.parseResponse ||\n ((json: unknown) => {\n if (!json || typeof json !== 'object') {\n throw new Error('Response is not a valid JSON object');\n }\n const obj = json as Record<string, unknown>;\n if (typeof obj.response === 'string') return obj.response; // Ollama generate\n if (typeof obj.text === 'string') return obj.text;\n const choices = obj.choices as Array<{ message?: { content?: string } }>;\n if (Array.isArray(choices) && choices[0]?.message?.content) {\n return choices[0].message.content; // OpenAI format\n }\n const message = obj.message as { content?: string };\n if (message && typeof message.content === 'string') {\n return message.content; // Ollama chat format\n }\n throw new Error('Unable to extract generated text from custom HTTP response');\n });\n }\n\n async generate(params: AiProviderGenerateParams): Promise<AiProviderGenerateResult> {\n const body = this.formatRequestBody(params);\n\n const res = await fetch(this.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...this.headers,\n },\n body: JSON.stringify(body),\n signal: params.signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `Custom HTTP adapter error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n const json = await res.json();\n const text = this.parseResponse(json);\n\n return { text };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACUA,gBAAuB,iBACrB,MACA,QACoC;AACpC,QAAM,SAAS,KAAK,UAAU;AAC9B,QAAM,UAAU,IAAI,YAAY;AAChC,MAAI,SAAS;AAEb,MAAI;AACF,WAAO,MAAM;AACX,UAAI,QAAQ,SAAS;AACnB,cAAM,IAAI,MAAM,6BAA6B;AAAA,MAC/C;AAEA,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,KAAM;AAEV,gBAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAChD,YAAM,SAAS,OAAO,MAAM,MAAM;AAClC,eAAS,OAAO,IAAI,KAAK;AAEzB,iBAAW,SAAS,QAAQ;AAC1B,mBAAW,QAAQ,MAAM,MAAM,IAAI,GAAG;AACpC,gBAAM,UAAU,KAAK,KAAK;AAC1B,cAAI,QAAQ,WAAW,OAAO,GAAG;AAC/B,kBAAM,QAAQ,MAAM,CAAC,EAAE,KAAK;AAAA,UAC9B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,UAAE;AACA,WAAO,YAAY;AAAA,EACrB;AACF;;;AChCO,SAAS,eAAe,SAAiD;AAC9E,MAAI,OAAO,QAAQ,YAAY,SAAU,QAAO,QAAQ;AACxD,SAAO,QAAQ,QACZ,OAAO,CAAC,UAA8D,MAAM,SAAS,MAAM,EAC3F,IAAI,CAAC,UAAU,MAAM,IAAI,EACzB,KAAK,IAAI;AACd;AAGO,SAAS,iBAAiB,SAA2D;AAC1F,MAAI,OAAO,QAAQ,YAAY,SAAU,QAAO,CAAC;AACjD,SAAO,QAAQ,QAAQ,OAAO,CAAC,MAA2B,EAAE,SAAS,UAAU;AACjF;AAGO,SAAS,oBAAoB,SAA8D;AAChG,MAAI,OAAO,QAAQ,YAAY,SAAU,QAAO,CAAC;AACjD,SAAO,QAAQ,QAAQ,OAAO,CAAC,MAA8B,EAAE,SAAS,aAAa;AACvF;;;ACdO,IAAM,gBAAN,MAAiD;AAAA,EAC7C,OAAO;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,SAA+B;AACzC,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AACA,SAAK,SAAS,QAAQ;AACtB,SAAK,QAAQ,QAAQ,SAAS;AAC9B,SAAK,cAAc,QAAQ,eAAe;AAC1C,SAAK,UACH,QAAQ,WAAW;AAAA,EACvB;AAAA,EAEQ,cACN,QACyD;AACzD,UAAM,EAAE,YAAY,UAAU,aAAa,IAAI;AAC/C,WAAO,gBAAgB,aAAa,SAAS,IACzC,aAAa,IAAI,CAAC,OAAO;AAAA,MACvB,MAAM,EAAE,SAAS,cAAc,UAAU;AAAA,MACzC,OAAO,CAAC,EAAE,MAAM,eAAe,CAAC,EAAE,CAAC;AAAA,IACrC,EAAE,IACF;AAAA,MACE;AAAA,QACE,MAAM;AAAA,QACN,OAAO,CAAC,EAAE,MAAM,WAAW,CAAC;AAAA,MAC9B;AAAA,IACF;AAAA,EACN;AAAA,EAEA,MAAM,SAAS,QAAqE;AAClF,UAAM,EAAE,cAAc,UAAU,cAAc,OAAO,IAAI;AAEzD,UAAM,MAAM,GAAG,KAAK,OAAO,WAAW,mBAAmB,KAAK,KAAK,CAAC,wBAAwB,mBAAmB,KAAK,MAAM,CAAC;AAC3H,UAAM,WAAW,KAAK,cAAc,MAAM;AAE1C,UAAM,mBAA4C;AAAA,MAChD,aAAa,KAAK;AAAA,IACpB;AAEA,QAAI,OAAO,cAAe,CAAC,iBAAiB,aAAa,SAAS,MAAM,KAAK,aAAa,SAAS,MAAM,IAAK;AAC5G,uBAAiB,mBAAmB;AAAA,IACtC;AAEA,UAAM,OAAgC;AAAA,MACpC,oBAAoB;AAAA,QAClB,OAAO,CAAC,EAAE,MAAM,aAAa,CAAC;AAAA,MAChC;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB;AAAA,IACF,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,UAAU,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC/C,YAAM,IAAI;AAAA,QACR,qBAAqB,IAAI,MAAM,IAAI,IAAI,UAAU,MAAM,WAAW,eAAe;AAAA,MACnF;AAAA,IACF;AAEA,UAAM,OAAQ,MAAM,IAAI,KAAK;AAY7B,UAAM,gBAAgB,KAAK,aAAa,CAAC,GAAG,SAAS,QAAQ,CAAC,GAAG;AACjE,QAAI,CAAC,eAAe;AAClB,YAAM,IAAI,MAAM,wDAAwD;AAAA,IAC1E;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO,KAAK,gBACR;AAAA,QACE,cAAc,KAAK,cAAc;AAAA,QACjC,kBAAkB,KAAK,cAAc;AAAA,MACvC,IACA;AAAA,IACN;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,eACL,QACwD;AACxD,UAAM,EAAE,cAAc,OAAO,IAAI;AACjC,UAAM,MAAM,GAAG,KAAK,OAAO,WAAW,mBAAmB,KAAK,KAAK,CAAC,sCAAsC,mBAAmB,KAAK,MAAM,CAAC;AACzI,UAAM,WAAW,KAAK,cAAc,MAAM;AAE1C,UAAM,mBAA4C;AAAA,MAChD,aAAa,KAAK;AAAA,IACpB;AAEA,UAAM,OAAgC;AAAA,MACpC,oBAAoB;AAAA,QAClB,OAAO,CAAC,EAAE,MAAM,aAAa,CAAC;AAAA,MAChC;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB;AAAA,IACF,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,UAAU,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC/C,YAAM,IAAI;AAAA,QACR,qBAAqB,IAAI,MAAM,IAAI,IAAI,UAAU,MAAM,WAAW,eAAe;AAAA,MACnF;AAAA,IACF;AAEA,QAAI,CAAC,IAAI,MAAM;AACb,YAAM,IAAI,MAAM,gDAAgD;AAAA,IAClE;AAEA,QAAI,OAAO;AACX,QAAI;AAEJ,qBAAiB,OAAO,iBAAiB,IAAI,MAAM,MAAM,GAAG;AAC1D,UAAI;AAIJ,UAAI;AACF,iBAAS,KAAK,MAAM,GAAG;AAAA,MACzB,QAAQ;AACN;AAAA,MACF;AAEA,YAAM,QAAQ,OAAO,aAAa,CAAC,GAAG,SAAS,QAAQ,CAAC,GAAG;AAC3D,UAAI,OAAO,UAAU,YAAY,MAAM,SAAS,GAAG;AACjD,gBAAQ;AACR,cAAM;AAAA,MACR;AAEA,UAAI,OAAO,eAAe;AACxB,gBAAQ;AAAA,UACN,cAAc,OAAO,cAAc;AAAA,UACnC,kBAAkB,OAAO,cAAc;AAAA,QACzC;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,MAAM,MAAM;AAAA,EACvB;AACF;;;ACnJA,SAAS,cAAc,cAAuD;AAC5E,UAAQ,cAAc;AAAA,IACpB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAQA,SAAS,eAAe,KAA4D;AAClF,MAAI,CAAC,MAAM,QAAQ,GAAG,KAAK,IAAI,WAAW,EAAG,QAAO,CAAC;AACrD,SAAO,IACJ,OAAO,CAAC,SAAS,MAAM,UAAU,IAAI,EACrC,IAAI,CAAC,MAAM,UAAU;AACpB,QAAI,QAAiC,CAAC;AACtC,UAAM,WAAW,KAAK,UAAU;AAChC,QAAI,YAAY,SAAS,KAAK,GAAG;AAC/B,UAAI;AACF,cAAM,SAAS,KAAK,MAAM,QAAQ;AAClC,YAAI,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,GAAG;AAClE,kBAAQ;AAAA,QACV;AAAA,MACF,QAAQ;AACN,gBAAQ,CAAC;AAAA,MACX;AAAA,IACF;AACA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,IAAI,KAAK,MAAM,QAAQ,KAAK;AAAA,MAC5B,MAAM,KAAK,SAAU;AAAA,MACrB;AAAA,IACF;AAAA,EACF,CAAC;AACL;AAEO,IAAM,gBAAN,MAAiD;AAAA,EAC7C,OAAO;AAAA,EACP,gBAAgB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,SAA+B;AACzC,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AACA,SAAK,SAAS,QAAQ;AACtB,SAAK,QAAQ,QAAQ,SAAS;AAC9B,SAAK,cAAc,QAAQ,eAAe;AAC1C,SAAK,WAAW,QAAQ,WAAW,6BAA6B,QAAQ,QAAQ,EAAE;AAClF,SAAK,gBAAgB,QAAQ;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,WAAW,SAAyC;AAC1D,UAAM,WAAW,iBAAiB,OAAO;AACzC,UAAM,cAAc,oBAAoB,OAAO;AAC/C,UAAM,OAAO,eAAe,OAAO;AAEnC,QAAI,YAAY,SAAS,GAAG;AAC1B,YAAM,UAA2B,YAAY,IAAI,CAAC,WAAW;AAAA,QAC3D,MAAM;AAAA,QACN,SAAS,MAAM;AAAA,QACf,cAAc,MAAM;AAAA,MACtB,EAAE;AAGF,UAAI,KAAK,KAAK,GAAG;AACf,gBAAQ,KAAK,EAAE,MAAM,QAAQ,SAAS,KAAK,CAAC;AAAA,MAC9C;AACA,aAAO;AAAA,IACT;AAEA,QAAI,SAAS,SAAS,GAAG;AACvB,aAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS,KAAK,KAAK,IAAI,OAAO;AAAA,UAC9B,YAAY,SAAS,IAAI,CAAC,WAAW;AAAA,YACnC,IAAI,MAAM;AAAA,YACV,MAAM;AAAA,YACN,UAAU;AAAA,cACR,MAAM,MAAM;AAAA,cACZ,WAAW,KAAK,UAAU,MAAM,SAAS,CAAC,CAAC;AAAA,YAC7C;AAAA,UACF,EAAE;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AAEA,WAAO,CAAC,EAAE,MAAM,QAAQ,MAAM,SAAS,KAAK,CAAC;AAAA,EAC/C;AAAA,EAEQ,cAAc,QAAmD;AACvE,UAAM,EAAE,cAAc,YAAY,UAAU,aAAa,IAAI;AAC7D,QAAI,gBAAgB,aAAa,SAAS,GAAG;AAC3C,aAAO;AAAA,QACL,EAAE,MAAM,UAAU,SAAS,aAAa;AAAA,QACxC,GAAG,aAAa,QAAQ,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC;AAAA,MACnD;AAAA,IACF;AACA,WAAO;AAAA,MACL,EAAE,MAAM,UAAU,SAAS,aAAa;AAAA,MACxC,EAAE,MAAM,QAAQ,SAAS,WAAW;AAAA,IACtC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,UAAU,QAA2D;AAC3E,UAAM,EAAE,cAAc,YAAY,UAAU,cAAc,OAAO,WAAW,IAAI;AAEhF,UAAM,OAAgC;AAAA,MACpC,OAAO,KAAK;AAAA,MACZ,aAAa,OAAO,eAAe,KAAK;AAAA,MACxC,UAAU,KAAK,cAAc,MAAM;AAAA,IACrC;AAEA,QAAI,SAAS,MAAM,SAAS,GAAG;AAC7B,WAAK,QAAQ,MAAM,IAAI,CAAC,UAAU;AAAA,QAChC,MAAM;AAAA,QACN,UAAU;AAAA,UACR,MAAM,KAAK;AAAA,UACX,aAAa,KAAK;AAAA,UAClB,YAAY,KAAK;AAAA,QACnB;AAAA,MACF,EAAE;AACF,UAAI,YAAY;AACd,aAAK,cACH,OAAO,eAAe,WAClB,aACA,EAAE,MAAM,YAAY,UAAU,EAAE,MAAM,WAAW,KAAK,EAAE;AAAA,MAChE;AACA,aAAO;AAAA,IACT;AAIA,QAAI,cAAe,CAAC,iBAAiB,aAAa,SAAS,MAAM,KAAK,aAAa,SAAS,MAAM,IAAK;AACrG,WAAK,kBAAkB,EAAE,MAAM,cAAc;AAAA,IAC/C;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,KAAK,MAA+B,QAAyC;AACzF,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,OAAO,qBAAqB;AAAA,MAC1D,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,eAAe,UAAU,KAAK,MAAM;AAAA,QACpC,GAAG,KAAK;AAAA,MACV;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB;AAAA,IACF,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,UAAU,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC/C,YAAM,IAAI;AAAA,QACR,qBAAqB,IAAI,MAAM,IAAI,IAAI,UAAU,MAAM,WAAW,eAAe;AAAA,MACnF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,SAAS,QAAqE;AAClF,UAAM,MAAM,MAAM,KAAK,KAAK,KAAK,UAAU,MAAM,GAAG,OAAO,MAAM;AAEjE,UAAM,OAAQ,MAAM,IAAI,KAAK;AAc7B,UAAM,SAAS,KAAK,UAAU,CAAC;AAC/B,UAAM,UAAU,QAAQ,SAAS;AACjC,UAAM,YAAY,eAAe,QAAQ,SAAS,UAAU;AAI5D,QAAI,CAAC,WAAW,UAAU,WAAW,GAAG;AACtC,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AAEA,WAAO;AAAA,MACL,MAAM,WAAW;AAAA,MACjB,WAAW,UAAU,SAAS,IAAI,YAAY;AAAA,MAC9C,YAAY,cAAc,QAAQ,aAAa;AAAA,MAC/C,OAAO,KAAK,QACR;AAAA,QACE,cAAc,KAAK,MAAM;AAAA,QACzB,kBAAkB,KAAK,MAAM;AAAA,MAC/B,IACA;AAAA,IACN;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,OAAO,eACL,QACwD;AACxD,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,MAAM,MAAM,KAAK,KAAK,EAAE,GAAG,KAAK,UAAU,MAAM,GAAG,QAAQ,KAAK,GAAG,MAAM;AAE/E,QAAI,CAAC,IAAI,MAAM;AACb,YAAM,IAAI,MAAM,gDAAgD;AAAA,IAClE;AAEA,QAAI,OAAO;AACX,QAAI;AACJ,QAAI;AACJ,UAAM,sBAAsB,oBAAI,IAA6D;AAE7F,qBAAiB,OAAO,iBAAiB,IAAI,MAAM,MAAM,GAAG;AAC1D,UAAI,QAAQ,SAAU;AAEtB,UAAI;AAUJ,UAAI;AACF,iBAAS,KAAK,MAAM,GAAG;AAAA,MACzB,QAAQ;AACN;AAAA,MACF;AAEA,YAAM,SAAS,OAAO,UAAU,CAAC;AAEjC,YAAM,QAAQ,QAAQ,OAAO;AAC7B,UAAI,OAAO,UAAU,YAAY,MAAM,SAAS,GAAG;AACjD,gBAAQ;AACR,cAAM;AAAA,MACR;AAEA,iBAAW,QAAQ,QAAQ,OAAO,cAAc,CAAC,GAAG;AAClD,cAAM,QAAQ,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAC5D,cAAM,QAAQ,oBAAoB,IAAI,KAAK,KAAK,EAAE,IAAI,IAAI,MAAM,IAAI,WAAW,GAAG;AAClF,YAAI,KAAK,GAAI,OAAM,KAAK,KAAK;AAC7B,YAAI,KAAK,UAAU,KAAM,OAAM,OAAO,KAAK,SAAS;AACpD,YAAI,KAAK,UAAU,UAAW,OAAM,aAAa,KAAK,SAAS;AAC/D,4BAAoB,IAAI,OAAO,KAAK;AAAA,MACtC;AAEA,UAAI,QAAQ,eAAe;AACzB,uBAAe,OAAO;AAAA,MACxB;AAEA,UAAI,OAAO,OAAO;AAChB,gBAAQ;AAAA,UACN,cAAc,OAAO,MAAM;AAAA,UAC3B,kBAAkB,OAAO,MAAM;AAAA,QACjC;AAAA,MACF;AAAA,IACF;AAEA,UAAM,YAAY;AAAA,MAChB,MAAM,KAAK,oBAAoB,QAAQ,CAAC,EACrC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,EACxB,IAAI,CAAC,CAAC,EAAE,KAAK,OAAO;AAAA,QACnB,IAAI,MAAM;AAAA,QACV,MAAM;AAAA,QACN,UAAU,EAAE,MAAM,MAAM,MAAM,WAAW,MAAM,UAAU;AAAA,MAC3D,EAAE;AAAA,IACN;AAEA,WAAO;AAAA,MACL;AAAA,MACA,WAAW,UAAU,SAAS,IAAI,YAAY;AAAA,MAC9C,YAAY,cAAc,YAAY;AAAA,MACtC;AAAA,IACF;AAAA,EACF;AACF;;;AC5VO,IAAM,mBAAN,MAAoD;AAAA,EAChD,OAAO;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,SAAkC;AAC5C,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AACA,SAAK,SAAS,QAAQ;AACtB,SAAK,QAAQ,QAAQ,SAAS;AAC9B,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,cAAc,QAAQ,eAAe;AAC1C,SAAK,WAAW,QAAQ,WAAW,gCAAgC,QAAQ,QAAQ,EAAE;AACrF,SAAK,mBAAmB,QAAQ,oBAAoB;AACpD,SAAK,gBAAgB,QAAQ;AAAA,EAC/B;AAAA,EAEQ,cACN,QACwD;AACxD,UAAM,EAAE,YAAY,UAAU,aAAa,IAAI;AAC/C,WAAO,gBAAgB,aAAa,SAAS,IACzC,aACG,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,SAAS,WAAW,EACzD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAA8B,SAAS,eAAe,CAAC,EAAE,EAAE,IACpF;AAAA,MACE;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACN;AAAA,EAEA,MAAM,SAAS,QAAqE;AAClF,UAAM,EAAE,cAAc,OAAO,IAAI;AAEjC,UAAM,MAAM,GAAG,KAAK,OAAO;AAC3B,UAAM,WAAW,KAAK,cAAc,MAAM;AAE1C,UAAM,OAAgC;AAAA,MACpC,OAAO,KAAK;AAAA,MACZ,YAAY,KAAK;AAAA,MACjB,aAAa,KAAK;AAAA,MAClB,QAAQ;AAAA,MACR;AAAA,IACF;AAEA,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,aAAa,KAAK;AAAA,QAClB,qBAAqB,KAAK;AAAA,QAC1B,GAAG,KAAK;AAAA,MACV;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB;AAAA,IACF,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,UAAU,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC/C,YAAM,IAAI;AAAA,QACR,wBAAwB,IAAI,MAAM,IAAI,IAAI,UAAU,MAAM,WAAW,eAAe;AAAA,MACtF;AAAA,IACF;AAEA,UAAM,OAAQ,MAAM,IAAI,KAAK;AAW7B,UAAM,WAAW,KAAK,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS,MAAM,GAAG;AAC/D,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO,KAAK,QACR;AAAA,QACE,cAAc,KAAK,MAAM;AAAA,QACzB,kBAAkB,KAAK,MAAM;AAAA,MAC/B,IACA;AAAA,IACN;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,eACL,QACwD;AACxD,UAAM,EAAE,cAAc,OAAO,IAAI;AACjC,UAAM,MAAM,GAAG,KAAK,OAAO;AAC3B,UAAM,WAAW,KAAK,cAAc,MAAM;AAE1C,UAAM,OAAgC;AAAA,MACpC,OAAO,KAAK;AAAA,MACZ,YAAY,KAAK;AAAA,MACjB,aAAa,KAAK;AAAA,MAClB,QAAQ;AAAA,MACR;AAAA,MACA,QAAQ;AAAA,IACV;AAEA,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,aAAa,KAAK;AAAA,QAClB,qBAAqB,KAAK;AAAA,QAC1B,GAAG,KAAK;AAAA,MACV;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB;AAAA,IACF,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,UAAU,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC/C,YAAM,IAAI;AAAA,QACR,wBAAwB,IAAI,MAAM,IAAI,IAAI,UAAU,MAAM,WAAW,eAAe;AAAA,MACtF;AAAA,IACF;AAEA,QAAI,CAAC,IAAI,MAAM;AACb,YAAM,IAAI,MAAM,mDAAmD;AAAA,IACrE;AAEA,QAAI,OAAO;AACX,QAAI;AACJ,QAAI;AAEJ,qBAAiB,OAAO,iBAAiB,IAAI,MAAM,MAAM,GAAG;AAC1D,UAAI,CAAC,IAAK;AAEV,UAAI;AAMJ,UAAI;AACF,cAAM,KAAK,MAAM,GAAG;AAAA,MACtB,QAAQ;AACN;AAAA,MACF;AAEA,UAAI,IAAI,SAAS,yBAAyB,IAAI,OAAO,SAAS,cAAc;AAC1E,cAAM,QAAQ,IAAI,MAAM;AACxB,YAAI,OAAO,UAAU,YAAY,MAAM,SAAS,GAAG;AACjD,kBAAQ;AACR,gBAAM;AAAA,QACR;AAAA,MACF,WAAW,IAAI,SAAS,mBAAmB,IAAI,SAAS,OAAO,iBAAiB,QAAW;AACzF,uBAAe,IAAI,QAAQ,MAAM;AAAA,MACnC,WAAW,IAAI,SAAS,mBAAmB,IAAI,OAAO,kBAAkB,QAAW;AACjF,2BAAmB,IAAI,MAAM;AAAA,MAC/B;AAAA,IACF;AAEA,WAAO;AAAA,MACL;AAAA,MACA,OACE,iBAAiB,UAAa,qBAAqB,SAC/C,EAAE,cAAc,iBAAiB,IACjC;AAAA,IACR;AAAA,EACF;AACF;;;AC1LO,IAAM,oBAAN,MAAqD;AAAA,EACjD;AAAA,EACD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,SAAmC;AAC7C,QAAI,CAAC,QAAQ,KAAK;AAChB,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AACA,SAAK,MAAM,QAAQ;AACnB,SAAK,OAAO,QAAQ,QAAQ;AAC5B,SAAK,UAAU,QAAQ;AAGvB,SAAK,oBACH,QAAQ,sBACP,CAAC,YAAY;AAAA,MACZ,UACE,OAAO,YAAY,OAAO,SAAS,SAAS,IACxC;AAAA,QACE,EAAE,MAAM,UAAU,SAAS,OAAO,aAAa;AAAA,QAC/C,GAAG,OAAO,SAAS,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,eAAe,CAAC,EAAE,EAAE;AAAA,MAC9E,IACA;AAAA,QACE,EAAE,MAAM,UAAU,SAAS,OAAO,aAAa;AAAA,QAC/C,EAAE,MAAM,QAAQ,SAAS,OAAO,WAAW;AAAA,MAC7C;AAAA,IACR;AAGF,SAAK,gBACH,QAAQ,kBACP,CAAC,SAAkB;AAClB,UAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,cAAM,IAAI,MAAM,qCAAqC;AAAA,MACvD;AACA,YAAM,MAAM;AACZ,UAAI,OAAO,IAAI,aAAa,SAAU,QAAO,IAAI;AACjD,UAAI,OAAO,IAAI,SAAS,SAAU,QAAO,IAAI;AAC7C,YAAM,UAAU,IAAI;AACpB,UAAI,MAAM,QAAQ,OAAO,KAAK,QAAQ,CAAC,GAAG,SAAS,SAAS;AAC1D,eAAO,QAAQ,CAAC,EAAE,QAAQ;AAAA,MAC5B;AACA,YAAM,UAAU,IAAI;AACpB,UAAI,WAAW,OAAO,QAAQ,YAAY,UAAU;AAClD,eAAO,QAAQ;AAAA,MACjB;AACA,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AAAA,EACJ;AAAA,EAEA,MAAM,SAAS,QAAqE;AAClF,UAAM,OAAO,KAAK,kBAAkB,MAAM;AAE1C,UAAM,MAAM,MAAM,MAAM,KAAK,KAAK;AAAA,MAChC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,GAAG,KAAK;AAAA,MACV;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB,QAAQ,OAAO;AAAA,IACjB,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,UAAU,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC/C,YAAM,IAAI;AAAA,QACR,8BAA8B,IAAI,MAAM,IAAI,IAAI,UAAU,MAAM,WAAW,eAAe;AAAA,MAC5F;AAAA,IACF;AAEA,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,UAAM,OAAO,KAAK,cAAc,IAAI;AAEpC,WAAO,EAAE,KAAK;AAAA,EAChB;AACF;","names":[]}
|
|
@@ -8,18 +8,43 @@ export interface OpenAiAdapterOptions {
|
|
|
8
8
|
}
|
|
9
9
|
export declare class OpenAiAdapter implements AiProviderAdapter {
|
|
10
10
|
readonly name = "openai";
|
|
11
|
+
readonly supportsTools = true;
|
|
11
12
|
private apiKey;
|
|
12
13
|
private model;
|
|
13
14
|
private temperature;
|
|
14
15
|
private baseUrl;
|
|
15
16
|
private customHeaders?;
|
|
16
17
|
constructor(options: OpenAiAdapterOptions);
|
|
18
|
+
/**
|
|
19
|
+
* Expands one `AiChatMessage` into the one-or-more OpenAI messages it corresponds to
|
|
20
|
+
* (STORA-530).
|
|
21
|
+
*
|
|
22
|
+
* The block model is Anthropic-shaped — tool results ride along inside a `user` message —
|
|
23
|
+
* while OpenAI wants each result as its own `role: 'tool'` message keyed by
|
|
24
|
+
* `tool_call_id`. That fan-out happens here so the rest of the stack stays
|
|
25
|
+
* provider-agnostic.
|
|
26
|
+
*/
|
|
27
|
+
private mapMessage;
|
|
17
28
|
private buildMessages;
|
|
29
|
+
/**
|
|
30
|
+
* Shared request body builder for `generate`/`generateStream` — notably the
|
|
31
|
+
* `response_format` decision, which must never fire in tool mode: OpenAI rejects
|
|
32
|
+
* `json_object` combined with `tools`, and a tool-calling turn's text output is a
|
|
33
|
+
* natural-language summary, not JSON.
|
|
34
|
+
*/
|
|
35
|
+
private buildBody;
|
|
36
|
+
private post;
|
|
18
37
|
generate(params: AiProviderGenerateParams): Promise<AiProviderGenerateResult>;
|
|
19
38
|
/**
|
|
20
39
|
* Token-level streaming via OpenAI's `stream: true` chat completions mode (STORA-515).
|
|
21
40
|
* Yields each `delta.content` fragment as it arrives and returns the fully assembled
|
|
22
41
|
* text + usage (when the final chunk includes it) once the stream ends.
|
|
42
|
+
*
|
|
43
|
+
* Tool calls arrive fragmented too (STORA-530): `delta.tool_calls[].function.arguments`
|
|
44
|
+
* is delivered as a character stream across many chunks, keyed by array `index`. They're
|
|
45
|
+
* accumulated per index and parsed only once the stream ends — parsing a partial
|
|
46
|
+
* arguments string would always fail. Tool call deltas are never `yield`ed as text,
|
|
47
|
+
* since they aren't part of the assistant's visible message.
|
|
23
48
|
*/
|
|
24
49
|
generateStream(params: AiProviderGenerateParams): AsyncGenerator<string, AiProviderGenerateResult, void>;
|
|
25
50
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/server/adapters/openai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/server/adapters/openai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,iBAAiB,EACjB,wBAAwB,EACxB,wBAAwB,EAGzB,MAAM,aAAa,CAAC;AAIrB,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAwED,qBAAa,aAAc,YAAW,iBAAiB;IACrD,QAAQ,CAAC,IAAI,YAAY;IACzB,QAAQ,CAAC,aAAa,QAAQ;IAC9B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,aAAa,CAAC,CAAyB;gBAEnC,OAAO,EAAE,oBAAoB;IAWzC;;;;;;;;OAQG;IACH,OAAO,CAAC,UAAU;IAuClB,OAAO,CAAC,aAAa;IAcrB;;;;;OAKG;IACH,OAAO,CAAC,SAAS;YAoCH,IAAI;IAsBZ,QAAQ,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAwCnF;;;;;;;;;;OAUG;IACI,cAAc,CACnB,MAAM,EAAE,wBAAwB,GAC/B,cAAc,CAAC,MAAM,EAAE,wBAAwB,EAAE,IAAI,CAAC;CA8E1D"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { AiAgentRequest, AiAgentRunResult, AiStreamEvent, KubuildAiEngineOptions } from '../types';
|
|
2
|
+
import type { KubuildAiEngine } from './engine';
|
|
3
|
+
import { type AgentTool } from './tools';
|
|
4
|
+
export interface KubuildAiAgentOptions extends KubuildAiEngineOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Engine used by the `insert_section` tool. Optional: without it the agent still runs,
|
|
7
|
+
* and `insert_section` returns a "not available" tool error telling the model to build
|
|
8
|
+
* the section with `insert_component` instead.
|
|
9
|
+
*/
|
|
10
|
+
engine?: KubuildAiEngine;
|
|
11
|
+
/** Overrides the default tool set (read + write document tools). */
|
|
12
|
+
tools?: AgentTool[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The page-editing agent (STORA-530).
|
|
16
|
+
*
|
|
17
|
+
* Unlike every `KubuildAiEngine` method, which is a single request/response, this runs a
|
|
18
|
+
* loop: the model reads the page through tools, makes a surgical change through tools, and
|
|
19
|
+
* stops. The document it mutates is a private **snapshot** — the run's output is an ordered
|
|
20
|
+
* list of `AgentOp`s that the editor replays through its own store actions, so an agent
|
|
21
|
+
* edit lands in the command engine and the undo stack exactly like a manual edit.
|
|
22
|
+
*
|
|
23
|
+
* Nothing here ever touches the user's live document.
|
|
24
|
+
*/
|
|
25
|
+
export declare class KubuildAiAgent {
|
|
26
|
+
private options;
|
|
27
|
+
private catalog;
|
|
28
|
+
private tools;
|
|
29
|
+
constructor(options: KubuildAiAgentOptions);
|
|
30
|
+
get adapterName(): string;
|
|
31
|
+
refreshCatalog(): void;
|
|
32
|
+
private log;
|
|
33
|
+
private findTool;
|
|
34
|
+
/**
|
|
35
|
+
* Runs one tool call against the current snapshot.
|
|
36
|
+
*
|
|
37
|
+
* An unknown tool name is answered with a normal (failed) tool result rather than an
|
|
38
|
+
* exception, for the same reason bad arguments are: it gives the model a chance to pick a
|
|
39
|
+
* real tool on the next step instead of killing the run.
|
|
40
|
+
*/
|
|
41
|
+
private executeToolCall;
|
|
42
|
+
/**
|
|
43
|
+
* Executes one agent turn, streaming progress as `AiStreamEvent`s and ending with a
|
|
44
|
+
* terminal `agent-complete` (or `error`) event.
|
|
45
|
+
*
|
|
46
|
+
* The loop stops as soon as the model returns a turn with no tool calls — that final
|
|
47
|
+
* message is the summary shown to the user. It also stops at `maxSteps` (cost guard) or
|
|
48
|
+
* on abort, and reports which of those happened via `stoppedBy` so the UI can say
|
|
49
|
+
* "stopped after N steps" rather than pretending the work is finished.
|
|
50
|
+
*/
|
|
51
|
+
run(request: AiAgentRequest, context?: {
|
|
52
|
+
signal?: AbortSignal;
|
|
53
|
+
}): AsyncIterable<AiStreamEvent>;
|
|
54
|
+
/** Non-streaming convenience wrapper — drains `run()` and returns its terminal result. */
|
|
55
|
+
execute(request: AiAgentRequest, context?: {
|
|
56
|
+
signal?: AbortSignal;
|
|
57
|
+
}): Promise<AiAgentRunResult>;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/server/agent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,cAAc,EACd,gBAAgB,EAIhB,aAAa,EAEb,sBAAsB,EACvB,MAAM,UAAU,CAAC;AAGlB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAA0C,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAEjF,MAAM,WAAW,qBAAsB,SAAQ,sBAAsB;IACnE;;;;OAIG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,oEAAoE;IACpE,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;CACrB;AAYD;;;;;;;;;;GAUG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,KAAK,CAAc;gBAEf,OAAO,EAAE,qBAAqB;IAgB1C,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,cAAc,IAAI,IAAI;IAItB,OAAO,CAAC,GAAG;IAQX,OAAO,CAAC,QAAQ;IAIhB;;;;;;OAMG;YACW,eAAe;IA2F7B;;;;;;;;OAQG;IACI,GAAG,CACR,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,aAAa,CAAC,aAAa,CAAC;IA0K/B,0FAA0F;IACpF,OAAO,CACX,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,OAAO,CAAC,gBAAgB,CAAC;CAiB7B"}
|