@contentgrowth/llm-service 1.2.2 → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -31,6 +31,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
31
31
  // src/index.js
32
32
  var index_exports = {};
33
33
  __export(index_exports, {
34
+ AnthropicProvider: () => AnthropicProvider,
34
35
  BaseConfigProvider: () => BaseConfigProvider,
35
36
  ConfigManager: () => ConfigManager,
36
37
  DefaultConfigProvider: () => DefaultConfigProvider,
@@ -194,35 +195,36 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
194
195
  // src/llm/config-manager.js
195
196
  var MODEL_CONFIGS = {
196
197
  openai: {
197
- default: "gpt-4o",
198
- edge: "gpt-4o",
199
- fast: "gpt-4o-mini",
200
- cost: "gpt-4o-mini",
201
- free: "gpt-4o-mini"
198
+ default: "gpt-5.5-instant",
199
+ edge: "gpt-5.5-thinking",
200
+ fast: "gpt-5.5-instant",
201
+ cost: "gpt-5.5-instant",
202
+ free: "gpt-5.5-instant"
202
203
  },
203
204
  gemini: {
204
- default: "gemini-3-flash-preview",
205
- // 'gemini-2.5-flash',
206
- edge: "gemini-3-pro-preview",
207
- // 'gemini-2.5-pro',
208
- fast: "gemini-3-flash-preview",
209
- // 'gemini-2.5-flash-lite',
210
- cost: "gemini-3-flash-preview",
211
- // 'gemini-2.5-flash-lite',
212
- free: "gemini-3-flash-preview",
213
- // 'gemini-2.0-flash-lite',
205
+ default: "gemini-3.5-flash",
206
+ edge: "gemini-3.5-pro",
207
+ fast: "gemini-3.5-flash",
208
+ cost: "gemini-3.5-flash",
209
+ free: "gemini-3.5-flash",
214
210
  video: "veo",
215
- image: "gemini-3-pro-image-preview"
216
- // Default image generation model
211
+ image: "gemini-3.5-pro-image-preview"
217
212
  },
218
213
  vertex: {
219
- default: "gemini-3-flash-preview",
220
- edge: "gemini-3-pro-preview",
221
- fast: "gemini-3-flash-preview",
222
- cost: "gemini-3-flash-preview",
223
- free: "gemini-3-flash-preview",
214
+ default: "gemini-3.5-flash",
215
+ edge: "gemini-3.5-pro",
216
+ fast: "gemini-3.5-flash",
217
+ cost: "gemini-3.5-flash",
218
+ free: "gemini-3.5-flash",
224
219
  video: "veo",
225
- image: "gemini-3-pro-image-preview"
220
+ image: "gemini-3.5-pro-image-preview"
221
+ },
222
+ anthropic: {
223
+ default: "claude-4.6-sonnet",
224
+ edge: "claude-4.8-opus",
225
+ fast: "claude-4.5-haiku",
226
+ cost: "claude-4.5-haiku",
227
+ free: "claude-4.5-haiku"
226
228
  }
227
229
  };
228
230
  var ConfigManager = class {
@@ -775,8 +777,20 @@ ${msg.content}`;
775
777
  const isLastAssistantMessage = index === geminiMessages.map((m, i) => m.role === "assistant" ? i : -1).filter((i) => i >= 0).pop();
776
778
  if (msg.tool_calls) {
777
779
  parts2 = msg.tool_calls.map((tc) => {
780
+ let args = tc.function.args;
781
+ if (tc.function.arguments) {
782
+ if (typeof tc.function.arguments === "string") {
783
+ try {
784
+ args = JSON.parse(tc.function.arguments);
785
+ } catch (e) {
786
+ args = {};
787
+ }
788
+ } else {
789
+ args = tc.function.arguments;
790
+ }
791
+ }
778
792
  const part = {
779
- functionCall: { name: tc.function.name, args: tc.function.arguments || tc.function.args }
793
+ functionCall: { name: tc.function.name, args }
780
794
  };
781
795
  if (tc.thought_signature) {
782
796
  part.thoughtSignature = tc.thought_signature;
@@ -861,7 +875,10 @@ ${msg.content}`;
861
875
  thought_signature: responseThoughtSignature,
862
876
  tool_calls: toolCalls ? (Array.isArray(toolCalls) ? toolCalls : [toolCalls]).map((fc) => ({
863
877
  type: "function",
864
- function: fc,
878
+ function: {
879
+ name: fc.name,
880
+ arguments: JSON.stringify(fc.args || {})
881
+ },
865
882
  thought_signature: fc.thought_signature
866
883
  })) : null,
867
884
  finishReason: normalizedFinishReason,
@@ -1182,6 +1199,170 @@ ${prompt}` : prompt;
1182
1199
  }
1183
1200
  };
1184
1201
 
1202
+ // src/llm/providers/anthropic-provider.js
1203
+ var AnthropicProvider = class extends BaseLLMProvider {
1204
+ constructor(config) {
1205
+ super(config);
1206
+ this.models = config.models || {};
1207
+ this.defaultModel = this.models.default || "claude-3-5-sonnet-20241022";
1208
+ }
1209
+ async chat(userMessage, systemPrompt = "", options = {}) {
1210
+ var _a;
1211
+ const messages = [{ role: "user", content: userMessage }];
1212
+ const tier = options.tier || "default";
1213
+ const effectiveModel = this._getModelForTier(tier);
1214
+ const effectiveMaxTokens = options.maxTokens || this.config.maxTokens || 4096;
1215
+ const effectiveTemperature = options.temperature !== void 0 ? options.temperature : (_a = this.config.temperature) != null ? _a : 0.7;
1216
+ const response = await this._chatCompletionWithModel(
1217
+ messages,
1218
+ systemPrompt,
1219
+ null,
1220
+ effectiveModel,
1221
+ effectiveMaxTokens,
1222
+ effectiveTemperature,
1223
+ options
1224
+ );
1225
+ return { text: response.content };
1226
+ }
1227
+ async chatCompletion(messages, systemPrompt, tools = null, options = {}) {
1228
+ var _a;
1229
+ const tier = options.tier || "default";
1230
+ const effectiveModel = this._getModelForTier(tier);
1231
+ const effectiveMaxTokens = options.maxTokens || this.config.maxTokens || 4096;
1232
+ const effectiveTemperature = options.temperature !== void 0 ? options.temperature : (_a = this.config.temperature) != null ? _a : 0.7;
1233
+ return this._chatCompletionWithModel(
1234
+ messages,
1235
+ systemPrompt,
1236
+ tools,
1237
+ effectiveModel,
1238
+ effectiveMaxTokens,
1239
+ effectiveTemperature,
1240
+ options
1241
+ );
1242
+ }
1243
+ async _chatCompletionWithModel(messages, systemPrompt, tools, modelName, maxTokens, temperature, options = {}) {
1244
+ const baseURL = this.config.baseURL || "https://api.anthropic.com";
1245
+ const url = `${baseURL.replace(/\/$/, "")}/v1/messages`;
1246
+ const formattedMessages = messages.map((m) => ({
1247
+ role: m.role === "assistant" ? "assistant" : "user",
1248
+ content: m.content
1249
+ }));
1250
+ const headers = {
1251
+ "Content-Type": "application/json",
1252
+ "x-api-key": this.config.apiKey || "",
1253
+ "anthropic-version": "2023-06-01"
1254
+ };
1255
+ const payload = {
1256
+ model: modelName,
1257
+ messages: formattedMessages,
1258
+ max_tokens: maxTokens,
1259
+ temperature
1260
+ };
1261
+ if (systemPrompt) {
1262
+ payload.system = systemPrompt;
1263
+ }
1264
+ if (options.responseFormat) {
1265
+ const formatType = typeof options.responseFormat === "string" ? options.responseFormat : options.responseFormat.type;
1266
+ if (formatType === "json" || formatType === "json_schema") {
1267
+ const schemaText = options.responseFormat.schema || options.responseSchema ? `
1268
+
1269
+ Your output must comply strictly with this JSON Schema: ${JSON.stringify(options.responseFormat.schema || options.responseSchema)}` : "";
1270
+ 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}`;
1271
+ if (payload.system) {
1272
+ payload.system = `${payload.system}
1273
+
1274
+ ${jsonPrompt}`;
1275
+ } else {
1276
+ payload.system = jsonPrompt;
1277
+ }
1278
+ }
1279
+ }
1280
+ let responseText;
1281
+ let responseData;
1282
+ try {
1283
+ const res = await fetch(url, {
1284
+ method: "POST",
1285
+ headers,
1286
+ body: JSON.stringify(payload)
1287
+ });
1288
+ responseText = await res.text();
1289
+ if (!res.ok) {
1290
+ console.error(`[AnthropicProvider] API request failed with status ${res.status}:`, responseText);
1291
+ throw new LLMServiceException(`Anthropic API Error: ${responseText}`, res.status);
1292
+ }
1293
+ responseData = JSON.parse(responseText);
1294
+ } catch (error) {
1295
+ console.error(`[AnthropicProvider] request failed (API Key: ${this._getMaskedApiKey()}):`, error);
1296
+ throw error;
1297
+ }
1298
+ const contentBlock = responseData.content && responseData.content[0];
1299
+ const contentText = contentBlock ? contentBlock.text : "";
1300
+ if (!contentText) {
1301
+ console.error("[AnthropicProvider] Model returned empty response content");
1302
+ throw new LLMServiceException(
1303
+ "Model returned empty response. This usually means the prompt or schema is confusing the model.",
1304
+ 500
1305
+ );
1306
+ }
1307
+ const rawFinishReason = responseData.stop_reason;
1308
+ const normalizedFinishReason = this.normalizeFinishReason(rawFinishReason);
1309
+ const result = {
1310
+ content: contentText,
1311
+ tool_calls: null,
1312
+ // REST client tool calls mapping not required for playbook studio
1313
+ finishReason: normalizedFinishReason,
1314
+ _rawFinishReason: rawFinishReason,
1315
+ _responseFormat: options.responseFormat,
1316
+ usage: {
1317
+ prompt_tokens: responseData.usage ? responseData.usage.input_tokens : 0,
1318
+ completion_tokens: responseData.usage ? responseData.usage.output_tokens : 0,
1319
+ total_tokens: responseData.usage ? responseData.usage.input_tokens + responseData.usage.output_tokens : 0
1320
+ },
1321
+ model: modelName
1322
+ };
1323
+ if (options.responseFormat && this._shouldAutoParse(options)) {
1324
+ result.parsedContent = this._safeJsonParse(contentText);
1325
+ }
1326
+ return result;
1327
+ }
1328
+ _getModelForTier(tier) {
1329
+ if (this.models[tier]) {
1330
+ return this.models[tier];
1331
+ }
1332
+ return this.defaultModel;
1333
+ }
1334
+ _shouldAutoParse(options) {
1335
+ if (!options.responseFormat) return false;
1336
+ const formatType = typeof options.responseFormat === "string" ? options.responseFormat : options.responseFormat.type;
1337
+ return formatType === "json" || formatType === "json_schema";
1338
+ }
1339
+ _safeJsonParse(text) {
1340
+ try {
1341
+ return extractJsonFromResponse(text);
1342
+ } catch (error) {
1343
+ console.error("[AnthropicProvider] Failed to extract JSON from response:", text, error);
1344
+ try {
1345
+ return JSON.parse(text);
1346
+ } catch (_) {
1347
+ return null;
1348
+ }
1349
+ }
1350
+ }
1351
+ normalizeFinishReason(providerReason) {
1352
+ const lower = (providerReason || "").toLowerCase();
1353
+ if (lower === "end_turn") {
1354
+ return "completed";
1355
+ }
1356
+ if (lower === "max_tokens") {
1357
+ return "truncated";
1358
+ }
1359
+ if (lower === "stop_sequence") {
1360
+ return "completed";
1361
+ }
1362
+ return super.normalizeFinishReason(providerReason);
1363
+ }
1364
+ };
1365
+
1185
1366
  // src/llm-service.js
1186
1367
  var LLMService = class {
1187
1368
  constructor(env, toolImplementations = {}) {
@@ -1203,6 +1384,8 @@ var LLMService = class {
1203
1384
  provider = new OpenAIProvider(config);
1204
1385
  } else if (config.provider === "gemini" || config.provider === "vertex") {
1205
1386
  provider = new GoogleProvider(config);
1387
+ } else if (config.provider === "anthropic") {
1388
+ provider = new AnthropicProvider(config);
1206
1389
  } else {
1207
1390
  throw new LLMServiceException(`Unsupported LLM provider: ${config.provider}`, 500);
1208
1391
  }
@@ -1771,6 +1954,7 @@ function createSpeechHandler(app, getConfig) {
1771
1954
  }
1772
1955
  // Annotate the CommonJS export names for ESM import in node:
1773
1956
  0 && (module.exports = {
1957
+ AnthropicProvider,
1774
1958
  BaseConfigProvider,
1775
1959
  ConfigManager,
1776
1960
  DefaultConfigProvider,