@adaptic/lumic-utils 1.0.24 → 1.0.25
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-CyxU1hTu.js → apollo-client.client-D6jG5B1n.js} +3 -3
- package/dist/{apollo-client.client-CyxU1hTu.js.map → apollo-client.client-D6jG5B1n.js.map} +1 -1
- package/dist/{apollo-client.client-9wJcufhf.js → apollo-client.client-DLW-p46Y.js} +4 -4
- package/dist/{apollo-client.client-9wJcufhf.js.map → apollo-client.client-DLW-p46Y.js.map} +1 -1
- package/dist/{apollo-client.server-CbagxkmK.js → apollo-client.server-CXLiLmSz.js} +3 -3
- package/dist/{apollo-client.server-CbagxkmK.js.map → apollo-client.server-CXLiLmSz.js.map} +1 -1
- package/dist/{apollo-client.server-DB3jLbBx.js → apollo-client.server-Cx30LaNh.js} +3 -3
- package/dist/{apollo-client.server-DB3jLbBx.js.map → apollo-client.server-Cx30LaNh.js.map} +1 -1
- package/dist/{index-EMQ1uJMh.js → index-Bvc61lYS.js} +348 -381
- package/dist/{index-EMQ1uJMh.js.map → index-Bvc61lYS.js.map} +1 -1
- package/dist/{index-KzQOh2uu.js → index-ChygDqeG.js} +348 -381
- package/dist/{index-KzQOh2uu.js.map → index-ChygDqeG.js.map} +1 -1
- package/dist/{index-B4yVKGNR.js → index-CqnZ-XiI.js} +2 -2
- package/dist/{index-B4yVKGNR.js.map → index-CqnZ-XiI.js.map} +1 -1
- package/dist/{index-CL79JTWc.js → index-D2TPDKw0.js} +2 -2
- package/dist/{index-CL79JTWc.js.map → index-D2TPDKw0.js.map} +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/test.cjs +17 -21
- package/dist/test.cjs.map +1 -1
- package/dist/test.mjs +17 -21
- 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 +8 -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 +26 -10
- package/dist/types/utils/aws-initialise.d.ts +4 -4
- package/dist/types/utils/retry.d.ts +8 -0
- package/package.json +2 -2
|
@@ -551,19 +551,10 @@ 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
|
|
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.
|
|
554
|
+
* Default model tiers per provider for use with LLM_MODEL_MINI/NORMAL/ADVANCED env vars
|
|
564
555
|
*/
|
|
565
556
|
const PROVIDER_DEFAULT_MODELS = {
|
|
566
|
-
openai: { mini: 'gpt-5.4-nano', normal: 'gpt-5.4-mini', advanced: 'gpt-5.
|
|
557
|
+
openai: { mini: 'gpt-5.4-nano', normal: 'gpt-5.4-mini', advanced: 'gpt-5.5' },
|
|
567
558
|
anthropic: { mini: 'claude-haiku-4-5', normal: 'claude-sonnet-4-6', advanced: 'claude-opus-4-7' },
|
|
568
559
|
deepseek: { mini: 'deepseek-v4-flash', normal: 'deepseek-v4-flash', advanced: 'deepseek-v4-pro' },
|
|
569
560
|
kimi: { mini: 'kimi-k2-0905-preview', normal: 'kimi-k2.5', advanced: 'kimi-k2.6' },
|
|
@@ -1517,13 +1508,11 @@ let openai;
|
|
|
1517
1508
|
function initializeOpenAI(apiKey) {
|
|
1518
1509
|
const key = apiKey || getSecrets().openai.apiKey;
|
|
1519
1510
|
if (!key) {
|
|
1520
|
-
throw new Error(
|
|
1511
|
+
throw new Error('OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set');
|
|
1521
1512
|
}
|
|
1522
1513
|
return new OpenAI({
|
|
1523
1514
|
apiKey: key,
|
|
1524
|
-
defaultHeaders: {
|
|
1525
|
-
"User-Agent": "My Server-side Application (compatible; OpenAI API Client)",
|
|
1526
|
-
},
|
|
1515
|
+
defaultHeaders: { 'User-Agent': 'My Server-side Application (compatible; OpenAI API Client)' },
|
|
1527
1516
|
});
|
|
1528
1517
|
}
|
|
1529
1518
|
/**
|
|
@@ -1541,7 +1530,9 @@ function initializeOpenAI(apiKey) {
|
|
|
1541
1530
|
*/
|
|
1542
1531
|
async function fixJsonWithAI(jsonStr, options) {
|
|
1543
1532
|
// Backward compatibility: handle string apiKey as first positional param
|
|
1544
|
-
const opts = typeof options ===
|
|
1533
|
+
const opts = typeof options === 'string'
|
|
1534
|
+
? { apiKey: options }
|
|
1535
|
+
: (options ?? {});
|
|
1545
1536
|
const apiKey = opts.apiKey;
|
|
1546
1537
|
const maxDepth = opts.maxDepth ?? 2;
|
|
1547
1538
|
const depth = opts._depth ?? 0;
|
|
@@ -1554,18 +1545,18 @@ async function fixJsonWithAI(jsonStr, options) {
|
|
|
1554
1545
|
openai = initializeOpenAI(apiKey);
|
|
1555
1546
|
}
|
|
1556
1547
|
const completion = await openai.chat.completions.create({
|
|
1557
|
-
model:
|
|
1548
|
+
model: 'gpt-5-mini',
|
|
1558
1549
|
messages: [
|
|
1559
1550
|
{
|
|
1560
|
-
role:
|
|
1561
|
-
content:
|
|
1551
|
+
role: 'system',
|
|
1552
|
+
content: 'You are a JSON fixer. Return only valid JSON without any additional text or explanation.'
|
|
1562
1553
|
},
|
|
1563
1554
|
{
|
|
1564
|
-
role:
|
|
1565
|
-
content: `Fix this broken JSON:\n${jsonStr}
|
|
1566
|
-
}
|
|
1555
|
+
role: 'user',
|
|
1556
|
+
content: `Fix this broken JSON:\n${jsonStr}`
|
|
1557
|
+
}
|
|
1567
1558
|
],
|
|
1568
|
-
response_format: { type:
|
|
1559
|
+
response_format: { type: 'json_object' }
|
|
1569
1560
|
});
|
|
1570
1561
|
const fixedJson = completion.choices[0]?.message?.content;
|
|
1571
1562
|
if (fixedJson && isValidJson(fixedJson)) {
|
|
@@ -1579,32 +1570,23 @@ async function fixJsonWithAI(jsonStr, options) {
|
|
|
1579
1570
|
_depth: depth + 1,
|
|
1580
1571
|
});
|
|
1581
1572
|
}
|
|
1582
|
-
throw new JsonParseError(
|
|
1573
|
+
throw new JsonParseError('Failed to fix JSON with AI: returned invalid JSON');
|
|
1583
1574
|
}
|
|
1584
1575
|
catch (err) {
|
|
1585
1576
|
// Re-throw JsonParseError as-is (e.g. max depth exceeded)
|
|
1586
1577
|
if (err instanceof JsonParseError) {
|
|
1587
1578
|
throw err;
|
|
1588
1579
|
}
|
|
1589
|
-
const errorMessage = err instanceof Error ? err.message :
|
|
1580
|
+
const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
|
|
1590
1581
|
throw new JsonParseError(`Error fixing JSON with AI: ${errorMessage}`);
|
|
1591
1582
|
}
|
|
1592
1583
|
}
|
|
1593
1584
|
|
|
1594
1585
|
// llm-utils.ts
|
|
1595
1586
|
function normalizeModelName(model) {
|
|
1596
|
-
return model.replace(/_/g,
|
|
1597
|
-
}
|
|
1598
|
-
const CODE_BLOCK_TYPES = [
|
|
1599
|
-
"javascript",
|
|
1600
|
-
"js",
|
|
1601
|
-
"graphql",
|
|
1602
|
-
"json",
|
|
1603
|
-
"typescript",
|
|
1604
|
-
"python",
|
|
1605
|
-
"markdown",
|
|
1606
|
-
"yaml",
|
|
1607
|
-
];
|
|
1587
|
+
return model.replace(/_/g, '-').toLowerCase();
|
|
1588
|
+
}
|
|
1589
|
+
const CODE_BLOCK_TYPES = ['javascript', 'js', 'graphql', 'json', 'typescript', 'python', 'markdown', 'yaml'];
|
|
1608
1590
|
/**
|
|
1609
1591
|
* Tries to parse JSON from the given content string according to the specified
|
|
1610
1592
|
* response format. If the response format is 'json' or a JSON schema object, it
|
|
@@ -1628,9 +1610,7 @@ async function parseResponse(content, responseFormat, options = {}) {
|
|
|
1628
1610
|
if (recursionDepth > maxRetries) {
|
|
1629
1611
|
throw new Error(`Maximum recursion depth (${maxRetries}) exceeded while parsing JSON. AI fixing may have returned broken JSON.`);
|
|
1630
1612
|
}
|
|
1631
|
-
if (responseFormat ===
|
|
1632
|
-
(typeof responseFormat === "object" &&
|
|
1633
|
-
responseFormat?.type === "json_schema")) {
|
|
1613
|
+
if (responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat?.type === 'json_schema')) {
|
|
1634
1614
|
let cleanedContent = content.trim();
|
|
1635
1615
|
let detectedType = null;
|
|
1636
1616
|
// Remove code block markers if present
|
|
@@ -1641,8 +1621,8 @@ async function parseResponse(content, responseFormat, options = {}) {
|
|
|
1641
1621
|
}
|
|
1642
1622
|
return false;
|
|
1643
1623
|
});
|
|
1644
|
-
if (startsWithCodeBlock && cleanedContent.endsWith(
|
|
1645
|
-
const firstLineEndIndex = cleanedContent.indexOf(
|
|
1624
|
+
if (startsWithCodeBlock && cleanedContent.endsWith('```')) {
|
|
1625
|
+
const firstLineEndIndex = cleanedContent.indexOf('\n');
|
|
1646
1626
|
if (firstLineEndIndex !== -1) {
|
|
1647
1627
|
cleanedContent = cleanedContent.slice(firstLineEndIndex + 1, -3).trim();
|
|
1648
1628
|
getLumicLogger().info(`Code block for type ${detectedType} detected and removed. Cleaned content: ${cleanedContent}`);
|
|
@@ -1675,7 +1655,7 @@ async function parseResponse(content, responseFormat, options = {}) {
|
|
|
1675
1655
|
},
|
|
1676
1656
|
// Strategy 3: Remove leading/trailing text and try parsing
|
|
1677
1657
|
() => {
|
|
1678
|
-
const jsonMatch = cleanedContent.replace(/^[^{[]*|[^}\]]*$/g,
|
|
1658
|
+
const jsonMatch = cleanedContent.replace(/^[^{[]*|[^}\]]*$/g, '');
|
|
1679
1659
|
try {
|
|
1680
1660
|
return JSON.parse(jsonMatch);
|
|
1681
1661
|
}
|
|
@@ -1697,7 +1677,7 @@ async function parseResponse(content, responseFormat, options = {}) {
|
|
|
1697
1677
|
}
|
|
1698
1678
|
// Strategy 5: Use AI fixing (only if explicitly enabled)
|
|
1699
1679
|
if (enableAiFix) {
|
|
1700
|
-
getLumicLogger().warn(
|
|
1680
|
+
getLumicLogger().warn('Using AI-powered JSON fixing. This adds cost and latency. Consider improving JSON generation instead.');
|
|
1701
1681
|
try {
|
|
1702
1682
|
const aiFixedJson = await fixJsonWithAI(cleanedContent);
|
|
1703
1683
|
if (aiFixedJson !== null) {
|
|
@@ -1710,7 +1690,7 @@ async function parseResponse(content, responseFormat, options = {}) {
|
|
|
1710
1690
|
catch {
|
|
1711
1691
|
// AI returned something that looks like JSON but isn't valid
|
|
1712
1692
|
// Increment recursion depth and try parsing the AI response
|
|
1713
|
-
getLumicLogger().warn(
|
|
1693
|
+
getLumicLogger().warn('AI fixing returned invalid JSON, attempting recursive parse...');
|
|
1714
1694
|
return parseResponse(JSON.stringify(aiFixedJson), responseFormat, {
|
|
1715
1695
|
...options,
|
|
1716
1696
|
_recursionDepth: recursionDepth + 1,
|
|
@@ -1719,14 +1699,14 @@ async function parseResponse(content, responseFormat, options = {}) {
|
|
|
1719
1699
|
}
|
|
1720
1700
|
}
|
|
1721
1701
|
catch (error) {
|
|
1722
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
1702
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
1723
1703
|
getLumicLogger().error(`AI JSON fixing failed: ${errorMessage}`);
|
|
1724
1704
|
// Continue to final error below
|
|
1725
1705
|
}
|
|
1726
1706
|
}
|
|
1727
1707
|
// If all strategies fail, throw an error
|
|
1728
1708
|
getLumicLogger().error(`Failed to parse JSON from content: ${cleanedContent}. Original JSON: ${content}`);
|
|
1729
|
-
throw new Error(
|
|
1709
|
+
throw new Error('Unable to parse JSON response');
|
|
1730
1710
|
}
|
|
1731
1711
|
else {
|
|
1732
1712
|
return content;
|
|
@@ -2003,11 +1983,19 @@ const DEFAULT_RETRY_CONFIG = {
|
|
|
2003
1983
|
* @throws The last error encountered if all retry attempts fail or if a non-retryable error occurs
|
|
2004
1984
|
*/
|
|
2005
1985
|
async function withRetry(fn, config = {}, label = 'unknown') {
|
|
2006
|
-
const { maxRetries, baseDelayMs, maxDelayMs, retryableErrors, circuitBreaker } = {
|
|
1986
|
+
const { maxRetries, baseDelayMs, maxDelayMs, retryableErrors, circuitBreaker, signal } = {
|
|
2007
1987
|
...DEFAULT_RETRY_CONFIG,
|
|
2008
1988
|
...config,
|
|
2009
1989
|
};
|
|
2010
1990
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1991
|
+
// Short-circuit on cancellation BEFORE starting a fresh attempt so
|
|
1992
|
+
// the caller does not pay the cost of issuing a request whose result
|
|
1993
|
+
// they have already discarded.
|
|
1994
|
+
if (signal?.aborted) {
|
|
1995
|
+
throw signal.reason instanceof Error
|
|
1996
|
+
? signal.reason
|
|
1997
|
+
: new Error('Aborted');
|
|
1998
|
+
}
|
|
2011
1999
|
try {
|
|
2012
2000
|
// If a circuit breaker is provided, execute through it
|
|
2013
2001
|
const result = circuitBreaker
|
|
@@ -2020,6 +2008,13 @@ async function withRetry(fn, config = {}, label = 'unknown') {
|
|
|
2020
2008
|
if (error instanceof CircuitBreakerOpenError) {
|
|
2021
2009
|
throw error;
|
|
2022
2010
|
}
|
|
2011
|
+
// Do not retry on cooperative cancellation — the caller has
|
|
2012
|
+
// explicitly asked us to stop.
|
|
2013
|
+
if (signal?.aborted) {
|
|
2014
|
+
throw signal.reason instanceof Error
|
|
2015
|
+
? signal.reason
|
|
2016
|
+
: new Error('Aborted');
|
|
2017
|
+
}
|
|
2023
2018
|
// Don't retry if we've exhausted all attempts
|
|
2024
2019
|
if (attempt === maxRetries) {
|
|
2025
2020
|
throw error;
|
|
@@ -2033,7 +2028,26 @@ async function withRetry(fn, config = {}, label = 'unknown') {
|
|
|
2033
2028
|
const jitter = Math.random() * 1000;
|
|
2034
2029
|
const delay = Math.min(exponentialDelay + jitter, maxDelayMs);
|
|
2035
2030
|
getLumicLogger().warn(`[${label}] Attempt ${attempt + 1} failed, retrying in ${Math.round(delay)}ms`);
|
|
2036
|
-
|
|
2031
|
+
// Sleep but bail early if signal aborts mid-backoff.
|
|
2032
|
+
await new Promise((resolve, reject) => {
|
|
2033
|
+
const timer = setTimeout(() => {
|
|
2034
|
+
if (signal)
|
|
2035
|
+
signal.removeEventListener('abort', onAbort);
|
|
2036
|
+
resolve();
|
|
2037
|
+
}, delay);
|
|
2038
|
+
const onAbort = () => {
|
|
2039
|
+
clearTimeout(timer);
|
|
2040
|
+
reject(signal?.reason instanceof Error ? signal.reason : new Error('Aborted'));
|
|
2041
|
+
};
|
|
2042
|
+
if (signal) {
|
|
2043
|
+
if (signal.aborted) {
|
|
2044
|
+
clearTimeout(timer);
|
|
2045
|
+
reject(signal.reason instanceof Error ? signal.reason : new Error('Aborted'));
|
|
2046
|
+
return;
|
|
2047
|
+
}
|
|
2048
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
2049
|
+
}
|
|
2050
|
+
});
|
|
2037
2051
|
}
|
|
2038
2052
|
}
|
|
2039
2053
|
// This should never be reached, but TypeScript needs it for type safety
|
|
@@ -2320,15 +2334,13 @@ const isRetryableLLMError = (error) => {
|
|
|
2320
2334
|
if (error instanceof Error) {
|
|
2321
2335
|
const message = error.message;
|
|
2322
2336
|
// Retry on rate limits (429)
|
|
2323
|
-
if (message.includes(
|
|
2324
|
-
message.includes("rate limit") ||
|
|
2325
|
-
message.includes("Rate limit")) {
|
|
2337
|
+
if (message.includes('429') || message.includes('rate limit') || message.includes('Rate limit')) {
|
|
2326
2338
|
return true;
|
|
2327
2339
|
}
|
|
2328
2340
|
// Retry on transient body-corruption 400s. Match the exact OpenAI error
|
|
2329
2341
|
// string to avoid retrying genuine client-side validation 400s (which
|
|
2330
2342
|
// would re-fail forever and waste retry budget).
|
|
2331
|
-
if (message.includes(
|
|
2343
|
+
if (message.includes('could not parse the JSON body of your request')) {
|
|
2332
2344
|
return true;
|
|
2333
2345
|
}
|
|
2334
2346
|
}
|
|
@@ -2397,26 +2409,23 @@ function isReasoningModel(model) {
|
|
|
2397
2409
|
* @throws Error if the response format is invalid or incompatible.
|
|
2398
2410
|
*/
|
|
2399
2411
|
function getResponseFormatOption(responseFormat, normalizedModel) {
|
|
2400
|
-
if (responseFormat ===
|
|
2401
|
-
return { type:
|
|
2412
|
+
if (responseFormat === 'text') {
|
|
2413
|
+
return { type: 'text' };
|
|
2402
2414
|
}
|
|
2403
|
-
if (responseFormat ===
|
|
2404
|
-
return supportsJsonMode(normalizedModel)
|
|
2405
|
-
? { type: "json_object" }
|
|
2406
|
-
: { type: "text" };
|
|
2415
|
+
if (responseFormat === 'json') {
|
|
2416
|
+
return supportsJsonMode(normalizedModel) ? { type: 'json_object' } : { type: 'text' };
|
|
2407
2417
|
}
|
|
2408
|
-
if (typeof responseFormat ===
|
|
2409
|
-
responseFormat.type === "json_schema") {
|
|
2418
|
+
if (typeof responseFormat === 'object' && responseFormat.type === 'json_schema') {
|
|
2410
2419
|
if (!isStructuredOutputCompatibleModel(normalizedModel)) {
|
|
2411
2420
|
throw new Error(`${normalizedModel} is not compatible with structured outputs / json object.`);
|
|
2412
2421
|
}
|
|
2413
2422
|
return {
|
|
2414
|
-
type:
|
|
2423
|
+
type: 'json_schema',
|
|
2415
2424
|
json_schema: {
|
|
2416
|
-
type:
|
|
2425
|
+
type: 'object',
|
|
2417
2426
|
properties: responseFormat.schema.properties,
|
|
2418
2427
|
required: responseFormat.schema.required || [],
|
|
2419
|
-
name:
|
|
2428
|
+
name: 'Response',
|
|
2420
2429
|
},
|
|
2421
2430
|
};
|
|
2422
2431
|
}
|
|
@@ -2475,7 +2484,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2475
2484
|
const normalizedModel = normalizeModelName(options.model || DEFAULT_MODEL);
|
|
2476
2485
|
const apiKey = options.apiKey || getSecrets().openai.apiKey;
|
|
2477
2486
|
if (!apiKey) {
|
|
2478
|
-
throw new Error(
|
|
2487
|
+
throw new Error('OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set');
|
|
2479
2488
|
}
|
|
2480
2489
|
const openai = new OpenAI({
|
|
2481
2490
|
apiKey: apiKey,
|
|
@@ -2485,7 +2494,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2485
2494
|
// Add developer prompt if present
|
|
2486
2495
|
if (options.developerPrompt && supportsDeveloperPrompt(normalizedModel)) {
|
|
2487
2496
|
messages.push({
|
|
2488
|
-
role:
|
|
2497
|
+
role: 'developer',
|
|
2489
2498
|
content: options.developerPrompt,
|
|
2490
2499
|
});
|
|
2491
2500
|
}
|
|
@@ -2494,15 +2503,15 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2494
2503
|
messages.push(...options.context);
|
|
2495
2504
|
}
|
|
2496
2505
|
// Add user content
|
|
2497
|
-
if (typeof content ===
|
|
2506
|
+
if (typeof content === 'string') {
|
|
2498
2507
|
messages.push({
|
|
2499
|
-
role:
|
|
2508
|
+
role: 'user',
|
|
2500
2509
|
content,
|
|
2501
2510
|
});
|
|
2502
2511
|
}
|
|
2503
2512
|
else {
|
|
2504
2513
|
messages.push({
|
|
2505
|
-
role:
|
|
2514
|
+
role: 'user',
|
|
2506
2515
|
content,
|
|
2507
2516
|
});
|
|
2508
2517
|
}
|
|
@@ -2516,8 +2525,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2516
2525
|
queryOptions.tools = options.tools;
|
|
2517
2526
|
}
|
|
2518
2527
|
// Only include temperature if the model supports it
|
|
2519
|
-
if (options.temperature !== undefined &&
|
|
2520
|
-
supportsTemperature(normalizedModel)) {
|
|
2528
|
+
if (options.temperature !== undefined && supportsTemperature(normalizedModel)) {
|
|
2521
2529
|
queryOptions.temperature = options.temperature;
|
|
2522
2530
|
}
|
|
2523
2531
|
// Only include max_completion_tokens when specified
|
|
@@ -2527,16 +2535,25 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2527
2535
|
// Only set response_format when it's not the default 'text' type
|
|
2528
2536
|
// (sending response_format: {type: 'text'} is redundant and may cause issues)
|
|
2529
2537
|
const responseFormatOption = getResponseFormatOption(responseFormat, normalizedModel);
|
|
2530
|
-
if (responseFormatOption.type !==
|
|
2538
|
+
if (responseFormatOption.type !== 'text') {
|
|
2531
2539
|
queryOptions.response_format = responseFormatOption;
|
|
2532
2540
|
}
|
|
2533
2541
|
let completion;
|
|
2534
2542
|
try {
|
|
2535
|
-
completion = await withRetry(
|
|
2543
|
+
completion = await withRetry(
|
|
2544
|
+
// Pass the AbortSignal into the OpenAI SDK request-options object.
|
|
2545
|
+
// The OpenAI SDK honours `signal` to cancel the in-flight fetch
|
|
2546
|
+
// immediately when it fires — this is what makes caller-driven
|
|
2547
|
+
// cancel-and-restart (e.g. signal-monitoring's materiality
|
|
2548
|
+
// re-trigger path in the engine) actually release the HTTP
|
|
2549
|
+
// connection and response-body buffer rather than just discarding
|
|
2550
|
+
// the resolved value at the engine boundary.
|
|
2551
|
+
() => openai.chat.completions.create(queryOptions, options.signal ? { signal: options.signal } : undefined), {
|
|
2536
2552
|
maxRetries: 3,
|
|
2537
2553
|
baseDelayMs: 2000,
|
|
2538
2554
|
maxDelayMs: 30000,
|
|
2539
2555
|
retryableErrors: isRetryableLLMError,
|
|
2556
|
+
signal: options.signal,
|
|
2540
2557
|
}, `OpenAI:${normalizedModel}`);
|
|
2541
2558
|
}
|
|
2542
2559
|
catch (error) {
|
|
@@ -2548,19 +2565,15 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2548
2565
|
// for production prompts containing sensitive context.
|
|
2549
2566
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2550
2567
|
const totalContentChars = messages.reduce((sum, msg) => {
|
|
2551
|
-
if (typeof msg.content ===
|
|
2568
|
+
if (typeof msg.content === 'string')
|
|
2552
2569
|
return sum + msg.content.length;
|
|
2553
2570
|
if (Array.isArray(msg.content)) {
|
|
2554
|
-
return
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
return s + part.text.length;
|
|
2561
|
-
}
|
|
2562
|
-
return s;
|
|
2563
|
-
}, 0));
|
|
2571
|
+
return sum + msg.content.reduce((s, part) => {
|
|
2572
|
+
if (typeof part === 'object' && part !== null && 'text' in part && typeof part.text === 'string') {
|
|
2573
|
+
return s + part.text.length;
|
|
2574
|
+
}
|
|
2575
|
+
return s;
|
|
2576
|
+
}, 0);
|
|
2564
2577
|
}
|
|
2565
2578
|
return sum;
|
|
2566
2579
|
}, 0);
|
|
@@ -2587,7 +2600,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2587
2600
|
const cachedTokens = completion.usage?.prompt_tokens_details?.cached_tokens ?? 0;
|
|
2588
2601
|
const response = {
|
|
2589
2602
|
id: completion.id,
|
|
2590
|
-
content: completion.choices[0]?.message?.content ||
|
|
2603
|
+
content: completion.choices[0]?.message?.content || '',
|
|
2591
2604
|
tool_calls: completion.choices[0]?.message?.tool_calls,
|
|
2592
2605
|
usage: {
|
|
2593
2606
|
prompt_tokens: completion.usage?.prompt_tokens ?? 0,
|
|
@@ -2597,7 +2610,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2597
2610
|
},
|
|
2598
2611
|
system_fingerprint: completion.system_fingerprint,
|
|
2599
2612
|
service_tier: options.service_tier,
|
|
2600
|
-
provider:
|
|
2613
|
+
provider: 'openai',
|
|
2601
2614
|
model: normalizedModel,
|
|
2602
2615
|
};
|
|
2603
2616
|
return response;
|
|
@@ -2610,7 +2623,7 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
|
|
|
2610
2623
|
* @param options The options for the LLM call. Defaults to an empty object.
|
|
2611
2624
|
* @return A promise that resolves to the response from the LLM.
|
|
2612
2625
|
*/
|
|
2613
|
-
const makeOpenAIChatCompletionCall = async (content, responseFormat =
|
|
2626
|
+
const makeOpenAIChatCompletionCall = async (content, responseFormat = 'text', options = {}) => {
|
|
2614
2627
|
const mergedOptions = {
|
|
2615
2628
|
...DEFAULT_OPTIONS$1,
|
|
2616
2629
|
...options,
|
|
@@ -2619,7 +2632,7 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = "text", op
|
|
|
2619
2632
|
// Track cost in the global cost tracker. Pass cached tokens through so the
|
|
2620
2633
|
// tracker applies the discounted cached-input rate (typically ~50% of the
|
|
2621
2634
|
// standard input rate) instead of billing every input token at full price.
|
|
2622
|
-
getLLMCostTracker().trackUsage(
|
|
2635
|
+
getLLMCostTracker().trackUsage('openai', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens);
|
|
2623
2636
|
// Handle tool calls differently
|
|
2624
2637
|
if (completion.tool_calls && completion.tool_calls.length > 0) {
|
|
2625
2638
|
const toolCallResponse = {
|
|
@@ -2635,10 +2648,10 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = "text", op
|
|
|
2635
2648
|
prompt_tokens: completion.usage.prompt_tokens,
|
|
2636
2649
|
completion_tokens: completion.usage.completion_tokens,
|
|
2637
2650
|
reasoning_tokens: 0,
|
|
2638
|
-
provider:
|
|
2651
|
+
provider: 'openai',
|
|
2639
2652
|
model: completion.model,
|
|
2640
2653
|
cached_tokens: completion.usage.cached_tokens,
|
|
2641
|
-
cost: calculateCost(
|
|
2654
|
+
cost: calculateCost('openai', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
|
|
2642
2655
|
},
|
|
2643
2656
|
tool_calls: completion.tool_calls,
|
|
2644
2657
|
};
|
|
@@ -2646,7 +2659,7 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = "text", op
|
|
|
2646
2659
|
// Handle regular responses
|
|
2647
2660
|
const parsedResponse = await parseResponse(completion.content, responseFormat);
|
|
2648
2661
|
if (parsedResponse === null) {
|
|
2649
|
-
throw new Error(
|
|
2662
|
+
throw new Error('Failed to parse response');
|
|
2650
2663
|
}
|
|
2651
2664
|
return {
|
|
2652
2665
|
response: parsedResponse,
|
|
@@ -2654,10 +2667,10 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = "text", op
|
|
|
2654
2667
|
prompt_tokens: completion.usage.prompt_tokens,
|
|
2655
2668
|
completion_tokens: completion.usage.completion_tokens,
|
|
2656
2669
|
reasoning_tokens: 0,
|
|
2657
|
-
provider:
|
|
2670
|
+
provider: 'openai',
|
|
2658
2671
|
model: completion.model,
|
|
2659
2672
|
cached_tokens: completion.usage.cached_tokens,
|
|
2660
|
-
cost: calculateCost(
|
|
2673
|
+
cost: calculateCost('openai', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
|
|
2661
2674
|
},
|
|
2662
2675
|
tool_calls: completion.tool_calls,
|
|
2663
2676
|
};
|
|
@@ -2692,44 +2705,45 @@ const makeResponsesAPICall = async (input, options = {}) => {
|
|
|
2692
2705
|
const normalizedModel = normalizeModelName(options.model || DEFAULT_MODEL);
|
|
2693
2706
|
const apiKey = options.apiKey || getSecrets().openai.apiKey;
|
|
2694
2707
|
if (!apiKey) {
|
|
2695
|
-
throw new Error(
|
|
2708
|
+
throw new Error('OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set');
|
|
2696
2709
|
}
|
|
2697
2710
|
const openai = new OpenAI({
|
|
2698
2711
|
apiKey: apiKey,
|
|
2699
2712
|
timeout: options.timeout ?? LLM_TIMEOUT_MS,
|
|
2700
2713
|
});
|
|
2701
|
-
// Remove apiKey, model, and
|
|
2702
|
-
const { apiKey: _, model: __, timeout: ___, ...cleanOptions } = options;
|
|
2714
|
+
// Remove apiKey, model, timeout, and signal from options before creating request body
|
|
2715
|
+
const { apiKey: _, model: __, timeout: ___, signal: ____, ...cleanOptions } = options;
|
|
2703
2716
|
const requestBody = {
|
|
2704
2717
|
model: normalizedModel,
|
|
2705
2718
|
input,
|
|
2706
2719
|
...cleanOptions,
|
|
2707
2720
|
};
|
|
2708
2721
|
// Make the API call to the Responses endpoint
|
|
2709
|
-
const response = await withRetry(() => openai.responses.create(requestBody), {
|
|
2722
|
+
const response = await withRetry(() => openai.responses.create(requestBody, options.signal ? { signal: options.signal } : undefined), {
|
|
2710
2723
|
maxRetries: 3,
|
|
2711
2724
|
baseDelayMs: 2000,
|
|
2712
2725
|
maxDelayMs: 30000,
|
|
2713
2726
|
retryableErrors: isRetryableLLMError,
|
|
2727
|
+
signal: options.signal,
|
|
2714
2728
|
}, `OpenAI-Responses:${normalizedModel}`);
|
|
2715
2729
|
// Responses API exposes cached input tokens under `input_tokens_details.cached_tokens`
|
|
2716
2730
|
// (the equivalent of Chat Completions' `prompt_tokens_details.cached_tokens`).
|
|
2717
2731
|
const responsesCachedTokens = response.usage?.input_tokens_details?.cached_tokens || 0;
|
|
2718
2732
|
// Track cost in the global cost tracker
|
|
2719
|
-
getLLMCostTracker().trackUsage(
|
|
2733
|
+
getLLMCostTracker().trackUsage('openai', normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens);
|
|
2720
2734
|
// Extract tool calls from the output
|
|
2721
2735
|
const toolCalls = response.output
|
|
2722
|
-
?.filter((item) => item.type ===
|
|
2736
|
+
?.filter((item) => item.type === 'function_call')
|
|
2723
2737
|
.map((toolCall) => ({
|
|
2724
2738
|
id: toolCall.call_id,
|
|
2725
|
-
type:
|
|
2739
|
+
type: 'function',
|
|
2726
2740
|
function: {
|
|
2727
2741
|
name: toolCall.name,
|
|
2728
2742
|
arguments: toolCall.arguments,
|
|
2729
2743
|
},
|
|
2730
2744
|
}));
|
|
2731
2745
|
// Extract code interpreter outputs if present
|
|
2732
|
-
const codeInterpreterCalls = response.output?.filter((item) => item.type ===
|
|
2746
|
+
const codeInterpreterCalls = response.output?.filter((item) => item.type === 'code_interpreter_call');
|
|
2733
2747
|
let codeInterpreterOutputs = undefined;
|
|
2734
2748
|
if (codeInterpreterCalls && codeInterpreterCalls.length > 0) {
|
|
2735
2749
|
// Each code_interpreter_call has an 'outputs' array property
|
|
@@ -2755,34 +2769,32 @@ const makeResponsesAPICall = async (input, options = {}) => {
|
|
|
2755
2769
|
prompt_tokens: response.usage?.input_tokens || 0,
|
|
2756
2770
|
completion_tokens: response.usage?.output_tokens || 0,
|
|
2757
2771
|
reasoning_tokens: response.usage?.output_tokens_details?.reasoning_tokens || 0,
|
|
2758
|
-
provider:
|
|
2772
|
+
provider: 'openai',
|
|
2759
2773
|
model: normalizedModel,
|
|
2760
2774
|
cached_tokens: responsesCachedTokens,
|
|
2761
|
-
cost: calculateCost(
|
|
2775
|
+
cost: calculateCost('openai', normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens),
|
|
2762
2776
|
},
|
|
2763
2777
|
tool_calls: toolCalls,
|
|
2764
|
-
...(codeInterpreterOutputs
|
|
2765
|
-
? { code_interpreter_outputs: codeInterpreterOutputs }
|
|
2766
|
-
: {}),
|
|
2778
|
+
...(codeInterpreterOutputs ? { code_interpreter_outputs: codeInterpreterOutputs } : {}),
|
|
2767
2779
|
};
|
|
2768
2780
|
}
|
|
2769
2781
|
// Extract text content from the response output
|
|
2770
2782
|
const textContent = response.output
|
|
2771
|
-
?.filter((item) => item.type ===
|
|
2783
|
+
?.filter((item) => item.type === 'message')
|
|
2772
2784
|
.map((item) => item.content
|
|
2773
|
-
.filter((content) => content.type ===
|
|
2785
|
+
.filter((content) => content.type === 'output_text')
|
|
2774
2786
|
.map((content) => content.text)
|
|
2775
|
-
.join(
|
|
2776
|
-
.join(
|
|
2787
|
+
.join(''))
|
|
2788
|
+
.join('') || '';
|
|
2777
2789
|
// Determine the format for parsing the response
|
|
2778
|
-
let parsingFormat =
|
|
2779
|
-
if (requestBody.text?.format?.type ===
|
|
2780
|
-
parsingFormat =
|
|
2790
|
+
let parsingFormat = 'text';
|
|
2791
|
+
if (requestBody.text?.format?.type === 'json_object') {
|
|
2792
|
+
parsingFormat = 'json';
|
|
2781
2793
|
}
|
|
2782
2794
|
// Handle regular responses
|
|
2783
2795
|
const parsedResponse = await parseResponse(textContent, parsingFormat);
|
|
2784
2796
|
if (parsedResponse === null) {
|
|
2785
|
-
throw new Error(
|
|
2797
|
+
throw new Error('Failed to parse response from Responses API');
|
|
2786
2798
|
}
|
|
2787
2799
|
return {
|
|
2788
2800
|
response: parsedResponse,
|
|
@@ -2790,15 +2802,13 @@ const makeResponsesAPICall = async (input, options = {}) => {
|
|
|
2790
2802
|
prompt_tokens: response.usage?.input_tokens || 0,
|
|
2791
2803
|
completion_tokens: response.usage?.output_tokens || 0,
|
|
2792
2804
|
reasoning_tokens: response.usage?.output_tokens_details?.reasoning_tokens || 0,
|
|
2793
|
-
provider:
|
|
2805
|
+
provider: 'openai',
|
|
2794
2806
|
model: normalizedModel,
|
|
2795
2807
|
cached_tokens: responsesCachedTokens,
|
|
2796
|
-
cost: calculateCost(
|
|
2808
|
+
cost: calculateCost('openai', normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens),
|
|
2797
2809
|
},
|
|
2798
2810
|
tool_calls: toolCalls,
|
|
2799
|
-
...(codeInterpreterOutputs
|
|
2800
|
-
? { code_interpreter_outputs: codeInterpreterOutputs }
|
|
2801
|
-
: {}),
|
|
2811
|
+
...(codeInterpreterOutputs ? { code_interpreter_outputs: codeInterpreterOutputs } : {}),
|
|
2802
2812
|
};
|
|
2803
2813
|
};
|
|
2804
2814
|
|
|
@@ -8108,12 +8118,12 @@ function sanitizeObject(obj, sensitiveFields = ['apiKey', 'token', 'secret', 'pa
|
|
|
8108
8118
|
const isRetryableAnthropicError = (error) => {
|
|
8109
8119
|
if (error instanceof Error) {
|
|
8110
8120
|
const message = error.message;
|
|
8111
|
-
if (message.includes(
|
|
8112
|
-
message.includes(
|
|
8113
|
-
message.includes(
|
|
8114
|
-
message.includes(
|
|
8115
|
-
message.includes(
|
|
8116
|
-
message.includes(
|
|
8121
|
+
if (message.includes('429') ||
|
|
8122
|
+
message.includes('500') ||
|
|
8123
|
+
message.includes('503') ||
|
|
8124
|
+
message.includes('529') ||
|
|
8125
|
+
message.includes('rate limit') ||
|
|
8126
|
+
message.includes('overloaded')) {
|
|
8117
8127
|
return true;
|
|
8118
8128
|
}
|
|
8119
8129
|
}
|
|
@@ -8128,11 +8138,8 @@ const isRetryableAnthropicError = (error) => {
|
|
|
8128
8138
|
function translateToolsToAnthropic(tools) {
|
|
8129
8139
|
return tools.map((tool) => ({
|
|
8130
8140
|
name: tool.function.name,
|
|
8131
|
-
description: tool.function.description ||
|
|
8132
|
-
input_schema: (tool.function.parameters || {
|
|
8133
|
-
type: "object",
|
|
8134
|
-
properties: {},
|
|
8135
|
-
}),
|
|
8141
|
+
description: tool.function.description || '',
|
|
8142
|
+
input_schema: (tool.function.parameters || { type: 'object', properties: {} }),
|
|
8136
8143
|
}));
|
|
8137
8144
|
}
|
|
8138
8145
|
/**
|
|
@@ -8145,31 +8152,27 @@ function translateContextToAnthropic(context) {
|
|
|
8145
8152
|
const systemParts = [];
|
|
8146
8153
|
const messages = [];
|
|
8147
8154
|
for (const msg of context) {
|
|
8148
|
-
if (msg.role ===
|
|
8149
|
-
if (typeof msg.content ===
|
|
8155
|
+
if (msg.role === 'system' || msg.role === 'developer') {
|
|
8156
|
+
if (typeof msg.content === 'string') {
|
|
8150
8157
|
systemParts.push(msg.content);
|
|
8151
8158
|
}
|
|
8152
8159
|
continue;
|
|
8153
8160
|
}
|
|
8154
|
-
if (msg.role ===
|
|
8155
|
-
const content = typeof msg.content ===
|
|
8161
|
+
if (msg.role === 'user') {
|
|
8162
|
+
const content = typeof msg.content === 'string'
|
|
8156
8163
|
? msg.content
|
|
8157
8164
|
: JSON.stringify(msg.content);
|
|
8158
|
-
messages.push({ role:
|
|
8165
|
+
messages.push({ role: 'user', content });
|
|
8159
8166
|
continue;
|
|
8160
8167
|
}
|
|
8161
|
-
if (msg.role ===
|
|
8168
|
+
if (msg.role === 'assistant') {
|
|
8162
8169
|
const assistantMsg = msg;
|
|
8163
8170
|
// If the assistant message has tool_calls, translate to Anthropic tool_use blocks
|
|
8164
8171
|
if (assistantMsg.tool_calls && assistantMsg.tool_calls.length > 0) {
|
|
8165
8172
|
const contentBlocks = [];
|
|
8166
8173
|
// Include text content if present
|
|
8167
|
-
if (typeof assistantMsg.content ===
|
|
8168
|
-
assistantMsg.content
|
|
8169
|
-
contentBlocks.push({
|
|
8170
|
-
type: "text",
|
|
8171
|
-
text: assistantMsg.content,
|
|
8172
|
-
});
|
|
8174
|
+
if (typeof assistantMsg.content === 'string' && assistantMsg.content.trim()) {
|
|
8175
|
+
contentBlocks.push({ type: 'text', text: assistantMsg.content });
|
|
8173
8176
|
}
|
|
8174
8177
|
// Translate each tool_call to a tool_use block
|
|
8175
8178
|
for (const tc of assistantMsg.tool_calls) {
|
|
@@ -8181,36 +8184,32 @@ function translateContextToAnthropic(context) {
|
|
|
8181
8184
|
input = { raw: tc.function.arguments };
|
|
8182
8185
|
}
|
|
8183
8186
|
contentBlocks.push({
|
|
8184
|
-
type:
|
|
8187
|
+
type: 'tool_use',
|
|
8185
8188
|
id: tc.id,
|
|
8186
8189
|
name: tc.function.name,
|
|
8187
8190
|
input,
|
|
8188
8191
|
});
|
|
8189
8192
|
}
|
|
8190
|
-
messages.push({ role:
|
|
8193
|
+
messages.push({ role: 'assistant', content: contentBlocks });
|
|
8191
8194
|
}
|
|
8192
8195
|
else {
|
|
8193
|
-
const content = typeof assistantMsg.content ===
|
|
8196
|
+
const content = typeof assistantMsg.content === 'string'
|
|
8194
8197
|
? assistantMsg.content
|
|
8195
8198
|
: JSON.stringify(assistantMsg.content);
|
|
8196
|
-
messages.push({ role:
|
|
8199
|
+
messages.push({ role: 'assistant', content });
|
|
8197
8200
|
}
|
|
8198
8201
|
continue;
|
|
8199
8202
|
}
|
|
8200
|
-
if (msg.role ===
|
|
8203
|
+
if (msg.role === 'tool') {
|
|
8201
8204
|
// Anthropic expects tool results as user messages with tool_result content blocks
|
|
8202
8205
|
const toolMsg = msg;
|
|
8203
8206
|
messages.push({
|
|
8204
|
-
role:
|
|
8205
|
-
content: [
|
|
8206
|
-
|
|
8207
|
-
type: "tool_result",
|
|
8207
|
+
role: 'user',
|
|
8208
|
+
content: [{
|
|
8209
|
+
type: 'tool_result',
|
|
8208
8210
|
tool_use_id: toolMsg.tool_call_id,
|
|
8209
|
-
content: typeof toolMsg.content ===
|
|
8210
|
-
|
|
8211
|
-
: JSON.stringify(toolMsg.content),
|
|
8212
|
-
},
|
|
8213
|
-
],
|
|
8211
|
+
content: typeof toolMsg.content === 'string' ? toolMsg.content : JSON.stringify(toolMsg.content),
|
|
8212
|
+
}],
|
|
8214
8213
|
});
|
|
8215
8214
|
continue;
|
|
8216
8215
|
}
|
|
@@ -8231,13 +8230,13 @@ function translateContextToAnthropic(context) {
|
|
|
8231
8230
|
merged.push({ role: msg.role, content: toContentBlocks(msg.content) });
|
|
8232
8231
|
}
|
|
8233
8232
|
}
|
|
8234
|
-
return { messages: merged, systemText: systemParts.join(
|
|
8233
|
+
return { messages: merged, systemText: systemParts.join('\n\n') };
|
|
8235
8234
|
}
|
|
8236
8235
|
/** Convert string or content block array to a uniform content block array. */
|
|
8237
8236
|
function toContentBlocks(content) {
|
|
8238
|
-
if (typeof content ===
|
|
8237
|
+
if (typeof content === 'string') {
|
|
8239
8238
|
const textBlock = {
|
|
8240
|
-
type:
|
|
8239
|
+
type: 'text',
|
|
8241
8240
|
text: content,
|
|
8242
8241
|
citations: null,
|
|
8243
8242
|
};
|
|
@@ -8257,11 +8256,11 @@ function toContentBlocks(content) {
|
|
|
8257
8256
|
* @param options The options for the LLM call.
|
|
8258
8257
|
* @return A promise that resolves to the response from the Anthropic API.
|
|
8259
8258
|
*/
|
|
8260
|
-
async function makeAnthropicCall(content, responseFormat =
|
|
8261
|
-
const model = (options.model ||
|
|
8259
|
+
async function makeAnthropicCall(content, responseFormat = 'text', options = {}) {
|
|
8260
|
+
const model = (options.model || 'claude-sonnet-4-6');
|
|
8262
8261
|
const apiKey = options.apiKey || getSecrets().anthropic.apiKey;
|
|
8263
8262
|
if (!apiKey) {
|
|
8264
|
-
throw new Error(
|
|
8263
|
+
throw new Error('Anthropic API key is not provided and ANTHROPIC_API_KEY environment variable is not set');
|
|
8265
8264
|
}
|
|
8266
8265
|
const client = new Anthropic({
|
|
8267
8266
|
apiKey,
|
|
@@ -8273,10 +8272,8 @@ async function makeAnthropicCall(content, responseFormat = "text", options = {})
|
|
|
8273
8272
|
systemParts.push(options.developerPrompt);
|
|
8274
8273
|
}
|
|
8275
8274
|
// If JSON response is requested, instruct the model via system prompt
|
|
8276
|
-
if (responseFormat ===
|
|
8277
|
-
(
|
|
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.");
|
|
8275
|
+
if (responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat.type === 'json_schema')) {
|
|
8276
|
+
systemParts.push('You MUST respond with valid JSON only. No markdown, no code blocks, no explanatory text — just the raw JSON object or array.');
|
|
8280
8277
|
}
|
|
8281
8278
|
// Build messages array
|
|
8282
8279
|
const messages = [];
|
|
@@ -8289,7 +8286,7 @@ async function makeAnthropicCall(content, responseFormat = "text", options = {})
|
|
|
8289
8286
|
messages.push(...translated.messages);
|
|
8290
8287
|
}
|
|
8291
8288
|
// Add user content
|
|
8292
|
-
messages.push({ role:
|
|
8289
|
+
messages.push({ role: 'user', content });
|
|
8293
8290
|
// Build request params
|
|
8294
8291
|
const requestParams = {
|
|
8295
8292
|
model,
|
|
@@ -8298,37 +8295,46 @@ async function makeAnthropicCall(content, responseFormat = "text", options = {})
|
|
|
8298
8295
|
};
|
|
8299
8296
|
// Add system prompt if present
|
|
8300
8297
|
if (systemParts.length > 0) {
|
|
8301
|
-
requestParams.system = systemParts.join(
|
|
8302
|
-
}
|
|
8303
|
-
//
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8298
|
+
requestParams.system = systemParts.join('\n\n');
|
|
8299
|
+
}
|
|
8300
|
+
// Sampling controls are intentionally disabled for the Anthropic provider.
|
|
8301
|
+
//
|
|
8302
|
+
// The claude-*-4.x model family rejects any request that specifies BOTH
|
|
8303
|
+
// `temperature` and `top_p` ("`temperature` and `top_p` cannot both be
|
|
8304
|
+
// specified for this model. Please use only one." — HTTP 400), which aborts
|
|
8305
|
+
// the entire caller flow (e.g. the engine's account decision session).
|
|
8306
|
+
// Callers across the org pass both knobs generically, so rather than make
|
|
8307
|
+
// each one provider-aware we drop both here and let Anthropic use its model
|
|
8308
|
+
// defaults. `options.temperature` / `options.top_p` are intentionally not
|
|
8309
|
+
// forwarded. This is the Anthropic adapter only — the OpenAI, DeepSeek, and
|
|
8310
|
+
// OpenAI-compatible adapters still honor `temperature` and `top_p`.
|
|
8310
8311
|
// Translate and add tools if present
|
|
8311
8312
|
if (options.tools && options.tools.length > 0) {
|
|
8312
8313
|
requestParams.tools = translateToolsToAnthropic(options.tools);
|
|
8313
8314
|
}
|
|
8314
8315
|
try {
|
|
8315
|
-
const response = await withRetry(
|
|
8316
|
+
const response = await withRetry(
|
|
8317
|
+
// Pass AbortSignal into the Anthropic SDK request-options object
|
|
8318
|
+
// so caller-driven cancellation interrupts the in-flight HTTP
|
|
8319
|
+
// request immediately. See LLMOptions.signal JSDoc for context.
|
|
8320
|
+
() => client.messages.create(requestParams, options.signal ? { signal: options.signal } : undefined), {
|
|
8316
8321
|
maxRetries: 3,
|
|
8317
8322
|
baseDelayMs: 2000,
|
|
8318
8323
|
maxDelayMs: 30000,
|
|
8319
8324
|
retryableErrors: isRetryableAnthropicError,
|
|
8325
|
+
signal: options.signal,
|
|
8320
8326
|
}, `Anthropic:${model}`);
|
|
8321
8327
|
const inputTokens = response.usage.input_tokens;
|
|
8322
8328
|
const outputTokens = response.usage.output_tokens;
|
|
8323
8329
|
// Track cost
|
|
8324
|
-
getLLMCostTracker().trackUsage(
|
|
8330
|
+
getLLMCostTracker().trackUsage('anthropic', model, inputTokens, outputTokens);
|
|
8325
8331
|
// Extract tool use blocks
|
|
8326
|
-
const toolUseBlocks = response.content.filter((block) => block.type ===
|
|
8332
|
+
const toolUseBlocks = response.content.filter((block) => block.type === 'tool_use');
|
|
8327
8333
|
if (toolUseBlocks.length > 0) {
|
|
8328
8334
|
// Translate Anthropic tool_use to OpenAI tool_calls format
|
|
8329
8335
|
const toolCalls = toolUseBlocks.map((block) => ({
|
|
8330
8336
|
id: block.id,
|
|
8331
|
-
type:
|
|
8337
|
+
type: 'function',
|
|
8332
8338
|
function: {
|
|
8333
8339
|
name: block.name,
|
|
8334
8340
|
arguments: JSON.stringify(block.input),
|
|
@@ -8347,22 +8353,22 @@ async function makeAnthropicCall(content, responseFormat = "text", options = {})
|
|
|
8347
8353
|
prompt_tokens: inputTokens,
|
|
8348
8354
|
completion_tokens: outputTokens,
|
|
8349
8355
|
reasoning_tokens: 0,
|
|
8350
|
-
provider:
|
|
8356
|
+
provider: 'anthropic',
|
|
8351
8357
|
model,
|
|
8352
|
-
cost: calculateCost(
|
|
8358
|
+
cost: calculateCost('anthropic', model, inputTokens, outputTokens),
|
|
8353
8359
|
},
|
|
8354
8360
|
tool_calls: toolCalls,
|
|
8355
8361
|
};
|
|
8356
8362
|
}
|
|
8357
8363
|
// Extract text content
|
|
8358
8364
|
const textContent = response.content
|
|
8359
|
-
.filter((block) => block.type ===
|
|
8365
|
+
.filter((block) => block.type === 'text')
|
|
8360
8366
|
.map((block) => block.text)
|
|
8361
|
-
.join(
|
|
8367
|
+
.join('');
|
|
8362
8368
|
// Parse response
|
|
8363
8369
|
const parsedResponse = await parseResponse(textContent, responseFormat);
|
|
8364
8370
|
if (parsedResponse === null) {
|
|
8365
|
-
throw new Error(
|
|
8371
|
+
throw new Error('Failed to parse Anthropic response');
|
|
8366
8372
|
}
|
|
8367
8373
|
return {
|
|
8368
8374
|
response: parsedResponse,
|
|
@@ -8370,9 +8376,9 @@ async function makeAnthropicCall(content, responseFormat = "text", options = {})
|
|
|
8370
8376
|
prompt_tokens: inputTokens,
|
|
8371
8377
|
completion_tokens: outputTokens,
|
|
8372
8378
|
reasoning_tokens: 0,
|
|
8373
|
-
provider:
|
|
8379
|
+
provider: 'anthropic',
|
|
8374
8380
|
model,
|
|
8375
|
-
cost: calculateCost(
|
|
8381
|
+
cost: calculateCost('anthropic', model, inputTokens, outputTokens),
|
|
8376
8382
|
},
|
|
8377
8383
|
};
|
|
8378
8384
|
}
|
|
@@ -8394,9 +8400,7 @@ async function makeAnthropicCall(content, responseFormat = "text", options = {})
|
|
|
8394
8400
|
const isRetryableError = (error) => {
|
|
8395
8401
|
if (error instanceof Error) {
|
|
8396
8402
|
const message = error.message;
|
|
8397
|
-
if (message.includes(
|
|
8398
|
-
message.includes("rate limit") ||
|
|
8399
|
-
message.includes("Rate limit")) {
|
|
8403
|
+
if (message.includes('429') || message.includes('rate limit') || message.includes('Rate limit')) {
|
|
8400
8404
|
return true;
|
|
8401
8405
|
}
|
|
8402
8406
|
}
|
|
@@ -8410,16 +8414,14 @@ function resolveApiKey(provider, optionsApiKey) {
|
|
|
8410
8414
|
if (optionsApiKey)
|
|
8411
8415
|
return optionsApiKey;
|
|
8412
8416
|
const secrets = getSecrets();
|
|
8413
|
-
const pathParts = provider.apiKeySecretPath.split(
|
|
8417
|
+
const pathParts = provider.apiKeySecretPath.split('.');
|
|
8414
8418
|
let current = secrets;
|
|
8415
8419
|
for (const part of pathParts) {
|
|
8416
|
-
if (current === null ||
|
|
8417
|
-
|
|
8418
|
-
typeof current !== "object")
|
|
8419
|
-
return "";
|
|
8420
|
+
if (current === null || current === undefined || typeof current !== 'object')
|
|
8421
|
+
return '';
|
|
8420
8422
|
current = current[part];
|
|
8421
8423
|
}
|
|
8422
|
-
return (typeof current ===
|
|
8424
|
+
return (typeof current === 'string' ? current : '') || '';
|
|
8423
8425
|
}
|
|
8424
8426
|
/**
|
|
8425
8427
|
* Makes a call to an OpenAI-compatible provider (Kimi, Qwen, or any future provider).
|
|
@@ -8433,12 +8435,12 @@ function resolveApiKey(provider, optionsApiKey) {
|
|
|
8433
8435
|
* @param providerName The provider key in OPENAI_COMPATIBLE_PROVIDERS.
|
|
8434
8436
|
* @return A promise that resolves to the response from the provider.
|
|
8435
8437
|
*/
|
|
8436
|
-
async function makeOpenAICompatibleCall(content, responseFormat =
|
|
8438
|
+
async function makeOpenAICompatibleCall(content, responseFormat = 'text', options = {}, providerName) {
|
|
8437
8439
|
const providerConfig = OPENAI_COMPATIBLE_PROVIDERS[providerName];
|
|
8438
8440
|
if (!providerConfig) {
|
|
8439
8441
|
throw new Error(`Unknown OpenAI-compatible provider: ${providerName}`);
|
|
8440
8442
|
}
|
|
8441
|
-
const model = normalizeModelName(options.model ||
|
|
8443
|
+
const model = normalizeModelName(options.model || '');
|
|
8442
8444
|
const apiKey = resolveApiKey(providerConfig, options.apiKey);
|
|
8443
8445
|
if (!apiKey) {
|
|
8444
8446
|
throw new Error(`${providerConfig.name} API key is not provided and ${providerConfig.apiKeyEnvVar} environment variable is not set`);
|
|
@@ -8451,36 +8453,31 @@ async function makeOpenAICompatibleCall(content, responseFormat = "text", option
|
|
|
8451
8453
|
const messages = [];
|
|
8452
8454
|
// Add system message with developer prompt if present
|
|
8453
8455
|
if (options.developerPrompt) {
|
|
8454
|
-
messages.push({ role:
|
|
8456
|
+
messages.push({ role: 'system', content: options.developerPrompt });
|
|
8455
8457
|
}
|
|
8456
8458
|
// Add context if present
|
|
8457
8459
|
if (options.context) {
|
|
8458
8460
|
messages.push(...options.context);
|
|
8459
8461
|
}
|
|
8460
8462
|
// Add user content
|
|
8461
|
-
if (typeof content ===
|
|
8462
|
-
messages.push({ role:
|
|
8463
|
+
if (typeof content === 'string') {
|
|
8464
|
+
messages.push({ role: 'user', content });
|
|
8463
8465
|
}
|
|
8464
8466
|
else {
|
|
8465
|
-
messages.push({ role:
|
|
8467
|
+
messages.push({ role: 'user', content });
|
|
8466
8468
|
}
|
|
8467
8469
|
// Determine if the model supports JSON mode
|
|
8468
8470
|
const supportsJson = isValidModel(model) && getModelCapabilities(model).supportsJson;
|
|
8469
8471
|
// If JSON response format, add hint to system prompt
|
|
8470
|
-
if ((responseFormat ===
|
|
8471
|
-
(typeof responseFormat === "object" &&
|
|
8472
|
-
responseFormat.type === "json_schema")) &&
|
|
8472
|
+
if ((responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat.type === 'json_schema')) &&
|
|
8473
8473
|
supportsJson) {
|
|
8474
|
-
if (!messages.some((m) => m.role ===
|
|
8475
|
-
messages.unshift({
|
|
8476
|
-
role: "system",
|
|
8477
|
-
content: "Please respond in valid JSON format.",
|
|
8478
|
-
});
|
|
8474
|
+
if (!messages.some((m) => m.role === 'system')) {
|
|
8475
|
+
messages.unshift({ role: 'system', content: 'Please respond in valid JSON format.' });
|
|
8479
8476
|
}
|
|
8480
8477
|
else {
|
|
8481
|
-
const systemMsgIndex = messages.findIndex((m) => m.role ===
|
|
8478
|
+
const systemMsgIndex = messages.findIndex((m) => m.role === 'system');
|
|
8482
8479
|
const systemMsg = messages[systemMsgIndex];
|
|
8483
|
-
if (typeof systemMsg.content ===
|
|
8480
|
+
if (typeof systemMsg.content === 'string') {
|
|
8484
8481
|
messages[systemMsgIndex] = {
|
|
8485
8482
|
...systemMsg,
|
|
8486
8483
|
content: `${systemMsg.content} Please respond in valid JSON format.`,
|
|
@@ -8493,8 +8490,8 @@ async function makeOpenAICompatibleCall(content, responseFormat = "text", option
|
|
|
8493
8490
|
messages,
|
|
8494
8491
|
};
|
|
8495
8492
|
// Add response format if JSON is supported
|
|
8496
|
-
if (responseFormat !==
|
|
8497
|
-
queryOptions.response_format = { type:
|
|
8493
|
+
if (responseFormat !== 'text' && supportsJson) {
|
|
8494
|
+
queryOptions.response_format = { type: 'json_object' };
|
|
8498
8495
|
}
|
|
8499
8496
|
// Temperature
|
|
8500
8497
|
if (options.temperature !== undefined) {
|
|
@@ -8518,11 +8515,16 @@ async function makeOpenAICompatibleCall(content, responseFormat = "text", option
|
|
|
8518
8515
|
queryOptions.tools = options.tools;
|
|
8519
8516
|
}
|
|
8520
8517
|
try {
|
|
8521
|
-
const completion = await withRetry(
|
|
8518
|
+
const completion = await withRetry(
|
|
8519
|
+
// Pass AbortSignal into the SDK request-options object so
|
|
8520
|
+
// caller-driven cancellation interrupts the in-flight HTTP
|
|
8521
|
+
// request immediately. See LLMOptions.signal JSDoc for context.
|
|
8522
|
+
() => openai.chat.completions.create(queryOptions, options.signal ? { signal: options.signal } : undefined), {
|
|
8522
8523
|
maxRetries: 3,
|
|
8523
8524
|
baseDelayMs: 2000,
|
|
8524
8525
|
maxDelayMs: 30000,
|
|
8525
8526
|
retryableErrors: isRetryableError,
|
|
8527
|
+
signal: options.signal,
|
|
8526
8528
|
}, `${providerConfig.name}:${model}`);
|
|
8527
8529
|
const promptTokens = completion.usage?.prompt_tokens || 0;
|
|
8528
8530
|
const completionTokens = completion.usage?.completion_tokens || 0;
|
|
@@ -8552,7 +8554,7 @@ async function makeOpenAICompatibleCall(content, responseFormat = "text", option
|
|
|
8552
8554
|
};
|
|
8553
8555
|
}
|
|
8554
8556
|
// Handle regular responses
|
|
8555
|
-
const textContent = completion.choices[0]?.message?.content ||
|
|
8557
|
+
const textContent = completion.choices[0]?.message?.content || '';
|
|
8556
8558
|
const parsedResponse = await parseResponse(textContent, responseFormat);
|
|
8557
8559
|
if (parsedResponse === null) {
|
|
8558
8560
|
throw new Error(`Failed to parse ${providerConfig.name} response`);
|
|
@@ -8844,9 +8846,7 @@ const isRetryableDeepseekError = (error) => {
|
|
|
8844
8846
|
if (error instanceof Error) {
|
|
8845
8847
|
const message = error.message;
|
|
8846
8848
|
// Retry only on rate limits (429)
|
|
8847
|
-
if (message.includes(
|
|
8848
|
-
message.includes("rate limit") ||
|
|
8849
|
-
message.includes("Rate limit")) {
|
|
8849
|
+
if (message.includes('429') || message.includes('rate limit') || message.includes('Rate limit')) {
|
|
8850
8850
|
return true;
|
|
8851
8851
|
}
|
|
8852
8852
|
}
|
|
@@ -8856,7 +8856,7 @@ const isRetryableDeepseekError = (error) => {
|
|
|
8856
8856
|
* Default options for Deepseek API calls
|
|
8857
8857
|
*/
|
|
8858
8858
|
const DEFAULT_DEEPSEEK_OPTIONS = {
|
|
8859
|
-
defaultModel:
|
|
8859
|
+
defaultModel: 'deepseek-chat',
|
|
8860
8860
|
};
|
|
8861
8861
|
/**
|
|
8862
8862
|
* Checks if the given model is a supported Deepseek model
|
|
@@ -8868,7 +8868,7 @@ const isSupportedDeepseekModel = (model) => {
|
|
|
8868
8868
|
return false;
|
|
8869
8869
|
}
|
|
8870
8870
|
const capabilities = getModelCapabilities(model);
|
|
8871
|
-
return capabilities.provider ===
|
|
8871
|
+
return capabilities.provider === 'deepseek';
|
|
8872
8872
|
};
|
|
8873
8873
|
/**
|
|
8874
8874
|
* Checks if the given Deepseek model supports JSON output format
|
|
@@ -8901,19 +8901,17 @@ const supportsToolCalling = (model) => {
|
|
|
8901
8901
|
*/
|
|
8902
8902
|
function getDeepseekResponseFormatOption(responseFormat, model) {
|
|
8903
8903
|
// Check if the model supports JSON output
|
|
8904
|
-
if (responseFormat !==
|
|
8904
|
+
if (responseFormat !== 'text' && !supportsJsonOutput(model)) {
|
|
8905
8905
|
getLumicLogger().warn(`Model ${model} does not support JSON output. Using text format instead.`);
|
|
8906
|
-
return { type:
|
|
8906
|
+
return { type: 'text' };
|
|
8907
8907
|
}
|
|
8908
|
-
if (responseFormat ===
|
|
8909
|
-
return { type:
|
|
8908
|
+
if (responseFormat === 'text') {
|
|
8909
|
+
return { type: 'text' };
|
|
8910
8910
|
}
|
|
8911
|
-
if (responseFormat ===
|
|
8912
|
-
|
|
8913
|
-
responseFormat.type === "json_schema")) {
|
|
8914
|
-
return { type: "json_object" };
|
|
8911
|
+
if (responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat.type === 'json_schema')) {
|
|
8912
|
+
return { type: 'json_object' };
|
|
8915
8913
|
}
|
|
8916
|
-
return { type:
|
|
8914
|
+
return { type: 'text' };
|
|
8917
8915
|
}
|
|
8918
8916
|
/**
|
|
8919
8917
|
* Creates a completion using the Deepseek API
|
|
@@ -8932,26 +8930,24 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
|
|
|
8932
8930
|
throw new Error(`Unsupported Deepseek model: ${normalizedModel}. Please use 'deepseek-chat' or 'deepseek-reasoner'.`);
|
|
8933
8931
|
}
|
|
8934
8932
|
// Check if tools are requested with a model that doesn't support them
|
|
8935
|
-
if (options.tools &&
|
|
8936
|
-
options.tools.length > 0 &&
|
|
8937
|
-
!supportsToolCalling(normalizedModel)) {
|
|
8933
|
+
if (options.tools && options.tools.length > 0 && !supportsToolCalling(normalizedModel)) {
|
|
8938
8934
|
throw new Error(`Model ${normalizedModel} does not support tool calling.`);
|
|
8939
8935
|
}
|
|
8940
8936
|
const apiKey = options.apiKey || getSecrets().deepseek.apiKey;
|
|
8941
8937
|
if (!apiKey) {
|
|
8942
|
-
throw new Error(
|
|
8938
|
+
throw new Error('Deepseek API key is not provided and DEEPSEEK_API_KEY environment variable is not set');
|
|
8943
8939
|
}
|
|
8944
8940
|
// Initialize OpenAI client with Deepseek API URL
|
|
8945
8941
|
const openai = new OpenAI({
|
|
8946
8942
|
apiKey,
|
|
8947
|
-
baseURL:
|
|
8943
|
+
baseURL: 'https://api.deepseek.com',
|
|
8948
8944
|
timeout: options.timeout ?? LLM_TIMEOUT_MS,
|
|
8949
8945
|
});
|
|
8950
8946
|
const messages = [];
|
|
8951
8947
|
// Add system message with developer prompt if present
|
|
8952
8948
|
if (options.developerPrompt) {
|
|
8953
8949
|
messages.push({
|
|
8954
|
-
role:
|
|
8950
|
+
role: 'system',
|
|
8955
8951
|
content: options.developerPrompt,
|
|
8956
8952
|
});
|
|
8957
8953
|
}
|
|
@@ -8960,35 +8956,33 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
|
|
|
8960
8956
|
messages.push(...options.context);
|
|
8961
8957
|
}
|
|
8962
8958
|
// Add user content
|
|
8963
|
-
if (typeof content ===
|
|
8959
|
+
if (typeof content === 'string') {
|
|
8964
8960
|
messages.push({
|
|
8965
|
-
role:
|
|
8961
|
+
role: 'user',
|
|
8966
8962
|
content,
|
|
8967
8963
|
});
|
|
8968
8964
|
}
|
|
8969
8965
|
else {
|
|
8970
8966
|
messages.push({
|
|
8971
|
-
role:
|
|
8967
|
+
role: 'user',
|
|
8972
8968
|
content,
|
|
8973
8969
|
});
|
|
8974
8970
|
}
|
|
8975
8971
|
// If JSON response format, include a hint in the system prompt
|
|
8976
|
-
if ((responseFormat ===
|
|
8977
|
-
(
|
|
8978
|
-
responseFormat.type === "json_schema")) &&
|
|
8979
|
-
supportsJsonOutput(normalizedModel)) {
|
|
8972
|
+
if ((responseFormat === 'json' || (typeof responseFormat === 'object' && responseFormat.type === 'json_schema'))
|
|
8973
|
+
&& supportsJsonOutput(normalizedModel)) {
|
|
8980
8974
|
// If there's no system message yet, add one
|
|
8981
|
-
if (!messages.some(
|
|
8975
|
+
if (!messages.some(m => m.role === 'system')) {
|
|
8982
8976
|
messages.unshift({
|
|
8983
|
-
role:
|
|
8984
|
-
content:
|
|
8977
|
+
role: 'system',
|
|
8978
|
+
content: 'Please respond in valid JSON format.',
|
|
8985
8979
|
});
|
|
8986
8980
|
}
|
|
8987
8981
|
else {
|
|
8988
8982
|
// Append to existing system message
|
|
8989
|
-
const systemMsgIndex = messages.findIndex(
|
|
8983
|
+
const systemMsgIndex = messages.findIndex(m => m.role === 'system');
|
|
8990
8984
|
const systemMsg = messages[systemMsgIndex];
|
|
8991
|
-
if (typeof systemMsg.content ===
|
|
8985
|
+
if (typeof systemMsg.content === 'string') {
|
|
8992
8986
|
messages[systemMsgIndex] = {
|
|
8993
8987
|
...systemMsg,
|
|
8994
8988
|
content: `${systemMsg.content} Please respond in valid JSON format.`,
|
|
@@ -9029,7 +9023,7 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
|
|
|
9029
9023
|
0;
|
|
9030
9024
|
return {
|
|
9031
9025
|
id: completion.id,
|
|
9032
|
-
content: completion.choices[0]?.message?.content ||
|
|
9026
|
+
content: completion.choices[0]?.message?.content || '',
|
|
9033
9027
|
tool_calls: completion.choices[0]?.message?.tool_calls,
|
|
9034
9028
|
usage: {
|
|
9035
9029
|
prompt_tokens: completion.usage?.prompt_tokens ?? 0,
|
|
@@ -9038,7 +9032,7 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
|
|
|
9038
9032
|
cached_tokens: cachedTokens,
|
|
9039
9033
|
},
|
|
9040
9034
|
system_fingerprint: completion.system_fingerprint,
|
|
9041
|
-
provider:
|
|
9035
|
+
provider: 'deepseek',
|
|
9042
9036
|
model: normalizedModel,
|
|
9043
9037
|
};
|
|
9044
9038
|
}
|
|
@@ -9055,7 +9049,7 @@ async function createDeepseekCompletion(content, responseFormat, options = {}) {
|
|
|
9055
9049
|
* @param options Configuration options including model ('deepseek-chat' or 'deepseek-reasoner'), tools, and apiKey.
|
|
9056
9050
|
* @return A promise that resolves to the response from the Deepseek API.
|
|
9057
9051
|
*/
|
|
9058
|
-
const makeDeepseekCall = async (content, responseFormat =
|
|
9052
|
+
const makeDeepseekCall = async (content, responseFormat = 'json', options = {}) => {
|
|
9059
9053
|
// Set default model if not provided
|
|
9060
9054
|
const mergedOptions = {
|
|
9061
9055
|
...options,
|
|
@@ -9065,17 +9059,17 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
|
|
|
9065
9059
|
}
|
|
9066
9060
|
const modelName = normalizeModelName(mergedOptions.model);
|
|
9067
9061
|
// Check if the requested response format is compatible with the model
|
|
9068
|
-
if (responseFormat !==
|
|
9062
|
+
if (responseFormat !== 'text' && !supportsJsonOutput(modelName)) {
|
|
9069
9063
|
getLumicLogger().warn(`Model ${modelName} does not support JSON output. Will return error in the response.`);
|
|
9070
9064
|
return {
|
|
9071
9065
|
response: {
|
|
9072
|
-
error: `Model ${modelName} does not support JSON output format
|
|
9066
|
+
error: `Model ${modelName} does not support JSON output format.`
|
|
9073
9067
|
},
|
|
9074
9068
|
usage: {
|
|
9075
9069
|
prompt_tokens: 0,
|
|
9076
9070
|
completion_tokens: 0,
|
|
9077
9071
|
reasoning_tokens: 0,
|
|
9078
|
-
provider:
|
|
9072
|
+
provider: 'deepseek',
|
|
9079
9073
|
model: modelName,
|
|
9080
9074
|
cached_tokens: 0,
|
|
9081
9075
|
cost: 0,
|
|
@@ -9084,19 +9078,17 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
|
|
|
9084
9078
|
};
|
|
9085
9079
|
}
|
|
9086
9080
|
// Check if tools are requested with a model that doesn't support them
|
|
9087
|
-
if (mergedOptions.tools &&
|
|
9088
|
-
mergedOptions.tools.length > 0 &&
|
|
9089
|
-
!supportsToolCalling(modelName)) {
|
|
9081
|
+
if (mergedOptions.tools && mergedOptions.tools.length > 0 && !supportsToolCalling(modelName)) {
|
|
9090
9082
|
getLumicLogger().warn(`Model ${modelName} does not support tool calling. Will return error in the response.`);
|
|
9091
9083
|
return {
|
|
9092
9084
|
response: {
|
|
9093
|
-
error: `Model ${modelName} does not support tool calling
|
|
9085
|
+
error: `Model ${modelName} does not support tool calling.`
|
|
9094
9086
|
},
|
|
9095
9087
|
usage: {
|
|
9096
9088
|
prompt_tokens: 0,
|
|
9097
9089
|
completion_tokens: 0,
|
|
9098
9090
|
reasoning_tokens: 0,
|
|
9099
|
-
provider:
|
|
9091
|
+
provider: 'deepseek',
|
|
9100
9092
|
model: modelName,
|
|
9101
9093
|
cached_tokens: 0,
|
|
9102
9094
|
cost: 0,
|
|
@@ -9108,7 +9100,7 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
|
|
|
9108
9100
|
const completion = await createDeepseekCompletion(content, responseFormat, mergedOptions);
|
|
9109
9101
|
// Track cost in the global cost tracker. Pass cached tokens through so the
|
|
9110
9102
|
// discounted cached-input pricing tier is applied.
|
|
9111
|
-
getLLMCostTracker().trackUsage(
|
|
9103
|
+
getLLMCostTracker().trackUsage('deepseek', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens);
|
|
9112
9104
|
// Handle tool calls similarly to OpenAI
|
|
9113
9105
|
if (completion.tool_calls && completion.tool_calls.length > 0) {
|
|
9114
9106
|
const toolCallResponse = {
|
|
@@ -9124,10 +9116,10 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
|
|
|
9124
9116
|
prompt_tokens: completion.usage.prompt_tokens,
|
|
9125
9117
|
completion_tokens: completion.usage.completion_tokens,
|
|
9126
9118
|
reasoning_tokens: 0, // Deepseek doesn't provide reasoning tokens separately
|
|
9127
|
-
provider:
|
|
9119
|
+
provider: 'deepseek',
|
|
9128
9120
|
model: completion.model,
|
|
9129
9121
|
cached_tokens: completion.usage.cached_tokens,
|
|
9130
|
-
cost: calculateCost(
|
|
9122
|
+
cost: calculateCost('deepseek', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
|
|
9131
9123
|
},
|
|
9132
9124
|
tool_calls: completion.tool_calls,
|
|
9133
9125
|
};
|
|
@@ -9135,7 +9127,7 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
|
|
|
9135
9127
|
// Handle regular responses
|
|
9136
9128
|
const parsedResponse = await parseResponse(completion.content, responseFormat);
|
|
9137
9129
|
if (parsedResponse === null) {
|
|
9138
|
-
throw new Error(
|
|
9130
|
+
throw new Error('Failed to parse Deepseek response');
|
|
9139
9131
|
}
|
|
9140
9132
|
return {
|
|
9141
9133
|
response: parsedResponse,
|
|
@@ -9143,10 +9135,10 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
|
|
|
9143
9135
|
prompt_tokens: completion.usage.prompt_tokens,
|
|
9144
9136
|
completion_tokens: completion.usage.completion_tokens,
|
|
9145
9137
|
reasoning_tokens: 0, // Deepseek doesn't provide reasoning tokens separately
|
|
9146
|
-
provider:
|
|
9138
|
+
provider: 'deepseek',
|
|
9147
9139
|
model: completion.model,
|
|
9148
9140
|
cached_tokens: completion.usage.cached_tokens,
|
|
9149
|
-
cost: calculateCost(
|
|
9141
|
+
cost: calculateCost('deepseek', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
|
|
9150
9142
|
},
|
|
9151
9143
|
tool_calls: completion.tool_calls,
|
|
9152
9144
|
};
|
|
@@ -9156,13 +9148,13 @@ const makeDeepseekCall = async (content, responseFormat = "json", options = {})
|
|
|
9156
9148
|
getLumicLogger().error(`Error in Deepseek API call: ${sanitizeError(error)}`);
|
|
9157
9149
|
return {
|
|
9158
9150
|
response: {
|
|
9159
|
-
error: sanitizeError(error)
|
|
9151
|
+
error: sanitizeError(error)
|
|
9160
9152
|
},
|
|
9161
9153
|
usage: {
|
|
9162
9154
|
prompt_tokens: 0,
|
|
9163
9155
|
completion_tokens: 0,
|
|
9164
9156
|
reasoning_tokens: 0,
|
|
9165
|
-
provider:
|
|
9157
|
+
provider: 'deepseek',
|
|
9166
9158
|
model: modelName,
|
|
9167
9159
|
cached_tokens: 0,
|
|
9168
9160
|
cost: 0,
|
|
@@ -9271,15 +9263,15 @@ const generateCacheKey = (auth, envVarsCheck = null) => {
|
|
|
9271
9263
|
if (auth) {
|
|
9272
9264
|
return `${auth.AWS_ACCESS_KEY_ID}:${auth.AWS_SECRET_ACCESS_KEY}:${auth.AWS_REGION}`;
|
|
9273
9265
|
}
|
|
9274
|
-
if (envVarsCheck?.source ===
|
|
9266
|
+
if (envVarsCheck?.source === 'lambda') {
|
|
9275
9267
|
// In Lambda, use function name as cache key since it uses IAM role
|
|
9276
9268
|
const secrets = getSecrets();
|
|
9277
|
-
return `lambda:${secrets.aws.lambdaFunctionName ||
|
|
9269
|
+
return `lambda:${secrets.aws.lambdaFunctionName || 'default'}`;
|
|
9278
9270
|
}
|
|
9279
9271
|
if (envVarsCheck?.vars) {
|
|
9280
9272
|
return `${envVarsCheck.vars.accessKeyId}:${envVarsCheck.vars.secretAccessKey}:${envVarsCheck.vars.region}`;
|
|
9281
9273
|
}
|
|
9282
|
-
return
|
|
9274
|
+
return 'default';
|
|
9283
9275
|
};
|
|
9284
9276
|
const validateEnvironmentVars = () => {
|
|
9285
9277
|
const secrets = getSecrets();
|
|
@@ -9292,37 +9284,37 @@ const validateEnvironmentVars = () => {
|
|
|
9292
9284
|
// We're in a Lambda environment
|
|
9293
9285
|
return {
|
|
9294
9286
|
isValid: true,
|
|
9295
|
-
source:
|
|
9296
|
-
vars: { accessKeyId:
|
|
9287
|
+
source: 'lambda',
|
|
9288
|
+
vars: { accessKeyId: '', secretAccessKey: '', region: '' } // No need for explicit credentials in Lambda
|
|
9297
9289
|
};
|
|
9298
9290
|
}
|
|
9299
9291
|
else if (areVarsComplete(secrets.aws.myAccessKeyId, secrets.aws.mySecretAccessKey, secrets.aws.myRegion)) {
|
|
9300
9292
|
return {
|
|
9301
9293
|
isValid: true,
|
|
9302
|
-
source:
|
|
9294
|
+
source: 'my_prefix',
|
|
9303
9295
|
vars: {
|
|
9304
|
-
accessKeyId: secrets.aws.myAccessKeyId
|
|
9305
|
-
secretAccessKey: secrets.aws.mySecretAccessKey
|
|
9306
|
-
region: secrets.aws.myRegion
|
|
9307
|
-
}
|
|
9296
|
+
accessKeyId: secrets.aws.myAccessKeyId,
|
|
9297
|
+
secretAccessKey: secrets.aws.mySecretAccessKey,
|
|
9298
|
+
region: secrets.aws.myRegion
|
|
9299
|
+
}
|
|
9308
9300
|
};
|
|
9309
9301
|
}
|
|
9310
9302
|
else if (areVarsComplete(secrets.aws.accessKeyId, secrets.aws.secretAccessKey, secrets.aws.region)) {
|
|
9311
9303
|
return {
|
|
9312
9304
|
isValid: true,
|
|
9313
|
-
source:
|
|
9305
|
+
source: 'standard',
|
|
9314
9306
|
vars: {
|
|
9315
|
-
accessKeyId: secrets.aws.accessKeyId
|
|
9316
|
-
secretAccessKey: secrets.aws.secretAccessKey
|
|
9317
|
-
region: secrets.aws.region
|
|
9318
|
-
}
|
|
9307
|
+
accessKeyId: secrets.aws.accessKeyId,
|
|
9308
|
+
secretAccessKey: secrets.aws.secretAccessKey,
|
|
9309
|
+
region: secrets.aws.region
|
|
9310
|
+
}
|
|
9319
9311
|
};
|
|
9320
9312
|
}
|
|
9321
9313
|
// If we get here, no complete set of variables was found
|
|
9322
9314
|
return {
|
|
9323
9315
|
isValid: false,
|
|
9324
9316
|
source: null,
|
|
9325
|
-
missingVars: []
|
|
9317
|
+
missingVars: []
|
|
9326
9318
|
};
|
|
9327
9319
|
};
|
|
9328
9320
|
const initialiseAWSClient = (ClientClass, auth = null) => {
|
|
@@ -9331,52 +9323,48 @@ const initialiseAWSClient = (ClientClass, auth = null) => {
|
|
|
9331
9323
|
const requestTimeout = ClientClass === S3Client ? AWS_S3_TIMEOUT_MS : AWS_LAMBDA_TIMEOUT_MS;
|
|
9332
9324
|
// Case 1: Explicit auth provided
|
|
9333
9325
|
if (auth) {
|
|
9334
|
-
if (typeof auth !==
|
|
9335
|
-
throw new Error(
|
|
9326
|
+
if (typeof auth !== 'object' || auth === null) {
|
|
9327
|
+
throw new Error('Auth parameter must be an object');
|
|
9336
9328
|
}
|
|
9337
|
-
const requiredAuthFields = [
|
|
9338
|
-
|
|
9339
|
-
"AWS_SECRET_ACCESS_KEY",
|
|
9340
|
-
"AWS_REGION",
|
|
9341
|
-
];
|
|
9342
|
-
const missingFields = requiredAuthFields.filter((field) => !auth[field]);
|
|
9329
|
+
const requiredAuthFields = ['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AWS_REGION'];
|
|
9330
|
+
const missingFields = requiredAuthFields.filter(field => !auth[field]);
|
|
9343
9331
|
if (missingFields.length > 0) {
|
|
9344
|
-
throw new Error(`Auth object is missing required fields: ${missingFields.join(
|
|
9332
|
+
throw new Error(`Auth object is missing required fields: ${missingFields.join(', ')}`);
|
|
9345
9333
|
}
|
|
9346
9334
|
return new ClientClass({
|
|
9347
9335
|
region: auth.AWS_REGION,
|
|
9348
9336
|
credentials: {
|
|
9349
9337
|
accessKeyId: auth.AWS_ACCESS_KEY_ID,
|
|
9350
|
-
secretAccessKey: auth.AWS_SECRET_ACCESS_KEY
|
|
9338
|
+
secretAccessKey: auth.AWS_SECRET_ACCESS_KEY
|
|
9351
9339
|
},
|
|
9352
9340
|
requestHandler: {
|
|
9353
|
-
requestTimeout
|
|
9354
|
-
}
|
|
9341
|
+
requestTimeout
|
|
9342
|
+
}
|
|
9355
9343
|
});
|
|
9356
9344
|
}
|
|
9357
9345
|
// Case 2: Check environment variables
|
|
9358
9346
|
const envVarsCheck = validateEnvironmentVars();
|
|
9359
9347
|
if (!envVarsCheck.isValid) {
|
|
9360
|
-
throw new Error(
|
|
9348
|
+
throw new Error('No valid AWS credentials found in environment variables');
|
|
9361
9349
|
}
|
|
9362
9350
|
// Case 2a: Lambda execution environment
|
|
9363
|
-
if (envVarsCheck.source ===
|
|
9351
|
+
if (envVarsCheck.source === 'lambda') {
|
|
9364
9352
|
return new ClientClass({
|
|
9365
9353
|
requestHandler: {
|
|
9366
|
-
requestTimeout
|
|
9367
|
-
}
|
|
9354
|
+
requestTimeout
|
|
9355
|
+
}
|
|
9368
9356
|
}); // AWS SDK will automatically use Lambda role credentials
|
|
9369
9357
|
}
|
|
9370
9358
|
// Case 2b: MY_ prefixed vars or standard AWS vars
|
|
9371
9359
|
return new ClientClass({
|
|
9372
|
-
region: envVarsCheck.vars
|
|
9360
|
+
region: envVarsCheck.vars.region,
|
|
9373
9361
|
credentials: {
|
|
9374
|
-
accessKeyId: envVarsCheck.vars
|
|
9375
|
-
secretAccessKey: envVarsCheck.vars
|
|
9362
|
+
accessKeyId: envVarsCheck.vars.accessKeyId,
|
|
9363
|
+
secretAccessKey: envVarsCheck.vars.secretAccessKey
|
|
9376
9364
|
},
|
|
9377
9365
|
requestHandler: {
|
|
9378
|
-
requestTimeout
|
|
9379
|
-
}
|
|
9366
|
+
requestTimeout
|
|
9367
|
+
}
|
|
9380
9368
|
});
|
|
9381
9369
|
}
|
|
9382
9370
|
catch (error) {
|
|
@@ -9389,9 +9377,7 @@ const initialiseS3Client = (auth = null) => {
|
|
|
9389
9377
|
const envVarsCheck = auth ? null : validateEnvironmentVars();
|
|
9390
9378
|
const cacheKey = generateCacheKey(auth, envVarsCheck);
|
|
9391
9379
|
if (s3ClientCache.has(cacheKey)) {
|
|
9392
|
-
|
|
9393
|
-
if (cached)
|
|
9394
|
-
return cached;
|
|
9380
|
+
return s3ClientCache.get(cacheKey);
|
|
9395
9381
|
}
|
|
9396
9382
|
// Create new client and cache it
|
|
9397
9383
|
const client = initialiseAWSClient(S3Client, auth);
|
|
@@ -9403,9 +9389,7 @@ const initialiseLambdaClient = (auth = null) => {
|
|
|
9403
9389
|
const envVarsCheck = auth ? null : validateEnvironmentVars();
|
|
9404
9390
|
const cacheKey = generateCacheKey(auth, envVarsCheck);
|
|
9405
9391
|
if (lambdaClientCache.has(cacheKey)) {
|
|
9406
|
-
|
|
9407
|
-
if (cached)
|
|
9408
|
-
return cached;
|
|
9392
|
+
return lambdaClientCache.get(cacheKey);
|
|
9409
9393
|
}
|
|
9410
9394
|
// Create new client and cache it
|
|
9411
9395
|
const client = initialiseAWSClient(Lambda, auth);
|
|
@@ -9905,17 +9889,15 @@ const isRetryableS3Error = (error) => {
|
|
|
9905
9889
|
if (error instanceof Error) {
|
|
9906
9890
|
const message = error.message;
|
|
9907
9891
|
// Retry on throttling
|
|
9908
|
-
if (message.includes(
|
|
9892
|
+
if (message.includes('SlowDown') || message.includes('RequestTimeout')) {
|
|
9909
9893
|
return true;
|
|
9910
9894
|
}
|
|
9911
9895
|
// Retry on 5xx server errors
|
|
9912
|
-
if (message.includes(
|
|
9913
|
-
message.includes("ServiceUnavailable")) {
|
|
9896
|
+
if (message.includes('InternalError') || message.includes('ServiceUnavailable')) {
|
|
9914
9897
|
return true;
|
|
9915
9898
|
}
|
|
9916
9899
|
// Retry on connection errors
|
|
9917
|
-
if (message.includes(
|
|
9918
|
-
message.includes("NetworkingError")) {
|
|
9900
|
+
if (message.includes('RequestTimeTooSkewed') || message.includes('NetworkingError')) {
|
|
9919
9901
|
return true;
|
|
9920
9902
|
}
|
|
9921
9903
|
}
|
|
@@ -9929,11 +9911,10 @@ const generateRandomHash = (length) => {
|
|
|
9929
9911
|
};
|
|
9930
9912
|
function generateDateTimeStamp() {
|
|
9931
9913
|
const now = new Date();
|
|
9932
|
-
return now
|
|
9933
|
-
.
|
|
9934
|
-
.replace(
|
|
9935
|
-
.replace(
|
|
9936
|
-
.replace(/:/g, "-");
|
|
9914
|
+
return now.toISOString()
|
|
9915
|
+
.replace(/T/, '-')
|
|
9916
|
+
.replace(/\..+/, '')
|
|
9917
|
+
.replace(/:/g, '-');
|
|
9937
9918
|
}
|
|
9938
9919
|
const isValidBucketName = (name) => {
|
|
9939
9920
|
const regex = /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/;
|
|
@@ -9964,7 +9945,7 @@ async function uploadFile(s3Client, bucketName, filePath, destinationPath, isPub
|
|
|
9964
9945
|
Bucket: bucketName,
|
|
9965
9946
|
Key: s3Key,
|
|
9966
9947
|
Body: fileContent,
|
|
9967
|
-
ACL: isPublic ?
|
|
9948
|
+
ACL: isPublic ? 'public-read' : undefined
|
|
9968
9949
|
});
|
|
9969
9950
|
await withRetry(() => s3Client.send(command), {
|
|
9970
9951
|
maxRetries: 3,
|
|
@@ -9984,7 +9965,7 @@ async function uploadDirectory(s3Client, bucketName, dirPath, destinationPath, i
|
|
|
9984
9965
|
Bucket: bucketName,
|
|
9985
9966
|
Key: s3Key,
|
|
9986
9967
|
Body: fileContent,
|
|
9987
|
-
ACL: isPublic ?
|
|
9968
|
+
ACL: isPublic ? 'public-read' : undefined
|
|
9988
9969
|
});
|
|
9989
9970
|
await withRetry(() => s3Client.send(command), {
|
|
9990
9971
|
maxRetries: 3,
|
|
@@ -10000,7 +9981,7 @@ async function zipDirectory(sourceDir, outPath) {
|
|
|
10000
9981
|
const zip = new AdmZip();
|
|
10001
9982
|
let fileCount = 0;
|
|
10002
9983
|
try {
|
|
10003
|
-
async function addFilesToZip(currentPath, relativePath =
|
|
9984
|
+
async function addFilesToZip(currentPath, relativePath = '') {
|
|
10004
9985
|
const files = await fs$1.readdir(currentPath, { withFileTypes: true });
|
|
10005
9986
|
for (const file of files) {
|
|
10006
9987
|
const filePath = path__default.join(currentPath, file.name);
|
|
@@ -10043,33 +10024,27 @@ async function downloadFromS3Helper(s3Client, bucketName, s3Path, localPath) {
|
|
|
10043
10024
|
Prefix: s3Path,
|
|
10044
10025
|
ContinuationToken: continuationToken,
|
|
10045
10026
|
});
|
|
10046
|
-
const listResponse =
|
|
10027
|
+
const listResponse = await withRetry(() => s3Client.send(listCommand), {
|
|
10047
10028
|
maxRetries: 3,
|
|
10048
10029
|
baseDelayMs: 1000,
|
|
10049
10030
|
maxDelayMs: 15000,
|
|
10050
10031
|
retryableErrors: isRetryableS3Error,
|
|
10051
|
-
}, `S3:ListObjects:${bucketName}`)
|
|
10032
|
+
}, `S3:ListObjects:${bucketName}`);
|
|
10052
10033
|
const objects = listResponse.Contents || [];
|
|
10053
10034
|
if (objects.length === 0) {
|
|
10054
10035
|
return { fileCount: 0, folderCount: 0, totalSize: 0, fileList: [] };
|
|
10055
10036
|
}
|
|
10056
|
-
if (objects.length === 1 && objects[0].Key?.endsWith(
|
|
10037
|
+
if (objects.length === 1 && objects[0].Key?.endsWith('.zip')) {
|
|
10057
10038
|
// It's a zip file, download and extract it
|
|
10058
10039
|
const zipPath = path__default.join(localPath, path__default.basename(objects[0].Key));
|
|
10059
10040
|
await downloadFile(s3Client, bucketName, objects[0].Key, zipPath);
|
|
10060
|
-
const { totalFiles, totalFolders, totalSize: extractedSize, fileList: extractedFileList
|
|
10041
|
+
const { totalFiles, totalFolders, totalSize: extractedSize, fileList: extractedFileList } = await unzipFile(zipPath, localPath);
|
|
10061
10042
|
await fs$1.unlink(zipPath);
|
|
10062
|
-
return {
|
|
10063
|
-
fileCount: totalFiles,
|
|
10064
|
-
folderCount: totalFolders,
|
|
10065
|
-
totalSize: extractedSize,
|
|
10066
|
-
fileList: extractedFileList,
|
|
10067
|
-
};
|
|
10043
|
+
return { fileCount: totalFiles, folderCount: totalFolders, totalSize: extractedSize, fileList: extractedFileList };
|
|
10068
10044
|
}
|
|
10069
10045
|
else {
|
|
10070
10046
|
// Non-zip files. Download files in batches.
|
|
10071
|
-
for (let i = 0; i < objects.length; i += 1000) {
|
|
10072
|
-
// AWS allows up to 1000 per request
|
|
10047
|
+
for (let i = 0; i < objects.length; i += 1000) { // AWS allows up to 1000 per request
|
|
10073
10048
|
const batch = objects.slice(i, i + 1000);
|
|
10074
10049
|
await Promise.all(batch.map(async (obj) => {
|
|
10075
10050
|
if (!obj.Key)
|
|
@@ -10085,9 +10060,7 @@ async function downloadFromS3Helper(s3Client, bucketName, s3Path, localPath) {
|
|
|
10085
10060
|
}
|
|
10086
10061
|
// Count folders
|
|
10087
10062
|
const relativePath = path__default.relative(localPath, path__default.dirname(localFilePath));
|
|
10088
|
-
if (relativePath &&
|
|
10089
|
-
!relativePath.startsWith("..") &&
|
|
10090
|
-
relativePath !== ".") {
|
|
10063
|
+
if (relativePath && !relativePath.startsWith('..') && relativePath !== '.') {
|
|
10091
10064
|
folderCount++;
|
|
10092
10065
|
}
|
|
10093
10066
|
}));
|
|
@@ -10108,7 +10081,7 @@ async function downloadFile(s3Client, bucketName, s3Key, localFilePath) {
|
|
|
10108
10081
|
await withRetry(async () => {
|
|
10109
10082
|
const { Body } = await s3Client.send(getCommand);
|
|
10110
10083
|
if (!Body)
|
|
10111
|
-
throw new Error(
|
|
10084
|
+
throw new Error('No body returned from S3');
|
|
10112
10085
|
const writeStream = createWriteStream(localFilePath);
|
|
10113
10086
|
await pipeline(Body, writeStream);
|
|
10114
10087
|
}, {
|
|
@@ -10164,19 +10137,17 @@ async function emptyBucket(s3Client, bucketName) {
|
|
|
10164
10137
|
Bucket: bucketName,
|
|
10165
10138
|
ContinuationToken: continuationToken,
|
|
10166
10139
|
});
|
|
10167
|
-
const response =
|
|
10140
|
+
const response = await withRetry(() => s3Client.send(listCommand), {
|
|
10168
10141
|
maxRetries: 3,
|
|
10169
10142
|
baseDelayMs: 1000,
|
|
10170
10143
|
maxDelayMs: 15000,
|
|
10171
10144
|
retryableErrors: isRetryableS3Error,
|
|
10172
|
-
}, `S3:ListObjects:${bucketName}`)
|
|
10145
|
+
}, `S3:ListObjects:${bucketName}`);
|
|
10173
10146
|
const { Contents, IsTruncated, NextContinuationToken } = response;
|
|
10174
10147
|
if (Contents && Contents.length > 0) {
|
|
10175
10148
|
const deleteCommand = new DeleteObjectsCommand({
|
|
10176
10149
|
Bucket: bucketName,
|
|
10177
|
-
Delete: {
|
|
10178
|
-
Objects: Contents.filter((c) => c.Key).map((c) => ({ Key: c.Key })),
|
|
10179
|
-
},
|
|
10150
|
+
Delete: { Objects: Contents.map(({ Key }) => ({ Key: Key })) },
|
|
10180
10151
|
});
|
|
10181
10152
|
await withRetry(() => s3Client.send(deleteCommand), {
|
|
10182
10153
|
maxRetries: 3,
|
|
@@ -23084,11 +23055,11 @@ let poolConfig = DEFAULT_POOL_CONFIG;
|
|
|
23084
23055
|
async function loadApolloModules() {
|
|
23085
23056
|
if (typeof window === "undefined" || process.env.AWS_EXECUTION_ENV) {
|
|
23086
23057
|
// Server-side (or Lambda): load the CommonJS‑based implementation.
|
|
23087
|
-
return (await import('./apollo-client.server-
|
|
23058
|
+
return (await import('./apollo-client.server-CXLiLmSz.js'));
|
|
23088
23059
|
}
|
|
23089
23060
|
else {
|
|
23090
23061
|
// Client-side: load the ESM‑based implementation.
|
|
23091
|
-
return (await import('./apollo-client.client-
|
|
23062
|
+
return (await import('./apollo-client.client-DLW-p46Y.js'));
|
|
23092
23063
|
}
|
|
23093
23064
|
}
|
|
23094
23065
|
/**
|
|
@@ -79115,7 +79086,7 @@ const RETRY_CONFIG = {
|
|
|
79115
79086
|
BASE_DELAY: 2000, // Increased base delay to 2 seconds
|
|
79116
79087
|
MAX_DELAY: 64000,
|
|
79117
79088
|
MAX_RETRIES: 5,
|
|
79118
|
-
MAX_RANDOM_DELAY: 1000
|
|
79089
|
+
MAX_RANDOM_DELAY: 1000
|
|
79119
79090
|
};
|
|
79120
79091
|
/**
|
|
79121
79092
|
* Determines if an error is related to Google Sheets API quotas or rate limits.
|
|
@@ -79141,10 +79112,10 @@ const RETRY_CONFIG = {
|
|
|
79141
79112
|
*/
|
|
79142
79113
|
function isQuotaError(error) {
|
|
79143
79114
|
const message = error.message.toLowerCase();
|
|
79144
|
-
return
|
|
79145
|
-
message.includes(
|
|
79146
|
-
message.includes(
|
|
79147
|
-
message.includes(
|
|
79115
|
+
return message.includes('quota exceeded') ||
|
|
79116
|
+
message.includes('rate limit') ||
|
|
79117
|
+
message.includes('429') ||
|
|
79118
|
+
message.includes('too many requests');
|
|
79148
79119
|
}
|
|
79149
79120
|
/**
|
|
79150
79121
|
* Implements exponential backoff retry mechanism for Google Sheets API calls.
|
|
@@ -79203,14 +79174,14 @@ async function withExponentialBackoff(operation) {
|
|
|
79203
79174
|
const exponentialDelay = Math.min(Math.pow(2, retries) * RETRY_CONFIG.BASE_DELAY + randomDelay, RETRY_CONFIG.MAX_DELAY);
|
|
79204
79175
|
logIfDebug(`Quota/Rate limit exceeded. Error: ${error.message}`);
|
|
79205
79176
|
logIfDebug(`Retrying in ${exponentialDelay}ms (attempt ${retries}/${RETRY_CONFIG.MAX_RETRIES})`);
|
|
79206
|
-
await new Promise(
|
|
79177
|
+
await new Promise(resolve => setTimeout(resolve, exponentialDelay));
|
|
79207
79178
|
}
|
|
79208
79179
|
}
|
|
79209
79180
|
}
|
|
79210
79181
|
function checkEnvVars() {
|
|
79211
79182
|
const secrets = getSecrets();
|
|
79212
79183
|
if (!secrets.googleSheets.clientEmail || !secrets.googleSheets.privateKey) {
|
|
79213
|
-
throw new Error(
|
|
79184
|
+
throw new Error('GOOGLE_SHEETS_CLIENT_EMAIL or GOOGLE_SHEETS_PRIVATE_KEY is not defined in environment variables.');
|
|
79214
79185
|
}
|
|
79215
79186
|
}
|
|
79216
79187
|
/**
|
|
@@ -79225,16 +79196,16 @@ async function getAuthClient() {
|
|
|
79225
79196
|
const secrets = getSecrets();
|
|
79226
79197
|
const credentials = {
|
|
79227
79198
|
client_email: secrets.googleSheets.clientEmail,
|
|
79228
|
-
private_key: secrets.googleSheets.privateKey?.replace(/\\n/g,
|
|
79199
|
+
private_key: secrets.googleSheets.privateKey?.replace(/\\n/g, '\n'),
|
|
79229
79200
|
};
|
|
79230
79201
|
const auth = new GoogleAuth({
|
|
79231
79202
|
credentials,
|
|
79232
|
-
scopes: [
|
|
79203
|
+
scopes: ['https://www.googleapis.com/auth/spreadsheets'],
|
|
79233
79204
|
});
|
|
79234
79205
|
return auth.getClient();
|
|
79235
79206
|
}
|
|
79236
79207
|
catch (error) {
|
|
79237
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
79208
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
79238
79209
|
logIfDebug(`Error initializing Google Sheets auth: ${errorMessage}`);
|
|
79239
79210
|
throw new Error(`Failed to initialize Google Sheets auth: ${errorMessage}`);
|
|
79240
79211
|
}
|
|
@@ -79246,7 +79217,7 @@ async function getSheetsClient() {
|
|
|
79246
79217
|
const auth = await getAuthClient();
|
|
79247
79218
|
return new buildExports.sheets_v4.Sheets({
|
|
79248
79219
|
auth,
|
|
79249
|
-
timeout: GOOGLE_SHEETS_TIMEOUT_MS
|
|
79220
|
+
timeout: GOOGLE_SHEETS_TIMEOUT_MS
|
|
79250
79221
|
});
|
|
79251
79222
|
}
|
|
79252
79223
|
/**
|
|
@@ -79279,7 +79250,7 @@ function escapeSheetName(sheetName) {
|
|
|
79279
79250
|
* @param startColumn Optional starting column (defaults to 'A')
|
|
79280
79251
|
* @returns Promise resolving to response with success status and row details
|
|
79281
79252
|
*/
|
|
79282
|
-
async function addRow(config, values, startColumn =
|
|
79253
|
+
async function addRow(config, values, startColumn = 'A') {
|
|
79283
79254
|
try {
|
|
79284
79255
|
const sheets = await getSheetsClient();
|
|
79285
79256
|
const escapedSheetName = escapeSheetName(config.sheetName);
|
|
@@ -79287,7 +79258,7 @@ async function addRow(config, values, startColumn = "A") {
|
|
|
79287
79258
|
const response = await withExponentialBackoff(() => sheets.spreadsheets.values.append({
|
|
79288
79259
|
spreadsheetId: config.spreadsheetId,
|
|
79289
79260
|
range,
|
|
79290
|
-
valueInputOption:
|
|
79261
|
+
valueInputOption: 'USER_ENTERED',
|
|
79291
79262
|
requestBody: {
|
|
79292
79263
|
values: [values],
|
|
79293
79264
|
},
|
|
@@ -79295,9 +79266,9 @@ async function addRow(config, values, startColumn = "A") {
|
|
|
79295
79266
|
//logIfDebug(`Added row to sheet ${config.sheetName}: ${values.join(', ')}`);
|
|
79296
79267
|
const rowNumber = response.data.updates?.updatedRange
|
|
79297
79268
|
? parseInt(response.data.updates.updatedRange
|
|
79298
|
-
.split(
|
|
79299
|
-
.split(
|
|
79300
|
-
.replace(/[^0-9]/g,
|
|
79269
|
+
.split('!')[1]
|
|
79270
|
+
.split(':')[0]
|
|
79271
|
+
.replace(/[^0-9]/g, ''))
|
|
79301
79272
|
: 0;
|
|
79302
79273
|
return {
|
|
79303
79274
|
success: true,
|
|
@@ -79308,7 +79279,7 @@ async function addRow(config, values, startColumn = "A") {
|
|
|
79308
79279
|
};
|
|
79309
79280
|
}
|
|
79310
79281
|
catch (error) {
|
|
79311
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
79282
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
79312
79283
|
logIfDebug(`Error adding row to sheet: ${errorMessage}`);
|
|
79313
79284
|
return {
|
|
79314
79285
|
success: false,
|
|
@@ -79350,7 +79321,7 @@ async function addSheet(spreadsheetId, sheetTitle) {
|
|
|
79350
79321
|
};
|
|
79351
79322
|
}
|
|
79352
79323
|
catch (error) {
|
|
79353
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
79324
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
79354
79325
|
logIfDebug(`Error adding sheet: ${errorMessage}`);
|
|
79355
79326
|
return {
|
|
79356
79327
|
success: false,
|
|
@@ -79374,7 +79345,7 @@ async function writeCell(config, cell, value) {
|
|
|
79374
79345
|
await withExponentialBackoff(() => sheets.spreadsheets.values.update({
|
|
79375
79346
|
spreadsheetId: config.spreadsheetId,
|
|
79376
79347
|
range,
|
|
79377
|
-
valueInputOption:
|
|
79348
|
+
valueInputOption: 'USER_ENTERED',
|
|
79378
79349
|
requestBody: {
|
|
79379
79350
|
values: [[value]],
|
|
79380
79351
|
},
|
|
@@ -79388,7 +79359,7 @@ async function writeCell(config, cell, value) {
|
|
|
79388
79359
|
};
|
|
79389
79360
|
}
|
|
79390
79361
|
catch (error) {
|
|
79391
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
79362
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
79392
79363
|
logIfDebug(`Error writing to cell: ${errorMessage}`);
|
|
79393
79364
|
return {
|
|
79394
79365
|
success: false,
|
|
@@ -79412,11 +79383,8 @@ async function getCell(config, cell) {
|
|
|
79412
79383
|
spreadsheetId: config.spreadsheetId,
|
|
79413
79384
|
range,
|
|
79414
79385
|
}));
|
|
79415
|
-
const
|
|
79416
|
-
|
|
79417
|
-
? null
|
|
79418
|
-
: rawValue;
|
|
79419
|
-
logIfDebug(`Read value from cell ${range}: ${String(value)}`);
|
|
79386
|
+
const value = response.data.values?.[0]?.[0];
|
|
79387
|
+
logIfDebug(`Read value from cell ${range}: ${value}`);
|
|
79420
79388
|
return {
|
|
79421
79389
|
success: true,
|
|
79422
79390
|
data: {
|
|
@@ -79425,7 +79393,7 @@ async function getCell(config, cell) {
|
|
|
79425
79393
|
};
|
|
79426
79394
|
}
|
|
79427
79395
|
catch (error) {
|
|
79428
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
79396
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
79429
79397
|
logIfDebug(`Error reading from cell: ${errorMessage}`);
|
|
79430
79398
|
return {
|
|
79431
79399
|
success: false,
|
|
@@ -79458,11 +79426,10 @@ function convertA1ToRowCol(a1Notation) {
|
|
|
79458
79426
|
* @returns Padded array of arrays
|
|
79459
79427
|
*/
|
|
79460
79428
|
function padArrays(arrays) {
|
|
79461
|
-
const maxLength = Math.max(...arrays.map(
|
|
79462
|
-
return arrays.map(
|
|
79429
|
+
const maxLength = Math.max(...arrays.map(arr => arr.length));
|
|
79430
|
+
return arrays.map(arr => {
|
|
79463
79431
|
if (arr.length < maxLength) {
|
|
79464
|
-
|
|
79465
|
-
return [...arr, ...padding];
|
|
79432
|
+
return [...arr, ...Array(maxLength - arr.length).fill('')];
|
|
79466
79433
|
}
|
|
79467
79434
|
return arr;
|
|
79468
79435
|
});
|
|
@@ -79474,10 +79441,10 @@ function padArrays(arrays) {
|
|
|
79474
79441
|
* @param startCell Optional starting cell in A1 notation (defaults to 'A1')
|
|
79475
79442
|
* @returns Promise resolving to response with success status and update details
|
|
79476
79443
|
*/
|
|
79477
|
-
async function writeArray(config, data, startCell =
|
|
79444
|
+
async function writeArray(config, data, startCell = 'A1') {
|
|
79478
79445
|
try {
|
|
79479
79446
|
if (!data.length) {
|
|
79480
|
-
throw new Error(
|
|
79447
|
+
throw new Error('Data array is empty');
|
|
79481
79448
|
}
|
|
79482
79449
|
const sheets = await getSheetsClient();
|
|
79483
79450
|
const { row: startRow, column: startCol } = convertA1ToRowCol(startCell);
|
|
@@ -79493,7 +79460,7 @@ async function writeArray(config, data, startCell = "A1") {
|
|
|
79493
79460
|
const response = await withExponentialBackoff(() => sheets.spreadsheets.values.update({
|
|
79494
79461
|
spreadsheetId: config.spreadsheetId,
|
|
79495
79462
|
range,
|
|
79496
|
-
valueInputOption:
|
|
79463
|
+
valueInputOption: 'USER_ENTERED',
|
|
79497
79464
|
requestBody: {
|
|
79498
79465
|
values: paddedData,
|
|
79499
79466
|
},
|
|
@@ -79509,7 +79476,7 @@ async function writeArray(config, data, startCell = "A1") {
|
|
|
79509
79476
|
};
|
|
79510
79477
|
}
|
|
79511
79478
|
catch (error) {
|
|
79512
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
79479
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
79513
79480
|
logIfDebug(`Error writing array to sheet: ${errorMessage}`);
|
|
79514
79481
|
return {
|
|
79515
79482
|
success: false,
|
|
@@ -81616,4 +81583,4 @@ const lumic = {
|
|
|
81616
81583
|
};
|
|
81617
81584
|
|
|
81618
81585
|
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 };
|
|
81619
|
-
//# sourceMappingURL=index-
|
|
81586
|
+
//# sourceMappingURL=index-Bvc61lYS.js.map
|