@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.
- package/dist/{apollo-client.client-DrSy1wz-.js → apollo-client.client-9wJcufhf.js} +4 -4
- package/dist/{apollo-client.client-DrSy1wz-.js.map → apollo-client.client-9wJcufhf.js.map} +1 -1
- package/dist/{apollo-client.client-FeVPHV0i.js → apollo-client.client-CyxU1hTu.js} +3 -3
- package/dist/{apollo-client.client-FeVPHV0i.js.map → apollo-client.client-CyxU1hTu.js.map} +1 -1
- package/dist/{apollo-client.server-CJ6iRLa2.js → apollo-client.server-CbagxkmK.js} +3 -3
- package/dist/{apollo-client.server-CJ6iRLa2.js.map → apollo-client.server-CbagxkmK.js.map} +1 -1
- package/dist/{apollo-client.server-BEGHAF48.js → apollo-client.server-DB3jLbBx.js} +3 -3
- package/dist/{apollo-client.server-BEGHAF48.js.map → apollo-client.server-DB3jLbBx.js.map} +1 -1
- package/dist/{index-Dz0wKyTF.js → index-B4yVKGNR.js} +2 -2
- package/dist/{index-Dz0wKyTF.js.map → index-B4yVKGNR.js.map} +1 -1
- package/dist/{index-vRskhk1H.js → index-CL79JTWc.js} +2 -2
- package/dist/{index-vRskhk1H.js.map → index-CL79JTWc.js.map} +1 -1
- package/dist/{index-CWoI2dXN.js → index-EMQ1uJMh.js} +365 -274
- package/dist/{index-WGzi__C5.js.map → index-EMQ1uJMh.js.map} +1 -1
- package/dist/{index-WGzi__C5.js → index-KzQOh2uu.js} +365 -274
- package/dist/{index-CWoI2dXN.js.map → index-KzQOh2uu.js.map} +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/test.cjs +21 -17
- package/dist/test.cjs.map +1 -1
- package/dist/test.mjs +21 -17
- package/dist/test.mjs.map +1 -1
- package/dist/types/functions/get-weather.d.ts +1 -1
- package/dist/types/functions/google-sheets.d.ts +2 -2
- package/dist/types/functions/json-llm-tools.d.ts +1 -1
- package/dist/types/functions/llm-anthropic.d.ts +1 -1
- package/dist/types/functions/llm-deepseek.d.ts +2 -2
- package/dist/types/functions/llm-openai-compatible.d.ts +2 -2
- package/dist/types/functions/llm-openai.d.ts +3 -3
- package/dist/types/functions/llm-utils.d.ts +1 -1
- package/dist/types/test-llm-functions-archive.d.ts +1 -1
- package/dist/types/test.d.ts +1 -1
- package/dist/types/types/openai-types.d.ts +10 -1
- package/dist/types/utils/aws-initialise.d.ts +4 -4
- package/package.json +2 -2
|
@@ -551,10 +551,19 @@ function getModelProvider(model) {
|
|
|
551
551
|
return SUPPORTED_MODELS[model].provider;
|
|
552
552
|
}
|
|
553
553
|
/**
|
|
554
|
-
* Default model tiers per provider for use with LLM_MODEL_MINI/NORMAL/ADVANCED env vars
|
|
554
|
+
* Default model tiers per provider for use with LLM_MODEL_MINI/NORMAL/ADVANCED env vars.
|
|
555
|
+
*
|
|
556
|
+
* OpenAI `advanced` was reverted from `gpt-5.5` back to `gpt-5.4` on 2026-05-30:
|
|
557
|
+
* the heavy tool-call prompt in adaptic-engine's trading-decision path was
|
|
558
|
+
* timing out even after the engine raised `LLM_CALL_TIMEOUT_MS` to 90s (5+
|
|
559
|
+
* tickers per loop still exceeded the budget). `gpt-5.5`'s 1M-context window
|
|
560
|
+
* is not required on that path — the prompt fits comfortably inside `gpt-5.4`'s
|
|
561
|
+
* envelope and `gpt-5.4` returns inside the original 25-30s p95. `gpt-5.5`
|
|
562
|
+
* remains registered in OPENAI_MODELS and reachable by explicit model id;
|
|
563
|
+
* only the `advanced` tier default is rolled back.
|
|
555
564
|
*/
|
|
556
565
|
const PROVIDER_DEFAULT_MODELS = {
|
|
557
|
-
openai: { mini: 'gpt-5.4-nano', normal: 'gpt-5.4-mini', advanced: 'gpt-5.
|
|
566
|
+
openai: { mini: 'gpt-5.4-nano', normal: 'gpt-5.4-mini', advanced: 'gpt-5.4' },
|
|
558
567
|
anthropic: { mini: 'claude-haiku-4-5', normal: 'claude-sonnet-4-6', advanced: 'claude-opus-4-7' },
|
|
559
568
|
deepseek: { mini: 'deepseek-v4-flash', normal: 'deepseek-v4-flash', advanced: 'deepseek-v4-pro' },
|
|
560
569
|
kimi: { mini: 'kimi-k2-0905-preview', normal: 'kimi-k2.5', advanced: 'kimi-k2.6' },
|
|
@@ -1508,11 +1517,13 @@ let openai;
|
|
|
1508
1517
|
function initializeOpenAI(apiKey) {
|
|
1509
1518
|
const key = apiKey || getSecrets().openai.apiKey;
|
|
1510
1519
|
if (!key) {
|
|
1511
|
-
throw new Error(
|
|
1520
|
+
throw new Error("OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set");
|
|
1512
1521
|
}
|
|
1513
1522
|
return new OpenAI({
|
|
1514
1523
|
apiKey: key,
|
|
1515
|
-
defaultHeaders: {
|
|
1524
|
+
defaultHeaders: {
|
|
1525
|
+
"User-Agent": "My Server-side Application (compatible; OpenAI API Client)",
|
|
1526
|
+
},
|
|
1516
1527
|
});
|
|
1517
1528
|
}
|
|
1518
1529
|
/**
|
|
@@ -1530,9 +1541,7 @@ function initializeOpenAI(apiKey) {
|
|
|
1530
1541
|
*/
|
|
1531
1542
|
async function fixJsonWithAI(jsonStr, options) {
|
|
1532
1543
|
// Backward compatibility: handle string apiKey as first positional param
|
|
1533
|
-
const opts = typeof options ===
|
|
1534
|
-
? { apiKey: options }
|
|
1535
|
-
: (options ?? {});
|
|
1544
|
+
const opts = typeof options === "string" ? { apiKey: options } : (options ?? {});
|
|
1536
1545
|
const apiKey = opts.apiKey;
|
|
1537
1546
|
const maxDepth = opts.maxDepth ?? 2;
|
|
1538
1547
|
const depth = opts._depth ?? 0;
|
|
@@ -1545,18 +1554,18 @@ async function fixJsonWithAI(jsonStr, options) {
|
|
|
1545
1554
|
openai = initializeOpenAI(apiKey);
|
|
1546
1555
|
}
|
|
1547
1556
|
const completion = await openai.chat.completions.create({
|
|
1548
|
-
model:
|
|
1557
|
+
model: "gpt-5-mini",
|
|
1549
1558
|
messages: [
|
|
1550
1559
|
{
|
|
1551
|
-
role:
|
|
1552
|
-
content:
|
|
1560
|
+
role: "system",
|
|
1561
|
+
content: "You are a JSON fixer. Return only valid JSON without any additional text or explanation.",
|
|
1553
1562
|
},
|
|
1554
1563
|
{
|
|
1555
|
-
role:
|
|
1556
|
-
content: `Fix this broken JSON:\n${jsonStr}
|
|
1557
|
-
}
|
|
1564
|
+
role: "user",
|
|
1565
|
+
content: `Fix this broken JSON:\n${jsonStr}`,
|
|
1566
|
+
},
|
|
1558
1567
|
],
|
|
1559
|
-
response_format: { type:
|
|
1568
|
+
response_format: { type: "json_object" },
|
|
1560
1569
|
});
|
|
1561
1570
|
const fixedJson = completion.choices[0]?.message?.content;
|
|
1562
1571
|
if (fixedJson && isValidJson(fixedJson)) {
|
|
@@ -1570,23 +1579,32 @@ async function fixJsonWithAI(jsonStr, options) {
|
|
|
1570
1579
|
_depth: depth + 1,
|
|
1571
1580
|
});
|
|
1572
1581
|
}
|
|
1573
|
-
throw new JsonParseError(
|
|
1582
|
+
throw new JsonParseError("Failed to fix JSON with AI: returned invalid JSON");
|
|
1574
1583
|
}
|
|
1575
1584
|
catch (err) {
|
|
1576
1585
|
// Re-throw JsonParseError as-is (e.g. max depth exceeded)
|
|
1577
1586
|
if (err instanceof JsonParseError) {
|
|
1578
1587
|
throw err;
|
|
1579
1588
|
}
|
|
1580
|
-
const errorMessage = err instanceof Error ? err.message :
|
|
1589
|
+
const errorMessage = err instanceof Error ? err.message : "Unknown error occurred";
|
|
1581
1590
|
throw new JsonParseError(`Error fixing JSON with AI: ${errorMessage}`);
|
|
1582
1591
|
}
|
|
1583
1592
|
}
|
|
1584
1593
|
|
|
1585
1594
|
// llm-utils.ts
|
|
1586
1595
|
function normalizeModelName(model) {
|
|
1587
|
-
return model.replace(/_/g,
|
|
1588
|
-
}
|
|
1589
|
-
const CODE_BLOCK_TYPES = [
|
|
1596
|
+
return model.replace(/_/g, "-").toLowerCase();
|
|
1597
|
+
}
|
|
1598
|
+
const CODE_BLOCK_TYPES = [
|
|
1599
|
+
"javascript",
|
|
1600
|
+
"js",
|
|
1601
|
+
"graphql",
|
|
1602
|
+
"json",
|
|
1603
|
+
"typescript",
|
|
1604
|
+
"python",
|
|
1605
|
+
"markdown",
|
|
1606
|
+
"yaml",
|
|
1607
|
+
];
|
|
1590
1608
|
/**
|
|
1591
1609
|
* Tries to parse JSON from the given content string according to the specified
|
|
1592
1610
|
* response format. If the response format is 'json' or a JSON schema object, it
|
|
@@ -1610,7 +1628,9 @@ async function parseResponse(content, responseFormat, options = {}) {
|
|
|
1610
1628
|
if (recursionDepth > maxRetries) {
|
|
1611
1629
|
throw new Error(`Maximum recursion depth (${maxRetries}) exceeded while parsing JSON. AI fixing may have returned broken JSON.`);
|
|
1612
1630
|
}
|
|
1613
|
-
if (responseFormat ===
|
|
1631
|
+
if (responseFormat === "json" ||
|
|
1632
|
+
(typeof responseFormat === "object" &&
|
|
1633
|
+
responseFormat?.type === "json_schema")) {
|
|
1614
1634
|
let cleanedContent = content.trim();
|
|
1615
1635
|
let detectedType = null;
|
|
1616
1636
|
// Remove code block markers if present
|
|
@@ -1621,8 +1641,8 @@ async function parseResponse(content, responseFormat, options = {}) {
|
|
|
1621
1641
|
}
|
|
1622
1642
|
return false;
|
|
1623
1643
|
});
|
|
1624
|
-
if (startsWithCodeBlock && cleanedContent.endsWith(
|
|
1625
|
-
const firstLineEndIndex = cleanedContent.indexOf(
|
|
1644
|
+
if (startsWithCodeBlock && cleanedContent.endsWith("```")) {
|
|
1645
|
+
const firstLineEndIndex = cleanedContent.indexOf("\n");
|
|
1626
1646
|
if (firstLineEndIndex !== -1) {
|
|
1627
1647
|
cleanedContent = cleanedContent.slice(firstLineEndIndex + 1, -3).trim();
|
|
1628
1648
|
getLumicLogger().info(`Code block for type ${detectedType} detected and removed. Cleaned content: ${cleanedContent}`);
|
|
@@ -1655,7 +1675,7 @@ async function parseResponse(content, responseFormat, options = {}) {
|
|
|
1655
1675
|
},
|
|
1656
1676
|
// Strategy 3: Remove leading/trailing text and try parsing
|
|
1657
1677
|
() => {
|
|
1658
|
-
const jsonMatch = cleanedContent.replace(/^[^{[]*|[^}\]]*$/g,
|
|
1678
|
+
const jsonMatch = cleanedContent.replace(/^[^{[]*|[^}\]]*$/g, "");
|
|
1659
1679
|
try {
|
|
1660
1680
|
return JSON.parse(jsonMatch);
|
|
1661
1681
|
}
|
|
@@ -1677,7 +1697,7 @@ async function parseResponse(content, responseFormat, options = {}) {
|
|
|
1677
1697
|
}
|
|
1678
1698
|
// Strategy 5: Use AI fixing (only if explicitly enabled)
|
|
1679
1699
|
if (enableAiFix) {
|
|
1680
|
-
getLumicLogger().warn(
|
|
1700
|
+
getLumicLogger().warn("Using AI-powered JSON fixing. This adds cost and latency. Consider improving JSON generation instead.");
|
|
1681
1701
|
try {
|
|
1682
1702
|
const aiFixedJson = await fixJsonWithAI(cleanedContent);
|
|
1683
1703
|
if (aiFixedJson !== null) {
|
|
@@ -1690,7 +1710,7 @@ async function parseResponse(content, responseFormat, options = {}) {
|
|
|
1690
1710
|
catch {
|
|
1691
1711
|
// AI returned something that looks like JSON but isn't valid
|
|
1692
1712
|
// Increment recursion depth and try parsing the AI response
|
|
1693
|
-
getLumicLogger().warn(
|
|
1713
|
+
getLumicLogger().warn("AI fixing returned invalid JSON, attempting recursive parse...");
|
|
1694
1714
|
return parseResponse(JSON.stringify(aiFixedJson), responseFormat, {
|
|
1695
1715
|
...options,
|
|
1696
1716
|
_recursionDepth: recursionDepth + 1,
|
|
@@ -1699,14 +1719,14 @@ async function parseResponse(content, responseFormat, options = {}) {
|
|
|
1699
1719
|
}
|
|
1700
1720
|
}
|
|
1701
1721
|
catch (error) {
|
|
1702
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
1722
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1703
1723
|
getLumicLogger().error(`AI JSON fixing failed: ${errorMessage}`);
|
|
1704
1724
|
// Continue to final error below
|
|
1705
1725
|
}
|
|
1706
1726
|
}
|
|
1707
1727
|
// If all strategies fail, throw an error
|
|
1708
1728
|
getLumicLogger().error(`Failed to parse JSON from content: ${cleanedContent}. Original JSON: ${content}`);
|
|
1709
|
-
throw new Error(
|
|
1729
|
+
throw new Error("Unable to parse JSON response");
|
|
1710
1730
|
}
|
|
1711
1731
|
else {
|
|
1712
1732
|
return content;
|
|
@@ -2300,13 +2320,15 @@ const isRetryableLLMError = (error) => {
|
|
|
2300
2320
|
if (error instanceof Error) {
|
|
2301
2321
|
const message = error.message;
|
|
2302
2322
|
// Retry on rate limits (429)
|
|
2303
|
-
if (message.includes(
|
|
2323
|
+
if (message.includes("429") ||
|
|
2324
|
+
message.includes("rate limit") ||
|
|
2325
|
+
message.includes("Rate limit")) {
|
|
2304
2326
|
return true;
|
|
2305
2327
|
}
|
|
2306
2328
|
// Retry on transient body-corruption 400s. Match the exact OpenAI error
|
|
2307
2329
|
// string to avoid retrying genuine client-side validation 400s (which
|
|
2308
2330
|
// would re-fail forever and waste retry budget).
|
|
2309
|
-
if (message.includes(
|
|
2331
|
+
if (message.includes("could not parse the JSON body of your request")) {
|
|
2310
2332
|
return true;
|
|
2311
2333
|
}
|
|
2312
2334
|
}
|
|
@@ -2375,23 +2397,26 @@ function isReasoningModel(model) {
|
|
|
2375
2397
|
* @throws Error if the response format is invalid or incompatible.
|
|
2376
2398
|
*/
|
|
2377
2399
|
function getResponseFormatOption(responseFormat, normalizedModel) {
|
|
2378
|
-
if (responseFormat ===
|
|
2379
|
-
return { type:
|
|
2400
|
+
if (responseFormat === "text") {
|
|
2401
|
+
return { type: "text" };
|
|
2380
2402
|
}
|
|
2381
|
-
if (responseFormat ===
|
|
2382
|
-
return supportsJsonMode(normalizedModel)
|
|
2403
|
+
if (responseFormat === "json") {
|
|
2404
|
+
return supportsJsonMode(normalizedModel)
|
|
2405
|
+
? { type: "json_object" }
|
|
2406
|
+
: { type: "text" };
|
|
2383
2407
|
}
|
|
2384
|
-
if (typeof responseFormat ===
|
|
2408
|
+
if (typeof responseFormat === "object" &&
|
|
2409
|
+
responseFormat.type === "json_schema") {
|
|
2385
2410
|
if (!isStructuredOutputCompatibleModel(normalizedModel)) {
|
|
2386
2411
|
throw new Error(`${normalizedModel} is not compatible with structured outputs / json object.`);
|
|
2387
2412
|
}
|
|
2388
2413
|
return {
|
|
2389
|
-
type:
|
|
2414
|
+
type: "json_schema",
|
|
2390
2415
|
json_schema: {
|
|
2391
|
-
type:
|
|
2416
|
+
type: "object",
|
|
2392
2417
|
properties: responseFormat.schema.properties,
|
|
2393
2418
|
required: responseFormat.schema.required || [],
|
|
2394
|
-
name:
|
|
2419
|
+
name: "Response",
|
|
2395
2420
|
},
|
|
2396
2421
|
};
|
|
2397
2422
|
}
|
|
@@ -2450,7 +2475,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2450
2475
|
const normalizedModel = normalizeModelName(options.model || DEFAULT_MODEL);
|
|
2451
2476
|
const apiKey = options.apiKey || getSecrets().openai.apiKey;
|
|
2452
2477
|
if (!apiKey) {
|
|
2453
|
-
throw new Error(
|
|
2478
|
+
throw new Error("OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set");
|
|
2454
2479
|
}
|
|
2455
2480
|
const openai = new OpenAI({
|
|
2456
2481
|
apiKey: apiKey,
|
|
@@ -2460,7 +2485,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2460
2485
|
// Add developer prompt if present
|
|
2461
2486
|
if (options.developerPrompt && supportsDeveloperPrompt(normalizedModel)) {
|
|
2462
2487
|
messages.push({
|
|
2463
|
-
role:
|
|
2488
|
+
role: "developer",
|
|
2464
2489
|
content: options.developerPrompt,
|
|
2465
2490
|
});
|
|
2466
2491
|
}
|
|
@@ -2469,15 +2494,15 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2469
2494
|
messages.push(...options.context);
|
|
2470
2495
|
}
|
|
2471
2496
|
// Add user content
|
|
2472
|
-
if (typeof content ===
|
|
2497
|
+
if (typeof content === "string") {
|
|
2473
2498
|
messages.push({
|
|
2474
|
-
role:
|
|
2499
|
+
role: "user",
|
|
2475
2500
|
content,
|
|
2476
2501
|
});
|
|
2477
2502
|
}
|
|
2478
2503
|
else {
|
|
2479
2504
|
messages.push({
|
|
2480
|
-
role:
|
|
2505
|
+
role: "user",
|
|
2481
2506
|
content,
|
|
2482
2507
|
});
|
|
2483
2508
|
}
|
|
@@ -2491,7 +2516,8 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2491
2516
|
queryOptions.tools = options.tools;
|
|
2492
2517
|
}
|
|
2493
2518
|
// Only include temperature if the model supports it
|
|
2494
|
-
if (options.temperature !== undefined &&
|
|
2519
|
+
if (options.temperature !== undefined &&
|
|
2520
|
+
supportsTemperature(normalizedModel)) {
|
|
2495
2521
|
queryOptions.temperature = options.temperature;
|
|
2496
2522
|
}
|
|
2497
2523
|
// Only include max_completion_tokens when specified
|
|
@@ -2501,7 +2527,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2501
2527
|
// Only set response_format when it's not the default 'text' type
|
|
2502
2528
|
// (sending response_format: {type: 'text'} is redundant and may cause issues)
|
|
2503
2529
|
const responseFormatOption = getResponseFormatOption(responseFormat, normalizedModel);
|
|
2504
|
-
if (responseFormatOption.type !==
|
|
2530
|
+
if (responseFormatOption.type !== "text") {
|
|
2505
2531
|
queryOptions.response_format = responseFormatOption;
|
|
2506
2532
|
}
|
|
2507
2533
|
let completion;
|
|
@@ -2522,15 +2548,19 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2522
2548
|
// for production prompts containing sensitive context.
|
|
2523
2549
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2524
2550
|
const totalContentChars = messages.reduce((sum, msg) => {
|
|
2525
|
-
if (typeof msg.content ===
|
|
2551
|
+
if (typeof msg.content === "string")
|
|
2526
2552
|
return sum + msg.content.length;
|
|
2527
2553
|
if (Array.isArray(msg.content)) {
|
|
2528
|
-
return sum +
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2554
|
+
return (sum +
|
|
2555
|
+
msg.content.reduce((s, part) => {
|
|
2556
|
+
if (typeof part === "object" &&
|
|
2557
|
+
part !== null &&
|
|
2558
|
+
"text" in part &&
|
|
2559
|
+
typeof part.text === "string") {
|
|
2560
|
+
return s + part.text.length;
|
|
2561
|
+
}
|
|
2562
|
+
return s;
|
|
2563
|
+
}, 0));
|
|
2534
2564
|
}
|
|
2535
2565
|
return sum;
|
|
2536
2566
|
}, 0);
|
|
@@ -2557,7 +2587,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2557
2587
|
const cachedTokens = completion.usage?.prompt_tokens_details?.cached_tokens ?? 0;
|
|
2558
2588
|
const response = {
|
|
2559
2589
|
id: completion.id,
|
|
2560
|
-
content: completion.choices[0]?.message?.content ||
|
|
2590
|
+
content: completion.choices[0]?.message?.content || "",
|
|
2561
2591
|
tool_calls: completion.choices[0]?.message?.tool_calls,
|
|
2562
2592
|
usage: {
|
|
2563
2593
|
prompt_tokens: completion.usage?.prompt_tokens ?? 0,
|
|
@@ -2567,7 +2597,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2567
2597
|
},
|
|
2568
2598
|
system_fingerprint: completion.system_fingerprint,
|
|
2569
2599
|
service_tier: options.service_tier,
|
|
2570
|
-
provider:
|
|
2600
|
+
provider: "openai",
|
|
2571
2601
|
model: normalizedModel,
|
|
2572
2602
|
};
|
|
2573
2603
|
return response;
|
|
@@ -2580,7 +2610,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2580
2610
|
* @param options The options for the LLM call. Defaults to an empty object.
|
|
2581
2611
|
* @return A promise that resolves to the response from the LLM.
|
|
2582
2612
|
*/
|
|
2583
|
-
const makeOpenAIChatCompletionCall = async (content, responseFormat =
|
|
2613
|
+
const makeOpenAIChatCompletionCall = async (content, responseFormat = "text", options = {}) => {
|
|
2584
2614
|
const mergedOptions = {
|
|
2585
2615
|
...DEFAULT_OPTIONS$1,
|
|
2586
2616
|
...options,
|
|
@@ -2589,7 +2619,7 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = 'text', op
|
|
|
2589
2619
|
// Track cost in the global cost tracker. Pass cached tokens through so the
|
|
2590
2620
|
// tracker applies the discounted cached-input rate (typically ~50% of the
|
|
2591
2621
|
// standard input rate) instead of billing every input token at full price.
|
|
2592
|
-
getLLMCostTracker().trackUsage(
|
|
2622
|
+
getLLMCostTracker().trackUsage("openai", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens);
|
|
2593
2623
|
// Handle tool calls differently
|
|
2594
2624
|
if (completion.tool_calls && completion.tool_calls.length > 0) {
|
|
2595
2625
|
const toolCallResponse = {
|
|
@@ -2605,10 +2635,10 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = 'text', op
|
|
|
2605
2635
|
prompt_tokens: completion.usage.prompt_tokens,
|
|
2606
2636
|
completion_tokens: completion.usage.completion_tokens,
|
|
2607
2637
|
reasoning_tokens: 0,
|
|
2608
|
-
provider:
|
|
2638
|
+
provider: "openai",
|
|
2609
2639
|
model: completion.model,
|
|
2610
2640
|
cached_tokens: completion.usage.cached_tokens,
|
|
2611
|
-
cost: calculateCost(
|
|
2641
|
+
cost: calculateCost("openai", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
|
|
2612
2642
|
},
|
|
2613
2643
|
tool_calls: completion.tool_calls,
|
|
2614
2644
|
};
|
|
@@ -2616,7 +2646,7 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = 'text', op
|
|
|
2616
2646
|
// Handle regular responses
|
|
2617
2647
|
const parsedResponse = await parseResponse(completion.content, responseFormat);
|
|
2618
2648
|
if (parsedResponse === null) {
|
|
2619
|
-
throw new Error(
|
|
2649
|
+
throw new Error("Failed to parse response");
|
|
2620
2650
|
}
|
|
2621
2651
|
return {
|
|
2622
2652
|
response: parsedResponse,
|
|
@@ -2624,10 +2654,10 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = 'text', op
|
|
|
2624
2654
|
prompt_tokens: completion.usage.prompt_tokens,
|
|
2625
2655
|
completion_tokens: completion.usage.completion_tokens,
|
|
2626
2656
|
reasoning_tokens: 0,
|
|
2627
|
-
provider:
|
|
2657
|
+
provider: "openai",
|
|
2628
2658
|
model: completion.model,
|
|
2629
2659
|
cached_tokens: completion.usage.cached_tokens,
|
|
2630
|
-
cost: calculateCost(
|
|
2660
|
+
cost: calculateCost("openai", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
|
|
2631
2661
|
},
|
|
2632
2662
|
tool_calls: completion.tool_calls,
|
|
2633
2663
|
};
|
|
@@ -2662,7 +2692,7 @@ const makeResponsesAPICall = async (input, options = {}) => {
|
|
|
2662
2692
|
const normalizedModel = normalizeModelName(options.model || DEFAULT_MODEL);
|
|
2663
2693
|
const apiKey = options.apiKey || getSecrets().openai.apiKey;
|
|
2664
2694
|
if (!apiKey) {
|
|
2665
|
-
throw new Error(
|
|
2695
|
+
throw new Error("OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set");
|
|
2666
2696
|
}
|
|
2667
2697
|
const openai = new OpenAI({
|
|
2668
2698
|
apiKey: apiKey,
|
|
@@ -2686,20 +2716,20 @@ const makeResponsesAPICall = async (input, options = {}) => {
|
|
|
2686
2716
|
// (the equivalent of Chat Completions' `prompt_tokens_details.cached_tokens`).
|
|
2687
2717
|
const responsesCachedTokens = response.usage?.input_tokens_details?.cached_tokens || 0;
|
|
2688
2718
|
// Track cost in the global cost tracker
|
|
2689
|
-
getLLMCostTracker().trackUsage(
|
|
2719
|
+
getLLMCostTracker().trackUsage("openai", normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens);
|
|
2690
2720
|
// Extract tool calls from the output
|
|
2691
2721
|
const toolCalls = response.output
|
|
2692
|
-
?.filter((item) => item.type ===
|
|
2722
|
+
?.filter((item) => item.type === "function_call")
|
|
2693
2723
|
.map((toolCall) => ({
|
|
2694
2724
|
id: toolCall.call_id,
|
|
2695
|
-
type:
|
|
2725
|
+
type: "function",
|
|
2696
2726
|
function: {
|
|
2697
2727
|
name: toolCall.name,
|
|
2698
2728
|
arguments: toolCall.arguments,
|
|
2699
2729
|
},
|
|
2700
2730
|
}));
|
|
2701
2731
|
// Extract code interpreter outputs if present
|
|
2702
|
-
const codeInterpreterCalls = response.output?.filter((item) => item.type ===
|
|
2732
|
+
const codeInterpreterCalls = response.output?.filter((item) => item.type === "code_interpreter_call");
|
|
2703
2733
|
let codeInterpreterOutputs = undefined;
|
|
2704
2734
|
if (codeInterpreterCalls && codeInterpreterCalls.length > 0) {
|
|
2705
2735
|
// Each code_interpreter_call has an 'outputs' array property
|
|
@@ -2725,32 +2755,34 @@ const makeResponsesAPICall = async (input, options = {}) => {
|
|
|
2725
2755
|
prompt_tokens: response.usage?.input_tokens || 0,
|
|
2726
2756
|
completion_tokens: response.usage?.output_tokens || 0,
|
|
2727
2757
|
reasoning_tokens: response.usage?.output_tokens_details?.reasoning_tokens || 0,
|
|
2728
|
-
provider:
|
|
2758
|
+
provider: "openai",
|
|
2729
2759
|
model: normalizedModel,
|
|
2730
2760
|
cached_tokens: responsesCachedTokens,
|
|
2731
|
-
cost: calculateCost(
|
|
2761
|
+
cost: calculateCost("openai", normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens),
|
|
2732
2762
|
},
|
|
2733
2763
|
tool_calls: toolCalls,
|
|
2734
|
-
...(codeInterpreterOutputs
|
|
2764
|
+
...(codeInterpreterOutputs
|
|
2765
|
+
? { code_interpreter_outputs: codeInterpreterOutputs }
|
|
2766
|
+
: {}),
|
|
2735
2767
|
};
|
|
2736
2768
|
}
|
|
2737
2769
|
// Extract text content from the response output
|
|
2738
2770
|
const textContent = response.output
|
|
2739
|
-
?.filter((item) => item.type ===
|
|
2771
|
+
?.filter((item) => item.type === "message")
|
|
2740
2772
|
.map((item) => item.content
|
|
2741
|
-
.filter((content) => content.type ===
|
|
2773
|
+
.filter((content) => content.type === "output_text")
|
|
2742
2774
|
.map((content) => content.text)
|
|
2743
|
-
.join(
|
|
2744
|
-
.join(
|
|
2775
|
+
.join(""))
|
|
2776
|
+
.join("") || "";
|
|
2745
2777
|
// Determine the format for parsing the response
|
|
2746
|
-
let parsingFormat =
|
|
2747
|
-
if (requestBody.text?.format?.type ===
|
|
2748
|
-
parsingFormat =
|
|
2778
|
+
let parsingFormat = "text";
|
|
2779
|
+
if (requestBody.text?.format?.type === "json_object") {
|
|
2780
|
+
parsingFormat = "json";
|
|
2749
2781
|
}
|
|
2750
2782
|
// Handle regular responses
|
|
2751
2783
|
const parsedResponse = await parseResponse(textContent, parsingFormat);
|
|
2752
2784
|
if (parsedResponse === null) {
|
|
2753
|
-
throw new Error(
|
|
2785
|
+
throw new Error("Failed to parse response from Responses API");
|
|
2754
2786
|
}
|
|
2755
2787
|
return {
|
|
2756
2788
|
response: parsedResponse,
|
|
@@ -2758,13 +2790,15 @@ const makeResponsesAPICall = async (input, options = {}) => {
|
|
|
2758
2790
|
prompt_tokens: response.usage?.input_tokens || 0,
|
|
2759
2791
|
completion_tokens: response.usage?.output_tokens || 0,
|
|
2760
2792
|
reasoning_tokens: response.usage?.output_tokens_details?.reasoning_tokens || 0,
|
|
2761
|
-
provider:
|
|
2793
|
+
provider: "openai",
|
|
2762
2794
|
model: normalizedModel,
|
|
2763
2795
|
cached_tokens: responsesCachedTokens,
|
|
2764
|
-
cost: calculateCost(
|
|
2796
|
+
cost: calculateCost("openai", normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens),
|
|
2765
2797
|
},
|
|
2766
2798
|
tool_calls: toolCalls,
|
|
2767
|
-
...(codeInterpreterOutputs
|
|
2799
|
+
...(codeInterpreterOutputs
|
|
2800
|
+
? { code_interpreter_outputs: codeInterpreterOutputs }
|
|
2801
|
+
: {}),
|
|
2768
2802
|
};
|
|
2769
2803
|
};
|
|
2770
2804
|
|
|
@@ -8074,12 +8108,12 @@ function sanitizeObject(obj, sensitiveFields = ['apiKey', 'token', 'secret', 'pa
|
|
|
8074
8108
|
const isRetryableAnthropicError = (error) => {
|
|
8075
8109
|
if (error instanceof Error) {
|
|
8076
8110
|
const message = error.message;
|
|
8077
|
-
if (message.includes(
|
|
8078
|
-
message.includes(
|
|
8079
|
-
message.includes(
|
|
8080
|
-
message.includes(
|
|
8081
|
-
message.includes(
|
|
8082
|
-
message.includes(
|
|
8111
|
+
if (message.includes("429") ||
|
|
8112
|
+
message.includes("500") ||
|
|
8113
|
+
message.includes("503") ||
|
|
8114
|
+
message.includes("529") ||
|
|
8115
|
+
message.includes("rate limit") ||
|
|
8116
|
+
message.includes("overloaded")) {
|
|
8083
8117
|
return true;
|
|
8084
8118
|
}
|
|
8085
8119
|
}
|
|
@@ -8094,8 +8128,11 @@ const isRetryableAnthropicError = (error) => {
|
|
|
8094
8128
|
function translateToolsToAnthropic(tools) {
|
|
8095
8129
|
return tools.map((tool) => ({
|
|
8096
8130
|
name: tool.function.name,
|
|
8097
|
-
description: tool.function.description ||
|
|
8098
|
-
input_schema: (tool.function.parameters || {
|
|
8131
|
+
description: tool.function.description || "",
|
|
8132
|
+
input_schema: (tool.function.parameters || {
|
|
8133
|
+
type: "object",
|
|
8134
|
+
properties: {},
|
|
8135
|
+
}),
|
|
8099
8136
|
}));
|
|
8100
8137
|
}
|
|
8101
8138
|
/**
|
|
@@ -8108,27 +8145,31 @@ function translateContextToAnthropic(context) {
|
|
|
8108
8145
|
const systemParts = [];
|
|
8109
8146
|
const messages = [];
|
|
8110
8147
|
for (const msg of context) {
|
|
8111
|
-
if (msg.role ===
|
|
8112
|
-
if (typeof msg.content ===
|
|
8148
|
+
if (msg.role === "system" || msg.role === "developer") {
|
|
8149
|
+
if (typeof msg.content === "string") {
|
|
8113
8150
|
systemParts.push(msg.content);
|
|
8114
8151
|
}
|
|
8115
8152
|
continue;
|
|
8116
8153
|
}
|
|
8117
|
-
if (msg.role ===
|
|
8118
|
-
const content = typeof msg.content ===
|
|
8154
|
+
if (msg.role === "user") {
|
|
8155
|
+
const content = typeof msg.content === "string"
|
|
8119
8156
|
? msg.content
|
|
8120
8157
|
: JSON.stringify(msg.content);
|
|
8121
|
-
messages.push({ role:
|
|
8158
|
+
messages.push({ role: "user", content });
|
|
8122
8159
|
continue;
|
|
8123
8160
|
}
|
|
8124
|
-
if (msg.role ===
|
|
8161
|
+
if (msg.role === "assistant") {
|
|
8125
8162
|
const assistantMsg = msg;
|
|
8126
8163
|
// If the assistant message has tool_calls, translate to Anthropic tool_use blocks
|
|
8127
8164
|
if (assistantMsg.tool_calls && assistantMsg.tool_calls.length > 0) {
|
|
8128
8165
|
const contentBlocks = [];
|
|
8129
8166
|
// Include text content if present
|
|
8130
|
-
if (typeof assistantMsg.content ===
|
|
8131
|
-
|
|
8167
|
+
if (typeof assistantMsg.content === "string" &&
|
|
8168
|
+
assistantMsg.content.trim()) {
|
|
8169
|
+
contentBlocks.push({
|
|
8170
|
+
type: "text",
|
|
8171
|
+
text: assistantMsg.content,
|
|
8172
|
+
});
|
|
8132
8173
|
}
|
|
8133
8174
|
// Translate each tool_call to a tool_use block
|
|
8134
8175
|
for (const tc of assistantMsg.tool_calls) {
|
|
@@ -8140,32 +8181,36 @@ function translateContextToAnthropic(context) {
|
|
|
8140
8181
|
input = { raw: tc.function.arguments };
|
|
8141
8182
|
}
|
|
8142
8183
|
contentBlocks.push({
|
|
8143
|
-
type:
|
|
8184
|
+
type: "tool_use",
|
|
8144
8185
|
id: tc.id,
|
|
8145
8186
|
name: tc.function.name,
|
|
8146
8187
|
input,
|
|
8147
8188
|
});
|
|
8148
8189
|
}
|
|
8149
|
-
messages.push({ role:
|
|
8190
|
+
messages.push({ role: "assistant", content: contentBlocks });
|
|
8150
8191
|
}
|
|
8151
8192
|
else {
|
|
8152
|
-
const content = typeof assistantMsg.content ===
|
|
8193
|
+
const content = typeof assistantMsg.content === "string"
|
|
8153
8194
|
? assistantMsg.content
|
|
8154
8195
|
: JSON.stringify(assistantMsg.content);
|
|
8155
|
-
messages.push({ role:
|
|
8196
|
+
messages.push({ role: "assistant", content });
|
|
8156
8197
|
}
|
|
8157
8198
|
continue;
|
|
8158
8199
|
}
|
|
8159
|
-
if (msg.role ===
|
|
8200
|
+
if (msg.role === "tool") {
|
|
8160
8201
|
// Anthropic expects tool results as user messages with tool_result content blocks
|
|
8161
8202
|
const toolMsg = msg;
|
|
8162
8203
|
messages.push({
|
|
8163
|
-
role:
|
|
8164
|
-
content: [
|
|
8165
|
-
|
|
8204
|
+
role: "user",
|
|
8205
|
+
content: [
|
|
8206
|
+
{
|
|
8207
|
+
type: "tool_result",
|
|
8166
8208
|
tool_use_id: toolMsg.tool_call_id,
|
|
8167
|
-
content: typeof toolMsg.content ===
|
|
8168
|
-
|
|
8209
|
+
content: typeof toolMsg.content === "string"
|
|
8210
|
+
? toolMsg.content
|
|
8211
|
+
: JSON.stringify(toolMsg.content),
|
|
8212
|
+
},
|
|
8213
|
+
],
|
|
8169
8214
|
});
|
|
8170
8215
|
continue;
|
|
8171
8216
|
}
|
|
@@ -8186,13 +8231,13 @@ function translateContextToAnthropic(context) {
|
|
|
8186
8231
|
merged.push({ role: msg.role, content: toContentBlocks(msg.content) });
|
|
8187
8232
|
}
|
|
8188
8233
|
}
|
|
8189
|
-
return { messages: merged, systemText: systemParts.join(
|
|
8234
|
+
return { messages: merged, systemText: systemParts.join("\n\n") };
|
|
8190
8235
|
}
|
|
8191
8236
|
/** Convert string or content block array to a uniform content block array. */
|
|
8192
8237
|
function toContentBlocks(content) {
|
|
8193
|
-
if (typeof content ===
|
|
8238
|
+
if (typeof content === "string") {
|
|
8194
8239
|
const textBlock = {
|
|
8195
|
-
type:
|
|
8240
|
+
type: "text",
|
|
8196
8241
|
text: content,
|
|
8197
8242
|
citations: null,
|
|
8198
8243
|
};
|
|
@@ -8212,11 +8257,11 @@ function toContentBlocks(content) {
|
|
|
8212
8257
|
* @param options The options for the LLM call.
|
|
8213
8258
|
* @return A promise that resolves to the response from the Anthropic API.
|
|
8214
8259
|
*/
|
|
8215
|
-
async function makeAnthropicCall(content, responseFormat =
|
|
8216
|
-
const model = (options.model ||
|
|
8260
|
+
async function makeAnthropicCall(content, responseFormat = "text", options = {}) {
|
|
8261
|
+
const model = (options.model || "claude-sonnet-4-6");
|
|
8217
8262
|
const apiKey = options.apiKey || getSecrets().anthropic.apiKey;
|
|
8218
8263
|
if (!apiKey) {
|
|
8219
|
-
throw new Error(
|
|
8264
|
+
throw new Error("Anthropic API key is not provided and ANTHROPIC_API_KEY environment variable is not set");
|
|
8220
8265
|
}
|
|
8221
8266
|
const client = new Anthropic({
|
|
8222
8267
|
apiKey,
|
|
@@ -8228,8 +8273,10 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
|
|
|
8228
8273
|
systemParts.push(options.developerPrompt);
|
|
8229
8274
|
}
|
|
8230
8275
|
// If JSON response is requested, instruct the model via system prompt
|
|
8231
|
-
if (responseFormat ===
|
|
8232
|
-
|
|
8276
|
+
if (responseFormat === "json" ||
|
|
8277
|
+
(typeof responseFormat === "object" &&
|
|
8278
|
+
responseFormat.type === "json_schema")) {
|
|
8279
|
+
systemParts.push("You MUST respond with valid JSON only. No markdown, no code blocks, no explanatory text — just the raw JSON object or array.");
|
|
8233
8280
|
}
|
|
8234
8281
|
// Build messages array
|
|
8235
8282
|
const messages = [];
|
|
@@ -8242,7 +8289,7 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
|
|
|
8242
8289
|
messages.push(...translated.messages);
|
|
8243
8290
|
}
|
|
8244
8291
|
// Add user content
|
|
8245
|
-
messages.push({ role:
|
|
8292
|
+
messages.push({ role: "user", content });
|
|
8246
8293
|
// Build request params
|
|
8247
8294
|
const requestParams = {
|
|
8248
8295
|
model,
|
|
@@ -8251,7 +8298,7 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
|
|
|
8251
8298
|
};
|
|
8252
8299
|
// Add system prompt if present
|
|
8253
8300
|
if (systemParts.length > 0) {
|
|
8254
|
-
requestParams.system = systemParts.join(
|
|
8301
|
+
requestParams.system = systemParts.join("\n\n");
|
|
8255
8302
|
}
|
|
8256
8303
|
// Add temperature if specified
|
|
8257
8304
|
if (options.temperature !== undefined) {
|
|
@@ -8274,14 +8321,14 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
|
|
|
8274
8321
|
const inputTokens = response.usage.input_tokens;
|
|
8275
8322
|
const outputTokens = response.usage.output_tokens;
|
|
8276
8323
|
// Track cost
|
|
8277
|
-
getLLMCostTracker().trackUsage(
|
|
8324
|
+
getLLMCostTracker().trackUsage("anthropic", model, inputTokens, outputTokens);
|
|
8278
8325
|
// Extract tool use blocks
|
|
8279
|
-
const toolUseBlocks = response.content.filter((block) => block.type ===
|
|
8326
|
+
const toolUseBlocks = response.content.filter((block) => block.type === "tool_use");
|
|
8280
8327
|
if (toolUseBlocks.length > 0) {
|
|
8281
8328
|
// Translate Anthropic tool_use to OpenAI tool_calls format
|
|
8282
8329
|
const toolCalls = toolUseBlocks.map((block) => ({
|
|
8283
8330
|
id: block.id,
|
|
8284
|
-
type:
|
|
8331
|
+
type: "function",
|
|
8285
8332
|
function: {
|
|
8286
8333
|
name: block.name,
|
|
8287
8334
|
arguments: JSON.stringify(block.input),
|
|
@@ -8300,22 +8347,22 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
|
|
|
8300
8347
|
prompt_tokens: inputTokens,
|
|
8301
8348
|
completion_tokens: outputTokens,
|
|
8302
8349
|
reasoning_tokens: 0,
|
|
8303
|
-
provider:
|
|
8350
|
+
provider: "anthropic",
|
|
8304
8351
|
model,
|
|
8305
|
-
cost: calculateCost(
|
|
8352
|
+
cost: calculateCost("anthropic", model, inputTokens, outputTokens),
|
|
8306
8353
|
},
|
|
8307
8354
|
tool_calls: toolCalls,
|
|
8308
8355
|
};
|
|
8309
8356
|
}
|
|
8310
8357
|
// Extract text content
|
|
8311
8358
|
const textContent = response.content
|
|
8312
|
-
.filter((block) => block.type ===
|
|
8359
|
+
.filter((block) => block.type === "text")
|
|
8313
8360
|
.map((block) => block.text)
|
|
8314
|
-
.join(
|
|
8361
|
+
.join("");
|
|
8315
8362
|
// Parse response
|
|
8316
8363
|
const parsedResponse = await parseResponse(textContent, responseFormat);
|
|
8317
8364
|
if (parsedResponse === null) {
|
|
8318
|
-
throw new Error(
|
|
8365
|
+
throw new Error("Failed to parse Anthropic response");
|
|
8319
8366
|
}
|
|
8320
8367
|
return {
|
|
8321
8368
|
response: parsedResponse,
|
|
@@ -8323,9 +8370,9 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
|
|
|
8323
8370
|
prompt_tokens: inputTokens,
|
|
8324
8371
|
completion_tokens: outputTokens,
|
|
8325
8372
|
reasoning_tokens: 0,
|
|
8326
|
-
provider:
|
|
8373
|
+
provider: "anthropic",
|
|
8327
8374
|
model,
|
|
8328
|
-
cost: calculateCost(
|
|
8375
|
+
cost: calculateCost("anthropic", model, inputTokens, outputTokens),
|
|
8329
8376
|
},
|
|
8330
8377
|
};
|
|
8331
8378
|
}
|
|
@@ -8347,7 +8394,9 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
|
|
|
8347
8394
|
const isRetryableError = (error) => {
|
|
8348
8395
|
if (error instanceof Error) {
|
|
8349
8396
|
const message = error.message;
|
|
8350
|
-
if (message.includes(
|
|
8397
|
+
if (message.includes("429") ||
|
|
8398
|
+
message.includes("rate limit") ||
|
|
8399
|
+
message.includes("Rate limit")) {
|
|
8351
8400
|
return true;
|
|
8352
8401
|
}
|
|
8353
8402
|
}
|
|
@@ -8361,14 +8410,16 @@ function resolveApiKey(provider, optionsApiKey) {
|
|
|
8361
8410
|
if (optionsApiKey)
|
|
8362
8411
|
return optionsApiKey;
|
|
8363
8412
|
const secrets = getSecrets();
|
|
8364
|
-
const pathParts = provider.apiKeySecretPath.split(
|
|
8413
|
+
const pathParts = provider.apiKeySecretPath.split(".");
|
|
8365
8414
|
let current = secrets;
|
|
8366
8415
|
for (const part of pathParts) {
|
|
8367
|
-
if (current === null ||
|
|
8368
|
-
|
|
8416
|
+
if (current === null ||
|
|
8417
|
+
current === undefined ||
|
|
8418
|
+
typeof current !== "object")
|
|
8419
|
+
return "";
|
|
8369
8420
|
current = current[part];
|
|
8370
8421
|
}
|
|
8371
|
-
return (typeof current ===
|
|
8422
|
+
return (typeof current === "string" ? current : "") || "";
|
|
8372
8423
|
}
|
|
8373
8424
|
/**
|
|
8374
8425
|
* Makes a call to an OpenAI-compatible provider (Kimi, Qwen, or any future provider).
|
|
@@ -8382,12 +8433,12 @@ function resolveApiKey(provider, optionsApiKey) {
|
|
|
8382
8433
|
* @param providerName The provider key in OPENAI_COMPATIBLE_PROVIDERS.
|
|
8383
8434
|
* @return A promise that resolves to the response from the provider.
|
|
8384
8435
|
*/
|
|
8385
|
-
async function makeOpenAICompatibleCall(content, responseFormat =
|
|
8436
|
+
async function makeOpenAICompatibleCall(content, responseFormat = "text", options = {}, providerName) {
|
|
8386
8437
|
const providerConfig = OPENAI_COMPATIBLE_PROVIDERS[providerName];
|
|
8387
8438
|
if (!providerConfig) {
|
|
8388
8439
|
throw new Error(`Unknown OpenAI-compatible provider: ${providerName}`);
|
|
8389
8440
|
}
|
|
8390
|
-
const model = normalizeModelName(options.model ||
|
|
8441
|
+
const model = normalizeModelName(options.model || "");
|
|
8391
8442
|
const apiKey = resolveApiKey(providerConfig, options.apiKey);
|
|
8392
8443
|
if (!apiKey) {
|
|
8393
8444
|
throw new Error(`${providerConfig.name} API key is not provided and ${providerConfig.apiKeyEnvVar} environment variable is not set`);
|
|
@@ -8400,31 +8451,36 @@ async function makeOpenAICompatibleCall(content, responseFormat = 'text', option
|
|
|
8400
8451
|
const messages = [];
|
|
8401
8452
|
// Add system message with developer prompt if present
|
|
8402
8453
|
if (options.developerPrompt) {
|
|
8403
|
-
messages.push({ role:
|
|
8454
|
+
messages.push({ role: "system", content: options.developerPrompt });
|
|
8404
8455
|
}
|
|
8405
8456
|
// Add context if present
|
|
8406
8457
|
if (options.context) {
|
|
8407
8458
|
messages.push(...options.context);
|
|
8408
8459
|
}
|
|
8409
8460
|
// Add user content
|
|
8410
|
-
if (typeof content ===
|
|
8411
|
-
messages.push({ role:
|
|
8461
|
+
if (typeof content === "string") {
|
|
8462
|
+
messages.push({ role: "user", content });
|
|
8412
8463
|
}
|
|
8413
8464
|
else {
|
|
8414
|
-
messages.push({ role:
|
|
8465
|
+
messages.push({ role: "user", content });
|
|
8415
8466
|
}
|
|
8416
8467
|
// Determine if the model supports JSON mode
|
|
8417
8468
|
const supportsJson = isValidModel(model) && getModelCapabilities(model).supportsJson;
|
|
8418
8469
|
// If JSON response format, add hint to system prompt
|
|
8419
|
-
if ((responseFormat ===
|
|
8470
|
+
if ((responseFormat === "json" ||
|
|
8471
|
+
(typeof responseFormat === "object" &&
|
|
8472
|
+
responseFormat.type === "json_schema")) &&
|
|
8420
8473
|
supportsJson) {
|
|
8421
|
-
if (!messages.some((m) => m.role ===
|
|
8422
|
-
messages.unshift({
|
|
8474
|
+
if (!messages.some((m) => m.role === "system")) {
|
|
8475
|
+
messages.unshift({
|
|
8476
|
+
role: "system",
|
|
8477
|
+
content: "Please respond in valid JSON format.",
|
|
8478
|
+
});
|
|
8423
8479
|
}
|
|
8424
8480
|
else {
|
|
8425
|
-
const systemMsgIndex = messages.findIndex((m) => m.role ===
|
|
8481
|
+
const systemMsgIndex = messages.findIndex((m) => m.role === "system");
|
|
8426
8482
|
const systemMsg = messages[systemMsgIndex];
|
|
8427
|
-
if (typeof systemMsg.content ===
|
|
8483
|
+
if (typeof systemMsg.content === "string") {
|
|
8428
8484
|
messages[systemMsgIndex] = {
|
|
8429
8485
|
...systemMsg,
|
|
8430
8486
|
content: `${systemMsg.content} Please respond in valid JSON format.`,
|
|
@@ -8437,8 +8493,8 @@ async function makeOpenAICompatibleCall(content, responseFormat = 'text', option
|
|
|
8437
8493
|
messages,
|
|
8438
8494
|
};
|
|
8439
8495
|
// Add response format if JSON is supported
|
|
8440
|
-
if (responseFormat !==
|
|
8441
|
-
queryOptions.response_format = { type:
|
|
8496
|
+
if (responseFormat !== "text" && supportsJson) {
|
|
8497
|
+
queryOptions.response_format = { type: "json_object" };
|
|
8442
8498
|
}
|
|
8443
8499
|
// Temperature
|
|
8444
8500
|
if (options.temperature !== undefined) {
|
|
@@ -8496,7 +8552,7 @@ async function makeOpenAICompatibleCall(content, responseFormat = 'text', option
|
|
|
8496
8552
|
};
|
|
8497
8553
|
}
|
|
8498
8554
|
// Handle regular responses
|
|
8499
|
-
const textContent = completion.choices[0]?.message?.content ||
|
|
8555
|
+
const textContent = completion.choices[0]?.message?.content || "";
|
|
8500
8556
|
const parsedResponse = await parseResponse(textContent, responseFormat);
|
|
8501
8557
|
if (parsedResponse === null) {
|
|
8502
8558
|
throw new Error(`Failed to parse ${providerConfig.name} response`);
|
|
@@ -8788,7 +8844,9 @@ const isRetryableDeepseekError = (error) => {
|
|
|
8788
8844
|
if (error instanceof Error) {
|
|
8789
8845
|
const message = error.message;
|
|
8790
8846
|
// Retry only on rate limits (429)
|
|
8791
|
-
if (message.includes(
|
|
8847
|
+
if (message.includes("429") ||
|
|
8848
|
+
message.includes("rate limit") ||
|
|
8849
|
+
message.includes("Rate limit")) {
|
|
8792
8850
|
return true;
|
|
8793
8851
|
}
|
|
8794
8852
|
}
|
|
@@ -8798,7 +8856,7 @@ const isRetryableDeepseekError = (error) => {
|
|
|
8798
8856
|
* Default options for Deepseek API calls
|
|
8799
8857
|
*/
|
|
8800
8858
|
const DEFAULT_DEEPSEEK_OPTIONS = {
|
|
8801
|
-
defaultModel:
|
|
8859
|
+
defaultModel: "deepseek-chat",
|
|
8802
8860
|
};
|
|
8803
8861
|
/**
|
|
8804
8862
|
* Checks if the given model is a supported Deepseek model
|
|
@@ -8810,7 +8868,7 @@ const isSupportedDeepseekModel = (model) => {
|
|
|
8810
8868
|
return false;
|
|
8811
8869
|
}
|
|
8812
8870
|
const capabilities = getModelCapabilities(model);
|
|
8813
|
-
return capabilities.provider ===
|
|
8871
|
+
return capabilities.provider === "deepseek";
|
|
8814
8872
|
};
|
|
8815
8873
|
/**
|
|
8816
8874
|
* Checks if the given Deepseek model supports JSON output format
|
|
@@ -8843,17 +8901,19 @@ const supportsToolCalling = (model) => {
|
|
|
8843
8901
|
*/
|
|
8844
8902
|
function getDeepseekResponseFormatOption(responseFormat, model) {
|
|
8845
8903
|
// Check if the model supports JSON output
|
|
8846
|
-
if (responseFormat !==
|
|
8904
|
+
if (responseFormat !== "text" && !supportsJsonOutput(model)) {
|
|
8847
8905
|
getLumicLogger().warn(`Model ${model} does not support JSON output. Using text format instead.`);
|
|
8848
|
-
return { type:
|
|
8906
|
+
return { type: "text" };
|
|
8849
8907
|
}
|
|
8850
|
-
if (responseFormat ===
|
|
8851
|
-
return { type:
|
|
8908
|
+
if (responseFormat === "text") {
|
|
8909
|
+
return { type: "text" };
|
|
8852
8910
|
}
|
|
8853
|
-
if (responseFormat ===
|
|
8854
|
-
|
|
8911
|
+
if (responseFormat === "json" ||
|
|
8912
|
+
(typeof responseFormat === "object" &&
|
|
8913
|
+
responseFormat.type === "json_schema")) {
|
|
8914
|
+
return { type: "json_object" };
|
|
8855
8915
|
}
|
|
8856
|
-
return { type:
|
|
8916
|
+
return { type: "text" };
|
|
8857
8917
|
}
|
|
8858
8918
|
/**
|
|
8859
8919
|
* Creates a completion using the Deepseek API
|
|
@@ -8872,24 +8932,26 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
|
|
|
8872
8932
|
throw new Error(`Unsupported Deepseek model: ${normalizedModel}. Please use 'deepseek-chat' or 'deepseek-reasoner'.`);
|
|
8873
8933
|
}
|
|
8874
8934
|
// Check if tools are requested with a model that doesn't support them
|
|
8875
|
-
if (options.tools &&
|
|
8935
|
+
if (options.tools &&
|
|
8936
|
+
options.tools.length > 0 &&
|
|
8937
|
+
!supportsToolCalling(normalizedModel)) {
|
|
8876
8938
|
throw new Error(`Model ${normalizedModel} does not support tool calling.`);
|
|
8877
8939
|
}
|
|
8878
8940
|
const apiKey = options.apiKey || getSecrets().deepseek.apiKey;
|
|
8879
8941
|
if (!apiKey) {
|
|
8880
|
-
throw new Error(
|
|
8942
|
+
throw new Error("Deepseek API key is not provided and DEEPSEEK_API_KEY environment variable is not set");
|
|
8881
8943
|
}
|
|
8882
8944
|
// Initialize OpenAI client with Deepseek API URL
|
|
8883
8945
|
const openai = new OpenAI({
|
|
8884
8946
|
apiKey,
|
|
8885
|
-
baseURL:
|
|
8947
|
+
baseURL: "https://api.deepseek.com",
|
|
8886
8948
|
timeout: options.timeout ?? LLM_TIMEOUT_MS,
|
|
8887
8949
|
});
|
|
8888
8950
|
const messages = [];
|
|
8889
8951
|
// Add system message with developer prompt if present
|
|
8890
8952
|
if (options.developerPrompt) {
|
|
8891
8953
|
messages.push({
|
|
8892
|
-
role:
|
|
8954
|
+
role: "system",
|
|
8893
8955
|
content: options.developerPrompt,
|
|
8894
8956
|
});
|
|
8895
8957
|
}
|
|
@@ -8898,33 +8960,35 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
|
|
|
8898
8960
|
messages.push(...options.context);
|
|
8899
8961
|
}
|
|
8900
8962
|
// Add user content
|
|
8901
|
-
if (typeof content ===
|
|
8963
|
+
if (typeof content === "string") {
|
|
8902
8964
|
messages.push({
|
|
8903
|
-
role:
|
|
8965
|
+
role: "user",
|
|
8904
8966
|
content,
|
|
8905
8967
|
});
|
|
8906
8968
|
}
|
|
8907
8969
|
else {
|
|
8908
8970
|
messages.push({
|
|
8909
|
-
role:
|
|
8971
|
+
role: "user",
|
|
8910
8972
|
content,
|
|
8911
8973
|
});
|
|
8912
8974
|
}
|
|
8913
8975
|
// If JSON response format, include a hint in the system prompt
|
|
8914
|
-
if ((responseFormat ===
|
|
8915
|
-
|
|
8976
|
+
if ((responseFormat === "json" ||
|
|
8977
|
+
(typeof responseFormat === "object" &&
|
|
8978
|
+
responseFormat.type === "json_schema")) &&
|
|
8979
|
+
supportsJsonOutput(normalizedModel)) {
|
|
8916
8980
|
// If there's no system message yet, add one
|
|
8917
|
-
if (!messages.some(m => m.role ===
|
|
8981
|
+
if (!messages.some((m) => m.role === "system")) {
|
|
8918
8982
|
messages.unshift({
|
|
8919
|
-
role:
|
|
8920
|
-
content:
|
|
8983
|
+
role: "system",
|
|
8984
|
+
content: "Please respond in valid JSON format.",
|
|
8921
8985
|
});
|
|
8922
8986
|
}
|
|
8923
8987
|
else {
|
|
8924
8988
|
// Append to existing system message
|
|
8925
|
-
const systemMsgIndex = messages.findIndex(m => m.role ===
|
|
8989
|
+
const systemMsgIndex = messages.findIndex((m) => m.role === "system");
|
|
8926
8990
|
const systemMsg = messages[systemMsgIndex];
|
|
8927
|
-
if (typeof systemMsg.content ===
|
|
8991
|
+
if (typeof systemMsg.content === "string") {
|
|
8928
8992
|
messages[systemMsgIndex] = {
|
|
8929
8993
|
...systemMsg,
|
|
8930
8994
|
content: `${systemMsg.content} Please respond in valid JSON format.`,
|
|
@@ -8965,7 +9029,7 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
|
|
|
8965
9029
|
0;
|
|
8966
9030
|
return {
|
|
8967
9031
|
id: completion.id,
|
|
8968
|
-
content: completion.choices[0]?.message?.content ||
|
|
9032
|
+
content: completion.choices[0]?.message?.content || "",
|
|
8969
9033
|
tool_calls: completion.choices[0]?.message?.tool_calls,
|
|
8970
9034
|
usage: {
|
|
8971
9035
|
prompt_tokens: completion.usage?.prompt_tokens ?? 0,
|
|
@@ -8974,7 +9038,7 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
|
|
|
8974
9038
|
cached_tokens: cachedTokens,
|
|
8975
9039
|
},
|
|
8976
9040
|
system_fingerprint: completion.system_fingerprint,
|
|
8977
|
-
provider:
|
|
9041
|
+
provider: "deepseek",
|
|
8978
9042
|
model: normalizedModel,
|
|
8979
9043
|
};
|
|
8980
9044
|
}
|
|
@@ -8991,7 +9055,7 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
|
|
|
8991
9055
|
* @param options Configuration options including model ('deepseek-chat' or 'deepseek-reasoner'), tools, and apiKey.
|
|
8992
9056
|
* @return A promise that resolves to the response from the Deepseek API.
|
|
8993
9057
|
*/
|
|
8994
|
-
const makeDeepseekCall = async (content, responseFormat =
|
|
9058
|
+
const makeDeepseekCall = async (content, responseFormat = "json", options = {}) => {
|
|
8995
9059
|
// Set default model if not provided
|
|
8996
9060
|
const mergedOptions = {
|
|
8997
9061
|
...options,
|
|
@@ -9001,17 +9065,17 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
|
|
|
9001
9065
|
}
|
|
9002
9066
|
const modelName = normalizeModelName(mergedOptions.model);
|
|
9003
9067
|
// Check if the requested response format is compatible with the model
|
|
9004
|
-
if (responseFormat !==
|
|
9068
|
+
if (responseFormat !== "text" && !supportsJsonOutput(modelName)) {
|
|
9005
9069
|
getLumicLogger().warn(`Model ${modelName} does not support JSON output. Will return error in the response.`);
|
|
9006
9070
|
return {
|
|
9007
9071
|
response: {
|
|
9008
|
-
error: `Model ${modelName} does not support JSON output format
|
|
9072
|
+
error: `Model ${modelName} does not support JSON output format.`,
|
|
9009
9073
|
},
|
|
9010
9074
|
usage: {
|
|
9011
9075
|
prompt_tokens: 0,
|
|
9012
9076
|
completion_tokens: 0,
|
|
9013
9077
|
reasoning_tokens: 0,
|
|
9014
|
-
provider:
|
|
9078
|
+
provider: "deepseek",
|
|
9015
9079
|
model: modelName,
|
|
9016
9080
|
cached_tokens: 0,
|
|
9017
9081
|
cost: 0,
|
|
@@ -9020,17 +9084,19 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
|
|
|
9020
9084
|
};
|
|
9021
9085
|
}
|
|
9022
9086
|
// Check if tools are requested with a model that doesn't support them
|
|
9023
|
-
if (mergedOptions.tools &&
|
|
9087
|
+
if (mergedOptions.tools &&
|
|
9088
|
+
mergedOptions.tools.length > 0 &&
|
|
9089
|
+
!supportsToolCalling(modelName)) {
|
|
9024
9090
|
getLumicLogger().warn(`Model ${modelName} does not support tool calling. Will return error in the response.`);
|
|
9025
9091
|
return {
|
|
9026
9092
|
response: {
|
|
9027
|
-
error: `Model ${modelName} does not support tool calling
|
|
9093
|
+
error: `Model ${modelName} does not support tool calling.`,
|
|
9028
9094
|
},
|
|
9029
9095
|
usage: {
|
|
9030
9096
|
prompt_tokens: 0,
|
|
9031
9097
|
completion_tokens: 0,
|
|
9032
9098
|
reasoning_tokens: 0,
|
|
9033
|
-
provider:
|
|
9099
|
+
provider: "deepseek",
|
|
9034
9100
|
model: modelName,
|
|
9035
9101
|
cached_tokens: 0,
|
|
9036
9102
|
cost: 0,
|
|
@@ -9042,7 +9108,7 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
|
|
|
9042
9108
|
const completion = await createDeepseekCompletion(content, responseFormat, mergedOptions);
|
|
9043
9109
|
// Track cost in the global cost tracker. Pass cached tokens through so the
|
|
9044
9110
|
// discounted cached-input pricing tier is applied.
|
|
9045
|
-
getLLMCostTracker().trackUsage(
|
|
9111
|
+
getLLMCostTracker().trackUsage("deepseek", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens);
|
|
9046
9112
|
// Handle tool calls similarly to OpenAI
|
|
9047
9113
|
if (completion.tool_calls && completion.tool_calls.length > 0) {
|
|
9048
9114
|
const toolCallResponse = {
|
|
@@ -9058,10 +9124,10 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
|
|
|
9058
9124
|
prompt_tokens: completion.usage.prompt_tokens,
|
|
9059
9125
|
completion_tokens: completion.usage.completion_tokens,
|
|
9060
9126
|
reasoning_tokens: 0, // Deepseek doesn't provide reasoning tokens separately
|
|
9061
|
-
provider:
|
|
9127
|
+
provider: "deepseek",
|
|
9062
9128
|
model: completion.model,
|
|
9063
9129
|
cached_tokens: completion.usage.cached_tokens,
|
|
9064
|
-
cost: calculateCost(
|
|
9130
|
+
cost: calculateCost("deepseek", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
|
|
9065
9131
|
},
|
|
9066
9132
|
tool_calls: completion.tool_calls,
|
|
9067
9133
|
};
|
|
@@ -9069,7 +9135,7 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
|
|
|
9069
9135
|
// Handle regular responses
|
|
9070
9136
|
const parsedResponse = await parseResponse(completion.content, responseFormat);
|
|
9071
9137
|
if (parsedResponse === null) {
|
|
9072
|
-
throw new Error(
|
|
9138
|
+
throw new Error("Failed to parse Deepseek response");
|
|
9073
9139
|
}
|
|
9074
9140
|
return {
|
|
9075
9141
|
response: parsedResponse,
|
|
@@ -9077,10 +9143,10 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
|
|
|
9077
9143
|
prompt_tokens: completion.usage.prompt_tokens,
|
|
9078
9144
|
completion_tokens: completion.usage.completion_tokens,
|
|
9079
9145
|
reasoning_tokens: 0, // Deepseek doesn't provide reasoning tokens separately
|
|
9080
|
-
provider:
|
|
9146
|
+
provider: "deepseek",
|
|
9081
9147
|
model: completion.model,
|
|
9082
9148
|
cached_tokens: completion.usage.cached_tokens,
|
|
9083
|
-
cost: calculateCost(
|
|
9149
|
+
cost: calculateCost("deepseek", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
|
|
9084
9150
|
},
|
|
9085
9151
|
tool_calls: completion.tool_calls,
|
|
9086
9152
|
};
|
|
@@ -9090,13 +9156,13 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
|
|
|
9090
9156
|
getLumicLogger().error(`Error in Deepseek API call: ${sanitizeError(error)}`);
|
|
9091
9157
|
return {
|
|
9092
9158
|
response: {
|
|
9093
|
-
error: sanitizeError(error)
|
|
9159
|
+
error: sanitizeError(error),
|
|
9094
9160
|
},
|
|
9095
9161
|
usage: {
|
|
9096
9162
|
prompt_tokens: 0,
|
|
9097
9163
|
completion_tokens: 0,
|
|
9098
9164
|
reasoning_tokens: 0,
|
|
9099
|
-
provider:
|
|
9165
|
+
provider: "deepseek",
|
|
9100
9166
|
model: modelName,
|
|
9101
9167
|
cached_tokens: 0,
|
|
9102
9168
|
cost: 0,
|
|
@@ -9205,15 +9271,15 @@ const generateCacheKey = (auth, envVarsCheck = null) => {
|
|
|
9205
9271
|
if (auth) {
|
|
9206
9272
|
return `${auth.AWS_ACCESS_KEY_ID}:${auth.AWS_SECRET_ACCESS_KEY}:${auth.AWS_REGION}`;
|
|
9207
9273
|
}
|
|
9208
|
-
if (envVarsCheck?.source ===
|
|
9274
|
+
if (envVarsCheck?.source === "lambda") {
|
|
9209
9275
|
// In Lambda, use function name as cache key since it uses IAM role
|
|
9210
9276
|
const secrets = getSecrets();
|
|
9211
|
-
return `lambda:${secrets.aws.lambdaFunctionName ||
|
|
9277
|
+
return `lambda:${secrets.aws.lambdaFunctionName || "default"}`;
|
|
9212
9278
|
}
|
|
9213
9279
|
if (envVarsCheck?.vars) {
|
|
9214
9280
|
return `${envVarsCheck.vars.accessKeyId}:${envVarsCheck.vars.secretAccessKey}:${envVarsCheck.vars.region}`;
|
|
9215
9281
|
}
|
|
9216
|
-
return
|
|
9282
|
+
return "default";
|
|
9217
9283
|
};
|
|
9218
9284
|
const validateEnvironmentVars = () => {
|
|
9219
9285
|
const secrets = getSecrets();
|
|
@@ -9226,37 +9292,37 @@ const validateEnvironmentVars = () => {
|
|
|
9226
9292
|
// We're in a Lambda environment
|
|
9227
9293
|
return {
|
|
9228
9294
|
isValid: true,
|
|
9229
|
-
source:
|
|
9230
|
-
vars: { accessKeyId:
|
|
9295
|
+
source: "lambda",
|
|
9296
|
+
vars: { accessKeyId: "", secretAccessKey: "", region: "" }, // No need for explicit credentials in Lambda
|
|
9231
9297
|
};
|
|
9232
9298
|
}
|
|
9233
9299
|
else if (areVarsComplete(secrets.aws.myAccessKeyId, secrets.aws.mySecretAccessKey, secrets.aws.myRegion)) {
|
|
9234
9300
|
return {
|
|
9235
9301
|
isValid: true,
|
|
9236
|
-
source:
|
|
9302
|
+
source: "my_prefix",
|
|
9237
9303
|
vars: {
|
|
9238
|
-
accessKeyId: secrets.aws.myAccessKeyId,
|
|
9239
|
-
secretAccessKey: secrets.aws.mySecretAccessKey,
|
|
9240
|
-
region: secrets.aws.myRegion
|
|
9241
|
-
}
|
|
9304
|
+
accessKeyId: secrets.aws.myAccessKeyId ?? "",
|
|
9305
|
+
secretAccessKey: secrets.aws.mySecretAccessKey ?? "",
|
|
9306
|
+
region: secrets.aws.myRegion ?? "",
|
|
9307
|
+
},
|
|
9242
9308
|
};
|
|
9243
9309
|
}
|
|
9244
9310
|
else if (areVarsComplete(secrets.aws.accessKeyId, secrets.aws.secretAccessKey, secrets.aws.region)) {
|
|
9245
9311
|
return {
|
|
9246
9312
|
isValid: true,
|
|
9247
|
-
source:
|
|
9313
|
+
source: "standard",
|
|
9248
9314
|
vars: {
|
|
9249
|
-
accessKeyId: secrets.aws.accessKeyId,
|
|
9250
|
-
secretAccessKey: secrets.aws.secretAccessKey,
|
|
9251
|
-
region: secrets.aws.region
|
|
9252
|
-
}
|
|
9315
|
+
accessKeyId: secrets.aws.accessKeyId ?? "",
|
|
9316
|
+
secretAccessKey: secrets.aws.secretAccessKey ?? "",
|
|
9317
|
+
region: secrets.aws.region ?? "",
|
|
9318
|
+
},
|
|
9253
9319
|
};
|
|
9254
9320
|
}
|
|
9255
9321
|
// If we get here, no complete set of variables was found
|
|
9256
9322
|
return {
|
|
9257
9323
|
isValid: false,
|
|
9258
9324
|
source: null,
|
|
9259
|
-
missingVars: []
|
|
9325
|
+
missingVars: [],
|
|
9260
9326
|
};
|
|
9261
9327
|
};
|
|
9262
9328
|
const initialiseAWSClient = (ClientClass, auth = null) => {
|
|
@@ -9265,48 +9331,52 @@ const initialiseAWSClient = (ClientClass, auth = null) => {
|
|
|
9265
9331
|
const requestTimeout = ClientClass === S3Client ? AWS_S3_TIMEOUT_MS : AWS_LAMBDA_TIMEOUT_MS;
|
|
9266
9332
|
// Case 1: Explicit auth provided
|
|
9267
9333
|
if (auth) {
|
|
9268
|
-
if (typeof auth !==
|
|
9269
|
-
throw new Error(
|
|
9334
|
+
if (typeof auth !== "object" || auth === null) {
|
|
9335
|
+
throw new Error("Auth parameter must be an object");
|
|
9270
9336
|
}
|
|
9271
|
-
const requiredAuthFields = [
|
|
9272
|
-
|
|
9337
|
+
const requiredAuthFields = [
|
|
9338
|
+
"AWS_ACCESS_KEY_ID",
|
|
9339
|
+
"AWS_SECRET_ACCESS_KEY",
|
|
9340
|
+
"AWS_REGION",
|
|
9341
|
+
];
|
|
9342
|
+
const missingFields = requiredAuthFields.filter((field) => !auth[field]);
|
|
9273
9343
|
if (missingFields.length > 0) {
|
|
9274
|
-
throw new Error(`Auth object is missing required fields: ${missingFields.join(
|
|
9344
|
+
throw new Error(`Auth object is missing required fields: ${missingFields.join(", ")}`);
|
|
9275
9345
|
}
|
|
9276
9346
|
return new ClientClass({
|
|
9277
9347
|
region: auth.AWS_REGION,
|
|
9278
9348
|
credentials: {
|
|
9279
9349
|
accessKeyId: auth.AWS_ACCESS_KEY_ID,
|
|
9280
|
-
secretAccessKey: auth.AWS_SECRET_ACCESS_KEY
|
|
9350
|
+
secretAccessKey: auth.AWS_SECRET_ACCESS_KEY,
|
|
9281
9351
|
},
|
|
9282
9352
|
requestHandler: {
|
|
9283
|
-
requestTimeout
|
|
9284
|
-
}
|
|
9353
|
+
requestTimeout,
|
|
9354
|
+
},
|
|
9285
9355
|
});
|
|
9286
9356
|
}
|
|
9287
9357
|
// Case 2: Check environment variables
|
|
9288
9358
|
const envVarsCheck = validateEnvironmentVars();
|
|
9289
9359
|
if (!envVarsCheck.isValid) {
|
|
9290
|
-
throw new Error(
|
|
9360
|
+
throw new Error("No valid AWS credentials found in environment variables");
|
|
9291
9361
|
}
|
|
9292
9362
|
// Case 2a: Lambda execution environment
|
|
9293
|
-
if (envVarsCheck.source ===
|
|
9363
|
+
if (envVarsCheck.source === "lambda") {
|
|
9294
9364
|
return new ClientClass({
|
|
9295
9365
|
requestHandler: {
|
|
9296
|
-
requestTimeout
|
|
9297
|
-
}
|
|
9366
|
+
requestTimeout,
|
|
9367
|
+
},
|
|
9298
9368
|
}); // AWS SDK will automatically use Lambda role credentials
|
|
9299
9369
|
}
|
|
9300
9370
|
// Case 2b: MY_ prefixed vars or standard AWS vars
|
|
9301
9371
|
return new ClientClass({
|
|
9302
|
-
region: envVarsCheck.vars
|
|
9372
|
+
region: envVarsCheck.vars?.region ?? "",
|
|
9303
9373
|
credentials: {
|
|
9304
|
-
accessKeyId: envVarsCheck.vars
|
|
9305
|
-
secretAccessKey: envVarsCheck.vars
|
|
9374
|
+
accessKeyId: envVarsCheck.vars?.accessKeyId ?? "",
|
|
9375
|
+
secretAccessKey: envVarsCheck.vars?.secretAccessKey ?? "",
|
|
9306
9376
|
},
|
|
9307
9377
|
requestHandler: {
|
|
9308
|
-
requestTimeout
|
|
9309
|
-
}
|
|
9378
|
+
requestTimeout,
|
|
9379
|
+
},
|
|
9310
9380
|
});
|
|
9311
9381
|
}
|
|
9312
9382
|
catch (error) {
|
|
@@ -9319,7 +9389,9 @@ const initialiseS3Client = (auth = null) => {
|
|
|
9319
9389
|
const envVarsCheck = auth ? null : validateEnvironmentVars();
|
|
9320
9390
|
const cacheKey = generateCacheKey(auth, envVarsCheck);
|
|
9321
9391
|
if (s3ClientCache.has(cacheKey)) {
|
|
9322
|
-
|
|
9392
|
+
const cached = s3ClientCache.get(cacheKey);
|
|
9393
|
+
if (cached)
|
|
9394
|
+
return cached;
|
|
9323
9395
|
}
|
|
9324
9396
|
// Create new client and cache it
|
|
9325
9397
|
const client = initialiseAWSClient(S3Client, auth);
|
|
@@ -9331,7 +9403,9 @@ const initialiseLambdaClient = (auth = null) => {
|
|
|
9331
9403
|
const envVarsCheck = auth ? null : validateEnvironmentVars();
|
|
9332
9404
|
const cacheKey = generateCacheKey(auth, envVarsCheck);
|
|
9333
9405
|
if (lambdaClientCache.has(cacheKey)) {
|
|
9334
|
-
|
|
9406
|
+
const cached = lambdaClientCache.get(cacheKey);
|
|
9407
|
+
if (cached)
|
|
9408
|
+
return cached;
|
|
9335
9409
|
}
|
|
9336
9410
|
// Create new client and cache it
|
|
9337
9411
|
const client = initialiseAWSClient(Lambda, auth);
|
|
@@ -9831,15 +9905,17 @@ const isRetryableS3Error = (error) => {
|
|
|
9831
9905
|
if (error instanceof Error) {
|
|
9832
9906
|
const message = error.message;
|
|
9833
9907
|
// Retry on throttling
|
|
9834
|
-
if (message.includes(
|
|
9908
|
+
if (message.includes("SlowDown") || message.includes("RequestTimeout")) {
|
|
9835
9909
|
return true;
|
|
9836
9910
|
}
|
|
9837
9911
|
// Retry on 5xx server errors
|
|
9838
|
-
if (message.includes(
|
|
9912
|
+
if (message.includes("InternalError") ||
|
|
9913
|
+
message.includes("ServiceUnavailable")) {
|
|
9839
9914
|
return true;
|
|
9840
9915
|
}
|
|
9841
9916
|
// Retry on connection errors
|
|
9842
|
-
if (message.includes(
|
|
9917
|
+
if (message.includes("RequestTimeTooSkewed") ||
|
|
9918
|
+
message.includes("NetworkingError")) {
|
|
9843
9919
|
return true;
|
|
9844
9920
|
}
|
|
9845
9921
|
}
|
|
@@ -9853,10 +9929,11 @@ const generateRandomHash = (length) => {
|
|
|
9853
9929
|
};
|
|
9854
9930
|
function generateDateTimeStamp() {
|
|
9855
9931
|
const now = new Date();
|
|
9856
|
-
return now
|
|
9857
|
-
.
|
|
9858
|
-
.replace(
|
|
9859
|
-
.replace(
|
|
9932
|
+
return now
|
|
9933
|
+
.toISOString()
|
|
9934
|
+
.replace(/T/, "-")
|
|
9935
|
+
.replace(/\..+/, "")
|
|
9936
|
+
.replace(/:/g, "-");
|
|
9860
9937
|
}
|
|
9861
9938
|
const isValidBucketName = (name) => {
|
|
9862
9939
|
const regex = /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/;
|
|
@@ -9887,7 +9964,7 @@ async function uploadFile(s3Client, bucketName, filePath, destinationPath, isPub
|
|
|
9887
9964
|
Bucket: bucketName,
|
|
9888
9965
|
Key: s3Key,
|
|
9889
9966
|
Body: fileContent,
|
|
9890
|
-
ACL: isPublic ?
|
|
9967
|
+
ACL: isPublic ? "public-read" : undefined,
|
|
9891
9968
|
});
|
|
9892
9969
|
await withRetry(() => s3Client.send(command), {
|
|
9893
9970
|
maxRetries: 3,
|
|
@@ -9907,7 +9984,7 @@ async function uploadDirectory(s3Client, bucketName, dirPath, destinationPath, i
|
|
|
9907
9984
|
Bucket: bucketName,
|
|
9908
9985
|
Key: s3Key,
|
|
9909
9986
|
Body: fileContent,
|
|
9910
|
-
ACL: isPublic ?
|
|
9987
|
+
ACL: isPublic ? "public-read" : undefined,
|
|
9911
9988
|
});
|
|
9912
9989
|
await withRetry(() => s3Client.send(command), {
|
|
9913
9990
|
maxRetries: 3,
|
|
@@ -9923,7 +10000,7 @@ async function zipDirectory(sourceDir, outPath) {
|
|
|
9923
10000
|
const zip = new AdmZip();
|
|
9924
10001
|
let fileCount = 0;
|
|
9925
10002
|
try {
|
|
9926
|
-
async function addFilesToZip(currentPath, relativePath =
|
|
10003
|
+
async function addFilesToZip(currentPath, relativePath = "") {
|
|
9927
10004
|
const files = await fs$1.readdir(currentPath, { withFileTypes: true });
|
|
9928
10005
|
for (const file of files) {
|
|
9929
10006
|
const filePath = path__default.join(currentPath, file.name);
|
|
@@ -9966,27 +10043,33 @@ async function downloadFromS3Helper(s3Client, bucketName, s3Path, localPath) {
|
|
|
9966
10043
|
Prefix: s3Path,
|
|
9967
10044
|
ContinuationToken: continuationToken,
|
|
9968
10045
|
});
|
|
9969
|
-
const listResponse = await withRetry(() => s3Client.send(listCommand), {
|
|
10046
|
+
const listResponse = (await withRetry(() => s3Client.send(listCommand), {
|
|
9970
10047
|
maxRetries: 3,
|
|
9971
10048
|
baseDelayMs: 1000,
|
|
9972
10049
|
maxDelayMs: 15000,
|
|
9973
10050
|
retryableErrors: isRetryableS3Error,
|
|
9974
|
-
}, `S3:ListObjects:${bucketName}`);
|
|
10051
|
+
}, `S3:ListObjects:${bucketName}`));
|
|
9975
10052
|
const objects = listResponse.Contents || [];
|
|
9976
10053
|
if (objects.length === 0) {
|
|
9977
10054
|
return { fileCount: 0, folderCount: 0, totalSize: 0, fileList: [] };
|
|
9978
10055
|
}
|
|
9979
|
-
if (objects.length === 1 && objects[0].Key?.endsWith(
|
|
10056
|
+
if (objects.length === 1 && objects[0].Key?.endsWith(".zip")) {
|
|
9980
10057
|
// It's a zip file, download and extract it
|
|
9981
10058
|
const zipPath = path__default.join(localPath, path__default.basename(objects[0].Key));
|
|
9982
10059
|
await downloadFile(s3Client, bucketName, objects[0].Key, zipPath);
|
|
9983
|
-
const { totalFiles, totalFolders, totalSize: extractedSize, fileList: extractedFileList } = await unzipFile(zipPath, localPath);
|
|
10060
|
+
const { totalFiles, totalFolders, totalSize: extractedSize, fileList: extractedFileList, } = await unzipFile(zipPath, localPath);
|
|
9984
10061
|
await fs$1.unlink(zipPath);
|
|
9985
|
-
return {
|
|
10062
|
+
return {
|
|
10063
|
+
fileCount: totalFiles,
|
|
10064
|
+
folderCount: totalFolders,
|
|
10065
|
+
totalSize: extractedSize,
|
|
10066
|
+
fileList: extractedFileList,
|
|
10067
|
+
};
|
|
9986
10068
|
}
|
|
9987
10069
|
else {
|
|
9988
10070
|
// Non-zip files. Download files in batches.
|
|
9989
|
-
for (let i = 0; i < objects.length; i += 1000) {
|
|
10071
|
+
for (let i = 0; i < objects.length; i += 1000) {
|
|
10072
|
+
// AWS allows up to 1000 per request
|
|
9990
10073
|
const batch = objects.slice(i, i + 1000);
|
|
9991
10074
|
await Promise.all(batch.map(async (obj) => {
|
|
9992
10075
|
if (!obj.Key)
|
|
@@ -10002,7 +10085,9 @@ async function downloadFromS3Helper(s3Client, bucketName, s3Path, localPath) {
|
|
|
10002
10085
|
}
|
|
10003
10086
|
// Count folders
|
|
10004
10087
|
const relativePath = path__default.relative(localPath, path__default.dirname(localFilePath));
|
|
10005
|
-
if (relativePath &&
|
|
10088
|
+
if (relativePath &&
|
|
10089
|
+
!relativePath.startsWith("..") &&
|
|
10090
|
+
relativePath !== ".") {
|
|
10006
10091
|
folderCount++;
|
|
10007
10092
|
}
|
|
10008
10093
|
}));
|
|
@@ -10023,7 +10108,7 @@ async function downloadFile(s3Client, bucketName, s3Key, localFilePath) {
|
|
|
10023
10108
|
await withRetry(async () => {
|
|
10024
10109
|
const { Body } = await s3Client.send(getCommand);
|
|
10025
10110
|
if (!Body)
|
|
10026
|
-
throw new Error(
|
|
10111
|
+
throw new Error("No body returned from S3");
|
|
10027
10112
|
const writeStream = createWriteStream(localFilePath);
|
|
10028
10113
|
await pipeline(Body, writeStream);
|
|
10029
10114
|
}, {
|
|
@@ -10079,17 +10164,19 @@ async function emptyBucket(s3Client, bucketName) {
|
|
|
10079
10164
|
Bucket: bucketName,
|
|
10080
10165
|
ContinuationToken: continuationToken,
|
|
10081
10166
|
});
|
|
10082
|
-
const response = await withRetry(() => s3Client.send(listCommand), {
|
|
10167
|
+
const response = (await withRetry(() => s3Client.send(listCommand), {
|
|
10083
10168
|
maxRetries: 3,
|
|
10084
10169
|
baseDelayMs: 1000,
|
|
10085
10170
|
maxDelayMs: 15000,
|
|
10086
10171
|
retryableErrors: isRetryableS3Error,
|
|
10087
|
-
}, `S3:ListObjects:${bucketName}`);
|
|
10172
|
+
}, `S3:ListObjects:${bucketName}`));
|
|
10088
10173
|
const { Contents, IsTruncated, NextContinuationToken } = response;
|
|
10089
10174
|
if (Contents && Contents.length > 0) {
|
|
10090
10175
|
const deleteCommand = new DeleteObjectsCommand({
|
|
10091
10176
|
Bucket: bucketName,
|
|
10092
|
-
Delete: {
|
|
10177
|
+
Delete: {
|
|
10178
|
+
Objects: Contents.filter((c) => c.Key).map((c) => ({ Key: c.Key })),
|
|
10179
|
+
},
|
|
10093
10180
|
});
|
|
10094
10181
|
await withRetry(() => s3Client.send(deleteCommand), {
|
|
10095
10182
|
maxRetries: 3,
|
|
@@ -22997,11 +23084,11 @@ let poolConfig = DEFAULT_POOL_CONFIG;
|
|
|
22997
23084
|
async function loadApolloModules() {
|
|
22998
23085
|
if (typeof window === "undefined" || process.env.AWS_EXECUTION_ENV) {
|
|
22999
23086
|
// Server-side (or Lambda): load the CommonJS‑based implementation.
|
|
23000
|
-
return (await import('./apollo-client.server-
|
|
23087
|
+
return (await import('./apollo-client.server-CbagxkmK.js'));
|
|
23001
23088
|
}
|
|
23002
23089
|
else {
|
|
23003
23090
|
// Client-side: load the ESM‑based implementation.
|
|
23004
|
-
return (await import('./apollo-client.client-
|
|
23091
|
+
return (await import('./apollo-client.client-9wJcufhf.js'));
|
|
23005
23092
|
}
|
|
23006
23093
|
}
|
|
23007
23094
|
/**
|
|
@@ -79028,7 +79115,7 @@ const RETRY_CONFIG = {
|
|
|
79028
79115
|
BASE_DELAY: 2000, // Increased base delay to 2 seconds
|
|
79029
79116
|
MAX_DELAY: 64000,
|
|
79030
79117
|
MAX_RETRIES: 5,
|
|
79031
|
-
MAX_RANDOM_DELAY: 1000
|
|
79118
|
+
MAX_RANDOM_DELAY: 1000,
|
|
79032
79119
|
};
|
|
79033
79120
|
/**
|
|
79034
79121
|
* Determines if an error is related to Google Sheets API quotas or rate limits.
|
|
@@ -79054,10 +79141,10 @@ const RETRY_CONFIG = {
|
|
|
79054
79141
|
*/
|
|
79055
79142
|
function isQuotaError(error) {
|
|
79056
79143
|
const message = error.message.toLowerCase();
|
|
79057
|
-
return message.includes(
|
|
79058
|
-
message.includes(
|
|
79059
|
-
message.includes(
|
|
79060
|
-
message.includes(
|
|
79144
|
+
return (message.includes("quota exceeded") ||
|
|
79145
|
+
message.includes("rate limit") ||
|
|
79146
|
+
message.includes("429") ||
|
|
79147
|
+
message.includes("too many requests"));
|
|
79061
79148
|
}
|
|
79062
79149
|
/**
|
|
79063
79150
|
* Implements exponential backoff retry mechanism for Google Sheets API calls.
|
|
@@ -79116,14 +79203,14 @@ async function withExponentialBackoff(operation) {
|
|
|
79116
79203
|
const exponentialDelay = Math.min(Math.pow(2, retries) * RETRY_CONFIG.BASE_DELAY + randomDelay, RETRY_CONFIG.MAX_DELAY);
|
|
79117
79204
|
logIfDebug(`Quota/Rate limit exceeded. Error: ${error.message}`);
|
|
79118
79205
|
logIfDebug(`Retrying in ${exponentialDelay}ms (attempt ${retries}/${RETRY_CONFIG.MAX_RETRIES})`);
|
|
79119
|
-
await new Promise(resolve => setTimeout(resolve, exponentialDelay));
|
|
79206
|
+
await new Promise((resolve) => setTimeout(resolve, exponentialDelay));
|
|
79120
79207
|
}
|
|
79121
79208
|
}
|
|
79122
79209
|
}
|
|
79123
79210
|
function checkEnvVars() {
|
|
79124
79211
|
const secrets = getSecrets();
|
|
79125
79212
|
if (!secrets.googleSheets.clientEmail || !secrets.googleSheets.privateKey) {
|
|
79126
|
-
throw new Error(
|
|
79213
|
+
throw new Error("GOOGLE_SHEETS_CLIENT_EMAIL or GOOGLE_SHEETS_PRIVATE_KEY is not defined in environment variables.");
|
|
79127
79214
|
}
|
|
79128
79215
|
}
|
|
79129
79216
|
/**
|
|
@@ -79138,16 +79225,16 @@ async function getAuthClient() {
|
|
|
79138
79225
|
const secrets = getSecrets();
|
|
79139
79226
|
const credentials = {
|
|
79140
79227
|
client_email: secrets.googleSheets.clientEmail,
|
|
79141
|
-
private_key: secrets.googleSheets.privateKey?.replace(/\\n/g,
|
|
79228
|
+
private_key: secrets.googleSheets.privateKey?.replace(/\\n/g, "\n"),
|
|
79142
79229
|
};
|
|
79143
79230
|
const auth = new GoogleAuth({
|
|
79144
79231
|
credentials,
|
|
79145
|
-
scopes: [
|
|
79232
|
+
scopes: ["https://www.googleapis.com/auth/spreadsheets"],
|
|
79146
79233
|
});
|
|
79147
79234
|
return auth.getClient();
|
|
79148
79235
|
}
|
|
79149
79236
|
catch (error) {
|
|
79150
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
79237
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
79151
79238
|
logIfDebug(`Error initializing Google Sheets auth: ${errorMessage}`);
|
|
79152
79239
|
throw new Error(`Failed to initialize Google Sheets auth: ${errorMessage}`);
|
|
79153
79240
|
}
|
|
@@ -79159,7 +79246,7 @@ async function getSheetsClient() {
|
|
|
79159
79246
|
const auth = await getAuthClient();
|
|
79160
79247
|
return new buildExports.sheets_v4.Sheets({
|
|
79161
79248
|
auth,
|
|
79162
|
-
timeout: GOOGLE_SHEETS_TIMEOUT_MS
|
|
79249
|
+
timeout: GOOGLE_SHEETS_TIMEOUT_MS,
|
|
79163
79250
|
});
|
|
79164
79251
|
}
|
|
79165
79252
|
/**
|
|
@@ -79192,7 +79279,7 @@ function escapeSheetName(sheetName) {
|
|
|
79192
79279
|
* @param startColumn Optional starting column (defaults to 'A')
|
|
79193
79280
|
* @returns Promise resolving to response with success status and row details
|
|
79194
79281
|
*/
|
|
79195
|
-
async function addRow(config, values, startColumn =
|
|
79282
|
+
async function addRow(config, values, startColumn = "A") {
|
|
79196
79283
|
try {
|
|
79197
79284
|
const sheets = await getSheetsClient();
|
|
79198
79285
|
const escapedSheetName = escapeSheetName(config.sheetName);
|
|
@@ -79200,7 +79287,7 @@ async function addRow(config, values, startColumn = 'A') {
|
|
|
79200
79287
|
const response = await withExponentialBackoff(() => sheets.spreadsheets.values.append({
|
|
79201
79288
|
spreadsheetId: config.spreadsheetId,
|
|
79202
79289
|
range,
|
|
79203
|
-
valueInputOption:
|
|
79290
|
+
valueInputOption: "USER_ENTERED",
|
|
79204
79291
|
requestBody: {
|
|
79205
79292
|
values: [values],
|
|
79206
79293
|
},
|
|
@@ -79208,9 +79295,9 @@ async function addRow(config, values, startColumn = 'A') {
|
|
|
79208
79295
|
//logIfDebug(`Added row to sheet ${config.sheetName}: ${values.join(', ')}`);
|
|
79209
79296
|
const rowNumber = response.data.updates?.updatedRange
|
|
79210
79297
|
? parseInt(response.data.updates.updatedRange
|
|
79211
|
-
.split(
|
|
79212
|
-
.split(
|
|
79213
|
-
.replace(/[^0-9]/g,
|
|
79298
|
+
.split("!")[1]
|
|
79299
|
+
.split(":")[0]
|
|
79300
|
+
.replace(/[^0-9]/g, ""))
|
|
79214
79301
|
: 0;
|
|
79215
79302
|
return {
|
|
79216
79303
|
success: true,
|
|
@@ -79221,7 +79308,7 @@ async function addRow(config, values, startColumn = 'A') {
|
|
|
79221
79308
|
};
|
|
79222
79309
|
}
|
|
79223
79310
|
catch (error) {
|
|
79224
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
79311
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
79225
79312
|
logIfDebug(`Error adding row to sheet: ${errorMessage}`);
|
|
79226
79313
|
return {
|
|
79227
79314
|
success: false,
|
|
@@ -79263,7 +79350,7 @@ async function addSheet(spreadsheetId, sheetTitle) {
|
|
|
79263
79350
|
};
|
|
79264
79351
|
}
|
|
79265
79352
|
catch (error) {
|
|
79266
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
79353
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
79267
79354
|
logIfDebug(`Error adding sheet: ${errorMessage}`);
|
|
79268
79355
|
return {
|
|
79269
79356
|
success: false,
|
|
@@ -79287,7 +79374,7 @@ async function writeCell(config, cell, value) {
|
|
|
79287
79374
|
await withExponentialBackoff(() => sheets.spreadsheets.values.update({
|
|
79288
79375
|
spreadsheetId: config.spreadsheetId,
|
|
79289
79376
|
range,
|
|
79290
|
-
valueInputOption:
|
|
79377
|
+
valueInputOption: "USER_ENTERED",
|
|
79291
79378
|
requestBody: {
|
|
79292
79379
|
values: [[value]],
|
|
79293
79380
|
},
|
|
@@ -79301,7 +79388,7 @@ async function writeCell(config, cell, value) {
|
|
|
79301
79388
|
};
|
|
79302
79389
|
}
|
|
79303
79390
|
catch (error) {
|
|
79304
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
79391
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
79305
79392
|
logIfDebug(`Error writing to cell: ${errorMessage}`);
|
|
79306
79393
|
return {
|
|
79307
79394
|
success: false,
|
|
@@ -79325,8 +79412,11 @@ async function getCell(config, cell) {
|
|
|
79325
79412
|
spreadsheetId: config.spreadsheetId,
|
|
79326
79413
|
range,
|
|
79327
79414
|
}));
|
|
79328
|
-
const
|
|
79329
|
-
|
|
79415
|
+
const rawValue = response.data.values?.[0]?.[0];
|
|
79416
|
+
const value = rawValue === undefined || rawValue === null
|
|
79417
|
+
? null
|
|
79418
|
+
: rawValue;
|
|
79419
|
+
logIfDebug(`Read value from cell ${range}: ${String(value)}`);
|
|
79330
79420
|
return {
|
|
79331
79421
|
success: true,
|
|
79332
79422
|
data: {
|
|
@@ -79335,7 +79425,7 @@ async function getCell(config, cell) {
|
|
|
79335
79425
|
};
|
|
79336
79426
|
}
|
|
79337
79427
|
catch (error) {
|
|
79338
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
79428
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
79339
79429
|
logIfDebug(`Error reading from cell: ${errorMessage}`);
|
|
79340
79430
|
return {
|
|
79341
79431
|
success: false,
|
|
@@ -79368,10 +79458,11 @@ function convertA1ToRowCol(a1Notation) {
|
|
|
79368
79458
|
* @returns Padded array of arrays
|
|
79369
79459
|
*/
|
|
79370
79460
|
function padArrays(arrays) {
|
|
79371
|
-
const maxLength = Math.max(...arrays.map(arr => arr.length));
|
|
79372
|
-
return arrays.map(arr => {
|
|
79461
|
+
const maxLength = Math.max(...arrays.map((arr) => arr.length));
|
|
79462
|
+
return arrays.map((arr) => {
|
|
79373
79463
|
if (arr.length < maxLength) {
|
|
79374
|
-
|
|
79464
|
+
const padding = Array(maxLength - arr.length).fill("");
|
|
79465
|
+
return [...arr, ...padding];
|
|
79375
79466
|
}
|
|
79376
79467
|
return arr;
|
|
79377
79468
|
});
|
|
@@ -79383,10 +79474,10 @@ function padArrays(arrays) {
|
|
|
79383
79474
|
* @param startCell Optional starting cell in A1 notation (defaults to 'A1')
|
|
79384
79475
|
* @returns Promise resolving to response with success status and update details
|
|
79385
79476
|
*/
|
|
79386
|
-
async function writeArray(config, data, startCell =
|
|
79477
|
+
async function writeArray(config, data, startCell = "A1") {
|
|
79387
79478
|
try {
|
|
79388
79479
|
if (!data.length) {
|
|
79389
|
-
throw new Error(
|
|
79480
|
+
throw new Error("Data array is empty");
|
|
79390
79481
|
}
|
|
79391
79482
|
const sheets = await getSheetsClient();
|
|
79392
79483
|
const { row: startRow, column: startCol } = convertA1ToRowCol(startCell);
|
|
@@ -79402,7 +79493,7 @@ async function writeArray(config, data, startCell = 'A1') {
|
|
|
79402
79493
|
const response = await withExponentialBackoff(() => sheets.spreadsheets.values.update({
|
|
79403
79494
|
spreadsheetId: config.spreadsheetId,
|
|
79404
79495
|
range,
|
|
79405
|
-
valueInputOption:
|
|
79496
|
+
valueInputOption: "USER_ENTERED",
|
|
79406
79497
|
requestBody: {
|
|
79407
79498
|
values: paddedData,
|
|
79408
79499
|
},
|
|
@@ -79418,7 +79509,7 @@ async function writeArray(config, data, startCell = 'A1') {
|
|
|
79418
79509
|
};
|
|
79419
79510
|
}
|
|
79420
79511
|
catch (error) {
|
|
79421
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
79512
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
79422
79513
|
logIfDebug(`Error writing array to sheet: ${errorMessage}`);
|
|
79423
79514
|
return {
|
|
79424
79515
|
success: false,
|
|
@@ -81525,4 +81616,4 @@ const lumic = {
|
|
|
81525
81616
|
};
|
|
81526
81617
|
|
|
81527
81618
|
export { GraphQLInterfaceType as $, print as A, getNamedType as B, isInputType as C, isRequiredArgument as D, isNamedType as E, GraphQLError as F, GraphQLNonNull as G, isOutputType as H, isRequiredInputField as I, isCompositeType as J, Kind as K, getNullableType as L, getEnterLeaveForKind as M, isNode as N, OperationTypeNode as O, didYouMean as P, naturalCompare as Q, suggestionList as R, specifiedScalarTypes as S, keyMap as T, isType as U, isNullableType as V, visit as W, visitInParallel as X, keyValMap as Y, assertObjectType as Z, GraphQLScalarType as _, isListType as a, validateGoogleSheetsRange as a$, GraphQLUnionType as a0, GraphQLInputObjectType as a1, assertNullableType as a2, assertInterfaceType as a3, mapValue as a4, isSpecifiedScalarType as a5, isPrintableAsBlockString as a6, printBlockString as a7, BREAK as a8, GRAPHQL_MAX_INT as a9, printSourceLocation as aA, resolveObjMapThunk as aB, resolveReadonlyArrayThunk as aC, valueFromASTUntyped as aD, version$4 as aE, versionInfo as aF, getAugmentedNamespace as aG, isDigit$1 as aH, isNameStart as aI, dedentBlockStringLines as aJ, isNameContinue as aK, setLumicLogger as aL, getLumicLogger as aM, sanitizeForLog as aN, sanitizeError as aO, sanitizeAWSAuth as aP, sanitizeObject as aQ, getSecrets as aR, resetSecrets as aS, requireSecret as aT, withRetry as aU, CircuitBreaker as aV, CircuitBreakerState as aW, CircuitBreakerOpenError as aX, DEFAULT_CIRCUIT_BREAKER_CONFIG as aY, validateSlackChannel as aZ, validateS3Key as a_, GRAPHQL_MIN_INT as aa, GraphQLFloat as ab, GraphQLInt as ac, Location as ad, Token as ae, assertAbstractType as af, assertCompositeType as ag, assertEnumType as ah, assertEnumValueName as ai, assertInputObjectType as aj, assertInputType as ak, assertLeafType as al, assertListType as am, assertNamedType as an, assertNonNullType as ao, assertOutputType as ap, assertScalarType as aq, assertType as ar, assertUnionType as as, assertWrappingType as at, formatError as au, getLocation as av, getVisitFn as aw, isWrappingType as ax, printError as ay, printLocation as az, isAbstractType as b, PDFError as b$, LLMCostTracker as b0, getLLMCostTracker as b1, setLLMCostTracker as b2, resetLLMCostTracker as b3, setMetricsCollector as b4, getMetricsCollector as b5, resetMetricsCollector as b6, withMetrics as b7, generateCorrelationId as b8, getCorrelationId as b9, openAIChatCompletionSchema as bA, openAIImageResponseSchema as bB, validateOpenAIChatCompletion as bC, safeValidateOpenAIChatCompletion as bD, perplexityResponseSchema as bE, validatePerplexityResponse as bF, safeValidatePerplexityResponse as bG, googleSheetsValueRangeSchema as bH, validateGoogleSheetsResponse as bI, safeValidateGoogleSheetsResponse as bJ, s3ListObjectsSchema as bK, s3GetObjectSchema as bL, lambdaInvokeResponseSchema as bM, validateS3ListObjects as bN, safeValidateS3ListObjects as bO, validateLambdaResponse as bP, safeValidateLambdaResponse as bQ, createValidator as bR, createSafeValidator as bS, LumicError as bT, SlackError as bU, LLMError as bV, AWSLambdaError as bW, AWSS3Error as bX, GoogleSheetsError as bY, PerplexityError as bZ, JsonParseError as b_, getCorrelationContext as ba, withCorrelationId as bb, getCorrelationHeaders as bc, TokenBucketRateLimiter as bd, RATE_LIMIT_PROFILES as be, getRateLimiter as bf, resetAllRateLimiters as bg, withRateLimit as bh, checkIntegrationHealth as bi, SUPPORTED_MODELS as bj, isValidModel as bk, getModelCapabilities as bl, getModelProvider as bm, MODEL_ALIASES as bn, OPENAI_COMPATIBLE_PROVIDERS as bo, PROVIDER_DEFAULT_MODELS as bp, LLM_DEFAULT_PROVIDER as bq, LLM_MINI_PROVIDER as br, LLM_NORMAL_PROVIDER as bs, LLM_ADVANCED_PROVIDER as bt, LLM_PROVIDER as bu, LLM_MODEL_MINI as bv, LLM_MODEL_NORMAL as bw, LLM_MODEL_ADVANCED as bx, makeAnthropicCall as by, makeOpenAICompatibleCall as bz, isInterfaceType as c, ZipError as c0, SLACK_TIMEOUT_MS as c1, PERPLEXITY_TIMEOUT_MS as c2, GOOGLE_SHEETS_TIMEOUT_MS as c3, LLM_TIMEOUT_MS as c4, AWS_LAMBDA_TIMEOUT_MS as c5, AWS_S3_TIMEOUT_MS as c6, OPENWEATHER_TIMEOUT_MS as c7, GENERIC_FETCH_TIMEOUT_MS as c8, isObjectType as d, assertName as e, devAssert as f, isObjectLike as g, defineArguments as h, isNonNullType as i, argsToArgsConfig as j, GraphQLBoolean as k, lumic as l, GraphQLString as m, instanceOf as n, inspect as o, isInputObjectType as p, isLeafType as q, isEnumType as r, GraphQLID as s, toObjMap as t, invariant as u, GraphQLObjectType as v, GraphQLEnumType as w, GraphQLList as x, isScalarType as y, isUnionType as z };
|
|
81528
|
-
//# sourceMappingURL=index-
|
|
81619
|
+
//# sourceMappingURL=index-EMQ1uJMh.js.map
|