@adaptic/lumic-utils 1.0.22 → 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-Bu5mc2I8.js → apollo-client.client-9wJcufhf.js} +4 -4
- package/dist/{apollo-client.client-Bu5mc2I8.js.map → apollo-client.client-9wJcufhf.js.map} +1 -1
- package/dist/{apollo-client.client-YzlpL7kf.js → apollo-client.client-CyxU1hTu.js} +3 -3
- package/dist/{apollo-client.client-YzlpL7kf.js.map → apollo-client.client-CyxU1hTu.js.map} +1 -1
- package/dist/{apollo-client.server-sD1QteQO.js → apollo-client.server-CbagxkmK.js} +3 -3
- package/dist/{apollo-client.server-sD1QteQO.js.map → apollo-client.server-CbagxkmK.js.map} +1 -1
- package/dist/{apollo-client.server-BTHAQqYl.js → apollo-client.server-DB3jLbBx.js} +3 -3
- package/dist/{apollo-client.server-BTHAQqYl.js.map → apollo-client.server-DB3jLbBx.js.map} +1 -1
- package/dist/{index-CWx6sW9a.js → index-B4yVKGNR.js} +2 -2
- package/dist/{index-CWx6sW9a.js.map → index-B4yVKGNR.js.map} +1 -1
- package/dist/{index-B_4Q2noT.js → index-CL79JTWc.js} +2 -2
- package/dist/{index-B_4Q2noT.js.map → index-CL79JTWc.js.map} +1 -1
- package/dist/{index-DPaCMqua.js → index-EMQ1uJMh.js} +380 -281
- package/dist/{index-DPaCMqua.js.map → index-EMQ1uJMh.js.map} +1 -1
- package/dist/{index-DfuMX-MS.js → index-KzQOh2uu.js} +380 -281
- package/dist/{index-DfuMX-MS.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 +17 -8
- package/dist/types/utils/aws-initialise.d.ts +4 -4
- package/package.json +2 -2
|
@@ -80,13 +80,21 @@ const OPENAI_COMPATIBLE_PROVIDERS = {
|
|
|
80
80
|
*/
|
|
81
81
|
const SUPPORTED_MODELS = {
|
|
82
82
|
// ── OpenAI GPT-5.5 series (current flagship) ─────────────────────────
|
|
83
|
+
// GPT-5.x series: OpenAI's 2026 API constraint — only the default
|
|
84
|
+
// temperature value (1.0) is accepted. Passing any other value returns
|
|
85
|
+
// HTTP 400: "Unsupported value: 'temperature' does not support 0.2 with
|
|
86
|
+
// this model. Only the default (1) value is supported." Mark
|
|
87
|
+
// supportsTemperature=false so the lumic-utils gate at llm-call.ts:194
|
|
88
|
+
// OMITS the parameter entirely (OpenAI then uses its default of 1.0).
|
|
89
|
+
// Ref: community.openai.com/t/temperature-in-gpt-5-models/1337133
|
|
90
|
+
// Ref: developers.openai.com/api/docs/models/gpt-5.4-nano
|
|
83
91
|
'gpt-5.5': {
|
|
84
92
|
provider: 'openai',
|
|
85
93
|
supportsTools: true,
|
|
86
94
|
supportsJson: true,
|
|
87
95
|
supportsStructuredOutput: true,
|
|
88
96
|
supportsDeveloperPrompt: true,
|
|
89
|
-
supportsTemperature:
|
|
97
|
+
supportsTemperature: false,
|
|
90
98
|
isReasoningModel: false,
|
|
91
99
|
},
|
|
92
100
|
'gpt-5.5-pro': {
|
|
@@ -95,7 +103,7 @@ const SUPPORTED_MODELS = {
|
|
|
95
103
|
supportsJson: true,
|
|
96
104
|
supportsStructuredOutput: true,
|
|
97
105
|
supportsDeveloperPrompt: true,
|
|
98
|
-
supportsTemperature:
|
|
106
|
+
supportsTemperature: false,
|
|
99
107
|
isReasoningModel: false,
|
|
100
108
|
},
|
|
101
109
|
// ── OpenAI GPT-5.4 series ────────────────────────────────────────────
|
|
@@ -105,7 +113,7 @@ const SUPPORTED_MODELS = {
|
|
|
105
113
|
supportsJson: true,
|
|
106
114
|
supportsStructuredOutput: true,
|
|
107
115
|
supportsDeveloperPrompt: true,
|
|
108
|
-
supportsTemperature:
|
|
116
|
+
supportsTemperature: false,
|
|
109
117
|
isReasoningModel: false,
|
|
110
118
|
},
|
|
111
119
|
'gpt-5.4-mini': {
|
|
@@ -114,7 +122,7 @@ const SUPPORTED_MODELS = {
|
|
|
114
122
|
supportsJson: true,
|
|
115
123
|
supportsStructuredOutput: true,
|
|
116
124
|
supportsDeveloperPrompt: true,
|
|
117
|
-
supportsTemperature:
|
|
125
|
+
supportsTemperature: false,
|
|
118
126
|
isReasoningModel: false,
|
|
119
127
|
},
|
|
120
128
|
'gpt-5.4-nano': {
|
|
@@ -123,7 +131,7 @@ const SUPPORTED_MODELS = {
|
|
|
123
131
|
supportsJson: true,
|
|
124
132
|
supportsStructuredOutput: true,
|
|
125
133
|
supportsDeveloperPrompt: true,
|
|
126
|
-
supportsTemperature:
|
|
134
|
+
supportsTemperature: false,
|
|
127
135
|
isReasoningModel: false,
|
|
128
136
|
},
|
|
129
137
|
// ── OpenAI GPT-5 series ──────────────────────────────────────────────
|
|
@@ -133,7 +141,7 @@ const SUPPORTED_MODELS = {
|
|
|
133
141
|
supportsJson: true,
|
|
134
142
|
supportsStructuredOutput: true,
|
|
135
143
|
supportsDeveloperPrompt: true,
|
|
136
|
-
supportsTemperature:
|
|
144
|
+
supportsTemperature: false,
|
|
137
145
|
isReasoningModel: false,
|
|
138
146
|
},
|
|
139
147
|
'gpt-5-mini': {
|
|
@@ -142,7 +150,7 @@ const SUPPORTED_MODELS = {
|
|
|
142
150
|
supportsJson: true,
|
|
143
151
|
supportsStructuredOutput: true,
|
|
144
152
|
supportsDeveloperPrompt: true,
|
|
145
|
-
supportsTemperature:
|
|
153
|
+
supportsTemperature: false,
|
|
146
154
|
isReasoningModel: false,
|
|
147
155
|
},
|
|
148
156
|
// ── OpenAI GPT-4.1 series ────────────────────────────────────────────
|
|
@@ -543,10 +551,19 @@ function getModelProvider(model) {
|
|
|
543
551
|
return SUPPORTED_MODELS[model].provider;
|
|
544
552
|
}
|
|
545
553
|
/**
|
|
546
|
-
* 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.
|
|
547
564
|
*/
|
|
548
565
|
const PROVIDER_DEFAULT_MODELS = {
|
|
549
|
-
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' },
|
|
550
567
|
anthropic: { mini: 'claude-haiku-4-5', normal: 'claude-sonnet-4-6', advanced: 'claude-opus-4-7' },
|
|
551
568
|
deepseek: { mini: 'deepseek-v4-flash', normal: 'deepseek-v4-flash', advanced: 'deepseek-v4-pro' },
|
|
552
569
|
kimi: { mini: 'kimi-k2-0905-preview', normal: 'kimi-k2.5', advanced: 'kimi-k2.6' },
|
|
@@ -1500,11 +1517,13 @@ let openai;
|
|
|
1500
1517
|
function initializeOpenAI(apiKey) {
|
|
1501
1518
|
const key = apiKey || getSecrets().openai.apiKey;
|
|
1502
1519
|
if (!key) {
|
|
1503
|
-
throw new Error(
|
|
1520
|
+
throw new Error("OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set");
|
|
1504
1521
|
}
|
|
1505
1522
|
return new OpenAI({
|
|
1506
1523
|
apiKey: key,
|
|
1507
|
-
defaultHeaders: {
|
|
1524
|
+
defaultHeaders: {
|
|
1525
|
+
"User-Agent": "My Server-side Application (compatible; OpenAI API Client)",
|
|
1526
|
+
},
|
|
1508
1527
|
});
|
|
1509
1528
|
}
|
|
1510
1529
|
/**
|
|
@@ -1522,9 +1541,7 @@ function initializeOpenAI(apiKey) {
|
|
|
1522
1541
|
*/
|
|
1523
1542
|
async function fixJsonWithAI(jsonStr, options) {
|
|
1524
1543
|
// Backward compatibility: handle string apiKey as first positional param
|
|
1525
|
-
const opts = typeof options ===
|
|
1526
|
-
? { apiKey: options }
|
|
1527
|
-
: (options ?? {});
|
|
1544
|
+
const opts = typeof options === "string" ? { apiKey: options } : (options ?? {});
|
|
1528
1545
|
const apiKey = opts.apiKey;
|
|
1529
1546
|
const maxDepth = opts.maxDepth ?? 2;
|
|
1530
1547
|
const depth = opts._depth ?? 0;
|
|
@@ -1537,18 +1554,18 @@ async function fixJsonWithAI(jsonStr, options) {
|
|
|
1537
1554
|
openai = initializeOpenAI(apiKey);
|
|
1538
1555
|
}
|
|
1539
1556
|
const completion = await openai.chat.completions.create({
|
|
1540
|
-
model:
|
|
1557
|
+
model: "gpt-5-mini",
|
|
1541
1558
|
messages: [
|
|
1542
1559
|
{
|
|
1543
|
-
role:
|
|
1544
|
-
content:
|
|
1560
|
+
role: "system",
|
|
1561
|
+
content: "You are a JSON fixer. Return only valid JSON without any additional text or explanation.",
|
|
1545
1562
|
},
|
|
1546
1563
|
{
|
|
1547
|
-
role:
|
|
1548
|
-
content: `Fix this broken JSON:\n${jsonStr}
|
|
1549
|
-
}
|
|
1564
|
+
role: "user",
|
|
1565
|
+
content: `Fix this broken JSON:\n${jsonStr}`,
|
|
1566
|
+
},
|
|
1550
1567
|
],
|
|
1551
|
-
response_format: { type:
|
|
1568
|
+
response_format: { type: "json_object" },
|
|
1552
1569
|
});
|
|
1553
1570
|
const fixedJson = completion.choices[0]?.message?.content;
|
|
1554
1571
|
if (fixedJson && isValidJson(fixedJson)) {
|
|
@@ -1562,23 +1579,32 @@ async function fixJsonWithAI(jsonStr, options) {
|
|
|
1562
1579
|
_depth: depth + 1,
|
|
1563
1580
|
});
|
|
1564
1581
|
}
|
|
1565
|
-
throw new JsonParseError(
|
|
1582
|
+
throw new JsonParseError("Failed to fix JSON with AI: returned invalid JSON");
|
|
1566
1583
|
}
|
|
1567
1584
|
catch (err) {
|
|
1568
1585
|
// Re-throw JsonParseError as-is (e.g. max depth exceeded)
|
|
1569
1586
|
if (err instanceof JsonParseError) {
|
|
1570
1587
|
throw err;
|
|
1571
1588
|
}
|
|
1572
|
-
const errorMessage = err instanceof Error ? err.message :
|
|
1589
|
+
const errorMessage = err instanceof Error ? err.message : "Unknown error occurred";
|
|
1573
1590
|
throw new JsonParseError(`Error fixing JSON with AI: ${errorMessage}`);
|
|
1574
1591
|
}
|
|
1575
1592
|
}
|
|
1576
1593
|
|
|
1577
1594
|
// llm-utils.ts
|
|
1578
1595
|
function normalizeModelName(model) {
|
|
1579
|
-
return model.replace(/_/g,
|
|
1580
|
-
}
|
|
1581
|
-
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
|
+
];
|
|
1582
1608
|
/**
|
|
1583
1609
|
* Tries to parse JSON from the given content string according to the specified
|
|
1584
1610
|
* response format. If the response format is 'json' or a JSON schema object, it
|
|
@@ -1602,7 +1628,9 @@ async function parseResponse(content, responseFormat, options = {}) {
|
|
|
1602
1628
|
if (recursionDepth > maxRetries) {
|
|
1603
1629
|
throw new Error(`Maximum recursion depth (${maxRetries}) exceeded while parsing JSON. AI fixing may have returned broken JSON.`);
|
|
1604
1630
|
}
|
|
1605
|
-
if (responseFormat ===
|
|
1631
|
+
if (responseFormat === "json" ||
|
|
1632
|
+
(typeof responseFormat === "object" &&
|
|
1633
|
+
responseFormat?.type === "json_schema")) {
|
|
1606
1634
|
let cleanedContent = content.trim();
|
|
1607
1635
|
let detectedType = null;
|
|
1608
1636
|
// Remove code block markers if present
|
|
@@ -1613,8 +1641,8 @@ async function parseResponse(content, responseFormat, options = {}) {
|
|
|
1613
1641
|
}
|
|
1614
1642
|
return false;
|
|
1615
1643
|
});
|
|
1616
|
-
if (startsWithCodeBlock && cleanedContent.endsWith(
|
|
1617
|
-
const firstLineEndIndex = cleanedContent.indexOf(
|
|
1644
|
+
if (startsWithCodeBlock && cleanedContent.endsWith("```")) {
|
|
1645
|
+
const firstLineEndIndex = cleanedContent.indexOf("\n");
|
|
1618
1646
|
if (firstLineEndIndex !== -1) {
|
|
1619
1647
|
cleanedContent = cleanedContent.slice(firstLineEndIndex + 1, -3).trim();
|
|
1620
1648
|
getLumicLogger().info(`Code block for type ${detectedType} detected and removed. Cleaned content: ${cleanedContent}`);
|
|
@@ -1647,7 +1675,7 @@ async function parseResponse(content, responseFormat, options = {}) {
|
|
|
1647
1675
|
},
|
|
1648
1676
|
// Strategy 3: Remove leading/trailing text and try parsing
|
|
1649
1677
|
() => {
|
|
1650
|
-
const jsonMatch = cleanedContent.replace(/^[^{[]*|[^}\]]*$/g,
|
|
1678
|
+
const jsonMatch = cleanedContent.replace(/^[^{[]*|[^}\]]*$/g, "");
|
|
1651
1679
|
try {
|
|
1652
1680
|
return JSON.parse(jsonMatch);
|
|
1653
1681
|
}
|
|
@@ -1669,7 +1697,7 @@ async function parseResponse(content, responseFormat, options = {}) {
|
|
|
1669
1697
|
}
|
|
1670
1698
|
// Strategy 5: Use AI fixing (only if explicitly enabled)
|
|
1671
1699
|
if (enableAiFix) {
|
|
1672
|
-
getLumicLogger().warn(
|
|
1700
|
+
getLumicLogger().warn("Using AI-powered JSON fixing. This adds cost and latency. Consider improving JSON generation instead.");
|
|
1673
1701
|
try {
|
|
1674
1702
|
const aiFixedJson = await fixJsonWithAI(cleanedContent);
|
|
1675
1703
|
if (aiFixedJson !== null) {
|
|
@@ -1682,7 +1710,7 @@ async function parseResponse(content, responseFormat, options = {}) {
|
|
|
1682
1710
|
catch {
|
|
1683
1711
|
// AI returned something that looks like JSON but isn't valid
|
|
1684
1712
|
// Increment recursion depth and try parsing the AI response
|
|
1685
|
-
getLumicLogger().warn(
|
|
1713
|
+
getLumicLogger().warn("AI fixing returned invalid JSON, attempting recursive parse...");
|
|
1686
1714
|
return parseResponse(JSON.stringify(aiFixedJson), responseFormat, {
|
|
1687
1715
|
...options,
|
|
1688
1716
|
_recursionDepth: recursionDepth + 1,
|
|
@@ -1691,14 +1719,14 @@ async function parseResponse(content, responseFormat, options = {}) {
|
|
|
1691
1719
|
}
|
|
1692
1720
|
}
|
|
1693
1721
|
catch (error) {
|
|
1694
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
1722
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1695
1723
|
getLumicLogger().error(`AI JSON fixing failed: ${errorMessage}`);
|
|
1696
1724
|
// Continue to final error below
|
|
1697
1725
|
}
|
|
1698
1726
|
}
|
|
1699
1727
|
// If all strategies fail, throw an error
|
|
1700
1728
|
getLumicLogger().error(`Failed to parse JSON from content: ${cleanedContent}. Original JSON: ${content}`);
|
|
1701
|
-
throw new Error(
|
|
1729
|
+
throw new Error("Unable to parse JSON response");
|
|
1702
1730
|
}
|
|
1703
1731
|
else {
|
|
1704
1732
|
return content;
|
|
@@ -2292,13 +2320,15 @@ const isRetryableLLMError = (error) => {
|
|
|
2292
2320
|
if (error instanceof Error) {
|
|
2293
2321
|
const message = error.message;
|
|
2294
2322
|
// Retry on rate limits (429)
|
|
2295
|
-
if (message.includes(
|
|
2323
|
+
if (message.includes("429") ||
|
|
2324
|
+
message.includes("rate limit") ||
|
|
2325
|
+
message.includes("Rate limit")) {
|
|
2296
2326
|
return true;
|
|
2297
2327
|
}
|
|
2298
2328
|
// Retry on transient body-corruption 400s. Match the exact OpenAI error
|
|
2299
2329
|
// string to avoid retrying genuine client-side validation 400s (which
|
|
2300
2330
|
// would re-fail forever and waste retry budget).
|
|
2301
|
-
if (message.includes(
|
|
2331
|
+
if (message.includes("could not parse the JSON body of your request")) {
|
|
2302
2332
|
return true;
|
|
2303
2333
|
}
|
|
2304
2334
|
}
|
|
@@ -2367,23 +2397,26 @@ function isReasoningModel(model) {
|
|
|
2367
2397
|
* @throws Error if the response format is invalid or incompatible.
|
|
2368
2398
|
*/
|
|
2369
2399
|
function getResponseFormatOption(responseFormat, normalizedModel) {
|
|
2370
|
-
if (responseFormat ===
|
|
2371
|
-
return { type:
|
|
2400
|
+
if (responseFormat === "text") {
|
|
2401
|
+
return { type: "text" };
|
|
2372
2402
|
}
|
|
2373
|
-
if (responseFormat ===
|
|
2374
|
-
return supportsJsonMode(normalizedModel)
|
|
2403
|
+
if (responseFormat === "json") {
|
|
2404
|
+
return supportsJsonMode(normalizedModel)
|
|
2405
|
+
? { type: "json_object" }
|
|
2406
|
+
: { type: "text" };
|
|
2375
2407
|
}
|
|
2376
|
-
if (typeof responseFormat ===
|
|
2408
|
+
if (typeof responseFormat === "object" &&
|
|
2409
|
+
responseFormat.type === "json_schema") {
|
|
2377
2410
|
if (!isStructuredOutputCompatibleModel(normalizedModel)) {
|
|
2378
2411
|
throw new Error(`${normalizedModel} is not compatible with structured outputs / json object.`);
|
|
2379
2412
|
}
|
|
2380
2413
|
return {
|
|
2381
|
-
type:
|
|
2414
|
+
type: "json_schema",
|
|
2382
2415
|
json_schema: {
|
|
2383
|
-
type:
|
|
2416
|
+
type: "object",
|
|
2384
2417
|
properties: responseFormat.schema.properties,
|
|
2385
2418
|
required: responseFormat.schema.required || [],
|
|
2386
|
-
name:
|
|
2419
|
+
name: "Response",
|
|
2387
2420
|
},
|
|
2388
2421
|
};
|
|
2389
2422
|
}
|
|
@@ -2442,7 +2475,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2442
2475
|
const normalizedModel = normalizeModelName(options.model || DEFAULT_MODEL);
|
|
2443
2476
|
const apiKey = options.apiKey || getSecrets().openai.apiKey;
|
|
2444
2477
|
if (!apiKey) {
|
|
2445
|
-
throw new Error(
|
|
2478
|
+
throw new Error("OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set");
|
|
2446
2479
|
}
|
|
2447
2480
|
const openai = new OpenAI({
|
|
2448
2481
|
apiKey: apiKey,
|
|
@@ -2452,7 +2485,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2452
2485
|
// Add developer prompt if present
|
|
2453
2486
|
if (options.developerPrompt && supportsDeveloperPrompt(normalizedModel)) {
|
|
2454
2487
|
messages.push({
|
|
2455
|
-
role:
|
|
2488
|
+
role: "developer",
|
|
2456
2489
|
content: options.developerPrompt,
|
|
2457
2490
|
});
|
|
2458
2491
|
}
|
|
@@ -2461,15 +2494,15 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2461
2494
|
messages.push(...options.context);
|
|
2462
2495
|
}
|
|
2463
2496
|
// Add user content
|
|
2464
|
-
if (typeof content ===
|
|
2497
|
+
if (typeof content === "string") {
|
|
2465
2498
|
messages.push({
|
|
2466
|
-
role:
|
|
2499
|
+
role: "user",
|
|
2467
2500
|
content,
|
|
2468
2501
|
});
|
|
2469
2502
|
}
|
|
2470
2503
|
else {
|
|
2471
2504
|
messages.push({
|
|
2472
|
-
role:
|
|
2505
|
+
role: "user",
|
|
2473
2506
|
content,
|
|
2474
2507
|
});
|
|
2475
2508
|
}
|
|
@@ -2483,7 +2516,8 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2483
2516
|
queryOptions.tools = options.tools;
|
|
2484
2517
|
}
|
|
2485
2518
|
// Only include temperature if the model supports it
|
|
2486
|
-
if (options.temperature !== undefined &&
|
|
2519
|
+
if (options.temperature !== undefined &&
|
|
2520
|
+
supportsTemperature(normalizedModel)) {
|
|
2487
2521
|
queryOptions.temperature = options.temperature;
|
|
2488
2522
|
}
|
|
2489
2523
|
// Only include max_completion_tokens when specified
|
|
@@ -2493,7 +2527,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2493
2527
|
// Only set response_format when it's not the default 'text' type
|
|
2494
2528
|
// (sending response_format: {type: 'text'} is redundant and may cause issues)
|
|
2495
2529
|
const responseFormatOption = getResponseFormatOption(responseFormat, normalizedModel);
|
|
2496
|
-
if (responseFormatOption.type !==
|
|
2530
|
+
if (responseFormatOption.type !== "text") {
|
|
2497
2531
|
queryOptions.response_format = responseFormatOption;
|
|
2498
2532
|
}
|
|
2499
2533
|
let completion;
|
|
@@ -2514,15 +2548,19 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2514
2548
|
// for production prompts containing sensitive context.
|
|
2515
2549
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2516
2550
|
const totalContentChars = messages.reduce((sum, msg) => {
|
|
2517
|
-
if (typeof msg.content ===
|
|
2551
|
+
if (typeof msg.content === "string")
|
|
2518
2552
|
return sum + msg.content.length;
|
|
2519
2553
|
if (Array.isArray(msg.content)) {
|
|
2520
|
-
return sum +
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
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));
|
|
2526
2564
|
}
|
|
2527
2565
|
return sum;
|
|
2528
2566
|
}, 0);
|
|
@@ -2549,7 +2587,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2549
2587
|
const cachedTokens = completion.usage?.prompt_tokens_details?.cached_tokens ?? 0;
|
|
2550
2588
|
const response = {
|
|
2551
2589
|
id: completion.id,
|
|
2552
|
-
content: completion.choices[0]?.message?.content ||
|
|
2590
|
+
content: completion.choices[0]?.message?.content || "",
|
|
2553
2591
|
tool_calls: completion.choices[0]?.message?.tool_calls,
|
|
2554
2592
|
usage: {
|
|
2555
2593
|
prompt_tokens: completion.usage?.prompt_tokens ?? 0,
|
|
@@ -2559,7 +2597,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2559
2597
|
},
|
|
2560
2598
|
system_fingerprint: completion.system_fingerprint,
|
|
2561
2599
|
service_tier: options.service_tier,
|
|
2562
|
-
provider:
|
|
2600
|
+
provider: "openai",
|
|
2563
2601
|
model: normalizedModel,
|
|
2564
2602
|
};
|
|
2565
2603
|
return response;
|
|
@@ -2572,7 +2610,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2572
2610
|
* @param options The options for the LLM call. Defaults to an empty object.
|
|
2573
2611
|
* @return A promise that resolves to the response from the LLM.
|
|
2574
2612
|
*/
|
|
2575
|
-
const makeOpenAIChatCompletionCall = async (content, responseFormat =
|
|
2613
|
+
const makeOpenAIChatCompletionCall = async (content, responseFormat = "text", options = {}) => {
|
|
2576
2614
|
const mergedOptions = {
|
|
2577
2615
|
...DEFAULT_OPTIONS$1,
|
|
2578
2616
|
...options,
|
|
@@ -2581,7 +2619,7 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = 'text', op
|
|
|
2581
2619
|
// Track cost in the global cost tracker. Pass cached tokens through so the
|
|
2582
2620
|
// tracker applies the discounted cached-input rate (typically ~50% of the
|
|
2583
2621
|
// standard input rate) instead of billing every input token at full price.
|
|
2584
|
-
getLLMCostTracker().trackUsage(
|
|
2622
|
+
getLLMCostTracker().trackUsage("openai", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens);
|
|
2585
2623
|
// Handle tool calls differently
|
|
2586
2624
|
if (completion.tool_calls && completion.tool_calls.length > 0) {
|
|
2587
2625
|
const toolCallResponse = {
|
|
@@ -2597,10 +2635,10 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = 'text', op
|
|
|
2597
2635
|
prompt_tokens: completion.usage.prompt_tokens,
|
|
2598
2636
|
completion_tokens: completion.usage.completion_tokens,
|
|
2599
2637
|
reasoning_tokens: 0,
|
|
2600
|
-
provider:
|
|
2638
|
+
provider: "openai",
|
|
2601
2639
|
model: completion.model,
|
|
2602
2640
|
cached_tokens: completion.usage.cached_tokens,
|
|
2603
|
-
cost: calculateCost(
|
|
2641
|
+
cost: calculateCost("openai", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
|
|
2604
2642
|
},
|
|
2605
2643
|
tool_calls: completion.tool_calls,
|
|
2606
2644
|
};
|
|
@@ -2608,7 +2646,7 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = 'text', op
|
|
|
2608
2646
|
// Handle regular responses
|
|
2609
2647
|
const parsedResponse = await parseResponse(completion.content, responseFormat);
|
|
2610
2648
|
if (parsedResponse === null) {
|
|
2611
|
-
throw new Error(
|
|
2649
|
+
throw new Error("Failed to parse response");
|
|
2612
2650
|
}
|
|
2613
2651
|
return {
|
|
2614
2652
|
response: parsedResponse,
|
|
@@ -2616,10 +2654,10 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = 'text', op
|
|
|
2616
2654
|
prompt_tokens: completion.usage.prompt_tokens,
|
|
2617
2655
|
completion_tokens: completion.usage.completion_tokens,
|
|
2618
2656
|
reasoning_tokens: 0,
|
|
2619
|
-
provider:
|
|
2657
|
+
provider: "openai",
|
|
2620
2658
|
model: completion.model,
|
|
2621
2659
|
cached_tokens: completion.usage.cached_tokens,
|
|
2622
|
-
cost: calculateCost(
|
|
2660
|
+
cost: calculateCost("openai", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
|
|
2623
2661
|
},
|
|
2624
2662
|
tool_calls: completion.tool_calls,
|
|
2625
2663
|
};
|
|
@@ -2654,7 +2692,7 @@ const makeResponsesAPICall = async (input, options = {}) => {
|
|
|
2654
2692
|
const normalizedModel = normalizeModelName(options.model || DEFAULT_MODEL);
|
|
2655
2693
|
const apiKey = options.apiKey || getSecrets().openai.apiKey;
|
|
2656
2694
|
if (!apiKey) {
|
|
2657
|
-
throw new Error(
|
|
2695
|
+
throw new Error("OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set");
|
|
2658
2696
|
}
|
|
2659
2697
|
const openai = new OpenAI({
|
|
2660
2698
|
apiKey: apiKey,
|
|
@@ -2678,20 +2716,20 @@ const makeResponsesAPICall = async (input, options = {}) => {
|
|
|
2678
2716
|
// (the equivalent of Chat Completions' `prompt_tokens_details.cached_tokens`).
|
|
2679
2717
|
const responsesCachedTokens = response.usage?.input_tokens_details?.cached_tokens || 0;
|
|
2680
2718
|
// Track cost in the global cost tracker
|
|
2681
|
-
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);
|
|
2682
2720
|
// Extract tool calls from the output
|
|
2683
2721
|
const toolCalls = response.output
|
|
2684
|
-
?.filter((item) => item.type ===
|
|
2722
|
+
?.filter((item) => item.type === "function_call")
|
|
2685
2723
|
.map((toolCall) => ({
|
|
2686
2724
|
id: toolCall.call_id,
|
|
2687
|
-
type:
|
|
2725
|
+
type: "function",
|
|
2688
2726
|
function: {
|
|
2689
2727
|
name: toolCall.name,
|
|
2690
2728
|
arguments: toolCall.arguments,
|
|
2691
2729
|
},
|
|
2692
2730
|
}));
|
|
2693
2731
|
// Extract code interpreter outputs if present
|
|
2694
|
-
const codeInterpreterCalls = response.output?.filter((item) => item.type ===
|
|
2732
|
+
const codeInterpreterCalls = response.output?.filter((item) => item.type === "code_interpreter_call");
|
|
2695
2733
|
let codeInterpreterOutputs = undefined;
|
|
2696
2734
|
if (codeInterpreterCalls && codeInterpreterCalls.length > 0) {
|
|
2697
2735
|
// Each code_interpreter_call has an 'outputs' array property
|
|
@@ -2717,32 +2755,34 @@ const makeResponsesAPICall = async (input, options = {}) => {
|
|
|
2717
2755
|
prompt_tokens: response.usage?.input_tokens || 0,
|
|
2718
2756
|
completion_tokens: response.usage?.output_tokens || 0,
|
|
2719
2757
|
reasoning_tokens: response.usage?.output_tokens_details?.reasoning_tokens || 0,
|
|
2720
|
-
provider:
|
|
2758
|
+
provider: "openai",
|
|
2721
2759
|
model: normalizedModel,
|
|
2722
2760
|
cached_tokens: responsesCachedTokens,
|
|
2723
|
-
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),
|
|
2724
2762
|
},
|
|
2725
2763
|
tool_calls: toolCalls,
|
|
2726
|
-
...(codeInterpreterOutputs
|
|
2764
|
+
...(codeInterpreterOutputs
|
|
2765
|
+
? { code_interpreter_outputs: codeInterpreterOutputs }
|
|
2766
|
+
: {}),
|
|
2727
2767
|
};
|
|
2728
2768
|
}
|
|
2729
2769
|
// Extract text content from the response output
|
|
2730
2770
|
const textContent = response.output
|
|
2731
|
-
?.filter((item) => item.type ===
|
|
2771
|
+
?.filter((item) => item.type === "message")
|
|
2732
2772
|
.map((item) => item.content
|
|
2733
|
-
.filter((content) => content.type ===
|
|
2773
|
+
.filter((content) => content.type === "output_text")
|
|
2734
2774
|
.map((content) => content.text)
|
|
2735
|
-
.join(
|
|
2736
|
-
.join(
|
|
2775
|
+
.join(""))
|
|
2776
|
+
.join("") || "";
|
|
2737
2777
|
// Determine the format for parsing the response
|
|
2738
|
-
let parsingFormat =
|
|
2739
|
-
if (requestBody.text?.format?.type ===
|
|
2740
|
-
parsingFormat =
|
|
2778
|
+
let parsingFormat = "text";
|
|
2779
|
+
if (requestBody.text?.format?.type === "json_object") {
|
|
2780
|
+
parsingFormat = "json";
|
|
2741
2781
|
}
|
|
2742
2782
|
// Handle regular responses
|
|
2743
2783
|
const parsedResponse = await parseResponse(textContent, parsingFormat);
|
|
2744
2784
|
if (parsedResponse === null) {
|
|
2745
|
-
throw new Error(
|
|
2785
|
+
throw new Error("Failed to parse response from Responses API");
|
|
2746
2786
|
}
|
|
2747
2787
|
return {
|
|
2748
2788
|
response: parsedResponse,
|
|
@@ -2750,13 +2790,15 @@ const makeResponsesAPICall = async (input, options = {}) => {
|
|
|
2750
2790
|
prompt_tokens: response.usage?.input_tokens || 0,
|
|
2751
2791
|
completion_tokens: response.usage?.output_tokens || 0,
|
|
2752
2792
|
reasoning_tokens: response.usage?.output_tokens_details?.reasoning_tokens || 0,
|
|
2753
|
-
provider:
|
|
2793
|
+
provider: "openai",
|
|
2754
2794
|
model: normalizedModel,
|
|
2755
2795
|
cached_tokens: responsesCachedTokens,
|
|
2756
|
-
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),
|
|
2757
2797
|
},
|
|
2758
2798
|
tool_calls: toolCalls,
|
|
2759
|
-
...(codeInterpreterOutputs
|
|
2799
|
+
...(codeInterpreterOutputs
|
|
2800
|
+
? { code_interpreter_outputs: codeInterpreterOutputs }
|
|
2801
|
+
: {}),
|
|
2760
2802
|
};
|
|
2761
2803
|
};
|
|
2762
2804
|
|
|
@@ -8066,12 +8108,12 @@ function sanitizeObject(obj, sensitiveFields = ['apiKey', 'token', 'secret', 'pa
|
|
|
8066
8108
|
const isRetryableAnthropicError = (error) => {
|
|
8067
8109
|
if (error instanceof Error) {
|
|
8068
8110
|
const message = error.message;
|
|
8069
|
-
if (message.includes(
|
|
8070
|
-
message.includes(
|
|
8071
|
-
message.includes(
|
|
8072
|
-
message.includes(
|
|
8073
|
-
message.includes(
|
|
8074
|
-
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")) {
|
|
8075
8117
|
return true;
|
|
8076
8118
|
}
|
|
8077
8119
|
}
|
|
@@ -8086,8 +8128,11 @@ const isRetryableAnthropicError = (error) => {
|
|
|
8086
8128
|
function translateToolsToAnthropic(tools) {
|
|
8087
8129
|
return tools.map((tool) => ({
|
|
8088
8130
|
name: tool.function.name,
|
|
8089
|
-
description: tool.function.description ||
|
|
8090
|
-
input_schema: (tool.function.parameters || {
|
|
8131
|
+
description: tool.function.description || "",
|
|
8132
|
+
input_schema: (tool.function.parameters || {
|
|
8133
|
+
type: "object",
|
|
8134
|
+
properties: {},
|
|
8135
|
+
}),
|
|
8091
8136
|
}));
|
|
8092
8137
|
}
|
|
8093
8138
|
/**
|
|
@@ -8100,27 +8145,31 @@ function translateContextToAnthropic(context) {
|
|
|
8100
8145
|
const systemParts = [];
|
|
8101
8146
|
const messages = [];
|
|
8102
8147
|
for (const msg of context) {
|
|
8103
|
-
if (msg.role ===
|
|
8104
|
-
if (typeof msg.content ===
|
|
8148
|
+
if (msg.role === "system" || msg.role === "developer") {
|
|
8149
|
+
if (typeof msg.content === "string") {
|
|
8105
8150
|
systemParts.push(msg.content);
|
|
8106
8151
|
}
|
|
8107
8152
|
continue;
|
|
8108
8153
|
}
|
|
8109
|
-
if (msg.role ===
|
|
8110
|
-
const content = typeof msg.content ===
|
|
8154
|
+
if (msg.role === "user") {
|
|
8155
|
+
const content = typeof msg.content === "string"
|
|
8111
8156
|
? msg.content
|
|
8112
8157
|
: JSON.stringify(msg.content);
|
|
8113
|
-
messages.push({ role:
|
|
8158
|
+
messages.push({ role: "user", content });
|
|
8114
8159
|
continue;
|
|
8115
8160
|
}
|
|
8116
|
-
if (msg.role ===
|
|
8161
|
+
if (msg.role === "assistant") {
|
|
8117
8162
|
const assistantMsg = msg;
|
|
8118
8163
|
// If the assistant message has tool_calls, translate to Anthropic tool_use blocks
|
|
8119
8164
|
if (assistantMsg.tool_calls && assistantMsg.tool_calls.length > 0) {
|
|
8120
8165
|
const contentBlocks = [];
|
|
8121
8166
|
// Include text content if present
|
|
8122
|
-
if (typeof assistantMsg.content ===
|
|
8123
|
-
|
|
8167
|
+
if (typeof assistantMsg.content === "string" &&
|
|
8168
|
+
assistantMsg.content.trim()) {
|
|
8169
|
+
contentBlocks.push({
|
|
8170
|
+
type: "text",
|
|
8171
|
+
text: assistantMsg.content,
|
|
8172
|
+
});
|
|
8124
8173
|
}
|
|
8125
8174
|
// Translate each tool_call to a tool_use block
|
|
8126
8175
|
for (const tc of assistantMsg.tool_calls) {
|
|
@@ -8132,32 +8181,36 @@ function translateContextToAnthropic(context) {
|
|
|
8132
8181
|
input = { raw: tc.function.arguments };
|
|
8133
8182
|
}
|
|
8134
8183
|
contentBlocks.push({
|
|
8135
|
-
type:
|
|
8184
|
+
type: "tool_use",
|
|
8136
8185
|
id: tc.id,
|
|
8137
8186
|
name: tc.function.name,
|
|
8138
8187
|
input,
|
|
8139
8188
|
});
|
|
8140
8189
|
}
|
|
8141
|
-
messages.push({ role:
|
|
8190
|
+
messages.push({ role: "assistant", content: contentBlocks });
|
|
8142
8191
|
}
|
|
8143
8192
|
else {
|
|
8144
|
-
const content = typeof assistantMsg.content ===
|
|
8193
|
+
const content = typeof assistantMsg.content === "string"
|
|
8145
8194
|
? assistantMsg.content
|
|
8146
8195
|
: JSON.stringify(assistantMsg.content);
|
|
8147
|
-
messages.push({ role:
|
|
8196
|
+
messages.push({ role: "assistant", content });
|
|
8148
8197
|
}
|
|
8149
8198
|
continue;
|
|
8150
8199
|
}
|
|
8151
|
-
if (msg.role ===
|
|
8200
|
+
if (msg.role === "tool") {
|
|
8152
8201
|
// Anthropic expects tool results as user messages with tool_result content blocks
|
|
8153
8202
|
const toolMsg = msg;
|
|
8154
8203
|
messages.push({
|
|
8155
|
-
role:
|
|
8156
|
-
content: [
|
|
8157
|
-
|
|
8204
|
+
role: "user",
|
|
8205
|
+
content: [
|
|
8206
|
+
{
|
|
8207
|
+
type: "tool_result",
|
|
8158
8208
|
tool_use_id: toolMsg.tool_call_id,
|
|
8159
|
-
content: typeof toolMsg.content ===
|
|
8160
|
-
|
|
8209
|
+
content: typeof toolMsg.content === "string"
|
|
8210
|
+
? toolMsg.content
|
|
8211
|
+
: JSON.stringify(toolMsg.content),
|
|
8212
|
+
},
|
|
8213
|
+
],
|
|
8161
8214
|
});
|
|
8162
8215
|
continue;
|
|
8163
8216
|
}
|
|
@@ -8178,13 +8231,13 @@ function translateContextToAnthropic(context) {
|
|
|
8178
8231
|
merged.push({ role: msg.role, content: toContentBlocks(msg.content) });
|
|
8179
8232
|
}
|
|
8180
8233
|
}
|
|
8181
|
-
return { messages: merged, systemText: systemParts.join(
|
|
8234
|
+
return { messages: merged, systemText: systemParts.join("\n\n") };
|
|
8182
8235
|
}
|
|
8183
8236
|
/** Convert string or content block array to a uniform content block array. */
|
|
8184
8237
|
function toContentBlocks(content) {
|
|
8185
|
-
if (typeof content ===
|
|
8238
|
+
if (typeof content === "string") {
|
|
8186
8239
|
const textBlock = {
|
|
8187
|
-
type:
|
|
8240
|
+
type: "text",
|
|
8188
8241
|
text: content,
|
|
8189
8242
|
citations: null,
|
|
8190
8243
|
};
|
|
@@ -8204,11 +8257,11 @@ function toContentBlocks(content) {
|
|
|
8204
8257
|
* @param options The options for the LLM call.
|
|
8205
8258
|
* @return A promise that resolves to the response from the Anthropic API.
|
|
8206
8259
|
*/
|
|
8207
|
-
async function makeAnthropicCall(content, responseFormat =
|
|
8208
|
-
const model = (options.model ||
|
|
8260
|
+
async function makeAnthropicCall(content, responseFormat = "text", options = {}) {
|
|
8261
|
+
const model = (options.model || "claude-sonnet-4-6");
|
|
8209
8262
|
const apiKey = options.apiKey || getSecrets().anthropic.apiKey;
|
|
8210
8263
|
if (!apiKey) {
|
|
8211
|
-
throw new Error(
|
|
8264
|
+
throw new Error("Anthropic API key is not provided and ANTHROPIC_API_KEY environment variable is not set");
|
|
8212
8265
|
}
|
|
8213
8266
|
const client = new Anthropic({
|
|
8214
8267
|
apiKey,
|
|
@@ -8220,8 +8273,10 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
|
|
|
8220
8273
|
systemParts.push(options.developerPrompt);
|
|
8221
8274
|
}
|
|
8222
8275
|
// If JSON response is requested, instruct the model via system prompt
|
|
8223
|
-
if (responseFormat ===
|
|
8224
|
-
|
|
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.");
|
|
8225
8280
|
}
|
|
8226
8281
|
// Build messages array
|
|
8227
8282
|
const messages = [];
|
|
@@ -8234,7 +8289,7 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
|
|
|
8234
8289
|
messages.push(...translated.messages);
|
|
8235
8290
|
}
|
|
8236
8291
|
// Add user content
|
|
8237
|
-
messages.push({ role:
|
|
8292
|
+
messages.push({ role: "user", content });
|
|
8238
8293
|
// Build request params
|
|
8239
8294
|
const requestParams = {
|
|
8240
8295
|
model,
|
|
@@ -8243,7 +8298,7 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
|
|
|
8243
8298
|
};
|
|
8244
8299
|
// Add system prompt if present
|
|
8245
8300
|
if (systemParts.length > 0) {
|
|
8246
|
-
requestParams.system = systemParts.join(
|
|
8301
|
+
requestParams.system = systemParts.join("\n\n");
|
|
8247
8302
|
}
|
|
8248
8303
|
// Add temperature if specified
|
|
8249
8304
|
if (options.temperature !== undefined) {
|
|
@@ -8266,14 +8321,14 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
|
|
|
8266
8321
|
const inputTokens = response.usage.input_tokens;
|
|
8267
8322
|
const outputTokens = response.usage.output_tokens;
|
|
8268
8323
|
// Track cost
|
|
8269
|
-
getLLMCostTracker().trackUsage(
|
|
8324
|
+
getLLMCostTracker().trackUsage("anthropic", model, inputTokens, outputTokens);
|
|
8270
8325
|
// Extract tool use blocks
|
|
8271
|
-
const toolUseBlocks = response.content.filter((block) => block.type ===
|
|
8326
|
+
const toolUseBlocks = response.content.filter((block) => block.type === "tool_use");
|
|
8272
8327
|
if (toolUseBlocks.length > 0) {
|
|
8273
8328
|
// Translate Anthropic tool_use to OpenAI tool_calls format
|
|
8274
8329
|
const toolCalls = toolUseBlocks.map((block) => ({
|
|
8275
8330
|
id: block.id,
|
|
8276
|
-
type:
|
|
8331
|
+
type: "function",
|
|
8277
8332
|
function: {
|
|
8278
8333
|
name: block.name,
|
|
8279
8334
|
arguments: JSON.stringify(block.input),
|
|
@@ -8292,22 +8347,22 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
|
|
|
8292
8347
|
prompt_tokens: inputTokens,
|
|
8293
8348
|
completion_tokens: outputTokens,
|
|
8294
8349
|
reasoning_tokens: 0,
|
|
8295
|
-
provider:
|
|
8350
|
+
provider: "anthropic",
|
|
8296
8351
|
model,
|
|
8297
|
-
cost: calculateCost(
|
|
8352
|
+
cost: calculateCost("anthropic", model, inputTokens, outputTokens),
|
|
8298
8353
|
},
|
|
8299
8354
|
tool_calls: toolCalls,
|
|
8300
8355
|
};
|
|
8301
8356
|
}
|
|
8302
8357
|
// Extract text content
|
|
8303
8358
|
const textContent = response.content
|
|
8304
|
-
.filter((block) => block.type ===
|
|
8359
|
+
.filter((block) => block.type === "text")
|
|
8305
8360
|
.map((block) => block.text)
|
|
8306
|
-
.join(
|
|
8361
|
+
.join("");
|
|
8307
8362
|
// Parse response
|
|
8308
8363
|
const parsedResponse = await parseResponse(textContent, responseFormat);
|
|
8309
8364
|
if (parsedResponse === null) {
|
|
8310
|
-
throw new Error(
|
|
8365
|
+
throw new Error("Failed to parse Anthropic response");
|
|
8311
8366
|
}
|
|
8312
8367
|
return {
|
|
8313
8368
|
response: parsedResponse,
|
|
@@ -8315,9 +8370,9 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
|
|
|
8315
8370
|
prompt_tokens: inputTokens,
|
|
8316
8371
|
completion_tokens: outputTokens,
|
|
8317
8372
|
reasoning_tokens: 0,
|
|
8318
|
-
provider:
|
|
8373
|
+
provider: "anthropic",
|
|
8319
8374
|
model,
|
|
8320
|
-
cost: calculateCost(
|
|
8375
|
+
cost: calculateCost("anthropic", model, inputTokens, outputTokens),
|
|
8321
8376
|
},
|
|
8322
8377
|
};
|
|
8323
8378
|
}
|
|
@@ -8339,7 +8394,9 @@ async function makeAnthropicCall(content, responseFormat = 'text', options = {})
|
|
|
8339
8394
|
const isRetryableError = (error) => {
|
|
8340
8395
|
if (error instanceof Error) {
|
|
8341
8396
|
const message = error.message;
|
|
8342
|
-
if (message.includes(
|
|
8397
|
+
if (message.includes("429") ||
|
|
8398
|
+
message.includes("rate limit") ||
|
|
8399
|
+
message.includes("Rate limit")) {
|
|
8343
8400
|
return true;
|
|
8344
8401
|
}
|
|
8345
8402
|
}
|
|
@@ -8353,14 +8410,16 @@ function resolveApiKey(provider, optionsApiKey) {
|
|
|
8353
8410
|
if (optionsApiKey)
|
|
8354
8411
|
return optionsApiKey;
|
|
8355
8412
|
const secrets = getSecrets();
|
|
8356
|
-
const pathParts = provider.apiKeySecretPath.split(
|
|
8413
|
+
const pathParts = provider.apiKeySecretPath.split(".");
|
|
8357
8414
|
let current = secrets;
|
|
8358
8415
|
for (const part of pathParts) {
|
|
8359
|
-
if (current === null ||
|
|
8360
|
-
|
|
8416
|
+
if (current === null ||
|
|
8417
|
+
current === undefined ||
|
|
8418
|
+
typeof current !== "object")
|
|
8419
|
+
return "";
|
|
8361
8420
|
current = current[part];
|
|
8362
8421
|
}
|
|
8363
|
-
return (typeof current ===
|
|
8422
|
+
return (typeof current === "string" ? current : "") || "";
|
|
8364
8423
|
}
|
|
8365
8424
|
/**
|
|
8366
8425
|
* Makes a call to an OpenAI-compatible provider (Kimi, Qwen, or any future provider).
|
|
@@ -8374,12 +8433,12 @@ function resolveApiKey(provider, optionsApiKey) {
|
|
|
8374
8433
|
* @param providerName The provider key in OPENAI_COMPATIBLE_PROVIDERS.
|
|
8375
8434
|
* @return A promise that resolves to the response from the provider.
|
|
8376
8435
|
*/
|
|
8377
|
-
async function makeOpenAICompatibleCall(content, responseFormat =
|
|
8436
|
+
async function makeOpenAICompatibleCall(content, responseFormat = "text", options = {}, providerName) {
|
|
8378
8437
|
const providerConfig = OPENAI_COMPATIBLE_PROVIDERS[providerName];
|
|
8379
8438
|
if (!providerConfig) {
|
|
8380
8439
|
throw new Error(`Unknown OpenAI-compatible provider: ${providerName}`);
|
|
8381
8440
|
}
|
|
8382
|
-
const model = normalizeModelName(options.model ||
|
|
8441
|
+
const model = normalizeModelName(options.model || "");
|
|
8383
8442
|
const apiKey = resolveApiKey(providerConfig, options.apiKey);
|
|
8384
8443
|
if (!apiKey) {
|
|
8385
8444
|
throw new Error(`${providerConfig.name} API key is not provided and ${providerConfig.apiKeyEnvVar} environment variable is not set`);
|
|
@@ -8392,31 +8451,36 @@ async function makeOpenAICompatibleCall(content, responseFormat = 'text', option
|
|
|
8392
8451
|
const messages = [];
|
|
8393
8452
|
// Add system message with developer prompt if present
|
|
8394
8453
|
if (options.developerPrompt) {
|
|
8395
|
-
messages.push({ role:
|
|
8454
|
+
messages.push({ role: "system", content: options.developerPrompt });
|
|
8396
8455
|
}
|
|
8397
8456
|
// Add context if present
|
|
8398
8457
|
if (options.context) {
|
|
8399
8458
|
messages.push(...options.context);
|
|
8400
8459
|
}
|
|
8401
8460
|
// Add user content
|
|
8402
|
-
if (typeof content ===
|
|
8403
|
-
messages.push({ role:
|
|
8461
|
+
if (typeof content === "string") {
|
|
8462
|
+
messages.push({ role: "user", content });
|
|
8404
8463
|
}
|
|
8405
8464
|
else {
|
|
8406
|
-
messages.push({ role:
|
|
8465
|
+
messages.push({ role: "user", content });
|
|
8407
8466
|
}
|
|
8408
8467
|
// Determine if the model supports JSON mode
|
|
8409
8468
|
const supportsJson = isValidModel(model) && getModelCapabilities(model).supportsJson;
|
|
8410
8469
|
// If JSON response format, add hint to system prompt
|
|
8411
|
-
if ((responseFormat ===
|
|
8470
|
+
if ((responseFormat === "json" ||
|
|
8471
|
+
(typeof responseFormat === "object" &&
|
|
8472
|
+
responseFormat.type === "json_schema")) &&
|
|
8412
8473
|
supportsJson) {
|
|
8413
|
-
if (!messages.some((m) => m.role ===
|
|
8414
|
-
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
|
+
});
|
|
8415
8479
|
}
|
|
8416
8480
|
else {
|
|
8417
|
-
const systemMsgIndex = messages.findIndex((m) => m.role ===
|
|
8481
|
+
const systemMsgIndex = messages.findIndex((m) => m.role === "system");
|
|
8418
8482
|
const systemMsg = messages[systemMsgIndex];
|
|
8419
|
-
if (typeof systemMsg.content ===
|
|
8483
|
+
if (typeof systemMsg.content === "string") {
|
|
8420
8484
|
messages[systemMsgIndex] = {
|
|
8421
8485
|
...systemMsg,
|
|
8422
8486
|
content: `${systemMsg.content} Please respond in valid JSON format.`,
|
|
@@ -8429,8 +8493,8 @@ async function makeOpenAICompatibleCall(content, responseFormat = 'text', option
|
|
|
8429
8493
|
messages,
|
|
8430
8494
|
};
|
|
8431
8495
|
// Add response format if JSON is supported
|
|
8432
|
-
if (responseFormat !==
|
|
8433
|
-
queryOptions.response_format = { type:
|
|
8496
|
+
if (responseFormat !== "text" && supportsJson) {
|
|
8497
|
+
queryOptions.response_format = { type: "json_object" };
|
|
8434
8498
|
}
|
|
8435
8499
|
// Temperature
|
|
8436
8500
|
if (options.temperature !== undefined) {
|
|
@@ -8488,7 +8552,7 @@ async function makeOpenAICompatibleCall(content, responseFormat = 'text', option
|
|
|
8488
8552
|
};
|
|
8489
8553
|
}
|
|
8490
8554
|
// Handle regular responses
|
|
8491
|
-
const textContent = completion.choices[0]?.message?.content ||
|
|
8555
|
+
const textContent = completion.choices[0]?.message?.content || "";
|
|
8492
8556
|
const parsedResponse = await parseResponse(textContent, responseFormat);
|
|
8493
8557
|
if (parsedResponse === null) {
|
|
8494
8558
|
throw new Error(`Failed to parse ${providerConfig.name} response`);
|
|
@@ -8780,7 +8844,9 @@ const isRetryableDeepseekError = (error) => {
|
|
|
8780
8844
|
if (error instanceof Error) {
|
|
8781
8845
|
const message = error.message;
|
|
8782
8846
|
// Retry only on rate limits (429)
|
|
8783
|
-
if (message.includes(
|
|
8847
|
+
if (message.includes("429") ||
|
|
8848
|
+
message.includes("rate limit") ||
|
|
8849
|
+
message.includes("Rate limit")) {
|
|
8784
8850
|
return true;
|
|
8785
8851
|
}
|
|
8786
8852
|
}
|
|
@@ -8790,7 +8856,7 @@ const isRetryableDeepseekError = (error) => {
|
|
|
8790
8856
|
* Default options for Deepseek API calls
|
|
8791
8857
|
*/
|
|
8792
8858
|
const DEFAULT_DEEPSEEK_OPTIONS = {
|
|
8793
|
-
defaultModel:
|
|
8859
|
+
defaultModel: "deepseek-chat",
|
|
8794
8860
|
};
|
|
8795
8861
|
/**
|
|
8796
8862
|
* Checks if the given model is a supported Deepseek model
|
|
@@ -8802,7 +8868,7 @@ const isSupportedDeepseekModel = (model) => {
|
|
|
8802
8868
|
return false;
|
|
8803
8869
|
}
|
|
8804
8870
|
const capabilities = getModelCapabilities(model);
|
|
8805
|
-
return capabilities.provider ===
|
|
8871
|
+
return capabilities.provider === "deepseek";
|
|
8806
8872
|
};
|
|
8807
8873
|
/**
|
|
8808
8874
|
* Checks if the given Deepseek model supports JSON output format
|
|
@@ -8835,17 +8901,19 @@ const supportsToolCalling = (model) => {
|
|
|
8835
8901
|
*/
|
|
8836
8902
|
function getDeepseekResponseFormatOption(responseFormat, model) {
|
|
8837
8903
|
// Check if the model supports JSON output
|
|
8838
|
-
if (responseFormat !==
|
|
8904
|
+
if (responseFormat !== "text" && !supportsJsonOutput(model)) {
|
|
8839
8905
|
getLumicLogger().warn(`Model ${model} does not support JSON output. Using text format instead.`);
|
|
8840
|
-
return { type:
|
|
8906
|
+
return { type: "text" };
|
|
8841
8907
|
}
|
|
8842
|
-
if (responseFormat ===
|
|
8843
|
-
return { type:
|
|
8908
|
+
if (responseFormat === "text") {
|
|
8909
|
+
return { type: "text" };
|
|
8844
8910
|
}
|
|
8845
|
-
if (responseFormat ===
|
|
8846
|
-
|
|
8911
|
+
if (responseFormat === "json" ||
|
|
8912
|
+
(typeof responseFormat === "object" &&
|
|
8913
|
+
responseFormat.type === "json_schema")) {
|
|
8914
|
+
return { type: "json_object" };
|
|
8847
8915
|
}
|
|
8848
|
-
return { type:
|
|
8916
|
+
return { type: "text" };
|
|
8849
8917
|
}
|
|
8850
8918
|
/**
|
|
8851
8919
|
* Creates a completion using the Deepseek API
|
|
@@ -8864,24 +8932,26 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
|
|
|
8864
8932
|
throw new Error(`Unsupported Deepseek model: ${normalizedModel}. Please use 'deepseek-chat' or 'deepseek-reasoner'.`);
|
|
8865
8933
|
}
|
|
8866
8934
|
// Check if tools are requested with a model that doesn't support them
|
|
8867
|
-
if (options.tools &&
|
|
8935
|
+
if (options.tools &&
|
|
8936
|
+
options.tools.length > 0 &&
|
|
8937
|
+
!supportsToolCalling(normalizedModel)) {
|
|
8868
8938
|
throw new Error(`Model ${normalizedModel} does not support tool calling.`);
|
|
8869
8939
|
}
|
|
8870
8940
|
const apiKey = options.apiKey || getSecrets().deepseek.apiKey;
|
|
8871
8941
|
if (!apiKey) {
|
|
8872
|
-
throw new Error(
|
|
8942
|
+
throw new Error("Deepseek API key is not provided and DEEPSEEK_API_KEY environment variable is not set");
|
|
8873
8943
|
}
|
|
8874
8944
|
// Initialize OpenAI client with Deepseek API URL
|
|
8875
8945
|
const openai = new OpenAI({
|
|
8876
8946
|
apiKey,
|
|
8877
|
-
baseURL:
|
|
8947
|
+
baseURL: "https://api.deepseek.com",
|
|
8878
8948
|
timeout: options.timeout ?? LLM_TIMEOUT_MS,
|
|
8879
8949
|
});
|
|
8880
8950
|
const messages = [];
|
|
8881
8951
|
// Add system message with developer prompt if present
|
|
8882
8952
|
if (options.developerPrompt) {
|
|
8883
8953
|
messages.push({
|
|
8884
|
-
role:
|
|
8954
|
+
role: "system",
|
|
8885
8955
|
content: options.developerPrompt,
|
|
8886
8956
|
});
|
|
8887
8957
|
}
|
|
@@ -8890,33 +8960,35 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
|
|
|
8890
8960
|
messages.push(...options.context);
|
|
8891
8961
|
}
|
|
8892
8962
|
// Add user content
|
|
8893
|
-
if (typeof content ===
|
|
8963
|
+
if (typeof content === "string") {
|
|
8894
8964
|
messages.push({
|
|
8895
|
-
role:
|
|
8965
|
+
role: "user",
|
|
8896
8966
|
content,
|
|
8897
8967
|
});
|
|
8898
8968
|
}
|
|
8899
8969
|
else {
|
|
8900
8970
|
messages.push({
|
|
8901
|
-
role:
|
|
8971
|
+
role: "user",
|
|
8902
8972
|
content,
|
|
8903
8973
|
});
|
|
8904
8974
|
}
|
|
8905
8975
|
// If JSON response format, include a hint in the system prompt
|
|
8906
|
-
if ((responseFormat ===
|
|
8907
|
-
|
|
8976
|
+
if ((responseFormat === "json" ||
|
|
8977
|
+
(typeof responseFormat === "object" &&
|
|
8978
|
+
responseFormat.type === "json_schema")) &&
|
|
8979
|
+
supportsJsonOutput(normalizedModel)) {
|
|
8908
8980
|
// If there's no system message yet, add one
|
|
8909
|
-
if (!messages.some(m => m.role ===
|
|
8981
|
+
if (!messages.some((m) => m.role === "system")) {
|
|
8910
8982
|
messages.unshift({
|
|
8911
|
-
role:
|
|
8912
|
-
content:
|
|
8983
|
+
role: "system",
|
|
8984
|
+
content: "Please respond in valid JSON format.",
|
|
8913
8985
|
});
|
|
8914
8986
|
}
|
|
8915
8987
|
else {
|
|
8916
8988
|
// Append to existing system message
|
|
8917
|
-
const systemMsgIndex = messages.findIndex(m => m.role ===
|
|
8989
|
+
const systemMsgIndex = messages.findIndex((m) => m.role === "system");
|
|
8918
8990
|
const systemMsg = messages[systemMsgIndex];
|
|
8919
|
-
if (typeof systemMsg.content ===
|
|
8991
|
+
if (typeof systemMsg.content === "string") {
|
|
8920
8992
|
messages[systemMsgIndex] = {
|
|
8921
8993
|
...systemMsg,
|
|
8922
8994
|
content: `${systemMsg.content} Please respond in valid JSON format.`,
|
|
@@ -8957,7 +9029,7 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
|
|
|
8957
9029
|
0;
|
|
8958
9030
|
return {
|
|
8959
9031
|
id: completion.id,
|
|
8960
|
-
content: completion.choices[0]?.message?.content ||
|
|
9032
|
+
content: completion.choices[0]?.message?.content || "",
|
|
8961
9033
|
tool_calls: completion.choices[0]?.message?.tool_calls,
|
|
8962
9034
|
usage: {
|
|
8963
9035
|
prompt_tokens: completion.usage?.prompt_tokens ?? 0,
|
|
@@ -8966,7 +9038,7 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
|
|
|
8966
9038
|
cached_tokens: cachedTokens,
|
|
8967
9039
|
},
|
|
8968
9040
|
system_fingerprint: completion.system_fingerprint,
|
|
8969
|
-
provider:
|
|
9041
|
+
provider: "deepseek",
|
|
8970
9042
|
model: normalizedModel,
|
|
8971
9043
|
};
|
|
8972
9044
|
}
|
|
@@ -8983,7 +9055,7 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
|
|
|
8983
9055
|
* @param options Configuration options including model ('deepseek-chat' or 'deepseek-reasoner'), tools, and apiKey.
|
|
8984
9056
|
* @return A promise that resolves to the response from the Deepseek API.
|
|
8985
9057
|
*/
|
|
8986
|
-
const makeDeepseekCall = async (content, responseFormat =
|
|
9058
|
+
const makeDeepseekCall = async (content, responseFormat = "json", options = {}) => {
|
|
8987
9059
|
// Set default model if not provided
|
|
8988
9060
|
const mergedOptions = {
|
|
8989
9061
|
...options,
|
|
@@ -8993,17 +9065,17 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
|
|
|
8993
9065
|
}
|
|
8994
9066
|
const modelName = normalizeModelName(mergedOptions.model);
|
|
8995
9067
|
// Check if the requested response format is compatible with the model
|
|
8996
|
-
if (responseFormat !==
|
|
9068
|
+
if (responseFormat !== "text" && !supportsJsonOutput(modelName)) {
|
|
8997
9069
|
getLumicLogger().warn(`Model ${modelName} does not support JSON output. Will return error in the response.`);
|
|
8998
9070
|
return {
|
|
8999
9071
|
response: {
|
|
9000
|
-
error: `Model ${modelName} does not support JSON output format
|
|
9072
|
+
error: `Model ${modelName} does not support JSON output format.`,
|
|
9001
9073
|
},
|
|
9002
9074
|
usage: {
|
|
9003
9075
|
prompt_tokens: 0,
|
|
9004
9076
|
completion_tokens: 0,
|
|
9005
9077
|
reasoning_tokens: 0,
|
|
9006
|
-
provider:
|
|
9078
|
+
provider: "deepseek",
|
|
9007
9079
|
model: modelName,
|
|
9008
9080
|
cached_tokens: 0,
|
|
9009
9081
|
cost: 0,
|
|
@@ -9012,17 +9084,19 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
|
|
|
9012
9084
|
};
|
|
9013
9085
|
}
|
|
9014
9086
|
// Check if tools are requested with a model that doesn't support them
|
|
9015
|
-
if (mergedOptions.tools &&
|
|
9087
|
+
if (mergedOptions.tools &&
|
|
9088
|
+
mergedOptions.tools.length > 0 &&
|
|
9089
|
+
!supportsToolCalling(modelName)) {
|
|
9016
9090
|
getLumicLogger().warn(`Model ${modelName} does not support tool calling. Will return error in the response.`);
|
|
9017
9091
|
return {
|
|
9018
9092
|
response: {
|
|
9019
|
-
error: `Model ${modelName} does not support tool calling
|
|
9093
|
+
error: `Model ${modelName} does not support tool calling.`,
|
|
9020
9094
|
},
|
|
9021
9095
|
usage: {
|
|
9022
9096
|
prompt_tokens: 0,
|
|
9023
9097
|
completion_tokens: 0,
|
|
9024
9098
|
reasoning_tokens: 0,
|
|
9025
|
-
provider:
|
|
9099
|
+
provider: "deepseek",
|
|
9026
9100
|
model: modelName,
|
|
9027
9101
|
cached_tokens: 0,
|
|
9028
9102
|
cost: 0,
|
|
@@ -9034,7 +9108,7 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
|
|
|
9034
9108
|
const completion = await createDeepseekCompletion(content, responseFormat, mergedOptions);
|
|
9035
9109
|
// Track cost in the global cost tracker. Pass cached tokens through so the
|
|
9036
9110
|
// discounted cached-input pricing tier is applied.
|
|
9037
|
-
getLLMCostTracker().trackUsage(
|
|
9111
|
+
getLLMCostTracker().trackUsage("deepseek", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens);
|
|
9038
9112
|
// Handle tool calls similarly to OpenAI
|
|
9039
9113
|
if (completion.tool_calls && completion.tool_calls.length > 0) {
|
|
9040
9114
|
const toolCallResponse = {
|
|
@@ -9050,10 +9124,10 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
|
|
|
9050
9124
|
prompt_tokens: completion.usage.prompt_tokens,
|
|
9051
9125
|
completion_tokens: completion.usage.completion_tokens,
|
|
9052
9126
|
reasoning_tokens: 0, // Deepseek doesn't provide reasoning tokens separately
|
|
9053
|
-
provider:
|
|
9127
|
+
provider: "deepseek",
|
|
9054
9128
|
model: completion.model,
|
|
9055
9129
|
cached_tokens: completion.usage.cached_tokens,
|
|
9056
|
-
cost: calculateCost(
|
|
9130
|
+
cost: calculateCost("deepseek", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
|
|
9057
9131
|
},
|
|
9058
9132
|
tool_calls: completion.tool_calls,
|
|
9059
9133
|
};
|
|
@@ -9061,7 +9135,7 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
|
|
|
9061
9135
|
// Handle regular responses
|
|
9062
9136
|
const parsedResponse = await parseResponse(completion.content, responseFormat);
|
|
9063
9137
|
if (parsedResponse === null) {
|
|
9064
|
-
throw new Error(
|
|
9138
|
+
throw new Error("Failed to parse Deepseek response");
|
|
9065
9139
|
}
|
|
9066
9140
|
return {
|
|
9067
9141
|
response: parsedResponse,
|
|
@@ -9069,10 +9143,10 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
|
|
|
9069
9143
|
prompt_tokens: completion.usage.prompt_tokens,
|
|
9070
9144
|
completion_tokens: completion.usage.completion_tokens,
|
|
9071
9145
|
reasoning_tokens: 0, // Deepseek doesn't provide reasoning tokens separately
|
|
9072
|
-
provider:
|
|
9146
|
+
provider: "deepseek",
|
|
9073
9147
|
model: completion.model,
|
|
9074
9148
|
cached_tokens: completion.usage.cached_tokens,
|
|
9075
|
-
cost: calculateCost(
|
|
9149
|
+
cost: calculateCost("deepseek", completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
|
|
9076
9150
|
},
|
|
9077
9151
|
tool_calls: completion.tool_calls,
|
|
9078
9152
|
};
|
|
@@ -9082,13 +9156,13 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
|
|
|
9082
9156
|
getLumicLogger().error(`Error in Deepseek API call: ${sanitizeError(error)}`);
|
|
9083
9157
|
return {
|
|
9084
9158
|
response: {
|
|
9085
|
-
error: sanitizeError(error)
|
|
9159
|
+
error: sanitizeError(error),
|
|
9086
9160
|
},
|
|
9087
9161
|
usage: {
|
|
9088
9162
|
prompt_tokens: 0,
|
|
9089
9163
|
completion_tokens: 0,
|
|
9090
9164
|
reasoning_tokens: 0,
|
|
9091
|
-
provider:
|
|
9165
|
+
provider: "deepseek",
|
|
9092
9166
|
model: modelName,
|
|
9093
9167
|
cached_tokens: 0,
|
|
9094
9168
|
cost: 0,
|
|
@@ -9197,15 +9271,15 @@ const generateCacheKey = (auth, envVarsCheck = null) => {
|
|
|
9197
9271
|
if (auth) {
|
|
9198
9272
|
return `${auth.AWS_ACCESS_KEY_ID}:${auth.AWS_SECRET_ACCESS_KEY}:${auth.AWS_REGION}`;
|
|
9199
9273
|
}
|
|
9200
|
-
if (envVarsCheck?.source ===
|
|
9274
|
+
if (envVarsCheck?.source === "lambda") {
|
|
9201
9275
|
// In Lambda, use function name as cache key since it uses IAM role
|
|
9202
9276
|
const secrets = getSecrets();
|
|
9203
|
-
return `lambda:${secrets.aws.lambdaFunctionName ||
|
|
9277
|
+
return `lambda:${secrets.aws.lambdaFunctionName || "default"}`;
|
|
9204
9278
|
}
|
|
9205
9279
|
if (envVarsCheck?.vars) {
|
|
9206
9280
|
return `${envVarsCheck.vars.accessKeyId}:${envVarsCheck.vars.secretAccessKey}:${envVarsCheck.vars.region}`;
|
|
9207
9281
|
}
|
|
9208
|
-
return
|
|
9282
|
+
return "default";
|
|
9209
9283
|
};
|
|
9210
9284
|
const validateEnvironmentVars = () => {
|
|
9211
9285
|
const secrets = getSecrets();
|
|
@@ -9218,37 +9292,37 @@ const validateEnvironmentVars = () => {
|
|
|
9218
9292
|
// We're in a Lambda environment
|
|
9219
9293
|
return {
|
|
9220
9294
|
isValid: true,
|
|
9221
|
-
source:
|
|
9222
|
-
vars: { accessKeyId:
|
|
9295
|
+
source: "lambda",
|
|
9296
|
+
vars: { accessKeyId: "", secretAccessKey: "", region: "" }, // No need for explicit credentials in Lambda
|
|
9223
9297
|
};
|
|
9224
9298
|
}
|
|
9225
9299
|
else if (areVarsComplete(secrets.aws.myAccessKeyId, secrets.aws.mySecretAccessKey, secrets.aws.myRegion)) {
|
|
9226
9300
|
return {
|
|
9227
9301
|
isValid: true,
|
|
9228
|
-
source:
|
|
9302
|
+
source: "my_prefix",
|
|
9229
9303
|
vars: {
|
|
9230
|
-
accessKeyId: secrets.aws.myAccessKeyId,
|
|
9231
|
-
secretAccessKey: secrets.aws.mySecretAccessKey,
|
|
9232
|
-
region: secrets.aws.myRegion
|
|
9233
|
-
}
|
|
9304
|
+
accessKeyId: secrets.aws.myAccessKeyId ?? "",
|
|
9305
|
+
secretAccessKey: secrets.aws.mySecretAccessKey ?? "",
|
|
9306
|
+
region: secrets.aws.myRegion ?? "",
|
|
9307
|
+
},
|
|
9234
9308
|
};
|
|
9235
9309
|
}
|
|
9236
9310
|
else if (areVarsComplete(secrets.aws.accessKeyId, secrets.aws.secretAccessKey, secrets.aws.region)) {
|
|
9237
9311
|
return {
|
|
9238
9312
|
isValid: true,
|
|
9239
|
-
source:
|
|
9313
|
+
source: "standard",
|
|
9240
9314
|
vars: {
|
|
9241
|
-
accessKeyId: secrets.aws.accessKeyId,
|
|
9242
|
-
secretAccessKey: secrets.aws.secretAccessKey,
|
|
9243
|
-
region: secrets.aws.region
|
|
9244
|
-
}
|
|
9315
|
+
accessKeyId: secrets.aws.accessKeyId ?? "",
|
|
9316
|
+
secretAccessKey: secrets.aws.secretAccessKey ?? "",
|
|
9317
|
+
region: secrets.aws.region ?? "",
|
|
9318
|
+
},
|
|
9245
9319
|
};
|
|
9246
9320
|
}
|
|
9247
9321
|
// If we get here, no complete set of variables was found
|
|
9248
9322
|
return {
|
|
9249
9323
|
isValid: false,
|
|
9250
9324
|
source: null,
|
|
9251
|
-
missingVars: []
|
|
9325
|
+
missingVars: [],
|
|
9252
9326
|
};
|
|
9253
9327
|
};
|
|
9254
9328
|
const initialiseAWSClient = (ClientClass, auth = null) => {
|
|
@@ -9257,48 +9331,52 @@ const initialiseAWSClient = (ClientClass, auth = null) => {
|
|
|
9257
9331
|
const requestTimeout = ClientClass === S3Client ? AWS_S3_TIMEOUT_MS : AWS_LAMBDA_TIMEOUT_MS;
|
|
9258
9332
|
// Case 1: Explicit auth provided
|
|
9259
9333
|
if (auth) {
|
|
9260
|
-
if (typeof auth !==
|
|
9261
|
-
throw new Error(
|
|
9334
|
+
if (typeof auth !== "object" || auth === null) {
|
|
9335
|
+
throw new Error("Auth parameter must be an object");
|
|
9262
9336
|
}
|
|
9263
|
-
const requiredAuthFields = [
|
|
9264
|
-
|
|
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]);
|
|
9265
9343
|
if (missingFields.length > 0) {
|
|
9266
|
-
throw new Error(`Auth object is missing required fields: ${missingFields.join(
|
|
9344
|
+
throw new Error(`Auth object is missing required fields: ${missingFields.join(", ")}`);
|
|
9267
9345
|
}
|
|
9268
9346
|
return new ClientClass({
|
|
9269
9347
|
region: auth.AWS_REGION,
|
|
9270
9348
|
credentials: {
|
|
9271
9349
|
accessKeyId: auth.AWS_ACCESS_KEY_ID,
|
|
9272
|
-
secretAccessKey: auth.AWS_SECRET_ACCESS_KEY
|
|
9350
|
+
secretAccessKey: auth.AWS_SECRET_ACCESS_KEY,
|
|
9273
9351
|
},
|
|
9274
9352
|
requestHandler: {
|
|
9275
|
-
requestTimeout
|
|
9276
|
-
}
|
|
9353
|
+
requestTimeout,
|
|
9354
|
+
},
|
|
9277
9355
|
});
|
|
9278
9356
|
}
|
|
9279
9357
|
// Case 2: Check environment variables
|
|
9280
9358
|
const envVarsCheck = validateEnvironmentVars();
|
|
9281
9359
|
if (!envVarsCheck.isValid) {
|
|
9282
|
-
throw new Error(
|
|
9360
|
+
throw new Error("No valid AWS credentials found in environment variables");
|
|
9283
9361
|
}
|
|
9284
9362
|
// Case 2a: Lambda execution environment
|
|
9285
|
-
if (envVarsCheck.source ===
|
|
9363
|
+
if (envVarsCheck.source === "lambda") {
|
|
9286
9364
|
return new ClientClass({
|
|
9287
9365
|
requestHandler: {
|
|
9288
|
-
requestTimeout
|
|
9289
|
-
}
|
|
9366
|
+
requestTimeout,
|
|
9367
|
+
},
|
|
9290
9368
|
}); // AWS SDK will automatically use Lambda role credentials
|
|
9291
9369
|
}
|
|
9292
9370
|
// Case 2b: MY_ prefixed vars or standard AWS vars
|
|
9293
9371
|
return new ClientClass({
|
|
9294
|
-
region: envVarsCheck.vars
|
|
9372
|
+
region: envVarsCheck.vars?.region ?? "",
|
|
9295
9373
|
credentials: {
|
|
9296
|
-
accessKeyId: envVarsCheck.vars
|
|
9297
|
-
secretAccessKey: envVarsCheck.vars
|
|
9374
|
+
accessKeyId: envVarsCheck.vars?.accessKeyId ?? "",
|
|
9375
|
+
secretAccessKey: envVarsCheck.vars?.secretAccessKey ?? "",
|
|
9298
9376
|
},
|
|
9299
9377
|
requestHandler: {
|
|
9300
|
-
requestTimeout
|
|
9301
|
-
}
|
|
9378
|
+
requestTimeout,
|
|
9379
|
+
},
|
|
9302
9380
|
});
|
|
9303
9381
|
}
|
|
9304
9382
|
catch (error) {
|
|
@@ -9311,7 +9389,9 @@ const initialiseS3Client = (auth = null) => {
|
|
|
9311
9389
|
const envVarsCheck = auth ? null : validateEnvironmentVars();
|
|
9312
9390
|
const cacheKey = generateCacheKey(auth, envVarsCheck);
|
|
9313
9391
|
if (s3ClientCache.has(cacheKey)) {
|
|
9314
|
-
|
|
9392
|
+
const cached = s3ClientCache.get(cacheKey);
|
|
9393
|
+
if (cached)
|
|
9394
|
+
return cached;
|
|
9315
9395
|
}
|
|
9316
9396
|
// Create new client and cache it
|
|
9317
9397
|
const client = initialiseAWSClient(S3Client, auth);
|
|
@@ -9323,7 +9403,9 @@ const initialiseLambdaClient = (auth = null) => {
|
|
|
9323
9403
|
const envVarsCheck = auth ? null : validateEnvironmentVars();
|
|
9324
9404
|
const cacheKey = generateCacheKey(auth, envVarsCheck);
|
|
9325
9405
|
if (lambdaClientCache.has(cacheKey)) {
|
|
9326
|
-
|
|
9406
|
+
const cached = lambdaClientCache.get(cacheKey);
|
|
9407
|
+
if (cached)
|
|
9408
|
+
return cached;
|
|
9327
9409
|
}
|
|
9328
9410
|
// Create new client and cache it
|
|
9329
9411
|
const client = initialiseAWSClient(Lambda, auth);
|
|
@@ -9823,15 +9905,17 @@ const isRetryableS3Error = (error) => {
|
|
|
9823
9905
|
if (error instanceof Error) {
|
|
9824
9906
|
const message = error.message;
|
|
9825
9907
|
// Retry on throttling
|
|
9826
|
-
if (message.includes(
|
|
9908
|
+
if (message.includes("SlowDown") || message.includes("RequestTimeout")) {
|
|
9827
9909
|
return true;
|
|
9828
9910
|
}
|
|
9829
9911
|
// Retry on 5xx server errors
|
|
9830
|
-
if (message.includes(
|
|
9912
|
+
if (message.includes("InternalError") ||
|
|
9913
|
+
message.includes("ServiceUnavailable")) {
|
|
9831
9914
|
return true;
|
|
9832
9915
|
}
|
|
9833
9916
|
// Retry on connection errors
|
|
9834
|
-
if (message.includes(
|
|
9917
|
+
if (message.includes("RequestTimeTooSkewed") ||
|
|
9918
|
+
message.includes("NetworkingError")) {
|
|
9835
9919
|
return true;
|
|
9836
9920
|
}
|
|
9837
9921
|
}
|
|
@@ -9845,10 +9929,11 @@ const generateRandomHash = (length) => {
|
|
|
9845
9929
|
};
|
|
9846
9930
|
function generateDateTimeStamp() {
|
|
9847
9931
|
const now = new Date();
|
|
9848
|
-
return now
|
|
9849
|
-
.
|
|
9850
|
-
.replace(
|
|
9851
|
-
.replace(
|
|
9932
|
+
return now
|
|
9933
|
+
.toISOString()
|
|
9934
|
+
.replace(/T/, "-")
|
|
9935
|
+
.replace(/\..+/, "")
|
|
9936
|
+
.replace(/:/g, "-");
|
|
9852
9937
|
}
|
|
9853
9938
|
const isValidBucketName = (name) => {
|
|
9854
9939
|
const regex = /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/;
|
|
@@ -9879,7 +9964,7 @@ async function uploadFile(s3Client, bucketName, filePath, destinationPath, isPub
|
|
|
9879
9964
|
Bucket: bucketName,
|
|
9880
9965
|
Key: s3Key,
|
|
9881
9966
|
Body: fileContent,
|
|
9882
|
-
ACL: isPublic ?
|
|
9967
|
+
ACL: isPublic ? "public-read" : undefined,
|
|
9883
9968
|
});
|
|
9884
9969
|
await withRetry(() => s3Client.send(command), {
|
|
9885
9970
|
maxRetries: 3,
|
|
@@ -9899,7 +9984,7 @@ async function uploadDirectory(s3Client, bucketName, dirPath, destinationPath, i
|
|
|
9899
9984
|
Bucket: bucketName,
|
|
9900
9985
|
Key: s3Key,
|
|
9901
9986
|
Body: fileContent,
|
|
9902
|
-
ACL: isPublic ?
|
|
9987
|
+
ACL: isPublic ? "public-read" : undefined,
|
|
9903
9988
|
});
|
|
9904
9989
|
await withRetry(() => s3Client.send(command), {
|
|
9905
9990
|
maxRetries: 3,
|
|
@@ -9915,7 +10000,7 @@ async function zipDirectory(sourceDir, outPath) {
|
|
|
9915
10000
|
const zip = new AdmZip();
|
|
9916
10001
|
let fileCount = 0;
|
|
9917
10002
|
try {
|
|
9918
|
-
async function addFilesToZip(currentPath, relativePath =
|
|
10003
|
+
async function addFilesToZip(currentPath, relativePath = "") {
|
|
9919
10004
|
const files = await fs$1.readdir(currentPath, { withFileTypes: true });
|
|
9920
10005
|
for (const file of files) {
|
|
9921
10006
|
const filePath = path__default.join(currentPath, file.name);
|
|
@@ -9958,27 +10043,33 @@ async function downloadFromS3Helper(s3Client, bucketName, s3Path, localPath) {
|
|
|
9958
10043
|
Prefix: s3Path,
|
|
9959
10044
|
ContinuationToken: continuationToken,
|
|
9960
10045
|
});
|
|
9961
|
-
const listResponse = await withRetry(() => s3Client.send(listCommand), {
|
|
10046
|
+
const listResponse = (await withRetry(() => s3Client.send(listCommand), {
|
|
9962
10047
|
maxRetries: 3,
|
|
9963
10048
|
baseDelayMs: 1000,
|
|
9964
10049
|
maxDelayMs: 15000,
|
|
9965
10050
|
retryableErrors: isRetryableS3Error,
|
|
9966
|
-
}, `S3:ListObjects:${bucketName}`);
|
|
10051
|
+
}, `S3:ListObjects:${bucketName}`));
|
|
9967
10052
|
const objects = listResponse.Contents || [];
|
|
9968
10053
|
if (objects.length === 0) {
|
|
9969
10054
|
return { fileCount: 0, folderCount: 0, totalSize: 0, fileList: [] };
|
|
9970
10055
|
}
|
|
9971
|
-
if (objects.length === 1 && objects[0].Key?.endsWith(
|
|
10056
|
+
if (objects.length === 1 && objects[0].Key?.endsWith(".zip")) {
|
|
9972
10057
|
// It's a zip file, download and extract it
|
|
9973
10058
|
const zipPath = path__default.join(localPath, path__default.basename(objects[0].Key));
|
|
9974
10059
|
await downloadFile(s3Client, bucketName, objects[0].Key, zipPath);
|
|
9975
|
-
const { totalFiles, totalFolders, totalSize: extractedSize, fileList: extractedFileList } = await unzipFile(zipPath, localPath);
|
|
10060
|
+
const { totalFiles, totalFolders, totalSize: extractedSize, fileList: extractedFileList, } = await unzipFile(zipPath, localPath);
|
|
9976
10061
|
await fs$1.unlink(zipPath);
|
|
9977
|
-
return {
|
|
10062
|
+
return {
|
|
10063
|
+
fileCount: totalFiles,
|
|
10064
|
+
folderCount: totalFolders,
|
|
10065
|
+
totalSize: extractedSize,
|
|
10066
|
+
fileList: extractedFileList,
|
|
10067
|
+
};
|
|
9978
10068
|
}
|
|
9979
10069
|
else {
|
|
9980
10070
|
// Non-zip files. Download files in batches.
|
|
9981
|
-
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
|
|
9982
10073
|
const batch = objects.slice(i, i + 1000);
|
|
9983
10074
|
await Promise.all(batch.map(async (obj) => {
|
|
9984
10075
|
if (!obj.Key)
|
|
@@ -9994,7 +10085,9 @@ async function downloadFromS3Helper(s3Client, bucketName, s3Path, localPath) {
|
|
|
9994
10085
|
}
|
|
9995
10086
|
// Count folders
|
|
9996
10087
|
const relativePath = path__default.relative(localPath, path__default.dirname(localFilePath));
|
|
9997
|
-
if (relativePath &&
|
|
10088
|
+
if (relativePath &&
|
|
10089
|
+
!relativePath.startsWith("..") &&
|
|
10090
|
+
relativePath !== ".") {
|
|
9998
10091
|
folderCount++;
|
|
9999
10092
|
}
|
|
10000
10093
|
}));
|
|
@@ -10015,7 +10108,7 @@ async function downloadFile(s3Client, bucketName, s3Key, localFilePath) {
|
|
|
10015
10108
|
await withRetry(async () => {
|
|
10016
10109
|
const { Body } = await s3Client.send(getCommand);
|
|
10017
10110
|
if (!Body)
|
|
10018
|
-
throw new Error(
|
|
10111
|
+
throw new Error("No body returned from S3");
|
|
10019
10112
|
const writeStream = createWriteStream(localFilePath);
|
|
10020
10113
|
await pipeline(Body, writeStream);
|
|
10021
10114
|
}, {
|
|
@@ -10071,17 +10164,19 @@ async function emptyBucket(s3Client, bucketName) {
|
|
|
10071
10164
|
Bucket: bucketName,
|
|
10072
10165
|
ContinuationToken: continuationToken,
|
|
10073
10166
|
});
|
|
10074
|
-
const response = await withRetry(() => s3Client.send(listCommand), {
|
|
10167
|
+
const response = (await withRetry(() => s3Client.send(listCommand), {
|
|
10075
10168
|
maxRetries: 3,
|
|
10076
10169
|
baseDelayMs: 1000,
|
|
10077
10170
|
maxDelayMs: 15000,
|
|
10078
10171
|
retryableErrors: isRetryableS3Error,
|
|
10079
|
-
}, `S3:ListObjects:${bucketName}`);
|
|
10172
|
+
}, `S3:ListObjects:${bucketName}`));
|
|
10080
10173
|
const { Contents, IsTruncated, NextContinuationToken } = response;
|
|
10081
10174
|
if (Contents && Contents.length > 0) {
|
|
10082
10175
|
const deleteCommand = new DeleteObjectsCommand({
|
|
10083
10176
|
Bucket: bucketName,
|
|
10084
|
-
Delete: {
|
|
10177
|
+
Delete: {
|
|
10178
|
+
Objects: Contents.filter((c) => c.Key).map((c) => ({ Key: c.Key })),
|
|
10179
|
+
},
|
|
10085
10180
|
});
|
|
10086
10181
|
await withRetry(() => s3Client.send(deleteCommand), {
|
|
10087
10182
|
maxRetries: 3,
|
|
@@ -22989,11 +23084,11 @@ let poolConfig = DEFAULT_POOL_CONFIG;
|
|
|
22989
23084
|
async function loadApolloModules() {
|
|
22990
23085
|
if (typeof window === "undefined" || process.env.AWS_EXECUTION_ENV) {
|
|
22991
23086
|
// Server-side (or Lambda): load the CommonJS‑based implementation.
|
|
22992
|
-
return (await import('./apollo-client.server-
|
|
23087
|
+
return (await import('./apollo-client.server-CbagxkmK.js'));
|
|
22993
23088
|
}
|
|
22994
23089
|
else {
|
|
22995
23090
|
// Client-side: load the ESM‑based implementation.
|
|
22996
|
-
return (await import('./apollo-client.client-
|
|
23091
|
+
return (await import('./apollo-client.client-9wJcufhf.js'));
|
|
22997
23092
|
}
|
|
22998
23093
|
}
|
|
22999
23094
|
/**
|
|
@@ -79020,7 +79115,7 @@ const RETRY_CONFIG = {
|
|
|
79020
79115
|
BASE_DELAY: 2000, // Increased base delay to 2 seconds
|
|
79021
79116
|
MAX_DELAY: 64000,
|
|
79022
79117
|
MAX_RETRIES: 5,
|
|
79023
|
-
MAX_RANDOM_DELAY: 1000
|
|
79118
|
+
MAX_RANDOM_DELAY: 1000,
|
|
79024
79119
|
};
|
|
79025
79120
|
/**
|
|
79026
79121
|
* Determines if an error is related to Google Sheets API quotas or rate limits.
|
|
@@ -79046,10 +79141,10 @@ const RETRY_CONFIG = {
|
|
|
79046
79141
|
*/
|
|
79047
79142
|
function isQuotaError(error) {
|
|
79048
79143
|
const message = error.message.toLowerCase();
|
|
79049
|
-
return message.includes(
|
|
79050
|
-
message.includes(
|
|
79051
|
-
message.includes(
|
|
79052
|
-
message.includes(
|
|
79144
|
+
return (message.includes("quota exceeded") ||
|
|
79145
|
+
message.includes("rate limit") ||
|
|
79146
|
+
message.includes("429") ||
|
|
79147
|
+
message.includes("too many requests"));
|
|
79053
79148
|
}
|
|
79054
79149
|
/**
|
|
79055
79150
|
* Implements exponential backoff retry mechanism for Google Sheets API calls.
|
|
@@ -79108,14 +79203,14 @@ async function withExponentialBackoff(operation) {
|
|
|
79108
79203
|
const exponentialDelay = Math.min(Math.pow(2, retries) * RETRY_CONFIG.BASE_DELAY + randomDelay, RETRY_CONFIG.MAX_DELAY);
|
|
79109
79204
|
logIfDebug(`Quota/Rate limit exceeded. Error: ${error.message}`);
|
|
79110
79205
|
logIfDebug(`Retrying in ${exponentialDelay}ms (attempt ${retries}/${RETRY_CONFIG.MAX_RETRIES})`);
|
|
79111
|
-
await new Promise(resolve => setTimeout(resolve, exponentialDelay));
|
|
79206
|
+
await new Promise((resolve) => setTimeout(resolve, exponentialDelay));
|
|
79112
79207
|
}
|
|
79113
79208
|
}
|
|
79114
79209
|
}
|
|
79115
79210
|
function checkEnvVars() {
|
|
79116
79211
|
const secrets = getSecrets();
|
|
79117
79212
|
if (!secrets.googleSheets.clientEmail || !secrets.googleSheets.privateKey) {
|
|
79118
|
-
throw new Error(
|
|
79213
|
+
throw new Error("GOOGLE_SHEETS_CLIENT_EMAIL or GOOGLE_SHEETS_PRIVATE_KEY is not defined in environment variables.");
|
|
79119
79214
|
}
|
|
79120
79215
|
}
|
|
79121
79216
|
/**
|
|
@@ -79130,16 +79225,16 @@ async function getAuthClient() {
|
|
|
79130
79225
|
const secrets = getSecrets();
|
|
79131
79226
|
const credentials = {
|
|
79132
79227
|
client_email: secrets.googleSheets.clientEmail,
|
|
79133
|
-
private_key: secrets.googleSheets.privateKey?.replace(/\\n/g,
|
|
79228
|
+
private_key: secrets.googleSheets.privateKey?.replace(/\\n/g, "\n"),
|
|
79134
79229
|
};
|
|
79135
79230
|
const auth = new GoogleAuth({
|
|
79136
79231
|
credentials,
|
|
79137
|
-
scopes: [
|
|
79232
|
+
scopes: ["https://www.googleapis.com/auth/spreadsheets"],
|
|
79138
79233
|
});
|
|
79139
79234
|
return auth.getClient();
|
|
79140
79235
|
}
|
|
79141
79236
|
catch (error) {
|
|
79142
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
79237
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
79143
79238
|
logIfDebug(`Error initializing Google Sheets auth: ${errorMessage}`);
|
|
79144
79239
|
throw new Error(`Failed to initialize Google Sheets auth: ${errorMessage}`);
|
|
79145
79240
|
}
|
|
@@ -79151,7 +79246,7 @@ async function getSheetsClient() {
|
|
|
79151
79246
|
const auth = await getAuthClient();
|
|
79152
79247
|
return new buildExports.sheets_v4.Sheets({
|
|
79153
79248
|
auth,
|
|
79154
|
-
timeout: GOOGLE_SHEETS_TIMEOUT_MS
|
|
79249
|
+
timeout: GOOGLE_SHEETS_TIMEOUT_MS,
|
|
79155
79250
|
});
|
|
79156
79251
|
}
|
|
79157
79252
|
/**
|
|
@@ -79184,7 +79279,7 @@ function escapeSheetName(sheetName) {
|
|
|
79184
79279
|
* @param startColumn Optional starting column (defaults to 'A')
|
|
79185
79280
|
* @returns Promise resolving to response with success status and row details
|
|
79186
79281
|
*/
|
|
79187
|
-
async function addRow(config, values, startColumn =
|
|
79282
|
+
async function addRow(config, values, startColumn = "A") {
|
|
79188
79283
|
try {
|
|
79189
79284
|
const sheets = await getSheetsClient();
|
|
79190
79285
|
const escapedSheetName = escapeSheetName(config.sheetName);
|
|
@@ -79192,7 +79287,7 @@ async function addRow(config, values, startColumn = 'A') {
|
|
|
79192
79287
|
const response = await withExponentialBackoff(() => sheets.spreadsheets.values.append({
|
|
79193
79288
|
spreadsheetId: config.spreadsheetId,
|
|
79194
79289
|
range,
|
|
79195
|
-
valueInputOption:
|
|
79290
|
+
valueInputOption: "USER_ENTERED",
|
|
79196
79291
|
requestBody: {
|
|
79197
79292
|
values: [values],
|
|
79198
79293
|
},
|
|
@@ -79200,9 +79295,9 @@ async function addRow(config, values, startColumn = 'A') {
|
|
|
79200
79295
|
//logIfDebug(`Added row to sheet ${config.sheetName}: ${values.join(', ')}`);
|
|
79201
79296
|
const rowNumber = response.data.updates?.updatedRange
|
|
79202
79297
|
? parseInt(response.data.updates.updatedRange
|
|
79203
|
-
.split(
|
|
79204
|
-
.split(
|
|
79205
|
-
.replace(/[^0-9]/g,
|
|
79298
|
+
.split("!")[1]
|
|
79299
|
+
.split(":")[0]
|
|
79300
|
+
.replace(/[^0-9]/g, ""))
|
|
79206
79301
|
: 0;
|
|
79207
79302
|
return {
|
|
79208
79303
|
success: true,
|
|
@@ -79213,7 +79308,7 @@ async function addRow(config, values, startColumn = 'A') {
|
|
|
79213
79308
|
};
|
|
79214
79309
|
}
|
|
79215
79310
|
catch (error) {
|
|
79216
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
79311
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
79217
79312
|
logIfDebug(`Error adding row to sheet: ${errorMessage}`);
|
|
79218
79313
|
return {
|
|
79219
79314
|
success: false,
|
|
@@ -79255,7 +79350,7 @@ async function addSheet(spreadsheetId, sheetTitle) {
|
|
|
79255
79350
|
};
|
|
79256
79351
|
}
|
|
79257
79352
|
catch (error) {
|
|
79258
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
79353
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
79259
79354
|
logIfDebug(`Error adding sheet: ${errorMessage}`);
|
|
79260
79355
|
return {
|
|
79261
79356
|
success: false,
|
|
@@ -79279,7 +79374,7 @@ async function writeCell(config, cell, value) {
|
|
|
79279
79374
|
await withExponentialBackoff(() => sheets.spreadsheets.values.update({
|
|
79280
79375
|
spreadsheetId: config.spreadsheetId,
|
|
79281
79376
|
range,
|
|
79282
|
-
valueInputOption:
|
|
79377
|
+
valueInputOption: "USER_ENTERED",
|
|
79283
79378
|
requestBody: {
|
|
79284
79379
|
values: [[value]],
|
|
79285
79380
|
},
|
|
@@ -79293,7 +79388,7 @@ async function writeCell(config, cell, value) {
|
|
|
79293
79388
|
};
|
|
79294
79389
|
}
|
|
79295
79390
|
catch (error) {
|
|
79296
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
79391
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
79297
79392
|
logIfDebug(`Error writing to cell: ${errorMessage}`);
|
|
79298
79393
|
return {
|
|
79299
79394
|
success: false,
|
|
@@ -79317,8 +79412,11 @@ async function getCell(config, cell) {
|
|
|
79317
79412
|
spreadsheetId: config.spreadsheetId,
|
|
79318
79413
|
range,
|
|
79319
79414
|
}));
|
|
79320
|
-
const
|
|
79321
|
-
|
|
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)}`);
|
|
79322
79420
|
return {
|
|
79323
79421
|
success: true,
|
|
79324
79422
|
data: {
|
|
@@ -79327,7 +79425,7 @@ async function getCell(config, cell) {
|
|
|
79327
79425
|
};
|
|
79328
79426
|
}
|
|
79329
79427
|
catch (error) {
|
|
79330
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
79428
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
79331
79429
|
logIfDebug(`Error reading from cell: ${errorMessage}`);
|
|
79332
79430
|
return {
|
|
79333
79431
|
success: false,
|
|
@@ -79360,10 +79458,11 @@ function convertA1ToRowCol(a1Notation) {
|
|
|
79360
79458
|
* @returns Padded array of arrays
|
|
79361
79459
|
*/
|
|
79362
79460
|
function padArrays(arrays) {
|
|
79363
|
-
const maxLength = Math.max(...arrays.map(arr => arr.length));
|
|
79364
|
-
return arrays.map(arr => {
|
|
79461
|
+
const maxLength = Math.max(...arrays.map((arr) => arr.length));
|
|
79462
|
+
return arrays.map((arr) => {
|
|
79365
79463
|
if (arr.length < maxLength) {
|
|
79366
|
-
|
|
79464
|
+
const padding = Array(maxLength - arr.length).fill("");
|
|
79465
|
+
return [...arr, ...padding];
|
|
79367
79466
|
}
|
|
79368
79467
|
return arr;
|
|
79369
79468
|
});
|
|
@@ -79375,10 +79474,10 @@ function padArrays(arrays) {
|
|
|
79375
79474
|
* @param startCell Optional starting cell in A1 notation (defaults to 'A1')
|
|
79376
79475
|
* @returns Promise resolving to response with success status and update details
|
|
79377
79476
|
*/
|
|
79378
|
-
async function writeArray(config, data, startCell =
|
|
79477
|
+
async function writeArray(config, data, startCell = "A1") {
|
|
79379
79478
|
try {
|
|
79380
79479
|
if (!data.length) {
|
|
79381
|
-
throw new Error(
|
|
79480
|
+
throw new Error("Data array is empty");
|
|
79382
79481
|
}
|
|
79383
79482
|
const sheets = await getSheetsClient();
|
|
79384
79483
|
const { row: startRow, column: startCol } = convertA1ToRowCol(startCell);
|
|
@@ -79394,7 +79493,7 @@ async function writeArray(config, data, startCell = 'A1') {
|
|
|
79394
79493
|
const response = await withExponentialBackoff(() => sheets.spreadsheets.values.update({
|
|
79395
79494
|
spreadsheetId: config.spreadsheetId,
|
|
79396
79495
|
range,
|
|
79397
|
-
valueInputOption:
|
|
79496
|
+
valueInputOption: "USER_ENTERED",
|
|
79398
79497
|
requestBody: {
|
|
79399
79498
|
values: paddedData,
|
|
79400
79499
|
},
|
|
@@ -79410,7 +79509,7 @@ async function writeArray(config, data, startCell = 'A1') {
|
|
|
79410
79509
|
};
|
|
79411
79510
|
}
|
|
79412
79511
|
catch (error) {
|
|
79413
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
79512
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
79414
79513
|
logIfDebug(`Error writing array to sheet: ${errorMessage}`);
|
|
79415
79514
|
return {
|
|
79416
79515
|
success: false,
|
|
@@ -81517,4 +81616,4 @@ const lumic = {
|
|
|
81517
81616
|
};
|
|
81518
81617
|
|
|
81519
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 };
|
|
81520
|
-
//# sourceMappingURL=index-
|
|
81619
|
+
//# sourceMappingURL=index-EMQ1uJMh.js.map
|