@adaptic/lumic-utils 1.0.23 → 1.0.24

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.
Files changed (35) hide show
  1. package/dist/{apollo-client.client-DrSy1wz-.js → apollo-client.client-9wJcufhf.js} +4 -4
  2. package/dist/{apollo-client.client-DrSy1wz-.js.map → apollo-client.client-9wJcufhf.js.map} +1 -1
  3. package/dist/{apollo-client.client-FeVPHV0i.js → apollo-client.client-CyxU1hTu.js} +3 -3
  4. package/dist/{apollo-client.client-FeVPHV0i.js.map → apollo-client.client-CyxU1hTu.js.map} +1 -1
  5. package/dist/{apollo-client.server-CJ6iRLa2.js → apollo-client.server-CbagxkmK.js} +3 -3
  6. package/dist/{apollo-client.server-CJ6iRLa2.js.map → apollo-client.server-CbagxkmK.js.map} +1 -1
  7. package/dist/{apollo-client.server-BEGHAF48.js → apollo-client.server-DB3jLbBx.js} +3 -3
  8. package/dist/{apollo-client.server-BEGHAF48.js.map → apollo-client.server-DB3jLbBx.js.map} +1 -1
  9. package/dist/{index-Dz0wKyTF.js → index-B4yVKGNR.js} +2 -2
  10. package/dist/{index-Dz0wKyTF.js.map → index-B4yVKGNR.js.map} +1 -1
  11. package/dist/{index-vRskhk1H.js → index-CL79JTWc.js} +2 -2
  12. package/dist/{index-vRskhk1H.js.map → index-CL79JTWc.js.map} +1 -1
  13. package/dist/{index-CWoI2dXN.js → index-EMQ1uJMh.js} +365 -274
  14. package/dist/{index-WGzi__C5.js.map → index-EMQ1uJMh.js.map} +1 -1
  15. package/dist/{index-WGzi__C5.js → index-KzQOh2uu.js} +365 -274
  16. package/dist/{index-CWoI2dXN.js.map → index-KzQOh2uu.js.map} +1 -1
  17. package/dist/index.cjs +1 -1
  18. package/dist/index.mjs +1 -1
  19. package/dist/test.cjs +21 -17
  20. package/dist/test.cjs.map +1 -1
  21. package/dist/test.mjs +21 -17
  22. package/dist/test.mjs.map +1 -1
  23. package/dist/types/functions/get-weather.d.ts +1 -1
  24. package/dist/types/functions/google-sheets.d.ts +2 -2
  25. package/dist/types/functions/json-llm-tools.d.ts +1 -1
  26. package/dist/types/functions/llm-anthropic.d.ts +1 -1
  27. package/dist/types/functions/llm-deepseek.d.ts +2 -2
  28. package/dist/types/functions/llm-openai-compatible.d.ts +2 -2
  29. package/dist/types/functions/llm-openai.d.ts +3 -3
  30. package/dist/types/functions/llm-utils.d.ts +1 -1
  31. package/dist/types/test-llm-functions-archive.d.ts +1 -1
  32. package/dist/types/test.d.ts +1 -1
  33. package/dist/types/types/openai-types.d.ts +10 -1
  34. package/dist/types/utils/aws-initialise.d.ts +4 -4
  35. package/package.json +2 -2
@@ -571,10 +571,19 @@ function getModelProvider(model) {
571
571
  return SUPPORTED_MODELS[model].provider;
572
572
  }
573
573
  /**
574
- * Default model tiers per provider for use with LLM_MODEL_MINI/NORMAL/ADVANCED env vars
574
+ * Default model tiers per provider for use with LLM_MODEL_MINI/NORMAL/ADVANCED env vars.
575
+ *
576
+ * OpenAI `advanced` was reverted from `gpt-5.5` back to `gpt-5.4` on 2026-05-30:
577
+ * the heavy tool-call prompt in adaptic-engine's trading-decision path was
578
+ * timing out even after the engine raised `LLM_CALL_TIMEOUT_MS` to 90s (5+
579
+ * tickers per loop still exceeded the budget). `gpt-5.5`'s 1M-context window
580
+ * is not required on that path — the prompt fits comfortably inside `gpt-5.4`'s
581
+ * envelope and `gpt-5.4` returns inside the original 25-30s p95. `gpt-5.5`
582
+ * remains registered in OPENAI_MODELS and reachable by explicit model id;
583
+ * only the `advanced` tier default is rolled back.
575
584
  */
576
585
  const PROVIDER_DEFAULT_MODELS = {
577
- openai: { mini: 'gpt-5.4-nano', normal: 'gpt-5.4-mini', advanced: 'gpt-5.5' },
586
+ openai: { mini: 'gpt-5.4-nano', normal: 'gpt-5.4-mini', advanced: 'gpt-5.4' },
578
587
  anthropic: { mini: 'claude-haiku-4-5', normal: 'claude-sonnet-4-6', advanced: 'claude-opus-4-7' },
579
588
  deepseek: { mini: 'deepseek-v4-flash', normal: 'deepseek-v4-flash', advanced: 'deepseek-v4-pro' },
580
589
  kimi: { mini: 'kimi-k2-0905-preview', normal: 'kimi-k2.5', advanced: 'kimi-k2.6' },
@@ -1528,11 +1537,13 @@ let openai;
1528
1537
  function initializeOpenAI(apiKey) {
1529
1538
  const key = apiKey || getSecrets().openai.apiKey;
1530
1539
  if (!key) {
1531
- throw new Error('OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set');
1540
+ throw new Error("OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set");
1532
1541
  }
1533
1542
  return new OpenAI({
1534
1543
  apiKey: key,
1535
- defaultHeaders: { 'User-Agent': 'My Server-side Application (compatible; OpenAI API Client)' },
1544
+ defaultHeaders: {
1545
+ "User-Agent": "My Server-side Application (compatible; OpenAI API Client)",
1546
+ },
1536
1547
  });
1537
1548
  }
1538
1549
  /**
@@ -1550,9 +1561,7 @@ function initializeOpenAI(apiKey) {
1550
1561
  */
1551
1562
  async function fixJsonWithAI(jsonStr, options) {
1552
1563
  // Backward compatibility: handle string apiKey as first positional param
1553
- const opts = typeof options === 'string'
1554
- ? { apiKey: options }
1555
- : (options ?? {});
1564
+ const opts = typeof options === "string" ? { apiKey: options } : (options ?? {});
1556
1565
  const apiKey = opts.apiKey;
1557
1566
  const maxDepth = opts.maxDepth ?? 2;
1558
1567
  const depth = opts._depth ?? 0;
@@ -1565,18 +1574,18 @@ async function fixJsonWithAI(jsonStr, options) {
1565
1574
  openai = initializeOpenAI(apiKey);
1566
1575
  }
1567
1576
  const completion = await openai.chat.completions.create({
1568
- model: 'gpt-5-mini',
1577
+ model: "gpt-5-mini",
1569
1578
  messages: [
1570
1579
  {
1571
- role: 'system',
1572
- content: 'You are a JSON fixer. Return only valid JSON without any additional text or explanation.'
1580
+ role: "system",
1581
+ content: "You are a JSON fixer. Return only valid JSON without any additional text or explanation.",
1573
1582
  },
1574
1583
  {
1575
- role: 'user',
1576
- content: `Fix this broken JSON:\n${jsonStr}`
1577
- }
1584
+ role: "user",
1585
+ content: `Fix this broken JSON:\n${jsonStr}`,
1586
+ },
1578
1587
  ],
1579
- response_format: { type: 'json_object' }
1588
+ response_format: { type: "json_object" },
1580
1589
  });
1581
1590
  const fixedJson = completion.choices[0]?.message?.content;
1582
1591
  if (fixedJson && isValidJson(fixedJson)) {
@@ -1590,23 +1599,32 @@ async function fixJsonWithAI(jsonStr, options) {
1590
1599
  _depth: depth + 1,
1591
1600
  });
1592
1601
  }
1593
- throw new JsonParseError('Failed to fix JSON with AI: returned invalid JSON');
1602
+ throw new JsonParseError("Failed to fix JSON with AI: returned invalid JSON");
1594
1603
  }
1595
1604
  catch (err) {
1596
1605
  // Re-throw JsonParseError as-is (e.g. max depth exceeded)
1597
1606
  if (err instanceof JsonParseError) {
1598
1607
  throw err;
1599
1608
  }
1600
- const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
1609
+ const errorMessage = err instanceof Error ? err.message : "Unknown error occurred";
1601
1610
  throw new JsonParseError(`Error fixing JSON with AI: ${errorMessage}`);
1602
1611
  }
1603
1612
  }
1604
1613
 
1605
1614
  // llm-utils.ts
