@p-sw/brainbox 0.1.2-alpha.11 → 0.1.2-alpha.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +212 -87
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -489,15 +489,100 @@ function stripThinkTags(content) {
|
|
|
489
489
|
}
|
|
490
490
|
function parseModelJson(content) {
|
|
491
491
|
const cleaned = stripThinkTags(content);
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
492
|
+
const candidates = [cleaned];
|
|
493
|
+
const fence = cleaned.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
494
|
+
if (fence?.[1])
|
|
495
|
+
candidates.push(fence[1].trim());
|
|
496
|
+
const extracted = extractJsonSlice(cleaned);
|
|
497
|
+
if (extracted)
|
|
498
|
+
candidates.push(extracted);
|
|
499
|
+
let lastErr;
|
|
500
|
+
for (const candidate of candidates) {
|
|
501
|
+
try {
|
|
502
|
+
return decodeJsonValue(candidate);
|
|
503
|
+
} catch (err) {
|
|
504
|
+
lastErr = err;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
throw lastErr instanceof Error ? lastErr : new Error("Failed to parse model JSON");
|
|
508
|
+
}
|
|
509
|
+
function decodeJsonValue(text) {
|
|
510
|
+
let value = JSON.parse(text);
|
|
511
|
+
for (let i = 0;i < 2 && typeof value === "string"; i++) {
|
|
512
|
+
const inner = value.trim();
|
|
513
|
+
if (!(inner.startsWith("{") && inner.endsWith("}") || inner.startsWith("[") && inner.endsWith("]") || inner.startsWith('"') && inner.endsWith('"'))) {
|
|
514
|
+
break;
|
|
498
515
|
}
|
|
499
|
-
|
|
516
|
+
value = JSON.parse(inner);
|
|
500
517
|
}
|
|
518
|
+
return value;
|
|
519
|
+
}
|
|
520
|
+
function extractJsonSlice(text) {
|
|
521
|
+
const start = text.search(/[\{\[]/);
|
|
522
|
+
if (start < 0)
|
|
523
|
+
return;
|
|
524
|
+
const open = text[start];
|
|
525
|
+
const close = open === "{" ? "}" : "]";
|
|
526
|
+
let depth = 0;
|
|
527
|
+
let inString = false;
|
|
528
|
+
let escape = false;
|
|
529
|
+
for (let i = start;i < text.length; i++) {
|
|
530
|
+
const ch = text[i];
|
|
531
|
+
if (inString) {
|
|
532
|
+
if (escape) {
|
|
533
|
+
escape = false;
|
|
534
|
+
} else if (ch === "\\") {
|
|
535
|
+
escape = true;
|
|
536
|
+
} else if (ch === '"') {
|
|
537
|
+
inString = false;
|
|
538
|
+
}
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
if (ch === '"') {
|
|
542
|
+
inString = true;
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
if (ch === open)
|
|
546
|
+
depth++;
|
|
547
|
+
else if (ch === close) {
|
|
548
|
+
depth--;
|
|
549
|
+
if (depth === 0)
|
|
550
|
+
return text.slice(start, i + 1);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
function schemaToolName(name) {
|
|
556
|
+
const cleaned = name.replace(/[^a-zA-Z0-9_]/g, "_").replace(/^(\d)/, "_$1");
|
|
557
|
+
return cleaned.length > 0 ? cleaned : "submit_result";
|
|
558
|
+
}
|
|
559
|
+
function buildStructuredJsonRequest(options) {
|
|
560
|
+
const toolName = schemaToolName(options.jsonSchemaName);
|
|
561
|
+
return {
|
|
562
|
+
toolName,
|
|
563
|
+
tool: {
|
|
564
|
+
name: toolName,
|
|
565
|
+
description: `Submit the structured ${options.jsonSchemaName} result.`,
|
|
566
|
+
parameters: options.jsonSchema ?? {
|
|
567
|
+
type: "object",
|
|
568
|
+
additionalProperties: true
|
|
569
|
+
}
|
|
570
|
+
},
|
|
571
|
+
instruction: `${options.instruction}
|
|
572
|
+
|
|
573
|
+
You MUST call the \`${toolName}\` tool exactly once with the complete answer.
|
|
574
|
+
Do not write the JSON as plain text or inside a markdown code fence.`
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
function parseStructuredJsonResult(choice, toolName) {
|
|
578
|
+
const call = choice.message.toolCalls?.find((c) => c.function.name === toolName) ?? choice.message.toolCalls?.[0];
|
|
579
|
+
if (call?.function.arguments) {
|
|
580
|
+
return parseModelJson(call.function.arguments);
|
|
581
|
+
}
|
|
582
|
+
if (choice.message.content) {
|
|
583
|
+
return parseModelJson(choice.message.content);
|
|
584
|
+
}
|
|
585
|
+
throw new Error("Empty response from model");
|
|
501
586
|
}
|
|
502
587
|
function readAuthString(auth, key, envName) {
|
|
503
588
|
const fromAuth = typeof auth?.[key] === "string" ? auth[key] : undefined;
|
|
@@ -781,6 +866,7 @@ class OpenAICompatibleExecutor extends LLMExecutor {
|
|
|
781
866
|
chatPath;
|
|
782
867
|
noBearerPrefix;
|
|
783
868
|
reasoningEffortInQuery;
|
|
869
|
+
supportsResponseFormat;
|
|
784
870
|
constructor(opts) {
|
|
785
871
|
super();
|
|
786
872
|
this.providerName = opts.providerName;
|
|
@@ -790,6 +876,7 @@ class OpenAICompatibleExecutor extends LLMExecutor {
|
|
|
790
876
|
this.chatPath = opts.chatPath ?? "/chat/completions";
|
|
791
877
|
this.noBearerPrefix = opts.noBearerPrefix ?? false;
|
|
792
878
|
this.reasoningEffortInQuery = opts.reasoningEffortInQuery ?? false;
|
|
879
|
+
this.supportsResponseFormat = opts.supportsResponseFormat ?? true;
|
|
793
880
|
this.models = {
|
|
794
881
|
conversation: opts.conversationModel,
|
|
795
882
|
identity: opts.identityModel
|
|
@@ -861,6 +948,9 @@ class OpenAICompatibleExecutor extends LLMExecutor {
|
|
|
861
948
|
return data;
|
|
862
949
|
}
|
|
863
950
|
async call(model, options) {
|
|
951
|
+
if ("jsonSchemaName" in options && !this.supportsResponseFormat) {
|
|
952
|
+
return this.callJsonViaTool(model, options);
|
|
953
|
+
}
|
|
864
954
|
const jsonMode = "jsonSchemaName" in options;
|
|
865
955
|
const reasoning = defaultReasoningEffort(options.reasoningEffort, model, this.models.identity);
|
|
866
956
|
log4.debug(`call: provider=${this.providerName} model=${model} jsonSchema=${jsonMode ? options.jsonSchemaName : "-"} msgLen=${options.message.length}`);
|
|
@@ -886,6 +976,19 @@ class OpenAICompatibleExecutor extends LLMExecutor {
|
|
|
886
976
|
log4.debug(`call: response ${content.length} chars`);
|
|
887
977
|
return jsonMode ? parseModelJson(content) : content;
|
|
888
978
|
}
|
|
979
|
+
async callJsonViaTool(model, options) {
|
|
980
|
+
const { toolName, tool, instruction } = buildStructuredJsonRequest(options);
|
|
981
|
+
log4.debug(`callJsonViaTool: provider=${this.providerName} model=${model} tool=${toolName}`);
|
|
982
|
+
const choice = await this.chatWithTools(model, {
|
|
983
|
+
caller: options.caller ?? options.jsonSchemaName,
|
|
984
|
+
instruction,
|
|
985
|
+
messages: [{ role: "user", content: options.message }],
|
|
986
|
+
tools: [tool],
|
|
987
|
+
reasoningEffort: "none",
|
|
988
|
+
parallelToolCalls: false
|
|
989
|
+
});
|
|
990
|
+
return parseStructuredJsonResult(choice, toolName);
|
|
991
|
+
}
|
|
889
992
|
async chatWithTools(model, options) {
|
|
890
993
|
const reasoning = defaultReasoningEffort(options.reasoningEffort, model, this.models.identity);
|
|
891
994
|
log4.debug(`chatWithTools: provider=${this.providerName} model=${model} msgs=${options.messages.length} tools=${options.tools.length}`);
|
|
@@ -1219,7 +1322,8 @@ function minimaxOpts(providerName, baseURL, opts) {
|
|
|
1219
1322
|
baseURL,
|
|
1220
1323
|
apiKey: opts.apiKey,
|
|
1221
1324
|
conversationModel: opts.conversationModel,
|
|
1222
|
-
identityModel: opts.identityModel
|
|
1325
|
+
identityModel: opts.identityModel,
|
|
1326
|
+
supportsResponseFormat: false
|
|
1223
1327
|
};
|
|
1224
1328
|
}
|
|
1225
1329
|
|
|
@@ -1295,7 +1399,8 @@ class LmStudioExecutor extends OpenAICompatibleExecutor {
|
|
|
1295
1399
|
baseURL: "http://127.0.0.1:1234/v1",
|
|
1296
1400
|
apiKey: opts.apiKey || "lm-studio",
|
|
1297
1401
|
conversationModel: opts.conversationModel,
|
|
1298
|
-
identityModel: opts.identityModel
|
|
1402
|
+
identityModel: opts.identityModel,
|
|
1403
|
+
supportsResponseFormat: false
|
|
1299
1404
|
});
|
|
1300
1405
|
}
|
|
1301
1406
|
}
|
|
@@ -1308,7 +1413,8 @@ class OllamaExecutor extends OpenAICompatibleExecutor {
|
|
|
1308
1413
|
baseURL: "http://127.0.0.1:11434/v1",
|
|
1309
1414
|
apiKey: opts.apiKey || "ollama",
|
|
1310
1415
|
conversationModel: opts.conversationModel,
|
|
1311
|
-
identityModel: opts.identityModel
|
|
1416
|
+
identityModel: opts.identityModel,
|
|
1417
|
+
supportsResponseFormat: false
|
|
1312
1418
|
});
|
|
1313
1419
|
}
|
|
1314
1420
|
}
|
|
@@ -1334,7 +1440,8 @@ class LlamaCppExecutor extends OpenAICompatibleExecutor {
|
|
|
1334
1440
|
baseURL: "http://127.0.0.1:8080/v1",
|
|
1335
1441
|
apiKey: opts.apiKey || "llama.cpp",
|
|
1336
1442
|
conversationModel: opts.conversationModel,
|
|
1337
|
-
identityModel: opts.identityModel
|
|
1443
|
+
identityModel: opts.identityModel,
|
|
1444
|
+
supportsResponseFormat: false
|
|
1338
1445
|
});
|
|
1339
1446
|
}
|
|
1340
1447
|
}
|
|
@@ -1573,17 +1680,17 @@ var AnthropicAuthSchema = z4.object({
|
|
|
1573
1680
|
apiVersion: z4.string().optional()
|
|
1574
1681
|
}).loose();
|
|
1575
1682
|
function toAnthropicMessages(messages) {
|
|
1576
|
-
let
|
|
1577
|
-
const
|
|
1683
|
+
let system2;
|
|
1684
|
+
const msgs2 = [];
|
|
1578
1685
|
for (const m of messages) {
|
|
1579
1686
|
if (m.role === "system") {
|
|
1580
|
-
|
|
1687
|
+
system2 = (system2 ? system2 + `
|
|
1581
1688
|
|
|
1582
1689
|
` : "") + m.content;
|
|
1583
1690
|
continue;
|
|
1584
1691
|
}
|
|
1585
1692
|
if (m.role === "user") {
|
|
1586
|
-
|
|
1693
|
+
msgs2.push({ role: "user", content: m.content });
|
|
1587
1694
|
continue;
|
|
1588
1695
|
}
|
|
1589
1696
|
if (m.role === "assistant") {
|
|
@@ -1606,11 +1713,11 @@ function toAnthropicMessages(messages) {
|
|
|
1606
1713
|
});
|
|
1607
1714
|
}
|
|
1608
1715
|
}
|
|
1609
|
-
|
|
1716
|
+
msgs2.push({ role: "assistant", content: blocks });
|
|
1610
1717
|
continue;
|
|
1611
1718
|
}
|
|
1612
1719
|
if (m.role === "tool") {
|
|
1613
|
-
|
|
1720
|
+
msgs2.push({
|
|
1614
1721
|
role: "user",
|
|
1615
1722
|
content: [
|
|
1616
1723
|
{
|
|
@@ -1622,7 +1729,7 @@ function toAnthropicMessages(messages) {
|
|
|
1622
1729
|
});
|
|
1623
1730
|
}
|
|
1624
1731
|
}
|
|
1625
|
-
return { system, msgs };
|
|
1732
|
+
return { system: system2, msgs: msgs2 };
|
|
1626
1733
|
}
|
|
1627
1734
|
function toAnthropicTool(t) {
|
|
1628
1735
|
return {
|
|
@@ -1669,10 +1776,25 @@ class AnthropicExecutor extends LLMExecutor {
|
|
|
1669
1776
|
return JSON.parse(responseRaw);
|
|
1670
1777
|
}
|
|
1671
1778
|
async call(model, options) {
|
|
1672
|
-
|
|
1779
|
+
if ("jsonSchemaName" in options) {
|
|
1780
|
+
const { toolName, tool, instruction } = buildStructuredJsonRequest({
|
|
1781
|
+
instruction: options.instruction,
|
|
1782
|
+
jsonSchemaName: options.jsonSchemaName,
|
|
1783
|
+
jsonSchema: options.jsonSchema
|
|
1784
|
+
});
|
|
1785
|
+
const choice = await this.chatWithTools(model, {
|
|
1786
|
+
caller: options.caller ?? options.jsonSchemaName,
|
|
1787
|
+
instruction,
|
|
1788
|
+
messages: [{ role: "user", content: options.message }],
|
|
1789
|
+
tools: [tool],
|
|
1790
|
+
reasoningEffort: "none",
|
|
1791
|
+
toolChoice: { type: "tool", name: toolName }
|
|
1792
|
+
});
|
|
1793
|
+
return parseStructuredJsonResult(choice, toolName);
|
|
1794
|
+
}
|
|
1673
1795
|
const reasoning = defaultReasoningEffort(options.reasoningEffort, model, this.models.identity);
|
|
1674
1796
|
const log5 = logger.child("llm:anthropic");
|
|
1675
|
-
log5.debug(`call: model=${model} jsonSchema
|
|
1797
|
+
log5.debug(`call: model=${model} jsonSchema=- msgLen=${options.message.length}`);
|
|
1676
1798
|
const outputCap = 4096;
|
|
1677
1799
|
const body = {
|
|
1678
1800
|
model,
|
|
@@ -1696,21 +1818,27 @@ class AnthropicExecutor extends LLMExecutor {
|
|
|
1696
1818
|
if (!text) {
|
|
1697
1819
|
throw new Error("Empty response from model");
|
|
1698
1820
|
}
|
|
1699
|
-
return
|
|
1821
|
+
return text;
|
|
1700
1822
|
}
|
|
1701
1823
|
async chatWithTools(model, options) {
|
|
1702
1824
|
const reasoning = defaultReasoningEffort(options.reasoningEffort, model, this.models.identity);
|
|
1703
1825
|
const log5 = logger.child("llm:anthropic");
|
|
1704
1826
|
log5.debug(`chatWithTools: model=${model} msgs=${options.messages.length} tools=${options.tools.length}`);
|
|
1705
|
-
const { system, msgs } = toAnthropicMessages(options.messages);
|
|
1827
|
+
const { system: system2, msgs: msgs2 } = toAnthropicMessages(options.messages);
|
|
1706
1828
|
const outputCap = 4096;
|
|
1707
1829
|
const body = {
|
|
1708
1830
|
model,
|
|
1709
1831
|
max_tokens: outputCap,
|
|
1710
|
-
system:
|
|
1711
|
-
messages:
|
|
1832
|
+
system: system2 ?? options.instruction,
|
|
1833
|
+
messages: msgs2,
|
|
1712
1834
|
tools: options.tools.map(toAnthropicTool)
|
|
1713
1835
|
};
|
|
1836
|
+
if (options.toolChoice) {
|
|
1837
|
+
body["tool_choice"] = {
|
|
1838
|
+
type: "tool",
|
|
1839
|
+
name: options.toolChoice.name
|
|
1840
|
+
};
|
|
1841
|
+
}
|
|
1714
1842
|
if (reasoning !== "none") {
|
|
1715
1843
|
const budget = REASONING_BUDGET[reasoning];
|
|
1716
1844
|
body["max_tokens"] = budget + outputCap;
|
|
@@ -1789,58 +1917,6 @@ function signRequest(opts) {
|
|
|
1789
1917
|
headers["X-Amz-Security-Token"] = opts.sessionToken;
|
|
1790
1918
|
return headers;
|
|
1791
1919
|
}
|
|
1792
|
-
function toAnthropicMessages2(messages) {
|
|
1793
|
-
let system;
|
|
1794
|
-
const msgs = [];
|
|
1795
|
-
for (const m of messages) {
|
|
1796
|
-
if (m.role === "system") {
|
|
1797
|
-
system = (system ? system + `
|
|
1798
|
-
|
|
1799
|
-
` : "") + m.content;
|
|
1800
|
-
continue;
|
|
1801
|
-
}
|
|
1802
|
-
if (m.role === "user") {
|
|
1803
|
-
msgs.push({ role: "user", content: [{ type: "text", text: m.content }] });
|
|
1804
|
-
continue;
|
|
1805
|
-
}
|
|
1806
|
-
if (m.role === "assistant") {
|
|
1807
|
-
const blocks = [];
|
|
1808
|
-
if (m.content)
|
|
1809
|
-
blocks.push({ type: "text", text: m.content });
|
|
1810
|
-
if (m.toolCalls) {
|
|
1811
|
-
for (const c of m.toolCalls) {
|
|
1812
|
-
let input = {};
|
|
1813
|
-
try {
|
|
1814
|
-
input = c.function.arguments ? JSON.parse(c.function.arguments) : {};
|
|
1815
|
-
} catch {
|
|
1816
|
-
input = {};
|
|
1817
|
-
}
|
|
1818
|
-
blocks.push({
|
|
1819
|
-
type: "tool_use",
|
|
1820
|
-
id: c.id,
|
|
1821
|
-
name: c.function.name,
|
|
1822
|
-
input
|
|
1823
|
-
});
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
msgs.push({ role: "assistant", content: blocks });
|
|
1827
|
-
continue;
|
|
1828
|
-
}
|
|
1829
|
-
if (m.role === "tool") {
|
|
1830
|
-
msgs.push({
|
|
1831
|
-
role: "user",
|
|
1832
|
-
content: [
|
|
1833
|
-
{
|
|
1834
|
-
type: "tool_result",
|
|
1835
|
-
tool_use_id: m.toolCallId,
|
|
1836
|
-
content: [{ type: "text", text: m.content }]
|
|
1837
|
-
}
|
|
1838
|
-
]
|
|
1839
|
-
});
|
|
1840
|
-
}
|
|
1841
|
-
}
|
|
1842
|
-
return { system, msgs };
|
|
1843
|
-
}
|
|
1844
1920
|
function toAnthropicTool2(t) {
|
|
1845
1921
|
return {
|
|
1846
1922
|
name: t.name,
|
|
@@ -1909,9 +1985,24 @@ class BedrockExecutor extends LLMExecutor {
|
|
|
1909
1985
|
return JSON.parse(responseRaw);
|
|
1910
1986
|
}
|
|
1911
1987
|
async call(model, options) {
|
|
1912
|
-
const jsonMode = "jsonSchemaName" in options;
|
|
1913
1988
|
const log5 = logger.child("llm:bedrock");
|
|
1914
|
-
|
|
1989
|
+
if ("jsonSchemaName" in options) {
|
|
1990
|
+
log5.debug(`call: model=${model} jsonSchema=${options.jsonSchemaName} via tool`);
|
|
1991
|
+
const { toolName, tool, instruction } = buildStructuredJsonRequest({
|
|
1992
|
+
instruction: options.instruction,
|
|
1993
|
+
jsonSchemaName: options.jsonSchemaName,
|
|
1994
|
+
jsonSchema: options.jsonSchema
|
|
1995
|
+
});
|
|
1996
|
+
const choice = await this.chatWithTools(model, {
|
|
1997
|
+
caller: options.caller ?? options.jsonSchemaName,
|
|
1998
|
+
instruction,
|
|
1999
|
+
messages: [{ role: "user", content: options.message }],
|
|
2000
|
+
tools: [tool],
|
|
2001
|
+
toolChoice: { type: "tool", name: toolName }
|
|
2002
|
+
});
|
|
2003
|
+
return parseStructuredJsonResult(choice, toolName);
|
|
2004
|
+
}
|
|
2005
|
+
log5.debug(`call: model=${model} jsonSchema=- msgLen=${options.message.length}`);
|
|
1915
2006
|
if (!model.startsWith("anthropic.") && !model.startsWith("us.anthropic.")) {
|
|
1916
2007
|
throw new Error(`bedrock provider currently only supports Anthropic models (got ${model})`);
|
|
1917
2008
|
}
|
|
@@ -1926,7 +2017,7 @@ class BedrockExecutor extends LLMExecutor {
|
|
|
1926
2017
|
if (!text) {
|
|
1927
2018
|
throw new Error("Empty response from model");
|
|
1928
2019
|
}
|
|
1929
|
-
return
|
|
2020
|
+
return text;
|
|
1930
2021
|
}
|
|
1931
2022
|
async chatWithTools(model, options) {
|
|
1932
2023
|
const log5 = logger.child("llm:bedrock");
|
|
@@ -1934,7 +2025,6 @@ class BedrockExecutor extends LLMExecutor {
|
|
|
1934
2025
|
if (!model.startsWith("anthropic.") && !model.startsWith("us.anthropic.")) {
|
|
1935
2026
|
throw new Error(`bedrock provider currently only supports Anthropic models (got ${model})`);
|
|
1936
2027
|
}
|
|
1937
|
-
const { system, msgs } = toAnthropicMessages2(options.messages);
|
|
1938
2028
|
const body = {
|
|
1939
2029
|
anthropic_version: "bedrock-2023-05-31",
|
|
1940
2030
|
max_tokens: 4096,
|
|
@@ -1942,6 +2032,12 @@ class BedrockExecutor extends LLMExecutor {
|
|
|
1942
2032
|
messages: msgs,
|
|
1943
2033
|
tools: options.tools.map(toAnthropicTool2)
|
|
1944
2034
|
};
|
|
2035
|
+
if (options.toolChoice) {
|
|
2036
|
+
body.tool_choice = {
|
|
2037
|
+
type: "tool",
|
|
2038
|
+
name: options.toolChoice.name
|
|
2039
|
+
};
|
|
2040
|
+
}
|
|
1945
2041
|
const data = await this.invoke(model, body, resolveLlmCaller(options));
|
|
1946
2042
|
const { text, toolCalls } = extractAnthropicContent(data);
|
|
1947
2043
|
return { message: { content: text || undefined, toolCalls } };
|
|
@@ -2208,9 +2304,23 @@ class GitLabDuoExecutor extends LLMExecutor {
|
|
|
2208
2304
|
return JSON.parse(responseRaw);
|
|
2209
2305
|
}
|
|
2210
2306
|
async call(model, options) {
|
|
2211
|
-
const jsonMode = "jsonSchemaName" in options;
|
|
2212
2307
|
const log5 = logger.child("llm:gitlab-duo");
|
|
2213
|
-
|
|
2308
|
+
if ("jsonSchemaName" in options) {
|
|
2309
|
+
log5.debug(`call: model=${model} jsonSchema=${options.jsonSchemaName} via tool`);
|
|
2310
|
+
const { toolName, tool, instruction } = buildStructuredJsonRequest({
|
|
2311
|
+
instruction: options.instruction,
|
|
2312
|
+
jsonSchemaName: options.jsonSchemaName,
|
|
2313
|
+
jsonSchema: options.jsonSchema
|
|
2314
|
+
});
|
|
2315
|
+
const choice = await this.chatWithTools(model, {
|
|
2316
|
+
caller: options.caller ?? options.jsonSchemaName,
|
|
2317
|
+
instruction,
|
|
2318
|
+
messages: [{ role: "user", content: options.message }],
|
|
2319
|
+
tools: [tool]
|
|
2320
|
+
});
|
|
2321
|
+
return parseStructuredJsonResult(choice, toolName);
|
|
2322
|
+
}
|
|
2323
|
+
log5.debug(`call: model=${model} jsonSchema=- msgLen=${options.message.length}`);
|
|
2214
2324
|
const body = {
|
|
2215
2325
|
model,
|
|
2216
2326
|
messages: [
|
|
@@ -2226,7 +2336,7 @@ class GitLabDuoExecutor extends LLMExecutor {
|
|
|
2226
2336
|
if (!content) {
|
|
2227
2337
|
throw new Error("Empty response from model");
|
|
2228
2338
|
}
|
|
2229
|
-
return
|
|
2339
|
+
return content;
|
|
2230
2340
|
}
|
|
2231
2341
|
async chatWithTools(model, options) {
|
|
2232
2342
|
const log5 = logger.child("llm:gitlab-duo");
|
|
@@ -2325,10 +2435,25 @@ class SnowflakeCortexExecutor extends LLMExecutor {
|
|
|
2325
2435
|
return JSON.parse(responseRaw);
|
|
2326
2436
|
}
|
|
2327
2437
|
async call(model, options) {
|
|
2328
|
-
const jsonMode = "jsonSchemaName" in options;
|
|
2329
|
-
const reasoning = defaultReasoningEffort(options.reasoningEffort, model, this.models.identity);
|
|
2330
2438
|
const log5 = logger.child("llm:snowflake-cortex");
|
|
2331
|
-
|
|
2439
|
+
if ("jsonSchemaName" in options) {
|
|
2440
|
+
log5.debug(`call: model=${model} jsonSchema=${options.jsonSchemaName} via tool`);
|
|
2441
|
+
const { toolName, tool, instruction } = buildStructuredJsonRequest({
|
|
2442
|
+
instruction: options.instruction,
|
|
2443
|
+
jsonSchemaName: options.jsonSchemaName,
|
|
2444
|
+
jsonSchema: options.jsonSchema
|
|
2445
|
+
});
|
|
2446
|
+
const choice = await this.chatWithTools(model, {
|
|
2447
|
+
caller: options.caller ?? options.jsonSchemaName,
|
|
2448
|
+
instruction,
|
|
2449
|
+
messages: [{ role: "user", content: options.message }],
|
|
2450
|
+
tools: [tool],
|
|
2451
|
+
reasoningEffort: "none"
|
|
2452
|
+
});
|
|
2453
|
+
return parseStructuredJsonResult(choice, toolName);
|
|
2454
|
+
}
|
|
2455
|
+
const reasoning = defaultReasoningEffort(options.reasoningEffort, model, this.models.identity);
|
|
2456
|
+
log5.debug(`call: model=${model} jsonSchema=- msgLen=${options.message.length}`);
|
|
2332
2457
|
const body = {
|
|
2333
2458
|
model,
|
|
2334
2459
|
messages: [
|
|
@@ -2347,7 +2472,7 @@ class SnowflakeCortexExecutor extends LLMExecutor {
|
|
|
2347
2472
|
if (!content) {
|
|
2348
2473
|
throw new Error("Empty response from model");
|
|
2349
2474
|
}
|
|
2350
|
-
return
|
|
2475
|
+
return content;
|
|
2351
2476
|
}
|
|
2352
2477
|
async chatWithTools(model, options) {
|
|
2353
2478
|
const log5 = logger.child("llm:snowflake-cortex");
|