@contentgrowth/llm-service 1.3.2 → 1.3.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
@@ -38,6 +38,7 @@ __export(index_exports, {
38
38
  FINISH_REASONS: () => FINISH_REASONS,
39
39
  GeminiProvider: () => GoogleProvider,
40
40
  GoogleProvider: () => GoogleProvider,
41
+ LEGACY_MODEL_ALIASES: () => LEGACY_MODEL_ALIASES,
41
42
  LLMService: () => LLMService,
42
43
  LLMServiceException: () => LLMServiceException,
43
44
  MODEL_CONFIGS: () => MODEL_CONFIGS,
@@ -49,6 +50,7 @@ __export(index_exports, {
49
50
  extractJsonFromResponse: () => extractJsonFromResponse,
50
51
  extractTextAndJson: () => extractTextAndJson,
51
52
  handleApiError: () => handleApiError,
53
+ normalizeModelId: () => normalizeModelId,
52
54
  sanitizeError: () => sanitizeError
53
55
  });
54
56
  module.exports = __toCommonJS(index_exports);
@@ -119,10 +121,27 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
119
121
  return null;
120
122
  }
121
123
  }
124
+ /**
125
+ * Resolve the model map for a tenant.
126
+ * Precedence (highest first):
127
+ * 1. tenantConfig.model - single model chosen by the tenant (becomes `default`)
128
+ * 2. tenantConfig.models[tier] - per-tier overrides supplied by the tenant
129
+ * 3. MODEL_CONFIGS[provider] - library defaults
130
+ */
131
+ _resolveTenantModels(tenantConfig) {
132
+ const models = {
133
+ ...MODEL_CONFIGS[tenantConfig.provider] || {},
134
+ ...tenantConfig.models || {}
135
+ };
136
+ if (typeof tenantConfig.model === "string" && tenantConfig.model.trim()) {
137
+ models.default = tenantConfig.model;
138
+ }
139
+ return normalizeModelMap(models);
140
+ }
122
141
  _buildTenantConfig(tenantConfig, env) {
123
142
  return {
124
143
  provider: tenantConfig.provider,
125
- models: MODEL_CONFIGS[tenantConfig.provider],
144
+ models: this._resolveTenantModels(tenantConfig),
126
145
  // for backward compatibility, api_key is deprecated and shall never be used!!
127
146
  apiKey: tenantConfig.apiKey || tenantConfig.api_key,
128
147
  baseURL: tenantConfig.baseURL || tenantConfig.base_url || void 0,
@@ -211,39 +230,70 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
211
230
 
212
231
  // src/llm/config-manager.js
213
232
  var MODEL_CONFIGS = {
233
+ // NOTE: these must be OpenAI *API* model IDs. ChatGPT product names such as
234
+ // "GPT-5.5 Instant" / "GPT-5.5 Thinking" are not valid API models and return 404.
214
235
  openai: {
215
- default: "gpt-5.5-instant",
216
- edge: "gpt-5.5-thinking",
217
- fast: "gpt-5.5-instant",
218
- cost: "gpt-5.5-instant",
219
- free: "gpt-5.5-instant"
236
+ default: "gpt-5.6-luna",
237
+ edge: "gpt-5.6-sol",
238
+ fast: "gpt-5.6-luna",
239
+ cost: "gpt-5.6-luna",
240
+ free: "gpt-5.6-luna"
220
241
  },
242
+ // Gemini Developer API (ai.google.dev). There is no "gemini-3.5-pro"; the Pro tier is gemini-3.1-pro-preview.
221
243
  gemini: {
222
- default: "gemini-3.5-flash",
223
- edge: "gemini-3.5-pro",
224
- fast: "gemini-3.5-flash",
225
- cost: "gemini-3.5-flash",
226
- free: "gemini-3.5-flash",
244
+ default: "gemini-3.8-flash",
245
+ edge: "gemini-3.1-pro-preview",
246
+ fast: "gemini-3.8-flash",
247
+ cost: "gemini-3.5-flash-lite",
248
+ free: "gemini-3.5-flash-lite",
227
249
  video: "veo",
228
- image: "gemini-3.5-pro-image-preview"
250
+ image: "gemini-3-pro-image"
229
251
  },
252
+ // Vertex AI / Gemini Enterprise Agent Platform
230
253
  vertex: {
231
- default: "gemini-3.5-flash",
232
- edge: "gemini-3.5-pro",
233
- fast: "gemini-3.5-flash",
234
- cost: "gemini-3.5-flash",
235
- free: "gemini-3.5-flash",
254
+ default: "gemini-3.7-flash",
255
+ edge: "gemini-3.1-pro-preview",
256
+ fast: "gemini-3.7-flash",
257
+ cost: "gemini-3.5-flash-lite",
258
+ free: "gemini-3.5-flash-lite",
236
259
  video: "veo",
237
- image: "gemini-3.5-pro-image-preview"
260
+ image: "gemini-3.1-flash-image"
238
261
  },
262
+ // Anthropic IDs are "claude-<family>-<major>-<minor>", e.g. claude-sonnet-5-5 (not claude-5.5-sonnet).
239
263
  anthropic: {
240
- default: "claude-4.6-sonnet",
241
- edge: "claude-4.8-opus",
242
- fast: "claude-4.5-haiku",
243
- cost: "claude-4.5-haiku",
244
- free: "claude-4.5-haiku"
264
+ default: "claude-sonnet-5-5",
265
+ edge: "claude-opus-5-5",
266
+ fast: "claude-haiku-5-5",
267
+ cost: "claude-haiku-5-5",
268
+ free: "claude-haiku-5-5"
245
269
  }
246
270
  };
271
+ var LEGACY_MODEL_ALIASES = {
272
+ // OpenAI: ChatGPT product names, not API model IDs
273
+ "gpt-5.5-instant": "gpt-5.6-luna",
274
+ "gpt-5.5-thinking": "gpt-5.6-sol",
275
+ // Gemini: never existed
276
+ "gemini-3.5-pro": "gemini-3.1-pro-preview",
277
+ "gemini-3.7-pro": "gemini-3.1-pro-preview",
278
+ "gemini-3.5-pro-image-preview": "gemini-3-pro-image",
279
+ "gemini-3.7-pro-image-preview": "gemini-3-pro-image",
280
+ // Anthropic: wrong segment order
281
+ "claude-4.5-haiku": "claude-haiku-4-5",
282
+ "claude-4.6-sonnet": "claude-sonnet-4-6",
283
+ "claude-4.8-opus": "claude-opus-4-8"
284
+ };
285
+ function normalizeModelId(model) {
286
+ if (typeof model !== "string") return model;
287
+ const trimmed = model.trim();
288
+ return LEGACY_MODEL_ALIASES[trimmed] || trimmed;
289
+ }
290
+ function normalizeModelMap(models = {}) {
291
+ const out = {};
292
+ for (const [tier, id] of Object.entries(models)) {
293
+ out[tier] = normalizeModelId(id);
294
+ }
295
+ return out;
296
+ }
247
297
  var ConfigManager = class {
248
298
  /**
249
299
  * Set a custom configuration provider.
@@ -471,6 +521,7 @@ function extractTextAndJson(input) {
471
521
  }
472
522
 
473
523
  // src/llm/providers/openai-provider.js
524
+ var REASONING_MODEL_PATTERN = /^(o\d|gpt-5|gpt-6)/i;
474
525
  var OpenAIProvider = class extends BaseLLMProvider {
475
526
  constructor(config) {
476
527
  super(config);
@@ -479,13 +530,12 @@ var OpenAIProvider = class extends BaseLLMProvider {
479
530
  clientOptions.baseURL = config.baseURL;
480
531
  }
481
532
  this.client = new import_openai.default(clientOptions);
482
- this.models = config.models;
483
- this.defaultModel = config.models.default;
533
+ this.models = normalizeModelMap(config.models);
534
+ this.defaultModel = this.models.default;
484
535
  }
485
536
  async chat(userMessage, systemPrompt = "", options = {}) {
486
537
  const messages = [{ role: "user", content: userMessage }];
487
- const tier = options.tier || "default";
488
- const effectiveModel = this._getModelForTier(tier);
538
+ const effectiveModel = this._resolveModel(options);
489
539
  const effectiveMaxTokens = options.maxTokens || this.config.maxTokens;
490
540
  const effectiveTemperature = options.temperature !== void 0 ? options.temperature : this.config.temperature;
491
541
  const response = await this._chatCompletionWithModel(
@@ -503,22 +553,42 @@ var OpenAIProvider = class extends BaseLLMProvider {
503
553
  messages,
504
554
  systemPrompt,
505
555
  tools,
506
- this.defaultModel,
556
+ this._resolveModel(options),
507
557
  this.config.maxTokens,
508
558
  this.config.temperature,
509
559
  options
510
560
  );
511
561
  }
562
+ /**
563
+ * Per-call model resolution: explicit options.model > options.tier > default
564
+ */
565
+ _resolveModel(options = {}) {
566
+ return normalizeModelId(options.model) || this._getModelForTier(options.tier || "default");
567
+ }
568
+ _isReasoningModel(modelName) {
569
+ return REASONING_MODEL_PATTERN.test(modelName || "");
570
+ }
512
571
  async _chatCompletionWithModel(messages, systemPrompt, tools, modelName, maxTokens, temperature, options = {}) {
513
572
  var _a, _b, _c, _d, _e;
573
+ const effectiveMaxTokens = (_a = options.maxTokens) != null ? _a : maxTokens;
514
574
  const requestPayload = {
515
575
  model: modelName,
516
- temperature: (_a = options.temperature) != null ? _a : temperature,
517
- max_tokens: (_b = options.maxTokens) != null ? _b : maxTokens,
518
576
  messages: [{ role: "system", content: systemPrompt }, ...messages],
519
577
  tools,
520
578
  tool_choice: tools ? "auto" : void 0
521
579
  };
580
+ if (this._isReasoningModel(modelName)) {
581
+ requestPayload.max_completion_tokens = effectiveMaxTokens;
582
+ if (options.reasoningEffort) {
583
+ requestPayload.reasoning_effort = options.reasoningEffort;
584
+ }
585
+ if (options.reasoningEffort === "none" && options.temperature !== void 0) {
586
+ requestPayload.temperature = options.temperature;
587
+ }
588
+ } else {
589
+ requestPayload.max_tokens = effectiveMaxTokens;
590
+ requestPayload.temperature = (_b = options.temperature) != null ? _b : temperature;
591
+ }
522
592
  if (options.responseFormat) {
523
593
  requestPayload.response_format = this._buildResponseFormat(options);
524
594
  }
@@ -657,8 +727,8 @@ var import_genai = require("@google/genai");
657
727
  var GoogleProvider = class extends BaseLLMProvider {
658
728
  constructor(config) {
659
729
  super(config);
660
- this.models = config.models;
661
- this.defaultModel = config.models.default;
730
+ this.models = normalizeModelMap(config.models);
731
+ this.defaultModel = this.models.default;
662
732
  this._pendingOperations = /* @__PURE__ */ new Map();
663
733
  const httpOptions = config.baseURL ? { baseUrl: config.baseURL } : void 0;
664
734
  if (config.provider === "vertex") {
@@ -697,7 +767,7 @@ var GoogleProvider = class extends BaseLLMProvider {
697
767
  async chat(userMessage, systemPrompt = "", options = {}) {
698
768
  const messages = [{ role: "user", content: userMessage }];
699
769
  const tier = options.tier || "default";
700
- const effectiveModel = this._getModelForTier(tier);
770
+ const effectiveModel = normalizeModelId(options.model) || this._getModelForTier(tier);
701
771
  const effectiveMaxTokens = options.maxTokens || this.config.maxTokens;
702
772
  const effectiveTemperature = options.temperature !== void 0 ? options.temperature : this.config.temperature;
703
773
  const response = await this._chatCompletionWithModel(
@@ -712,7 +782,7 @@ var GoogleProvider = class extends BaseLLMProvider {
712
782
  }
713
783
  async chatCompletion(messages, systemPrompt, tools = null, options = {}) {
714
784
  const tier = options.tier || "default";
715
- const modelName = this.models[tier] || this.defaultModel;
785
+ const modelName = normalizeModelId(options.model) || this.models[tier] || this.defaultModel;
716
786
  return this._chatCompletionWithModel(
717
787
  messages,
718
788
  systemPrompt,
@@ -1012,13 +1082,21 @@ ${msg.content}`;
1012
1082
  const tool = toolImplementations[toolName];
1013
1083
  const tool_call_id = `gemini-tool-call-${index}`;
1014
1084
  toolCall.id = tool_call_id;
1015
- console.log(`[Tool Call] > ${toolName}:`, JSON.stringify(toolCall.function.args));
1085
+ let toolArgs = toolCall.function.args;
1086
+ if (toolArgs === void 0 && toolCall.function.arguments) {
1087
+ try {
1088
+ toolArgs = typeof toolCall.function.arguments === "string" ? JSON.parse(toolCall.function.arguments) : toolCall.function.arguments;
1089
+ } catch (e) {
1090
+ toolArgs = {};
1091
+ }
1092
+ }
1093
+ console.log(`[Tool Call] > ${toolName}:`, JSON.stringify(toolArgs));
1016
1094
  if (!tool) {
1017
1095
  console.error(`[Tool Error] Tool '${toolName}' not found`);
1018
1096
  return { tool_call_id, output: JSON.stringify({ error: `Tool '${toolName}' not found.` }) };
1019
1097
  }
1020
1098
  try {
1021
- const output = await tool(toolCall.function.args, { env, tenantId });
1099
+ const output = await tool(toolArgs, { env, tenantId });
1022
1100
  const preview = typeof output === "string" ? output.length > 200 ? output.substring(0, 200) + "..." : output : JSON.stringify(output).substring(0, 200) + "...";
1023
1101
  console.log(`[Tool Result] < ${toolName}: ${preview}`);
1024
1102
  return { tool_call_id, output };
@@ -1032,7 +1110,7 @@ ${msg.content}`;
1032
1110
  }
1033
1111
  async imageGeneration(prompt, systemPrompt, options = {}) {
1034
1112
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1035
- const modelName = options.model || this.models.image || "gemini-3-pro-image-preview";
1113
+ const modelName = normalizeModelId(options.model) || this.models.image || "gemini-3-pro-image";
1036
1114
  console.log(`[${this.constructor.name}] Generating image with model: ${modelName}`);
1037
1115
  const hasReferenceImages = options.images && options.images.length > 0;
1038
1116
  const generationConfig = {
@@ -1281,11 +1359,13 @@ ${prompt}` : prompt;
1281
1359
 
1282
1360
  // src/llm/providers/anthropic-provider.js
1283
1361
  var import_sdk = __toESM(require("@anthropic-ai/sdk"), 1);
1362
+ var SAMPLING_PARAMS_MODEL_PATTERN = /^claude-(3[-.]|(opus|sonnet|haiku)-4(-[0-6])?([-@]\d{8})?$)/i;
1363
+ var NON_STREAMING_TIMEOUT_MS = 10 * 60 * 1e3;
1284
1364
  var AnthropicProvider = class extends BaseLLMProvider {
1285
1365
  constructor(config) {
1286
1366
  super(config);
1287
- this.models = config.models || {};
1288
- this.defaultModel = this.models.default || "claude-3-5-sonnet-20241022";
1367
+ this.models = normalizeModelMap(config.models || {});
1368
+ this.defaultModel = this.models.default || "claude-sonnet-5-5";
1289
1369
  const clientOptions = { apiKey: config.apiKey || "" };
1290
1370
  if (config.baseURL) {
1291
1371
  clientOptions.baseURL = config.baseURL;
@@ -1295,8 +1375,7 @@ var AnthropicProvider = class extends BaseLLMProvider {
1295
1375
  async chat(userMessage, systemPrompt = "", options = {}) {
1296
1376
  var _a;
1297
1377
  const messages = [{ role: "user", content: userMessage }];
1298
- const tier = options.tier || "default";
1299
- const effectiveModel = this._getModelForTier(tier);
1378
+ const effectiveModel = this._resolveModel(options);
1300
1379
  const effectiveMaxTokens = options.maxTokens || this.config.maxTokens || 4096;
1301
1380
  const effectiveTemperature = options.temperature !== void 0 ? options.temperature : (_a = this.config.temperature) != null ? _a : 0.7;
1302
1381
  const response = await this._chatCompletionWithModel(
@@ -1312,8 +1391,7 @@ var AnthropicProvider = class extends BaseLLMProvider {
1312
1391
  }
1313
1392
  async chatCompletion(messages, systemPrompt, tools = null, options = {}) {
1314
1393
  var _a;
1315
- const tier = options.tier || "default";
1316
- const effectiveModel = this._getModelForTier(tier);
1394
+ const effectiveModel = this._resolveModel(options);
1317
1395
  const effectiveMaxTokens = options.maxTokens || this.config.maxTokens || 4096;
1318
1396
  const effectiveTemperature = options.temperature !== void 0 ? options.temperature : (_a = this.config.temperature) != null ? _a : 0.7;
1319
1397
  return this._chatCompletionWithModel(
@@ -1327,7 +1405,7 @@ var AnthropicProvider = class extends BaseLLMProvider {
1327
1405
  );
1328
1406
  }
1329
1407
  async _chatCompletionWithModel(messages, systemPrompt, tools, modelName, maxTokens, temperature, options = {}) {
1330
- var _a, _b, _c, _d;
1408
+ var _a, _b, _c, _d, _e;
1331
1409
  let effectiveSystem = systemPrompt || void 0;
1332
1410
  if (options.responseFormat) {
1333
1411
  const formatType = typeof options.responseFormat === "string" ? options.responseFormat : options.responseFormat.type;
@@ -1347,14 +1425,18 @@ ${jsonPrompt}` : jsonPrompt;
1347
1425
  const requestParams = {
1348
1426
  model: modelName,
1349
1427
  max_tokens: maxTokens,
1350
- temperature,
1351
1428
  messages: anthropicMessages,
1352
1429
  ...effectiveSystem ? { system: effectiveSystem } : {},
1353
1430
  ...anthropicTools ? { tools: anthropicTools } : {}
1354
1431
  };
1432
+ if (this._supportsSamplingParams(modelName) && temperature !== void 0 && temperature !== null) {
1433
+ requestParams.temperature = temperature;
1434
+ }
1355
1435
  let response;
1356
1436
  try {
1357
- response = await this.client.messages.create(requestParams);
1437
+ response = await this.client.messages.create(requestParams, {
1438
+ timeout: (_a = this.config.timeout) != null ? _a : NON_STREAMING_TIMEOUT_MS
1439
+ });
1358
1440
  } catch (error) {
1359
1441
  console.error(`[AnthropicProvider] request failed (API Key: ${this._getMaskedApiKey()}):`, error);
1360
1442
  throw error;
@@ -1387,9 +1469,9 @@ ${jsonPrompt}` : jsonPrompt;
1387
1469
  _rawFinishReason: rawFinishReason,
1388
1470
  _responseFormat: options.responseFormat,
1389
1471
  usage: {
1390
- prompt_tokens: ((_a = response.usage) == null ? void 0 : _a.input_tokens) || 0,
1391
- completion_tokens: ((_b = response.usage) == null ? void 0 : _b.output_tokens) || 0,
1392
- total_tokens: (((_c = response.usage) == null ? void 0 : _c.input_tokens) || 0) + (((_d = response.usage) == null ? void 0 : _d.output_tokens) || 0)
1472
+ prompt_tokens: ((_b = response.usage) == null ? void 0 : _b.input_tokens) || 0,
1473
+ completion_tokens: ((_c = response.usage) == null ? void 0 : _c.output_tokens) || 0,
1474
+ total_tokens: (((_d = response.usage) == null ? void 0 : _d.input_tokens) || 0) + (((_e = response.usage) == null ? void 0 : _e.output_tokens) || 0)
1393
1475
  },
1394
1476
  model: modelName
1395
1477
  };
@@ -1503,6 +1585,15 @@ ${jsonPrompt}` : jsonPrompt;
1503
1585
  _getModelForTier(tier) {
1504
1586
  return this.models[tier] || this.defaultModel;
1505
1587
  }
1588
+ /**
1589
+ * Per-call model resolution: explicit options.model > options.tier > default
1590
+ */
1591
+ _resolveModel(options = {}) {
1592
+ return normalizeModelId(options.model) || this._getModelForTier(options.tier || "default");
1593
+ }
1594
+ _supportsSamplingParams(modelName) {
1595
+ return SAMPLING_PARAMS_MODEL_PATTERN.test(modelName || "");
1596
+ }
1506
1597
  _shouldAutoParse(options) {
1507
1598
  if (!options.responseFormat) return false;
1508
1599
  const formatType = typeof options.responseFormat === "string" ? options.responseFormat : options.responseFormat.type;
@@ -1609,7 +1700,7 @@ var LLMService = class {
1609
1700
  return { tools: param1, options: param2 || {} };
1610
1701
  }
1611
1702
  if (typeof param1 === "object") {
1612
- const optionsKeys = ["responseFormat", "responseSchema", "temperature", "maxTokens", "tier", "autoParse", "strictSchema", "schemaName"];
1703
+ const optionsKeys = ["responseFormat", "responseSchema", "temperature", "maxTokens", "tier", "model", "reasoningEffort", "autoParse", "strictSchema", "schemaName"];
1613
1704
  const hasOptionsKeys = optionsKeys.some((key) => key in param1);
1614
1705
  if (hasOptionsKeys) {
1615
1706
  return { tools: null, options: param1 };
@@ -2139,6 +2230,7 @@ function createSpeechHandler(app, getConfig) {
2139
2230
  FINISH_REASONS,
2140
2231
  GeminiProvider,
2141
2232
  GoogleProvider,
2233
+ LEGACY_MODEL_ALIASES,
2142
2234
  LLMService,
2143
2235
  LLMServiceException,
2144
2236
  MODEL_CONFIGS,
@@ -2150,6 +2242,7 @@ function createSpeechHandler(app, getConfig) {
2150
2242
  extractJsonFromResponse,
2151
2243
  extractTextAndJson,
2152
2244
  handleApiError,
2245
+ normalizeModelId,
2153
2246
  sanitizeError
2154
2247
  });
2155
2248
  //# sourceMappingURL=index.cjs.map