1606
1615
  function normalizeModelName(model) {
1607
- return model.replace(/_/g, '-').toLowerCase();
1608
- }
1609
- const CODE_BLOCK_TYPES = ['javascript', 'js', 'graphql', 'json', 'typescript', 'python', 'markdown', 'yaml'];
1616
+ return model.replace(/_/g, "-").toLowerCase();
1617
+ }
1618
+ const CODE_BLOCK_TYPES = [
1619
+ "javascript",
1620
+ "js",
1621
+ "graphql",
1622
+ "json",
1623
+ "typescript",
1624
+ "python",
1625
+ "markdown",
1626
+ "yaml",
1627
+ ];
1610
1628
  /**
1611
1629
  * Tries to parse JSON from the given content string according to the specified
1612
1630
  * response format. If the response format is 'json' or a JSON schema object, it
@@ -1630,7 +1648,9 @@ async function parseResponse(content, responseFormat, options = {}) {
1630
1648
  if (recursionDepth > maxRetries) {
1631
1649
  throw new Error(`Maximum recursion depth (${maxRetries}) exceeded while parsing JSON. AI fixing may have returned broken JSON.`);
1632
1650
  }
1633
- if (responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat?.type === 'json_schema')) {
1651
+ if (responseFormat === "json" ||
1652
+ (typeof responseFormat === "object" &&
1653
+ responseFormat?.type === "json_schema")) {
1634
1654
  let cleanedContent = content.trim();
1635
1655
  let detectedType = null;
1636
1656
  // Remove code block markers if present
@@ -1641,8 +1661,8 @@ async function parseResponse(content, responseFormat, options = {}) {
1641
1661
  }
1642
1662
  return false;
1643
1663
  });
1644
- if (startsWithCodeBlock && cleanedContent.endsWith('```')) {
1645
- const firstLineEndIndex = cleanedContent.indexOf('\n');
1664
+ if (startsWithCodeBlock && cleanedContent.endsWith("```")) {
1665
+ const firstLineEndIndex = cleanedContent.indexOf("\n");
1646
1666
  if (firstLineEndIndex !== -1) {
1647
1667
  cleanedContent = cleanedContent.slice(firstLineEndIndex + 1, -3).trim();
1648
1668
  getLumicLogger().info(`Code block for type ${detectedType} detected and removed. Cleaned content: ${cleanedContent}`);
@@ -1675,7 +1695,7 @@ async function parseResponse(content, responseFormat, options = {}) {
1675
1695
  },
1676
1696
  // Strategy 3: Remove leading/trailing text and try parsing
1677
1697
  () => {
1678
- const jsonMatch = cleanedContent.replace(/^[^{[]*|[^}\]]*$/g, '');
1698
+ const jsonMatch = cleanedContent.replace(/^[^{[]*|[^}\]]*$/g, "");
1679
1699
  try {
1680
1700
  return JSON.parse(jsonMatch);
1681
1701
  }
@@ -1697,7 +1717,7 @@ async function parseResponse(content, responseFormat, options = {}) {
1697
1717
  }
1698
1718
  // Strategy 5: Use AI fixing (only if explicitly enabled)
1699
1719
  if (enableAiFix) {
1700
- getLumicLogger().warn('Using AI-powered JSON fixing. This adds cost and latency. Consider improving JSON generation instead.');
1720
+ getLumicLogger().warn("Using AI-powered JSON fixing. This adds cost and latency. Consider improving JSON generation instead.");
1701
1721
  try {
1702
1722
  const aiFixedJson = await fixJsonWithAI(cleanedContent);
1703
1723
  if (aiFixedJson !== null) {
@@ -1710,7 +1730,7 @@ async function parseResponse(content, responseFormat, options = {}) {
1710
1730
  catch {
1711
1731
  // AI returned something that looks like JSON but isn't valid
1712
1732
  // Increment recursion depth and try parsing the AI response
1713
- getLumicLogger().warn('AI fixing returned invalid JSON, attempting recursive parse...');
1733
+ getLumicLogger().warn("AI fixing returned invalid JSON, attempting recursive parse...");
1714
1734
  return parseResponse(JSON.stringify(aiFixedJson), responseFormat, {
1715
1735
  ...options,
1716
1736
  _recursionDepth: recursionDepth + 1,
@@ -1719,14 +1739,14 @@ async function parseResponse(content, responseFormat, options = {}) {
1719
1739
  }
1720
1740
  }
1721
1741
  catch (error) {
1722
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
1742
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
1723
1743
  getLumicLogger().error(`AI JSON fixing failed: ${errorMessage}`);
1724
1744
  // Continue to final error below
1725
1745
  }
1726
1746
  }
1727
1747
  // If all strategies fail, throw an error
1728
1748
  getLumicLogger().error(`Failed to parse JSON from content: ${cleanedContent}. Original JSON: ${content}`);
1729
- throw new Error('Unable to parse JSON response');
1749
+ throw new Error("Unable to parse JSON response");
1730
1750
  }
1731
1751
  else {
1732
1752
  return content;
@@ -2320,13 +2340,15 @@ const isRetryableLLMError = (error) => {
2320
2340
  if (error instanceof Error) {
2321
2341
  const message = error.message;
2322
2342
  // Retry on rate limits (429)
2323
- if (message.includes('429') || message.includes('rate limit') || message.includes('Rate limit')) {
2343
+ if (message.includes("429") ||
2344
+ message.includes("rate limit") ||
2345
+ message.includes("Rate limit")) {
2324
2346
  return true;
2325
2347
  }
2326
2348
  // Retry on transient body-corruption 400s. Match the exact OpenAI error
2327
2349
  // string to avoid retrying genuine client-side validation 400s (which
2328
2350
  // would re-fail forever and waste retry budget).
2329
- if (message.includes('could not parse the JSON body of your request')) {
2351
+ if (message.includes("could not parse the JSON body of your request")) {
2330
2352
  return true;
2331
2353
  }
2332
2354
  }
@@ -2395,23 +2417,26 @@ function isReasoningModel(model) {
2395
2417
  * @throws Error if the response format is invalid or incompatible.
2396
2418
  */
