@mcp-use/agent 2.0.0-beta.2 → 2.0.0-beta.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/browser-agent.js +1 -1
- package/dist/index.js +1 -1
- package/dist/langchain.js +1 -1
- package/package.json +5 -9
- package/dist/browser-agent.cjs +0 -3275
- package/dist/browser-agent.cjs.map +0 -1
- package/dist/index.cjs +0 -3302
- package/dist/index.cjs.map +0 -1
- package/dist/langchain.cjs +0 -46340
- package/dist/langchain.cjs.map +0 -1
package/dist/index.cjs
DELETED
|
@@ -1,3302 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
|
|
30
|
-
// src/index.ts
|
|
31
|
-
var index_exports = {};
|
|
32
|
-
__export(index_exports, {
|
|
33
|
-
BaseAdapter: () => BaseAdapter,
|
|
34
|
-
DEFAULT_OLLAMA_BASE_URL: () => DEFAULT_OLLAMA_BASE_URL,
|
|
35
|
-
LlmRequestError: () => LlmRequestError,
|
|
36
|
-
MCPAgent: () => MCPAgent,
|
|
37
|
-
NativeAdapter: () => NativeAdapter,
|
|
38
|
-
OllamaCorsError: () => OllamaCorsError,
|
|
39
|
-
PROMPTS: () => PROMPTS,
|
|
40
|
-
RemoteAgent: () => RemoteAgent,
|
|
41
|
-
buildOllamaApiUrl: () => buildOllamaApiUrl,
|
|
42
|
-
chat: () => completeChat,
|
|
43
|
-
completeChat: () => completeChat,
|
|
44
|
-
convertMessagesToProvider: () => convertMessagesToProvider,
|
|
45
|
-
normalizeOllamaBaseUrl: () => normalizeOllamaBaseUrl,
|
|
46
|
-
parseLLMStringToProviderConfig: () => parseLLMStringToProviderConfig,
|
|
47
|
-
providerConfigFromOptions: () => providerConfigFromOptions
|
|
48
|
-
});
|
|
49
|
-
module.exports = __toCommonJS(index_exports);
|
|
50
|
-
|
|
51
|
-
// src/agents/mcp_agent.ts
|
|
52
|
-
var import_client5 = require("@mcp-use/client");
|
|
53
|
-
|
|
54
|
-
// src/adapters/native_adapter.ts
|
|
55
|
-
var import_client2 = require("@mcp-use/client");
|
|
56
|
-
|
|
57
|
-
// src/adapters/base.ts
|
|
58
|
-
var import_client = require("@mcp-use/client");
|
|
59
|
-
var BaseAdapter = class {
|
|
60
|
-
/**
|
|
61
|
-
* List of tool names that should not be available.
|
|
62
|
-
*/
|
|
63
|
-
disallowedTools;
|
|
64
|
-
/**
|
|
65
|
-
* Internal cache that maps a connector instance to the list of tools
|
|
66
|
-
* generated for it.
|
|
67
|
-
*/
|
|
68
|
-
connectorToolMap = /* @__PURE__ */ new Map();
|
|
69
|
-
constructor(disallowedTools) {
|
|
70
|
-
this.disallowedTools = disallowedTools ?? [];
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Create tools from an MCPClient instance.
|
|
74
|
-
*
|
|
75
|
-
* This is the recommended way to create tools from an MCPClient, as it handles
|
|
76
|
-
* session creation and connector extraction automatically.
|
|
77
|
-
*
|
|
78
|
-
* @param client The MCPClient to extract tools from.
|
|
79
|
-
* @param disallowedTools Optional list of tool names to exclude.
|
|
80
|
-
* @returns A promise that resolves with a list of converted tools.
|
|
81
|
-
*/
|
|
82
|
-
static async createTools(client, disallowedTools) {
|
|
83
|
-
const adapter = new this(disallowedTools);
|
|
84
|
-
if (!client.activeSessions || Object.keys(client.activeSessions).length === 0) {
|
|
85
|
-
import_client.logger.debug("No active sessions found, creating new ones...");
|
|
86
|
-
await client.createAllSessions();
|
|
87
|
-
}
|
|
88
|
-
const sessions = client.getAllActiveSessions();
|
|
89
|
-
const connectors = Object.values(sessions).map(
|
|
90
|
-
(session) => session.connector
|
|
91
|
-
);
|
|
92
|
-
return adapter.createToolsFromConnectors(connectors);
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Dynamically load tools for a specific connector.
|
|
96
|
-
*
|
|
97
|
-
* @param connector The connector to load tools for.
|
|
98
|
-
* @returns The list of tools that were loaded in the target framework's format.
|
|
99
|
-
*/
|
|
100
|
-
async loadToolsForConnector(connector) {
|
|
101
|
-
if (this.connectorToolMap.has(connector)) {
|
|
102
|
-
const cached = this.connectorToolMap.get(connector);
|
|
103
|
-
import_client.logger.debug(`Returning ${cached.length} existing tools for connector`);
|
|
104
|
-
return cached;
|
|
105
|
-
}
|
|
106
|
-
const connectorTools = [];
|
|
107
|
-
const success = await this.ensureConnectorInitialized(connector);
|
|
108
|
-
if (!success) {
|
|
109
|
-
return [];
|
|
110
|
-
}
|
|
111
|
-
for (const tool of connector.tools) {
|
|
112
|
-
const converted = this.convertTool(tool, connector);
|
|
113
|
-
if (converted) {
|
|
114
|
-
connectorTools.push(converted);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
this.connectorToolMap.set(connector, connectorTools);
|
|
118
|
-
import_client.logger.debug(
|
|
119
|
-
`Loaded ${connectorTools.length} new tools for connector: ${connectorTools.map((t) => t?.name ?? String(t)).join(", ")}`
|
|
120
|
-
);
|
|
121
|
-
return connectorTools;
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Create tools from MCP tools in all provided connectors.
|
|
125
|
-
*
|
|
126
|
-
* @param connectors List of MCP connectors to create tools from.
|
|
127
|
-
* @returns A promise that resolves with all converted tools.
|
|
128
|
-
*/
|
|
129
|
-
async createToolsFromConnectors(connectors) {
|
|
130
|
-
const tools = [];
|
|
131
|
-
for (const connector of connectors) {
|
|
132
|
-
const connectorTools = await this.loadToolsForConnector(connector);
|
|
133
|
-
tools.push(...connectorTools);
|
|
134
|
-
}
|
|
135
|
-
import_client.logger.debug(`Available tools: ${tools.length}`);
|
|
136
|
-
return tools;
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Dynamically load resources for a specific connector.
|
|
140
|
-
*
|
|
141
|
-
* @param connector The connector to load resources for.
|
|
142
|
-
* @returns The list of resources that were loaded in the target framework's format.
|
|
143
|
-
*/
|
|
144
|
-
async loadResourcesForConnector(connector) {
|
|
145
|
-
const connectorResources = [];
|
|
146
|
-
const success = await this.ensureConnectorInitialized(connector);
|
|
147
|
-
if (!success) {
|
|
148
|
-
return [];
|
|
149
|
-
}
|
|
150
|
-
try {
|
|
151
|
-
const resourcesResult = await connector.listAllResources();
|
|
152
|
-
const resources = resourcesResult?.resources || [];
|
|
153
|
-
if (this.convertResource) {
|
|
154
|
-
for (const resource of resources) {
|
|
155
|
-
const converted = this.convertResource(resource, connector);
|
|
156
|
-
if (converted) {
|
|
157
|
-
connectorResources.push(converted);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
import_client.logger.debug(
|
|
162
|
-
`Loaded ${connectorResources.length} new resources for connector: ${connectorResources.map((r) => r?.name ?? String(r)).join(", ")}`
|
|
163
|
-
);
|
|
164
|
-
} catch (err) {
|
|
165
|
-
import_client.logger.warn(`Error loading resources for connector: ${err}`);
|
|
166
|
-
}
|
|
167
|
-
return connectorResources;
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Dynamically load prompts for a specific connector.
|
|
171
|
-
*
|
|
172
|
-
* @param connector The connector to load prompts for.
|
|
173
|
-
* @returns The list of prompts that were loaded in the target framework's format.
|
|
174
|
-
*/
|
|
175
|
-
async loadPromptsForConnector(connector) {
|
|
176
|
-
const connectorPrompts = [];
|
|
177
|
-
const success = await this.ensureConnectorInitialized(connector);
|
|
178
|
-
if (!success) {
|
|
179
|
-
return [];
|
|
180
|
-
}
|
|
181
|
-
try {
|
|
182
|
-
const promptsResult = await connector.listPrompts();
|
|
183
|
-
const prompts = promptsResult?.prompts || [];
|
|
184
|
-
if (this.convertPrompt) {
|
|
185
|
-
for (const prompt of prompts) {
|
|
186
|
-
const converted = this.convertPrompt(prompt, connector);
|
|
187
|
-
if (converted) {
|
|
188
|
-
connectorPrompts.push(converted);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
import_client.logger.debug(
|
|
193
|
-
`Loaded ${connectorPrompts.length} new prompts for connector: ${connectorPrompts.map((p) => p?.name ?? String(p)).join(", ")}`
|
|
194
|
-
);
|
|
195
|
-
} catch (err) {
|
|
196
|
-
import_client.logger.warn(`Error loading prompts for connector: ${err}`);
|
|
197
|
-
}
|
|
198
|
-
return connectorPrompts;
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Create resources from MCP resources in all provided connectors.
|
|
202
|
-
*
|
|
203
|
-
* @param connectors List of MCP connectors to create resources from.
|
|
204
|
-
* @returns A promise that resolves with all converted resources.
|
|
205
|
-
*/
|
|
206
|
-
async createResourcesFromConnectors(connectors) {
|
|
207
|
-
const resources = [];
|
|
208
|
-
for (const connector of connectors) {
|
|
209
|
-
const connectorResources = await this.loadResourcesForConnector(connector);
|
|
210
|
-
resources.push(...connectorResources);
|
|
211
|
-
}
|
|
212
|
-
import_client.logger.debug(`Available resources: ${resources.length}`);
|
|
213
|
-
return resources;
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* Create prompts from MCP prompts in all provided connectors.
|
|
217
|
-
*
|
|
218
|
-
* @param connectors List of MCP connectors to create prompts from.
|
|
219
|
-
* @returns A promise that resolves with all converted prompts.
|
|
220
|
-
*/
|
|
221
|
-
async createPromptsFromConnectors(connectors) {
|
|
222
|
-
const prompts = [];
|
|
223
|
-
for (const connector of connectors) {
|
|
224
|
-
const connectorPrompts = await this.loadPromptsForConnector(connector);
|
|
225
|
-
prompts.push(...connectorPrompts);
|
|
226
|
-
}
|
|
227
|
-
import_client.logger.debug(`Available prompts: ${prompts.length}`);
|
|
228
|
-
return prompts;
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Check if a connector is initialized and has tools.
|
|
232
|
-
*
|
|
233
|
-
* @param connector The connector to check.
|
|
234
|
-
* @returns True if the connector is initialized and has tools, false otherwise.
|
|
235
|
-
*/
|
|
236
|
-
checkConnectorInitialized(connector) {
|
|
237
|
-
return Boolean(connector.tools && connector.tools.length);
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Ensure a connector is initialized.
|
|
241
|
-
*
|
|
242
|
-
* @param connector The connector to initialize.
|
|
243
|
-
* @returns True if initialization succeeded, false otherwise.
|
|
244
|
-
*/
|
|
245
|
-
async ensureConnectorInitialized(connector) {
|
|
246
|
-
if (!this.checkConnectorInitialized(connector)) {
|
|
247
|
-
import_client.logger.debug("Connector doesn't have tools, initializing it");
|
|
248
|
-
try {
|
|
249
|
-
await connector.initialize();
|
|
250
|
-
return true;
|
|
251
|
-
} catch (err) {
|
|
252
|
-
import_client.logger.error(`Error initializing connector: ${err}`);
|
|
253
|
-
return false;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
return true;
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
// src/adapters/native_adapter.ts
|
|
261
|
-
function sanitizeToolName(name) {
|
|
262
|
-
return name.replace(/[^A-Za-z0-9_]+/g, "_").toLowerCase().replace(/^_+|_+$/g, "");
|
|
263
|
-
}
|
|
264
|
-
var NativeAdapter = class extends BaseAdapter {
|
|
265
|
-
usedToolNames = /* @__PURE__ */ new Set();
|
|
266
|
-
handlers = /* @__PURE__ */ new Map();
|
|
267
|
-
constructor(disallowedTools = []) {
|
|
268
|
-
super(disallowedTools);
|
|
269
|
-
}
|
|
270
|
-
async createToolsFromConnectors(connectors) {
|
|
271
|
-
this.usedToolNames.clear();
|
|
272
|
-
this.handlers.clear();
|
|
273
|
-
return super.createToolsFromConnectors(connectors);
|
|
274
|
-
}
|
|
275
|
-
createCallTool() {
|
|
276
|
-
const handlers = this.handlers;
|
|
277
|
-
return async (name, args) => {
|
|
278
|
-
const handler = handlers.get(name);
|
|
279
|
-
if (!handler) {
|
|
280
|
-
throw new Error(`Unknown tool: ${name}`);
|
|
281
|
-
}
|
|
282
|
-
try {
|
|
283
|
-
if (handler.kind === "tool") {
|
|
284
|
-
return await handler.connector.callTool(handler.mcpName, args);
|
|
285
|
-
}
|
|
286
|
-
if (handler.kind === "resource") {
|
|
287
|
-
const uri = handler.resourceUri ?? handler.mcpName;
|
|
288
|
-
return await handler.connector.readResource(uri);
|
|
289
|
-
}
|
|
290
|
-
return await handler.connector.getPrompt(handler.mcpName, args);
|
|
291
|
-
} catch (err) {
|
|
292
|
-
import_client2.logger.error(`Error executing native MCP tool ${name}: ${err}`);
|
|
293
|
-
throw err;
|
|
294
|
-
}
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
toProviderTools(entries) {
|
|
298
|
-
return entries.map(({ name, description, inputSchema }) => ({
|
|
299
|
-
name,
|
|
300
|
-
description,
|
|
301
|
-
inputSchema
|
|
302
|
-
}));
|
|
303
|
-
}
|
|
304
|
-
reserveName(name, kind) {
|
|
305
|
-
if (!this.usedToolNames.has(name)) {
|
|
306
|
-
this.usedToolNames.add(name);
|
|
307
|
-
return name;
|
|
308
|
-
}
|
|
309
|
-
if (kind) {
|
|
310
|
-
const prefixed = `${kind}_${name}`;
|
|
311
|
-
if (!this.usedToolNames.has(prefixed)) {
|
|
312
|
-
this.usedToolNames.add(prefixed);
|
|
313
|
-
return prefixed;
|
|
314
|
-
}
|
|
315
|
-
let i2 = 2;
|
|
316
|
-
while (this.usedToolNames.has(`${prefixed}_${i2}`)) i2++;
|
|
317
|
-
const fallback2 = `${prefixed}_${i2}`;
|
|
318
|
-
this.usedToolNames.add(fallback2);
|
|
319
|
-
return fallback2;
|
|
320
|
-
}
|
|
321
|
-
let i = 2;
|
|
322
|
-
while (this.usedToolNames.has(`${name}_${i}`)) i++;
|
|
323
|
-
const fallback = `${name}_${i}`;
|
|
324
|
-
this.usedToolNames.add(fallback);
|
|
325
|
-
return fallback;
|
|
326
|
-
}
|
|
327
|
-
register(entry, handler) {
|
|
328
|
-
this.handlers.set(entry.name, handler);
|
|
329
|
-
return entry;
|
|
330
|
-
}
|
|
331
|
-
convertTool(mcpTool, connector) {
|
|
332
|
-
if (this.disallowedTools.includes(mcpTool.name)) return null;
|
|
333
|
-
const toolName = this.reserveName(mcpTool.name ?? "tool");
|
|
334
|
-
const entry = {
|
|
335
|
-
name: toolName,
|
|
336
|
-
dispatchKey: toolName,
|
|
337
|
-
description: mcpTool.description ?? "",
|
|
338
|
-
inputSchema: mcpTool.inputSchema ?? {
|
|
339
|
-
type: "object",
|
|
340
|
-
properties: {}
|
|
341
|
-
}
|
|
342
|
-
};
|
|
343
|
-
return this.register(entry, {
|
|
344
|
-
connector,
|
|
345
|
-
kind: "tool",
|
|
346
|
-
mcpName: mcpTool.name
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
convertResource(mcpResource, connector) {
|
|
350
|
-
const resourceBaseName = sanitizeToolName(mcpResource.name || mcpResource.uri) || "resource";
|
|
351
|
-
const resourceName = this.reserveName(resourceBaseName, "resource");
|
|
352
|
-
const entry = {
|
|
353
|
-
name: resourceName,
|
|
354
|
-
dispatchKey: resourceName,
|
|
355
|
-
description: mcpResource.description || `Return the content of the resource located at URI ${mcpResource.uri}.`,
|
|
356
|
-
inputSchema: { type: "object", properties: {} }
|
|
357
|
-
};
|
|
358
|
-
return this.register(entry, {
|
|
359
|
-
connector,
|
|
360
|
-
kind: "resource",
|
|
361
|
-
mcpName: mcpResource.uri,
|
|
362
|
-
resourceUri: mcpResource.uri
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
|
-
convertPrompt(mcpPrompt, connector) {
|
|
366
|
-
const properties = {};
|
|
367
|
-
const required = [];
|
|
368
|
-
if (mcpPrompt.arguments?.length) {
|
|
369
|
-
for (const arg of mcpPrompt.arguments) {
|
|
370
|
-
properties[arg.name] = { type: "string", description: arg.description };
|
|
371
|
-
if (arg.required !== false) required.push(arg.name);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
const promptBaseName = sanitizeToolName(mcpPrompt.name || "prompt") || "prompt";
|
|
375
|
-
const promptName = this.reserveName(promptBaseName, "prompt");
|
|
376
|
-
const entry = {
|
|
377
|
-
name: promptName,
|
|
378
|
-
dispatchKey: promptName,
|
|
379
|
-
description: mcpPrompt.description || "",
|
|
380
|
-
inputSchema: {
|
|
381
|
-
type: "object",
|
|
382
|
-
properties,
|
|
383
|
-
...required.length > 0 ? { required } : {}
|
|
384
|
-
}
|
|
385
|
-
};
|
|
386
|
-
return this.register(entry, {
|
|
387
|
-
connector,
|
|
388
|
-
kind: "prompt",
|
|
389
|
-
mcpName: mcpPrompt.name
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
// src/llm/toolResultParts.ts
|
|
395
|
-
function makeText(text) {
|
|
396
|
-
return { type: "text", text };
|
|
397
|
-
}
|
|
398
|
-
function makeImage(data, mimeType) {
|
|
399
|
-
return {
|
|
400
|
-
type: "image",
|
|
401
|
-
url: `data:${mimeType};base64,${data}`,
|
|
402
|
-
mimeType,
|
|
403
|
-
data
|
|
404
|
-
};
|
|
405
|
-
}
|
|
406
|
-
function isMcpResultShape(value) {
|
|
407
|
-
if (!value || typeof value !== "object") return false;
|
|
408
|
-
const v = value;
|
|
409
|
-
if (Array.isArray(v.content)) return true;
|
|
410
|
-
return "structuredContent" in v;
|
|
411
|
-
}
|
|
412
|
-
function isToolResultError(result) {
|
|
413
|
-
return typeof result === "object" && result !== null && result.isError === true;
|
|
414
|
-
}
|
|
415
|
-
function blockToPart(block) {
|
|
416
|
-
if (!block || typeof block !== "object") return null;
|
|
417
|
-
switch (block.type) {
|
|
418
|
-
case "text": {
|
|
419
|
-
const text = block.text;
|
|
420
|
-
return typeof text === "string" && text.length > 0 ? makeText(text) : null;
|
|
421
|
-
}
|
|
422
|
-
case "image": {
|
|
423
|
-
const b = block;
|
|
424
|
-
if (typeof b.data !== "string" || !b.data) return null;
|
|
425
|
-
const mime = typeof b.mimeType === "string" && b.mimeType ? b.mimeType : "image/png";
|
|
426
|
-
return makeImage(b.data, mime);
|
|
427
|
-
}
|
|
428
|
-
case "audio": {
|
|
429
|
-
const b = block;
|
|
430
|
-
const mime = typeof b.mimeType === "string" && b.mimeType ? b.mimeType : "audio/*";
|
|
431
|
-
const bytes = typeof b.data === "string" ? b.data.length : 0;
|
|
432
|
-
return makeText(`[audio: ${mime}, base64 omitted (${bytes} chars)]`);
|
|
433
|
-
}
|
|
434
|
-
case "resource": {
|
|
435
|
-
const r = block.resource ?? {};
|
|
436
|
-
if (typeof r.text === "string" && r.text.length > 0) {
|
|
437
|
-
return makeText(r.text);
|
|
438
|
-
}
|
|
439
|
-
if (typeof r.blob === "string" && typeof r.mimeType === "string" && r.mimeType.startsWith("image/")) {
|
|
440
|
-
return makeImage(r.blob, r.mimeType);
|
|
441
|
-
}
|
|
442
|
-
const uri = typeof r.uri === "string" ? r.uri : "<unknown>";
|
|
443
|
-
const mime = typeof r.mimeType === "string" ? r.mimeType : "unknown";
|
|
444
|
-
return makeText(`[resource: ${uri} (${mime})]`);
|
|
445
|
-
}
|
|
446
|
-
case "resource_link": {
|
|
447
|
-
const b = block;
|
|
448
|
-
const uri = typeof b.uri === "string" ? b.uri : "<unknown>";
|
|
449
|
-
const name = typeof b.name === "string" ? ` "${b.name}"` : "";
|
|
450
|
-
return makeText(`[resource_link${name}: ${uri}]`);
|
|
451
|
-
}
|
|
452
|
-
default: {
|
|
453
|
-
return makeText(
|
|
454
|
-
`[unsupported tool content block: ${String(block.type)}]`
|
|
455
|
-
);
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
function stripMeta(value) {
|
|
460
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
461
|
-
const { _meta: _ignored, ...rest } = value;
|
|
462
|
-
return rest;
|
|
463
|
-
}
|
|
464
|
-
return value;
|
|
465
|
-
}
|
|
466
|
-
function extractToolResultParts(result) {
|
|
467
|
-
const stripped = stripMeta(result);
|
|
468
|
-
if (typeof stripped === "string") {
|
|
469
|
-
return stripped.length > 0 ? [makeText(stripped)] : [];
|
|
470
|
-
}
|
|
471
|
-
if (isMcpResultShape(stripped)) {
|
|
472
|
-
const r = stripped;
|
|
473
|
-
const parts = [];
|
|
474
|
-
if (Array.isArray(r.content)) {
|
|
475
|
-
for (const block of r.content) {
|
|
476
|
-
const p = blockToPart(block);
|
|
477
|
-
if (p) parts.push(p);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
if (r.structuredContent !== void 0) {
|
|
481
|
-
try {
|
|
482
|
-
parts.push(
|
|
483
|
-
makeText(`structuredContent: ${JSON.stringify(r.structuredContent)}`)
|
|
484
|
-
);
|
|
485
|
-
} catch {
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
if (r.isError) {
|
|
489
|
-
parts.unshift(makeText("[tool reported isError=true]"));
|
|
490
|
-
}
|
|
491
|
-
return parts;
|
|
492
|
-
}
|
|
493
|
-
try {
|
|
494
|
-
return [makeText(JSON.stringify(stripped))];
|
|
495
|
-
} catch {
|
|
496
|
-
return [makeText(String(stripped))];
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
function collapseToString(parts) {
|
|
500
|
-
if (parts.length === 0) return "";
|
|
501
|
-
if (parts.every((p) => p.type === "text")) {
|
|
502
|
-
return parts.map((p) => p.text).join("\n");
|
|
503
|
-
}
|
|
504
|
-
return null;
|
|
505
|
-
}
|
|
506
|
-
function toolResultToContent(result) {
|
|
507
|
-
const parts = extractToolResultParts(result);
|
|
508
|
-
const collapsed = collapseToString(parts);
|
|
509
|
-
return collapsed !== null ? collapsed : parts;
|
|
510
|
-
}
|
|
511
|
-
function partitionToolContent(content) {
|
|
512
|
-
if (typeof content === "string") {
|
|
513
|
-
return { text: content, imageParts: [], isString: true };
|
|
514
|
-
}
|
|
515
|
-
const texts = [];
|
|
516
|
-
const imageParts = [];
|
|
517
|
-
for (const p of content) {
|
|
518
|
-
if (p.type === "text") {
|
|
519
|
-
if (p.text) texts.push(p.text);
|
|
520
|
-
} else if (p.type === "image") {
|
|
521
|
-
imageParts.push(p);
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
return { text: texts.join("\n"), imageParts, isString: false };
|
|
525
|
-
}
|
|
526
|
-
function toolImageFollowupHeader(toolName, count) {
|
|
527
|
-
return `(Tool "${toolName ?? "<tool>"}" returned the following image${count === 1 ? "" : "s"}:)`;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
// src/llm/messageFormat.ts
|
|
531
|
-
function extractText(m) {
|
|
532
|
-
const raw = typeof m.content === "string" ? m.content : Array.isArray(m.content) ? m.content.map((x) => x?.text ?? "").join("\n") : JSON.stringify(m.content ?? "");
|
|
533
|
-
if (raw.trim()) return raw.trim();
|
|
534
|
-
if (m.parts?.length) {
|
|
535
|
-
return m.parts.filter((p) => p.type === "text" && p.text).map((p) => p.text).join("").trim();
|
|
536
|
-
}
|
|
537
|
-
return "";
|
|
538
|
-
}
|
|
539
|
-
function convertMessagesToProvider(messages) {
|
|
540
|
-
const out = [];
|
|
541
|
-
messages.forEach((m, mi) => {
|
|
542
|
-
if (m.role === "user") {
|
|
543
|
-
const text2 = extractText(m) || "[no content]";
|
|
544
|
-
if (m.attachments?.length) {
|
|
545
|
-
const parts = [{ type: "text", text: text2 }];
|
|
546
|
-
for (const a of m.attachments) {
|
|
547
|
-
if (a.type === "image") {
|
|
548
|
-
parts.push({
|
|
549
|
-
type: "image",
|
|
550
|
-
url: `data:${a.mimeType};base64,${a.data}`,
|
|
551
|
-
mimeType: a.mimeType,
|
|
552
|
-
data: a.data
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
out.push({ role: "user", content: parts });
|
|
557
|
-
} else {
|
|
558
|
-
out.push({ role: "user", content: text2 });
|
|
559
|
-
}
|
|
560
|
-
return;
|
|
561
|
-
}
|
|
562
|
-
const toolParts = (m.parts ?? []).filter(
|
|
563
|
-
(p) => p.type === "tool-invocation" && p.toolInvocation && p.toolInvocation.result !== void 0
|
|
564
|
-
);
|
|
565
|
-
if (toolParts.length === 0) {
|
|
566
|
-
const text2 = extractText(m) || "[no content]";
|
|
567
|
-
out.push({ role: "assistant", content: text2 });
|
|
568
|
-
return;
|
|
569
|
-
}
|
|
570
|
-
const text = extractText(m);
|
|
571
|
-
const toolCalls = toolParts.map((p, i) => ({
|
|
572
|
-
id: `call_${mi}_${i}_${p.toolInvocation.toolName}`,
|
|
573
|
-
name: p.toolInvocation.toolName,
|
|
574
|
-
args: p.toolInvocation.args
|
|
575
|
-
}));
|
|
576
|
-
out.push({
|
|
577
|
-
role: "assistant",
|
|
578
|
-
content: text,
|
|
579
|
-
toolCalls
|
|
580
|
-
});
|
|
581
|
-
toolParts.forEach((p, i) => {
|
|
582
|
-
const result = p.toolInvocation.result;
|
|
583
|
-
out.push({
|
|
584
|
-
role: "tool",
|
|
585
|
-
content: toolResultToContent(result),
|
|
586
|
-
toolCallId: `call_${mi}_${i}_${p.toolInvocation.toolName}`,
|
|
587
|
-
toolName: p.toolInvocation.toolName,
|
|
588
|
-
toolResult: result,
|
|
589
|
-
toolIsError: isToolResultError(result)
|
|
590
|
-
});
|
|
591
|
-
});
|
|
592
|
-
});
|
|
593
|
-
return out;
|
|
594
|
-
}
|
|
595
|
-
function extractSystem(messages) {
|
|
596
|
-
const sys = [];
|
|
597
|
-
const rest = [];
|
|
598
|
-
for (const m of messages) {
|
|
599
|
-
if (m.role === "system") {
|
|
600
|
-
if (typeof m.content === "string") sys.push(m.content);
|
|
601
|
-
else if (Array.isArray(m.content)) {
|
|
602
|
-
for (const p of m.content) if (p.type === "text") sys.push(p.text);
|
|
603
|
-
}
|
|
604
|
-
} else {
|
|
605
|
-
rest.push(m);
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
return {
|
|
609
|
-
system: sys.length > 0 ? sys.join("\n\n") : void 0,
|
|
610
|
-
rest
|
|
611
|
-
};
|
|
612
|
-
}
|
|
613
|
-
function parseDataUrl(url) {
|
|
614
|
-
const m = url.match(/^data:([^;,]+);base64,(.+)$/);
|
|
615
|
-
if (!m) return null;
|
|
616
|
-
return { mimeType: m[1], data: m[2] };
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
// src/llm/sse.ts
|
|
620
|
-
async function* parseSSE(body, signal) {
|
|
621
|
-
const reader = body.getReader();
|
|
622
|
-
const decoder = new TextDecoder("utf-8");
|
|
623
|
-
let buffer = "";
|
|
624
|
-
try {
|
|
625
|
-
while (true) {
|
|
626
|
-
if (signal?.aborted) return;
|
|
627
|
-
const { value, done } = await reader.read();
|
|
628
|
-
if (done) break;
|
|
629
|
-
buffer += decoder.decode(value, { stream: true });
|
|
630
|
-
let sep;
|
|
631
|
-
while ((sep = indexOfEventSeparator(buffer)) !== -1) {
|
|
632
|
-
const raw = buffer.slice(0, sep);
|
|
633
|
-
buffer = buffer.slice(sep).replace(/^(\r\n\r\n|\n\n|\r\r)/, "");
|
|
634
|
-
const parsed = parseSseBlock(raw);
|
|
635
|
-
if (parsed) yield parsed;
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
if (buffer.trim()) {
|
|
639
|
-
const parsed = parseSseBlock(buffer);
|
|
640
|
-
if (parsed) yield parsed;
|
|
641
|
-
}
|
|
642
|
-
} finally {
|
|
643
|
-
try {
|
|
644
|
-
reader.releaseLock();
|
|
645
|
-
} catch {
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
function indexOfEventSeparator(s) {
|
|
650
|
-
const a = s.indexOf("\n\n");
|
|
651
|
-
const b = s.indexOf("\r\n\r\n");
|
|
652
|
-
if (a === -1) return b;
|
|
653
|
-
if (b === -1) return a;
|
|
654
|
-
return Math.min(a, b);
|
|
655
|
-
}
|
|
656
|
-
function parseSseBlock(raw) {
|
|
657
|
-
let event;
|
|
658
|
-
const dataLines = [];
|
|
659
|
-
for (const line of raw.split(/\r?\n/)) {
|
|
660
|
-
if (!line || line.startsWith(":")) continue;
|
|
661
|
-
const idx = line.indexOf(":");
|
|
662
|
-
const field = idx === -1 ? line : line.slice(0, idx);
|
|
663
|
-
let value = idx === -1 ? "" : line.slice(idx + 1);
|
|
664
|
-
if (value.startsWith(" ")) value = value.slice(1);
|
|
665
|
-
if (field === "event") event = value;
|
|
666
|
-
else if (field === "data") dataLines.push(value);
|
|
667
|
-
}
|
|
668
|
-
if (dataLines.length === 0) return null;
|
|
669
|
-
return { event, data: dataLines.join("\n") };
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
// src/llm/usage.ts
|
|
673
|
-
function numberAt(value, ...keys) {
|
|
674
|
-
for (const key of keys) {
|
|
675
|
-
const candidate = value[key];
|
|
676
|
-
if (typeof candidate === "number" && Number.isFinite(candidate)) {
|
|
677
|
-
return candidate;
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
return void 0;
|
|
681
|
-
}
|
|
682
|
-
function tokenUsageFromRecord(raw) {
|
|
683
|
-
if (!raw || typeof raw !== "object") return void 0;
|
|
684
|
-
const usage = raw;
|
|
685
|
-
const inputTokens = numberAt(
|
|
686
|
-
usage,
|
|
687
|
-
"inputTokens",
|
|
688
|
-
"input_tokens",
|
|
689
|
-
"prompt_tokens",
|
|
690
|
-
"promptTokenCount",
|
|
691
|
-
"prompt_eval_count"
|
|
692
|
-
);
|
|
693
|
-
const outputTokens = numberAt(
|
|
694
|
-
usage,
|
|
695
|
-
"outputTokens",
|
|
696
|
-
"output_tokens",
|
|
697
|
-
"completion_tokens",
|
|
698
|
-
"candidatesTokenCount",
|
|
699
|
-
"eval_count"
|
|
700
|
-
);
|
|
701
|
-
const totalTokens = numberAt(usage, "totalTokens", "total_tokens", "totalTokenCount") ?? (inputTokens !== void 0 && outputTokens !== void 0 ? inputTokens + outputTokens : void 0);
|
|
702
|
-
const inputDetails = usage.input_tokens_details && typeof usage.input_tokens_details === "object" ? usage.input_tokens_details : void 0;
|
|
703
|
-
const outputDetails = usage.output_tokens_details && typeof usage.output_tokens_details === "object" ? usage.output_tokens_details : void 0;
|
|
704
|
-
const cachedInputTokens = numberAt(usage, "cachedInputTokens", "cache_read_input_tokens") ?? (inputDetails ? numberAt(inputDetails, "cached_tokens") : void 0);
|
|
705
|
-
const reasoningTokens = numberAt(usage, "reasoningTokens", "thoughtsTokenCount") ?? (outputDetails ? numberAt(outputDetails, "reasoning_tokens") : void 0);
|
|
706
|
-
if (inputTokens === void 0 && outputTokens === void 0 && totalTokens === void 0 && cachedInputTokens === void 0 && reasoningTokens === void 0) {
|
|
707
|
-
return void 0;
|
|
708
|
-
}
|
|
709
|
-
return {
|
|
710
|
-
inputTokens,
|
|
711
|
-
outputTokens,
|
|
712
|
-
totalTokens,
|
|
713
|
-
cachedInputTokens,
|
|
714
|
-
reasoningTokens
|
|
715
|
-
};
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
// src/llm/providers/anthropic.ts
|
|
719
|
-
var DEFAULT_ENDPOINT = "https://api.anthropic.com/v1/messages";
|
|
720
|
-
var ANTHROPIC_VERSION = "2023-06-01";
|
|
721
|
-
var DEFAULT_MAX_TOKENS = 4096;
|
|
722
|
-
function buildImageBlock(p) {
|
|
723
|
-
if (p.type !== "image") return null;
|
|
724
|
-
if (p.data && p.mimeType) {
|
|
725
|
-
return {
|
|
726
|
-
type: "image",
|
|
727
|
-
source: { type: "base64", media_type: p.mimeType, data: p.data }
|
|
728
|
-
};
|
|
729
|
-
}
|
|
730
|
-
const parsed = parseDataUrl(p.url);
|
|
731
|
-
if (parsed) {
|
|
732
|
-
return {
|
|
733
|
-
type: "image",
|
|
734
|
-
source: {
|
|
735
|
-
type: "base64",
|
|
736
|
-
media_type: parsed.mimeType,
|
|
737
|
-
data: parsed.data
|
|
738
|
-
}
|
|
739
|
-
};
|
|
740
|
-
}
|
|
741
|
-
return { type: "image", source: { type: "url", url: p.url } };
|
|
742
|
-
}
|
|
743
|
-
function toAnthropicContent(content) {
|
|
744
|
-
if (typeof content === "string") {
|
|
745
|
-
return [{ type: "text", text: content || "[no content]" }];
|
|
746
|
-
}
|
|
747
|
-
const blocks = [];
|
|
748
|
-
for (const p of content) {
|
|
749
|
-
if (p.type === "text") {
|
|
750
|
-
if (p.text) blocks.push({ type: "text", text: p.text });
|
|
751
|
-
} else {
|
|
752
|
-
const img = buildImageBlock(p);
|
|
753
|
-
if (img) blocks.push(img);
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
if (blocks.length === 0) blocks.push({ type: "text", text: "[no content]" });
|
|
757
|
-
return blocks;
|
|
758
|
-
}
|
|
759
|
-
function toAnthropicToolResultContent(content) {
|
|
760
|
-
if (typeof content === "string") {
|
|
761
|
-
return content;
|
|
762
|
-
}
|
|
763
|
-
const blocks = [];
|
|
764
|
-
for (const p of content) {
|
|
765
|
-
if (p.type === "text") {
|
|
766
|
-
if (p.text) blocks.push({ type: "text", text: p.text });
|
|
767
|
-
} else if (p.type === "image") {
|
|
768
|
-
const img = buildImageBlock(p);
|
|
769
|
-
if (img) blocks.push(img);
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
if (blocks.length === 0) {
|
|
773
|
-
return "[no content]";
|
|
774
|
-
}
|
|
775
|
-
return blocks;
|
|
776
|
-
}
|
|
777
|
-
function toAnthropicMessages(messages) {
|
|
778
|
-
const out = [];
|
|
779
|
-
for (const m of messages) {
|
|
780
|
-
if (m.role === "tool") {
|
|
781
|
-
const last = out[out.length - 1];
|
|
782
|
-
const block = {
|
|
783
|
-
type: "tool_result",
|
|
784
|
-
tool_use_id: m.toolCallId,
|
|
785
|
-
content: toAnthropicToolResultContent(m.content),
|
|
786
|
-
...m.toolIsError ? { is_error: true } : {}
|
|
787
|
-
};
|
|
788
|
-
if (last && last.role === "user" && Array.isArray(last.content)) {
|
|
789
|
-
last.content.push(block);
|
|
790
|
-
} else {
|
|
791
|
-
out.push({ role: "user", content: [block] });
|
|
792
|
-
}
|
|
793
|
-
continue;
|
|
794
|
-
}
|
|
795
|
-
if (m.role === "assistant") {
|
|
796
|
-
const blocks = [];
|
|
797
|
-
const textContent = typeof m.content === "string" ? m.content : Array.isArray(m.content) ? m.content.map((p) => p.type === "text" ? p.text : "").join("") : "";
|
|
798
|
-
if (textContent) blocks.push({ type: "text", text: textContent });
|
|
799
|
-
if (m.toolCalls?.length) {
|
|
800
|
-
for (const tc of m.toolCalls) {
|
|
801
|
-
blocks.push({
|
|
802
|
-
type: "tool_use",
|
|
803
|
-
id: tc.id,
|
|
804
|
-
name: tc.name,
|
|
805
|
-
input: tc.args ?? {}
|
|
806
|
-
});
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
if (blocks.length === 0) {
|
|
810
|
-
blocks.push({ type: "text", text: "[no content]" });
|
|
811
|
-
}
|
|
812
|
-
out.push({ role: "assistant", content: blocks });
|
|
813
|
-
continue;
|
|
814
|
-
}
|
|
815
|
-
out.push({ role: "user", content: toAnthropicContent(m.content) });
|
|
816
|
-
}
|
|
817
|
-
return out;
|
|
818
|
-
}
|
|
819
|
-
function buildBody(params, stream) {
|
|
820
|
-
const { config, messages, tools } = params;
|
|
821
|
-
const { system, rest } = extractSystem(messages);
|
|
822
|
-
const body = {
|
|
823
|
-
model: config.model,
|
|
824
|
-
max_tokens: config.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
825
|
-
messages: toAnthropicMessages(rest),
|
|
826
|
-
...stream ? { stream: true } : {}
|
|
827
|
-
};
|
|
828
|
-
if (config.temperature !== void 0) body.temperature = config.temperature;
|
|
829
|
-
if (system) body.system = system;
|
|
830
|
-
if (tools && tools.length > 0) {
|
|
831
|
-
body.tools = tools.map((t) => ({
|
|
832
|
-
name: t.name,
|
|
833
|
-
description: t.description,
|
|
834
|
-
input_schema: t.inputSchema
|
|
835
|
-
}));
|
|
836
|
-
}
|
|
837
|
-
return body;
|
|
838
|
-
}
|
|
839
|
-
async function* streamChat(params) {
|
|
840
|
-
const { config, signal } = params;
|
|
841
|
-
const res = await fetch(DEFAULT_ENDPOINT, {
|
|
842
|
-
method: "POST",
|
|
843
|
-
headers: {
|
|
844
|
-
"Content-Type": "application/json",
|
|
845
|
-
"x-api-key": config.apiKey,
|
|
846
|
-
"anthropic-version": ANTHROPIC_VERSION,
|
|
847
|
-
"anthropic-dangerous-direct-browser-access": "true"
|
|
848
|
-
},
|
|
849
|
-
body: JSON.stringify(buildBody(params, true)),
|
|
850
|
-
signal
|
|
851
|
-
});
|
|
852
|
-
if (!res.ok || !res.body) {
|
|
853
|
-
const text = await res.text().catch(() => "");
|
|
854
|
-
throw new Error(
|
|
855
|
-
`Anthropic request failed (${res.status} ${res.statusText}): ${text}`
|
|
856
|
-
);
|
|
857
|
-
}
|
|
858
|
-
const blocks = /* @__PURE__ */ new Map();
|
|
859
|
-
let usage;
|
|
860
|
-
for await (const ev of parseSSE(res.body, signal)) {
|
|
861
|
-
let parsed;
|
|
862
|
-
try {
|
|
863
|
-
parsed = JSON.parse(ev.data);
|
|
864
|
-
} catch {
|
|
865
|
-
continue;
|
|
866
|
-
}
|
|
867
|
-
const t = parsed?.type;
|
|
868
|
-
if (t === "message_start") {
|
|
869
|
-
usage = tokenUsageFromRecord(parsed?.message?.usage);
|
|
870
|
-
} else if (t === "message_delta") {
|
|
871
|
-
const deltaUsage = tokenUsageFromRecord(parsed?.usage);
|
|
872
|
-
if (deltaUsage) usage = { ...usage, ...deltaUsage };
|
|
873
|
-
} else if (t === "content_block_start") {
|
|
874
|
-
const idx = parsed.index;
|
|
875
|
-
const cb = parsed.content_block ?? {};
|
|
876
|
-
if (cb.type === "tool_use") {
|
|
877
|
-
blocks.set(idx, {
|
|
878
|
-
kind: "tool_use",
|
|
879
|
-
id: cb.id,
|
|
880
|
-
name: cb.name,
|
|
881
|
-
argsJson: "",
|
|
882
|
-
emittedStart: false
|
|
883
|
-
});
|
|
884
|
-
yield {
|
|
885
|
-
type: "tool-call-start",
|
|
886
|
-
index: idx,
|
|
887
|
-
toolCallId: cb.id,
|
|
888
|
-
toolName: cb.name
|
|
889
|
-
};
|
|
890
|
-
const entry = blocks.get(idx);
|
|
891
|
-
if (entry) entry.emittedStart = true;
|
|
892
|
-
} else {
|
|
893
|
-
blocks.set(idx, {
|
|
894
|
-
kind: "text",
|
|
895
|
-
argsJson: "",
|
|
896
|
-
emittedStart: false
|
|
897
|
-
});
|
|
898
|
-
}
|
|
899
|
-
} else if (t === "content_block_delta") {
|
|
900
|
-
const idx = parsed.index;
|
|
901
|
-
const d = parsed.delta ?? {};
|
|
902
|
-
const entry = blocks.get(idx);
|
|
903
|
-
if (!entry) continue;
|
|
904
|
-
if (d.type === "text_delta" && typeof d.text === "string") {
|
|
905
|
-
yield { type: "text-delta", delta: d.text };
|
|
906
|
-
} else if (d.type === "input_json_delta" && typeof d.partial_json === "string" && entry.kind === "tool_use") {
|
|
907
|
-
entry.argsJson += d.partial_json;
|
|
908
|
-
yield {
|
|
909
|
-
type: "tool-call-args-delta",
|
|
910
|
-
index: idx,
|
|
911
|
-
toolCallId: entry.id,
|
|
912
|
-
toolName: entry.name,
|
|
913
|
-
argsDelta: d.partial_json
|
|
914
|
-
};
|
|
915
|
-
}
|
|
916
|
-
} else if (t === "content_block_stop") {
|
|
917
|
-
const idx = parsed.index;
|
|
918
|
-
const entry = blocks.get(idx);
|
|
919
|
-
if (entry && entry.kind === "tool_use" && entry.id && entry.name) {
|
|
920
|
-
let args = {};
|
|
921
|
-
if (entry.argsJson) {
|
|
922
|
-
try {
|
|
923
|
-
args = JSON.parse(entry.argsJson);
|
|
924
|
-
} catch {
|
|
925
|
-
args = {};
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
yield {
|
|
929
|
-
type: "tool-call-ready",
|
|
930
|
-
index: idx,
|
|
931
|
-
toolCallId: entry.id,
|
|
932
|
-
toolName: entry.name,
|
|
933
|
-
args
|
|
934
|
-
};
|
|
935
|
-
}
|
|
936
|
-
} else if (t === "message_stop") {
|
|
937
|
-
if (usage) {
|
|
938
|
-
const totalTokens = usage.totalTokens ?? (usage.inputTokens !== void 0 && usage.outputTokens !== void 0 ? usage.inputTokens + usage.outputTokens : void 0);
|
|
939
|
-
yield { type: "usage", usage: { ...usage, totalTokens } };
|
|
940
|
-
}
|
|
941
|
-
} else if (t === "error") {
|
|
942
|
-
yield {
|
|
943
|
-
type: "error",
|
|
944
|
-
message: parsed?.error?.message ?? "Anthropic stream error"
|
|
945
|
-
};
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
yield { type: "done" };
|
|
949
|
-
}
|
|
950
|
-
async function chat(params) {
|
|
951
|
-
const { config, signal } = params;
|
|
952
|
-
const res = await fetch(DEFAULT_ENDPOINT, {
|
|
953
|
-
method: "POST",
|
|
954
|
-
headers: {
|
|
955
|
-
"Content-Type": "application/json",
|
|
956
|
-
"x-api-key": config.apiKey,
|
|
957
|
-
"anthropic-version": ANTHROPIC_VERSION,
|
|
958
|
-
"anthropic-dangerous-direct-browser-access": "true"
|
|
959
|
-
},
|
|
960
|
-
body: JSON.stringify(buildBody(params, false)),
|
|
961
|
-
signal
|
|
962
|
-
});
|
|
963
|
-
if (!res.ok) {
|
|
964
|
-
const text2 = await res.text().catch(() => "");
|
|
965
|
-
throw new Error(
|
|
966
|
-
`Anthropic request failed (${res.status} ${res.statusText}): ${text2}`
|
|
967
|
-
);
|
|
968
|
-
}
|
|
969
|
-
const json = await res.json();
|
|
970
|
-
let text = "";
|
|
971
|
-
const toolCalls = [];
|
|
972
|
-
for (const block of json?.content ?? []) {
|
|
973
|
-
if (block.type === "text" && typeof block.text === "string") {
|
|
974
|
-
text += block.text;
|
|
975
|
-
} else if (block.type === "tool_use") {
|
|
976
|
-
toolCalls.push({
|
|
977
|
-
id: block.id,
|
|
978
|
-
name: block.name,
|
|
979
|
-
args: block.input && typeof block.input === "object" ? block.input : {}
|
|
980
|
-
});
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
return { text, toolCalls };
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
// src/llm/schemaUtils.ts
|
|
987
|
-
var ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
988
|
-
"type",
|
|
989
|
-
"description",
|
|
990
|
-
"enum",
|
|
991
|
-
"properties",
|
|
992
|
-
"required",
|
|
993
|
-
"items",
|
|
994
|
-
"format",
|
|
995
|
-
"nullable",
|
|
996
|
-
"minimum",
|
|
997
|
-
"maximum",
|
|
998
|
-
"minItems",
|
|
999
|
-
"maxItems",
|
|
1000
|
-
"minLength",
|
|
1001
|
-
"maxLength",
|
|
1002
|
-
"pattern"
|
|
1003
|
-
]);
|
|
1004
|
-
function sanitizeSchemaForGemini(schema) {
|
|
1005
|
-
if (!schema || typeof schema !== "object") {
|
|
1006
|
-
return { type: "object" };
|
|
1007
|
-
}
|
|
1008
|
-
const cleaned = clean(schema);
|
|
1009
|
-
if (typeof cleaned !== "object" || cleaned === null) {
|
|
1010
|
-
return { type: "object" };
|
|
1011
|
-
}
|
|
1012
|
-
const out = cleaned;
|
|
1013
|
-
if (!out.type) out.type = "object";
|
|
1014
|
-
return out;
|
|
1015
|
-
}
|
|
1016
|
-
function clean(value) {
|
|
1017
|
-
if (Array.isArray(value)) return value.map(clean);
|
|
1018
|
-
if (!value || typeof value !== "object") return value;
|
|
1019
|
-
const input = value;
|
|
1020
|
-
for (const key of ["oneOf", "anyOf", "allOf"]) {
|
|
1021
|
-
const arr = input[key];
|
|
1022
|
-
if (Array.isArray(arr) && arr.length === 1) {
|
|
1023
|
-
return clean(arr[0]);
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
const out = {};
|
|
1027
|
-
for (const [k, v] of Object.entries(input)) {
|
|
1028
|
-
if (!ALLOWED_KEYS.has(k)) continue;
|
|
1029
|
-
if (k === "properties" && v && typeof v === "object") {
|
|
1030
|
-
const props = {};
|
|
1031
|
-
for (const [pk, pv] of Object.entries(v)) {
|
|
1032
|
-
props[pk] = clean(pv);
|
|
1033
|
-
}
|
|
1034
|
-
out[k] = props;
|
|
1035
|
-
} else if (k === "items") {
|
|
1036
|
-
out[k] = clean(v);
|
|
1037
|
-
} else {
|
|
1038
|
-
out[k] = v;
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
return out;
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
// src/llm/providers/google.ts
|
|
1045
|
-
function normalizeModelId(model) {
|
|
1046
|
-
const trimmed = model.trim();
|
|
1047
|
-
return trimmed.startsWith("models/") ? trimmed.slice("models/".length) : trimmed;
|
|
1048
|
-
}
|
|
1049
|
-
function endpointFor(model, mode) {
|
|
1050
|
-
const action = mode === "stream" ? "streamGenerateContent?alt=sse" : "generateContent";
|
|
1051
|
-
return `https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(
|
|
1052
|
-
normalizeModelId(model)
|
|
1053
|
-
)}:${action}`;
|
|
1054
|
-
}
|
|
1055
|
-
function buildInlineData(p) {
|
|
1056
|
-
if (p.type !== "image") return null;
|
|
1057
|
-
if (p.data && p.mimeType) {
|
|
1058
|
-
return { inlineData: { mimeType: p.mimeType, data: p.data } };
|
|
1059
|
-
}
|
|
1060
|
-
const parsed = parseDataUrl(p.url);
|
|
1061
|
-
if (parsed) {
|
|
1062
|
-
return {
|
|
1063
|
-
inlineData: { mimeType: parsed.mimeType, data: parsed.data }
|
|
1064
|
-
};
|
|
1065
|
-
}
|
|
1066
|
-
return null;
|
|
1067
|
-
}
|
|
1068
|
-
function toGeminiParts(content) {
|
|
1069
|
-
if (typeof content === "string") {
|
|
1070
|
-
return [{ text: content || "[no content]" }];
|
|
1071
|
-
}
|
|
1072
|
-
const parts = [];
|
|
1073
|
-
for (const p of content) {
|
|
1074
|
-
if (p.type === "text") {
|
|
1075
|
-
if (p.text) parts.push({ text: p.text });
|
|
1076
|
-
} else {
|
|
1077
|
-
const block = buildInlineData(p);
|
|
1078
|
-
if (block) parts.push(block);
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
if (parts.length === 0) parts.push({ text: "[no content]" });
|
|
1082
|
-
return parts;
|
|
1083
|
-
}
|
|
1084
|
-
function buildGeminiToolResponse(content) {
|
|
1085
|
-
if (typeof content === "string") {
|
|
1086
|
-
let parsed;
|
|
1087
|
-
try {
|
|
1088
|
-
parsed = JSON.parse(content);
|
|
1089
|
-
} catch {
|
|
1090
|
-
parsed = { result: content };
|
|
1091
|
-
}
|
|
1092
|
-
if (!parsed || typeof parsed !== "object") {
|
|
1093
|
-
parsed = { result: parsed };
|
|
1094
|
-
}
|
|
1095
|
-
return { response: parsed, imageParts: [] };
|
|
1096
|
-
}
|
|
1097
|
-
const { text, imageParts } = partitionToolContent(content);
|
|
1098
|
-
const response = {};
|
|
1099
|
-
if (text) response.text = text;
|
|
1100
|
-
if (imageParts.length > 0) {
|
|
1101
|
-
response.images = imageParts.map((p) => ({
|
|
1102
|
-
mimeType: p.mimeType ?? "image/*",
|
|
1103
|
-
omitted: true
|
|
1104
|
-
}));
|
|
1105
|
-
response.note = "Image bytes were sent in the next user turn as inlineData.";
|
|
1106
|
-
}
|
|
1107
|
-
if (Object.keys(response).length === 0) {
|
|
1108
|
-
response.result = null;
|
|
1109
|
-
}
|
|
1110
|
-
return { response, imageParts };
|
|
1111
|
-
}
|
|
1112
|
-
function toGeminiContents(messages) {
|
|
1113
|
-
const out = [];
|
|
1114
|
-
for (const m of messages) {
|
|
1115
|
-
if (m.role === "tool") {
|
|
1116
|
-
const { response, imageParts } = buildGeminiToolResponse(m.content);
|
|
1117
|
-
const last = out[out.length - 1];
|
|
1118
|
-
const part = {
|
|
1119
|
-
functionResponse: {
|
|
1120
|
-
name: m.toolName,
|
|
1121
|
-
response
|
|
1122
|
-
}
|
|
1123
|
-
};
|
|
1124
|
-
if (last && last.role === "function") {
|
|
1125
|
-
last.parts.push(part);
|
|
1126
|
-
} else {
|
|
1127
|
-
out.push({ role: "function", parts: [part] });
|
|
1128
|
-
}
|
|
1129
|
-
if (imageParts.length > 0) {
|
|
1130
|
-
const userParts = [
|
|
1131
|
-
{ text: toolImageFollowupHeader(m.toolName, imageParts.length) }
|
|
1132
|
-
];
|
|
1133
|
-
for (const p of imageParts) {
|
|
1134
|
-
const block = buildInlineData(p);
|
|
1135
|
-
if (block) userParts.push(block);
|
|
1136
|
-
}
|
|
1137
|
-
out.push({ role: "user", parts: userParts });
|
|
1138
|
-
}
|
|
1139
|
-
continue;
|
|
1140
|
-
}
|
|
1141
|
-
if (m.role === "assistant") {
|
|
1142
|
-
const parts = [];
|
|
1143
|
-
const textContent = typeof m.content === "string" ? m.content : Array.isArray(m.content) ? m.content.map((p) => p.type === "text" ? p.text : "").join("") : "";
|
|
1144
|
-
if (textContent) parts.push({ text: textContent });
|
|
1145
|
-
if (m.toolCalls?.length) {
|
|
1146
|
-
for (const tc of m.toolCalls) {
|
|
1147
|
-
parts.push({
|
|
1148
|
-
functionCall: { name: tc.name, args: tc.args ?? {} }
|
|
1149
|
-
});
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
if (parts.length === 0) parts.push({ text: "[no content]" });
|
|
1153
|
-
out.push({ role: "model", parts });
|
|
1154
|
-
continue;
|
|
1155
|
-
}
|
|
1156
|
-
out.push({ role: "user", parts: toGeminiParts(m.content) });
|
|
1157
|
-
}
|
|
1158
|
-
return out;
|
|
1159
|
-
}
|
|
1160
|
-
function buildBody2(params) {
|
|
1161
|
-
const { config, messages, tools } = params;
|
|
1162
|
-
const { system, rest } = extractSystem(messages);
|
|
1163
|
-
const body = {
|
|
1164
|
-
contents: toGeminiContents(rest)
|
|
1165
|
-
};
|
|
1166
|
-
if (system) {
|
|
1167
|
-
body.systemInstruction = { parts: [{ text: system }] };
|
|
1168
|
-
}
|
|
1169
|
-
const genConfig = {};
|
|
1170
|
-
if (config.temperature !== void 0)
|
|
1171
|
-
genConfig.temperature = config.temperature;
|
|
1172
|
-
if (config.maxTokens !== void 0)
|
|
1173
|
-
genConfig.maxOutputTokens = config.maxTokens;
|
|
1174
|
-
if (Object.keys(genConfig).length > 0) body.generationConfig = genConfig;
|
|
1175
|
-
if (tools && tools.length > 0) {
|
|
1176
|
-
body.tools = [
|
|
1177
|
-
{
|
|
1178
|
-
functionDeclarations: tools.map((t) => ({
|
|
1179
|
-
name: t.name,
|
|
1180
|
-
description: t.description,
|
|
1181
|
-
parameters: sanitizeSchemaForGemini(t.inputSchema)
|
|
1182
|
-
}))
|
|
1183
|
-
}
|
|
1184
|
-
];
|
|
1185
|
-
}
|
|
1186
|
-
return body;
|
|
1187
|
-
}
|
|
1188
|
-
async function* streamChat2(params) {
|
|
1189
|
-
const { config, signal } = params;
|
|
1190
|
-
const url = `${endpointFor(config.model, "stream")}&key=${encodeURIComponent(
|
|
1191
|
-
config.apiKey
|
|
1192
|
-
)}`;
|
|
1193
|
-
const res = await fetch(url, {
|
|
1194
|
-
method: "POST",
|
|
1195
|
-
headers: { "Content-Type": "application/json" },
|
|
1196
|
-
body: JSON.stringify(buildBody2(params)),
|
|
1197
|
-
signal
|
|
1198
|
-
});
|
|
1199
|
-
if (!res.ok || !res.body) {
|
|
1200
|
-
const text = await res.text().catch(() => "");
|
|
1201
|
-
throw new Error(
|
|
1202
|
-
`Google request failed (${res.status} ${res.statusText}): ${text}`
|
|
1203
|
-
);
|
|
1204
|
-
}
|
|
1205
|
-
let toolCallCounter = 0;
|
|
1206
|
-
let usage;
|
|
1207
|
-
for await (const ev of parseSSE(res.body, signal)) {
|
|
1208
|
-
let parsed;
|
|
1209
|
-
try {
|
|
1210
|
-
parsed = JSON.parse(ev.data);
|
|
1211
|
-
} catch {
|
|
1212
|
-
continue;
|
|
1213
|
-
}
|
|
1214
|
-
usage = tokenUsageFromRecord(parsed?.usageMetadata) ?? usage;
|
|
1215
|
-
const parts = parsed?.candidates?.[0]?.content?.parts ?? [];
|
|
1216
|
-
for (const p of parts) {
|
|
1217
|
-
if (typeof p.text === "string" && p.text.length > 0) {
|
|
1218
|
-
yield { type: "text-delta", delta: p.text };
|
|
1219
|
-
} else if (p.functionCall && typeof p.functionCall === "object") {
|
|
1220
|
-
const idx = toolCallCounter++;
|
|
1221
|
-
const id = `call_${idx}_${p.functionCall.name ?? "tool"}`;
|
|
1222
|
-
const name = p.functionCall.name ?? "";
|
|
1223
|
-
const args = p.functionCall.args && typeof p.functionCall.args === "object" ? p.functionCall.args : {};
|
|
1224
|
-
yield {
|
|
1225
|
-
type: "tool-call-start",
|
|
1226
|
-
index: idx,
|
|
1227
|
-
toolCallId: id,
|
|
1228
|
-
toolName: name
|
|
1229
|
-
};
|
|
1230
|
-
const argsJson = JSON.stringify(args);
|
|
1231
|
-
if (argsJson && argsJson !== "{}") {
|
|
1232
|
-
yield {
|
|
1233
|
-
type: "tool-call-args-delta",
|
|
1234
|
-
index: idx,
|
|
1235
|
-
toolCallId: id,
|
|
1236
|
-
toolName: name,
|
|
1237
|
-
argsDelta: argsJson
|
|
1238
|
-
};
|
|
1239
|
-
}
|
|
1240
|
-
yield {
|
|
1241
|
-
type: "tool-call-ready",
|
|
1242
|
-
index: idx,
|
|
1243
|
-
toolCallId: id,
|
|
1244
|
-
toolName: name,
|
|
1245
|
-
args
|
|
1246
|
-
};
|
|
1247
|
-
}
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
if (usage) yield { type: "usage", usage };
|
|
1251
|
-
yield { type: "done" };
|
|
1252
|
-
}
|
|
1253
|
-
async function chat2(params) {
|
|
1254
|
-
const { config, signal } = params;
|
|
1255
|
-
const url = `${endpointFor(config.model, "single")}?key=${encodeURIComponent(
|
|
1256
|
-
config.apiKey
|
|
1257
|
-
)}`;
|
|
1258
|
-
const res = await fetch(url, {
|
|
1259
|
-
method: "POST",
|
|
1260
|
-
headers: { "Content-Type": "application/json" },
|
|
1261
|
-
body: JSON.stringify(buildBody2(params)),
|
|
1262
|
-
signal
|
|
1263
|
-
});
|
|
1264
|
-
if (!res.ok) {
|
|
1265
|
-
const text2 = await res.text().catch(() => "");
|
|
1266
|
-
throw new Error(
|
|
1267
|
-
`Google request failed (${res.status} ${res.statusText}): ${text2}`
|
|
1268
|
-
);
|
|
1269
|
-
}
|
|
1270
|
-
const json = await res.json();
|
|
1271
|
-
let text = "";
|
|
1272
|
-
const toolCalls = [];
|
|
1273
|
-
const parts = json?.candidates?.[0]?.content?.parts ?? [];
|
|
1274
|
-
for (const p of parts) {
|
|
1275
|
-
if (typeof p.text === "string") text += p.text;
|
|
1276
|
-
else if (p.functionCall) {
|
|
1277
|
-
toolCalls.push({
|
|
1278
|
-
id: `call_${toolCalls.length}_${p.functionCall.name ?? "tool"}`,
|
|
1279
|
-
name: p.functionCall.name ?? "",
|
|
1280
|
-
args: p.functionCall.args && typeof p.functionCall.args === "object" ? p.functionCall.args : {}
|
|
1281
|
-
});
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
return { text, toolCalls };
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
// src/llm/ndjson.ts
|
|
1288
|
-
async function* parseNDJSON(body, signal) {
|
|
1289
|
-
const reader = body.getReader();
|
|
1290
|
-
const decoder = new TextDecoder();
|
|
1291
|
-
let buffer = "";
|
|
1292
|
-
try {
|
|
1293
|
-
while (true) {
|
|
1294
|
-
if (signal?.aborted) return;
|
|
1295
|
-
const { done, value } = await reader.read();
|
|
1296
|
-
if (done) break;
|
|
1297
|
-
buffer += decoder.decode(value, { stream: true });
|
|
1298
|
-
const lines = buffer.split("\n");
|
|
1299
|
-
buffer = lines.pop() ?? "";
|
|
1300
|
-
for (const line of lines) {
|
|
1301
|
-
const trimmed = line.trim();
|
|
1302
|
-
if (!trimmed) continue;
|
|
1303
|
-
try {
|
|
1304
|
-
yield JSON.parse(trimmed);
|
|
1305
|
-
} catch {
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
const trailing = buffer.trim();
|
|
1310
|
-
if (trailing) {
|
|
1311
|
-
try {
|
|
1312
|
-
yield JSON.parse(trailing);
|
|
1313
|
-
} catch {
|
|
1314
|
-
}
|
|
1315
|
-
}
|
|
1316
|
-
} finally {
|
|
1317
|
-
reader.releaseLock();
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
|
|
1321
|
-
// src/llm/providers/ollama/utils.ts
|
|
1322
|
-
var DEFAULT_OLLAMA_BASE_URL = "http://localhost:11434";
|
|
1323
|
-
function normalizeOllamaBaseUrl(baseUrl) {
|
|
1324
|
-
const raw = (baseUrl || DEFAULT_OLLAMA_BASE_URL).trim();
|
|
1325
|
-
let end = raw.length;
|
|
1326
|
-
while (end > 0 && raw.charCodeAt(end - 1) === 47) {
|
|
1327
|
-
end--;
|
|
1328
|
-
}
|
|
1329
|
-
const trimmed = raw.slice(0, end);
|
|
1330
|
-
return trimmed.endsWith("/api") ? trimmed.slice(0, -4) : trimmed;
|
|
1331
|
-
}
|
|
1332
|
-
function buildOllamaApiUrl(baseUrl, path) {
|
|
1333
|
-
return `${normalizeOllamaBaseUrl(baseUrl)}${path}`;
|
|
1334
|
-
}
|
|
1335
|
-
var OllamaCorsError = class extends Error {
|
|
1336
|
-
constructor(cause) {
|
|
1337
|
-
super(
|
|
1338
|
-
"Could not reach Ollama. If it's running, allow this origin by starting Ollama with `OLLAMA_ORIGINS=*` (or your inspector origin) and try again."
|
|
1339
|
-
);
|
|
1340
|
-
this.name = "OllamaCorsError";
|
|
1341
|
-
this.cause = cause;
|
|
1342
|
-
}
|
|
1343
|
-
};
|
|
1344
|
-
|
|
1345
|
-
// src/llm/providers/ollama/index.ts
|
|
1346
|
-
function toOllamaImages(content) {
|
|
1347
|
-
const images = [];
|
|
1348
|
-
for (const part of content) {
|
|
1349
|
-
if (part.type !== "image") continue;
|
|
1350
|
-
if (part.data) {
|
|
1351
|
-
images.push(part.data);
|
|
1352
|
-
continue;
|
|
1353
|
-
}
|
|
1354
|
-
const parsed = parseDataUrl(part.url);
|
|
1355
|
-
if (parsed?.data) {
|
|
1356
|
-
images.push(parsed.data);
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1359
|
-
return images;
|
|
1360
|
-
}
|
|
1361
|
-
function toOllamaContent(content) {
|
|
1362
|
-
if (typeof content === "string") {
|
|
1363
|
-
return { content };
|
|
1364
|
-
}
|
|
1365
|
-
const text = content.filter(
|
|
1366
|
-
(part) => part.type === "text"
|
|
1367
|
-
).map((part) => part.text).join("");
|
|
1368
|
-
const images = toOllamaImages(content);
|
|
1369
|
-
return {
|
|
1370
|
-
content: text,
|
|
1371
|
-
...images.length > 0 ? { images } : {}
|
|
1372
|
-
};
|
|
1373
|
-
}
|
|
1374
|
-
function toOllamaMessages(messages) {
|
|
1375
|
-
return messages.map((message) => {
|
|
1376
|
-
if (message.role === "tool") {
|
|
1377
|
-
return {
|
|
1378
|
-
role: "tool",
|
|
1379
|
-
tool_name: message.toolName,
|
|
1380
|
-
content: typeof message.content === "string" ? message.content : JSON.stringify(message.toolResult ?? message.content)
|
|
1381
|
-
};
|
|
1382
|
-
}
|
|
1383
|
-
if (message.role === "assistant") {
|
|
1384
|
-
const content = typeof message.content === "string" ? { content: message.content } : toOllamaContent(message.content);
|
|
1385
|
-
return {
|
|
1386
|
-
role: "assistant",
|
|
1387
|
-
...content,
|
|
1388
|
-
...message.toolCalls?.length ? {
|
|
1389
|
-
tool_calls: message.toolCalls.map((toolCall, index) => ({
|
|
1390
|
-
type: "function",
|
|
1391
|
-
function: {
|
|
1392
|
-
index,
|
|
1393
|
-
name: toolCall.name,
|
|
1394
|
-
arguments: toolCall.args
|
|
1395
|
-
}
|
|
1396
|
-
}))
|
|
1397
|
-
} : {}
|
|
1398
|
-
};
|
|
1399
|
-
}
|
|
1400
|
-
return {
|
|
1401
|
-
role: message.role,
|
|
1402
|
-
...toOllamaContent(message.content)
|
|
1403
|
-
};
|
|
1404
|
-
});
|
|
1405
|
-
}
|
|
1406
|
-
function buildBody3(params, stream) {
|
|
1407
|
-
const { config, messages, tools } = params;
|
|
1408
|
-
const body = {
|
|
1409
|
-
model: config.model,
|
|
1410
|
-
messages: toOllamaMessages(messages),
|
|
1411
|
-
stream
|
|
1412
|
-
};
|
|
1413
|
-
const options = {};
|
|
1414
|
-
if (config.temperature !== void 0)
|
|
1415
|
-
options.temperature = config.temperature;
|
|
1416
|
-
if (config.maxTokens !== void 0) options.num_predict = config.maxTokens;
|
|
1417
|
-
if (Object.keys(options).length > 0) body.options = options;
|
|
1418
|
-
if (tools && tools.length > 0) {
|
|
1419
|
-
body.tools = tools.map((tool) => ({
|
|
1420
|
-
type: "function",
|
|
1421
|
-
function: {
|
|
1422
|
-
name: tool.name,
|
|
1423
|
-
description: tool.description,
|
|
1424
|
-
parameters: tool.inputSchema
|
|
1425
|
-
}
|
|
1426
|
-
}));
|
|
1427
|
-
}
|
|
1428
|
-
return body;
|
|
1429
|
-
}
|
|
1430
|
-
function buildHeaders(config) {
|
|
1431
|
-
return {
|
|
1432
|
-
"Content-Type": "application/json",
|
|
1433
|
-
...config.apiKey.trim() ? { Authorization: `Bearer ${config.apiKey.trim()}` } : {}
|
|
1434
|
-
};
|
|
1435
|
-
}
|
|
1436
|
-
function normalizeToolCalls(toolCalls) {
|
|
1437
|
-
if (!Array.isArray(toolCalls)) return [];
|
|
1438
|
-
return toolCalls.map((toolCall) => {
|
|
1439
|
-
const functionCall = toolCall && typeof toolCall === "object" ? toolCall.function : void 0;
|
|
1440
|
-
const name = typeof functionCall?.name === "string" ? functionCall.name : "";
|
|
1441
|
-
const rawArgs = functionCall?.arguments;
|
|
1442
|
-
const args = rawArgs && typeof rawArgs === "object" ? rawArgs : {};
|
|
1443
|
-
return {
|
|
1444
|
-
name,
|
|
1445
|
-
args
|
|
1446
|
-
};
|
|
1447
|
-
}).filter((toolCall) => toolCall.name);
|
|
1448
|
-
}
|
|
1449
|
-
async function* streamChat3(params) {
|
|
1450
|
-
const { config, signal } = params;
|
|
1451
|
-
const res = await fetch(buildOllamaApiUrl(config.baseUrl, "/api/chat"), {
|
|
1452
|
-
method: "POST",
|
|
1453
|
-
headers: buildHeaders(config),
|
|
1454
|
-
body: JSON.stringify(buildBody3(params, true)),
|
|
1455
|
-
signal
|
|
1456
|
-
});
|
|
1457
|
-
if (!res.ok || !res.body) {
|
|
1458
|
-
const text = await res.text().catch(() => "");
|
|
1459
|
-
throw new Error(
|
|
1460
|
-
`Ollama request failed (${res.status} ${res.statusText}): ${text}`
|
|
1461
|
-
);
|
|
1462
|
-
}
|
|
1463
|
-
let toolCallsEmitted = false;
|
|
1464
|
-
let usage;
|
|
1465
|
-
for await (const chunk of parseNDJSON(res.body, signal)) {
|
|
1466
|
-
const message = chunk && typeof chunk === "object" ? chunk.message : void 0;
|
|
1467
|
-
if (typeof message?.content === "string" && message.content.length > 0) {
|
|
1468
|
-
yield { type: "text-delta", delta: message.content };
|
|
1469
|
-
}
|
|
1470
|
-
if (!toolCallsEmitted) {
|
|
1471
|
-
const toolCalls = normalizeToolCalls(message?.tool_calls);
|
|
1472
|
-
if (toolCalls.length > 0) {
|
|
1473
|
-
for (const [index, toolCall] of toolCalls.entries()) {
|
|
1474
|
-
const toolCallId = `call_${index}_${toolCall.name || "tool"}`;
|
|
1475
|
-
yield {
|
|
1476
|
-
type: "tool-call-start",
|
|
1477
|
-
index,
|
|
1478
|
-
toolCallId,
|
|
1479
|
-
toolName: toolCall.name
|
|
1480
|
-
};
|
|
1481
|
-
const argsJson = JSON.stringify(toolCall.args);
|
|
1482
|
-
if (argsJson && argsJson !== "{}") {
|
|
1483
|
-
yield {
|
|
1484
|
-
type: "tool-call-args-delta",
|
|
1485
|
-
index,
|
|
1486
|
-
toolCallId,
|
|
1487
|
-
toolName: toolCall.name,
|
|
1488
|
-
argsDelta: argsJson
|
|
1489
|
-
};
|
|
1490
|
-
}
|
|
1491
|
-
yield {
|
|
1492
|
-
type: "tool-call-ready",
|
|
1493
|
-
index,
|
|
1494
|
-
toolCallId,
|
|
1495
|
-
toolName: toolCall.name,
|
|
1496
|
-
args: toolCall.args
|
|
1497
|
-
};
|
|
1498
|
-
}
|
|
1499
|
-
toolCallsEmitted = true;
|
|
1500
|
-
}
|
|
1501
|
-
}
|
|
1502
|
-
if (chunk && typeof chunk === "object" && chunk.error) {
|
|
1503
|
-
yield {
|
|
1504
|
-
type: "error",
|
|
1505
|
-
message: String(chunk.error)
|
|
1506
|
-
};
|
|
1507
|
-
}
|
|
1508
|
-
usage = tokenUsageFromRecord(chunk) ?? usage;
|
|
1509
|
-
}
|
|
1510
|
-
if (usage) yield { type: "usage", usage };
|
|
1511
|
-
yield { type: "done" };
|
|
1512
|
-
}
|
|
1513
|
-
async function chat3(params) {
|
|
1514
|
-
const { config, signal } = params;
|
|
1515
|
-
const res = await fetch(buildOllamaApiUrl(config.baseUrl, "/api/chat"), {
|
|
1516
|
-
method: "POST",
|
|
1517
|
-
headers: buildHeaders(config),
|
|
1518
|
-
body: JSON.stringify(buildBody3(params, false)),
|
|
1519
|
-
signal
|
|
1520
|
-
});
|
|
1521
|
-
if (!res.ok) {
|
|
1522
|
-
const text = await res.text().catch(() => "");
|
|
1523
|
-
throw new Error(
|
|
1524
|
-
`Ollama request failed (${res.status} ${res.statusText}): ${text}`
|
|
1525
|
-
);
|
|
1526
|
-
}
|
|
1527
|
-
const json = await res.json();
|
|
1528
|
-
const message = json?.message ?? {};
|
|
1529
|
-
return {
|
|
1530
|
-
text: typeof message.content === "string" ? message.content : "",
|
|
1531
|
-
toolCalls: normalizeToolCalls(message.tool_calls).map((tc, index) => ({
|
|
1532
|
-
id: `call_${index}_${tc.name || "tool"}`,
|
|
1533
|
-
name: tc.name,
|
|
1534
|
-
args: tc.args
|
|
1535
|
-
}))
|
|
1536
|
-
};
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
|
-
// src/llm/providers/openai-chat-completions.ts
|
|
1540
|
-
var LlmRequestError = class extends Error {
|
|
1541
|
-
status;
|
|
1542
|
-
body;
|
|
1543
|
-
constructor(status, message, body) {
|
|
1544
|
-
super(message);
|
|
1545
|
-
this.name = "LlmRequestError";
|
|
1546
|
-
this.status = status;
|
|
1547
|
-
this.body = body;
|
|
1548
|
-
}
|
|
1549
|
-
};
|
|
1550
|
-
async function throwLlmRequestError(res) {
|
|
1551
|
-
const text = await res.text().catch(() => "");
|
|
1552
|
-
let body = text;
|
|
1553
|
-
if (text) {
|
|
1554
|
-
try {
|
|
1555
|
-
body = JSON.parse(text);
|
|
1556
|
-
} catch {
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
throw new LlmRequestError(
|
|
1560
|
-
res.status,
|
|
1561
|
-
`OpenAI request failed (${res.status} ${res.statusText}): ${text}`,
|
|
1562
|
-
body
|
|
1563
|
-
);
|
|
1564
|
-
}
|
|
1565
|
-
var OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
1566
|
-
function buildEndpoint(config, path) {
|
|
1567
|
-
return `${config.baseUrl ?? OPENAI_BASE_URL}${path}`;
|
|
1568
|
-
}
|
|
1569
|
-
function buildHeaders2(config) {
|
|
1570
|
-
const headers = {
|
|
1571
|
-
"Content-Type": "application/json",
|
|
1572
|
-
...config.extraHeaders
|
|
1573
|
-
};
|
|
1574
|
-
if (config.apiKey) {
|
|
1575
|
-
headers.Authorization = `Bearer ${config.apiKey}`;
|
|
1576
|
-
}
|
|
1577
|
-
return headers;
|
|
1578
|
-
}
|
|
1579
|
-
function toOpenAIContent(content) {
|
|
1580
|
-
if (typeof content === "string") return content;
|
|
1581
|
-
return content.map((p) => {
|
|
1582
|
-
if (p.type === "text") return { type: "text", text: p.text };
|
|
1583
|
-
return { type: "image_url", image_url: { url: p.url } };
|
|
1584
|
-
});
|
|
1585
|
-
}
|
|
1586
|
-
function toOpenAIMessages(messages) {
|
|
1587
|
-
const out = [];
|
|
1588
|
-
for (const m of messages) {
|
|
1589
|
-
if (m.role === "tool") {
|
|
1590
|
-
const { text, imageParts } = partitionToolContent(m.content);
|
|
1591
|
-
const fallback = imageParts.length > 0 ? "[image content; see next message]" : "[no content]";
|
|
1592
|
-
out.push({
|
|
1593
|
-
role: "tool",
|
|
1594
|
-
tool_call_id: m.toolCallId,
|
|
1595
|
-
content: text || fallback
|
|
1596
|
-
});
|
|
1597
|
-
if (imageParts.length > 0) {
|
|
1598
|
-
const userParts = [
|
|
1599
|
-
{
|
|
1600
|
-
type: "text",
|
|
1601
|
-
text: toolImageFollowupHeader(m.toolName, imageParts.length)
|
|
1602
|
-
},
|
|
1603
|
-
...imageParts.map((p) => ({
|
|
1604
|
-
type: "image_url",
|
|
1605
|
-
image_url: { url: p.url }
|
|
1606
|
-
}))
|
|
1607
|
-
];
|
|
1608
|
-
out.push({ role: "user", content: userParts });
|
|
1609
|
-
}
|
|
1610
|
-
continue;
|
|
1611
|
-
}
|
|
1612
|
-
if (m.role === "assistant") {
|
|
1613
|
-
const entry = {
|
|
1614
|
-
role: "assistant",
|
|
1615
|
-
content: typeof m.content === "string" && m.content.length > 0 ? m.content : null
|
|
1616
|
-
};
|
|
1617
|
-
if (m.toolCalls?.length) {
|
|
1618
|
-
entry.tool_calls = m.toolCalls.map((tc) => ({
|
|
1619
|
-
id: tc.id,
|
|
1620
|
-
type: "function",
|
|
1621
|
-
function: { name: tc.name, arguments: JSON.stringify(tc.args) }
|
|
1622
|
-
}));
|
|
1623
|
-
}
|
|
1624
|
-
out.push(entry);
|
|
1625
|
-
continue;
|
|
1626
|
-
}
|
|
1627
|
-
out.push({ role: m.role, content: toOpenAIContent(m.content) });
|
|
1628
|
-
}
|
|
1629
|
-
return out;
|
|
1630
|
-
}
|
|
1631
|
-
async function* streamChat4(params) {
|
|
1632
|
-
const { config, messages, tools, signal } = params;
|
|
1633
|
-
const body = {
|
|
1634
|
-
model: config.model,
|
|
1635
|
-
messages: toOpenAIMessages(messages),
|
|
1636
|
-
stream: true
|
|
1637
|
-
};
|
|
1638
|
-
if (config.provider === "openrouter") {
|
|
1639
|
-
body.stream_options = { include_usage: true };
|
|
1640
|
-
}
|
|
1641
|
-
if (config.temperature !== void 0) body.temperature = config.temperature;
|
|
1642
|
-
if (config.maxTokens !== void 0) body.max_tokens = config.maxTokens;
|
|
1643
|
-
if (tools && tools.length > 0) {
|
|
1644
|
-
body.tools = tools.map((t) => ({
|
|
1645
|
-
type: "function",
|
|
1646
|
-
function: {
|
|
1647
|
-
name: t.name,
|
|
1648
|
-
description: t.description,
|
|
1649
|
-
parameters: t.inputSchema
|
|
1650
|
-
}
|
|
1651
|
-
}));
|
|
1652
|
-
}
|
|
1653
|
-
const endpoint = buildEndpoint(config, "/chat/completions");
|
|
1654
|
-
const res = await fetch(endpoint, {
|
|
1655
|
-
method: "POST",
|
|
1656
|
-
headers: buildHeaders2(config),
|
|
1657
|
-
body: JSON.stringify(body),
|
|
1658
|
-
signal,
|
|
1659
|
-
...config.credentials ? { credentials: config.credentials } : {}
|
|
1660
|
-
});
|
|
1661
|
-
const responseBody = res.body;
|
|
1662
|
-
if (!res.ok) {
|
|
1663
|
-
await throwLlmRequestError(res);
|
|
1664
|
-
}
|
|
1665
|
-
if (!responseBody) {
|
|
1666
|
-
await throwLlmRequestError(res);
|
|
1667
|
-
}
|
|
1668
|
-
const stream = responseBody;
|
|
1669
|
-
const buffers = /* @__PURE__ */ new Map();
|
|
1670
|
-
for await (const ev of parseSSE(stream, signal)) {
|
|
1671
|
-
if (!ev.data || ev.data === "[DONE]") continue;
|
|
1672
|
-
let parsed;
|
|
1673
|
-
try {
|
|
1674
|
-
parsed = JSON.parse(ev.data);
|
|
1675
|
-
} catch {
|
|
1676
|
-
continue;
|
|
1677
|
-
}
|
|
1678
|
-
const usage = tokenUsageFromRecord(parsed?.usage);
|
|
1679
|
-
if (usage) {
|
|
1680
|
-
yield { type: "usage", usage };
|
|
1681
|
-
}
|
|
1682
|
-
const choice = parsed?.choices?.[0];
|
|
1683
|
-
if (!choice) continue;
|
|
1684
|
-
const delta = choice.delta ?? {};
|
|
1685
|
-
if (typeof delta.content === "string" && delta.content.length > 0) {
|
|
1686
|
-
yield { type: "text-delta", delta: delta.content };
|
|
1687
|
-
}
|
|
1688
|
-
if (Array.isArray(delta.tool_calls)) {
|
|
1689
|
-
for (const tc of delta.tool_calls) {
|
|
1690
|
-
const idx = typeof tc.index === "number" ? tc.index : 0;
|
|
1691
|
-
let buf = buffers.get(idx);
|
|
1692
|
-
if (!buf) {
|
|
1693
|
-
buf = {
|
|
1694
|
-
id: tc.id ?? `call_${idx}`,
|
|
1695
|
-
name: tc.function?.name ?? "",
|
|
1696
|
-
argsJson: "",
|
|
1697
|
-
started: false
|
|
1698
|
-
};
|
|
1699
|
-
buffers.set(idx, buf);
|
|
1700
|
-
}
|
|
1701
|
-
if (tc.id && !buf.id.startsWith("call_")) buf.id = tc.id;
|
|
1702
|
-
else if (tc.id) buf.id = tc.id;
|
|
1703
|
-
if (tc.function?.name) buf.name = tc.function.name;
|
|
1704
|
-
if (!buf.started && buf.name) {
|
|
1705
|
-
buf.started = true;
|
|
1706
|
-
yield {
|
|
1707
|
-
type: "tool-call-start",
|
|
1708
|
-
index: idx,
|
|
1709
|
-
toolCallId: buf.id,
|
|
1710
|
-
toolName: buf.name
|
|
1711
|
-
};
|
|
1712
|
-
}
|
|
1713
|
-
const argsChunk = tc.function?.arguments;
|
|
1714
|
-
if (typeof argsChunk === "string" && argsChunk.length > 0) {
|
|
1715
|
-
buf.argsJson += argsChunk;
|
|
1716
|
-
if (buf.started) {
|
|
1717
|
-
yield {
|
|
1718
|
-
type: "tool-call-args-delta",
|
|
1719
|
-
index: idx,
|
|
1720
|
-
toolCallId: buf.id,
|
|
1721
|
-
toolName: buf.name,
|
|
1722
|
-
argsDelta: argsChunk
|
|
1723
|
-
};
|
|
1724
|
-
}
|
|
1725
|
-
}
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
if (choice.finish_reason) {
|
|
1729
|
-
for (const [idx, buf] of buffers) {
|
|
1730
|
-
let args = {};
|
|
1731
|
-
if (buf.argsJson) {
|
|
1732
|
-
try {
|
|
1733
|
-
args = JSON.parse(buf.argsJson);
|
|
1734
|
-
} catch {
|
|
1735
|
-
args = {};
|
|
1736
|
-
}
|
|
1737
|
-
}
|
|
1738
|
-
yield {
|
|
1739
|
-
type: "tool-call-ready",
|
|
1740
|
-
index: idx,
|
|
1741
|
-
toolCallId: buf.id,
|
|
1742
|
-
toolName: buf.name,
|
|
1743
|
-
args
|
|
1744
|
-
};
|
|
1745
|
-
}
|
|
1746
|
-
buffers.clear();
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
1749
|
-
yield { type: "done" };
|
|
1750
|
-
}
|
|
1751
|
-
async function chat4(params) {
|
|
1752
|
-
const { config, messages, tools, signal } = params;
|
|
1753
|
-
const body = {
|
|
1754
|
-
model: config.model,
|
|
1755
|
-
messages: toOpenAIMessages(messages)
|
|
1756
|
-
};
|
|
1757
|
-
if (config.temperature !== void 0) body.temperature = config.temperature;
|
|
1758
|
-
if (config.maxTokens !== void 0) body.max_tokens = config.maxTokens;
|
|
1759
|
-
if (tools && tools.length > 0) {
|
|
1760
|
-
body.tools = tools.map((t) => ({
|
|
1761
|
-
type: "function",
|
|
1762
|
-
function: {
|
|
1763
|
-
name: t.name,
|
|
1764
|
-
description: t.description,
|
|
1765
|
-
parameters: t.inputSchema
|
|
1766
|
-
}
|
|
1767
|
-
}));
|
|
1768
|
-
}
|
|
1769
|
-
const endpoint = buildEndpoint(config, "/chat/completions");
|
|
1770
|
-
const res = await fetch(endpoint, {
|
|
1771
|
-
method: "POST",
|
|
1772
|
-
headers: buildHeaders2(config),
|
|
1773
|
-
body: JSON.stringify(body),
|
|
1774
|
-
signal,
|
|
1775
|
-
...config.credentials ? { credentials: config.credentials } : {}
|
|
1776
|
-
});
|
|
1777
|
-
if (!res.ok) {
|
|
1778
|
-
await throwLlmRequestError(res);
|
|
1779
|
-
}
|
|
1780
|
-
const json = await res.json();
|
|
1781
|
-
const choice = json?.choices?.[0]?.message;
|
|
1782
|
-
const text = typeof choice?.content === "string" ? choice.content : "";
|
|
1783
|
-
const toolCalls = Array.isArray(choice?.tool_calls) ? choice.tool_calls.map((tc) => {
|
|
1784
|
-
let args = {};
|
|
1785
|
-
try {
|
|
1786
|
-
args = JSON.parse(tc?.function?.arguments ?? "{}");
|
|
1787
|
-
} catch {
|
|
1788
|
-
args = {};
|
|
1789
|
-
}
|
|
1790
|
-
return {
|
|
1791
|
-
id: tc.id,
|
|
1792
|
-
name: tc.function?.name ?? "",
|
|
1793
|
-
args
|
|
1794
|
-
};
|
|
1795
|
-
}) : [];
|
|
1796
|
-
return { text, toolCalls };
|
|
1797
|
-
}
|
|
1798
|
-
|
|
1799
|
-
// src/llm/providers/index.ts
|
|
1800
|
-
function withOpenRouter(params) {
|
|
1801
|
-
return {
|
|
1802
|
-
...params,
|
|
1803
|
-
config: {
|
|
1804
|
-
...params.config,
|
|
1805
|
-
baseUrl: "https://openrouter.ai/api/v1",
|
|
1806
|
-
extraHeaders: {
|
|
1807
|
-
"HTTP-Referer": "https://inspector.mcp-use.com",
|
|
1808
|
-
"X-Title": "mcp-use Inspector"
|
|
1809
|
-
}
|
|
1810
|
-
}
|
|
1811
|
-
};
|
|
1812
|
-
}
|
|
1813
|
-
function streamChat5(params) {
|
|
1814
|
-
switch (params.config.provider) {
|
|
1815
|
-
case "openai":
|
|
1816
|
-
throw new Error(
|
|
1817
|
-
"provider 'openai' uses the Responses API via OpenAIResponsesDriver, not streamChat"
|
|
1818
|
-
);
|
|
1819
|
-
case "openai-compatible":
|
|
1820
|
-
return streamChat4(params);
|
|
1821
|
-
case "anthropic":
|
|
1822
|
-
return streamChat(params);
|
|
1823
|
-
case "google":
|
|
1824
|
-
return streamChat2(params);
|
|
1825
|
-
case "openrouter":
|
|
1826
|
-
return streamChat4(withOpenRouter(params));
|
|
1827
|
-
case "ollama":
|
|
1828
|
-
return streamChat3(params);
|
|
1829
|
-
default:
|
|
1830
|
-
throw new Error(`Unsupported LLM provider: ${params.config.provider}`);
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1833
|
-
function chat5(params) {
|
|
1834
|
-
switch (params.config.provider) {
|
|
1835
|
-
case "openai":
|
|
1836
|
-
throw new Error(
|
|
1837
|
-
"provider 'openai' uses the Responses API via OpenAIResponsesDriver, not chat"
|
|
1838
|
-
);
|
|
1839
|
-
case "openai-compatible":
|
|
1840
|
-
return chat4(params);
|
|
1841
|
-
case "anthropic":
|
|
1842
|
-
return chat(params);
|
|
1843
|
-
case "google":
|
|
1844
|
-
return chat2(params);
|
|
1845
|
-
case "openrouter":
|
|
1846
|
-
return chat4(withOpenRouter(params));
|
|
1847
|
-
case "ollama":
|
|
1848
|
-
return chat3(params);
|
|
1849
|
-
default:
|
|
1850
|
-
throw new Error(`Unsupported LLM provider: ${params.config.provider}`);
|
|
1851
|
-
}
|
|
1852
|
-
}
|
|
1853
|
-
|
|
1854
|
-
// src/llm/providers/openai-shared.ts
|
|
1855
|
-
var OPENAI_BASE_URL2 = "https://api.openai.com/v1";
|
|
1856
|
-
function buildEndpoint2(config, path) {
|
|
1857
|
-
const base = config.baseUrl ?? OPENAI_BASE_URL2;
|
|
1858
|
-
return `${base.replace(/\/$/, "")}${path}`;
|
|
1859
|
-
}
|
|
1860
|
-
function buildHeaders3(config) {
|
|
1861
|
-
const headers = {
|
|
1862
|
-
"Content-Type": "application/json",
|
|
1863
|
-
...config.extraHeaders
|
|
1864
|
-
};
|
|
1865
|
-
if (config.apiKey) {
|
|
1866
|
-
headers.Authorization = `Bearer ${config.apiKey}`;
|
|
1867
|
-
}
|
|
1868
|
-
return headers;
|
|
1869
|
-
}
|
|
1870
|
-
async function readOpenAIError(res) {
|
|
1871
|
-
const text = await res.text().catch(() => "");
|
|
1872
|
-
return `OpenAI request failed (${res.status} ${res.statusText}): ${text}`;
|
|
1873
|
-
}
|
|
1874
|
-
|
|
1875
|
-
// src/llm/providers/openai-responses.ts
|
|
1876
|
-
function toResponsesUserContent(content) {
|
|
1877
|
-
if (typeof content === "string") return content;
|
|
1878
|
-
return content.map((p) => {
|
|
1879
|
-
if (p.type === "text") return { type: "input_text", text: p.text };
|
|
1880
|
-
return { type: "input_image", image_url: p.url };
|
|
1881
|
-
});
|
|
1882
|
-
}
|
|
1883
|
-
function seedInputFromMessages(messages) {
|
|
1884
|
-
const systemParts = [];
|
|
1885
|
-
const input = [];
|
|
1886
|
-
for (const m of messages) {
|
|
1887
|
-
if (m.role === "system") {
|
|
1888
|
-
const text = typeof m.content === "string" ? m.content : m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
|
|
1889
|
-
if (text) systemParts.push(text);
|
|
1890
|
-
continue;
|
|
1891
|
-
}
|
|
1892
|
-
if (m.role === "user") {
|
|
1893
|
-
input.push({
|
|
1894
|
-
role: "user",
|
|
1895
|
-
content: toResponsesUserContent(m.content)
|
|
1896
|
-
});
|
|
1897
|
-
continue;
|
|
1898
|
-
}
|
|
1899
|
-
if (m.role === "assistant") {
|
|
1900
|
-
if (typeof m.content === "string" && m.content.length > 0) {
|
|
1901
|
-
input.push({
|
|
1902
|
-
type: "message",
|
|
1903
|
-
role: "assistant",
|
|
1904
|
-
content: [{ type: "output_text", text: m.content }]
|
|
1905
|
-
});
|
|
1906
|
-
}
|
|
1907
|
-
for (const tc of m.toolCalls ?? []) {
|
|
1908
|
-
input.push({
|
|
1909
|
-
type: "function_call",
|
|
1910
|
-
call_id: tc.id,
|
|
1911
|
-
name: tc.name,
|
|
1912
|
-
arguments: JSON.stringify(tc.args)
|
|
1913
|
-
});
|
|
1914
|
-
}
|
|
1915
|
-
continue;
|
|
1916
|
-
}
|
|
1917
|
-
if (m.role === "tool") {
|
|
1918
|
-
const { text, imageParts } = partitionToolContent(m.content);
|
|
1919
|
-
const fallback = imageParts.length > 0 ? "[image content; see next message]" : "[no content]";
|
|
1920
|
-
input.push({
|
|
1921
|
-
type: "function_call_output",
|
|
1922
|
-
call_id: m.toolCallId,
|
|
1923
|
-
output: text || fallback
|
|
1924
|
-
});
|
|
1925
|
-
if (imageParts.length > 0) {
|
|
1926
|
-
input.push({
|
|
1927
|
-
role: "user",
|
|
1928
|
-
content: [
|
|
1929
|
-
{
|
|
1930
|
-
type: "input_text",
|
|
1931
|
-
text: toolImageFollowupHeader(m.toolName, imageParts.length)
|
|
1932
|
-
},
|
|
1933
|
-
...imageParts.map((p) => ({
|
|
1934
|
-
type: "input_image",
|
|
1935
|
-
image_url: p.url
|
|
1936
|
-
}))
|
|
1937
|
-
]
|
|
1938
|
-
});
|
|
1939
|
-
}
|
|
1940
|
-
}
|
|
1941
|
-
}
|
|
1942
|
-
return {
|
|
1943
|
-
instructions: systemParts.length > 0 ? systemParts.join("\n\n") : void 0,
|
|
1944
|
-
input
|
|
1945
|
-
};
|
|
1946
|
-
}
|
|
1947
|
-
function toResponsesTools(tools) {
|
|
1948
|
-
return tools.map((t) => ({
|
|
1949
|
-
type: "function",
|
|
1950
|
-
name: t.name,
|
|
1951
|
-
description: t.description,
|
|
1952
|
-
parameters: t.inputSchema,
|
|
1953
|
-
strict: false
|
|
1954
|
-
}));
|
|
1955
|
-
}
|
|
1956
|
-
function appendToolOutputsToInput(input, callId, toolName, result) {
|
|
1957
|
-
const content = toolResultToContent(result);
|
|
1958
|
-
if (typeof content === "string") {
|
|
1959
|
-
input.push({
|
|
1960
|
-
type: "function_call_output",
|
|
1961
|
-
call_id: callId,
|
|
1962
|
-
output: content
|
|
1963
|
-
});
|
|
1964
|
-
return;
|
|
1965
|
-
}
|
|
1966
|
-
const { text, imageParts } = partitionToolContent(content);
|
|
1967
|
-
input.push({
|
|
1968
|
-
type: "function_call_output",
|
|
1969
|
-
call_id: callId,
|
|
1970
|
-
output: text || "[image content; see next message]"
|
|
1971
|
-
});
|
|
1972
|
-
if (imageParts.length > 0) {
|
|
1973
|
-
input.push({
|
|
1974
|
-
role: "user",
|
|
1975
|
-
content: [
|
|
1976
|
-
{
|
|
1977
|
-
type: "input_text",
|
|
1978
|
-
text: toolImageFollowupHeader(toolName, imageParts.length)
|
|
1979
|
-
},
|
|
1980
|
-
...imageParts.map((p) => ({
|
|
1981
|
-
type: "input_image",
|
|
1982
|
-
image_url: p.url
|
|
1983
|
-
}))
|
|
1984
|
-
]
|
|
1985
|
-
});
|
|
1986
|
-
}
|
|
1987
|
-
}
|
|
1988
|
-
function buildResponsesBody(params, stream) {
|
|
1989
|
-
const body = {
|
|
1990
|
-
model: params.config.model,
|
|
1991
|
-
input: params.input,
|
|
1992
|
-
store: false,
|
|
1993
|
-
include: ["reasoning.encrypted_content"],
|
|
1994
|
-
reasoning: { effort: params.config.reasoningEffort ?? "low" },
|
|
1995
|
-
stream
|
|
1996
|
-
};
|
|
1997
|
-
if (params.instructions) body.instructions = params.instructions;
|
|
1998
|
-
if (params.tools && params.tools.length > 0) {
|
|
1999
|
-
body.tools = toResponsesTools(params.tools);
|
|
2000
|
-
}
|
|
2001
|
-
if (params.config.maxTokens !== void 0) {
|
|
2002
|
-
body.max_output_tokens = params.config.maxTokens;
|
|
2003
|
-
}
|
|
2004
|
-
return body;
|
|
2005
|
-
}
|
|
2006
|
-
function extractFunctionCalls(output) {
|
|
2007
|
-
const calls = [];
|
|
2008
|
-
for (const item of output) {
|
|
2009
|
-
if (!item || typeof item !== "object") continue;
|
|
2010
|
-
const row = item;
|
|
2011
|
-
if (row.type !== "function_call") continue;
|
|
2012
|
-
if (typeof row.call_id !== "string" || typeof row.name !== "string") {
|
|
2013
|
-
continue;
|
|
2014
|
-
}
|
|
2015
|
-
calls.push({
|
|
2016
|
-
call_id: row.call_id,
|
|
2017
|
-
name: row.name,
|
|
2018
|
-
arguments: typeof row.arguments === "string" ? row.arguments : "{}"
|
|
2019
|
-
});
|
|
2020
|
-
}
|
|
2021
|
-
return calls;
|
|
2022
|
-
}
|
|
2023
|
-
function extractMessageText(output) {
|
|
2024
|
-
const parts = [];
|
|
2025
|
-
for (const item of output) {
|
|
2026
|
-
if (!item || typeof item !== "object") continue;
|
|
2027
|
-
const row = item;
|
|
2028
|
-
if (row.type !== "message" || row.role !== "assistant") continue;
|
|
2029
|
-
const content = row.content;
|
|
2030
|
-
if (!Array.isArray(content)) continue;
|
|
2031
|
-
for (const block of content) {
|
|
2032
|
-
if (!block || typeof block !== "object") continue;
|
|
2033
|
-
const b = block;
|
|
2034
|
-
if (b.type === "output_text" && typeof b.text === "string") {
|
|
2035
|
-
parts.push(b.text);
|
|
2036
|
-
}
|
|
2037
|
-
}
|
|
2038
|
-
}
|
|
2039
|
-
return parts.join("");
|
|
2040
|
-
}
|
|
2041
|
-
function parseArgs(argsJson) {
|
|
2042
|
-
if (!argsJson) return {};
|
|
2043
|
-
try {
|
|
2044
|
-
return JSON.parse(argsJson);
|
|
2045
|
-
} catch {
|
|
2046
|
-
return {};
|
|
2047
|
-
}
|
|
2048
|
-
}
|
|
2049
|
-
async function* streamResponsesTurn(params) {
|
|
2050
|
-
const res = await fetch(buildEndpoint2(params.config, "/responses"), {
|
|
2051
|
-
method: "POST",
|
|
2052
|
-
headers: buildHeaders3(params.config),
|
|
2053
|
-
body: JSON.stringify(buildResponsesBody(params, true)),
|
|
2054
|
-
signal: params.signal
|
|
2055
|
-
});
|
|
2056
|
-
if (!res.ok || !res.body) {
|
|
2057
|
-
throw new Error(await readOpenAIError(res));
|
|
2058
|
-
}
|
|
2059
|
-
const callBuffers = /* @__PURE__ */ new Map();
|
|
2060
|
-
let nextIndex = 0;
|
|
2061
|
-
let completedOutput = [];
|
|
2062
|
-
for await (const ev of parseSSE(res.body, params.signal)) {
|
|
2063
|
-
if (!ev.data || ev.data === "[DONE]") continue;
|
|
2064
|
-
let parsed;
|
|
2065
|
-
try {
|
|
2066
|
-
parsed = JSON.parse(ev.data);
|
|
2067
|
-
} catch {
|
|
2068
|
-
continue;
|
|
2069
|
-
}
|
|
2070
|
-
const type = typeof parsed.type === "string" ? parsed.type : ev.event;
|
|
2071
|
-
if (type === "response.output_text.delta") {
|
|
2072
|
-
const delta = parsed.delta;
|
|
2073
|
-
if (typeof delta === "string" && delta.length > 0) {
|
|
2074
|
-
yield { type: "text-delta", delta };
|
|
2075
|
-
}
|
|
2076
|
-
continue;
|
|
2077
|
-
}
|
|
2078
|
-
if (type === "response.output_item.added") {
|
|
2079
|
-
const item = parsed.item;
|
|
2080
|
-
if (item?.type === "function_call") {
|
|
2081
|
-
const callId = typeof item.call_id === "string" ? item.call_id : `call_${nextIndex}`;
|
|
2082
|
-
const name = typeof item.name === "string" ? item.name : "";
|
|
2083
|
-
const idx = nextIndex++;
|
|
2084
|
-
callBuffers.set(callId, {
|
|
2085
|
-
index: idx,
|
|
2086
|
-
name,
|
|
2087
|
-
argsJson: "",
|
|
2088
|
-
started: true
|
|
2089
|
-
});
|
|
2090
|
-
yield {
|
|
2091
|
-
type: "tool-call-start",
|
|
2092
|
-
index: idx,
|
|
2093
|
-
toolCallId: callId,
|
|
2094
|
-
toolName: name
|
|
2095
|
-
};
|
|
2096
|
-
}
|
|
2097
|
-
continue;
|
|
2098
|
-
}
|
|
2099
|
-
if (type === "response.function_call_arguments.delta") {
|
|
2100
|
-
const callId = typeof parsed.call_id === "string" ? parsed.call_id : "";
|
|
2101
|
-
const delta = typeof parsed.delta === "string" ? parsed.delta : "";
|
|
2102
|
-
const buf = callBuffers.get(callId);
|
|
2103
|
-
if (buf && delta.length > 0) {
|
|
2104
|
-
buf.argsJson += delta;
|
|
2105
|
-
yield {
|
|
2106
|
-
type: "tool-call-args-delta",
|
|
2107
|
-
index: buf.index,
|
|
2108
|
-
toolCallId: callId,
|
|
2109
|
-
toolName: buf.name,
|
|
2110
|
-
argsDelta: delta
|
|
2111
|
-
};
|
|
2112
|
-
}
|
|
2113
|
-
continue;
|
|
2114
|
-
}
|
|
2115
|
-
if (type === "response.function_call_arguments.done") {
|
|
2116
|
-
const callId = typeof parsed.call_id === "string" ? parsed.call_id : "";
|
|
2117
|
-
const argsRaw = typeof parsed.arguments === "string" ? parsed.arguments : "";
|
|
2118
|
-
const buf = callBuffers.get(callId);
|
|
2119
|
-
if (buf) {
|
|
2120
|
-
if (argsRaw) buf.argsJson = argsRaw;
|
|
2121
|
-
yield {
|
|
2122
|
-
type: "tool-call-ready",
|
|
2123
|
-
index: buf.index,
|
|
2124
|
-
toolCallId: callId,
|
|
2125
|
-
toolName: buf.name,
|
|
2126
|
-
args: parseArgs(buf.argsJson || argsRaw)
|
|
2127
|
-
};
|
|
2128
|
-
}
|
|
2129
|
-
continue;
|
|
2130
|
-
}
|
|
2131
|
-
if (type === "response.completed") {
|
|
2132
|
-
const response = parsed.response;
|
|
2133
|
-
if (response && Array.isArray(response.output)) {
|
|
2134
|
-
completedOutput = response.output;
|
|
2135
|
-
}
|
|
2136
|
-
const usage = tokenUsageFromRecord(response?.usage);
|
|
2137
|
-
if (usage) {
|
|
2138
|
-
yield { type: "usage", usage };
|
|
2139
|
-
}
|
|
2140
|
-
continue;
|
|
2141
|
-
}
|
|
2142
|
-
if (type === "error") {
|
|
2143
|
-
const message = typeof parsed.message === "string" ? parsed.message : "OpenAI Responses stream error";
|
|
2144
|
-
yield { type: "error", message };
|
|
2145
|
-
return [];
|
|
2146
|
-
}
|
|
2147
|
-
}
|
|
2148
|
-
yield { type: "done" };
|
|
2149
|
-
return completedOutput;
|
|
2150
|
-
}
|
|
2151
|
-
async function completeResponsesTurn(params) {
|
|
2152
|
-
const res = await fetch(buildEndpoint2(params.config, "/responses"), {
|
|
2153
|
-
method: "POST",
|
|
2154
|
-
headers: buildHeaders3(params.config),
|
|
2155
|
-
body: JSON.stringify(buildResponsesBody(params, false)),
|
|
2156
|
-
signal: params.signal
|
|
2157
|
-
});
|
|
2158
|
-
if (!res.ok) {
|
|
2159
|
-
throw new Error(await readOpenAIError(res));
|
|
2160
|
-
}
|
|
2161
|
-
const json = await res.json();
|
|
2162
|
-
const status = json.status;
|
|
2163
|
-
if (status !== "completed" && status !== void 0) {
|
|
2164
|
-
throw new Error(`OpenAI response ended with status ${String(status)}`);
|
|
2165
|
-
}
|
|
2166
|
-
const output = Array.isArray(json.output) ? json.output : [];
|
|
2167
|
-
const fnCalls = extractFunctionCalls(output);
|
|
2168
|
-
const text = typeof json.output_text === "string" ? json.output_text : extractMessageText(output);
|
|
2169
|
-
return {
|
|
2170
|
-
text,
|
|
2171
|
-
output,
|
|
2172
|
-
toolCalls: fnCalls.map((c) => ({
|
|
2173
|
-
id: c.call_id,
|
|
2174
|
-
name: c.name,
|
|
2175
|
-
args: parseArgs(c.arguments)
|
|
2176
|
-
}))
|
|
2177
|
-
};
|
|
2178
|
-
}
|
|
2179
|
-
|
|
2180
|
-
// src/llm/providers/openai-responses-driver.ts
|
|
2181
|
-
var OpenAIResponsesDriver = class {
|
|
2182
|
-
constructor(config) {
|
|
2183
|
-
this.config = config;
|
|
2184
|
-
}
|
|
2185
|
-
managesToolLoop = true;
|
|
2186
|
-
stream(params) {
|
|
2187
|
-
return this.streamSingleTurn(params);
|
|
2188
|
-
}
|
|
2189
|
-
async complete(params) {
|
|
2190
|
-
const { instructions, input } = seedInputFromMessages(params.messages);
|
|
2191
|
-
const result = await completeResponsesTurn({
|
|
2192
|
-
config: this.config,
|
|
2193
|
-
instructions,
|
|
2194
|
-
input,
|
|
2195
|
-
tools: params.tools,
|
|
2196
|
-
signal: params.signal
|
|
2197
|
-
});
|
|
2198
|
-
return { text: result.text, toolCalls: result.toolCalls };
|
|
2199
|
-
}
|
|
2200
|
-
async *streamToolLoop(params) {
|
|
2201
|
-
const { instructions, input: seeded } = seedInputFromMessages(
|
|
2202
|
-
params.messages
|
|
2203
|
-
);
|
|
2204
|
-
const input = [...seeded];
|
|
2205
|
-
const maxSteps = params.maxSteps ?? 10;
|
|
2206
|
-
for (let step = 0; step < maxSteps; step++) {
|
|
2207
|
-
if (params.signal?.aborted) return;
|
|
2208
|
-
const turn = streamResponsesTurn({
|
|
2209
|
-
config: this.config,
|
|
2210
|
-
instructions,
|
|
2211
|
-
input,
|
|
2212
|
-
tools: params.tools,
|
|
2213
|
-
signal: params.signal
|
|
2214
|
-
});
|
|
2215
|
-
let output = [];
|
|
2216
|
-
for (; ; ) {
|
|
2217
|
-
const next = await turn.next();
|
|
2218
|
-
if (next.done) {
|
|
2219
|
-
output = next.value ?? [];
|
|
2220
|
-
break;
|
|
2221
|
-
}
|
|
2222
|
-
yield next.value;
|
|
2223
|
-
if (next.value.type === "error") return;
|
|
2224
|
-
}
|
|
2225
|
-
input.push(...output);
|
|
2226
|
-
const functionCalls = extractFunctionCalls(output);
|
|
2227
|
-
if (functionCalls.length === 0) return;
|
|
2228
|
-
for (const call of functionCalls) {
|
|
2229
|
-
if (params.signal?.aborted) return;
|
|
2230
|
-
let args = {};
|
|
2231
|
-
try {
|
|
2232
|
-
args = JSON.parse(call.arguments);
|
|
2233
|
-
} catch {
|
|
2234
|
-
args = {};
|
|
2235
|
-
}
|
|
2236
|
-
let result;
|
|
2237
|
-
let isError = false;
|
|
2238
|
-
try {
|
|
2239
|
-
result = await params.callTool(call.name, args);
|
|
2240
|
-
isError = isToolResultError(result);
|
|
2241
|
-
} catch (err) {
|
|
2242
|
-
isError = true;
|
|
2243
|
-
result = {
|
|
2244
|
-
isError: true,
|
|
2245
|
-
error: err instanceof Error ? err.message : String(err)
|
|
2246
|
-
};
|
|
2247
|
-
}
|
|
2248
|
-
appendToolOutputsToInput(input, call.call_id, call.name, result);
|
|
2249
|
-
yield {
|
|
2250
|
-
type: "tool-result",
|
|
2251
|
-
toolCallId: call.call_id,
|
|
2252
|
-
toolName: call.name,
|
|
2253
|
-
result,
|
|
2254
|
-
isError
|
|
2255
|
-
};
|
|
2256
|
-
}
|
|
2257
|
-
}
|
|
2258
|
-
}
|
|
2259
|
-
async runToolLoopNonStreaming(params) {
|
|
2260
|
-
const { instructions, input: seeded } = seedInputFromMessages(
|
|
2261
|
-
params.messages
|
|
2262
|
-
);
|
|
2263
|
-
const input = [...seeded];
|
|
2264
|
-
const maxSteps = params.maxSteps ?? 10;
|
|
2265
|
-
const transcriptToolCalls = [];
|
|
2266
|
-
let finalText = "";
|
|
2267
|
-
for (let step = 0; step < maxSteps; step++) {
|
|
2268
|
-
if (params.signal?.aborted) break;
|
|
2269
|
-
const turn = await completeResponsesTurn({
|
|
2270
|
-
config: this.config,
|
|
2271
|
-
instructions,
|
|
2272
|
-
input,
|
|
2273
|
-
tools: params.tools,
|
|
2274
|
-
signal: params.signal
|
|
2275
|
-
});
|
|
2276
|
-
input.push(...turn.output);
|
|
2277
|
-
if (turn.toolCalls.length === 0) {
|
|
2278
|
-
finalText = turn.text;
|
|
2279
|
-
break;
|
|
2280
|
-
}
|
|
2281
|
-
for (const tc of turn.toolCalls) {
|
|
2282
|
-
let result;
|
|
2283
|
-
let isError = false;
|
|
2284
|
-
try {
|
|
2285
|
-
result = await params.callTool(tc.name, tc.args);
|
|
2286
|
-
isError = isToolResultError(result);
|
|
2287
|
-
} catch (err) {
|
|
2288
|
-
isError = true;
|
|
2289
|
-
result = {
|
|
2290
|
-
isError: true,
|
|
2291
|
-
error: err instanceof Error ? err.message : String(err)
|
|
2292
|
-
};
|
|
2293
|
-
}
|
|
2294
|
-
transcriptToolCalls.push({
|
|
2295
|
-
toolName: tc.name,
|
|
2296
|
-
args: tc.args,
|
|
2297
|
-
result
|
|
2298
|
-
});
|
|
2299
|
-
appendToolOutputsToInput(input, tc.id, tc.name, result);
|
|
2300
|
-
void isError;
|
|
2301
|
-
}
|
|
2302
|
-
}
|
|
2303
|
-
return { content: finalText, toolCalls: transcriptToolCalls };
|
|
2304
|
-
}
|
|
2305
|
-
async *streamSingleTurn(params) {
|
|
2306
|
-
const { instructions, input } = seedInputFromMessages(params.messages);
|
|
2307
|
-
const turn = streamResponsesTurn({
|
|
2308
|
-
config: this.config,
|
|
2309
|
-
instructions,
|
|
2310
|
-
input,
|
|
2311
|
-
tools: params.tools,
|
|
2312
|
-
signal: params.signal
|
|
2313
|
-
});
|
|
2314
|
-
for (; ; ) {
|
|
2315
|
-
const next = await turn.next();
|
|
2316
|
-
if (next.done) return;
|
|
2317
|
-
yield next.value;
|
|
2318
|
-
if (next.value.type === "error") return;
|
|
2319
|
-
}
|
|
2320
|
-
}
|
|
2321
|
-
};
|
|
2322
|
-
|
|
2323
|
-
// src/llm/driver.ts
|
|
2324
|
-
function createLlmDriver(config) {
|
|
2325
|
-
if (config.provider === "openai") {
|
|
2326
|
-
return new OpenAIResponsesDriver(config);
|
|
2327
|
-
}
|
|
2328
|
-
return new RestLlmDriver(config);
|
|
2329
|
-
}
|
|
2330
|
-
var RestLlmDriver = class {
|
|
2331
|
-
constructor(config) {
|
|
2332
|
-
this.config = config;
|
|
2333
|
-
}
|
|
2334
|
-
stream(params) {
|
|
2335
|
-
return streamChat5({
|
|
2336
|
-
config: this.config,
|
|
2337
|
-
messages: params.messages,
|
|
2338
|
-
tools: params.tools,
|
|
2339
|
-
signal: params.signal
|
|
2340
|
-
});
|
|
2341
|
-
}
|
|
2342
|
-
async complete(params) {
|
|
2343
|
-
const result = await chat5({
|
|
2344
|
-
config: this.config,
|
|
2345
|
-
messages: params.messages,
|
|
2346
|
-
tools: params.tools,
|
|
2347
|
-
signal: params.signal
|
|
2348
|
-
});
|
|
2349
|
-
return { text: result.text, toolCalls: result.toolCalls };
|
|
2350
|
-
}
|
|
2351
|
-
};
|
|
2352
|
-
|
|
2353
|
-
// src/llm/toolLoop.ts
|
|
2354
|
-
async function* runToolLoop(params) {
|
|
2355
|
-
if (params.driver.managesToolLoop && params.driver.streamToolLoop) {
|
|
2356
|
-
yield* params.driver.streamToolLoop(params);
|
|
2357
|
-
return;
|
|
2358
|
-
}
|
|
2359
|
-
const { driver, tools, callTool, signal } = params;
|
|
2360
|
-
const maxSteps = params.maxSteps ?? 10;
|
|
2361
|
-
const messages = [...params.messages];
|
|
2362
|
-
for (let step = 0; step < maxSteps; step++) {
|
|
2363
|
-
if (signal?.aborted) return;
|
|
2364
|
-
const pendingToolCalls = [];
|
|
2365
|
-
let assistantText = "";
|
|
2366
|
-
try {
|
|
2367
|
-
for await (const ev of driver.stream({
|
|
2368
|
-
messages,
|
|
2369
|
-
tools,
|
|
2370
|
-
signal
|
|
2371
|
-
})) {
|
|
2372
|
-
if (ev.type === "text-delta") {
|
|
2373
|
-
assistantText += ev.delta;
|
|
2374
|
-
} else if (ev.type === "tool-call-ready") {
|
|
2375
|
-
pendingToolCalls.push({
|
|
2376
|
-
id: ev.toolCallId,
|
|
2377
|
-
name: ev.toolName,
|
|
2378
|
-
args: ev.args
|
|
2379
|
-
});
|
|
2380
|
-
}
|
|
2381
|
-
yield ev;
|
|
2382
|
-
}
|
|
2383
|
-
} catch (err) {
|
|
2384
|
-
if (err instanceof LlmRequestError) {
|
|
2385
|
-
throw err;
|
|
2386
|
-
}
|
|
2387
|
-
yield {
|
|
2388
|
-
type: "error",
|
|
2389
|
-
message: err instanceof Error ? err.message : String(err)
|
|
2390
|
-
};
|
|
2391
|
-
return;
|
|
2392
|
-
}
|
|
2393
|
-
if (pendingToolCalls.length === 0) {
|
|
2394
|
-
return;
|
|
2395
|
-
}
|
|
2396
|
-
messages.push({
|
|
2397
|
-
role: "assistant",
|
|
2398
|
-
content: assistantText,
|
|
2399
|
-
toolCalls: pendingToolCalls
|
|
2400
|
-
});
|
|
2401
|
-
for (const tc of pendingToolCalls) {
|
|
2402
|
-
if (signal?.aborted) return;
|
|
2403
|
-
let result;
|
|
2404
|
-
let isError = false;
|
|
2405
|
-
try {
|
|
2406
|
-
result = await callTool(tc.name, tc.args);
|
|
2407
|
-
isError = isToolResultError(result);
|
|
2408
|
-
} catch (err) {
|
|
2409
|
-
isError = true;
|
|
2410
|
-
result = {
|
|
2411
|
-
isError: true,
|
|
2412
|
-
error: err instanceof Error ? err.message : String(err)
|
|
2413
|
-
};
|
|
2414
|
-
}
|
|
2415
|
-
messages.push({
|
|
2416
|
-
role: "tool",
|
|
2417
|
-
content: toolResultToContent(result),
|
|
2418
|
-
toolCallId: tc.id,
|
|
2419
|
-
toolName: tc.name,
|
|
2420
|
-
toolResult: result,
|
|
2421
|
-
toolIsError: isError
|
|
2422
|
-
});
|
|
2423
|
-
yield {
|
|
2424
|
-
type: "tool-result",
|
|
2425
|
-
toolCallId: tc.id,
|
|
2426
|
-
toolName: tc.name,
|
|
2427
|
-
result,
|
|
2428
|
-
isError
|
|
2429
|
-
};
|
|
2430
|
-
}
|
|
2431
|
-
}
|
|
2432
|
-
}
|
|
2433
|
-
async function runToolLoopNonStreaming(params) {
|
|
2434
|
-
if (params.driver.managesToolLoop && params.driver.runToolLoopNonStreaming) {
|
|
2435
|
-
return params.driver.runToolLoopNonStreaming(params);
|
|
2436
|
-
}
|
|
2437
|
-
const { driver, tools, callTool, signal } = params;
|
|
2438
|
-
const maxSteps = params.maxSteps ?? 10;
|
|
2439
|
-
const messages = [...params.messages];
|
|
2440
|
-
const transcriptToolCalls = [];
|
|
2441
|
-
let finalText = "";
|
|
2442
|
-
for (let step = 0; step < maxSteps; step++) {
|
|
2443
|
-
if (signal?.aborted) break;
|
|
2444
|
-
const { text, toolCalls } = await driver.complete({
|
|
2445
|
-
messages,
|
|
2446
|
-
tools,
|
|
2447
|
-
signal
|
|
2448
|
-
});
|
|
2449
|
-
if (toolCalls.length === 0) {
|
|
2450
|
-
finalText = text;
|
|
2451
|
-
break;
|
|
2452
|
-
}
|
|
2453
|
-
messages.push({
|
|
2454
|
-
role: "assistant",
|
|
2455
|
-
content: text,
|
|
2456
|
-
toolCalls
|
|
2457
|
-
});
|
|
2458
|
-
for (const tc of toolCalls) {
|
|
2459
|
-
let result;
|
|
2460
|
-
let isError = false;
|
|
2461
|
-
try {
|
|
2462
|
-
result = await callTool(tc.name, tc.args);
|
|
2463
|
-
isError = isToolResultError(result);
|
|
2464
|
-
} catch (err) {
|
|
2465
|
-
isError = true;
|
|
2466
|
-
result = {
|
|
2467
|
-
isError: true,
|
|
2468
|
-
error: err instanceof Error ? err.message : String(err)
|
|
2469
|
-
};
|
|
2470
|
-
}
|
|
2471
|
-
transcriptToolCalls.push({
|
|
2472
|
-
toolName: tc.name,
|
|
2473
|
-
args: tc.args,
|
|
2474
|
-
result
|
|
2475
|
-
});
|
|
2476
|
-
messages.push({
|
|
2477
|
-
role: "tool",
|
|
2478
|
-
content: toolResultToContent(result),
|
|
2479
|
-
toolCallId: tc.id,
|
|
2480
|
-
toolName: tc.name,
|
|
2481
|
-
toolResult: result,
|
|
2482
|
-
toolIsError: isError
|
|
2483
|
-
});
|
|
2484
|
-
}
|
|
2485
|
-
}
|
|
2486
|
-
return { content: finalText, toolCalls: transcriptToolCalls };
|
|
2487
|
-
}
|
|
2488
|
-
|
|
2489
|
-
// src/llm/native_runner.ts
|
|
2490
|
-
async function* streamNativeAgent(driver, options) {
|
|
2491
|
-
yield* runToolLoop({
|
|
2492
|
-
driver,
|
|
2493
|
-
messages: options.messages,
|
|
2494
|
-
tools: options.tools,
|
|
2495
|
-
callTool: options.callTool,
|
|
2496
|
-
maxSteps: options.maxSteps,
|
|
2497
|
-
signal: options.signal
|
|
2498
|
-
});
|
|
2499
|
-
}
|
|
2500
|
-
async function* streamNativeAgentSteps(driver, options) {
|
|
2501
|
-
let finalText = "";
|
|
2502
|
-
let pendingStep = null;
|
|
2503
|
-
for await (const ev of streamNativeAgent(driver, options)) {
|
|
2504
|
-
if (ev.type === "text-delta") {
|
|
2505
|
-
finalText += ev.delta;
|
|
2506
|
-
} else if (ev.type === "tool-call-ready") {
|
|
2507
|
-
pendingStep = {
|
|
2508
|
-
action: {
|
|
2509
|
-
tool: ev.toolName,
|
|
2510
|
-
toolInput: ev.args,
|
|
2511
|
-
log: `Calling tool ${ev.toolName}`
|
|
2512
|
-
},
|
|
2513
|
-
observation: ""
|
|
2514
|
-
};
|
|
2515
|
-
yield pendingStep;
|
|
2516
|
-
} else if (ev.type === "tool-result" && pendingStep) {
|
|
2517
|
-
const observation = typeof ev.result === "string" ? ev.result : JSON.stringify(ev.result ?? "");
|
|
2518
|
-
yield {
|
|
2519
|
-
action: pendingStep.action,
|
|
2520
|
-
observation
|
|
2521
|
-
};
|
|
2522
|
-
pendingStep = null;
|
|
2523
|
-
} else if (ev.type === "error") {
|
|
2524
|
-
throw new Error(ev.message);
|
|
2525
|
-
}
|
|
2526
|
-
}
|
|
2527
|
-
return finalText;
|
|
2528
|
-
}
|
|
2529
|
-
async function runNativeAgent(driver, options) {
|
|
2530
|
-
const result = await runToolLoopNonStreaming({
|
|
2531
|
-
driver,
|
|
2532
|
-
messages: options.messages,
|
|
2533
|
-
tools: options.tools,
|
|
2534
|
-
callTool: options.callTool,
|
|
2535
|
-
maxSteps: options.maxSteps,
|
|
2536
|
-
signal: options.signal
|
|
2537
|
-
});
|
|
2538
|
-
return result.content;
|
|
2539
|
-
}
|
|
2540
|
-
|
|
2541
|
-
// src/llm/provider_config.ts
|
|
2542
|
-
var import_client3 = require("@mcp-use/client");
|
|
2543
|
-
var PROVIDER_ENV = {
|
|
2544
|
-
openai: ["OPENAI_API_KEY"],
|
|
2545
|
-
anthropic: ["ANTHROPIC_API_KEY"],
|
|
2546
|
-
google: ["GOOGLE_API_KEY", "GOOGLE_GENERATIVE_AI_API_KEY"],
|
|
2547
|
-
openrouter: ["OPENROUTER_API_KEY"],
|
|
2548
|
-
ollama: [],
|
|
2549
|
-
"openai-compatible": ["OPENAI_API_KEY"]
|
|
2550
|
-
};
|
|
2551
|
-
function resolveApiKey(provider, config) {
|
|
2552
|
-
if (config?.apiKey) return config.apiKey;
|
|
2553
|
-
const envVars = PROVIDER_ENV[provider] ?? [];
|
|
2554
|
-
if (typeof process !== "undefined" && process.env) {
|
|
2555
|
-
for (const envVar of envVars) {
|
|
2556
|
-
const key = process.env[envVar];
|
|
2557
|
-
if (key) {
|
|
2558
|
-
import_client3.logger.debug(`Using API key from ${envVar} for ${provider}`);
|
|
2559
|
-
return key;
|
|
2560
|
-
}
|
|
2561
|
-
}
|
|
2562
|
-
}
|
|
2563
|
-
if (provider === "ollama") return "";
|
|
2564
|
-
const hint = envVars.length > 0 ? envVars.join(" or ") : "apiKey in llmConfig";
|
|
2565
|
-
throw new Error(`API key not found for provider '${provider}'. Set ${hint}.`);
|
|
2566
|
-
}
|
|
2567
|
-
function parseLLMStringToProviderConfig(llmString, config) {
|
|
2568
|
-
const parts = llmString.split("/");
|
|
2569
|
-
if (parts.length < 2) {
|
|
2570
|
-
throw new Error(
|
|
2571
|
-
`Invalid LLM string '${llmString}'. Expected 'provider/model'.`
|
|
2572
|
-
);
|
|
2573
|
-
}
|
|
2574
|
-
const provider = parts[0].toLowerCase();
|
|
2575
|
-
const model = parts.slice(1).join("/");
|
|
2576
|
-
const supported = [
|
|
2577
|
-
"openai",
|
|
2578
|
-
"anthropic",
|
|
2579
|
-
"google",
|
|
2580
|
-
"openrouter",
|
|
2581
|
-
"ollama",
|
|
2582
|
-
"openai-compatible"
|
|
2583
|
-
];
|
|
2584
|
-
if (!supported.includes(provider)) {
|
|
2585
|
-
throw new Error(
|
|
2586
|
-
`Unsupported provider '${provider}'. Supported: ${supported.join(", ")}`
|
|
2587
|
-
);
|
|
2588
|
-
}
|
|
2589
|
-
return {
|
|
2590
|
-
provider,
|
|
2591
|
-
model,
|
|
2592
|
-
apiKey: resolveApiKey(provider, config),
|
|
2593
|
-
temperature: config?.temperature,
|
|
2594
|
-
maxTokens: config?.maxTokens,
|
|
2595
|
-
baseUrl: config?.baseUrl,
|
|
2596
|
-
extraHeaders: config?.extraHeaders,
|
|
2597
|
-
credentials: config?.credentials
|
|
2598
|
-
};
|
|
2599
|
-
}
|
|
2600
|
-
function providerConfigFromOptions(provider, model, config) {
|
|
2601
|
-
return {
|
|
2602
|
-
provider,
|
|
2603
|
-
model,
|
|
2604
|
-
apiKey: resolveApiKey(provider, config),
|
|
2605
|
-
temperature: config?.temperature,
|
|
2606
|
-
maxTokens: config?.maxTokens,
|
|
2607
|
-
baseUrl: config?.baseUrl,
|
|
2608
|
-
extraHeaders: config?.extraHeaders,
|
|
2609
|
-
credentials: config?.credentials
|
|
2610
|
-
};
|
|
2611
|
-
}
|
|
2612
|
-
|
|
2613
|
-
// src/version.ts
|
|
2614
|
-
var VERSION = "2.0.0-beta.1";
|
|
2615
|
-
function getPackageVersion() {
|
|
2616
|
-
return VERSION;
|
|
2617
|
-
}
|
|
2618
|
-
|
|
2619
|
-
// src/agents/normalize_run_options.ts
|
|
2620
|
-
function normalizeRunOptions(queryOrOptions, maxSteps, manageConnector, _externalHistory, outputSchema, signal) {
|
|
2621
|
-
if (typeof queryOrOptions === "object" && queryOrOptions !== null) {
|
|
2622
|
-
return {
|
|
2623
|
-
prompt: queryOrOptions.prompt,
|
|
2624
|
-
maxSteps: queryOrOptions.maxSteps,
|
|
2625
|
-
manageConnector: queryOrOptions.manageConnector,
|
|
2626
|
-
messages: queryOrOptions.messages,
|
|
2627
|
-
schema: queryOrOptions.schema,
|
|
2628
|
-
signal: queryOrOptions.signal
|
|
2629
|
-
};
|
|
2630
|
-
}
|
|
2631
|
-
return {
|
|
2632
|
-
prompt: queryOrOptions,
|
|
2633
|
-
maxSteps,
|
|
2634
|
-
manageConnector,
|
|
2635
|
-
schema: outputSchema,
|
|
2636
|
-
signal
|
|
2637
|
-
};
|
|
2638
|
-
}
|
|
2639
|
-
|
|
2640
|
-
// src/agents/remote.ts
|
|
2641
|
-
var import_zod = require("zod");
|
|
2642
|
-
var import_client4 = require("@mcp-use/client");
|
|
2643
|
-
var API_CHATS_ENDPOINT = "/api/v1/chats";
|
|
2644
|
-
var API_CHAT_EXECUTE_ENDPOINT = "/api/v1/chats/{chat_id}/execute";
|
|
2645
|
-
function normalizeRemoteRunOptions(queryOrOptions, maxSteps, manageConnector, externalHistory, outputSchema) {
|
|
2646
|
-
if (typeof queryOrOptions === "object" && queryOrOptions !== null) {
|
|
2647
|
-
const options = queryOrOptions;
|
|
2648
|
-
return {
|
|
2649
|
-
query: options.prompt ?? "",
|
|
2650
|
-
maxSteps: options.maxSteps,
|
|
2651
|
-
manageConnector: options.manageConnector,
|
|
2652
|
-
externalHistory: options.externalHistory,
|
|
2653
|
-
outputSchema: options.schema
|
|
2654
|
-
};
|
|
2655
|
-
}
|
|
2656
|
-
return {
|
|
2657
|
-
query: queryOrOptions,
|
|
2658
|
-
maxSteps,
|
|
2659
|
-
manageConnector,
|
|
2660
|
-
externalHistory,
|
|
2661
|
-
outputSchema
|
|
2662
|
-
};
|
|
2663
|
-
}
|
|
2664
|
-
var RemoteAgent = class {
|
|
2665
|
-
agentId;
|
|
2666
|
-
apiKey;
|
|
2667
|
-
baseUrl;
|
|
2668
|
-
chatId = null;
|
|
2669
|
-
constructor(options) {
|
|
2670
|
-
this.agentId = options.agentId;
|
|
2671
|
-
this.baseUrl = options.baseUrl ?? "https://cloud.manufact.com";
|
|
2672
|
-
const apiKey = options.apiKey ?? (typeof process !== "undefined" && process.env?.MCP_USE_API_KEY);
|
|
2673
|
-
if (!apiKey) {
|
|
2674
|
-
throw new Error(
|
|
2675
|
-
"API key is required for remote execution. Please provide it as a parameter or set the MCP_USE_API_KEY environment variable. You can get an API key from https://cloud.manufact.com"
|
|
2676
|
-
);
|
|
2677
|
-
}
|
|
2678
|
-
this.apiKey = apiKey;
|
|
2679
|
-
}
|
|
2680
|
-
pydanticToJsonSchema(schema) {
|
|
2681
|
-
return (0, import_zod.toJSONSchema)(schema);
|
|
2682
|
-
}
|
|
2683
|
-
parseStructuredResponse(responseData, outputSchema) {
|
|
2684
|
-
let resultData;
|
|
2685
|
-
if (typeof responseData === "object" && responseData !== null) {
|
|
2686
|
-
if ("result" in responseData) {
|
|
2687
|
-
const outerResult = responseData.result;
|
|
2688
|
-
if (typeof outerResult === "object" && outerResult !== null && "result" in outerResult) {
|
|
2689
|
-
resultData = outerResult.result;
|
|
2690
|
-
} else {
|
|
2691
|
-
resultData = outerResult;
|
|
2692
|
-
}
|
|
2693
|
-
} else {
|
|
2694
|
-
resultData = responseData;
|
|
2695
|
-
}
|
|
2696
|
-
} else if (typeof responseData === "string") {
|
|
2697
|
-
try {
|
|
2698
|
-
resultData = JSON.parse(responseData);
|
|
2699
|
-
} catch {
|
|
2700
|
-
resultData = { content: responseData };
|
|
2701
|
-
}
|
|
2702
|
-
} else {
|
|
2703
|
-
resultData = responseData;
|
|
2704
|
-
}
|
|
2705
|
-
try {
|
|
2706
|
-
return outputSchema.parse(resultData);
|
|
2707
|
-
} catch (e) {
|
|
2708
|
-
import_client4.logger.warn(`Failed to parse structured output: ${e}`);
|
|
2709
|
-
const schemaShape = outputSchema._def?.shape();
|
|
2710
|
-
if (schemaShape && "content" in schemaShape) {
|
|
2711
|
-
return outputSchema.parse({ content: String(resultData) });
|
|
2712
|
-
}
|
|
2713
|
-
throw e;
|
|
2714
|
-
}
|
|
2715
|
-
}
|
|
2716
|
-
async createChatSession() {
|
|
2717
|
-
const chatPayload = {
|
|
2718
|
-
title: `Remote Agent Session - ${this.agentId}`,
|
|
2719
|
-
agent_id: this.agentId,
|
|
2720
|
-
type: "agent_execution"
|
|
2721
|
-
};
|
|
2722
|
-
const headers = {
|
|
2723
|
-
"Content-Type": "application/json",
|
|
2724
|
-
"x-api-key": this.apiKey
|
|
2725
|
-
};
|
|
2726
|
-
const chatUrl = `${this.baseUrl}${API_CHATS_ENDPOINT}`;
|
|
2727
|
-
import_client4.logger.debug(`\u{1F4DD} Creating chat session for agent ${this.agentId}`);
|
|
2728
|
-
try {
|
|
2729
|
-
const response = await fetch(chatUrl, {
|
|
2730
|
-
method: "POST",
|
|
2731
|
-
headers,
|
|
2732
|
-
body: JSON.stringify(chatPayload)
|
|
2733
|
-
});
|
|
2734
|
-
if (!response.ok) {
|
|
2735
|
-
const responseText = await response.text();
|
|
2736
|
-
const statusCode = response.status;
|
|
2737
|
-
if (statusCode === 404) {
|
|
2738
|
-
throw new Error(
|
|
2739
|
-
`Agent not found: Agent '${this.agentId}' does not exist or you don't have access to it. Please verify the agent ID and ensure it exists in your account.`
|
|
2740
|
-
);
|
|
2741
|
-
}
|
|
2742
|
-
throw new Error(
|
|
2743
|
-
`Failed to create chat session: ${statusCode} - ${responseText}`
|
|
2744
|
-
);
|
|
2745
|
-
}
|
|
2746
|
-
const chatData = await response.json();
|
|
2747
|
-
const chatId = chatData.id;
|
|
2748
|
-
import_client4.logger.debug(`\u2705 Chat session created: ${chatId}`);
|
|
2749
|
-
return chatId;
|
|
2750
|
-
} catch (e) {
|
|
2751
|
-
if (e instanceof Error) {
|
|
2752
|
-
throw new TypeError(`Failed to create chat session: ${e.message}`);
|
|
2753
|
-
}
|
|
2754
|
-
throw new Error(`Failed to create chat session: ${String(e)}`);
|
|
2755
|
-
}
|
|
2756
|
-
}
|
|
2757
|
-
async run(queryOrOptions, maxSteps, manageConnector, externalHistory, outputSchema) {
|
|
2758
|
-
const {
|
|
2759
|
-
query,
|
|
2760
|
-
maxSteps: steps,
|
|
2761
|
-
externalHistory: history,
|
|
2762
|
-
outputSchema: schema
|
|
2763
|
-
} = normalizeRemoteRunOptions(
|
|
2764
|
-
queryOrOptions,
|
|
2765
|
-
maxSteps,
|
|
2766
|
-
manageConnector,
|
|
2767
|
-
externalHistory,
|
|
2768
|
-
outputSchema
|
|
2769
|
-
);
|
|
2770
|
-
if (history !== void 0) {
|
|
2771
|
-
import_client4.logger.warn("External history is not yet supported for remote execution");
|
|
2772
|
-
}
|
|
2773
|
-
try {
|
|
2774
|
-
import_client4.logger.debug(`\u{1F310} Executing query on remote agent ${this.agentId}`);
|
|
2775
|
-
if (this.chatId === null) {
|
|
2776
|
-
this.chatId = await this.createChatSession();
|
|
2777
|
-
}
|
|
2778
|
-
const chatId = this.chatId;
|
|
2779
|
-
const executionPayload = {
|
|
2780
|
-
query,
|
|
2781
|
-
max_steps: steps ?? 10
|
|
2782
|
-
};
|
|
2783
|
-
if (schema) {
|
|
2784
|
-
executionPayload.output_schema = this.pydanticToJsonSchema(schema);
|
|
2785
|
-
import_client4.logger.debug(`\u{1F527} Using structured output with schema`);
|
|
2786
|
-
}
|
|
2787
|
-
const headers = {
|
|
2788
|
-
"Content-Type": "application/json",
|
|
2789
|
-
"x-api-key": this.apiKey
|
|
2790
|
-
};
|
|
2791
|
-
const executionUrl = `${this.baseUrl}${API_CHAT_EXECUTE_ENDPOINT.replace("{chat_id}", chatId)}`;
|
|
2792
|
-
import_client4.logger.debug(`\u{1F680} Executing agent in chat ${chatId}`);
|
|
2793
|
-
const response = await fetch(executionUrl, {
|
|
2794
|
-
method: "POST",
|
|
2795
|
-
headers,
|
|
2796
|
-
body: JSON.stringify(executionPayload),
|
|
2797
|
-
signal: AbortSignal.timeout(3e5)
|
|
2798
|
-
// 5 minute timeout
|
|
2799
|
-
});
|
|
2800
|
-
if (!response.ok) {
|
|
2801
|
-
const responseText = await response.text();
|
|
2802
|
-
const statusCode = response.status;
|
|
2803
|
-
if (statusCode === 401) {
|
|
2804
|
-
import_client4.logger.error(`\u274C Authentication failed: ${responseText}`);
|
|
2805
|
-
throw new Error(
|
|
2806
|
-
"Authentication failed: Invalid or missing API key. Please check your API key and ensure the MCP_USE_API_KEY environment variable is set correctly."
|
|
2807
|
-
);
|
|
2808
|
-
} else if (statusCode === 403) {
|
|
2809
|
-
import_client4.logger.error(`\u274C Access forbidden: ${responseText}`);
|
|
2810
|
-
throw new Error(
|
|
2811
|
-
`Access denied: You don't have permission to execute agent '${this.agentId}'. Check if the agent exists and you have the necessary permissions.`
|
|
2812
|
-
);
|
|
2813
|
-
} else if (statusCode === 404) {
|
|
2814
|
-
import_client4.logger.error(`\u274C Agent not found: ${responseText}`);
|
|
2815
|
-
throw new Error(
|
|
2816
|
-
`Agent not found: Agent '${this.agentId}' does not exist or you don't have access to it. Please verify the agent ID and ensure it exists in your account.`
|
|
2817
|
-
);
|
|
2818
|
-
} else if (statusCode === 422) {
|
|
2819
|
-
import_client4.logger.error(`\u274C Validation error: ${responseText}`);
|
|
2820
|
-
throw new Error(
|
|
2821
|
-
`Request validation failed: ${responseText}. Please check your query parameters and output schema format.`
|
|
2822
|
-
);
|
|
2823
|
-
} else if (statusCode === 500) {
|
|
2824
|
-
import_client4.logger.error(`\u274C Server error: ${responseText}`);
|
|
2825
|
-
throw new Error(
|
|
2826
|
-
"Internal server error occurred during agent execution. Please try again later or contact support if the issue persists."
|
|
2827
|
-
);
|
|
2828
|
-
} else {
|
|
2829
|
-
import_client4.logger.error(
|
|
2830
|
-
`\u274C Remote execution failed with status ${statusCode}: ${responseText}`
|
|
2831
|
-
);
|
|
2832
|
-
throw new Error(
|
|
2833
|
-
`Remote agent execution failed: ${statusCode} - ${responseText}`
|
|
2834
|
-
);
|
|
2835
|
-
}
|
|
2836
|
-
}
|
|
2837
|
-
const result = await response.json();
|
|
2838
|
-
import_client4.logger.debug(`\u{1F527} Response: ${JSON.stringify(result)}`);
|
|
2839
|
-
import_client4.logger.debug("\u2705 Remote execution completed successfully");
|
|
2840
|
-
if (typeof result === "object" && result !== null) {
|
|
2841
|
-
if (result.status === "error" || result.error !== null) {
|
|
2842
|
-
const errorMsg = result.error ?? String(result);
|
|
2843
|
-
import_client4.logger.error(`\u274C Remote agent execution failed: ${errorMsg}`);
|
|
2844
|
-
throw new Error(`Remote agent execution failed: ${errorMsg}`);
|
|
2845
|
-
}
|
|
2846
|
-
if (String(result).includes("failed to initialize")) {
|
|
2847
|
-
import_client4.logger.error(`\u274C Agent initialization failed: ${result}`);
|
|
2848
|
-
throw new Error(
|
|
2849
|
-
`Agent initialization failed on remote server. This usually indicates:
|
|
2850
|
-
\u2022 Invalid agent configuration (LLM model, system prompt)
|
|
2851
|
-
\u2022 Missing or invalid MCP server configurations
|
|
2852
|
-
\u2022 Network connectivity issues with MCP servers
|
|
2853
|
-
\u2022 Missing environment variables or credentials
|
|
2854
|
-
Raw error: ${result}`
|
|
2855
|
-
);
|
|
2856
|
-
}
|
|
2857
|
-
}
|
|
2858
|
-
if (schema) {
|
|
2859
|
-
return this.parseStructuredResponse(result, schema);
|
|
2860
|
-
}
|
|
2861
|
-
if (typeof result === "object" && result !== null && "result" in result) {
|
|
2862
|
-
return result.result;
|
|
2863
|
-
} else if (typeof result === "string") {
|
|
2864
|
-
return result;
|
|
2865
|
-
} else {
|
|
2866
|
-
return String(result);
|
|
2867
|
-
}
|
|
2868
|
-
} catch (e) {
|
|
2869
|
-
if (e instanceof Error) {
|
|
2870
|
-
if (e.name === "AbortError") {
|
|
2871
|
-
import_client4.logger.error(`\u274C Remote execution timed out: ${e}`);
|
|
2872
|
-
throw new Error(
|
|
2873
|
-
"Remote agent execution timed out. The server may be overloaded or the query is taking too long to process. Try again or use a simpler query."
|
|
2874
|
-
);
|
|
2875
|
-
}
|
|
2876
|
-
import_client4.logger.error(`\u274C Remote execution error: ${e}`);
|
|
2877
|
-
throw new Error(`Remote agent execution failed: ${e.message}`);
|
|
2878
|
-
}
|
|
2879
|
-
import_client4.logger.error(`\u274C Remote execution error: ${e}`);
|
|
2880
|
-
throw new Error(`Remote agent execution failed: ${String(e)}`);
|
|
2881
|
-
}
|
|
2882
|
-
}
|
|
2883
|
-
// eslint-disable-next-line require-yield
|
|
2884
|
-
async *stream(queryOrOptions, maxSteps, manageConnector, externalHistory, outputSchema) {
|
|
2885
|
-
const result = await this.run(
|
|
2886
|
-
queryOrOptions,
|
|
2887
|
-
maxSteps,
|
|
2888
|
-
manageConnector,
|
|
2889
|
-
externalHistory,
|
|
2890
|
-
outputSchema
|
|
2891
|
-
);
|
|
2892
|
-
return result;
|
|
2893
|
-
}
|
|
2894
|
-
async close() {
|
|
2895
|
-
import_client4.logger.debug("\u{1F50C} Remote agent client closed");
|
|
2896
|
-
}
|
|
2897
|
-
};
|
|
2898
|
-
|
|
2899
|
-
// src/agents/mcp_agent.ts
|
|
2900
|
-
var MCPAgent = class {
|
|
2901
|
-
static getPackageVersion() {
|
|
2902
|
-
return getPackageVersion();
|
|
2903
|
-
}
|
|
2904
|
-
driver;
|
|
2905
|
-
client;
|
|
2906
|
-
connectors = [];
|
|
2907
|
-
clientOwnedByAgent = false;
|
|
2908
|
-
nativeAdapter;
|
|
2909
|
-
providerTools = [];
|
|
2910
|
-
callTool;
|
|
2911
|
-
maxSteps;
|
|
2912
|
-
autoInitialize;
|
|
2913
|
-
systemPrompt;
|
|
2914
|
-
disallowedTools;
|
|
2915
|
-
exposeResourcesAsTools;
|
|
2916
|
-
exposePromptsAsTools;
|
|
2917
|
-
initialized = false;
|
|
2918
|
-
isRemote = false;
|
|
2919
|
-
remoteAgent = null;
|
|
2920
|
-
isSimplifiedMode = false;
|
|
2921
|
-
llmString;
|
|
2922
|
-
llmConfig;
|
|
2923
|
-
mcpServersConfig;
|
|
2924
|
-
explicitProviderConfig;
|
|
2925
|
-
conversationMessages = [];
|
|
2926
|
-
memoryEnabled;
|
|
2927
|
-
boundConnections;
|
|
2928
|
-
constructor(options) {
|
|
2929
|
-
if (options.agentId) {
|
|
2930
|
-
this.isRemote = true;
|
|
2931
|
-
this.remoteAgent = new RemoteAgent({
|
|
2932
|
-
agentId: options.agentId,
|
|
2933
|
-
apiKey: options.apiKey,
|
|
2934
|
-
baseUrl: options.baseUrl
|
|
2935
|
-
});
|
|
2936
|
-
this.maxSteps = options.maxSteps ?? 10;
|
|
2937
|
-
this.autoInitialize = options.autoInitialize ?? false;
|
|
2938
|
-
this.nativeAdapter = new NativeAdapter();
|
|
2939
|
-
this.systemPrompt = options.systemPrompt ?? "You are a helpful assistant with access to MCP tools.";
|
|
2940
|
-
this.disallowedTools = [];
|
|
2941
|
-
this.exposeResourcesAsTools = true;
|
|
2942
|
-
this.exposePromptsAsTools = true;
|
|
2943
|
-
this.memoryEnabled = options.memoryEnabled ?? true;
|
|
2944
|
-
return;
|
|
2945
|
-
}
|
|
2946
|
-
this.maxSteps = options.maxSteps ?? 10;
|
|
2947
|
-
this.autoInitialize = options.autoInitialize ?? false;
|
|
2948
|
-
this.systemPrompt = options.systemPrompt ?? "You are a helpful assistant with access to MCP tools.";
|
|
2949
|
-
this.disallowedTools = options.disallowedTools ?? [];
|
|
2950
|
-
this.exposeResourcesAsTools = options.exposeResourcesAsTools ?? true;
|
|
2951
|
-
this.exposePromptsAsTools = options.exposePromptsAsTools ?? true;
|
|
2952
|
-
this.memoryEnabled = options.memoryEnabled ?? true;
|
|
2953
|
-
this.nativeAdapter = new NativeAdapter(this.disallowedTools);
|
|
2954
|
-
this.llmConfig = options.llmConfig;
|
|
2955
|
-
this.resolveMcpServers(options.mcpServers);
|
|
2956
|
-
if (typeof options.llm === "string") {
|
|
2957
|
-
this.isSimplifiedMode = true;
|
|
2958
|
-
this.llmString = options.llm;
|
|
2959
|
-
if (!this.hasLiveConnections() && (!this.mcpServersConfig || Object.keys(this.mcpServersConfig).length === 0) && !options.client && !options.connectors?.length) {
|
|
2960
|
-
throw new Error(
|
|
2961
|
-
"Simplified mode requires mcpServers, or an existing client/connectors."
|
|
2962
|
-
);
|
|
2963
|
-
}
|
|
2964
|
-
} else {
|
|
2965
|
-
this.explicitProviderConfig = options.llm;
|
|
2966
|
-
this.client = options.client;
|
|
2967
|
-
this.connectors = options.connectors ?? [];
|
|
2968
|
-
if (!this.hasLiveConnections() && !this.client && this.connectors.length === 0 && (!this.mcpServersConfig || Object.keys(this.mcpServersConfig).length === 0)) {
|
|
2969
|
-
throw new Error(
|
|
2970
|
-
"Explicit mode requires mcpServers, client, or connectors."
|
|
2971
|
-
);
|
|
2972
|
-
}
|
|
2973
|
-
}
|
|
2974
|
-
if (options.client) this.client = options.client;
|
|
2975
|
-
if (options.connectors?.length) this.connectors = options.connectors;
|
|
2976
|
-
if (this.hasLiveConnections() && this.explicitProviderConfig) {
|
|
2977
|
-
this.bindConnections(this.boundConnections, this.explicitProviderConfig);
|
|
2978
|
-
} else if (this.hasLiveConnections() && this.llmString) {
|
|
2979
|
-
this.driver = createLlmDriver(
|
|
2980
|
-
parseLLMStringToProviderConfig(this.llmString, this.llmConfig)
|
|
2981
|
-
);
|
|
2982
|
-
this.bindConnections(
|
|
2983
|
-
this.boundConnections,
|
|
2984
|
-
parseLLMStringToProviderConfig(this.llmString, this.llmConfig)
|
|
2985
|
-
);
|
|
2986
|
-
}
|
|
2987
|
-
}
|
|
2988
|
-
resolveMcpServers(mcpServers) {
|
|
2989
|
-
if (!mcpServers) return;
|
|
2990
|
-
if (Array.isArray(mcpServers)) {
|
|
2991
|
-
this.boundConnections = mcpServers;
|
|
2992
|
-
return;
|
|
2993
|
-
}
|
|
2994
|
-
this.mcpServersConfig = mcpServers;
|
|
2995
|
-
}
|
|
2996
|
-
hasLiveConnections() {
|
|
2997
|
-
return (this.boundConnections?.length ?? 0) > 0;
|
|
2998
|
-
}
|
|
2999
|
-
async initialize() {
|
|
3000
|
-
if (this.isRemote) {
|
|
3001
|
-
this.initialized = true;
|
|
3002
|
-
return;
|
|
3003
|
-
}
|
|
3004
|
-
if (this.initialized) return;
|
|
3005
|
-
if (this.isSimplifiedMode) {
|
|
3006
|
-
if (!this.client && this.mcpServersConfig) {
|
|
3007
|
-
const { MCPClient } = await import("@mcp-use/client");
|
|
3008
|
-
this.client = new MCPClient({ mcpServers: this.mcpServersConfig });
|
|
3009
|
-
this.clientOwnedByAgent = true;
|
|
3010
|
-
}
|
|
3011
|
-
if (this.llmString) {
|
|
3012
|
-
this.driver = createLlmDriver(
|
|
3013
|
-
parseLLMStringToProviderConfig(this.llmString, this.llmConfig)
|
|
3014
|
-
);
|
|
3015
|
-
}
|
|
3016
|
-
} else if (this.explicitProviderConfig) {
|
|
3017
|
-
this.driver = createLlmDriver(this.explicitProviderConfig);
|
|
3018
|
-
}
|
|
3019
|
-
if (!this.driver) {
|
|
3020
|
-
throw new Error("LLM driver not configured.");
|
|
3021
|
-
}
|
|
3022
|
-
if (!this.hasLiveConnections()) {
|
|
3023
|
-
await this.loadTools();
|
|
3024
|
-
}
|
|
3025
|
-
this.initialized = true;
|
|
3026
|
-
import_client5.logger.debug("MCPAgent initialized");
|
|
3027
|
-
}
|
|
3028
|
-
async loadTools() {
|
|
3029
|
-
const entries = [];
|
|
3030
|
-
if (this.client) {
|
|
3031
|
-
if (!this.client.activeSessions || Object.keys(this.client.activeSessions).length === 0) {
|
|
3032
|
-
await this.client.createAllSessions();
|
|
3033
|
-
}
|
|
3034
|
-
const sessions = this.client.getAllActiveSessions();
|
|
3035
|
-
const connectors = Object.values(sessions).map((s) => s.connector);
|
|
3036
|
-
entries.push(
|
|
3037
|
-
...await this.nativeAdapter.createToolsFromConnectors(connectors)
|
|
3038
|
-
);
|
|
3039
|
-
if (this.exposeResourcesAsTools) {
|
|
3040
|
-
entries.push(
|
|
3041
|
-
...await this.nativeAdapter.createResourcesFromConnectors(
|
|
3042
|
-
connectors
|
|
3043
|
-
)
|
|
3044
|
-
);
|
|
3045
|
-
}
|
|
3046
|
-
if (this.exposePromptsAsTools) {
|
|
3047
|
-
entries.push(
|
|
3048
|
-
...await this.nativeAdapter.createPromptsFromConnectors(connectors)
|
|
3049
|
-
);
|
|
3050
|
-
}
|
|
3051
|
-
} else {
|
|
3052
|
-
for (const connector of this.connectors) {
|
|
3053
|
-
if (!connector.isClientConnected) await connector.connect();
|
|
3054
|
-
}
|
|
3055
|
-
entries.push(
|
|
3056
|
-
...await this.nativeAdapter.createToolsFromConnectors(this.connectors)
|
|
3057
|
-
);
|
|
3058
|
-
if (this.exposeResourcesAsTools) {
|
|
3059
|
-
entries.push(
|
|
3060
|
-
...await this.nativeAdapter.createResourcesFromConnectors(
|
|
3061
|
-
this.connectors
|
|
3062
|
-
)
|
|
3063
|
-
);
|
|
3064
|
-
}
|
|
3065
|
-
if (this.exposePromptsAsTools) {
|
|
3066
|
-
entries.push(
|
|
3067
|
-
...await this.nativeAdapter.createPromptsFromConnectors(
|
|
3068
|
-
this.connectors
|
|
3069
|
-
)
|
|
3070
|
-
);
|
|
3071
|
-
}
|
|
3072
|
-
}
|
|
3073
|
-
this.providerTools = this.nativeAdapter.toProviderTools(entries);
|
|
3074
|
-
this.callTool = this.nativeAdapter.createCallTool();
|
|
3075
|
-
}
|
|
3076
|
-
bindConnections(connections, providerConfig) {
|
|
3077
|
-
const disallowed = new Set(this.disallowedTools);
|
|
3078
|
-
const usedNames = /* @__PURE__ */ new Set();
|
|
3079
|
-
const routes = /* @__PURE__ */ new Map();
|
|
3080
|
-
const reserveName = (name) => {
|
|
3081
|
-
if (!usedNames.has(name)) {
|
|
3082
|
-
usedNames.add(name);
|
|
3083
|
-
return name;
|
|
3084
|
-
}
|
|
3085
|
-
let i = 2;
|
|
3086
|
-
while (usedNames.has(`${name}_${i}`)) i++;
|
|
3087
|
-
const fallback = `${name}_${i}`;
|
|
3088
|
-
usedNames.add(fallback);
|
|
3089
|
-
return fallback;
|
|
3090
|
-
};
|
|
3091
|
-
const providerTools = [];
|
|
3092
|
-
for (const connection of connections) {
|
|
3093
|
-
for (const tool of connection.tools ?? []) {
|
|
3094
|
-
if (disallowed.has(tool.name)) continue;
|
|
3095
|
-
const name = reserveName(tool.name);
|
|
3096
|
-
routes.set(name, { connection, mcpName: tool.name });
|
|
3097
|
-
providerTools.push({
|
|
3098
|
-
name,
|
|
3099
|
-
description: tool.description,
|
|
3100
|
-
inputSchema: tool.inputSchema ?? {
|
|
3101
|
-
type: "object",
|
|
3102
|
-
properties: {}
|
|
3103
|
-
}
|
|
3104
|
-
});
|
|
3105
|
-
}
|
|
3106
|
-
}
|
|
3107
|
-
this.providerTools = providerTools;
|
|
3108
|
-
this.callTool = async (name, args) => {
|
|
3109
|
-
const route = routes.get(name);
|
|
3110
|
-
if (!route) {
|
|
3111
|
-
throw new Error(`Unknown tool: ${name}`);
|
|
3112
|
-
}
|
|
3113
|
-
return route.connection.callTool(route.mcpName, args);
|
|
3114
|
-
};
|
|
3115
|
-
this.driver = createLlmDriver(providerConfig);
|
|
3116
|
-
this.initialized = true;
|
|
3117
|
-
}
|
|
3118
|
-
buildMessages(options) {
|
|
3119
|
-
const messages = [
|
|
3120
|
-
{ role: "system", content: this.systemPrompt }
|
|
3121
|
-
];
|
|
3122
|
-
if (this.memoryEnabled && this.conversationMessages.length > 0) {
|
|
3123
|
-
messages.push(...this.conversationMessages);
|
|
3124
|
-
}
|
|
3125
|
-
if (options.messages?.length) {
|
|
3126
|
-
messages.push(...options.messages);
|
|
3127
|
-
}
|
|
3128
|
-
if (options.prompt) {
|
|
3129
|
-
messages.push({ role: "user", content: options.prompt });
|
|
3130
|
-
}
|
|
3131
|
-
return messages;
|
|
3132
|
-
}
|
|
3133
|
-
getConversationHistory() {
|
|
3134
|
-
return [...this.conversationMessages];
|
|
3135
|
-
}
|
|
3136
|
-
clearConversationHistory() {
|
|
3137
|
-
this.conversationMessages = [];
|
|
3138
|
-
}
|
|
3139
|
-
setSystemMessage(message) {
|
|
3140
|
-
this.systemPrompt = message;
|
|
3141
|
-
}
|
|
3142
|
-
async ensureReady(manageConnector = true) {
|
|
3143
|
-
if (manageConnector && !this.initialized) {
|
|
3144
|
-
if (this.autoInitialize) {
|
|
3145
|
-
await this.initialize();
|
|
3146
|
-
} else {
|
|
3147
|
-
throw new Error("MCPAgent not initialized. Call initialize() first.");
|
|
3148
|
-
}
|
|
3149
|
-
}
|
|
3150
|
-
if (!this.driver || !this.callTool) {
|
|
3151
|
-
throw new Error("MCPAgent driver or tools not ready.");
|
|
3152
|
-
}
|
|
3153
|
-
}
|
|
3154
|
-
nativeRunParams(options) {
|
|
3155
|
-
return {
|
|
3156
|
-
messages: this.buildMessages(options),
|
|
3157
|
-
tools: this.providerTools,
|
|
3158
|
-
callTool: this.callTool,
|
|
3159
|
-
maxSteps: options.maxSteps ?? this.maxSteps,
|
|
3160
|
-
signal: options.signal
|
|
3161
|
-
};
|
|
3162
|
-
}
|
|
3163
|
-
async run(queryOrOptions, maxSteps, manageConnector, externalHistory, outputSchema, signal) {
|
|
3164
|
-
const options = normalizeRunOptions(
|
|
3165
|
-
queryOrOptions,
|
|
3166
|
-
maxSteps,
|
|
3167
|
-
manageConnector,
|
|
3168
|
-
externalHistory,
|
|
3169
|
-
outputSchema,
|
|
3170
|
-
signal
|
|
3171
|
-
);
|
|
3172
|
-
if (options.schema) {
|
|
3173
|
-
throw new Error(
|
|
3174
|
-
"Structured output requires @mcp-use/agent/langchain or pass a JSON schema via prompt."
|
|
3175
|
-
);
|
|
3176
|
-
}
|
|
3177
|
-
if (this.isRemote && this.remoteAgent) {
|
|
3178
|
-
return this.remoteAgent.run(
|
|
3179
|
-
options.prompt ?? "",
|
|
3180
|
-
options.maxSteps,
|
|
3181
|
-
options.manageConnector
|
|
3182
|
-
);
|
|
3183
|
-
}
|
|
3184
|
-
await this.ensureReady(options.manageConnector ?? true);
|
|
3185
|
-
const result = await runNativeAgent(
|
|
3186
|
-
this.driver,
|
|
3187
|
-
this.nativeRunParams(options)
|
|
3188
|
-
);
|
|
3189
|
-
if (this.memoryEnabled && options.prompt) {
|
|
3190
|
-
this.conversationMessages.push({
|
|
3191
|
-
role: "user",
|
|
3192
|
-
content: options.prompt
|
|
3193
|
-
});
|
|
3194
|
-
this.conversationMessages.push({
|
|
3195
|
-
role: "assistant",
|
|
3196
|
-
content: result
|
|
3197
|
-
});
|
|
3198
|
-
}
|
|
3199
|
-
return result;
|
|
3200
|
-
}
|
|
3201
|
-
async *stream(queryOrOptions, maxSteps, manageConnector, externalHistory, outputSchema, signal) {
|
|
3202
|
-
const options = normalizeRunOptions(
|
|
3203
|
-
queryOrOptions,
|
|
3204
|
-
maxSteps,
|
|
3205
|
-
manageConnector,
|
|
3206
|
-
externalHistory,
|
|
3207
|
-
outputSchema,
|
|
3208
|
-
signal
|
|
3209
|
-
);
|
|
3210
|
-
if (this.isRemote && this.remoteAgent) {
|
|
3211
|
-
const result2 = await this.remoteAgent.run(
|
|
3212
|
-
options.prompt ?? "",
|
|
3213
|
-
options.maxSteps,
|
|
3214
|
-
options.manageConnector
|
|
3215
|
-
);
|
|
3216
|
-
return result2;
|
|
3217
|
-
}
|
|
3218
|
-
await this.ensureReady(options.manageConnector ?? true);
|
|
3219
|
-
const result = yield* streamNativeAgentSteps(
|
|
3220
|
-
this.driver,
|
|
3221
|
-
this.nativeRunParams(options)
|
|
3222
|
-
);
|
|
3223
|
-
if (this.memoryEnabled && options.prompt) {
|
|
3224
|
-
this.conversationMessages.push({
|
|
3225
|
-
role: "user",
|
|
3226
|
-
content: options.prompt
|
|
3227
|
-
});
|
|
3228
|
-
this.conversationMessages.push({
|
|
3229
|
-
role: "assistant",
|
|
3230
|
-
content: result
|
|
3231
|
-
});
|
|
3232
|
-
}
|
|
3233
|
-
return result;
|
|
3234
|
-
}
|
|
3235
|
-
async *streamEvents(queryOrOptions, maxSteps, manageConnector, externalHistory, outputSchema, signal) {
|
|
3236
|
-
const options = normalizeRunOptions(
|
|
3237
|
-
queryOrOptions,
|
|
3238
|
-
maxSteps,
|
|
3239
|
-
manageConnector,
|
|
3240
|
-
externalHistory,
|
|
3241
|
-
outputSchema,
|
|
3242
|
-
signal
|
|
3243
|
-
);
|
|
3244
|
-
if (this.isRemote && this.remoteAgent) {
|
|
3245
|
-
throw new Error("streamEvents is not supported for remote agents.");
|
|
3246
|
-
}
|
|
3247
|
-
await this.ensureReady(options.manageConnector ?? true);
|
|
3248
|
-
yield* streamNativeAgent(this.driver, this.nativeRunParams(options));
|
|
3249
|
-
}
|
|
3250
|
-
/** Single-shot completion without tools (sampling, props generation). */
|
|
3251
|
-
async chat(options) {
|
|
3252
|
-
await this.ensureReady();
|
|
3253
|
-
const result = await this.driver.complete({
|
|
3254
|
-
messages: options.messages,
|
|
3255
|
-
tools: [],
|
|
3256
|
-
signal: options.signal
|
|
3257
|
-
});
|
|
3258
|
-
return result.text;
|
|
3259
|
-
}
|
|
3260
|
-
async close() {
|
|
3261
|
-
if (this.clientOwnedByAgent && this.client) {
|
|
3262
|
-
await this.client.closeAllSessions?.();
|
|
3263
|
-
}
|
|
3264
|
-
this.initialized = false;
|
|
3265
|
-
}
|
|
3266
|
-
};
|
|
3267
|
-
|
|
3268
|
-
// src/llm/chat.ts
|
|
3269
|
-
async function completeChat(params) {
|
|
3270
|
-
const driver = createLlmDriver(params.config);
|
|
3271
|
-
const result = await driver.complete({
|
|
3272
|
-
messages: params.messages,
|
|
3273
|
-
tools: [],
|
|
3274
|
-
signal: params.signal
|
|
3275
|
-
});
|
|
3276
|
-
return result.text;
|
|
3277
|
-
}
|
|
3278
|
-
|
|
3279
|
-
// src/agents/prompts/index.ts
|
|
3280
|
-
var CODE_MODE_PROMPT = "Use code execution mode to discover and call MCP tools programmatically.";
|
|
3281
|
-
var PROMPTS = {
|
|
3282
|
-
CODE_MODE: CODE_MODE_PROMPT
|
|
3283
|
-
};
|
|
3284
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
3285
|
-
0 && (module.exports = {
|
|
3286
|
-
BaseAdapter,
|
|
3287
|
-
DEFAULT_OLLAMA_BASE_URL,
|
|
3288
|
-
LlmRequestError,
|
|
3289
|
-
MCPAgent,
|
|
3290
|
-
NativeAdapter,
|
|
3291
|
-
OllamaCorsError,
|
|
3292
|
-
PROMPTS,
|
|
3293
|
-
RemoteAgent,
|
|
3294
|
-
buildOllamaApiUrl,
|
|
3295
|
-
chat,
|
|
3296
|
-
completeChat,
|
|
3297
|
-
convertMessagesToProvider,
|
|
3298
|
-
normalizeOllamaBaseUrl,
|
|
3299
|
-
parseLLMStringToProviderConfig,
|
|
3300
|
-
providerConfigFromOptions
|
|
3301
|
-
});
|
|
3302
|
-
//# sourceMappingURL=index.cjs.map
|