@contentgrowth/llm-service 1.3.3 → 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,
@@ -1040,7 +1110,7 @@ ${msg.content}`;
1040
1110
  }
1041
1111
  async imageGeneration(prompt, systemPrompt, options = {}) {
1042
1112
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1043
- 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";
1044
1114
  console.log(`[${this.constructor.name}] Generating image with model: ${modelName}`);
1045
1115
  const hasReferenceImages = options.images && options.images.length > 0;
1046
1116
  const generationConfig = {
@@ -1289,11 +1359,13 @@ ${prompt}` : prompt;
1289
1359
 
1290
1360
  // src/llm/providers/anthropic-provider.js
1291
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;
1292
1364
  var AnthropicProvider = class extends BaseLLMProvider {
1293
1365
  constructor(config) {
1294
1366
  super(config);
1295
- this.models = config.models || {};
1296
- 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";
1297
1369
  const clientOptions = { apiKey: config.apiKey || "" };
1298
1370
  if (config.baseURL) {
1299
1371
  clientOptions.baseURL = config.baseURL;
@@ -1303,8 +1375,7 @@ var AnthropicProvider = class extends BaseLLMProvider {
1303
1375
  async chat(userMessage, systemPrompt = "", options = {}) {
1304
1376
  var _a;
1305
1377
  const messages = [{ role: "user", content: userMessage }];
1306
- const tier = options.tier || "default";
1307
- const effectiveModel = this._getModelForTier(tier);
1378
+ const effectiveModel = this._resolveModel(options);
1308
1379
  const effectiveMaxTokens = options.maxTokens || this.config.maxTokens || 4096;
1309
1380
  const effectiveTemperature = options.temperature !== void 0 ? options.temperature : (_a = this.config.temperature) != null ? _a : 0.7;
1310
1381
  const response = await this._chatCompletionWithModel(
@@ -1320,8 +1391,7 @@ var AnthropicProvider = class extends BaseLLMProvider {
1320
1391
  }
1321
1392
  async chatCompletion(messages, systemPrompt, tools = null, options = {}) {
1322
1393
  var _a;
1323
- const tier = options.tier || "default";
1324
- const effectiveModel = this._getModelForTier(tier);
1394
+ const effectiveModel = this._resolveModel(options);
1325
1395
  const effectiveMaxTokens = options.maxTokens || this.config.maxTokens || 4096;
1326
1396
  const effectiveTemperature = options.temperature !== void 0 ? options.temperature : (_a = this.config.temperature) != null ? _a : 0.7;
1327
1397
  return this._chatCompletionWithModel(
@@ -1335,7 +1405,7 @@ var AnthropicProvider = class extends BaseLLMProvider {
1335
1405
  );
1336
1406
  }
1337
1407
  async _chatCompletionWithModel(messages, systemPrompt, tools, modelName, maxTokens, temperature, options = {}) {
1338
- var _a, _b, _c, _d;
1408
+ var _a, _b, _c, _d, _e;
1339
1409
  let effectiveSystem = systemPrompt || void 0;
1340
1410
  if (options.responseFormat) {
1341
1411
  const formatType = typeof options.responseFormat === "string" ? options.responseFormat : options.responseFormat.type;
@@ -1355,14 +1425,18 @@ ${jsonPrompt}` : jsonPrompt;
1355
1425
  const requestParams = {
1356
1426
  model: modelName,
1357
1427
  max_tokens: maxTokens,
1358
- temperature,
1359
1428
  messages: anthropicMessages,
1360
1429
  ...effectiveSystem ? { system: effectiveSystem } : {},
1361
1430
  ...anthropicTools ? { tools: anthropicTools } : {}
1362
1431
  };
1432
+ if (this._supportsSamplingParams(modelName) && temperature !== void 0 && temperature !== null) {
1433
+ requestParams.temperature = temperature;
1434
+ }
1363
1435
  let response;
1364
1436
  try {
1365
- 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
+ });
1366
1440
  } catch (error) {
1367
1441
  console.error(`[AnthropicProvider] request failed (API Key: ${this._getMaskedApiKey()}):`, error);
1368
1442
  throw error;
@@ -1395,9 +1469,9 @@ ${jsonPrompt}` : jsonPrompt;
1395
1469
  _rawFinishReason: rawFinishReason,
1396
1470
  _responseFormat: options.responseFormat,
1397
1471
  usage: {
1398
- prompt_tokens: ((_a = response.usage) == null ? void 0 : _a.input_tokens) || 0,
1399
- completion_tokens: ((_b = response.usage) == null ? void 0 : _b.output_tokens) || 0,
1400
- 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)
1401
1475
  },
1402
1476
  model: modelName
1403
1477
  };
@@ -1511,6 +1585,15 @@ ${jsonPrompt}` : jsonPrompt;
1511
1585
  _getModelForTier(tier) {
1512
1586
  return this.models[tier] || this.defaultModel;
1513
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
+ }
1514
1597
  _shouldAutoParse(options) {
1515
1598
  if (!options.responseFormat) return false;
1516
1599
  const formatType = typeof options.responseFormat === "string" ? options.responseFormat : options.responseFormat.type;
@@ -1617,7 +1700,7 @@ var LLMService = class {
1617
1700
  return { tools: param1, options: param2 || {} };
1618
1701
  }
1619
1702
  if (typeof param1 === "object") {
1620
- const optionsKeys = ["responseFormat", "responseSchema", "temperature", "maxTokens", "tier", "autoParse", "strictSchema", "schemaName"];
1703
+ const optionsKeys = ["responseFormat", "responseSchema", "temperature", "maxTokens", "tier", "model", "reasoningEffort", "autoParse", "strictSchema", "schemaName"];
1621
1704
  const hasOptionsKeys = optionsKeys.some((key) => key in param1);
1622
1705
  if (hasOptionsKeys) {
1623
1706
  return { tools: null, options: param1 };
@@ -2147,6 +2230,7 @@ function createSpeechHandler(app, getConfig) {
2147
2230
  FINISH_REASONS,
2148
2231
  GeminiProvider,
2149
2232
  GoogleProvider,
2233
+ LEGACY_MODEL_ALIASES,
2150
2234
  LLMService,
2151
2235
  LLMServiceException,
2152
2236
  MODEL_CONFIGS,
@@ -2158,6 +2242,7 @@ function createSpeechHandler(app, getConfig) {
2158
2242
  extractJsonFromResponse,
2159
2243
  extractTextAndJson,
2160
2244
  handleApiError,
2245
+ normalizeModelId,
2161
2246
  sanitizeError
2162
2247
  });
2163
2248
  //# sourceMappingURL=index.cjs.map