2397
2419
  function getResponseFormatOption(responseFormat, normalizedModel) {
2398
- if (responseFormat === 'text') {
2399
- return { type: 'text' };
2420
+ if (responseFormat === "text") {
2421
+ return { type: "text" };
2400
2422
  }
2401
- if (responseFormat === 'json') {
2402
- return supportsJsonMode(normalizedModel) ? { type: 'json_object' } : { type: 'text' };
2423
+ if (responseFormat === "json") {
2424
+ return supportsJsonMode(normalizedModel)
2425
+ ? { type: "json_object" }
2426
+ : { type: "text" };
2403
2427
  }
2404
- if (typeof responseFormat === 'object' && responseFormat.type === 'json_schema') {
2428
+ if (typeof responseFormat === "object" &&
2429
+ responseFormat.type === "json_schema") {
2405
2430
  if (!isStructuredOutputCompatibleModel(normalizedModel)) {
2406
2431
  throw new Error(`${normalizedModel} is not compatible with structured outputs / json object.`);
2407
2432
  }
2408
2433
  return {
2409
- type: 'json_schema',
2434
+ type: "json_schema",
2410
2435
  json_schema: {
2411
- type: 'object',
2436
+ type: "object",
2412
2437
  properties: responseFormat.schema.properties,
2413
2438
  required: responseFormat.schema.required || [],
2414
- name: 'Response',
2439
+ name: "Response",
2415
2440
  },
2416
2441
  };
2417
2442
  }
@@ -2470,7 +2495,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2470
2495
  const normalizedModel = normalizeModelName(options.model || DEFAULT_MODEL);
2471
2496
  const apiKey = options.apiKey || getSecrets().openai.apiKey;
2472
2497
  if (!apiKey) {
2473
- throw new Error('OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set');
2498
+ throw new Error("OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set");
2474
2499
  }
2475
2500
  const openai = new OpenAI({
2476
2501
  apiKey: apiKey,
@@ -2480,7 +2505,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2480
2505
  // Add developer prompt if present
2481
2506
  if (options.developerPrompt && supportsDeveloperPrompt(normalizedModel)) {
2482
2507
  messages.push({
2483
- role: 'developer',
2508
+ role: "developer",
2484
2509
  content: options.developerPrompt,
2485
2510
  });
2486
2511
  }
@@ -2489,15 +2514,15 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2489
2514
  messages.push(...options.context);
2490
2515
  }
2491
2516
  // Add user content
2492
- if (typeof content === 'string') {
2517
+ if (typeof content === "string") {
2493
2518
  messages.push({
2494
- role: 'user',
2519
+ role: "user",
2495
2520
  content,
2496
2521
  });
2497
2522
  }
2498
2523
  else {
2499
2524
  messages.push({
2500
- role: 'user',
2525
+ role: "user",
2501
2526
  content,
2502
2527
  });
2503
2528
  }
@@ -2511,7 +2536,8 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2511
2536
  queryOptions.tools = options.tools;
2512
2537
  }
2513
2538
  // Only include temperature if the model supports it
2514
- if (options.temperature !== undefined && supportsTemperature(normalizedModel)) {
2539
+ if (options.temperature !== undefined &&
2540
+ supportsTemperature(normalizedModel)) {
2515
2541
  queryOptions.temperature = options.temperature;
2516
2542
  }
2517
2543
  // Only include max_completion_tokens when specified
@@ -2521,7 +2547,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2521
2547
  // Only set response_format when it's not the default 'text' type
2522
2548
  // (sending response_format: {type: 'text'} is redundant and may cause issues)
2523
2549
  const responseFormatOption = getResponseFormatOption(responseFormat, normalizedModel);
2524
- if (responseFormatOption.type !== 'text') {
2550
+ if (responseFormatOption.type !== "text") {
2525
2551
  queryOptions.response_format = responseFormatOption;
2526
2552
  }
2527
2553
  let completion;
@@ -2542,15 +2568,19 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2542
2568
  // for production prompts containing sensitive context.
2543
2569
  const errorMessage = error instanceof Error ? error.message : String(error);
2544
2570
  const totalContentChars = messages.reduce((sum, msg) => {
2545
- if (typeof msg.content === 'string')
2571
+ if (typeof msg.content === "string")
2546
2572
  return sum + msg.content.length;
2547
2573
  if (Array.isArray(msg.content)) {
2548
- return sum + msg.content.reduce((s, part) => {
2549
- if (typeof part === 'object' && part !== null && 'text' in part && typeof part.text === 'string') {
2550
- return s + part.text.length;
2551
- }
2552
- return s;
2553
- }, 0);
2574
+ return (sum +
2575
+ msg.content.reduce((s, part) => {
2576
+ if (typeof part === "object" &&
2577
+ part !== null &&
2578
+ "text" in part &&
2579
+ typeof part.text === "string") {
2580
+ return s + part.text.length;
2581
+ }
2582
+ return s;
2583
+ }, 0));
2554
2584
  }
2555
2585
  return sum;
2556
2586
  }, 0);
@@ -2577,7 +2607,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2577
2607
  const cachedTokens = completion.usage?.prompt_tokens_details?.cached_tokens ?? 0;
2578
2608
  const response = {
2579
2609
  id: completion.id,
2580
- content: completion.choices[0]?.message?.content || '',
2610
+ content: completion.choices[0]?.message?.content || "",
2581
2611
  tool_calls: completion.choices[0]?.message?.tool_calls,
2582
2612
  usage: {
2583
2613
  prompt_tokens: completion.usage?.prompt_tokens ?? 0,
@@ -2587,7 +2617,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2587
2617
  },
2588
2618
  system_fingerprint: completion.system_fingerprint,
2589
2619
  service_tier: options.service_tier,
2590
- provider: 'openai',
2620
+ provider: "openai",
2591
2621
  model: normalizedModel,
2592
2622
  };
2593
2623
  return response;
@@ -2600,7 +2630,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2600
2630
  * @param options The options for the LLM call. Defaults to an empty object.
2601
2631
  * @return A promise that resolves to the response from the LLM.
2602
2632
  */
2603
- const makeOpenAIChatCompletionCall = async (content, responseFormat = 'text', options = {}) => {
2633
+ const makeOpenAIChatCompletionCall = async (content, responseFormat = "text", options = {}) => {
2604
2634
  const mergedOptions = {
2605
2635
  ...DEFAULT_OPTIONS$1,
2606
2636
  ...options,
@@ -2609,7 +2639,7 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = 'text', op
2609
2639
  // Track cost in the global cost tracker. Pass cached tokens through so the
2610
2640
  // tracker applies the discounted cached-input rate (typically ~50% of the
2611
2641
  // standard input rate) instead of billing every input token at full price.
2612
- getLLMCostTracker().trackUsage('openai', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens);
2642
+ getLLMCostTracker().trackUsage("openai", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens);
2613
2643
  // Handle tool calls differently
2614
2644
  if (completion.tool_calls && completion.tool_calls.length > 0) {
2615
2645
  const toolCallResponse = {
@@ -2625,10 +2655,10 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = 'text', op
2625
2655
  prompt_tokens: completion.usage.prompt_tokens,
2626
2656
  completion_tokens: completion.usage.completion_tokens,
2627
2657
  reasoning_tokens: 0,
2628
- provider: 'openai',
2658
+ provider: "openai",
2629
2659
  model: completion.model,
2630
2660
  cached_tokens: completion.usage.cached_tokens,
2631
- cost: calculateCost('openai', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
2661
+ cost: calculateCost("openai", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
2632
2662
  },
2633
2663
  tool_calls: completion.tool_calls,
2634
2664
  };
@@ -2636,7 +2666,7 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = 'text', op
2636
2666
  // Handle regular responses
2637
2667
  const parsedResponse = await parseResponse(completion.content, responseFormat);
2638
2668
  if (parsedResponse === null) {
2639
- throw new Error('Failed to parse response');
2669
+ throw new Error("Failed to parse response");
2640
2670
  }
2641
2671
  return {
2642
2672
  response: parsedResponse,
@@ -2644,10 +2674,10 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = 'text', op
2644
2674
  prompt_tokens: completion.usage.prompt_tokens,
2645
2675
  completion_tokens: completion.usage.completion_tokens,
2646
2676
  reasoning_tokens: 0,
2647
- provider: 'openai',
2677
+ provider: "openai",
2648
2678
  model: completion.model,
2649
2679
  cached_tokens: completion.usage.cached_tokens,
2650
- cost: calculateCost('openai', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
2680
+ cost: calculateCost("openai", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
2651
2681
  },
2652
2682
  tool_calls: completion.tool_calls,
2653
2683
  };
@@ -2682,7 +2712,7 @@ const makeResponsesAPICall = async (input, options = {}) => {
2682
2712
  const normalizedModel = normalizeModelName(options.model || DEFAULT_MODEL);
2683
2713
  const apiKey = options.apiKey || getSecrets().openai.apiKey;
2684
2714
  if (!apiKey) {
2685
- throw new Error('OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set');
2715
+ throw new Error("OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set");
2686
2716
  }
2687
2717
  const openai = new OpenAI({
2688
2718
  apiKey: apiKey,
@@ -2706,20 +2736,20 @@ const makeResponsesAPICall = async (input, options = {}) => {
2706
2736
  // (the equivalent of Chat Completions' `prompt_tokens_details.cached_tokens`).
2707
2737
  const responsesCachedTokens = response.usage?.input_tokens_details?.cached_tokens || 0;
2708
2738
  // Track cost in the global cost tracker
2709
- getLLMCostTracker().trackUsage('openai', normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens);
2739
+ getLLMCostTracker().trackUsage("openai", normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens);
2710
2740
  // Extract tool calls from the output
2711
2741
  const toolCalls = response.output
2712
- ?.filter((item) => item.type === 'function_call')
2742
+ ?.filter((item) => item.type === "function_call")
2713
2743
  .map((toolCall) => ({
2714
2744
  id: toolCall.call_id,
2715
- type: 'function',
2745
+ type: "function",
2716
2746
  function: {
2717
2747
  name: toolCall.name,
2718
2748
  arguments: toolCall.arguments,
2719
2749
  },
2720
2750
  }));
2721
2751
  // Extract code interpreter outputs if present
2722
- const codeInterpreterCalls = response.output?.filter((item) => item.type === 'code_interpreter_call');
2752
+ const codeInterpreterCalls = response.output?.filter((item) => item.type === "code_interpreter_call");
2723
2753
  let codeInterpreterOutputs = undefined;
2724
2754
  if (codeInterpreterCalls && codeInterpreterCalls.length > 0) {
2725
2755
  // Each code_interpreter_call has an 'outputs' array property
@@ -2745,32 +2775,34 @@ const makeResponsesAPICall = async (input, options = {}) => {
2745
2775
  prompt_tokens: response.usage?.input_tokens || 0,
2746
2776
  completion_tokens: response.usage?.output_tokens || 0,
2747
2777
  reasoning_tokens: response.usage?.output_tokens_details?.reasoning_tokens || 0,
2748
- provider: 'openai',
2778
+ provider: "openai",
2749
2779
  model: normalizedModel,
2750
2780
  cached_tokens: responsesCachedTokens,
2751
- cost: calculateCost('openai', normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens),
2781
+ cost: calculateCost("openai", normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens),
2752
2782
  },
2753
2783
  tool_calls: toolCalls,
2754
- ...(codeInterpreterOutputs ? { code_interpreter_outputs: codeInterpreterOutputs } : {}),
2784
+ ...(codeInterpreterOutputs
2785
+ ? { code_interpreter_outputs: codeInterpreterOutputs }
2786
+ : {}),
2755
2787
  };
2756
2788
  }
2757
2789
  // Extract text content from the response output
2758
2790
  const textContent = response.output
2759
- ?.filter((item) => item.type === 'message')
2791
+ ?.filter((item) => item.type === "message")
2760
2792
  .map((item) => item.content
2761
- .filter((content) => content.type === 'output_text')
2793
+ .filter((content) => content.type === "output_text")
2762
2794
  .map((content) => content.text)
2763
- .join(''))
2764
- .join('') || '';
2795
+ .join(""))
2796
+ .join("") || "";
2765
2797
  // Determine the format for parsing the response
2766
- let parsingFormat = 'text';
2767
- if (requestBody.text?.format?.type === 'json_object') {
2768
- parsingFormat = 'json';
2798
+ let parsingFormat = "text";
2799
+ if (requestBody.text?.format?.type === "json_object") {
2800
+ parsingFormat = "json";
2769
2801
  }
2770
2802
  // Handle regular responses
2771
2803
  const parsedResponse = await parseResponse(textContent, parsingFormat);
2772
2804
  if (parsedResponse === null) {
2773
- throw new Error('Failed to parse response from Responses API');
2805
+ throw new Error("Failed to parse response from Responses API");
2774
2806
  }
2775
2807
  return {
2776
2808
  response: parsedResponse,
@@ -2778,13 +2810,15 @@ const makeResponsesAPICall = async (input, options = {}) => {
2778
2810
  prompt_tokens: response.usage?.input_tokens || 0,
2779
2811
  completion_tokens: response.usage?.output_tokens || 0,
2780
2812
  reasoning_tokens: response.usage?.output_tokens_details?.reasoning_tokens || 0,
2781
- provider: 'openai',
2813
+ provider: "openai",
2782
2814
  model: normalizedModel,
2783
2815
  cached_tokens: responsesCachedTokens,
2784
- cost: calculateCost('openai', normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens),
2816
+ cost: calculateCost("openai", normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens),
2785
2817
  },
2786
2818
  tool_calls: toolCalls,
2787
- ...(codeInterpreterOutputs ? { code_interpreter_outputs: codeInterpreterOutputs } : {}),
2819
+ ...(codeInterpreterOutputs
2820
+ ? { code_interpreter_outputs: codeInterpreterOutputs }
2821
+ : {}),
2788
2822
  };
2789
2823
  };
2790
2824
 
@@ -8094,12 +8128,12 @@ function sanitizeObject(obj, sensitiveFields = ['apiKey', 'token', 'secret', 'pa
8094
8128
  const isRetryableAnthropicError = (error) => {
8095
8129
  if (error instanceof Error) {
8096
8130
  const message = error.message;
8097
- if (message.includes('429') ||
8098
- message.includes('500') ||
8099
- message.includes('503') ||
8100
- message.includes('529') ||
8101
- message.includes('rate limit') ||
8102
- message.includes('overloaded')) {
8131
+ if (message.includes("429") ||
8132
+ message.includes("500") ||
8133
+ message.includes("503") ||
8134
+ message.includes("529") ||
8135
+ message.includes("rate limit") ||
8136
+ message.includes("overloaded")) {
8103
8137
  return true;
8104
8138
  }
8105
8139
  }
@@ -8114,8 +8148,11 @@ const isRetryableAnthropicError = (error) => {
8114
8148
  function translateToolsToAnthropic(tools) {
8115
8149
  return tools.map((tool) => ({
8116
8150
  name: tool.function.name,
8117
- description: tool.function.description || '',
8118
- input_schema: (tool.function.parameters || { type: 'object', properties: {} }),
8151
+ description: tool.function.description || "",
8152
+ input_schema: (tool.function.parameters || {
8153
+ type: "object",
8154
+ properties: {},
8155
+ }),
8119
8156
  }));
8120
8157
  }
8121
8158
  /**
@@ -8128,27 +8165,31 @@ function translateContextToAnthropic(context) {
8128
8165
  const systemParts = [];
8129
8166
  const messages = [];
8130
8167
  for (const msg of context) {
8131
- if (msg.role === 'system' || msg.role === 'developer') {
8132
- if (typeof msg.content === 'string') {
8168
+ if (msg.role === "system" || msg.role === "developer") {
8169
+ if (typeof msg.content === "string") {
8133
8170
  systemParts.push(msg.content);
8134
8171
  }
8135
8172
  continue;
8136
8173
  }
8137
- if (msg.role === 'user') {
8138
- const content = typeof msg.content === 'string'
8174
+ if (msg.role === "user") {
8175
+ const content = typeof msg.content === "string"
8139
8176
  ? msg.content
8140
8177
  : JSON.stringify(msg.content);
8141
- messages.push({ role: 'user', content });
8178
+ messages.push({ role: "user", content });
8142
8179
  continue;
8143
8180
  }
8144
- if (msg.role === 'assistant') {
8181
+ if (msg.role === "assistant") {
8145
8182
  const assistantMsg = msg;
8146
8183
  // If the assistant message has tool_calls, translate to Anthropic tool_use blocks
8147
8184
  if (assistantMsg.tool_calls && assistantMsg.tool_calls.length > 0) {
8148
8185
  const contentBlocks = [];
8149
8186
  // Include text content if present
8150
- if (typeof assistantMsg.content === 'string' && assistantMsg.content.trim()) {
8151
- contentBlocks.push({ type: 'text', text: assistantMsg.content });
8187
+ if (typeof assistantMsg.content === "string" &&
8188
+ assistantMsg.content.trim()) {
8189
+ contentBlocks.push({
8190
+ type: "text",
8191
+ text: assistantMsg.content,
8192
+ });
8152
8193
  }
8153
8194
  // Translate each tool_call to a tool_use block
8154
8195
  for (const tc of assistantMsg.tool_calls) {
@@ -8160,32 +8201,36 @@ function translateContextToAnthropic(context) {
8160
8201
  input = { raw: tc.function.arguments };
8161
8202
  }
8162
8203
  contentBlocks.push({
8163
- type: 'tool_use',
8204
+ type: "tool_use",
8164
8205
  id: tc.id,
8165
8206
  name: tc.function.name,
8166
8207
  input,
8167
8208
  });
8168
8209
  }
8169
- messages.push({ role: 'assistant', content: contentBlocks });
8210
+ messages.push({ role: "assistant", content: contentBlocks });
8170
8211
  }
8171
8212
  else {
8172
- const content = typeof assistantMsg.content === 'string'
8213
+ const content = typeof assistantMsg.content === "string"
8173
8214
  ? assistantMsg.content
8174
8215
  : JSON.stringify(assistantMsg.content);
8175
- messages.push({ role: 'assistant', content });
8216
+ messages.push({ role: "assistant", content });
8176
8217
  }
8177
8218
  continue;
8178
8219
  }
8179
- if (msg.role === 'tool') {
8220
+ if (msg.role === "tool") {
8180
8221
  // Anthropic expects tool results as user messages with tool_result content blocks
8181
8222
  const toolMsg = msg;
8182
8223
  messages.push({
8183
- role: 'user',
8184
- content: [{
8185
- type: 'tool_result',
8224
+ role: "user",
8225
+ content: [
8226
+ {
8227
+ type: "tool_result",
8186
8228
  tool_use_id: toolMsg.tool_call_id,
8187
- content: typeof toolMsg.content === 'string' ? toolMsg.content : JSON.stringify(toolMsg.content),
8188
- }],
8229
+ content: typeof toolMsg.content === "string"
8230
+ ? toolMsg.content
8231
+ : JSON.stringify(toolMsg.content),
8232
+ },
8233
+ ],
8189
8234
  });
8190
8235
  continue;
8191
8236
  }
@@ -8206,13 +8251,13 @@ function translateContextToAnthropic(context) {
8206
8251
  merged.push({ role: msg.role, content: toContentBlocks(msg.content) });
8207
8252
  }
8208
8253
  }
8209
- return { messages: merged, systemText: systemParts.join('\n\n') };
8254
+ return { messages: merged, systemText: systemParts.join("\n\n") };
8210
8255
  }
8211
8256
  /** Convert string or content block array to a uniform content block array. */
8212
8257
  function toContentBlocks(content) {
8213
- if (typeof content === 'string') {
8258
+ if (typeof content === "string") {
8214
8259
  const textBlock = {
8215
- type: 'text',
8260
+ type: "text",
8216
8261
  text: content,
8217
8262
  citations: null,
8218
8263
  };
@@ -8232,11 +8277,11 @@ function toContentBlocks(content) {
8232
8277
  * @param options The options for the LLM call.
8233
8278
  * @return A promise that resolves to the response from the Anthropic API.
8234
8279
  */
8235
- async function makeAnthropicCall(content, responseFormat = 'text', options = {}) {
8236
- const model = (options.model || 'claude-sonnet-4-6');
8280
+ async function makeAnthropicCall(content, responseFormat = "text", options = {}) {
8281
+ const model = (options.model || "claude-sonnet-4-6");
8237
8282
  const apiKey = options.apiKey || getSecrets().anthropic.apiKey;
8238
8283
  if (!apiKey) {
8239
- throw new Error('Anthropic API key is not provided and ANTHROPIC_API_KEY environment variable is not set');
8284
+ throw new Error("Anthropic API key is not provided and ANTHROPIC_API_KEY environment variable is not set");
8240
8285
  }
8241
8286
  const client = new Anthropic({
8242
8287
  apiKey,
@@ -8248,8 +8293,10 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
8248
8293
  systemParts.push(options.developerPrompt);
8249
8294
  }
8250
8295
  // If JSON response is requested, instruct the model via system prompt
8251
- if (responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat.type === 'json_schema')) {
8252
- systemParts.push('You MUST respond with valid JSON only. No markdown, no code blocks, no explanatory text — just the raw JSON object or array.');
8296
+ if (responseFormat === "json" ||
8297
+ (typeof responseFormat === "object" &&
8298
+ responseFormat.type === "json_schema")) {
8299
+ systemParts.push("You MUST respond with valid JSON only. No markdown, no code blocks, no explanatory text — just the raw JSON object or array.");
8253
8300
  }
8254
8301
  // Build messages array
8255
8302
  const messages = [];
@@ -8262,7 +8309,7 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
8262
8309
  messages.push(...translated.messages);
8263
8310
  }
8264
8311
  // Add user content
8265
- messages.push({ role: 'user', content });
8312
+ messages.push({ role: "user", content });
8266
8313
  // Build request params
8267
8314
  const requestParams = {
8268
8315
  model,
@@ -8271,7 +8318,7 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
8271
8318
  };
8272
8319
  // Add system prompt if present
8273
8320
  if (systemParts.length > 0) {
8274
- requestParams.system = systemParts.join('\n\n');
8321
+ requestParams.system = systemParts.join("\n\n");
8275
8322
  }
8276
8323
  // Add temperature if specified
8277
8324
  if (options.temperature !== undefined) {
@@ -8294,14 +8341,14 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
8294
8341
  const inputTokens = response.usage.input_tokens;
8295
8342
  const outputTokens = response.usage.output_tokens;
8296
8343
  // Track cost
8297
- getLLMCostTracker().trackUsage('anthropic', model, inputTokens, outputTokens);
8344
+ getLLMCostTracker().trackUsage("anthropic", model, inputTokens, outputTokens);
8298
8345
  // Extract tool use blocks
8299
- const toolUseBlocks = response.content.filter((block) => block.type === 'tool_use');
8346
+ const toolUseBlocks = response.content.filter((block) => block.type === "tool_use");
8300
8347
  if (toolUseBlocks.length > 0) {
8301
8348
  // Translate Anthropic tool_use to OpenAI tool_calls format
8302
8349
  const toolCalls = toolUseBlocks.map((block) => ({
8303
8350
  id: block.id,
8304
- type: 'function',
8351
+ type: "function",
8305
8352
  function: {
8306
8353
  name: block.name,
8307
8354
  arguments: JSON.stringify(block.input),
@@ -8320,22 +8367,22 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
8320
8367
  prompt_tokens: inputTokens,
8321
8368
  completion_tokens: outputTokens,
8322
8369
  reasoning_tokens: 0,
8323
- provider: 'anthropic',
8370
+ provider: "anthropic",
8324
8371
  model,
8325
- cost: calculateCost('anthropic', model, inputTokens, outputTokens),
8372
+ cost: calculateCost("anthropic", model, inputTokens, outputTokens),
8326
8373
  },
8327
8374
  tool_calls: toolCalls,
8328
8375
  };
8329
8376
  }
8330
8377
  // Extract text content
8331
8378
  const textContent = response.content
8332
- .filter((block) => block.type === 'text')
8379
+ .filter((block) => block.type === "text")
8333
8380
  .map((block) => block.text)
8334
- .join('');
8381
+ .join("");
8335
8382
  // Parse response
8336
8383
  const parsedResponse = await parseResponse(textContent, responseFormat);
8337
8384
  if (parsedResponse === null) {
8338
- throw new Error('Failed to parse Anthropic response');
8385
+ throw new Error("Failed to parse Anthropic response");
8339
8386
  }
8340
8387
  return {
8341
8388
  response: parsedResponse,
@@ -8343,9 +8390,9 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
8343
8390
  prompt_tokens: inputTokens,
8344
8391
  completion_tokens: outputTokens,
8345
8392
  reasoning_tokens: 0,
8346
- provider: 'anthropic',
8393
+ provider: "anthropic",
8347
8394
  model,
8348
- cost: calculateCost('anthropic', model, inputTokens, outputTokens),
8395
+ cost: calculateCost("anthropic", model, inputTokens, outputTokens),
8349
8396
  },
8350
8397
  };
8351
8398
  }
@@ -8367,7 +8414,9 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
8367
8414
  const isRetryableError = (error) => {
8368
8415
  if (error instanceof Error) {
8369
8416
  const message = error.message;
8370
- if (message.includes('429') || message.includes('rate limit') || message.includes('Rate limit')) {
8417
+ if (message.includes("429") ||
8418
+ message.includes("rate limit") ||
8419
+ message.includes("Rate limit")) {
8371
8420
  return true;
8372
8421
  }
8373
8422
  }
@@ -8381,14 +8430,16 @@ function resolveApiKey(provider, optionsApiKey) {
8381
8430
  if (optionsApiKey)
8382
8431
  return optionsApiKey;
8383
8432
  const secrets = getSecrets();
8384
- const pathParts = provider.apiKeySecretPath.split('.');
8433
+ const pathParts = provider.apiKeySecretPath.split(".");
8385
8434
  let current = secrets;
8386
8435
  for (const part of pathParts) {
8387
- if (current === null || current === undefined || typeof current !== 'object')
8388
- return '';
8436
+ if (current === null ||
8437
+ current === undefined ||
8438
+ typeof current !== "object")
8439
+ return "";
8389
8440
  current = current[part];
8390
8441
  }
8391
- return (typeof current === 'string' ? current : '') || '';
8442
+ return (typeof current === "string" ? current : "") || "";
8392
8443
  }
8393
8444
  /**
8394
8445
  * Makes a call to an OpenAI-compatible provider (Kimi, Qwen, or any future provider).
@@ -8402,12 +8453,12 @@ function resolveApiKey(provider, optionsApiKey) {
8402
8453
  * @param providerName The provider key in OPENAI_COMPATIBLE_PROVIDERS.
8403
8454
  * @return A promise that resolves to the response from the provider.
8404
8455
  */
8405
- async function makeOpenAICompatibleCall(content, responseFormat = 'text', options = {}, providerName) {
8456
+ async function makeOpenAICompatibleCall(content, responseFormat = "text", options = {}, providerName) {
8406
8457
  const providerConfig = OPENAI_COMPATIBLE_PROVIDERS[providerName];
8407
8458
  if (!providerConfig) {
8408
8459
  throw new Error(`Unknown OpenAI-compatible provider: ${providerName}`);
8409
8460
  }
8410
- const model = normalizeModelName(options.model || '');
8461
+ const model = normalizeModelName(options.model || "");
8411
8462
  const apiKey = resolveApiKey(providerConfig, options.apiKey);
8412
8463
  if (!apiKey) {
8413
8464
  throw new Error(`${providerConfig.name} API key is not provided and ${providerConfig.apiKeyEnvVar} environment variable is not set`);
@@ -8420,31 +8471,36 @@ async function makeOpenAICompatibleCall(content, responseFormat = 'text', option
8420
8471
  const messages = [];
8421
8472
  // Add system message with developer prompt if present
8422
8473
  if (options.developerPrompt) {
8423
- messages.push({ role: 'system', content: options.developerPrompt });
8474
+ messages.push({ role: "system", content: options.developerPrompt });
8424
8475
  }
8425
8476
  // Add context if present
8426
8477
  if (options.context) {
8427
8478
  messages.push(...options.context);
8428
8479
  }
8429
8480
  // Add user content
8430
- if (typeof content === 'string') {
8431
- messages.push({ role: 'user', content });
8481
+ if (typeof content === "string") {
8482
+ messages.push({ role: "user", content });
8432
8483
  }
8433
8484
  else {
8434
- messages.push({ role: 'user', content });
8485
+ messages.push({ role: "user", content });
8435
8486
  }
8436
8487
  // Determine if the model supports JSON mode
8437
8488
  const supportsJson = isValidModel(model) && getModelCapabilities(model).supportsJson;
8438
8489
  // If JSON response format, add hint to system prompt
8439
- if ((responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat.type === 'json_schema')) &&
8490
+ if ((responseFormat === "json" ||
8491
+ (typeof responseFormat === "object" &&
8492
+ responseFormat.type === "json_schema")) &&
8440
8493
  supportsJson) {
8441
- if (!messages.some((m) => m.role === 'system')) {
8442
- messages.unshift({ role: 'system', content: 'Please respond in valid JSON format.' });
8494
+ if (!messages.some((m) => m.role === "system")) {
8495
+ messages.unshift({
8496
+ role: "system",
8497
+ content: "Please respond in valid JSON format.",
8498
+ });
8443
8499
  }
8444
8500
  else {
8445
- const systemMsgIndex = messages.findIndex((m) => m.role === 'system');
8501
+ const systemMsgIndex = messages.findIndex((m) => m.role === "system");
8446
8502
  const systemMsg = messages[systemMsgIndex];
8447
- if (typeof systemMsg.content === 'string') {
8503
+ if (typeof systemMsg.content === "string") {
8448
8504
  messages[systemMsgIndex] = {
8449
8505
  ...systemMsg,
8450
8506
  content: `${systemMsg.content} Please respond in valid JSON format.`,
@@ -8457,8 +8513,8 @@ async function makeOpenAICompatibleCall(content, responseFormat = 'text', option
8457
8513
  messages,
8458
8514
  };
8459
8515
  // Add response format if JSON is supported
8460
- if (responseFormat !== 'text' && supportsJson) {
8461
- queryOptions.response_format = { type: 'json_object' };
8516
+ if (responseFormat !== "text" && supportsJson) {
8517
+ queryOptions.response_format = { type: "json_object" };
8462
8518
  }
8463
8519
  // Temperature
8464
8520
  if (options.temperature !== undefined) {
@@ -8516,7 +8572,7 @@ async function makeOpenAICompatibleCall(content, responseFormat = 'text', option
8516
8572
  };
8517
8573
  }
8518
8574
  // Handle regular responses
8519
- const textContent = completion.choices[0]?.message?.content || '';
8575
+ const textContent = completion.choices[0]?.message?.content || "";
8520
8576
  const parsedResponse = await parseResponse(textContent, responseFormat);
8521
8577
  if (parsedResponse === null) {
8522
8578
  throw new Error(`Failed to parse ${providerConfig.name} response`);
@@ -8808,7 +8864,9 @@ const isRetryableDeepseekError = (error) => {
8808
8864
  if (error instanceof Error) {
8809
8865
  const message = error.message;
8810
8866
  // Retry only on rate limits (429)
8811
- if (message.includes('429') || message.includes('rate limit') || message.includes('Rate limit')) {
8867
+ if (message.includes("429") ||
8868
+ message.includes("rate limit") ||
8869
+ message.includes("Rate limit")) {
8812
8870
  return true;
8813
8871
  }
8814
8872
  }
@@ -8818,7 +8876,7 @@ const isRetryableDeepseekError = (error) => {
8818
8876
  * Default options for Deepseek API calls
8819
8877
  */
8820
8878
  const DEFAULT_DEEPSEEK_OPTIONS = {
8821
- defaultModel: 'deepseek-chat',
8879
+ defaultModel: "deepseek-chat",
8822
8880
  };
8823
8881
  /**
8824
8882
  * Checks if the given model is a supported Deepseek model
@@ -8830,7 +8888,7 @@ const isSupportedDeepseekModel = (model) => {
8830
8888
  return false;
8831
8889
  }
8832
8890
  const capabilities = getModelCapabilities(model);
8833
- return capabilities.provider === 'deepseek';
8891
+ return capabilities.provider === "deepseek";
8834
8892
  };
8835
8893
  /**
8836
8894
  * Checks if the given Deepseek model supports JSON output format
@@ -8863,17 +8921,19 @@ const supportsToolCalling = (model) => {
8863
8921
  */
8864
8922
  function getDeepseekResponseFormatOption(responseFormat, model) {
8865
8923
  // Check if the model supports JSON output
8866
- if (responseFormat !== 'text' && !supportsJsonOutput(model)) {
8924
+ if (responseFormat !== "text" && !supportsJsonOutput(model)) {
8867
8925
  getLumicLogger().warn(`Model ${model} does not support JSON output. Using text format instead.`);
8868
- return { type: 'text' };
8926
+ return { type: "text" };
8869
8927
  }
8870
- if (responseFormat === 'text') {
8871
- return { type: 'text' };
8928
+ if (responseFormat === "text") {
8929
+ return { type: "text" };
8872
8930
  }
8873
- if (responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat.type === 'json_schema')) {
8874
- return { type: 'json_object' };
8931
+ if (responseFormat === "json" ||
8932
+ (typeof responseFormat === "object" &&
8933
+ responseFormat.type === "json_schema")) {
8934
+ return { type: "json_object" };
8875
8935
  }
8876
- return { type: 'text' };
8936
+ return { type: "text" };
8877
8937
  }
8878
8938
  /**
8879
8939
  * Creates a completion using the Deepseek API
@@ -8892,24 +8952,26 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
8892
8952
  throw new Error(`Unsupported Deepseek model: ${normalizedModel}. Please use 'deepseek-chat' or 'deepseek-reasoner'.`);
8893
8953
  }
8894
8954
  // Check if tools are requested with a model that doesn't support them
8895
- if (options.tools && options.tools.length > 0 && !supportsToolCalling(normalizedModel)) {
8955
+ if (options.tools &&
8956
+ options.tools.length > 0 &&
8957
+ !supportsToolCalling(normalizedModel)) {
8896
8958
  throw new Error(`Model ${normalizedModel} does not support tool calling.`);
8897
8959
  }
8898
8960
  const apiKey = options.apiKey || getSecrets().deepseek.apiKey;
8899
8961
  if (!apiKey) {
8900
- throw new Error('Deepseek API key is not provided and DEEPSEEK_API_KEY environment variable is not set');
8962
+ throw new Error("Deepseek API key is not provided and DEEPSEEK_API_KEY environment variable is not set");
8901
8963
  }
8902
8964
  // Initialize OpenAI client with Deepseek API URL
8903
8965
  const openai = new OpenAI({
8904
8966
  apiKey,
8905
- baseURL: 'https://api.deepseek.com',
8967
+ baseURL: "https://api.deepseek.com",
8906
8968
  timeout: options.timeout ?? LLM_TIMEOUT_MS,
8907
8969
  });
8908
8970
  const messages = [];
8909
8971
  // Add system message with developer prompt if present
8910
8972
  if (options.developerPrompt) {
8911
8973
  messages.push({
8912
- role: 'system',
8974
+ role: "system",
8913
8975
  content: options.developerPrompt,
8914
8976
  });
8915
8977
  }
@@ -8918,33 +8980,35 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
8918
8980
  messages.push(...options.context);
8919
8981
  }
8920
8982
  // Add user content
8921
- if (typeof content === 'string') {
8983
+ if (typeof content === "string") {
8922
8984
  messages.push({
8923
- role: 'user',
8985
+ role: "user",
8924
8986
  content,
8925
8987
  });
8926
8988
  }
8927
8989
  else {
8928
8990
  messages.push({
8929
- role: 'user',
8991
+ role: "user",
8930
8992
  content,
8931
8993
  });
8932
8994
  }
8933
8995
  // If JSON response format, include a hint in the system prompt
8934
- if ((responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat.type === 'json_schema'))
8935
- && supportsJsonOutput(normalizedModel)) {
8996
+ if ((responseFormat === "json" ||
8997
+ (typeof responseFormat === "object" &&
8998
+ responseFormat.type === "json_schema")) &&
8999
+ supportsJsonOutput(normalizedModel)) {
8936
9000
  // If there's no system message yet, add one
8937
- if (!messages.some(m => m.role === 'system')) {
9001
+ if (!messages.some((m) => m.role === "system")) {
8938
9002
  messages.unshift({
8939
- role: 'system',
8940
- content: 'Please respond in valid JSON format.',
9003
+ role: "system",
9004
+ content: "Please respond in valid JSON format.",
8941
9005
  });
8942
9006
  }
8943
9007
  else {
8944
9008
  // Append to existing system message
8945
- const systemMsgIndex = messages.findIndex(m => m.role === 'system');
9009
+ const systemMsgIndex = messages.findIndex((m) => m.role === "system");
8946
9010
  const systemMsg = messages[systemMsgIndex];
8947
- if (typeof systemMsg.content === 'string') {
9011
+ if (typeof systemMsg.content === "string") {
8948
9012
  messages[systemMsgIndex] = {
8949
9013
  ...systemMsg,
8950
9014
  content: `${systemMsg.content} Please respond in valid JSON format.`,
@@ -8985,7 +9049,7 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
8985
9049
  0;
8986
9050
  return {
8987
9051
  id: completion.id,
8988
- content: completion.choices[0]?.message?.content || '',
9052
+ content: completion.choices[0]?.message?.content || "",
8989
9053
  tool_calls: completion.choices[0]?.message?.tool_calls,
8990
9054
  usage: {
8991
9055
  prompt_tokens: completion.usage?.prompt_tokens ?? 0,
@@ -8994,7 +9058,7 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
8994
9058
  cached_tokens: cachedTokens,
8995
9059
  },
8996
9060
  system_fingerprint: completion.system_fingerprint,
8997
- provider: 'deepseek',
9061
+ provider: "deepseek",
8998
9062
  model: normalizedModel,
8999
9063
  };
9000
9064
  }
@@ -9011,7 +9075,7 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
9011
9075
  * @param options Configuration options including model ('deepseek-chat' or 'deepseek-reasoner'), tools, and apiKey.
9012
9076
  * @return A promise that resolves to the response from the Deepseek API.
9013
9077
  */
9014
- const makeDeepseekCall = async (content, responseFormat = 'json', options = {}) => {
9078
+ const makeDeepseekCall = async (content, responseFormat = "json", options = {}) => {
9015
9079
  // Set default model if not provided
9016
9080
  const mergedOptions = {
9017
9081
  ...options,
@@ -9021,17 +9085,17 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
9021
9085
  }
9022
9086
  const modelName = normalizeModelName(mergedOptions.model);
9023
9087
  // Check if the requested response format is compatible with the model
9024
- if (responseFormat !== 'text' && !supportsJsonOutput(modelName)) {
9088
+ if (responseFormat !== "text" && !supportsJsonOutput(modelName)) {
9025
9089
  getLumicLogger().warn(`Model ${modelName} does not support JSON output. Will return error in the response.`);
9026
9090
  return {
9027
9091
  response: {
9028
- error: `Model ${modelName} does not support JSON output format.`
9092
+ error: `Model ${modelName} does not support JSON output format.`,
9029
9093
  },
9030
9094
  usage: {
9031
9095
  prompt_tokens: 0,
9032
9096
  completion_tokens: 0,
9033
9097
  reasoning_tokens: 0,
9034
- provider: 'deepseek',
9098
+ provider: "deepseek",
9035
9099
  model: modelName,
9036
9100
  cached_tokens: 0,
9037
9101
  cost: 0,
@@ -9040,17 +9104,19 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
9040
9104
  };
9041
9105
  }
9042
9106
  // Check if tools are requested with a model that doesn't support them
9043
- if (mergedOptions.tools && mergedOptions.tools.length > 0 && !supportsToolCalling(modelName)) {
9107
+ if (mergedOptions.tools &&
9108
+ mergedOptions.tools.length > 0 &&
9109
+ !supportsToolCalling(modelName)) {
9044
9110
  getLumicLogger().warn(`Model ${modelName} does not support tool calling. Will return error in the response.`);
9045
9111
  return {
9046
9112
  response: {
9047
- error: `Model ${modelName} does not support tool calling.`
9113
+ error: `Model ${modelName} does not support tool calling.`,
9048
9114
  },
9049
9115
  usage: {
9050
9116
  prompt_tokens: 0,
9051
9117
  completion_tokens: 0,
9052
9118
  reasoning_tokens: 0,
9053
- provider: 'deepseek',
9119
+ provider: "deepseek",
9054
9120
  model: modelName,
9055
9121
  cached_tokens: 0,
9056
9122
  cost: 0,
@@ -9062,7 +9128,7 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
9062
9128
  const completion = await createDeepseekCompletion(content, responseFormat, mergedOptions);
9063
9129
  // Track cost in the global cost tracker. Pass cached tokens through so the
9064
9130
  // discounted cached-input pricing tier is applied.
9065
- getLLMCostTracker().trackUsage('deepseek', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens);
9131
+ getLLMCostTracker().trackUsage("deepseek", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens);
9066
9132
  // Handle tool calls similarly to OpenAI
9067
9133
  if (completion.tool_calls && completion.tool_calls.length > 0) {
9068
9134
  const toolCallResponse = {
@@ -9078,10 +9144,10 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
9078
9144
  prompt_tokens: completion.usage.prompt_tokens,
9079
9145
  completion_tokens: completion.usage.completion_tokens,
9080
9146
  reasoning_tokens: 0, // Deepseek doesn't provide reasoning tokens separately
9081
- provider: 'deepseek',
9147
+ provider: "deepseek",
9082
9148
  model: completion.model,
9083
9149
  cached_tokens: completion.usage.cached_tokens,
9084
- cost: calculateCost('deepseek', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
9150
+ cost: calculateCost("deepseek", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
9085
9151
  },
9086
9152
  tool_calls: completion.tool_calls,
9087
9153
  };
@@ -9089,7 +9155,7 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
9089
9155
  // Handle regular responses
9090
9156
  const parsedResponse = await parseResponse(completion.content, responseFormat);
9091
9157
  if (parsedResponse === null) {
9092
- throw new Error('Failed to parse Deepseek response');
9158
+ throw new Error("Failed to parse Deepseek response");
9093
9159
  }
9094
9160
  return {
9095
9161
  response: parsedResponse,
@@ -9097,10 +9163,10 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
9097
9163
  prompt_tokens: completion.usage.prompt_tokens,
9098
9164
  completion_tokens: completion.usage.completion_tokens,
9099
9165
  reasoning_tokens: 0, // Deepseek doesn't provide reasoning tokens separately
9100
- provider: 'deepseek',
9166
+ provider: "deepseek",
9101
9167
  model: completion.model,
9102
9168
  cached_tokens: completion.usage.cached_tokens,
9103
- cost: calculateCost('deepseek', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
9169
+ cost: calculateCost("deepseek", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
9104
9170
  },
9105
9171
  tool_calls: completion.tool_calls,
9106
9172
  };
@@ -9110,13 +9176,13 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
9110
9176
  getLumicLogger().error(`Error in Deepseek API call: ${sanitizeError(error)}`);
9111
9177
  return {
9112
9178
  response: {
9113
- error: sanitizeError(error)
9179
+ error: sanitizeError(error),
9114
9180
  },
9115
9181
  usage: {
9116
9182
  prompt_tokens: 0,
9117
9183
  completion_tokens: 0,
9118
9184
  reasoning_tokens: 0,
9119
- provider: 'deepseek',
9185
+ provider: "deepseek",
9120
9186
  model: modelName,
9121
9187
  cached_tokens: 0,
9122
9188
  cost: 0,
@@ -9225,15 +9291,15 @@ const generateCacheKey = (auth, envVarsCheck = null) => {
9225
9291
  if (auth) {
9226
9292
  return `${auth.AWS_ACCESS_KEY_ID}:${auth.AWS_SECRET_ACCESS_KEY}:${auth.AWS_REGION}`;
9227
9293
  }
9228
- if (envVarsCheck?.source === 'lambda') {
9294
+ if (envVarsCheck?.source === "lambda") {
9229
9295
  // In Lambda, use function name as cache key since it uses IAM role
9230
9296
  const secrets = getSecrets();
9231
- return `lambda:${secrets.aws.lambdaFunctionName || 'default'}`;
9297
+ return `lambda:${secrets.aws.lambdaFunctionName || "default"}`;
9232
9298
  }
9233
9299
  if (envVarsCheck?.vars) {
9234
9300
  return `${envVarsCheck.vars.accessKeyId}:${envVarsCheck.vars.secretAccessKey}:${envVarsCheck.vars.region}`;
9235
9301
  }
9236
- return 'default';
9302
+ return "default";
9237
9303
  };
9238
9304
  const validateEnvironmentVars = () => {
9239
9305
  const secrets = getSecrets();
@@ -9246,37 +9312,37 @@ const validateEnvironmentVars = () => {
9246
9312
  // We're in a Lambda environment
9247
9313
  return {
9248
9314
  isValid: true,
9249
- source: 'lambda',
9250
- vars: { accessKeyId: '', secretAccessKey: '', region: '' } // No need for explicit credentials in Lambda
9315
+ source: "lambda",
9316
+ vars: { accessKeyId: "", secretAccessKey: "", region: "" }, // No need for explicit credentials in Lambda
9251
9317
  };
9252
9318
  }
9253
9319
  else if (areVarsComplete(secrets.aws.myAccessKeyId, secrets.aws.mySecretAccessKey, secrets.aws.myRegion)) {
9254
9320
  return {
9255
9321
  isValid: true,
9256
- source: 'my_prefix',
9322
+ source: "my_prefix",
9257
9323
  vars: {
9258
- accessKeyId: secrets.aws.myAccessKeyId,
9259
- secretAccessKey: secrets.aws.mySecretAccessKey,
9260
- region: secrets.aws.myRegion
9261
- }
9324
+ accessKeyId: secrets.aws.myAccessKeyId ?? "",
9325
+ secretAccessKey: secrets.aws.mySecretAccessKey ?? "",
9326
+ region: secrets.aws.myRegion ?? "",
9327
+ },
9262
9328
  };
9263
9329
  }
9264
9330
  else if (areVarsComplete(secrets.aws.accessKeyId, secrets.aws.secretAccessKey, secrets.aws.region)) {
9265
9331
  return {
9266
9332
  isValid: true,
9267
- source: 'standard',
9333
+ source: "standard",
9268
9334
  vars: {
9269
- accessKeyId: secrets.aws.accessKeyId,
9270
- secretAccessKey: secrets.aws.secretAccessKey,
9271
- region: secrets.aws.region
9272
- }
9335
+ accessKeyId: secrets.aws.accessKeyId ?? "",
9336
+ secretAccessKey: secrets.aws.secretAccessKey ?? "",
9337
+ region: secrets.aws.region ?? "",
9338
+ },
9273
9339
  };
9274
9340
  }
9275
9341
  // If we get here, no complete set of variables was found
9276
9342
  return {
9277
9343
  isValid: false,
9278
9344
  source: null,
9279
- missingVars: []
9345
+ missingVars: [],
9280
9346
  };
9281
9347
  };
9282
9348
  const initialiseAWSClient = (ClientClass, auth = null) => {
@@ -9285,48 +9351,52 @@ const initialiseAWSClient = (ClientClass, auth = null) => {
9285
9351
  const requestTimeout = ClientClass === clientS3.S3Client ? AWS_S3_TIMEOUT_MS : AWS_LAMBDA_TIMEOUT_MS;
9286
9352
  // Case 1: Explicit auth provided
9287
9353
  if (auth) {
9288
- if (typeof auth !== 'object' || auth === null) {
9289
- throw new Error('Auth parameter must be an object');
9354
+ if (typeof auth !== "object" || auth === null) {
9355
+ throw new Error("Auth parameter must be an object");
9290
9356
  }
9291
- const requiredAuthFields = ['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AWS_REGION'];
9292
- const missingFields = requiredAuthFields.filter(field => !auth[field]);
9357
+ const requiredAuthFields = [
9358
+ "AWS_ACCESS_KEY_ID",
9359
+ "AWS_SECRET_ACCESS_KEY",
9360
+ "AWS_REGION",
9361
+ ];
9362
+ const missingFields = requiredAuthFields.filter((field) => !auth[field]);
9293
9363
  if (missingFields.length > 0) {
9294
- throw new Error(`Auth object is missing required fields: ${missingFields.join(', ')}`);
9364
+ throw new Error(`Auth object is missing required fields: ${missingFields.join(", ")}`);
9295
9365
  }
9296
9366
  return new ClientClass({
9297
9367
  region: auth.AWS_REGION,
9298
9368
  credentials: {
9299
9369
  accessKeyId: auth.AWS_ACCESS_KEY_ID,
9300
- secretAccessKey: auth.AWS_SECRET_ACCESS_KEY
9370
+ secretAccessKey: auth.AWS_SECRET_ACCESS_KEY,
9301
9371
  },
9302
9372
  requestHandler: {
9303
- requestTimeout
9304
- }
9373
+ requestTimeout,
9374
+ },
9305
9375
  });
9306
9376
  }
9307
9377
  // Case 2: Check environment variables
9308
9378
  const envVarsCheck = validateEnvironmentVars();
9309
9379
  if (!envVarsCheck.isValid) {
9310
- throw new Error('No valid AWS credentials found in environment variables');
9380
+ throw new Error("No valid AWS credentials found in environment variables");
9311
9381
  }
9312
9382
  // Case 2a: Lambda execution environment
9313
- if (envVarsCheck.source === 'lambda') {
9383
+ if (envVarsCheck.source === "lambda") {
9314
9384
  return new ClientClass({
9315
9385
  requestHandler: {
9316
- requestTimeout
9317
- }
9386
+ requestTimeout,
9387
+ },
9318
9388
  }); // AWS SDK will automatically use Lambda role credentials
9319
9389
  }
9320
9390
  // Case 2b: MY_ prefixed vars or standard AWS vars
9321
9391
  return new ClientClass({
9322
- region: envVarsCheck.vars.region,
9392
+ region: envVarsCheck.vars?.region ?? "",
9323
9393
  credentials: {
9324
- accessKeyId: envVarsCheck.vars.accessKeyId,
9325
- secretAccessKey: envVarsCheck.vars.secretAccessKey
9394
+ accessKeyId: envVarsCheck.vars?.accessKeyId ?? "",
9395
+ secretAccessKey: envVarsCheck.vars?.secretAccessKey ?? "",
9326
9396
  },
9327
9397
  requestHandler: {
9328
- requestTimeout
9329
- }
9398
+ requestTimeout,
9399
+ },
9330
9400
  });
9331
9401
  }
9332
9402
  catch (error) {
@@ -9339,7 +9409,9 @@ const initialiseS3Client = (auth = null) => {
9339
9409
  const envVarsCheck = auth ? null : validateEnvironmentVars();
9340
9410
  const cacheKey = generateCacheKey(auth, envVarsCheck);
9341
9411
  if (s3ClientCache.has(cacheKey)) {
9342
- return s3ClientCache.get(cacheKey);
9412
+ const cached = s3ClientCache.get(cacheKey);
9413
+ if (cached)
9414
+ return cached;
9343
9415
  }
9344
9416
  // Create new client and cache it
9345
9417
  const client = initialiseAWSClient(clientS3.S3Client, auth);
@@ -9351,7 +9423,9 @@ const initialiseLambdaClient = (auth = null) => {
9351
9423
  const envVarsCheck = auth ? null : validateEnvironmentVars();
9352
9424
  const cacheKey = generateCacheKey(auth, envVarsCheck);
9353
9425
  if (lambdaClientCache.has(cacheKey)) {
9354
- return lambdaClientCache.get(cacheKey);
9426
+ const cached = lambdaClientCache.get(cacheKey);
9427
+ if (cached)
9428
+ return cached;
9355
9429
  }
9356
9430
  // Create new client and cache it
9357
9431
  const client = initialiseAWSClient(clientLambda.Lambda, auth);
@@ -9851,15 +9925,17 @@ const isRetryableS3Error = (error) => {
9851
9925
  if (error instanceof Error) {
9852
9926
  const message = error.message;
9853
9927
  // Retry on throttling
9854
- if (message.includes('SlowDown') || message.includes('RequestTimeout')) {
9928
+ if (message.includes("SlowDown") || message.includes("RequestTimeout")) {
9855
9929
  return true;
9856
9930
  }
9857
9931
  // Retry on 5xx server errors
9858
- if (message.includes('InternalError') || message.includes('ServiceUnavailable')) {
9932
+ if (message.includes("InternalError") ||
9933
+ message.includes("ServiceUnavailable")) {
9859
9934
  return true;
9860
9935
  }
9861
9936
  // Retry on connection errors
9862
- if (message.includes('RequestTimeTooSkewed') || message.includes('NetworkingError')) {
9937
+ if (message.includes("RequestTimeTooSkewed") ||
9938
+ message.includes("NetworkingError")) {
9863
9939
  return true;
9864
9940
  }
9865
9941
  }
@@ -9873,10 +9949,11 @@ const generateRandomHash = (length) => {
9873
9949
  };
9874
9950
  function generateDateTimeStamp() {
9875
9951
  const now = new Date();
9876
- return now.toISOString()
9877
- .replace(/T/, '-')
9878
- .replace(/\..+/, '')
9879
- .replace(/:/g, '-');
9952
+ return now
9953
+ .toISOString()
9954
+ .replace(/T/, "-")
9955
+ .replace(/\..+/, "")
9956
+ .replace(/:/g, "-");
9880
9957
  }
9881
9958
  const isValidBucketName = (name) => {
9882
9959
  const regex = /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/;
@@ -9907,7 +9984,7 @@ async function uploadFile(s3Client, bucketName, filePath, destinationPath, isPub
9907
9984
  Bucket: bucketName,
9908
9985
  Key: s3Key,
9909
9986
  Body: fileContent,
9910
- ACL: isPublic ? 'public-read' : undefined
9987
+ ACL: isPublic ? "public-read" : undefined,
9911
9988
  });
9912
9989
  await withRetry(() => s3Client.send(command), {
9913
9990
  maxRetries: 3,
@@ -9927,7 +10004,7 @@ async function uploadDirectory(s3Client, bucketName, dirPath, destinationPath, i
9927
10004
  Bucket: bucketName,
9928
10005
  Key: s3Key,
9929
10006
  Body: fileContent,
9930
- ACL: isPublic ? 'public-read' : undefined
10007
+ ACL: isPublic ? "public-read" : undefined,
9931
10008
  });
9932
10009
  await withRetry(() => s3Client.send(command), {
9933
10010
  maxRetries: 3,
@@ -9943,7 +10020,7 @@ async function zipDirectory(sourceDir, outPath) {
9943
10020
  const zip = new AdmZip();
9944
10021
  let fileCount = 0;
9945
10022
  try {
9946
- async function addFilesToZip(currentPath, relativePath = '') {
10023
+ async function addFilesToZip(currentPath, relativePath = "") {
9947
10024
  const files = await fs$1.readdir(currentPath, { withFileTypes: true });
9948
10025
  for (const file of files) {
9949
10026
  const filePath = path$1.join(currentPath, file.name);
@@ -9986,27 +10063,33 @@ async function downloadFromS3Helper(s3Client, bucketName, s3Path, localPath) {
9986
10063
  Prefix: s3Path,
9987
10064
  ContinuationToken: continuationToken,
9988
10065
  });
9989
- const listResponse = await withRetry(() => s3Client.send(listCommand), {
10066
+ const listResponse = (await withRetry(() => s3Client.send(listCommand), {
9990
10067
  maxRetries: 3,
9991
10068
  baseDelayMs: 1000,
9992
10069
  maxDelayMs: 15000,
9993
10070
  retryableErrors: isRetryableS3Error,
9994
- }, `S3:ListObjects:${bucketName}`);
10071
+ }, `S3:ListObjects:${bucketName}`));
9995
10072
  const objects = listResponse.Contents || [];
9996
10073
  if (objects.length === 0) {
9997
10074
  return { fileCount: 0, folderCount: 0, totalSize: 0, fileList: [] };
9998
10075
  }
9999
- if (objects.length === 1 && objects[0].Key?.endsWith('.zip')) {
10076
+ if (objects.length === 1 && objects[0].Key?.endsWith(".zip")) {
10000
10077
  // It's a zip file, download and extract it
10001
10078
  const zipPath = path$1.join(localPath, path$1.basename(objects[0].Key));
10002
10079
  await downloadFile(s3Client, bucketName, objects[0].Key, zipPath);
10003
- const { totalFiles, totalFolders, totalSize: extractedSize, fileList: extractedFileList } = await unzipFile(zipPath, localPath);
10080
+ const { totalFiles, totalFolders, totalSize: extractedSize, fileList: extractedFileList, } = await unzipFile(zipPath, localPath);
10004
10081
  await fs$1.unlink(zipPath);
10005
- return { fileCount: totalFiles, folderCount: totalFolders, totalSize: extractedSize, fileList: extractedFileList };
10082
+ return {
10083
+ fileCount: totalFiles,
10084
+ folderCount: totalFolders,
10085
+ totalSize: extractedSize,
10086
+ fileList: extractedFileList,
10087
+ };
10006
10088
  }
10007
10089
  else {
10008
10090
  // Non-zip files. Download files in batches.
10009
- for (let i = 0; i < objects.length; i += 1000) { // AWS allows up to 1000 per request
10091
+ for (let i = 0; i < objects.length; i += 1000) {
10092
+ // AWS allows up to 1000 per request
10010
10093
  const batch = objects.slice(i, i + 1000);
10011
10094
  await Promise.all(batch.map(async (obj) => {
10012
10095
  if (!obj.Key)
@@ -10022,7 +10105,9 @@ async function downloadFromS3Helper(s3Client, bucketName, s3Path, localPath) {
10022
10105
  }
10023
10106
  // Count folders
10024
10107
  const relativePath = path$1.relative(localPath, path$1.dirname(localFilePath));
10025
- if (relativePath && !relativePath.startsWith('..') && relativePath !== '.') {
10108
+ if (relativePath &&
10109
+ !relativePath.startsWith("..") &&
10110
+ relativePath !== ".") {
10026
10111
  folderCount++;
10027
10112
  }
10028
10113
  }));
@@ -10043,7 +10128,7 @@ async function downloadFile(s3Client, bucketName, s3Key, localFilePath) {
10043
10128
  await withRetry(async () => {
10044
10129
  const { Body } = await s3Client.send(getCommand);
10045
10130
  if (!Body)
10046
- throw new Error('No body returned from S3');
10131
+ throw new Error("No body returned from S3");
10047
10132
  const writeStream = fs.createWriteStream(localFilePath);
10048
10133
  await promises.pipeline(Body, writeStream);
10049
10134
  }, {
@@ -10099,17 +10184,19 @@ async function emptyBucket(s3Client, bucketName) {
10099
10184
  Bucket: bucketName,
10100
10185
  ContinuationToken: continuationToken,
10101
10186
  });
10102
- const response = await withRetry(() => s3Client.send(listCommand), {
10187
+ const response = (await withRetry(() => s3Client.send(listCommand), {
10103
10188
  maxRetries: 3,
10104
10189
  baseDelayMs: 1000,
10105
10190
  maxDelayMs: 15000,
10106
10191
  retryableErrors: isRetryableS3Error,
10107
- }, `S3:ListObjects:${bucketName}`);
10192
+ }, `S3:ListObjects:${bucketName}`));
10108
10193
  const { Contents, IsTruncated, NextContinuationToken } = response;
10109
10194
  if (Contents && Contents.length > 0) {
10110
10195
  const deleteCommand = new clientS3.DeleteObjectsCommand({
10111
10196
  Bucket: bucketName,
10112
- Delete: { Objects: Contents.map(({ Key }) => ({ Key: Key })) },
10197
+ Delete: {
10198
+ Objects: Contents.filter((c) => c.Key).map((c) => ({ Key: c.Key })),
10199
+ },
10113
10200
  });
10114
10201
  await withRetry(() => s3Client.send(deleteCommand), {
10115
10202
  maxRetries: 3,
@@ -23017,11 +23104,11 @@ let poolConfig = DEFAULT_POOL_CONFIG;
23017
23104
  async function loadApolloModules() {
23018
23105
  if (typeof window === "undefined" || process.env.AWS_EXECUTION_ENV) {
23019
23106
  // Server-side (or Lambda): load the CommonJS‑based implementation.
23020
- return (await Promise.resolve().then(function () { return require('./apollo-client.server-BEGHAF48.js'); }));
23107
+ return (await Promise.resolve().then(function () { return require('./apollo-client.server-DB3jLbBx.js'); }));
23021
23108
  }
23022
23109
  else {
23023
23110
  // Client-side: load the ESM‑based implementation.
23024
- return (await Promise.resolve().then(function () { return require('./apollo-client.client-FeVPHV0i.js'); }));
23111
+ return (await Promise.resolve().then(function () { return require('./apollo-client.client-CyxU1hTu.js'); }));
23025
23112
  }
23026
23113
  }
23027
23114
  /**
@@ -79048,7 +79135,7 @@ const RETRY_CONFIG = {
79048
79135
  BASE_DELAY: 2000, // Increased base delay to 2 seconds
79049
79136
  MAX_DELAY: 64000,
79050
79137
  MAX_RETRIES: 5,
79051
- MAX_RANDOM_DELAY: 1000
79138
+ MAX_RANDOM_DELAY: 1000,
79052
79139
  };
79053
79140
  /**
79054
79141
  * Determines if an error is related to Google Sheets API quotas or rate limits.
@@ -79074,10 +79161,10 @@ const RETRY_CONFIG = {
79074
79161
  */
79075
79162
  function isQuotaError(error) {
79076
79163
  const message = error.message.toLowerCase();
79077
- return message.includes('quota exceeded') ||
79078
- message.includes('rate limit') ||
79079
- message.includes('429') ||
79080
- message.includes('too many requests');
79164
+ return (message.includes("quota exceeded") ||
79165
+ message.includes("rate limit") ||
79166
+ message.includes("429") ||
79167
+ message.includes("too many requests"));
79081
79168
  }
79082
79169
  /**
79083
79170
  * Implements exponential backoff retry mechanism for Google Sheets API calls.
@@ -79136,14 +79223,14 @@ async function withExponentialBackoff(operation) {
79136
79223
  const exponentialDelay = Math.min(Math.pow(2, retries) * RETRY_CONFIG.BASE_DELAY + randomDelay, RETRY_CONFIG.MAX_DELAY);
79137
79224
  logIfDebug(`Quota/Rate limit exceeded. Error: ${error.message}`);
79138
79225
  logIfDebug(`Retrying in ${exponentialDelay}ms (attempt ${retries}/${RETRY_CONFIG.MAX_RETRIES})`);
79139
- await new Promise(resolve => setTimeout(resolve, exponentialDelay));
79226
+ await new Promise((resolve) => setTimeout(resolve, exponentialDelay));
79140
79227
  }
79141
79228
  }
79142
79229
  }
79143
79230
  function checkEnvVars() {
79144
79231
  const secrets = getSecrets();
79145
79232
  if (!secrets.googleSheets.clientEmail || !secrets.googleSheets.privateKey) {
79146
- throw new Error('GOOGLE_SHEETS_CLIENT_EMAIL or GOOGLE_SHEETS_PRIVATE_KEY is not defined in environment variables.');
79233
+ throw new Error("GOOGLE_SHEETS_CLIENT_EMAIL or GOOGLE_SHEETS_PRIVATE_KEY is not defined in environment variables.");
79147
79234
  }
79148
79235
  }
79149
79236
  /**
@@ -79158,16 +79245,16 @@ async function getAuthClient() {
79158
79245
  const secrets = getSecrets();
79159
79246
  const credentials = {
79160
79247
  client_email: secrets.googleSheets.clientEmail,
79161
- private_key: secrets.googleSheets.privateKey?.replace(/\\n/g, '\n'),
79248
+ private_key: secrets.googleSheets.privateKey?.replace(/\\n/g, "\n"),
79162
79249
  };
79163
79250
  const auth = new require$$0$6.GoogleAuth({
79164
79251
  credentials,
79165
- scopes: ['https://www.googleapis.com/auth/spreadsheets'],
79252
+ scopes: ["https://www.googleapis.com/auth/spreadsheets"],
79166
79253
  });
79167
79254
  return auth.getClient();
79168
79255
  }
79169
79256
  catch (error) {
79170
- const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
79257
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
79171
79258
  logIfDebug(`Error initializing Google Sheets auth: ${errorMessage}`);
79172
79259
  throw new Error(`Failed to initialize Google Sheets auth: ${errorMessage}`);
79173
79260
  }
@@ -79179,7 +79266,7 @@ async function getSheetsClient() {
79179
79266
  const auth = await getAuthClient();
79180
79267
  return new buildExports.sheets_v4.Sheets({
79181
79268
  auth,
79182
- timeout: GOOGLE_SHEETS_TIMEOUT_MS
79269
+ timeout: GOOGLE_SHEETS_TIMEOUT_MS,
79183
79270
  });
79184
79271
  }
79185
79272
  /**
@@ -79212,7 +79299,7 @@ function escapeSheetName(sheetName) {
79212
79299
  * @param startColumn Optional starting column (defaults to 'A')
79213
79300
  * @returns Promise resolving to response with success status and row details
79214
79301
  */
79215
- async function addRow(config, values, startColumn = 'A') {
79302
+ async function addRow(config, values, startColumn = "A") {
79216
79303
  try {
79217
79304
  const sheets = await getSheetsClient();
79218
79305
  const escapedSheetName = escapeSheetName(config.sheetName);
@@ -79220,7 +79307,7 @@ async function addRow(config, values, startColumn = 'A') {
79220
79307
  const response = await withExponentialBackoff(() => sheets.spreadsheets.values.append({
79221
79308
  spreadsheetId: config.spreadsheetId,
79222
79309
  range,
79223
- valueInputOption: 'USER_ENTERED',
79310
+ valueInputOption: "USER_ENTERED",
79224
79311
  requestBody: {
79225
79312
  values: [values],
79226
79313
  },
@@ -79228,9 +79315,9 @@ async function addRow(config, values, startColumn = 'A') {
79228
79315
  //logIfDebug(`Added row to sheet ${config.sheetName}: ${values.join(', ')}`);
79229
79316
  const rowNumber = response.data.updates?.updatedRange
79230
79317
  ? parseInt(response.data.updates.updatedRange
79231
- .split('!')[1]
79232
- .split(':')[0]
79233
- .replace(/[^0-9]/g, ''))
79318
+ .split("!")[1]
79319
+ .split(":")[0]
79320
+ .replace(/[^0-9]/g, ""))
79234
79321
  : 0;
79235
79322
  return {
79236
79323
  success: true,
@@ -79241,7 +79328,7 @@ async function addRow(config, values, startColumn = 'A') {
79241
79328
  };
79242
79329
  }
79243
79330
  catch (error) {
79244
- const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
79331
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
79245
79332
  logIfDebug(`Error adding row to sheet: ${errorMessage}`);
79246
79333
  return {
79247
79334
  success: false,
@@ -79283,7 +79370,7 @@ async function addSheet(spreadsheetId, sheetTitle) {
79283
79370
  };
79284
79371
  }
79285
79372
  catch (error) {
79286
- const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
79373
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
79287
79374
  logIfDebug(`Error adding sheet: ${errorMessage}`);
79288
79375
  return {
79289
79376
  success: false,
@@ -79307,7 +79394,7 @@ async function writeCell(config, cell, value) {
79307
79394
  await withExponentialBackoff(() => sheets.spreadsheets.values.update({
79308
79395
  spreadsheetId: config.spreadsheetId,
79309
79396
  range,
79310
- valueInputOption: 'USER_ENTERED',
79397
+ valueInputOption: "USER_ENTERED",
79311
79398
  requestBody: {
79312
79399
  values: [[value]],
79313
79400
  },
@@ -79321,7 +79408,7 @@ async function writeCell(config, cell, value) {
79321
79408
  };
79322
79409
  }
79323
79410
  catch (error) {
79324
- const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
79411
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
79325
79412
  logIfDebug(`Error writing to cell: ${errorMessage}`);
79326
79413
  return {
79327
79414
  success: false,
@@ -79345,8 +79432,11 @@ async function getCell(config, cell) {
79345
79432
  spreadsheetId: config.spreadsheetId,
79346
79433
  range,
79347
79434
  }));
79348
- const value = response.data.values?.[0]?.[0];
79349
- logIfDebug(`Read value from cell ${range}: ${value}`);
79435
+ const rawValue = response.data.values?.[0]?.[0];
79436
+ const value = rawValue === undefined || rawValue === null
79437
+ ? null
79438
+ : rawValue;
79439
+ logIfDebug(`Read value from cell ${range}: ${String(value)}`);
79350
79440
  return {
79351
79441
  success: true,
79352
79442
  data: {
@@ -79355,7 +79445,7 @@ async function getCell(config, cell) {
79355
79445
  };
79356
79446
  }
79357
79447
  catch (error) {
79358
- const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
79448
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
79359
79449
  logIfDebug(`Error reading from cell: ${errorMessage}`);
79360
79450
  return {
79361
79451
  success: false,
@@ -79388,10 +79478,11 @@ function convertA1ToRowCol(a1Notation) {
79388
79478
  * @returns Padded array of arrays
79389
79479
  */
79390
79480
  function padArrays(arrays) {
79391
- const maxLength = Math.max(...arrays.map(arr => arr.length));
79392
- return arrays.map(arr => {
79481
+ const maxLength = Math.max(...arrays.map((arr) => arr.length));
79482
+ return arrays.map((arr) => {
79393
79483
  if (arr.length < maxLength) {
79394
- return [...arr, ...Array(maxLength - arr.length).fill('')];
79484
+ const padding = Array(maxLength - arr.length).fill("");
79485
+ return [...arr, ...padding];
79395
79486
  }
79396
79487
  return arr;
79397
79488
  });
@@ -79403,10 +79494,10 @@ function padArrays(arrays) {
79403
79494
  * @param startCell Optional starting cell in A1 notation (defaults to 'A1')
79404
79495
  * @returns Promise resolving to response with success status and update details
79405
79496
  */
79406
- async function writeArray(config, data, startCell = 'A1') {
79497
+ async function writeArray(config, data, startCell = "A1") {
79407
79498
  try {
79408
79499
  if (!data.length) {
79409
- throw new Error('Data array is empty');
79500
+ throw new Error("Data array is empty");
79410
79501
  }
79411
79502
  const sheets = await getSheetsClient();
79412
79503
  const { row: startRow, column: startCol } = convertA1ToRowCol(startCell);
@@ -79422,7 +79513,7 @@ async function writeArray(config, data, startCell = 'A1') {
79422
79513
  const response = await withExponentialBackoff(() => sheets.spreadsheets.values.update({
79423
79514
  spreadsheetId: config.spreadsheetId,
79424
79515
  range,
79425
- valueInputOption: 'USER_ENTERED',
79516
+ valueInputOption: "USER_ENTERED",
79426
79517
  requestBody: {
79427
79518
  values: paddedData,
79428
79519
  },
@@ -79438,7 +79529,7 @@ async function writeArray(config, data, startCell = 'A1') {
79438
79529
  };
79439
79530
  }
79440
79531
  catch (error) {
79441
- const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
79532
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
79442
79533
  logIfDebug(`Error writing array to sheet: ${errorMessage}`);
79443
79534
  return {
79444
79535
  success: false,
@@ -81732,4 +81823,4 @@ exports.withCorrelationId = withCorrelationId;
81732
81823
  exports.withMetrics = withMetrics;
81733
81824
  exports.withRateLimit = withRateLimit;
81734
81825
  exports.withRetry = withRetry;
81735
- //# sourceMappingURL=index-WGzi__C5.js.map
81826
+ //# sourceMappingURL=index-KzQOh2uu.js.map