@contentgrowth/llm-service 1.2.4 → 1.3.1
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/README.md +232 -0
- package/dist/index.cjs +226 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +69 -13
- package/dist/index.d.ts +69 -13
- package/dist/index.js +226 -68
- package/dist/index.js.map +1 -1
- package/dist/styles.css +2 -1
- package/dist/ui/react/components/index.d.cts +2 -3
- package/dist/ui/react/components/index.d.ts +2 -3
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -74,6 +74,7 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
|
|
|
74
74
|
models: MODEL_CONFIGS[tenantConfig.provider],
|
|
75
75
|
// for backward compatibility, api_key is deprecated and shall never be used!!
|
|
76
76
|
apiKey: tenantConfig.apiKey || tenantConfig.api_key,
|
|
77
|
+
baseURL: tenantConfig.baseURL || tenantConfig.base_url || void 0,
|
|
77
78
|
project: tenantConfig.project,
|
|
78
79
|
location: tenantConfig.location,
|
|
79
80
|
temperature: parseFloat(env.DEFAULT_TEMPERATURE || "0.7"),
|
|
@@ -87,9 +88,11 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
|
|
|
87
88
|
const provider = ((_a = env.LLM_PROVIDER) == null ? void 0 : _a.toLowerCase()) || "openai";
|
|
88
89
|
const providerDefaults = MODEL_CONFIGS[provider] || MODEL_CONFIGS["openai"];
|
|
89
90
|
let apiKey;
|
|
91
|
+
let baseURL;
|
|
90
92
|
let models = { ...providerDefaults };
|
|
91
93
|
if (provider === "openai") {
|
|
92
94
|
apiKey = env.OPENAI_API_KEY;
|
|
95
|
+
baseURL = env.OPENAI_BASE_URL || env.LLM_BASE_URL || void 0;
|
|
93
96
|
models = {
|
|
94
97
|
default: env.OPENAI_MODEL || providerDefaults.default,
|
|
95
98
|
edge: env.OPENAI_MODEL_EDGE || providerDefaults.edge,
|
|
@@ -99,6 +102,7 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
|
|
|
99
102
|
};
|
|
100
103
|
} else if (provider === "gemini") {
|
|
101
104
|
apiKey = env.GEMINI_API_KEY;
|
|
105
|
+
baseURL = env.GEMINI_BASE_URL || env.LLM_BASE_URL || void 0;
|
|
102
106
|
models = {
|
|
103
107
|
default: env.GEMINI_MODEL || providerDefaults.default,
|
|
104
108
|
edge: env.GEMINI_MODEL_EDGE || providerDefaults.edge,
|
|
@@ -108,8 +112,19 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
|
|
|
108
112
|
image: env.GEMINI_IMAGE_MODEL || providerDefaults.image,
|
|
109
113
|
video: env.GEMINI_VIDEO_MODEL || providerDefaults.video
|
|
110
114
|
};
|
|
115
|
+
} else if (provider === "anthropic") {
|
|
116
|
+
apiKey = env.ANTHROPIC_API_KEY;
|
|
117
|
+
baseURL = env.ANTHROPIC_BASE_URL || env.LLM_BASE_URL || void 0;
|
|
118
|
+
models = {
|
|
119
|
+
default: env.ANTHROPIC_MODEL || providerDefaults.default,
|
|
120
|
+
edge: env.ANTHROPIC_MODEL_EDGE || providerDefaults.edge,
|
|
121
|
+
fast: env.ANTHROPIC_MODEL_FAST || providerDefaults.fast,
|
|
122
|
+
cost: env.ANTHROPIC_MODEL_COST || providerDefaults.cost,
|
|
123
|
+
free: env.ANTHROPIC_MODEL_FREE || providerDefaults.free
|
|
124
|
+
};
|
|
111
125
|
} else if (provider === "vertex") {
|
|
112
126
|
apiKey = env.VERTEX_API_KEY;
|
|
127
|
+
baseURL = env.VERTEX_BASE_URL || env.LLM_BASE_URL || void 0;
|
|
113
128
|
const project = env.VERTEX_PROJECT || env.GOOGLE_CLOUD_PROJECT;
|
|
114
129
|
const location = env.VERTEX_LOCATION || env.GOOGLE_CLOUD_LOCATION || "us-central1";
|
|
115
130
|
models = {
|
|
@@ -124,6 +139,7 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
|
|
|
124
139
|
return {
|
|
125
140
|
provider,
|
|
126
141
|
apiKey,
|
|
142
|
+
baseURL,
|
|
127
143
|
project,
|
|
128
144
|
location,
|
|
129
145
|
models,
|
|
@@ -134,6 +150,7 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
|
|
|
134
150
|
return {
|
|
135
151
|
provider,
|
|
136
152
|
apiKey,
|
|
153
|
+
baseURL,
|
|
137
154
|
models,
|
|
138
155
|
temperature: parseFloat(env.DEFAULT_TEMPERATURE || "0.7"),
|
|
139
156
|
maxTokens: parseInt(env.DEFAULT_MAX_TOKENS || "65536")
|
|
@@ -397,7 +414,11 @@ function extractTextAndJson(input) {
|
|
|
397
414
|
var OpenAIProvider = class extends BaseLLMProvider {
|
|
398
415
|
constructor(config) {
|
|
399
416
|
super(config);
|
|
400
|
-
|
|
417
|
+
const clientOptions = { apiKey: config.apiKey };
|
|
418
|
+
if (config.baseURL) {
|
|
419
|
+
clientOptions.baseURL = config.baseURL;
|
|
420
|
+
}
|
|
421
|
+
this.client = new OpenAI(clientOptions);
|
|
401
422
|
this.models = config.models;
|
|
402
423
|
this.defaultModel = config.models.default;
|
|
403
424
|
}
|
|
@@ -579,11 +600,13 @@ var GoogleProvider = class extends BaseLLMProvider {
|
|
|
579
600
|
this.models = config.models;
|
|
580
601
|
this.defaultModel = config.models.default;
|
|
581
602
|
this._pendingOperations = /* @__PURE__ */ new Map();
|
|
603
|
+
const httpOptions = config.baseURL ? { baseUrl: config.baseURL } : void 0;
|
|
582
604
|
if (config.provider === "vertex") {
|
|
583
605
|
if (config.apiKey) {
|
|
584
606
|
this.client = new GoogleGenAI({
|
|
585
607
|
vertexai: true,
|
|
586
|
-
apiKey: config.apiKey
|
|
608
|
+
apiKey: config.apiKey,
|
|
609
|
+
...httpOptions ? { httpOptions } : {}
|
|
587
610
|
});
|
|
588
611
|
} else {
|
|
589
612
|
if (!config.project) {
|
|
@@ -592,12 +615,14 @@ var GoogleProvider = class extends BaseLLMProvider {
|
|
|
592
615
|
this.client = new GoogleGenAI({
|
|
593
616
|
vertexai: true,
|
|
594
617
|
project: config.project,
|
|
595
|
-
location: config.location || "us-central1"
|
|
618
|
+
location: config.location || "us-central1",
|
|
619
|
+
...httpOptions ? { httpOptions } : {}
|
|
596
620
|
});
|
|
597
621
|
}
|
|
598
622
|
} else {
|
|
599
623
|
this.client = new GoogleGenAI({
|
|
600
|
-
apiKey: config.apiKey
|
|
624
|
+
apiKey: config.apiKey,
|
|
625
|
+
...httpOptions ? { httpOptions } : {}
|
|
601
626
|
});
|
|
602
627
|
}
|
|
603
628
|
}
|
|
@@ -1146,14 +1171,66 @@ ${prompt}` : prompt;
|
|
|
1146
1171
|
throw error;
|
|
1147
1172
|
}
|
|
1148
1173
|
}
|
|
1174
|
+
/**
|
|
1175
|
+
* Text-to-Speech synthesis using Gemini TTS models
|
|
1176
|
+
* @param {string} text - Text to synthesize
|
|
1177
|
+
* @param {Object} options - TTS options
|
|
1178
|
+
* @param {string} options.voice - Voice name (default: 'Aoede')
|
|
1179
|
+
* @param {string} options.model - Model to use (default: from config)
|
|
1180
|
+
* @returns {Promise<Buffer>} Audio data as Buffer
|
|
1181
|
+
*/
|
|
1182
|
+
async textToSpeech(text, options = {}) {
|
|
1183
|
+
var _a, _b, _c, _d, _e;
|
|
1184
|
+
const voice = options.voice || "Aoede";
|
|
1185
|
+
const modelName = options.model || this.models.tts || "gemini-3.1-flash-tts-preview";
|
|
1186
|
+
const requestOptions = {
|
|
1187
|
+
model: modelName,
|
|
1188
|
+
contents: [{
|
|
1189
|
+
role: "user",
|
|
1190
|
+
parts: [{ text }]
|
|
1191
|
+
}],
|
|
1192
|
+
config: {
|
|
1193
|
+
responseModalities: ["AUDIO"],
|
|
1194
|
+
speechConfig: {
|
|
1195
|
+
voiceConfig: {
|
|
1196
|
+
prebuiltVoiceConfig: {
|
|
1197
|
+
voiceName: voice
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
};
|
|
1203
|
+
try {
|
|
1204
|
+
const response = await this._generateContent(requestOptions);
|
|
1205
|
+
const candidate = (_a = response.candidates) == null ? void 0 : _a[0];
|
|
1206
|
+
if (!candidate) {
|
|
1207
|
+
throw new LLMServiceException("No candidates returned from TTS model", 500);
|
|
1208
|
+
}
|
|
1209
|
+
const audioPart = (_c = (_b = candidate.content) == null ? void 0 : _b.parts) == null ? void 0 : _c.find((p) => p.inlineData || p.inline_data);
|
|
1210
|
+
const audioB64 = ((_d = audioPart == null ? void 0 : audioPart.inlineData) == null ? void 0 : _d.data) || ((_e = audioPart == null ? void 0 : audioPart.inline_data) == null ? void 0 : _e.data);
|
|
1211
|
+
if (!audioB64) {
|
|
1212
|
+
throw new LLMServiceException("No audio data returned from TTS model", 500);
|
|
1213
|
+
}
|
|
1214
|
+
return Buffer.from(audioB64, "base64");
|
|
1215
|
+
} catch (error) {
|
|
1216
|
+
console.error(`[${this.constructor.name}] textToSpeech failed (API Key: ${this._getMaskedApiKey()}):`, error);
|
|
1217
|
+
throw error;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1149
1220
|
};
|
|
1150
1221
|
|
|
1151
1222
|
// src/llm/providers/anthropic-provider.js
|
|
1223
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
1152
1224
|
var AnthropicProvider = class extends BaseLLMProvider {
|
|
1153
1225
|
constructor(config) {
|
|
1154
1226
|
super(config);
|
|
1155
1227
|
this.models = config.models || {};
|
|
1156
1228
|
this.defaultModel = this.models.default || "claude-3-5-sonnet-20241022";
|
|
1229
|
+
const clientOptions = { apiKey: config.apiKey || "" };
|
|
1230
|
+
if (config.baseURL) {
|
|
1231
|
+
clientOptions.baseURL = config.baseURL;
|
|
1232
|
+
}
|
|
1233
|
+
this.client = new Anthropic(clientOptions);
|
|
1157
1234
|
}
|
|
1158
1235
|
async chat(userMessage, systemPrompt = "", options = {}) {
|
|
1159
1236
|
var _a;
|
|
@@ -1190,82 +1267,69 @@ var AnthropicProvider = class extends BaseLLMProvider {
|
|
|
1190
1267
|
);
|
|
1191
1268
|
}
|
|
1192
1269
|
async _chatCompletionWithModel(messages, systemPrompt, tools, modelName, maxTokens, temperature, options = {}) {
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
const formattedMessages = messages.map((m) => ({
|
|
1196
|
-
role: m.role === "assistant" ? "assistant" : "user",
|
|
1197
|
-
content: m.content
|
|
1198
|
-
}));
|
|
1199
|
-
const headers = {
|
|
1200
|
-
"Content-Type": "application/json",
|
|
1201
|
-
"x-api-key": this.config.apiKey || "",
|
|
1202
|
-
"anthropic-version": "2023-06-01"
|
|
1203
|
-
};
|
|
1204
|
-
const payload = {
|
|
1205
|
-
model: modelName,
|
|
1206
|
-
messages: formattedMessages,
|
|
1207
|
-
max_tokens: maxTokens,
|
|
1208
|
-
temperature
|
|
1209
|
-
};
|
|
1210
|
-
if (systemPrompt) {
|
|
1211
|
-
payload.system = systemPrompt;
|
|
1212
|
-
}
|
|
1270
|
+
var _a, _b, _c, _d;
|
|
1271
|
+
let effectiveSystem = systemPrompt || void 0;
|
|
1213
1272
|
if (options.responseFormat) {
|
|
1214
1273
|
const formatType = typeof options.responseFormat === "string" ? options.responseFormat : options.responseFormat.type;
|
|
1215
1274
|
if (formatType === "json" || formatType === "json_schema") {
|
|
1216
|
-
const
|
|
1275
|
+
const schema = options.responseFormat.schema || options.responseSchema;
|
|
1276
|
+
const schemaText = schema ? `
|
|
1217
1277
|
|
|
1218
|
-
Your output must comply strictly with this JSON Schema: ${JSON.stringify(
|
|
1278
|
+
Your output must comply strictly with this JSON Schema: ${JSON.stringify(schema)}` : "";
|
|
1219
1279
|
const jsonPrompt = `IMPORTANT: You must respond ONLY in raw JSON format. Do not write any conversational text, explanations, or markdown code blocks (e.g. do NOT wrap your answer in \`\`\`json ... \`\`\`). Your response must start with '{' and end with '}'.${schemaText}`;
|
|
1220
|
-
|
|
1221
|
-
payload.system = `${payload.system}
|
|
1280
|
+
effectiveSystem = effectiveSystem ? `${effectiveSystem}
|
|
1222
1281
|
|
|
1223
|
-
${jsonPrompt}
|
|
1224
|
-
} else {
|
|
1225
|
-
payload.system = jsonPrompt;
|
|
1226
|
-
}
|
|
1282
|
+
${jsonPrompt}` : jsonPrompt;
|
|
1227
1283
|
}
|
|
1228
1284
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1285
|
+
const anthropicMessages = this._formatMessages(messages);
|
|
1286
|
+
const anthropicTools = tools ? this._formatTools(tools) : void 0;
|
|
1287
|
+
const requestParams = {
|
|
1288
|
+
model: modelName,
|
|
1289
|
+
max_tokens: maxTokens,
|
|
1290
|
+
temperature,
|
|
1291
|
+
messages: anthropicMessages,
|
|
1292
|
+
...effectiveSystem ? { system: effectiveSystem } : {},
|
|
1293
|
+
...anthropicTools ? { tools: anthropicTools } : {}
|
|
1294
|
+
};
|
|
1295
|
+
let response;
|
|
1231
1296
|
try {
|
|
1232
|
-
|
|
1233
|
-
method: "POST",
|
|
1234
|
-
headers,
|
|
1235
|
-
body: JSON.stringify(payload)
|
|
1236
|
-
});
|
|
1237
|
-
responseText = await res.text();
|
|
1238
|
-
if (!res.ok) {
|
|
1239
|
-
console.error(`[AnthropicProvider] API request failed with status ${res.status}:`, responseText);
|
|
1240
|
-
throw new LLMServiceException(`Anthropic API Error: ${responseText}`, res.status);
|
|
1241
|
-
}
|
|
1242
|
-
responseData = JSON.parse(responseText);
|
|
1297
|
+
response = await this.client.messages.create(requestParams);
|
|
1243
1298
|
} catch (error) {
|
|
1244
1299
|
console.error(`[AnthropicProvider] request failed (API Key: ${this._getMaskedApiKey()}):`, error);
|
|
1245
1300
|
throw error;
|
|
1246
1301
|
}
|
|
1247
|
-
const
|
|
1248
|
-
const
|
|
1249
|
-
|
|
1250
|
-
|
|
1302
|
+
const textBlocks = response.content.filter((b) => b.type === "text");
|
|
1303
|
+
const toolUseBlocks = response.content.filter((b) => b.type === "tool_use");
|
|
1304
|
+
const contentText = textBlocks.map((b) => b.text).join("");
|
|
1305
|
+
if (!contentText && toolUseBlocks.length === 0) {
|
|
1306
|
+
console.error("[AnthropicProvider] Model returned empty response (no text, no tool calls)");
|
|
1251
1307
|
throw new LLMServiceException(
|
|
1252
1308
|
"Model returned empty response. This usually means the prompt or schema is confusing the model.",
|
|
1253
1309
|
500
|
|
1254
1310
|
);
|
|
1255
1311
|
}
|
|
1256
|
-
const
|
|
1312
|
+
const toolCalls = toolUseBlocks.length > 0 ? toolUseBlocks.map((block) => ({
|
|
1313
|
+
id: block.id,
|
|
1314
|
+
type: "function",
|
|
1315
|
+
function: {
|
|
1316
|
+
name: block.name,
|
|
1317
|
+
// Anthropic returns args as a parsed object; serialize to string to match OpenAI shape
|
|
1318
|
+
arguments: JSON.stringify(block.input || {})
|
|
1319
|
+
}
|
|
1320
|
+
})) : null;
|
|
1321
|
+
const rawFinishReason = response.stop_reason;
|
|
1257
1322
|
const normalizedFinishReason = this.normalizeFinishReason(rawFinishReason);
|
|
1258
1323
|
const result = {
|
|
1259
1324
|
content: contentText,
|
|
1260
|
-
tool_calls:
|
|
1261
|
-
// REST client tool calls mapping not required for playbook studio
|
|
1325
|
+
tool_calls: toolCalls,
|
|
1262
1326
|
finishReason: normalizedFinishReason,
|
|
1263
1327
|
_rawFinishReason: rawFinishReason,
|
|
1264
1328
|
_responseFormat: options.responseFormat,
|
|
1265
1329
|
usage: {
|
|
1266
|
-
prompt_tokens:
|
|
1267
|
-
completion_tokens:
|
|
1268
|
-
total_tokens:
|
|
1330
|
+
prompt_tokens: ((_a = response.usage) == null ? void 0 : _a.input_tokens) || 0,
|
|
1331
|
+
completion_tokens: ((_b = response.usage) == null ? void 0 : _b.output_tokens) || 0,
|
|
1332
|
+
total_tokens: (((_c = response.usage) == null ? void 0 : _c.input_tokens) || 0) + (((_d = response.usage) == null ? void 0 : _d.output_tokens) || 0)
|
|
1269
1333
|
},
|
|
1270
1334
|
model: modelName
|
|
1271
1335
|
};
|
|
@@ -1274,11 +1338,110 @@ ${jsonPrompt}`;
|
|
|
1274
1338
|
}
|
|
1275
1339
|
return result;
|
|
1276
1340
|
}
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1341
|
+
/**
|
|
1342
|
+
* Convert standard {role, content, tool_calls, tool_call_id} messages
|
|
1343
|
+
* to Anthropic's message format.
|
|
1344
|
+
*
|
|
1345
|
+
* Key differences from OpenAI:
|
|
1346
|
+
* - No 'system' role in messages array (handled via top-level `system` param)
|
|
1347
|
+
* - Tool results go as role:'user' with content type:'tool_result'
|
|
1348
|
+
* - Assistant messages with tool calls use an array of content blocks
|
|
1349
|
+
* - Roles must strictly alternate user/assistant
|
|
1350
|
+
*/
|
|
1351
|
+
_formatMessages(messages) {
|
|
1352
|
+
const result = [];
|
|
1353
|
+
for (const msg of messages) {
|
|
1354
|
+
if (msg.role === "system") {
|
|
1355
|
+
continue;
|
|
1356
|
+
}
|
|
1357
|
+
if (msg.role === "tool") {
|
|
1358
|
+
const toolResultBlock = {
|
|
1359
|
+
type: "tool_result",
|
|
1360
|
+
tool_use_id: msg.tool_call_id,
|
|
1361
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
|
|
1362
|
+
};
|
|
1363
|
+
const last = result[result.length - 1];
|
|
1364
|
+
if (last && last.role === "user" && Array.isArray(last.content)) {
|
|
1365
|
+
last.content.push(toolResultBlock);
|
|
1366
|
+
} else {
|
|
1367
|
+
result.push({ role: "user", content: [toolResultBlock] });
|
|
1368
|
+
}
|
|
1369
|
+
continue;
|
|
1370
|
+
}
|
|
1371
|
+
if (msg.role === "assistant") {
|
|
1372
|
+
if (msg.tool_calls && msg.tool_calls.length > 0) {
|
|
1373
|
+
const contentBlocks = [];
|
|
1374
|
+
if (msg.content) {
|
|
1375
|
+
contentBlocks.push({ type: "text", text: msg.content });
|
|
1376
|
+
}
|
|
1377
|
+
for (const tc of msg.tool_calls) {
|
|
1378
|
+
let inputArgs = tc.function.arguments;
|
|
1379
|
+
if (typeof inputArgs === "string") {
|
|
1380
|
+
try {
|
|
1381
|
+
inputArgs = JSON.parse(inputArgs);
|
|
1382
|
+
} catch (_) {
|
|
1383
|
+
inputArgs = {};
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
contentBlocks.push({
|
|
1387
|
+
type: "tool_use",
|
|
1388
|
+
id: tc.id,
|
|
1389
|
+
name: tc.function.name,
|
|
1390
|
+
input: inputArgs
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
result.push({ role: "assistant", content: contentBlocks });
|
|
1394
|
+
} else {
|
|
1395
|
+
result.push({ role: "assistant", content: msg.content || "" });
|
|
1396
|
+
}
|
|
1397
|
+
continue;
|
|
1398
|
+
}
|
|
1399
|
+
result.push({ role: "user", content: msg.content || "" });
|
|
1280
1400
|
}
|
|
1281
|
-
return
|
|
1401
|
+
return result;
|
|
1402
|
+
}
|
|
1403
|
+
/**
|
|
1404
|
+
* Convert OpenAI-style tool definitions to Anthropic format.
|
|
1405
|
+
* OpenAI: { type: 'function', function: { name, description, parameters } }
|
|
1406
|
+
* Anthropic: { name, description, input_schema }
|
|
1407
|
+
*/
|
|
1408
|
+
_formatTools(tools) {
|
|
1409
|
+
return tools.map((tool) => {
|
|
1410
|
+
const fn = tool.function || tool;
|
|
1411
|
+
return {
|
|
1412
|
+
name: fn.name,
|
|
1413
|
+
description: fn.description || "",
|
|
1414
|
+
input_schema: fn.parameters || fn.input_schema || { type: "object", properties: {} }
|
|
1415
|
+
};
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
async executeTools(tool_calls, messages, tenantId, toolImplementations, env) {
|
|
1419
|
+
const toolResults = await Promise.all(
|
|
1420
|
+
tool_calls.map(async (toolCall) => {
|
|
1421
|
+
const toolName = toolCall.function.name;
|
|
1422
|
+
const tool = toolImplementations[toolName];
|
|
1423
|
+
console.log(`[Tool Call] ${toolName} with arguments:`, toolCall.function.arguments);
|
|
1424
|
+
if (!tool) {
|
|
1425
|
+
console.error(`[Tool Error] Tool '${toolName}' not found`);
|
|
1426
|
+
return { tool_call_id: toolCall.id, output: JSON.stringify({ error: `Tool '${toolName}' not found.` }) };
|
|
1427
|
+
}
|
|
1428
|
+
try {
|
|
1429
|
+
const output = await tool(toolCall.function.arguments, { env, tenantId });
|
|
1430
|
+
const preview = typeof output === "string" ? output.length > 200 ? output.substring(0, 200) + "..." : output : JSON.stringify(output).substring(0, 200) + "...";
|
|
1431
|
+
console.log(`[Tool Result] ${toolName} returned:`, preview);
|
|
1432
|
+
return { tool_call_id: toolCall.id, output };
|
|
1433
|
+
} catch (error) {
|
|
1434
|
+
console.error(`[Tool Error] ${toolName} failed:`, error.message);
|
|
1435
|
+
return { tool_call_id: toolCall.id, output: JSON.stringify({ error: `Error executing tool '${toolName}': ${error.message}` }) };
|
|
1436
|
+
}
|
|
1437
|
+
})
|
|
1438
|
+
);
|
|
1439
|
+
toolResults.forEach(
|
|
1440
|
+
(result) => messages.push({ role: "tool", tool_call_id: result.tool_call_id, content: result.output })
|
|
1441
|
+
);
|
|
1442
|
+
}
|
|
1443
|
+
_getModelForTier(tier) {
|
|
1444
|
+
return this.models[tier] || this.defaultModel;
|
|
1282
1445
|
}
|
|
1283
1446
|
_shouldAutoParse(options) {
|
|
1284
1447
|
if (!options.responseFormat) return false;
|
|
@@ -1299,15 +1462,10 @@ ${jsonPrompt}`;
|
|
|
1299
1462
|
}
|
|
1300
1463
|
normalizeFinishReason(providerReason) {
|
|
1301
1464
|
const lower = (providerReason || "").toLowerCase();
|
|
1302
|
-
if (lower === "end_turn")
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
if (lower === "
|
|
1306
|
-
return "truncated";
|
|
1307
|
-
}
|
|
1308
|
-
if (lower === "stop_sequence") {
|
|
1309
|
-
return "completed";
|
|
1310
|
-
}
|
|
1465
|
+
if (lower === "end_turn") return "completed";
|
|
1466
|
+
if (lower === "max_tokens") return "truncated";
|
|
1467
|
+
if (lower === "stop_sequence") return "completed";
|
|
1468
|
+
if (lower === "tool_use") return "tool_call";
|
|
1311
1469
|
return super.normalizeFinishReason(providerReason);
|
|
1312
1470
|
}
|
|
1313
1471
|
};
|