@contentgrowth/llm-service 1.2.1 → 1.2.3

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,
@@ -122,7 +123,8 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
122
123
  return {
123
124
  provider: tenantConfig.provider,
124
125
  models: MODEL_CONFIGS[tenantConfig.provider],
125
- apiKey: tenantConfig.api_key,
126
+ // for backward compatibility, api_key is deprecated and shall never be used!!
127
+ apiKey: tenantConfig.apiKey || tenantConfig.api_key,
126
128
  project: tenantConfig.project,
127
129
  location: tenantConfig.location,
128
130
  temperature: parseFloat(env.DEFAULT_TEMPERATURE || "0.7"),
@@ -193,35 +195,36 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
193
195
  // src/llm/config-manager.js
194
196
  var MODEL_CONFIGS = {
195
197
  openai: {
196
- default: "gpt-4o",
197
- edge: "gpt-4o",
198
- fast: "gpt-4o-mini",
199
- cost: "gpt-4o-mini",
200
- 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"
201
203
  },
202
204
  gemini: {
203
- default: "gemini-3-flash-preview",
204
- // 'gemini-2.5-flash',
205
- edge: "gemini-3-pro-preview",
206
- // 'gemini-2.5-pro',
207
- fast: "gemini-3-flash-preview",
208
- // 'gemini-2.5-flash-lite',
209
- cost: "gemini-3-flash-preview",
210
- // 'gemini-2.5-flash-lite',
211
- free: "gemini-3-flash-preview",
212
- // '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",
213
210
  video: "veo",
214
- image: "gemini-3-pro-image-preview"
215
- // Default image generation model
211
+ image: "gemini-3.5-pro-image-preview"
216
212
  },
217
213
  vertex: {
218
- default: "gemini-3-flash-preview",
219
- edge: "gemini-3-pro-preview",
220
- fast: "gemini-3-flash-preview",
221
- cost: "gemini-3-flash-preview",
222
- 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",
223
219
  video: "veo",
224
- 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"
225
228
  }
226
229
  };
227
230
  var ConfigManager = class {
@@ -1181,6 +1184,170 @@ ${prompt}` : prompt;
1181
1184
  }
1182
1185
  };
1183
1186
 
1187
+ // src/llm/providers/anthropic-provider.js
1188
+ var AnthropicProvider = class extends BaseLLMProvider {
1189
+ constructor(config) {
1190
+ super(config);
1191
+ this.models = config.models || {};
1192
+ this.defaultModel = this.models.default || "claude-3-5-sonnet-20241022";
1193
+ }
1194
+ async chat(userMessage, systemPrompt = "", options = {}) {
1195
+ var _a;
1196
+ const messages = [{ role: "user", content: userMessage }];
1197
+ const tier = options.tier || "default";
1198
+ const effectiveModel = this._getModelForTier(tier);
1199
+ const effectiveMaxTokens = options.maxTokens || this.config.maxTokens || 4096;
1200
+ const effectiveTemperature = options.temperature !== void 0 ? options.temperature : (_a = this.config.temperature) != null ? _a : 0.7;
1201
+ const response = await this._chatCompletionWithModel(
1202
+ messages,
1203
+ systemPrompt,
1204
+ null,
1205
+ effectiveModel,
1206
+ effectiveMaxTokens,
1207
+ effectiveTemperature,
1208
+ options
1209
+ );
1210
+ return { text: response.content };
1211
+ }
1212
+ async chatCompletion(messages, systemPrompt, tools = null, options = {}) {
1213
+ var _a;
1214
+ const tier = options.tier || "default";
1215
+ const effectiveModel = this._getModelForTier(tier);
1216
+ const effectiveMaxTokens = options.maxTokens || this.config.maxTokens || 4096;
1217
+ const effectiveTemperature = options.temperature !== void 0 ? options.temperature : (_a = this.config.temperature) != null ? _a : 0.7;
1218
+ return this._chatCompletionWithModel(
1219
+ messages,
1220
+ systemPrompt,
1221
+ tools,
1222
+ effectiveModel,
1223
+ effectiveMaxTokens,
1224
+ effectiveTemperature,
1225
+ options
1226
+ );
1227
+ }
1228
+ async _chatCompletionWithModel(messages, systemPrompt, tools, modelName, maxTokens, temperature, options = {}) {
1229
+ const baseURL = this.config.baseURL || "https://api.anthropic.com";
1230
+ const url = `${baseURL.replace(/\/$/, "")}/v1/messages`;
1231
+ const formattedMessages = messages.map((m) => ({
1232
+ role: m.role === "assistant" ? "assistant" : "user",
1233
+ content: m.content
1234
+ }));
1235
+ const headers = {
1236
+ "Content-Type": "application/json",
1237
+ "x-api-key": this.config.apiKey || "",
1238
+ "anthropic-version": "2023-06-01"
1239
+ };
1240
+ const payload = {
1241
+ model: modelName,
1242
+ messages: formattedMessages,
1243
+ max_tokens: maxTokens,
1244
+ temperature
1245
+ };
1246
+ if (systemPrompt) {
1247
+ payload.system = systemPrompt;
1248
+ }
1249
+ if (options.responseFormat) {
1250
+ const formatType = typeof options.responseFormat === "string" ? options.responseFormat : options.responseFormat.type;
1251
+ if (formatType === "json" || formatType === "json_schema") {
1252
+ const schemaText = options.responseFormat.schema || options.responseSchema ? `
1253
+
1254
+ Your output must comply strictly with this JSON Schema: ${JSON.stringify(options.responseFormat.schema || options.responseSchema)}` : "";
1255
+ 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}`;
1256
+ if (payload.system) {
1257
+ payload.system = `${payload.system}
1258
+
1259
+ ${jsonPrompt}`;
1260
+ } else {
1261
+ payload.system = jsonPrompt;
1262
+ }
1263
+ }
1264
+ }
1265
+ let responseText;
1266
+ let responseData;
1267
+ try {
1268
+ const res = await fetch(url, {
1269
+ method: "POST",
1270
+ headers,
1271
+ body: JSON.stringify(payload)
1272
+ });
1273
+ responseText = await res.text();
1274
+ if (!res.ok) {
1275
+ console.error(`[AnthropicProvider] API request failed with status ${res.status}:`, responseText);
1276
+ throw new LLMServiceException(`Anthropic API Error: ${responseText}`, res.status);
1277
+ }
1278
+ responseData = JSON.parse(responseText);
1279
+ } catch (error) {
1280
+ console.error(`[AnthropicProvider] request failed (API Key: ${this._getMaskedApiKey()}):`, error);
1281
+ throw error;
1282
+ }
1283
+ const contentBlock = responseData.content && responseData.content[0];
1284
+ const contentText = contentBlock ? contentBlock.text : "";
1285
+ if (!contentText) {
1286
+ console.error("[AnthropicProvider] Model returned empty response content");
1287
+ throw new LLMServiceException(
1288
+ "Model returned empty response. This usually means the prompt or schema is confusing the model.",
1289
+ 500
1290
+ );
1291
+ }
1292
+ const rawFinishReason = responseData.stop_reason;
1293
+ const normalizedFinishReason = this.normalizeFinishReason(rawFinishReason);
1294
+ const result = {
1295
+ content: contentText,
1296
+ tool_calls: null,
1297
+ // REST client tool calls mapping not required for playbook studio
1298
+ finishReason: normalizedFinishReason,
1299
+ _rawFinishReason: rawFinishReason,
1300
+ _responseFormat: options.responseFormat,
1301
+ usage: {
1302
+ prompt_tokens: responseData.usage ? responseData.usage.input_tokens : 0,
1303
+ completion_tokens: responseData.usage ? responseData.usage.output_tokens : 0,
1304
+ total_tokens: responseData.usage ? responseData.usage.input_tokens + responseData.usage.output_tokens : 0
1305
+ },
1306
+ model: modelName
1307
+ };
1308
+ if (options.responseFormat && this._shouldAutoParse(options)) {
1309
+ result.parsedContent = this._safeJsonParse(contentText);
1310
+ }
1311
+ return result;
1312
+ }
1313
+ _getModelForTier(tier) {
1314
+ if (this.models[tier]) {
1315
+ return this.models[tier];
1316
+ }
1317
+ return this.defaultModel;
1318
+ }
1319
+ _shouldAutoParse(options) {
1320
+ if (!options.responseFormat) return false;
1321
+ const formatType = typeof options.responseFormat === "string" ? options.responseFormat : options.responseFormat.type;
1322
+ return formatType === "json" || formatType === "json_schema";
1323
+ }
1324
+ _safeJsonParse(text) {
1325
+ try {
1326
+ return extractJsonFromResponse(text);
1327
+ } catch (error) {
1328
+ console.error("[AnthropicProvider] Failed to extract JSON from response:", text, error);
1329
+ try {
1330
+ return JSON.parse(text);
1331
+ } catch (_) {
1332
+ return null;
1333
+ }
1334
+ }
1335
+ }
1336
+ normalizeFinishReason(providerReason) {
1337
+ const lower = (providerReason || "").toLowerCase();
1338
+ if (lower === "end_turn") {
1339
+ return "completed";
1340
+ }
1341
+ if (lower === "max_tokens") {
1342
+ return "truncated";
1343
+ }
1344
+ if (lower === "stop_sequence") {
1345
+ return "completed";
1346
+ }
1347
+ return super.normalizeFinishReason(providerReason);
1348
+ }
1349
+ };
1350
+
1184
1351
  // src/llm-service.js
1185
1352
  var LLMService = class {
1186
1353
  constructor(env, toolImplementations = {}) {
@@ -1202,6 +1369,8 @@ var LLMService = class {
1202
1369
  provider = new OpenAIProvider(config);
1203
1370
  } else if (config.provider === "gemini" || config.provider === "vertex") {
1204
1371
  provider = new GoogleProvider(config);
1372
+ } else if (config.provider === "anthropic") {
1373
+ provider = new AnthropicProvider(config);
1205
1374
  } else {
1206
1375
  throw new LLMServiceException(`Unsupported LLM provider: ${config.provider}`, 500);
1207
1376
  }
@@ -1770,6 +1939,7 @@ function createSpeechHandler(app, getConfig) {
1770
1939
  }
1771
1940
  // Annotate the CommonJS export names for ESM import in node:
1772
1941
  0 && (module.exports = {
1942
+ AnthropicProvider,
1773
1943
  BaseConfigProvider,
1774
1944
  ConfigManager,
1775
1945
  DefaultConfigProvider,