@contentgrowth/llm-service 1.3.3 → 1.3.5

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.js CHANGED
@@ -68,10 +68,27 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
68
68
  return null;
69
69
  }
70
70
  }
71
+ /**
72
+ * Resolve the model map for a tenant.
73
+ * Precedence (highest first):
74
+ * 1. tenantConfig.model - single model chosen by the tenant (becomes `default`)
75
+ * 2. tenantConfig.models[tier] - per-tier overrides supplied by the tenant
76
+ * 3. MODEL_CONFIGS[provider] - library defaults
77
+ */
78
+ _resolveTenantModels(tenantConfig) {
79
+ const models = {
80
+ ...MODEL_CONFIGS[tenantConfig.provider] || {},
81
+ ...tenantConfig.models || {}
82
+ };
83
+ if (typeof tenantConfig.model === "string" && tenantConfig.model.trim()) {
84
+ models.default = tenantConfig.model;
85
+ }
86
+ return normalizeModelMap(models);
87
+ }
71
88
  _buildTenantConfig(tenantConfig, env) {
72
89
  return {
73
90
  provider: tenantConfig.provider,
74
- models: MODEL_CONFIGS[tenantConfig.provider],
91
+ models: this._resolveTenantModels(tenantConfig),
75
92
  // for backward compatibility, api_key is deprecated and shall never be used!!
76
93
  apiKey: tenantConfig.apiKey || tenantConfig.api_key,
77
94
  baseURL: tenantConfig.baseURL || tenantConfig.base_url || void 0,
@@ -160,39 +177,70 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
160
177
 
161
178
  // src/llm/config-manager.js
162
179
  var MODEL_CONFIGS = {
180
+ // NOTE: these must be OpenAI *API* model IDs. ChatGPT product names such as
181
+ // "GPT-5.5 Instant" / "GPT-5.5 Thinking" are not valid API models and return 404.
163
182
  openai: {
164
- default: "gpt-5.5-instant",
165
- edge: "gpt-5.5-thinking",
166
- fast: "gpt-5.5-instant",
167
- cost: "gpt-5.5-instant",
168
- free: "gpt-5.5-instant"
183
+ default: "gpt-5.6-luna",
184
+ edge: "gpt-5.6-sol",
185
+ fast: "gpt-5.6-luna",
186
+ cost: "gpt-5.6-luna",
187
+ free: "gpt-5.6-luna"
169
188
  },
189
+ // Gemini Developer API (ai.google.dev). There is no "gemini-3.5-pro"; the Pro tier is gemini-3.1-pro-preview.
170
190
  gemini: {
171
- default: "gemini-3.5-flash",
172
- edge: "gemini-3.5-pro",
173
- fast: "gemini-3.5-flash",
174
- cost: "gemini-3.5-flash",
175
- free: "gemini-3.5-flash",
191
+ default: "gemini-3.8-flash",
192
+ edge: "gemini-3.1-pro-preview",
193
+ fast: "gemini-3.8-flash",
194
+ cost: "gemini-3.5-flash-lite",
195
+ free: "gemini-3.5-flash-lite",
176
196
  video: "veo",
177
- image: "gemini-3.5-pro-image-preview"
197
+ image: "gemini-3-pro-image"
178
198
  },
199
+ // Vertex AI / Gemini Enterprise Agent Platform
179
200
  vertex: {
180
- default: "gemini-3.5-flash",
181
- edge: "gemini-3.5-pro",
182
- fast: "gemini-3.5-flash",
183
- cost: "gemini-3.5-flash",
184
- free: "gemini-3.5-flash",
201
+ default: "gemini-3.7-flash",
202
+ edge: "gemini-3.1-pro-preview",
203
+ fast: "gemini-3.7-flash",
204
+ cost: "gemini-3.5-flash-lite",
205
+ free: "gemini-3.5-flash-lite",
185
206
  video: "veo",
186
- image: "gemini-3.5-pro-image-preview"
207
+ image: "gemini-3.1-flash-image"
187
208
  },
209
+ // Anthropic IDs are "claude-<family>-<major>-<minor>", e.g. claude-sonnet-5-5 (not claude-5.5-sonnet).
188
210
  anthropic: {
189
- default: "claude-4.6-sonnet",
190
- edge: "claude-4.8-opus",
191
- fast: "claude-4.5-haiku",
192
- cost: "claude-4.5-haiku",
193
- free: "claude-4.5-haiku"
211
+ default: "claude-sonnet-5-5",
212
+ edge: "claude-opus-5-5",
213
+ fast: "claude-haiku-5-5",
214
+ cost: "claude-haiku-5-5",
215
+ free: "claude-haiku-5-5"
194
216
  }
195
217
  };
218
+ var LEGACY_MODEL_ALIASES = {
219
+ // OpenAI: ChatGPT product names, not API model IDs
220
+ "gpt-5.5-instant": "gpt-5.6-luna",
221
+ "gpt-5.5-thinking": "gpt-5.6-sol",
222
+ // Gemini: never existed
223
+ "gemini-3.5-pro": "gemini-3.1-pro-preview",
224
+ "gemini-3.7-pro": "gemini-3.1-pro-preview",
225
+ "gemini-3.5-pro-image-preview": "gemini-3-pro-image",
226
+ "gemini-3.7-pro-image-preview": "gemini-3-pro-image",
227
+ // Anthropic: wrong segment order
228
+ "claude-4.5-haiku": "claude-haiku-4-5",
229
+ "claude-4.6-sonnet": "claude-sonnet-4-6",
230
+ "claude-4.8-opus": "claude-opus-4-8"
231
+ };
232
+ function normalizeModelId(model) {
233
+ if (typeof model !== "string") return model;
234
+ const trimmed = model.trim();
235
+ return LEGACY_MODEL_ALIASES[trimmed] || trimmed;
236
+ }
237
+ function normalizeModelMap(models = {}) {
238
+ const out = {};
239
+ for (const [tier, id] of Object.entries(models)) {
240
+ out[tier] = normalizeModelId(id);
241
+ }
242
+ return out;
243
+ }
196
244
  var ConfigManager = class {
197
245
  /**
198
246
  * Set a custom configuration provider.
@@ -331,6 +379,13 @@ var BaseLLMProvider = class {
331
379
  async extractWithLLM(fileData, mimeType, prompt, schema, options) {
332
380
  throw new Error("This provider does not support extractWithLLM (Multimodal extraction)");
333
381
  }
382
+ /**
383
+ * Connect to a bidirectional real-time Live session
384
+ * @param {Object} options - { model, config, callbacks }
385
+ */
386
+ async connectLive(options) {
387
+ throw new Error(`This provider (${this.constructor.name}) does not support live real-time sessions`);
388
+ }
334
389
  /**
335
390
  * Helper to get the last 6 digits of the API key for logging.
336
391
  * @returns {string} "..." + last 6 chars, or "not_set"
@@ -420,6 +475,7 @@ function extractTextAndJson(input) {
420
475
  }
421
476
 
422
477
  // src/llm/providers/openai-provider.js
478
+ var REASONING_MODEL_PATTERN = /^(o\d|gpt-5|gpt-6)/i;
423
479
  var OpenAIProvider = class extends BaseLLMProvider {
424
480
  constructor(config) {
425
481
  super(config);
@@ -428,13 +484,12 @@ var OpenAIProvider = class extends BaseLLMProvider {
428
484
  clientOptions.baseURL = config.baseURL;
429
485
  }
430
486
  this.client = new OpenAI(clientOptions);
431
- this.models = config.models;
432
- this.defaultModel = config.models.default;
487
+ this.models = normalizeModelMap(config.models);
488
+ this.defaultModel = this.models.default;
433
489
  }
434
490
  async chat(userMessage, systemPrompt = "", options = {}) {
435
491
  const messages = [{ role: "user", content: userMessage }];
436
- const tier = options.tier || "default";
437
- const effectiveModel = this._getModelForTier(tier);
492
+ const effectiveModel = this._resolveModel(options);
438
493
  const effectiveMaxTokens = options.maxTokens || this.config.maxTokens;
439
494
  const effectiveTemperature = options.temperature !== void 0 ? options.temperature : this.config.temperature;
440
495
  const response = await this._chatCompletionWithModel(
@@ -452,22 +507,42 @@ var OpenAIProvider = class extends BaseLLMProvider {
452
507
  messages,
453
508
  systemPrompt,
454
509
  tools,
455
- this.defaultModel,
510
+ this._resolveModel(options),
456
511
  this.config.maxTokens,
457
512
  this.config.temperature,
458
513
  options
459
514
  );
460
515
  }
516
+ /**
517
+ * Per-call model resolution: explicit options.model > options.tier > default
518
+ */
519
+ _resolveModel(options = {}) {
520
+ return normalizeModelId(options.model) || this._getModelForTier(options.tier || "default");
521
+ }
522
+ _isReasoningModel(modelName) {
523
+ return REASONING_MODEL_PATTERN.test(modelName || "");
524
+ }
461
525
  async _chatCompletionWithModel(messages, systemPrompt, tools, modelName, maxTokens, temperature, options = {}) {
462
526
  var _a, _b, _c, _d, _e;
527
+ const effectiveMaxTokens = (_a = options.maxTokens) != null ? _a : maxTokens;
463
528
  const requestPayload = {
464
529
  model: modelName,
465
- temperature: (_a = options.temperature) != null ? _a : temperature,
466
- max_tokens: (_b = options.maxTokens) != null ? _b : maxTokens,
467
530
  messages: [{ role: "system", content: systemPrompt }, ...messages],
468
531
  tools,
469
532
  tool_choice: tools ? "auto" : void 0
470
533
  };
534
+ if (this._isReasoningModel(modelName)) {
535
+ requestPayload.max_completion_tokens = effectiveMaxTokens;
536
+ if (options.reasoningEffort) {
537
+ requestPayload.reasoning_effort = options.reasoningEffort;
538
+ }
539
+ if (options.reasoningEffort === "none" && options.temperature !== void 0) {
540
+ requestPayload.temperature = options.temperature;
541
+ }
542
+ } else {
543
+ requestPayload.max_tokens = effectiveMaxTokens;
544
+ requestPayload.temperature = (_b = options.temperature) != null ? _b : temperature;
545
+ }
471
546
  if (options.responseFormat) {
472
547
  requestPayload.response_format = this._buildResponseFormat(options);
473
548
  }
@@ -606,8 +681,8 @@ import { GoogleGenAI } from "@google/genai";
606
681
  var GoogleProvider = class extends BaseLLMProvider {
607
682
  constructor(config) {
608
683
  super(config);
609
- this.models = config.models;
610
- this.defaultModel = config.models.default;
684
+ this.models = normalizeModelMap(config.models);
685
+ this.defaultModel = this.models.default;
611
686
  this._pendingOperations = /* @__PURE__ */ new Map();
612
687
  const httpOptions = config.baseURL ? { baseUrl: config.baseURL } : void 0;
613
688
  if (config.provider === "vertex") {
@@ -646,7 +721,7 @@ var GoogleProvider = class extends BaseLLMProvider {
646
721
  async chat(userMessage, systemPrompt = "", options = {}) {
647
722
  const messages = [{ role: "user", content: userMessage }];
648
723
  const tier = options.tier || "default";
649
- const effectiveModel = this._getModelForTier(tier);
724
+ const effectiveModel = normalizeModelId(options.model) || this._getModelForTier(tier);
650
725
  const effectiveMaxTokens = options.maxTokens || this.config.maxTokens;
651
726
  const effectiveTemperature = options.temperature !== void 0 ? options.temperature : this.config.temperature;
652
727
  const response = await this._chatCompletionWithModel(
@@ -661,7 +736,7 @@ var GoogleProvider = class extends BaseLLMProvider {
661
736
  }
662
737
  async chatCompletion(messages, systemPrompt, tools = null, options = {}) {
663
738
  const tier = options.tier || "default";
664
- const modelName = this.models[tier] || this.defaultModel;
739
+ const modelName = normalizeModelId(options.model) || this.models[tier] || this.defaultModel;
665
740
  return this._chatCompletionWithModel(
666
741
  messages,
667
742
  systemPrompt,
@@ -989,7 +1064,7 @@ ${msg.content}`;
989
1064
  }
990
1065
  async imageGeneration(prompt, systemPrompt, options = {}) {
991
1066
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
992
- const modelName = options.model || this.models.image || "gemini-3-pro-image-preview";
1067
+ const modelName = normalizeModelId(options.model) || this.models.image || "gemini-3-pro-image";
993
1068
  console.log(`[${this.constructor.name}] Generating image with model: ${modelName}`);
994
1069
  const hasReferenceImages = options.images && options.images.length > 0;
995
1070
  const generationConfig = {
@@ -1234,15 +1309,38 @@ ${prompt}` : prompt;
1234
1309
  throw error;
1235
1310
  }
1236
1311
  }
1312
+ /**
1313
+ * Connect to Gemini Live bidirectional real-time audio/text session
1314
+ *
1315
+ * @param {Object} options
1316
+ * @param {string} [options.model] - Model name (e.g. models/gemini-2.5-flash-native-audio-preview-09-2025)
1317
+ * @param {Object} options.config - Live session configuration
1318
+ * @param {Object} options.callbacks - Event callbacks (onopen, onmessage, onerror, onclose)
1319
+ * @returns {Promise<any>} The active Live session
1320
+ */
1321
+ async connectLive({ model, config, callbacks }) {
1322
+ var _a, _b, _c;
1323
+ if (!((_b = (_a = this.client) == null ? void 0 : _a.live) == null ? void 0 : _b.connect)) {
1324
+ throw new LLMServiceException("The configured Google provider SDK does not support live sessions.", 500);
1325
+ }
1326
+ const resolvedModel = model || ((_c = this.models) == null ? void 0 : _c.live) || this.defaultModel || "models/gemini-2.5-flash-native-audio-preview-09-2025";
1327
+ return this.client.live.connect({
1328
+ model: resolvedModel,
1329
+ config,
1330
+ callbacks
1331
+ });
1332
+ }
1237
1333
  };
1238
1334
 
1239
1335
  // src/llm/providers/anthropic-provider.js
1240
1336
  import Anthropic from "@anthropic-ai/sdk";
1337
+ var SAMPLING_PARAMS_MODEL_PATTERN = /^claude-(3[-.]|(opus|sonnet|haiku)-4(-[0-6])?([-@]\d{8})?$)/i;
1338
+ var NON_STREAMING_TIMEOUT_MS = 10 * 60 * 1e3;
1241
1339
  var AnthropicProvider = class extends BaseLLMProvider {
1242
1340
  constructor(config) {
1243
1341
  super(config);
1244
- this.models = config.models || {};
1245
- this.defaultModel = this.models.default || "claude-3-5-sonnet-20241022";
1342
+ this.models = normalizeModelMap(config.models || {});
1343
+ this.defaultModel = this.models.default || "claude-sonnet-5-5";
1246
1344
  const clientOptions = { apiKey: config.apiKey || "" };
1247
1345
  if (config.baseURL) {
1248
1346
  clientOptions.baseURL = config.baseURL;
@@ -1252,8 +1350,7 @@ var AnthropicProvider = class extends BaseLLMProvider {
1252
1350
  async chat(userMessage, systemPrompt = "", options = {}) {
1253
1351
  var _a;
1254
1352
  const messages = [{ role: "user", content: userMessage }];
1255
- const tier = options.tier || "default";
1256
- const effectiveModel = this._getModelForTier(tier);
1353
+ const effectiveModel = this._resolveModel(options);
1257
1354
  const effectiveMaxTokens = options.maxTokens || this.config.maxTokens || 4096;
1258
1355
  const effectiveTemperature = options.temperature !== void 0 ? options.temperature : (_a = this.config.temperature) != null ? _a : 0.7;
1259
1356
  const response = await this._chatCompletionWithModel(
@@ -1269,8 +1366,7 @@ var AnthropicProvider = class extends BaseLLMProvider {
1269
1366
  }
1270
1367
  async chatCompletion(messages, systemPrompt, tools = null, options = {}) {
1271
1368
  var _a;
1272
- const tier = options.tier || "default";
1273
- const effectiveModel = this._getModelForTier(tier);
1369
+ const effectiveModel = this._resolveModel(options);
1274
1370
  const effectiveMaxTokens = options.maxTokens || this.config.maxTokens || 4096;
1275
1371
  const effectiveTemperature = options.temperature !== void 0 ? options.temperature : (_a = this.config.temperature) != null ? _a : 0.7;
1276
1372
  return this._chatCompletionWithModel(
@@ -1284,7 +1380,7 @@ var AnthropicProvider = class extends BaseLLMProvider {
1284
1380
  );
1285
1381
  }
1286
1382
  async _chatCompletionWithModel(messages, systemPrompt, tools, modelName, maxTokens, temperature, options = {}) {
1287
- var _a, _b, _c, _d;
1383
+ var _a, _b, _c, _d, _e;
1288
1384
  let effectiveSystem = systemPrompt || void 0;
1289
1385
  if (options.responseFormat) {
1290
1386
  const formatType = typeof options.responseFormat === "string" ? options.responseFormat : options.responseFormat.type;
@@ -1304,14 +1400,18 @@ ${jsonPrompt}` : jsonPrompt;
1304
1400
  const requestParams = {
1305
1401
  model: modelName,
1306
1402
  max_tokens: maxTokens,
1307
- temperature,
1308
1403
  messages: anthropicMessages,
1309
1404
  ...effectiveSystem ? { system: effectiveSystem } : {},
1310
1405
  ...anthropicTools ? { tools: anthropicTools } : {}
1311
1406
  };
1407
+ if (this._supportsSamplingParams(modelName) && temperature !== void 0 && temperature !== null) {
1408
+ requestParams.temperature = temperature;
1409
+ }
1312
1410
  let response;
1313
1411
  try {
1314
- response = await this.client.messages.create(requestParams);
1412
+ response = await this.client.messages.create(requestParams, {
1413
+ timeout: (_a = this.config.timeout) != null ? _a : NON_STREAMING_TIMEOUT_MS
1414
+ });
1315
1415
  } catch (error) {
1316
1416
  console.error(`[AnthropicProvider] request failed (API Key: ${this._getMaskedApiKey()}):`, error);
1317
1417
  throw error;
@@ -1344,9 +1444,9 @@ ${jsonPrompt}` : jsonPrompt;
1344
1444
  _rawFinishReason: rawFinishReason,
1345
1445
  _responseFormat: options.responseFormat,
1346
1446
  usage: {
1347
- prompt_tokens: ((_a = response.usage) == null ? void 0 : _a.input_tokens) || 0,
1348
- completion_tokens: ((_b = response.usage) == null ? void 0 : _b.output_tokens) || 0,
1349
- total_tokens: (((_c = response.usage) == null ? void 0 : _c.input_tokens) || 0) + (((_d = response.usage) == null ? void 0 : _d.output_tokens) || 0)
1447
+ prompt_tokens: ((_b = response.usage) == null ? void 0 : _b.input_tokens) || 0,
1448
+ completion_tokens: ((_c = response.usage) == null ? void 0 : _c.output_tokens) || 0,
1449
+ total_tokens: (((_d = response.usage) == null ? void 0 : _d.input_tokens) || 0) + (((_e = response.usage) == null ? void 0 : _e.output_tokens) || 0)
1350
1450
  },
1351
1451
  model: modelName
1352
1452
  };
@@ -1460,6 +1560,15 @@ ${jsonPrompt}` : jsonPrompt;
1460
1560
  _getModelForTier(tier) {
1461
1561
  return this.models[tier] || this.defaultModel;
1462
1562
  }
1563
+ /**
1564
+ * Per-call model resolution: explicit options.model > options.tier > default
1565
+ */
1566
+ _resolveModel(options = {}) {
1567
+ return normalizeModelId(options.model) || this._getModelForTier(options.tier || "default");
1568
+ }
1569
+ _supportsSamplingParams(modelName) {
1570
+ return SAMPLING_PARAMS_MODEL_PATTERN.test(modelName || "");
1571
+ }
1463
1572
  _shouldAutoParse(options) {
1464
1573
  if (!options.responseFormat) return false;
1465
1574
  const formatType = typeof options.responseFormat === "string" ? options.responseFormat : options.responseFormat.type;
@@ -1566,7 +1675,7 @@ var LLMService = class {
1566
1675
  return { tools: param1, options: param2 || {} };
1567
1676
  }
1568
1677
  if (typeof param1 === "object") {
1569
- const optionsKeys = ["responseFormat", "responseSchema", "temperature", "maxTokens", "tier", "autoParse", "strictSchema", "schemaName"];
1678
+ const optionsKeys = ["responseFormat", "responseSchema", "temperature", "maxTokens", "tier", "model", "reasoningEffort", "autoParse", "strictSchema", "schemaName"];
1570
1679
  const hasOptionsKeys = optionsKeys.some((key) => key in param1);
1571
1680
  if (hasOptionsKeys) {
1572
1681
  return { tools: null, options: param1 };
@@ -1753,6 +1862,21 @@ var LLMService = class {
1753
1862
  }
1754
1863
  return provider.extractWithLLM(fileData, mimeType, prompt, schema, options);
1755
1864
  }
1865
+ /**
1866
+ * Connect to a bidirectional real-time Live session
1867
+ *
1868
+ * @param {Object} params - { model, config, callbacks }
1869
+ * @param {string|null} [tenantId] - Tenant ID
1870
+ * @returns {Promise<any>} The active Live session
1871
+ */
1872
+ async connectLive(params, tenantId = null) {
1873
+ var _a;
1874
+ const provider = await this._getProvider(tenantId);
1875
+ if (typeof provider.connectLive !== "function") {
1876
+ throw new LLMServiceException(`Provider ${((_a = provider.config) == null ? void 0 : _a.provider) || provider.constructor.name} does not support live real-time sessions`, 400);
1877
+ }
1878
+ return provider.connectLive(params);
1879
+ }
1756
1880
  };
1757
1881
  var LLMServiceException = class extends Error {
1758
1882
  constructor(message, statusCode = 500, details = null) {
@@ -2087,6 +2211,9 @@ function createSpeechHandler(app, getConfig) {
2087
2211
  });
2088
2212
  return app;
2089
2213
  }
2214
+
2215
+ // src/index.js
2216
+ import { Modality } from "@google/genai";
2090
2217
  export {
2091
2218
  AnthropicProvider,
2092
2219
  BaseConfigProvider,
@@ -2095,9 +2222,11 @@ export {
2095
2222
  FINISH_REASONS,
2096
2223
  GoogleProvider as GeminiProvider,
2097
2224
  GoogleProvider,
2225
+ LEGACY_MODEL_ALIASES,
2098
2226
  LLMService,
2099
2227
  LLMServiceException,
2100
2228
  MODEL_CONFIGS,
2229
+ Modality,
2101
2230
  OpenAIProvider,
2102
2231
  TranscriptionService,
2103
2232
  TranscriptionServiceException,
@@ -2106,6 +2235,7 @@ export {
2106
2235
  extractJsonFromResponse,
2107
2236
  extractTextAndJson,
2108
2237
  handleApiError,
2238
+ normalizeModelId,
2109
2239
  sanitizeError
2110
2240
  };
2111
2241
  //# sourceMappingURL=index.js.map