@kubuild/ai 0.3.1 → 0.5.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-K7CBTQCY.js} +91 -1
- package/dist/chunk-K7CBTQCY.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 +27 -1
- package/dist/client/ai-client.d.ts.map +1 -1
- package/dist/client/index.cjs +90 -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 +233 -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 +143 -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/react/use-ai-generator.d.ts +2 -1
- package/dist/react/use-ai-generator.d.ts.map +1 -1
- 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 +5 -1
- package/dist/server/engine.d.ts.map +1 -1
- package/dist/server/handler.d.ts +18 -2
- package/dist/server/handler.d.ts.map +1 -1
- package/dist/server/index.cjs +1843 -258
- 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 +1379 -133
- 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 +224 -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
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getMessageText,
|
|
3
|
+
getToolResultBlocks,
|
|
4
|
+
getToolUseBlocks
|
|
5
|
+
} from "./chunk-ILLNRABB.js";
|
|
6
|
+
|
|
1
7
|
// src/server/adapters/stream-utils.ts
|
|
2
8
|
async function* readSseDataLines(body, signal) {
|
|
3
9
|
const reader = body.getReader();
|
|
@@ -47,7 +53,7 @@ var GeminiAdapter = class {
|
|
|
47
53
|
const { userPrompt, messages: chatMessages } = params;
|
|
48
54
|
return chatMessages && chatMessages.length > 0 ? chatMessages.map((m) => ({
|
|
49
55
|
role: m.role === "assistant" ? "model" : "user",
|
|
50
|
-
parts: [{ text: m
|
|
56
|
+
parts: [{ text: getMessageText(m) }]
|
|
51
57
|
})) : [
|
|
52
58
|
{
|
|
53
59
|
role: "user",
|
|
@@ -159,8 +165,47 @@ var GeminiAdapter = class {
|
|
|
159
165
|
};
|
|
160
166
|
|
|
161
167
|
// src/server/adapters/openai.ts
|
|
168
|
+
function mapStopReason(finishReason) {
|
|
169
|
+
switch (finishReason) {
|
|
170
|
+
case "stop":
|
|
171
|
+
return "stop";
|
|
172
|
+
case "tool_calls":
|
|
173
|
+
case "function_call":
|
|
174
|
+
return "tool_calls";
|
|
175
|
+
case "length":
|
|
176
|
+
return "length";
|
|
177
|
+
case "content_filter":
|
|
178
|
+
return "content_filter";
|
|
179
|
+
default:
|
|
180
|
+
return "unknown";
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function parseToolCalls(raw) {
|
|
184
|
+
if (!Array.isArray(raw) || raw.length === 0) return [];
|
|
185
|
+
return raw.filter((call) => call?.function?.name).map((call, index) => {
|
|
186
|
+
let input = {};
|
|
187
|
+
const argsText = call.function?.arguments;
|
|
188
|
+
if (argsText && argsText.trim()) {
|
|
189
|
+
try {
|
|
190
|
+
const parsed = JSON.parse(argsText);
|
|
191
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
192
|
+
input = parsed;
|
|
193
|
+
}
|
|
194
|
+
} catch {
|
|
195
|
+
input = {};
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
type: "tool_use",
|
|
200
|
+
id: call.id || `call_${index}`,
|
|
201
|
+
name: call.function.name,
|
|
202
|
+
input
|
|
203
|
+
};
|
|
204
|
+
});
|
|
205
|
+
}
|
|
162
206
|
var OpenAiAdapter = class {
|
|
163
207
|
name = "openai";
|
|
208
|
+
supportsTools = true;
|
|
164
209
|
apiKey;
|
|
165
210
|
model;
|
|
166
211
|
temperature;
|
|
@@ -176,30 +221,95 @@ var OpenAiAdapter = class {
|
|
|
176
221
|
this.baseUrl = (options.baseUrl || "https://api.openai.com/v1").replace(/\/+$/, "");
|
|
177
222
|
this.customHeaders = options.headers;
|
|
178
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* Expands one `AiChatMessage` into the one-or-more OpenAI messages it corresponds to
|
|
226
|
+
* (STORA-530).
|
|
227
|
+
*
|
|
228
|
+
* The block model is Anthropic-shaped — tool results ride along inside a `user` message —
|
|
229
|
+
* while OpenAI wants each result as its own `role: 'tool'` message keyed by
|
|
230
|
+
* `tool_call_id`. That fan-out happens here so the rest of the stack stays
|
|
231
|
+
* provider-agnostic.
|
|
232
|
+
*/
|
|
233
|
+
mapMessage(message) {
|
|
234
|
+
const toolUses = getToolUseBlocks(message);
|
|
235
|
+
const toolResults = getToolResultBlocks(message);
|
|
236
|
+
const text = getMessageText(message);
|
|
237
|
+
if (toolResults.length > 0) {
|
|
238
|
+
const results = toolResults.map((block) => ({
|
|
239
|
+
role: "tool",
|
|
240
|
+
content: block.content,
|
|
241
|
+
tool_call_id: block.toolUseId
|
|
242
|
+
}));
|
|
243
|
+
if (text.trim()) {
|
|
244
|
+
results.push({ role: "user", content: text });
|
|
245
|
+
}
|
|
246
|
+
return results;
|
|
247
|
+
}
|
|
248
|
+
if (toolUses.length > 0) {
|
|
249
|
+
return [
|
|
250
|
+
{
|
|
251
|
+
role: "assistant",
|
|
252
|
+
content: text.trim() ? text : null,
|
|
253
|
+
tool_calls: toolUses.map((block) => ({
|
|
254
|
+
id: block.id,
|
|
255
|
+
type: "function",
|
|
256
|
+
function: {
|
|
257
|
+
name: block.name,
|
|
258
|
+
arguments: JSON.stringify(block.input ?? {})
|
|
259
|
+
}
|
|
260
|
+
}))
|
|
261
|
+
}
|
|
262
|
+
];
|
|
263
|
+
}
|
|
264
|
+
return [{ role: message.role, content: text }];
|
|
265
|
+
}
|
|
179
266
|
buildMessages(params) {
|
|
180
267
|
const { systemPrompt, userPrompt, messages: chatMessages } = params;
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
268
|
+
if (chatMessages && chatMessages.length > 0) {
|
|
269
|
+
return [
|
|
270
|
+
{ role: "system", content: systemPrompt },
|
|
271
|
+
...chatMessages.flatMap((m) => this.mapMessage(m))
|
|
272
|
+
];
|
|
273
|
+
}
|
|
274
|
+
return [
|
|
185
275
|
{ role: "system", content: systemPrompt },
|
|
186
276
|
{ role: "user", content: userPrompt }
|
|
187
277
|
];
|
|
188
278
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
279
|
+
/**
|
|
280
|
+
* Shared request body builder for `generate`/`generateStream` — notably the
|
|
281
|
+
* `response_format` decision, which must never fire in tool mode: OpenAI rejects
|
|
282
|
+
* `json_object` combined with `tools`, and a tool-calling turn's text output is a
|
|
283
|
+
* natural-language summary, not JSON.
|
|
284
|
+
*/
|
|
285
|
+
buildBody(params) {
|
|
286
|
+
const { systemPrompt, jsonSchema, messages: chatMessages, tools, toolChoice } = params;
|
|
194
287
|
const body = {
|
|
195
288
|
model: this.model,
|
|
196
|
-
temperature: this.temperature,
|
|
197
|
-
messages
|
|
289
|
+
temperature: params.temperature ?? this.temperature,
|
|
290
|
+
messages: this.buildMessages(params)
|
|
198
291
|
};
|
|
199
|
-
if (
|
|
292
|
+
if (tools && tools.length > 0) {
|
|
293
|
+
body.tools = tools.map((tool) => ({
|
|
294
|
+
type: "function",
|
|
295
|
+
function: {
|
|
296
|
+
name: tool.name,
|
|
297
|
+
description: tool.description,
|
|
298
|
+
parameters: tool.inputSchema
|
|
299
|
+
}
|
|
300
|
+
}));
|
|
301
|
+
if (toolChoice) {
|
|
302
|
+
body.tool_choice = typeof toolChoice === "string" ? toolChoice : { type: "function", function: { name: toolChoice.name } };
|
|
303
|
+
}
|
|
304
|
+
return body;
|
|
305
|
+
}
|
|
306
|
+
if (jsonSchema || !chatMessages && (systemPrompt.includes("JSON") || systemPrompt.includes("json"))) {
|
|
200
307
|
body.response_format = { type: "json_object" };
|
|
201
308
|
}
|
|
202
|
-
|
|
309
|
+
return body;
|
|
310
|
+
}
|
|
311
|
+
async post(body, signal) {
|
|
312
|
+
const res = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
203
313
|
method: "POST",
|
|
204
314
|
headers: {
|
|
205
315
|
"Content-Type": "application/json",
|
|
@@ -215,13 +325,21 @@ var OpenAiAdapter = class {
|
|
|
215
325
|
`OpenAI API error [${res.status} ${res.statusText}]: ${errText || "Unknown error"}`
|
|
216
326
|
);
|
|
217
327
|
}
|
|
328
|
+
return res;
|
|
329
|
+
}
|
|
330
|
+
async generate(params) {
|
|
331
|
+
const res = await this.post(this.buildBody(params), params.signal);
|
|
218
332
|
const data = await res.json();
|
|
219
|
-
const
|
|
220
|
-
|
|
333
|
+
const choice = data.choices?.[0];
|
|
334
|
+
const content = choice?.message?.content;
|
|
335
|
+
const toolCalls = parseToolCalls(choice?.message?.tool_calls);
|
|
336
|
+
if (!content && toolCalls.length === 0) {
|
|
221
337
|
throw new Error("OpenAI returned an empty message content");
|
|
222
338
|
}
|
|
223
339
|
return {
|
|
224
|
-
text: content,
|
|
340
|
+
text: content || "",
|
|
341
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
342
|
+
stopReason: mapStopReason(choice?.finish_reason),
|
|
225
343
|
usage: data.usage ? {
|
|
226
344
|
promptTokens: data.usage.prompt_tokens,
|
|
227
345
|
completionTokens: data.usage.completion_tokens
|
|
@@ -232,38 +350,23 @@ var OpenAiAdapter = class {
|
|
|
232
350
|
* Token-level streaming via OpenAI's `stream: true` chat completions mode (STORA-515).
|
|
233
351
|
* Yields each `delta.content` fragment as it arrives and returns the fully assembled
|
|
234
352
|
* text + usage (when the final chunk includes it) once the stream ends.
|
|
353
|
+
*
|
|
354
|
+
* Tool calls arrive fragmented too (STORA-530): `delta.tool_calls[].function.arguments`
|
|
355
|
+
* is delivered as a character stream across many chunks, keyed by array `index`. They're
|
|
356
|
+
* accumulated per index and parsed only once the stream ends — parsing a partial
|
|
357
|
+
* arguments string would always fail. Tool call deltas are never `yield`ed as text,
|
|
358
|
+
* since they aren't part of the assistant's visible message.
|
|
235
359
|
*/
|
|
236
360
|
async *generateStream(params) {
|
|
237
361
|
const { signal } = params;
|
|
238
|
-
const
|
|
239
|
-
const messages = this.buildMessages(params);
|
|
240
|
-
const body = {
|
|
241
|
-
model: this.model,
|
|
242
|
-
temperature: this.temperature,
|
|
243
|
-
messages,
|
|
244
|
-
stream: true
|
|
245
|
-
};
|
|
246
|
-
const res = await fetch(url, {
|
|
247
|
-
method: "POST",
|
|
248
|
-
headers: {
|
|
249
|
-
"Content-Type": "application/json",
|
|
250
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
251
|
-
...this.customHeaders
|
|
252
|
-
},
|
|
253
|
-
body: JSON.stringify(body),
|
|
254
|
-
signal
|
|
255
|
-
});
|
|
256
|
-
if (!res.ok) {
|
|
257
|
-
const errText = await res.text().catch(() => "");
|
|
258
|
-
throw new Error(
|
|
259
|
-
`OpenAI API error [${res.status} ${res.statusText}]: ${errText || "Unknown error"}`
|
|
260
|
-
);
|
|
261
|
-
}
|
|
362
|
+
const res = await this.post({ ...this.buildBody(params), stream: true }, signal);
|
|
262
363
|
if (!res.body) {
|
|
263
364
|
throw new Error("OpenAI streaming response has no readable body");
|
|
264
365
|
}
|
|
265
366
|
let text = "";
|
|
266
367
|
let usage;
|
|
368
|
+
let finishReason;
|
|
369
|
+
const toolCallAccumulator = /* @__PURE__ */ new Map();
|
|
267
370
|
for await (const raw of readSseDataLines(res.body, signal)) {
|
|
268
371
|
if (raw === "[DONE]") break;
|
|
269
372
|
let parsed;
|
|
@@ -272,11 +375,23 @@ var OpenAiAdapter = class {
|
|
|
272
375
|
} catch {
|
|
273
376
|
continue;
|
|
274
377
|
}
|
|
275
|
-
const
|
|
378
|
+
const choice = parsed.choices?.[0];
|
|
379
|
+
const delta = choice?.delta?.content;
|
|
276
380
|
if (typeof delta === "string" && delta.length > 0) {
|
|
277
381
|
text += delta;
|
|
278
382
|
yield delta;
|
|
279
383
|
}
|
|
384
|
+
for (const call of choice?.delta?.tool_calls ?? []) {
|
|
385
|
+
const index = typeof call.index === "number" ? call.index : 0;
|
|
386
|
+
const entry = toolCallAccumulator.get(index) ?? { id: "", name: "", arguments: "" };
|
|
387
|
+
if (call.id) entry.id = call.id;
|
|
388
|
+
if (call.function?.name) entry.name = call.function.name;
|
|
389
|
+
if (call.function?.arguments) entry.arguments += call.function.arguments;
|
|
390
|
+
toolCallAccumulator.set(index, entry);
|
|
391
|
+
}
|
|
392
|
+
if (choice?.finish_reason) {
|
|
393
|
+
finishReason = choice.finish_reason;
|
|
394
|
+
}
|
|
280
395
|
if (parsed.usage) {
|
|
281
396
|
usage = {
|
|
282
397
|
promptTokens: parsed.usage.prompt_tokens,
|
|
@@ -284,7 +399,19 @@ var OpenAiAdapter = class {
|
|
|
284
399
|
};
|
|
285
400
|
}
|
|
286
401
|
}
|
|
287
|
-
|
|
402
|
+
const toolCalls = parseToolCalls(
|
|
403
|
+
Array.from(toolCallAccumulator.entries()).sort(([a], [b]) => a - b).map(([, entry]) => ({
|
|
404
|
+
id: entry.id,
|
|
405
|
+
type: "function",
|
|
406
|
+
function: { name: entry.name, arguments: entry.arguments }
|
|
407
|
+
}))
|
|
408
|
+
);
|
|
409
|
+
return {
|
|
410
|
+
text,
|
|
411
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
412
|
+
stopReason: mapStopReason(finishReason),
|
|
413
|
+
usage
|
|
414
|
+
};
|
|
288
415
|
}
|
|
289
416
|
};
|
|
290
417
|
|
|
@@ -312,7 +439,7 @@ var AnthropicAdapter = class {
|
|
|
312
439
|
}
|
|
313
440
|
buildMessages(params) {
|
|
314
441
|
const { userPrompt, messages: chatMessages } = params;
|
|
315
|
-
return chatMessages && chatMessages.length > 0 ? chatMessages.filter((m) => m.role === "user" || m.role === "assistant").map((m) => ({ role: m.role, content: m
|
|
442
|
+
return chatMessages && chatMessages.length > 0 ? chatMessages.filter((m) => m.role === "user" || m.role === "assistant").map((m) => ({ role: m.role, content: getMessageText(m) })) : [
|
|
316
443
|
{
|
|
317
444
|
role: "user",
|
|
318
445
|
content: userPrompt
|
|
@@ -444,7 +571,7 @@ var CustomHttpAdapter = class {
|
|
|
444
571
|
this.formatRequestBody = options.formatRequestBody || ((params) => ({
|
|
445
572
|
messages: params.messages && params.messages.length > 0 ? [
|
|
446
573
|
{ role: "system", content: params.systemPrompt },
|
|
447
|
-
...params.messages.map((m) => ({ role: m.role, content: m
|
|
574
|
+
...params.messages.map((m) => ({ role: m.role, content: getMessageText(m) }))
|
|
448
575
|
] : [
|
|
449
576
|
{ role: "system", content: params.systemPrompt },
|
|
450
577
|
{ role: "user", content: params.userPrompt }
|
|
@@ -497,4 +624,4 @@ export {
|
|
|
497
624
|
AnthropicAdapter,
|
|
498
625
|
CustomHttpAdapter
|
|
499
626
|
};
|
|
500
|
-
//# sourceMappingURL=chunk-
|
|
627
|
+
//# sourceMappingURL=chunk-3DRAVYU3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../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":["/**\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';\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":"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","names":[]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// src/core/messages.ts
|
|
2
|
+
function getMessageText(message) {
|
|
3
|
+
if (typeof message.content === "string") return message.content;
|
|
4
|
+
return message.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
5
|
+
}
|
|
6
|
+
function getToolUseBlocks(message) {
|
|
7
|
+
if (typeof message.content === "string") return [];
|
|
8
|
+
return message.content.filter((b) => b.type === "tool_use");
|
|
9
|
+
}
|
|
10
|
+
function getToolResultBlocks(message) {
|
|
11
|
+
if (typeof message.content === "string") return [];
|
|
12
|
+
return message.content.filter((b) => b.type === "tool_result");
|
|
13
|
+
}
|
|
14
|
+
function hasStructuredContent(message) {
|
|
15
|
+
return typeof message.content !== "string" && message.content.some((b) => b.type !== "text");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
getMessageText,
|
|
20
|
+
getToolUseBlocks,
|
|
21
|
+
getToolResultBlocks,
|
|
22
|
+
hasStructuredContent
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=chunk-ILLNRABB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/messages.ts"],"sourcesContent":["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"],"mappings":";AAWO,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;AAGO,SAAS,qBAAqB,SAAkD;AACrF,SAAO,OAAO,QAAQ,YAAY,YAAY,QAAQ,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,MAAM;AAC7F;","names":[]}
|
|
@@ -3,6 +3,7 @@ var KubuildAiClient = class {
|
|
|
3
3
|
endpoint;
|
|
4
4
|
headers;
|
|
5
5
|
fetchFn;
|
|
6
|
+
credentials;
|
|
6
7
|
constructor(options) {
|
|
7
8
|
if (!options.endpoint) {
|
|
8
9
|
throw new Error("KubuildAiClient requires an endpoint URL");
|
|
@@ -10,6 +11,7 @@ var KubuildAiClient = class {
|
|
|
10
11
|
this.endpoint = options.endpoint;
|
|
11
12
|
this.headers = options.headers;
|
|
12
13
|
this.fetchFn = options.fetch || globalThis.fetch.bind(globalThis);
|
|
14
|
+
this.credentials = options.credentials;
|
|
13
15
|
}
|
|
14
16
|
async resolveHeaders() {
|
|
15
17
|
if (!this.headers) return {};
|
|
@@ -27,6 +29,7 @@ var KubuildAiClient = class {
|
|
|
27
29
|
...dynamicHeaders
|
|
28
30
|
},
|
|
29
31
|
body: JSON.stringify(body),
|
|
32
|
+
credentials: this.credentials,
|
|
30
33
|
signal
|
|
31
34
|
});
|
|
32
35
|
const json = await res.json();
|
|
@@ -111,6 +114,7 @@ var KubuildAiClient = class {
|
|
|
111
114
|
stream: true,
|
|
112
115
|
...params
|
|
113
116
|
}),
|
|
117
|
+
credentials: this.credentials,
|
|
114
118
|
signal: options?.signal
|
|
115
119
|
});
|
|
116
120
|
if (!res.ok) {
|
|
@@ -142,6 +146,35 @@ var KubuildAiClient = class {
|
|
|
142
146
|
}
|
|
143
147
|
return finalDoc;
|
|
144
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Plans the website structure (title, description, and list of sections)
|
|
151
|
+
* before generation so the user can review and approve.
|
|
152
|
+
*/
|
|
153
|
+
async planPage(params, options) {
|
|
154
|
+
const dynamicHeaders = await this.resolveHeaders();
|
|
155
|
+
const res = await this.fetchFn(this.endpoint, {
|
|
156
|
+
method: "POST",
|
|
157
|
+
headers: {
|
|
158
|
+
"Content-Type": "application/json",
|
|
159
|
+
...dynamicHeaders
|
|
160
|
+
},
|
|
161
|
+
body: JSON.stringify({
|
|
162
|
+
mode: "plan",
|
|
163
|
+
...params
|
|
164
|
+
}),
|
|
165
|
+
credentials: this.credentials,
|
|
166
|
+
signal: options?.signal
|
|
167
|
+
});
|
|
168
|
+
if (!res.ok) {
|
|
169
|
+
const errText = await res.text().catch(() => "");
|
|
170
|
+
throw new Error(`Plan request failed [${res.status} ${res.statusText}]: ${errText}`);
|
|
171
|
+
}
|
|
172
|
+
const json = await res.json();
|
|
173
|
+
if (!json.success || !json.data) {
|
|
174
|
+
throw new Error(json.error?.message || "Failed to generate page plan");
|
|
175
|
+
}
|
|
176
|
+
return json.data;
|
|
177
|
+
}
|
|
145
178
|
/**
|
|
146
179
|
* Token-level streaming chat (STORA-515). Calls `onChatChunk` for every partial chunk
|
|
147
180
|
* as it arrives and resolves with the fully assembled assistant `AiChatMessage` once
|
|
@@ -161,6 +194,7 @@ var KubuildAiClient = class {
|
|
|
161
194
|
stream: true,
|
|
162
195
|
...params
|
|
163
196
|
}),
|
|
197
|
+
credentials: this.credentials,
|
|
164
198
|
signal: options?.signal
|
|
165
199
|
});
|
|
166
200
|
if (!res.ok) {
|
|
@@ -188,6 +222,62 @@ var KubuildAiClient = class {
|
|
|
188
222
|
}
|
|
189
223
|
return finalMessage;
|
|
190
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Runs an agent turn over SSE (STORA-530).
|
|
227
|
+
*
|
|
228
|
+
* Streams the agent's progress — each reasoning step, each tool call and its result —
|
|
229
|
+
* through `callbacks`, and resolves with the run's `AiAgentRunResult`. The resolved value
|
|
230
|
+
* carries the `ops` the editor replays; nothing is applied to any document by this call.
|
|
231
|
+
* Reuses the same `consumeSse` transport as `streamPage`/`chatStream`.
|
|
232
|
+
*/
|
|
233
|
+
async runAgent(params, callbacks, options) {
|
|
234
|
+
const dynamicHeaders = await this.resolveHeaders();
|
|
235
|
+
const res = await this.fetchFn(this.endpoint, {
|
|
236
|
+
method: "POST",
|
|
237
|
+
headers: {
|
|
238
|
+
"Content-Type": "application/json",
|
|
239
|
+
...dynamicHeaders
|
|
240
|
+
},
|
|
241
|
+
body: JSON.stringify({
|
|
242
|
+
mode: "agent",
|
|
243
|
+
stream: true,
|
|
244
|
+
...params
|
|
245
|
+
}),
|
|
246
|
+
credentials: this.credentials,
|
|
247
|
+
signal: options?.signal
|
|
248
|
+
});
|
|
249
|
+
if (!res.ok) {
|
|
250
|
+
const errText = await res.text().catch(() => "");
|
|
251
|
+
const err = new Error(`Agent run failed [${res.status} ${res.statusText}]: ${errText}`);
|
|
252
|
+
callbacks?.onError?.(err);
|
|
253
|
+
throw err;
|
|
254
|
+
}
|
|
255
|
+
let finalResult = null;
|
|
256
|
+
let streamError = null;
|
|
257
|
+
await this.consumeSse(res, (event) => {
|
|
258
|
+
if (event.type === "agent-step") {
|
|
259
|
+
callbacks?.onAgentStep?.(event.step, event.maxSteps);
|
|
260
|
+
} else if (event.type === "tool-call") {
|
|
261
|
+
callbacks?.onToolCall?.({ id: event.id, name: event.name, input: event.input });
|
|
262
|
+
} else if (event.type === "tool-result") {
|
|
263
|
+
callbacks?.onToolResult?.({
|
|
264
|
+
id: event.id,
|
|
265
|
+
name: event.name,
|
|
266
|
+
ok: event.ok,
|
|
267
|
+
summary: event.summary
|
|
268
|
+
});
|
|
269
|
+
} else if (event.type === "agent-complete") {
|
|
270
|
+
finalResult = event.result;
|
|
271
|
+
callbacks?.onAgentComplete?.(event.result);
|
|
272
|
+
} else if (event.type === "error") {
|
|
273
|
+
streamError = new Error(event.error.message || "Agent error");
|
|
274
|
+
streamError.code = event.error.code;
|
|
275
|
+
callbacks?.onError?.(streamError);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
if (finalResult) return finalResult;
|
|
279
|
+
throw streamError ?? new Error("Agent stream ended without a result");
|
|
280
|
+
}
|
|
191
281
|
async generateSection(params, options) {
|
|
192
282
|
return this.sendRequest(
|
|
193
283
|
{
|
|
@@ -224,4 +314,4 @@ export {
|
|
|
224
314
|
KubuildAiClient,
|
|
225
315
|
createAiClient
|
|
226
316
|
};
|
|
227
|
-
//# sourceMappingURL=chunk-
|
|
317
|
+
//# sourceMappingURL=chunk-K7CBTQCY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client/ai-client.ts"],"sourcesContent":["import type { PageDocument, Node } from '@kubuild/schema';\nimport type {\n AiAgentRequest,\n AiAgentRunResult,\n AiGeneratePageRequest,\n AiGenerateSectionRequest,\n AiRefactorNodeRequest,\n AiChatRequest,\n AiChatResponse,\n AiChatMessage,\n AiGenerateResponse,\n AiStreamCallbacks,\n AiStreamEvent,\n PagePlan,\n AiPlanPageRequest,\n} from '../types';\n\nexport interface AiClientOptions {\n endpoint: string;\n headers?:\n | Record<string, string>\n | (() => Promise<Record<string, string>> | Record<string, string>);\n fetch?: typeof fetch;\n /**\n * Forwarded to every request (STORA-530). Hosts whose AI endpoint authenticates with\n * HttpOnly cookies rather than a bearer header need `'include'` here — cross-origin\n * `fetch` omits cookies by default, so without it the proxy endpoint sees an\n * unauthenticated request no matter what the browser has stored.\n */\n credentials?: RequestCredentials;\n}\n\nexport class KubuildAiClient {\n private endpoint: string;\n private headers?: AiClientOptions['headers'];\n private fetchFn: typeof fetch;\n private credentials?: RequestCredentials;\n\n constructor(options: AiClientOptions) {\n if (!options.endpoint) {\n throw new Error('KubuildAiClient requires an endpoint URL');\n }\n this.endpoint = options.endpoint;\n this.headers = options.headers;\n this.fetchFn = options.fetch || globalThis.fetch.bind(globalThis);\n this.credentials = options.credentials;\n }\n\n private async resolveHeaders(): Promise<Record<string, string>> {\n if (!this.headers) return {};\n if (typeof this.headers === 'function') {\n return await this.headers();\n }\n return this.headers;\n }\n\n private async sendRequest<T>(\n body: Record<string, unknown>,\n signal?: AbortSignal,\n ): Promise<T> {\n const dynamicHeaders = await this.resolveHeaders();\n\n const res = await this.fetchFn(this.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...dynamicHeaders,\n },\n body: JSON.stringify(body),\n credentials: this.credentials,\n signal,\n });\n\n const json = (await res.json()) as AiGenerateResponse<T>;\n\n if (!res.ok || !json.success || !json.data) {\n const errMessage = json?.error?.message || `HTTP ${res.status}: ${res.statusText}`;\n const error = new Error(errMessage);\n (error as unknown as { code?: string; details?: unknown }).code = json?.error?.code;\n (error as unknown as { code?: string; details?: unknown }).details = json?.error?.details;\n throw error;\n }\n\n return json.data;\n }\n\n /**\n * Standard single-pass full page generator (non-streaming).\n */\n async generatePage(\n params: AiGeneratePageRequest,\n options?: { signal?: AbortSignal },\n ): Promise<PageDocument> {\n return this.sendRequest<PageDocument>(\n {\n mode: 'full-page',\n ...params,\n },\n options?.signal,\n );\n }\n\n /**\n * Low-level SSE frame consumer shared by every streaming method on this client\n * (`streamPage`, `chatStream`) — one transport implementation, reused rather than\n * duplicated per stream \"flavor\" (STORA-515).\n *\n * Reads `res.body`, decodes `data: <json>\\n\\n` frames into `AiStreamEvent`s, and\n * invokes `onEvent` for each one in arrival order. If `onEvent` throws (the\n * conventional way callers signal a fatal `error` event), the throw propagates out of\n * this method after the reader lock is released.\n */\n private async consumeSse(\n res: Response,\n onEvent: (event: AiStreamEvent) => void,\n ): Promise<void> {\n if (!res.body) {\n throw new Error('Response body is null, cannot stream');\n }\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const parts = buffer.split('\\n\\n');\n buffer = parts.pop() || '';\n\n for (const part of parts) {\n const lines = part.split('\\n');\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n let event: AiStreamEvent;\n try {\n event = JSON.parse(line.slice(6).trim()) as AiStreamEvent;\n } catch {\n // Ignore incomplete or non-JSON data frames\n continue;\n }\n onEvent(event);\n }\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n /**\n * Progressive section streamer (SSE).\n * Calls callbacks on each event (status, metadata, section, complete).\n */\n async streamPage(\n params: AiGeneratePageRequest,\n callbacks?: AiStreamCallbacks,\n options?: { signal?: AbortSignal },\n ): Promise<PageDocument> {\n const dynamicHeaders = await this.resolveHeaders();\n\n const res = await this.fetchFn(this.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...dynamicHeaders,\n },\n body: JSON.stringify({\n mode: 'full-page',\n stream: true,\n ...params,\n }),\n credentials: this.credentials,\n signal: options?.signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n const err = new Error(`Streaming failed [${res.status} ${res.statusText}]: ${errText}`);\n callbacks?.onError?.(err);\n throw err;\n }\n\n let finalDoc: PageDocument | null = null;\n\n await this.consumeSse(res, (event) => {\n if (event.type === 'status') {\n callbacks?.onStatus?.(event.message);\n } else if (event.type === 'metadata') {\n callbacks?.onMetadata?.(event.metadata, event.rootPageNode);\n } else if (event.type === 'section') {\n callbacks?.onSection?.(event.section, event.index, event.total);\n } else if (event.type === 'complete') {\n finalDoc = event.document;\n callbacks?.onComplete?.(event.document);\n } else if (event.type === 'error') {\n const err = new Error(event.error.message || 'Stream error');\n (err as unknown as { code?: string }).code = event.error.code;\n callbacks?.onError?.(err);\n throw err;\n }\n });\n\n if (!finalDoc) {\n throw new Error('Stream ended without receiving complete document');\n }\n\n return finalDoc;\n }\n\n /**\n * Plans the website structure (title, description, and list of sections)\n * before generation so the user can review and approve.\n */\n async planPage(\n params: AiPlanPageRequest,\n options?: { signal?: AbortSignal },\n ): Promise<PagePlan> {\n const dynamicHeaders = await this.resolveHeaders();\n\n const res = await this.fetchFn(this.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...dynamicHeaders,\n },\n body: JSON.stringify({\n mode: 'plan',\n ...params,\n }),\n credentials: this.credentials,\n signal: options?.signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(`Plan request failed [${res.status} ${res.statusText}]: ${errText}`);\n }\n\n const json = (await res.json()) as AiGenerateResponse<PagePlan>;\n if (!json.success || !json.data) {\n throw new Error(json.error?.message || 'Failed to generate page plan');\n }\n\n return json.data;\n }\n\n /**\n * Token-level streaming chat (STORA-515). Calls `onChatChunk` for every partial chunk\n * as it arrives and resolves with the fully assembled assistant `AiChatMessage` once\n * the stream's terminal `chat-complete` event is received. Reuses the exact same SSE\n * transport as `streamPage` via `consumeSse` — no separate parsing path.\n */\n async chatStream(\n params: AiChatRequest,\n callbacks?: Pick<AiStreamCallbacks, 'onChatChunk' | 'onChatComplete' | 'onError'>,\n options?: { signal?: AbortSignal },\n ): Promise<AiChatMessage> {\n const dynamicHeaders = await this.resolveHeaders();\n\n const res = await this.fetchFn(this.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...dynamicHeaders,\n },\n body: JSON.stringify({\n mode: 'chat',\n stream: true,\n ...params,\n }),\n credentials: this.credentials,\n signal: options?.signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n const err = new Error(`Chat streaming failed [${res.status} ${res.statusText}]: ${errText}`);\n callbacks?.onError?.(err);\n throw err;\n }\n\n let finalMessage: AiChatMessage | null = null;\n\n await this.consumeSse(res, (event) => {\n if (event.type === 'chat-chunk') {\n callbacks?.onChatChunk?.(event.delta, event.content);\n } else if (event.type === 'chat-complete') {\n finalMessage = event.message;\n callbacks?.onChatComplete?.(event.message);\n } else if (event.type === 'error') {\n const err = new Error(event.error.message || 'Stream error');\n (err as unknown as { code?: string }).code = event.error.code;\n callbacks?.onError?.(err);\n throw err;\n }\n });\n\n if (!finalMessage) {\n throw new Error('Chat stream ended without receiving a complete message');\n }\n\n return finalMessage;\n }\n\n /**\n * Runs an agent turn over SSE (STORA-530).\n *\n * Streams the agent's progress — each reasoning step, each tool call and its result —\n * through `callbacks`, and resolves with the run's `AiAgentRunResult`. The resolved value\n * carries the `ops` the editor replays; nothing is applied to any document by this call.\n * Reuses the same `consumeSse` transport as `streamPage`/`chatStream`.\n */\n async runAgent(\n params: AiAgentRequest,\n callbacks?: Pick<\n AiStreamCallbacks,\n 'onAgentStep' | 'onToolCall' | 'onToolResult' | 'onAgentComplete' | 'onError'\n >,\n options?: { signal?: AbortSignal },\n ): Promise<AiAgentRunResult> {\n const dynamicHeaders = await this.resolveHeaders();\n\n const res = await this.fetchFn(this.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...dynamicHeaders,\n },\n body: JSON.stringify({\n mode: 'agent',\n stream: true,\n ...params,\n }),\n credentials: this.credentials,\n signal: options?.signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n const err = new Error(`Agent run failed [${res.status} ${res.statusText}]: ${errText}`);\n callbacks?.onError?.(err);\n throw err;\n }\n\n let finalResult: AiAgentRunResult | null = null;\n let streamError: Error | null = null;\n\n await this.consumeSse(res, (event) => {\n if (event.type === 'agent-step') {\n callbacks?.onAgentStep?.(event.step, event.maxSteps);\n } else if (event.type === 'tool-call') {\n callbacks?.onToolCall?.({ id: event.id, name: event.name, input: event.input });\n } else if (event.type === 'tool-result') {\n callbacks?.onToolResult?.({\n id: event.id,\n name: event.name,\n ok: event.ok,\n summary: event.summary,\n });\n } else if (event.type === 'agent-complete') {\n finalResult = event.result;\n callbacks?.onAgentComplete?.(event.result);\n } else if (event.type === 'error') {\n // The agent emits `agent-complete` before `error`, so ops produced before a\n // mid-run failure survive. Record the error instead of throwing out of the SSE\n // reader, and let the terminal result (if any) decide what the caller sees.\n streamError = new Error(event.error.message || 'Agent error');\n (streamError as unknown as { code?: string }).code = event.error.code;\n callbacks?.onError?.(streamError);\n }\n });\n\n if (finalResult) return finalResult;\n throw streamError ?? new Error('Agent stream ended without a result');\n }\n\n async generateSection(\n params: AiGenerateSectionRequest,\n options?: { signal?: AbortSignal },\n ): Promise<Node> {\n return this.sendRequest<Node>(\n {\n mode: 'section',\n ...params,\n },\n options?.signal,\n );\n }\n\n async refactorNode(\n params: AiRefactorNodeRequest,\n options?: { signal?: AbortSignal },\n ): Promise<Node> {\n return this.sendRequest<Node>(\n {\n mode: 'refactor',\n ...params,\n },\n options?.signal,\n );\n }\n\n async chat(\n params: AiChatRequest,\n options?: { signal?: AbortSignal },\n ): Promise<AiChatResponse> {\n return this.sendRequest<AiChatResponse>(\n {\n mode: 'chat',\n ...params,\n },\n options?.signal,\n );\n }\n}\n\nexport function createAiClient(options: AiClientOptions): KubuildAiClient {\n return new KubuildAiClient(options);\n}\n"],"mappings":"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","names":[]}
|