@browser-ai/core 2.0.2 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +463 -452
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +465 -454
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -28,10 +28,82 @@ __export(index_exports, {
|
|
|
28
28
|
});
|
|
29
29
|
module.exports = __toCommonJS(index_exports);
|
|
30
30
|
|
|
31
|
-
// src/
|
|
32
|
-
|
|
31
|
+
// ../shared/src/utils/tool-utils.ts
|
|
32
|
+
function isFunctionTool(tool) {
|
|
33
|
+
return tool.type === "function";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ../shared/src/utils/warnings.ts
|
|
37
|
+
function createUnsupportedSettingWarning(feature, details) {
|
|
38
|
+
return {
|
|
39
|
+
type: "unsupported",
|
|
40
|
+
feature,
|
|
41
|
+
details
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function createUnsupportedToolWarning(tool, details) {
|
|
45
|
+
return {
|
|
46
|
+
type: "unsupported",
|
|
47
|
+
feature: `tool:${tool.name}`,
|
|
48
|
+
details
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ../shared/src/tool-calling/build-json-system-prompt.ts
|
|
53
|
+
function buildJsonToolSystemPrompt(originalSystemPrompt, tools, options) {
|
|
54
|
+
if (!tools || tools.length === 0) {
|
|
55
|
+
return originalSystemPrompt || "";
|
|
56
|
+
}
|
|
57
|
+
const parallelInstruction = "Only request one tool call at a time. Wait for tool results before asking for another tool.";
|
|
58
|
+
const toolSchemas = tools.map((tool) => {
|
|
59
|
+
const schema = getParameters(tool);
|
|
60
|
+
return {
|
|
61
|
+
name: tool.name,
|
|
62
|
+
description: tool.description ?? "No description provided.",
|
|
63
|
+
parameters: schema || { type: "object", properties: {} }
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
const toolsJson = JSON.stringify(toolSchemas, null, 2);
|
|
67
|
+
const instructionBody = `You are a helpful AI assistant with access to tools.
|
|
68
|
+
|
|
69
|
+
# Available Tools
|
|
70
|
+
${toolsJson}
|
|
71
|
+
|
|
72
|
+
# Tool Calling Instructions
|
|
73
|
+
${parallelInstruction}
|
|
74
|
+
|
|
75
|
+
To call a tool, output JSON in this exact format inside a \`\`\`tool_call code fence:
|
|
76
|
+
|
|
77
|
+
\`\`\`tool_call
|
|
78
|
+
{"name": "tool_name", "arguments": {"param1": "value1", "param2": "value2"}}
|
|
79
|
+
\`\`\`
|
|
80
|
+
|
|
81
|
+
Tool responses will be provided in \`\`\`tool_result fences. Each line contains JSON like:
|
|
82
|
+
\`\`\`tool_result
|
|
83
|
+
{"id": "call_123", "name": "tool_name", "result": {...}, "error": false}
|
|
84
|
+
\`\`\`
|
|
85
|
+
Use the \`result\` payload (and treat \`error\` as a boolean flag) when continuing the conversation.
|
|
33
86
|
|
|
34
|
-
|
|
87
|
+
Important:
|
|
88
|
+
- Use exact tool and parameter names from the schema above
|
|
89
|
+
- Arguments must be a valid JSON object matching the tool's parameters
|
|
90
|
+
- You can include brief reasoning before or after the tool call
|
|
91
|
+
- If no tool is needed, respond directly without tool_call fences`;
|
|
92
|
+
if (originalSystemPrompt?.trim()) {
|
|
93
|
+
return `${originalSystemPrompt.trim()}
|
|
94
|
+
|
|
95
|
+
${instructionBody}`;
|
|
96
|
+
}
|
|
97
|
+
return instructionBody;
|
|
98
|
+
}
|
|
99
|
+
function getParameters(tool) {
|
|
100
|
+
if ("parameters" in tool) {
|
|
101
|
+
return tool.parameters;
|
|
102
|
+
}
|
|
103
|
+
return tool.inputSchema;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ../shared/src/tool-calling/format-tool-results.ts
|
|
35
107
|
function buildResultPayload(result) {
|
|
36
108
|
const payload = {
|
|
37
109
|
name: result.toolName,
|
|
@@ -55,7 +127,365 @@ ${payloads.join("\n")}
|
|
|
55
127
|
\`\`\``;
|
|
56
128
|
}
|
|
57
129
|
|
|
130
|
+
// ../shared/src/tool-calling/parse-json-function-calls.ts
|
|
131
|
+
var DEFAULT_OPTIONS = {
|
|
132
|
+
supportXmlTags: true,
|
|
133
|
+
supportPythonStyle: true,
|
|
134
|
+
supportParametersField: true
|
|
135
|
+
};
|
|
136
|
+
function generateToolCallId() {
|
|
137
|
+
return `call_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
138
|
+
}
|
|
139
|
+
function buildRegex(options) {
|
|
140
|
+
const patterns = [];
|
|
141
|
+
patterns.push("```tool[_-]?call\\s*([\\s\\S]*?)```");
|
|
142
|
+
if (options.supportXmlTags) {
|
|
143
|
+
patterns.push("<tool_call>\\s*([\\s\\S]*?)\\s*</tool_call>");
|
|
144
|
+
}
|
|
145
|
+
if (options.supportPythonStyle) {
|
|
146
|
+
patterns.push("\\[(\\w+)\\(([^)]*)\\)\\]");
|
|
147
|
+
}
|
|
148
|
+
return new RegExp(patterns.join("|"), "gi");
|
|
149
|
+
}
|
|
150
|
+
function parseJsonFunctionCalls(response, options = DEFAULT_OPTIONS) {
|
|
151
|
+
const mergedOptions = { ...DEFAULT_OPTIONS, ...options };
|
|
152
|
+
const regex = buildRegex(mergedOptions);
|
|
153
|
+
const matches = Array.from(response.matchAll(regex));
|
|
154
|
+
regex.lastIndex = 0;
|
|
155
|
+
if (matches.length === 0) {
|
|
156
|
+
return { toolCalls: [], textContent: response };
|
|
157
|
+
}
|
|
158
|
+
const toolCalls = [];
|
|
159
|
+
let textContent = response;
|
|
160
|
+
for (const match of matches) {
|
|
161
|
+
const fullMatch = match[0];
|
|
162
|
+
textContent = textContent.replace(fullMatch, "");
|
|
163
|
+
try {
|
|
164
|
+
if (mergedOptions.supportPythonStyle && match[0].startsWith("[")) {
|
|
165
|
+
const pythonMatch = /\[(\w+)\(([^)]*)\)\]/.exec(match[0]);
|
|
166
|
+
if (pythonMatch) {
|
|
167
|
+
const [, funcName, pythonArgs] = pythonMatch;
|
|
168
|
+
const args = {};
|
|
169
|
+
if (pythonArgs && pythonArgs.trim()) {
|
|
170
|
+
const argPairs = pythonArgs.split(",").map((s) => s.trim());
|
|
171
|
+
for (const pair of argPairs) {
|
|
172
|
+
const equalIndex = pair.indexOf("=");
|
|
173
|
+
if (equalIndex > 0) {
|
|
174
|
+
const key = pair.substring(0, equalIndex).trim();
|
|
175
|
+
let value = pair.substring(equalIndex + 1).trim();
|
|
176
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
177
|
+
value = value.substring(1, value.length - 1);
|
|
178
|
+
}
|
|
179
|
+
args[key] = value;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
toolCalls.push({
|
|
184
|
+
type: "tool-call",
|
|
185
|
+
toolCallId: generateToolCallId(),
|
|
186
|
+
toolName: funcName,
|
|
187
|
+
args
|
|
188
|
+
});
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const innerContent = match[1] || match[2] || "";
|
|
193
|
+
const trimmed = innerContent.trim();
|
|
194
|
+
if (!trimmed) continue;
|
|
195
|
+
try {
|
|
196
|
+
const parsed = JSON.parse(trimmed);
|
|
197
|
+
const callsArray = Array.isArray(parsed) ? parsed : [parsed];
|
|
198
|
+
for (const call of callsArray) {
|
|
199
|
+
if (!call.name) continue;
|
|
200
|
+
let args = call.arguments || (mergedOptions.supportParametersField ? call.parameters : null) || {};
|
|
201
|
+
if (typeof args === "string") {
|
|
202
|
+
try {
|
|
203
|
+
args = JSON.parse(args);
|
|
204
|
+
} catch {
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
toolCalls.push({
|
|
208
|
+
type: "tool-call",
|
|
209
|
+
toolCallId: call.id || generateToolCallId(),
|
|
210
|
+
toolName: call.name,
|
|
211
|
+
args
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
} catch {
|
|
215
|
+
const lines = trimmed.split("\n").filter((line) => line.trim());
|
|
216
|
+
for (const line of lines) {
|
|
217
|
+
try {
|
|
218
|
+
const call = JSON.parse(line.trim());
|
|
219
|
+
if (!call.name) continue;
|
|
220
|
+
let args = call.arguments || (mergedOptions.supportParametersField ? call.parameters : null) || {};
|
|
221
|
+
if (typeof args === "string") {
|
|
222
|
+
try {
|
|
223
|
+
args = JSON.parse(args);
|
|
224
|
+
} catch {
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
toolCalls.push({
|
|
228
|
+
type: "tool-call",
|
|
229
|
+
toolCallId: call.id || generateToolCallId(),
|
|
230
|
+
toolName: call.name,
|
|
231
|
+
args
|
|
232
|
+
});
|
|
233
|
+
} catch {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
} catch (error) {
|
|
239
|
+
console.warn("Failed to parse JSON tool call:", error);
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
textContent = textContent.replace(/\n{2,}/g, "\n");
|
|
244
|
+
return { toolCalls, textContent: textContent.trim() };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ../shared/src/streaming/tool-call-detector.ts
|
|
248
|
+
var DEFAULT_FENCE_PATTERNS = [
|
|
249
|
+
{ start: "```tool_call", end: "```", reconstructStart: "```tool_call\n" },
|
|
250
|
+
{ start: "```tool-call", end: "```", reconstructStart: "```tool-call\n" }
|
|
251
|
+
];
|
|
252
|
+
var EXTENDED_FENCE_PATTERNS = [
|
|
253
|
+
...DEFAULT_FENCE_PATTERNS,
|
|
254
|
+
{
|
|
255
|
+
start: "<tool_call>",
|
|
256
|
+
end: "</tool_call>",
|
|
257
|
+
reconstructStart: "<tool_call>"
|
|
258
|
+
}
|
|
259
|
+
];
|
|
260
|
+
var ToolCallFenceDetector = class {
|
|
261
|
+
constructor(options = {}) {
|
|
262
|
+
this.pythonStyleRegex = /\[(\w+)\(/g;
|
|
263
|
+
this.buffer = "";
|
|
264
|
+
this.inFence = false;
|
|
265
|
+
this.fenceStartBuffer = "";
|
|
266
|
+
// Accumulated fence content
|
|
267
|
+
this.currentFencePattern = null;
|
|
268
|
+
this.fencePatterns = options.patterns ?? EXTENDED_FENCE_PATTERNS;
|
|
269
|
+
this.enablePythonStyle = options.enablePythonStyle ?? true;
|
|
270
|
+
this.fenceStarts = this.fencePatterns.map((p) => p.start);
|
|
271
|
+
}
|
|
272
|
+
addChunk(chunk) {
|
|
273
|
+
this.buffer += chunk;
|
|
274
|
+
}
|
|
275
|
+
getBuffer() {
|
|
276
|
+
return this.buffer;
|
|
277
|
+
}
|
|
278
|
+
clearBuffer() {
|
|
279
|
+
this.buffer = "";
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Detects if there's a complete fence in the buffer
|
|
283
|
+
* @returns Detection result with fence info and safe text
|
|
284
|
+
*/
|
|
285
|
+
detectFence() {
|
|
286
|
+
const {
|
|
287
|
+
index: startIdx,
|
|
288
|
+
prefix: matchedPrefix,
|
|
289
|
+
pattern
|
|
290
|
+
} = this.findFenceStart(this.buffer);
|
|
291
|
+
if (startIdx === -1) {
|
|
292
|
+
const overlap = this.computeOverlapLength(this.buffer, this.fenceStarts);
|
|
293
|
+
const safeTextLength = this.buffer.length - overlap;
|
|
294
|
+
const prefixText2 = safeTextLength > 0 ? this.buffer.slice(0, safeTextLength) : "";
|
|
295
|
+
const remaining = overlap > 0 ? this.buffer.slice(-overlap) : "";
|
|
296
|
+
this.buffer = remaining;
|
|
297
|
+
return {
|
|
298
|
+
fence: null,
|
|
299
|
+
prefixText: prefixText2,
|
|
300
|
+
remainingText: "",
|
|
301
|
+
overlapLength: overlap
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
const prefixText = this.buffer.slice(0, startIdx);
|
|
305
|
+
this.buffer = this.buffer.slice(startIdx);
|
|
306
|
+
const prefixLength = matchedPrefix?.length ?? 0;
|
|
307
|
+
const fenceEnd = pattern?.end ?? "```";
|
|
308
|
+
const closingIdx = this.buffer.indexOf(fenceEnd, prefixLength);
|
|
309
|
+
if (closingIdx === -1) {
|
|
310
|
+
return {
|
|
311
|
+
fence: null,
|
|
312
|
+
prefixText,
|
|
313
|
+
remainingText: "",
|
|
314
|
+
overlapLength: 0
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
const endPos = closingIdx + fenceEnd.length;
|
|
318
|
+
const fence = this.buffer.slice(0, endPos);
|
|
319
|
+
const remainingText = this.buffer.slice(endPos);
|
|
320
|
+
this.buffer = "";
|
|
321
|
+
return {
|
|
322
|
+
fence,
|
|
323
|
+
prefixText,
|
|
324
|
+
remainingText,
|
|
325
|
+
overlapLength: 0
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Finds the first occurrence of any fence start marker
|
|
330
|
+
*
|
|
331
|
+
* @param text - Text to search in
|
|
332
|
+
* @returns Index of first fence start and which pattern matched
|
|
333
|
+
* @private
|
|
334
|
+
*/
|
|
335
|
+
findFenceStart(text) {
|
|
336
|
+
let bestIndex = -1;
|
|
337
|
+
let matchedPrefix = null;
|
|
338
|
+
let matchedPattern = null;
|
|
339
|
+
for (const pattern of this.fencePatterns) {
|
|
340
|
+
const idx = text.indexOf(pattern.start);
|
|
341
|
+
if (idx !== -1 && (bestIndex === -1 || idx < bestIndex)) {
|
|
342
|
+
bestIndex = idx;
|
|
343
|
+
matchedPrefix = pattern.start;
|
|
344
|
+
matchedPattern = pattern;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (this.enablePythonStyle) {
|
|
348
|
+
this.pythonStyleRegex.lastIndex = 0;
|
|
349
|
+
const pythonMatch = this.pythonStyleRegex.exec(text);
|
|
350
|
+
if (pythonMatch && (bestIndex === -1 || pythonMatch.index < bestIndex)) {
|
|
351
|
+
bestIndex = pythonMatch.index;
|
|
352
|
+
matchedPrefix = pythonMatch[0];
|
|
353
|
+
matchedPattern = {
|
|
354
|
+
start: pythonMatch[0],
|
|
355
|
+
end: ")]",
|
|
356
|
+
reconstructStart: pythonMatch[0],
|
|
357
|
+
isRegex: true
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return { index: bestIndex, prefix: matchedPrefix, pattern: matchedPattern };
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Computes the maximum overlap between the end of text and the start of any prefix
|
|
365
|
+
* @param text - Text to check for overlap
|
|
366
|
+
* @param prefixes - List of prefixes to check against
|
|
367
|
+
* @returns Length of the maximum overlap found
|
|
368
|
+
*/
|
|
369
|
+
computeOverlapLength(text, prefixes) {
|
|
370
|
+
let overlap = 0;
|
|
371
|
+
for (const prefix of prefixes) {
|
|
372
|
+
const maxLength = Math.min(text.length, prefix.length - 1);
|
|
373
|
+
for (let size = maxLength; size > 0; size -= 1) {
|
|
374
|
+
if (prefix.startsWith(text.slice(-size))) {
|
|
375
|
+
overlap = Math.max(overlap, size);
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return overlap;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Checks if the buffer currently contains any text
|
|
384
|
+
*/
|
|
385
|
+
hasContent() {
|
|
386
|
+
return this.buffer.length > 0;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Gets the buffer size
|
|
390
|
+
*/
|
|
391
|
+
getBufferSize() {
|
|
392
|
+
return this.buffer.length;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Detect and stream fence content in real-time for true incremental streaming
|
|
396
|
+
* @returns Streaming result with current state and safe content to emit
|
|
397
|
+
*/
|
|
398
|
+
detectStreamingFence() {
|
|
399
|
+
if (!this.inFence) {
|
|
400
|
+
const {
|
|
401
|
+
index: startIdx,
|
|
402
|
+
prefix: matchedPrefix,
|
|
403
|
+
pattern
|
|
404
|
+
} = this.findFenceStart(this.buffer);
|
|
405
|
+
if (startIdx === -1) {
|
|
406
|
+
const overlap = this.computeOverlapLength(
|
|
407
|
+
this.buffer,
|
|
408
|
+
this.fenceStarts
|
|
409
|
+
);
|
|
410
|
+
const safeTextLength = this.buffer.length - overlap;
|
|
411
|
+
const safeContent = safeTextLength > 0 ? this.buffer.slice(0, safeTextLength) : "";
|
|
412
|
+
this.buffer = this.buffer.slice(safeTextLength);
|
|
413
|
+
return {
|
|
414
|
+
inFence: false,
|
|
415
|
+
safeContent,
|
|
416
|
+
completeFence: null,
|
|
417
|
+
textAfterFence: ""
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
const prefixText = this.buffer.slice(0, startIdx);
|
|
421
|
+
const fenceStartLength = matchedPrefix?.length ?? 0;
|
|
422
|
+
this.buffer = this.buffer.slice(startIdx + fenceStartLength);
|
|
423
|
+
if (pattern && pattern.start.startsWith("```") && this.buffer.startsWith("\n")) {
|
|
424
|
+
this.buffer = this.buffer.slice(1);
|
|
425
|
+
}
|
|
426
|
+
this.inFence = true;
|
|
427
|
+
this.fenceStartBuffer = "";
|
|
428
|
+
this.currentFencePattern = pattern;
|
|
429
|
+
return {
|
|
430
|
+
inFence: true,
|
|
431
|
+
safeContent: prefixText,
|
|
432
|
+
// Emit any text before the fence
|
|
433
|
+
completeFence: null,
|
|
434
|
+
textAfterFence: ""
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
const fenceEnd = this.currentFencePattern?.end ?? "```";
|
|
438
|
+
const closingIdx = this.buffer.indexOf(fenceEnd);
|
|
439
|
+
if (closingIdx === -1) {
|
|
440
|
+
const overlap = this.computeOverlapLength(this.buffer, [fenceEnd]);
|
|
441
|
+
const safeContentLength = this.buffer.length - overlap;
|
|
442
|
+
if (safeContentLength > 0) {
|
|
443
|
+
const safeContent = this.buffer.slice(0, safeContentLength);
|
|
444
|
+
this.fenceStartBuffer += safeContent;
|
|
445
|
+
this.buffer = this.buffer.slice(safeContentLength);
|
|
446
|
+
return {
|
|
447
|
+
inFence: true,
|
|
448
|
+
safeContent,
|
|
449
|
+
completeFence: null,
|
|
450
|
+
textAfterFence: ""
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
return {
|
|
454
|
+
inFence: true,
|
|
455
|
+
safeContent: "",
|
|
456
|
+
completeFence: null,
|
|
457
|
+
textAfterFence: ""
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
const fenceContent = this.buffer.slice(0, closingIdx);
|
|
461
|
+
this.fenceStartBuffer += fenceContent;
|
|
462
|
+
const reconstructStart = this.currentFencePattern?.reconstructStart ?? "```tool_call\n";
|
|
463
|
+
const completeFence = `${reconstructStart}${this.fenceStartBuffer}${fenceEnd}`;
|
|
464
|
+
const textAfterFence = this.buffer.slice(closingIdx + fenceEnd.length);
|
|
465
|
+
this.inFence = false;
|
|
466
|
+
this.fenceStartBuffer = "";
|
|
467
|
+
this.currentFencePattern = null;
|
|
468
|
+
this.buffer = textAfterFence;
|
|
469
|
+
return {
|
|
470
|
+
inFence: false,
|
|
471
|
+
safeContent: fenceContent,
|
|
472
|
+
// Emit the last bit of fence content
|
|
473
|
+
completeFence,
|
|
474
|
+
textAfterFence
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
isInFence() {
|
|
478
|
+
return this.inFence;
|
|
479
|
+
}
|
|
480
|
+
resetStreamingState() {
|
|
481
|
+
this.inFence = false;
|
|
482
|
+
this.fenceStartBuffer = "";
|
|
483
|
+
this.currentFencePattern = null;
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
|
|
58
487
|
// src/convert-to-browser-ai-messages.ts
|
|
488
|
+
var import_provider = require("@ai-sdk/provider");
|
|
59
489
|
function convertBase64ToUint8Array(base64) {
|
|
60
490
|
try {
|
|
61
491
|
const binaryString = atob(base64);
|
|
@@ -246,151 +676,27 @@ function convertToBrowserAIMessages(prompt) {
|
|
|
246
676
|
break;
|
|
247
677
|
}
|
|
248
678
|
case "tool": {
|
|
249
|
-
const toolParts = message.content;
|
|
250
|
-
const results = toolParts.map(toToolResult);
|
|
251
|
-
const toolResultsJson = formatToolResults(results);
|
|
252
|
-
messages.push({
|
|
253
|
-
role: "user",
|
|
254
|
-
content: toolResultsJson
|
|
255
|
-
});
|
|
256
|
-
break;
|
|
257
|
-
}
|
|
258
|
-
default: {
|
|
259
|
-
const exhaustiveCheck = message;
|
|
260
|
-
throw new Error(
|
|
261
|
-
`Unsupported role: ${exhaustiveCheck.role ?? "unknown"}`
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
return { systemMessage, messages };
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// src/tool-calling/build-json-system-prompt.ts
|
|
270
|
-
function buildJsonToolSystemPrompt(originalSystemPrompt, tools, options) {
|
|
271
|
-
if (!tools || tools.length === 0) {
|
|
272
|
-
return originalSystemPrompt || "";
|
|
273
|
-
}
|
|
274
|
-
const parallelInstruction = "Only request one tool call at a time. Wait for tool results before asking for another tool.";
|
|
275
|
-
const toolSchemas = tools.map((tool) => {
|
|
276
|
-
const schema = getParameters(tool);
|
|
277
|
-
return {
|
|
278
|
-
name: tool.name,
|
|
279
|
-
description: tool.description ?? "No description provided.",
|
|
280
|
-
parameters: schema || { type: "object", properties: {} }
|
|
281
|
-
};
|
|
282
|
-
});
|
|
283
|
-
const toolsJson = JSON.stringify(toolSchemas, null, 2);
|
|
284
|
-
const instructionBody = `You are a helpful AI assistant with access to tools.
|
|
285
|
-
|
|
286
|
-
# Available Tools
|
|
287
|
-
${toolsJson}
|
|
288
|
-
|
|
289
|
-
# Tool Calling Instructions
|
|
290
|
-
${parallelInstruction}
|
|
291
|
-
|
|
292
|
-
To call a tool, output JSON in this exact format inside a \`\`\`tool_call code fence:
|
|
293
|
-
|
|
294
|
-
\`\`\`tool_call
|
|
295
|
-
{"name": "tool_name", "arguments": {"param1": "value1", "param2": "value2"}}
|
|
296
|
-
\`\`\`
|
|
297
|
-
|
|
298
|
-
Tool responses will be provided in \`\`\`tool_result fences. Each line contains JSON like:
|
|
299
|
-
\`\`\`tool_result
|
|
300
|
-
{"id": "call_123", "name": "tool_name", "result": {...}, "error": false}
|
|
301
|
-
\`\`\`
|
|
302
|
-
Use the \`result\` payload (and treat \`error\` as a boolean flag) when continuing the conversation.
|
|
303
|
-
|
|
304
|
-
Important:
|
|
305
|
-
- Use exact tool and parameter names from the schema above
|
|
306
|
-
- Arguments must be a valid JSON object matching the tool's parameters
|
|
307
|
-
- You can include brief reasoning before or after the tool call
|
|
308
|
-
- If no tool is needed, respond directly without tool_call fences`;
|
|
309
|
-
if (originalSystemPrompt?.trim()) {
|
|
310
|
-
return `${originalSystemPrompt.trim()}
|
|
311
|
-
|
|
312
|
-
${instructionBody}`;
|
|
313
|
-
}
|
|
314
|
-
return instructionBody;
|
|
315
|
-
}
|
|
316
|
-
function getParameters(tool) {
|
|
317
|
-
if ("parameters" in tool) {
|
|
318
|
-
return tool.parameters;
|
|
319
|
-
}
|
|
320
|
-
return tool.inputSchema;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// src/tool-calling/parse-json-function-calls.ts
|
|
324
|
-
var JSON_TOOL_CALL_FENCE_REGEX = /```tool[_-]?call\s*([\s\S]*?)```/gi;
|
|
325
|
-
function generateToolCallId() {
|
|
326
|
-
return `call_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
327
|
-
}
|
|
328
|
-
function parseJsonFunctionCalls(response) {
|
|
329
|
-
const matches = Array.from(response.matchAll(JSON_TOOL_CALL_FENCE_REGEX));
|
|
330
|
-
JSON_TOOL_CALL_FENCE_REGEX.lastIndex = 0;
|
|
331
|
-
if (matches.length === 0) {
|
|
332
|
-
return { toolCalls: [], textContent: response };
|
|
333
|
-
}
|
|
334
|
-
const toolCalls = [];
|
|
335
|
-
let textContent = response;
|
|
336
|
-
for (const match of matches) {
|
|
337
|
-
const [fullFence, innerContent] = match;
|
|
338
|
-
textContent = textContent.replace(fullFence, "");
|
|
339
|
-
try {
|
|
340
|
-
const trimmed = innerContent.trim();
|
|
341
|
-
try {
|
|
342
|
-
const parsed = JSON.parse(trimmed);
|
|
343
|
-
const callsArray = Array.isArray(parsed) ? parsed : [parsed];
|
|
344
|
-
for (const call of callsArray) {
|
|
345
|
-
if (!call.name) continue;
|
|
346
|
-
toolCalls.push({
|
|
347
|
-
type: "tool-call",
|
|
348
|
-
toolCallId: call.id || generateToolCallId(),
|
|
349
|
-
toolName: call.name,
|
|
350
|
-
args: call.arguments || {}
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
} catch {
|
|
354
|
-
const lines = trimmed.split("\n").filter((line) => line.trim());
|
|
355
|
-
for (const line of lines) {
|
|
356
|
-
try {
|
|
357
|
-
const call = JSON.parse(line.trim());
|
|
358
|
-
if (!call.name) continue;
|
|
359
|
-
toolCalls.push({
|
|
360
|
-
type: "tool-call",
|
|
361
|
-
toolCallId: call.id || generateToolCallId(),
|
|
362
|
-
toolName: call.name,
|
|
363
|
-
args: call.arguments || {}
|
|
364
|
-
});
|
|
365
|
-
} catch {
|
|
366
|
-
continue;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
679
|
+
const toolParts = message.content;
|
|
680
|
+
const results = toolParts.map(toToolResult);
|
|
681
|
+
const toolResultsJson = formatToolResults(results);
|
|
682
|
+
messages.push({
|
|
683
|
+
role: "user",
|
|
684
|
+
content: toolResultsJson
|
|
685
|
+
});
|
|
686
|
+
break;
|
|
687
|
+
}
|
|
688
|
+
default: {
|
|
689
|
+
const exhaustiveCheck = message;
|
|
690
|
+
throw new Error(
|
|
691
|
+
`Unsupported role: ${exhaustiveCheck.role ?? "unknown"}`
|
|
692
|
+
);
|
|
369
693
|
}
|
|
370
|
-
} catch (error) {
|
|
371
|
-
console.warn("Failed to parse JSON tool call:", error);
|
|
372
|
-
continue;
|
|
373
694
|
}
|
|
374
695
|
}
|
|
375
|
-
|
|
376
|
-
return { toolCalls, textContent: textContent.trim() };
|
|
696
|
+
return { systemMessage, messages };
|
|
377
697
|
}
|
|
378
698
|
|
|
379
699
|
// src/utils/warnings.ts
|
|
380
|
-
function createUnsupportedSettingWarning(feature, details) {
|
|
381
|
-
return {
|
|
382
|
-
type: "unsupported",
|
|
383
|
-
feature,
|
|
384
|
-
details
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
function createUnsupportedToolWarning(tool, details) {
|
|
388
|
-
return {
|
|
389
|
-
type: "unsupported",
|
|
390
|
-
feature: `tool:${tool.name}`,
|
|
391
|
-
details
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
700
|
function gatherUnsupportedSettingWarnings(options) {
|
|
395
701
|
const warnings = [];
|
|
396
702
|
if (options.maxOutputTokens != null) {
|
|
@@ -482,49 +788,6 @@ function getExpectedInputs(prompt) {
|
|
|
482
788
|
}
|
|
483
789
|
return Array.from(inputs).map((type) => ({ type }));
|
|
484
790
|
}
|
|
485
|
-
function prependSystemPromptToMessages(messages, systemPrompt) {
|
|
486
|
-
if (!systemPrompt.trim()) {
|
|
487
|
-
return messages;
|
|
488
|
-
}
|
|
489
|
-
const prompts = messages.map((message) => ({ ...message }));
|
|
490
|
-
const firstUserIndex = prompts.findIndex(
|
|
491
|
-
(message) => message.role === "user"
|
|
492
|
-
);
|
|
493
|
-
if (firstUserIndex !== -1) {
|
|
494
|
-
const firstUserMessage = prompts[firstUserIndex];
|
|
495
|
-
if (Array.isArray(firstUserMessage.content)) {
|
|
496
|
-
const content = firstUserMessage.content.slice();
|
|
497
|
-
content.unshift({
|
|
498
|
-
type: "text",
|
|
499
|
-
value: `${systemPrompt}
|
|
500
|
-
|
|
501
|
-
`
|
|
502
|
-
});
|
|
503
|
-
prompts[firstUserIndex] = {
|
|
504
|
-
...firstUserMessage,
|
|
505
|
-
content
|
|
506
|
-
};
|
|
507
|
-
} else if (typeof firstUserMessage.content === "string") {
|
|
508
|
-
prompts[firstUserIndex] = {
|
|
509
|
-
...firstUserMessage,
|
|
510
|
-
content: `${systemPrompt}
|
|
511
|
-
|
|
512
|
-
${firstUserMessage.content}`
|
|
513
|
-
};
|
|
514
|
-
}
|
|
515
|
-
} else {
|
|
516
|
-
prompts.unshift({
|
|
517
|
-
role: "user",
|
|
518
|
-
content: systemPrompt
|
|
519
|
-
});
|
|
520
|
-
}
|
|
521
|
-
return prompts;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
// src/utils/tool-utils.ts
|
|
525
|
-
function isFunctionTool(tool) {
|
|
526
|
-
return tool.type === "function";
|
|
527
|
-
}
|
|
528
791
|
|
|
529
792
|
// src/models/session-manager.ts
|
|
530
793
|
var import_provider2 = require("@ai-sdk/provider");
|
|
@@ -696,258 +959,6 @@ var SessionManager = class {
|
|
|
696
959
|
}
|
|
697
960
|
};
|
|
698
961
|
|
|
699
|
-
// src/streaming/tool-call-detector.ts
|
|
700
|
-
var ToolCallFenceDetector = class {
|
|
701
|
-
constructor() {
|
|
702
|
-
this.FENCE_STARTS = ["```tool_call"];
|
|
703
|
-
this.FENCE_END = "```";
|
|
704
|
-
this.buffer = "";
|
|
705
|
-
// Streaming state
|
|
706
|
-
this.inFence = false;
|
|
707
|
-
this.fenceStartBuffer = "";
|
|
708
|
-
}
|
|
709
|
-
// Accumulated fence content
|
|
710
|
-
/**
|
|
711
|
-
* Adds a chunk of text to the internal buffer
|
|
712
|
-
*
|
|
713
|
-
* @param chunk - Text chunk from the stream
|
|
714
|
-
*/
|
|
715
|
-
addChunk(chunk) {
|
|
716
|
-
this.buffer += chunk;
|
|
717
|
-
}
|
|
718
|
-
/**
|
|
719
|
-
* Gets the current buffer content
|
|
720
|
-
*/
|
|
721
|
-
getBuffer() {
|
|
722
|
-
return this.buffer;
|
|
723
|
-
}
|
|
724
|
-
/**
|
|
725
|
-
* Clears the internal buffer
|
|
726
|
-
*/
|
|
727
|
-
clearBuffer() {
|
|
728
|
-
this.buffer = "";
|
|
729
|
-
}
|
|
730
|
-
/**
|
|
731
|
-
* Detects if there's a complete fence in the buffer
|
|
732
|
-
*
|
|
733
|
-
* This method:
|
|
734
|
-
* 1. Searches for fence start markers
|
|
735
|
-
* 2. If found, looks for closing fence
|
|
736
|
-
* 3. Computes overlap for partial fences
|
|
737
|
-
* 4. Returns safe text that can be emitted
|
|
738
|
-
*
|
|
739
|
-
* @returns Detection result with fence info and safe text
|
|
740
|
-
*/
|
|
741
|
-
detectFence() {
|
|
742
|
-
const { index: startIdx, prefix: matchedPrefix } = this.findFenceStart(
|
|
743
|
-
this.buffer
|
|
744
|
-
);
|
|
745
|
-
if (startIdx === -1) {
|
|
746
|
-
const overlap = this.computeOverlapLength(this.buffer, this.FENCE_STARTS);
|
|
747
|
-
const safeTextLength = this.buffer.length - overlap;
|
|
748
|
-
const prefixText2 = safeTextLength > 0 ? this.buffer.slice(0, safeTextLength) : "";
|
|
749
|
-
const remaining = overlap > 0 ? this.buffer.slice(-overlap) : "";
|
|
750
|
-
this.buffer = remaining;
|
|
751
|
-
return {
|
|
752
|
-
fence: null,
|
|
753
|
-
prefixText: prefixText2,
|
|
754
|
-
remainingText: "",
|
|
755
|
-
overlapLength: overlap
|
|
756
|
-
};
|
|
757
|
-
}
|
|
758
|
-
const prefixText = this.buffer.slice(0, startIdx);
|
|
759
|
-
this.buffer = this.buffer.slice(startIdx);
|
|
760
|
-
const prefixLength = matchedPrefix?.length ?? 0;
|
|
761
|
-
const closingIdx = this.buffer.indexOf(this.FENCE_END, prefixLength);
|
|
762
|
-
if (closingIdx === -1) {
|
|
763
|
-
return {
|
|
764
|
-
fence: null,
|
|
765
|
-
prefixText,
|
|
766
|
-
remainingText: "",
|
|
767
|
-
overlapLength: 0
|
|
768
|
-
};
|
|
769
|
-
}
|
|
770
|
-
const endPos = closingIdx + this.FENCE_END.length;
|
|
771
|
-
const fence = this.buffer.slice(0, endPos);
|
|
772
|
-
const remainingText = this.buffer.slice(endPos);
|
|
773
|
-
this.buffer = "";
|
|
774
|
-
return {
|
|
775
|
-
fence,
|
|
776
|
-
prefixText,
|
|
777
|
-
remainingText,
|
|
778
|
-
overlapLength: 0
|
|
779
|
-
};
|
|
780
|
-
}
|
|
781
|
-
/**
|
|
782
|
-
* Finds the first occurrence of any fence start marker
|
|
783
|
-
*
|
|
784
|
-
* @param text - Text to search in
|
|
785
|
-
* @returns Index of first fence start and which prefix matched
|
|
786
|
-
* @private
|
|
787
|
-
*/
|
|
788
|
-
findFenceStart(text) {
|
|
789
|
-
let bestIndex = -1;
|
|
790
|
-
let matchedPrefix = null;
|
|
791
|
-
for (const prefix of this.FENCE_STARTS) {
|
|
792
|
-
const idx = text.indexOf(prefix);
|
|
793
|
-
if (idx !== -1 && (bestIndex === -1 || idx < bestIndex)) {
|
|
794
|
-
bestIndex = idx;
|
|
795
|
-
matchedPrefix = prefix;
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
return { index: bestIndex, prefix: matchedPrefix };
|
|
799
|
-
}
|
|
800
|
-
/**
|
|
801
|
-
* Computes the maximum overlap between the end of text and the start of any prefix
|
|
802
|
-
*
|
|
803
|
-
* This is crucial for streaming: if the buffer ends with "``", we can't emit it
|
|
804
|
-
* because the next chunk might be "`tool_call", completing a fence marker.
|
|
805
|
-
*
|
|
806
|
-
* @param text - Text to check for overlap
|
|
807
|
-
* @param prefixes - List of prefixes to check against
|
|
808
|
-
* @returns Length of the maximum overlap found
|
|
809
|
-
*
|
|
810
|
-
* @example
|
|
811
|
-
* ```typescript
|
|
812
|
-
* computeOverlapLength("hello ``", ["```tool_call"])
|
|
813
|
-
* // Returns: 2 (because "``" matches start of "```tool_call")
|
|
814
|
-
*
|
|
815
|
-
* computeOverlapLength("hello `", ["```tool_call"])
|
|
816
|
-
* // Returns: 1
|
|
817
|
-
*
|
|
818
|
-
* computeOverlapLength("hello world", ["```tool_call"])
|
|
819
|
-
* // Returns: 0 (no overlap)
|
|
820
|
-
* ```
|
|
821
|
-
*
|
|
822
|
-
* @private
|
|
823
|
-
*/
|
|
824
|
-
computeOverlapLength(text, prefixes) {
|
|
825
|
-
let overlap = 0;
|
|
826
|
-
for (const prefix of prefixes) {
|
|
827
|
-
const maxLength = Math.min(text.length, prefix.length - 1);
|
|
828
|
-
for (let size = maxLength; size > 0; size -= 1) {
|
|
829
|
-
if (prefix.startsWith(text.slice(-size))) {
|
|
830
|
-
overlap = Math.max(overlap, size);
|
|
831
|
-
break;
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
return overlap;
|
|
836
|
-
}
|
|
837
|
-
/**
|
|
838
|
-
* Checks if the buffer currently contains any text
|
|
839
|
-
*/
|
|
840
|
-
hasContent() {
|
|
841
|
-
return this.buffer.length > 0;
|
|
842
|
-
}
|
|
843
|
-
/**
|
|
844
|
-
* Gets the buffer size
|
|
845
|
-
*/
|
|
846
|
-
getBufferSize() {
|
|
847
|
-
return this.buffer.length;
|
|
848
|
-
}
|
|
849
|
-
/**
|
|
850
|
-
* Detect and stream fence content in real-time for true incremental streaming
|
|
851
|
-
*
|
|
852
|
-
* This method is designed for streaming tool calls as they arrive:
|
|
853
|
-
* 1. Detects when a fence starts and transitions to "inFence" state
|
|
854
|
-
* 2. While inFence, emits safe content that won't conflict with fence end marker
|
|
855
|
-
* 3. When fence ends, returns the complete fence for parsing
|
|
856
|
-
*
|
|
857
|
-
* @returns Streaming result with current state and safe content to emit
|
|
858
|
-
*/
|
|
859
|
-
detectStreamingFence() {
|
|
860
|
-
if (!this.inFence) {
|
|
861
|
-
const { index: startIdx, prefix: matchedPrefix } = this.findFenceStart(
|
|
862
|
-
this.buffer
|
|
863
|
-
);
|
|
864
|
-
if (startIdx === -1) {
|
|
865
|
-
const overlap = this.computeOverlapLength(
|
|
866
|
-
this.buffer,
|
|
867
|
-
this.FENCE_STARTS
|
|
868
|
-
);
|
|
869
|
-
const safeTextLength = this.buffer.length - overlap;
|
|
870
|
-
const safeContent = safeTextLength > 0 ? this.buffer.slice(0, safeTextLength) : "";
|
|
871
|
-
this.buffer = this.buffer.slice(safeTextLength);
|
|
872
|
-
return {
|
|
873
|
-
inFence: false,
|
|
874
|
-
safeContent,
|
|
875
|
-
completeFence: null,
|
|
876
|
-
textAfterFence: ""
|
|
877
|
-
};
|
|
878
|
-
}
|
|
879
|
-
const prefixText = this.buffer.slice(0, startIdx);
|
|
880
|
-
const fenceStartLength = matchedPrefix?.length ?? 0;
|
|
881
|
-
this.buffer = this.buffer.slice(startIdx + fenceStartLength);
|
|
882
|
-
if (this.buffer.startsWith("\n")) {
|
|
883
|
-
this.buffer = this.buffer.slice(1);
|
|
884
|
-
}
|
|
885
|
-
this.inFence = true;
|
|
886
|
-
this.fenceStartBuffer = "";
|
|
887
|
-
return {
|
|
888
|
-
inFence: true,
|
|
889
|
-
safeContent: prefixText,
|
|
890
|
-
// Emit any text before the fence
|
|
891
|
-
completeFence: null,
|
|
892
|
-
textAfterFence: ""
|
|
893
|
-
};
|
|
894
|
-
}
|
|
895
|
-
const closingIdx = this.buffer.indexOf(this.FENCE_END);
|
|
896
|
-
if (closingIdx === -1) {
|
|
897
|
-
const overlap = this.computeOverlapLength(this.buffer, [this.FENCE_END]);
|
|
898
|
-
const safeContentLength = this.buffer.length - overlap;
|
|
899
|
-
if (safeContentLength > 0) {
|
|
900
|
-
const safeContent = this.buffer.slice(0, safeContentLength);
|
|
901
|
-
this.fenceStartBuffer += safeContent;
|
|
902
|
-
this.buffer = this.buffer.slice(safeContentLength);
|
|
903
|
-
return {
|
|
904
|
-
inFence: true,
|
|
905
|
-
safeContent,
|
|
906
|
-
completeFence: null,
|
|
907
|
-
textAfterFence: ""
|
|
908
|
-
};
|
|
909
|
-
}
|
|
910
|
-
return {
|
|
911
|
-
inFence: true,
|
|
912
|
-
safeContent: "",
|
|
913
|
-
completeFence: null,
|
|
914
|
-
textAfterFence: ""
|
|
915
|
-
};
|
|
916
|
-
}
|
|
917
|
-
const fenceContent = this.buffer.slice(0, closingIdx);
|
|
918
|
-
this.fenceStartBuffer += fenceContent;
|
|
919
|
-
const completeFence = `${this.FENCE_STARTS[0]}
|
|
920
|
-
${this.fenceStartBuffer}
|
|
921
|
-
${this.FENCE_END}`;
|
|
922
|
-
const textAfterFence = this.buffer.slice(
|
|
923
|
-
closingIdx + this.FENCE_END.length
|
|
924
|
-
);
|
|
925
|
-
this.inFence = false;
|
|
926
|
-
this.fenceStartBuffer = "";
|
|
927
|
-
this.buffer = textAfterFence;
|
|
928
|
-
return {
|
|
929
|
-
inFence: false,
|
|
930
|
-
safeContent: fenceContent,
|
|
931
|
-
// Emit the last bit of fence content
|
|
932
|
-
completeFence,
|
|
933
|
-
textAfterFence
|
|
934
|
-
};
|
|
935
|
-
}
|
|
936
|
-
/**
|
|
937
|
-
* Check if currently inside a fence
|
|
938
|
-
*/
|
|
939
|
-
isInFence() {
|
|
940
|
-
return this.inFence;
|
|
941
|
-
}
|
|
942
|
-
/**
|
|
943
|
-
* Reset streaming state
|
|
944
|
-
*/
|
|
945
|
-
resetStreamingState() {
|
|
946
|
-
this.inFence = false;
|
|
947
|
-
this.fenceStartBuffer = "";
|
|
948
|
-
}
|
|
949
|
-
};
|
|
950
|
-
|
|
951
962
|
// src/browser-ai-language-model.ts
|
|
952
963
|
function doesBrowserSupportBrowserAI() {
|
|
953
964
|
return typeof LanguageModel !== "undefined";
|
|
@@ -1117,19 +1128,19 @@ var BrowserAIChatLanguageModel = class {
|
|
|
1117
1128
|
expectedInputs,
|
|
1118
1129
|
functionTools
|
|
1119
1130
|
} = converted;
|
|
1120
|
-
const
|
|
1121
|
-
const systemPrompt = await buildJsonToolSystemPrompt(
|
|
1131
|
+
const systemPrompt = buildJsonToolSystemPrompt(
|
|
1122
1132
|
systemMessage,
|
|
1123
1133
|
functionTools,
|
|
1124
1134
|
{
|
|
1125
1135
|
allowParallelToolCalls: false
|
|
1126
1136
|
}
|
|
1127
1137
|
);
|
|
1128
|
-
const
|
|
1129
|
-
|
|
1130
|
-
|
|
1138
|
+
const session = await this.getSession(
|
|
1139
|
+
void 0,
|
|
1140
|
+
expectedInputs,
|
|
1141
|
+
systemPrompt || void 0
|
|
1131
1142
|
);
|
|
1132
|
-
const rawResponse = await session.prompt(
|
|
1143
|
+
const rawResponse = await session.prompt(messages, promptOptions);
|
|
1133
1144
|
const { toolCalls, textContent } = parseJsonFunctionCalls(rawResponse);
|
|
1134
1145
|
if (toolCalls.length > 0) {
|
|
1135
1146
|
const toolCallsToEmit = toolCalls.slice(0, 1);
|
|
@@ -1164,7 +1175,7 @@ var BrowserAIChatLanguageModel = class {
|
|
|
1164
1175
|
reasoning: void 0
|
|
1165
1176
|
}
|
|
1166
1177
|
},
|
|
1167
|
-
request: { body: { messages
|
|
1178
|
+
request: { body: { messages, options: promptOptions } },
|
|
1168
1179
|
warnings
|
|
1169
1180
|
};
|
|
1170
1181
|
}
|
|
@@ -1190,7 +1201,7 @@ var BrowserAIChatLanguageModel = class {
|
|
|
1190
1201
|
reasoning: void 0
|
|
1191
1202
|
}
|
|
1192
1203
|
},
|
|
1193
|
-
request: { body: { messages
|
|
1204
|
+
request: { body: { messages, options: promptOptions } },
|
|
1194
1205
|
warnings
|
|
1195
1206
|
};
|
|
1196
1207
|
}
|
|
@@ -1237,23 +1248,23 @@ var BrowserAIChatLanguageModel = class {
|
|
|
1237
1248
|
expectedInputs,
|
|
1238
1249
|
functionTools
|
|
1239
1250
|
} = converted;
|
|
1240
|
-
const
|
|
1241
|
-
const systemPrompt = await buildJsonToolSystemPrompt(
|
|
1251
|
+
const systemPrompt = buildJsonToolSystemPrompt(
|
|
1242
1252
|
systemMessage,
|
|
1243
1253
|
functionTools,
|
|
1244
1254
|
{
|
|
1245
1255
|
allowParallelToolCalls: false
|
|
1246
1256
|
}
|
|
1247
1257
|
);
|
|
1248
|
-
const
|
|
1249
|
-
|
|
1250
|
-
|
|
1258
|
+
const session = await this.getSession(
|
|
1259
|
+
void 0,
|
|
1260
|
+
expectedInputs,
|
|
1261
|
+
systemPrompt || void 0
|
|
1251
1262
|
);
|
|
1252
1263
|
const streamOptions = {
|
|
1253
1264
|
...promptOptions,
|
|
1254
1265
|
signal: options.abortSignal
|
|
1255
1266
|
};
|
|
1256
|
-
const conversationHistory = [...
|
|
1267
|
+
const conversationHistory = [...messages];
|
|
1257
1268
|
const textId = "text-0";
|
|
1258
1269
|
const stream = new ReadableStream({
|
|
1259
1270
|
start: async (controller) => {
|
|
@@ -1559,7 +1570,7 @@ var BrowserAIChatLanguageModel = class {
|
|
|
1559
1570
|
});
|
|
1560
1571
|
return {
|
|
1561
1572
|
stream,
|
|
1562
|
-
request: { body: { messages
|
|
1573
|
+
request: { body: { messages, options: promptOptions } }
|
|
1563
1574
|
};
|
|
1564
1575
|
}
|
|
1565
1576
|
};
|