@workglow/ai-provider 0.1.2 → 0.2.0
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/common/ToolCallParsers.d.ts +253 -0
- package/dist/common/ToolCallParsers.d.ts.map +1 -0
- package/dist/provider-anthropic/AnthropicProvider.d.ts +3 -2
- package/dist/provider-anthropic/AnthropicProvider.d.ts.map +1 -1
- package/dist/provider-anthropic/AnthropicQueuedProvider.d.ts +3 -2
- package/dist/provider-anthropic/AnthropicQueuedProvider.d.ts.map +1 -1
- package/dist/provider-anthropic/common/Anthropic_Client.d.ts.map +1 -1
- package/dist/provider-anthropic/common/Anthropic_JobRunFns.d.ts.map +1 -1
- package/dist/provider-anthropic/common/Anthropic_ToolCalling.d.ts +10 -0
- package/dist/provider-anthropic/common/Anthropic_ToolCalling.d.ts.map +1 -0
- package/dist/provider-anthropic/index.js +3 -4
- package/dist/provider-anthropic/index.js.map +3 -3
- package/dist/provider-anthropic/runtime.js +225 -8
- package/dist/provider-anthropic/runtime.js.map +9 -8
- package/dist/provider-chrome/WebBrowserProvider.d.ts +2 -1
- package/dist/provider-chrome/WebBrowserProvider.d.ts.map +1 -1
- package/dist/provider-chrome/WebBrowserQueuedProvider.d.ts +2 -1
- package/dist/provider-chrome/WebBrowserQueuedProvider.d.ts.map +1 -1
- package/dist/provider-chrome/index.js +2 -4
- package/dist/provider-chrome/index.js.map +3 -3
- package/dist/provider-chrome/runtime.js +3 -7
- package/dist/provider-chrome/runtime.js.map +4 -4
- package/dist/provider-gemini/GoogleGeminiProvider.d.ts +3 -2
- package/dist/provider-gemini/GoogleGeminiProvider.d.ts.map +1 -1
- package/dist/provider-gemini/GoogleGeminiQueuedProvider.d.ts +3 -2
- package/dist/provider-gemini/GoogleGeminiQueuedProvider.d.ts.map +1 -1
- package/dist/provider-gemini/common/Gemini_JobRunFns.d.ts.map +1 -1
- package/dist/provider-gemini/common/Gemini_ToolCalling.d.ts +10 -0
- package/dist/provider-gemini/common/Gemini_ToolCalling.d.ts.map +1 -0
- package/dist/provider-gemini/index.js +3 -4
- package/dist/provider-gemini/index.js.map +3 -3
- package/dist/provider-gemini/runtime.js +188 -8
- package/dist/provider-gemini/runtime.js.map +7 -6
- package/dist/provider-hf-inference/HfInferenceProvider.d.ts +3 -2
- package/dist/provider-hf-inference/HfInferenceProvider.d.ts.map +1 -1
- package/dist/provider-hf-inference/HfInferenceQueuedProvider.d.ts +3 -2
- package/dist/provider-hf-inference/HfInferenceQueuedProvider.d.ts.map +1 -1
- package/dist/provider-hf-inference/common/HFI_JobRunFns.d.ts.map +1 -1
- package/dist/provider-hf-inference/common/HFI_ToolCalling.d.ts +10 -0
- package/dist/provider-hf-inference/common/HFI_ToolCalling.d.ts.map +1 -0
- package/dist/provider-hf-inference/index.js +3 -4
- package/dist/provider-hf-inference/index.js.map +3 -3
- package/dist/provider-hf-inference/runtime.js +157 -8
- package/dist/provider-hf-inference/runtime.js.map +7 -6
- package/dist/provider-hf-transformers/HuggingFaceTransformersProvider.d.ts +3 -2
- package/dist/provider-hf-transformers/HuggingFaceTransformersProvider.d.ts.map +1 -1
- package/dist/provider-hf-transformers/HuggingFaceTransformersQueuedProvider.d.ts +3 -3
- package/dist/provider-hf-transformers/HuggingFaceTransformersQueuedProvider.d.ts.map +1 -1
- package/dist/provider-hf-transformers/common/HFT_JobRunFns.d.ts +52 -0
- package/dist/provider-hf-transformers/common/HFT_JobRunFns.d.ts.map +1 -1
- package/dist/provider-hf-transformers/common/HFT_Pipeline.d.ts +1 -1
- package/dist/provider-hf-transformers/common/HFT_Pipeline.d.ts.map +1 -1
- package/dist/provider-hf-transformers/common/HFT_StructuredGeneration.d.ts.map +1 -1
- package/dist/provider-hf-transformers/common/HFT_TextClassification.d.ts.map +1 -1
- package/dist/provider-hf-transformers/common/HFT_TextQuestionAnswer.d.ts.map +1 -1
- package/dist/provider-hf-transformers/common/HFT_TextSummary.d.ts.map +1 -1
- package/dist/provider-hf-transformers/common/HFT_TextTranslation.d.ts.map +1 -1
- package/dist/provider-hf-transformers/common/HFT_ToolCalling.d.ts +10 -0
- package/dist/provider-hf-transformers/common/HFT_ToolCalling.d.ts.map +1 -0
- package/dist/provider-hf-transformers/common/HFT_ToolMarkup.d.ts +20 -0
- package/dist/provider-hf-transformers/common/HFT_ToolMarkup.d.ts.map +1 -0
- package/dist/provider-hf-transformers/common/HFT_ToolParser.d.ts +67 -0
- package/dist/provider-hf-transformers/common/HFT_ToolParser.d.ts.map +1 -0
- package/dist/provider-hf-transformers/index.d.ts +1 -0
- package/dist/provider-hf-transformers/index.d.ts.map +1 -1
- package/dist/provider-hf-transformers/index.js +71 -7
- package/dist/provider-hf-transformers/index.js.map +7 -6
- package/dist/provider-hf-transformers/runtime.d.ts +1 -0
- package/dist/provider-hf-transformers/runtime.d.ts.map +1 -1
- package/dist/provider-hf-transformers/runtime.js +1244 -13
- package/dist/provider-hf-transformers/runtime.js.map +18 -15
- package/dist/provider-llamacpp/LlamaCppProvider.d.ts +3 -2
- package/dist/provider-llamacpp/LlamaCppProvider.d.ts.map +1 -1
- package/dist/provider-llamacpp/LlamaCppQueuedProvider.d.ts +3 -2
- package/dist/provider-llamacpp/LlamaCppQueuedProvider.d.ts.map +1 -1
- package/dist/provider-llamacpp/common/LlamaCpp_JobRunFns.d.ts.map +1 -1
- package/dist/provider-llamacpp/common/LlamaCpp_ToolCalling.d.ts +10 -0
- package/dist/provider-llamacpp/common/LlamaCpp_ToolCalling.d.ts.map +1 -0
- package/dist/provider-llamacpp/common/LlamaCpp_ToolParser.d.ts +35 -0
- package/dist/provider-llamacpp/common/LlamaCpp_ToolParser.d.ts.map +1 -0
- package/dist/provider-llamacpp/index.js +3 -4
- package/dist/provider-llamacpp/index.js.map +3 -3
- package/dist/provider-llamacpp/runtime.js +1394 -9
- package/dist/provider-llamacpp/runtime.js.map +11 -8
- package/dist/provider-ollama/OllamaProvider.d.ts +3 -2
- package/dist/provider-ollama/OllamaProvider.d.ts.map +1 -1
- package/dist/provider-ollama/OllamaQueuedProvider.d.ts +3 -2
- package/dist/provider-ollama/OllamaQueuedProvider.d.ts.map +1 -1
- package/dist/provider-ollama/common/Ollama_JobRunFns.browser.d.ts +47 -1
- package/dist/provider-ollama/common/Ollama_JobRunFns.browser.d.ts.map +1 -1
- package/dist/provider-ollama/common/Ollama_JobRunFns.d.ts +46 -0
- package/dist/provider-ollama/common/Ollama_JobRunFns.d.ts.map +1 -1
- package/dist/provider-ollama/common/Ollama_ToolCalling.d.ts +16 -0
- package/dist/provider-ollama/common/Ollama_ToolCalling.d.ts.map +1 -0
- package/dist/provider-ollama/index.browser.js +3 -4
- package/dist/provider-ollama/index.browser.js.map +3 -3
- package/dist/provider-ollama/index.js +3 -4
- package/dist/provider-ollama/index.js.map +3 -3
- package/dist/provider-ollama/runtime.browser.js +130 -8
- package/dist/provider-ollama/runtime.browser.js.map +8 -7
- package/dist/provider-ollama/runtime.js +125 -8
- package/dist/provider-ollama/runtime.js.map +8 -7
- package/dist/provider-openai/OpenAiProvider.d.ts +3 -2
- package/dist/provider-openai/OpenAiProvider.d.ts.map +1 -1
- package/dist/provider-openai/OpenAiQueuedProvider.d.ts +3 -2
- package/dist/provider-openai/OpenAiQueuedProvider.d.ts.map +1 -1
- package/dist/provider-openai/common/OpenAI_Client.d.ts.map +1 -1
- package/dist/provider-openai/common/OpenAI_JobRunFns.browser.d.ts.map +1 -1
- package/dist/provider-openai/common/OpenAI_JobRunFns.d.ts.map +1 -1
- package/dist/provider-openai/common/OpenAI_ToolCalling.d.ts +10 -0
- package/dist/provider-openai/common/OpenAI_ToolCalling.d.ts.map +1 -0
- package/dist/provider-openai/index.browser.js +3 -4
- package/dist/provider-openai/index.browser.js.map +3 -3
- package/dist/provider-openai/index.js +3 -4
- package/dist/provider-openai/index.js.map +3 -3
- package/dist/provider-openai/runtime.browser.js +138 -8
- package/dist/provider-openai/runtime.browser.js.map +8 -7
- package/dist/provider-openai/runtime.js +138 -8
- package/dist/provider-openai/runtime.js.map +8 -7
- package/dist/provider-tf-mediapipe/TensorFlowMediaPipeProvider.d.ts +2 -1
- package/dist/provider-tf-mediapipe/TensorFlowMediaPipeProvider.d.ts.map +1 -1
- package/dist/provider-tf-mediapipe/TensorFlowMediaPipeQueuedProvider.d.ts +2 -1
- package/dist/provider-tf-mediapipe/TensorFlowMediaPipeQueuedProvider.d.ts.map +1 -1
- package/dist/provider-tf-mediapipe/common/TFMP_Download.d.ts.map +1 -1
- package/dist/provider-tf-mediapipe/common/TFMP_ImageClassification.d.ts.map +1 -1
- package/dist/provider-tf-mediapipe/common/TFMP_Runtime.d.ts +1 -1
- package/dist/provider-tf-mediapipe/common/TFMP_Runtime.d.ts.map +1 -1
- package/dist/provider-tf-mediapipe/index.js.map +2 -2
- package/dist/provider-tf-mediapipe/runtime.js +2 -4
- package/dist/provider-tf-mediapipe/runtime.js.map +13 -13
- package/dist/test.d.ts +7 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +913 -0
- package/dist/test.js.map +10 -0
- package/package.json +24 -17
|
@@ -612,6 +612,71 @@ function parseOnnxQuantizations(params) {
|
|
|
612
612
|
return set !== undefined && set.size === allBaseNames.size;
|
|
613
613
|
});
|
|
614
614
|
}
|
|
615
|
+
// src/provider-hf-transformers/common/HFT_ToolMarkup.ts
|
|
616
|
+
function createToolCallMarkupFilter(emit) {
|
|
617
|
+
const OPEN_TAG = "<tool_call>";
|
|
618
|
+
const CLOSE_TAG = "</tool_call>";
|
|
619
|
+
let state = "text";
|
|
620
|
+
let pending = "";
|
|
621
|
+
function feed(token) {
|
|
622
|
+
if (state === "tag") {
|
|
623
|
+
pending += token;
|
|
624
|
+
const closeIdx = pending.indexOf(CLOSE_TAG);
|
|
625
|
+
if (closeIdx !== -1) {
|
|
626
|
+
const afterClose = pending.slice(closeIdx + CLOSE_TAG.length);
|
|
627
|
+
pending = "";
|
|
628
|
+
state = "text";
|
|
629
|
+
if (afterClose.length > 0) {
|
|
630
|
+
feed(afterClose);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
const combined = pending + token;
|
|
636
|
+
const openIdx = combined.indexOf(OPEN_TAG);
|
|
637
|
+
if (openIdx !== -1) {
|
|
638
|
+
const before = combined.slice(0, openIdx);
|
|
639
|
+
if (before.length > 0) {
|
|
640
|
+
emit(before);
|
|
641
|
+
}
|
|
642
|
+
pending = "";
|
|
643
|
+
state = "tag";
|
|
644
|
+
const afterOpen = combined.slice(openIdx + OPEN_TAG.length);
|
|
645
|
+
if (afterOpen.length > 0) {
|
|
646
|
+
feed(afterOpen);
|
|
647
|
+
}
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
let prefixLen = 0;
|
|
651
|
+
for (let len = Math.min(combined.length, OPEN_TAG.length - 1);len >= 1; len--) {
|
|
652
|
+
if (combined.endsWith(OPEN_TAG.slice(0, len))) {
|
|
653
|
+
prefixLen = len;
|
|
654
|
+
break;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
if (prefixLen > 0) {
|
|
658
|
+
const safe = combined.slice(0, combined.length - prefixLen);
|
|
659
|
+
if (safe.length > 0) {
|
|
660
|
+
emit(safe);
|
|
661
|
+
}
|
|
662
|
+
pending = combined.slice(combined.length - prefixLen);
|
|
663
|
+
} else {
|
|
664
|
+
if (combined.length > 0) {
|
|
665
|
+
emit(combined);
|
|
666
|
+
}
|
|
667
|
+
pending = "";
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
function flush() {
|
|
671
|
+
if (pending.length > 0 && state === "text") {
|
|
672
|
+
emit(pending);
|
|
673
|
+
pending = "";
|
|
674
|
+
}
|
|
675
|
+
pending = "";
|
|
676
|
+
state = "text";
|
|
677
|
+
}
|
|
678
|
+
return { feed, flush };
|
|
679
|
+
}
|
|
615
680
|
// src/provider-hf-transformers/common/HFT_InlineLifecycle.ts
|
|
616
681
|
async function clearHftInlinePipelineCache() {
|
|
617
682
|
const { clearPipelineCache: clearPipelineCache2 } = await Promise.resolve().then(() => (init_HFT_Pipeline(), exports_HFT_Pipeline));
|
|
@@ -1072,11 +1137,15 @@ ${schemaStr}
|
|
|
1072
1137
|
|
|
1073
1138
|
` + `Output ONLY the JSON object, no other text.`;
|
|
1074
1139
|
}
|
|
1140
|
+
function stripThinkingAndSpecialTokens(text) {
|
|
1141
|
+
return text.replace(/<think>[\s\S]*?<\/think>/g, "").replace(/<\|[a-z_]+\|>/g, "").trim();
|
|
1142
|
+
}
|
|
1075
1143
|
function extractJsonFromText(text) {
|
|
1144
|
+
const cleaned = stripThinkingAndSpecialTokens(text);
|
|
1076
1145
|
try {
|
|
1077
|
-
return JSON.parse(
|
|
1146
|
+
return JSON.parse(cleaned);
|
|
1078
1147
|
} catch {
|
|
1079
|
-
const match =
|
|
1148
|
+
const match = cleaned.match(/\{[\s\S]*\}/);
|
|
1080
1149
|
if (match) {
|
|
1081
1150
|
try {
|
|
1082
1151
|
return JSON.parse(match[0]);
|
|
@@ -1123,13 +1192,41 @@ var HFT_StructuredGeneration_Stream = async function* (input, model, signal) {
|
|
|
1123
1192
|
const queue = createStreamEventQueue();
|
|
1124
1193
|
const streamer = createStreamingTextStreamer(generateText.tokenizer, queue, TextStreamer, signal);
|
|
1125
1194
|
let fullText = "";
|
|
1195
|
+
let cleanedText = "";
|
|
1196
|
+
let inThinkBlock = false;
|
|
1197
|
+
let jsonStart = -1;
|
|
1126
1198
|
const originalPush = queue.push;
|
|
1127
1199
|
queue.push = (event) => {
|
|
1128
1200
|
if (event.type === "text-delta" && "textDelta" in event) {
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1201
|
+
const delta = event.textDelta;
|
|
1202
|
+
fullText += delta;
|
|
1203
|
+
let remaining = delta;
|
|
1204
|
+
while (remaining.length > 0) {
|
|
1205
|
+
if (inThinkBlock) {
|
|
1206
|
+
const closeIdx = remaining.indexOf("</think>");
|
|
1207
|
+
if (closeIdx !== -1) {
|
|
1208
|
+
inThinkBlock = false;
|
|
1209
|
+
remaining = remaining.slice(closeIdx + "</think>".length);
|
|
1210
|
+
} else {
|
|
1211
|
+
remaining = "";
|
|
1212
|
+
}
|
|
1213
|
+
} else {
|
|
1214
|
+
const openIdx = remaining.indexOf("<think>");
|
|
1215
|
+
if (openIdx !== -1) {
|
|
1216
|
+
cleanedText += remaining.slice(0, openIdx).replace(/<\|[a-z_]+\|>/g, "");
|
|
1217
|
+
inThinkBlock = true;
|
|
1218
|
+
remaining = remaining.slice(openIdx + "<think>".length);
|
|
1219
|
+
} else {
|
|
1220
|
+
cleanedText += remaining.replace(/<\|[a-z_]+\|>/g, "");
|
|
1221
|
+
remaining = "";
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
if (jsonStart === -1) {
|
|
1226
|
+
jsonStart = cleanedText.indexOf("{");
|
|
1227
|
+
}
|
|
1228
|
+
if (jsonStart !== -1) {
|
|
1229
|
+
const partial = parsePartialJson(cleanedText.slice(jsonStart));
|
|
1133
1230
|
if (partial !== undefined) {
|
|
1134
1231
|
originalPush({
|
|
1135
1232
|
type: "object-delta",
|
|
@@ -1480,6 +1577,1139 @@ var HFT_TextTranslation_Stream = async function* (input, model, signal) {
|
|
|
1480
1577
|
yield { type: "finish", data: { target_lang: input.target_lang } };
|
|
1481
1578
|
};
|
|
1482
1579
|
|
|
1580
|
+
// src/provider-hf-transformers/common/HFT_ToolCalling.ts
|
|
1581
|
+
init_HFT_Pipeline();
|
|
1582
|
+
import {
|
|
1583
|
+
buildToolDescription,
|
|
1584
|
+
filterValidToolCalls,
|
|
1585
|
+
toTextFlatMessages
|
|
1586
|
+
} from "@workglow/ai/worker";
|
|
1587
|
+
|
|
1588
|
+
// src/common/ToolCallParsers.ts
|
|
1589
|
+
function stripModelArtifacts(text) {
|
|
1590
|
+
return text.replace(/<think>(?:[^<]|<(?!\/think>))*<\/think>/g, "").replace(/<\|[a-z_]+\|>/g, "").trim();
|
|
1591
|
+
}
|
|
1592
|
+
function makeToolCall(name, args, id = null) {
|
|
1593
|
+
return { name, arguments: args, id };
|
|
1594
|
+
}
|
|
1595
|
+
function tryParseJson(text) {
|
|
1596
|
+
try {
|
|
1597
|
+
return JSON.parse(text);
|
|
1598
|
+
} catch {
|
|
1599
|
+
return;
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
function findBalancedBlocks(source, openChar, closeChar, startFrom = 0) {
|
|
1603
|
+
const results = [];
|
|
1604
|
+
const length = source.length;
|
|
1605
|
+
let i = startFrom;
|
|
1606
|
+
while (i < length) {
|
|
1607
|
+
if (source[i] !== openChar) {
|
|
1608
|
+
i++;
|
|
1609
|
+
continue;
|
|
1610
|
+
}
|
|
1611
|
+
let depth = 1;
|
|
1612
|
+
let j = i + 1;
|
|
1613
|
+
let inString = false;
|
|
1614
|
+
let escape = false;
|
|
1615
|
+
while (j < length && depth > 0) {
|
|
1616
|
+
const ch = source[j];
|
|
1617
|
+
if (inString) {
|
|
1618
|
+
if (escape) {
|
|
1619
|
+
escape = false;
|
|
1620
|
+
} else if (ch === "\\") {
|
|
1621
|
+
escape = true;
|
|
1622
|
+
} else if (ch === '"') {
|
|
1623
|
+
inString = false;
|
|
1624
|
+
}
|
|
1625
|
+
} else {
|
|
1626
|
+
if (ch === '"') {
|
|
1627
|
+
inString = true;
|
|
1628
|
+
} else if (ch === openChar) {
|
|
1629
|
+
depth++;
|
|
1630
|
+
} else if (ch === closeChar) {
|
|
1631
|
+
depth--;
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
j++;
|
|
1635
|
+
}
|
|
1636
|
+
if (depth === 0) {
|
|
1637
|
+
results.push({ text: source.slice(i, j), start: i, end: j });
|
|
1638
|
+
i = j;
|
|
1639
|
+
} else {
|
|
1640
|
+
break;
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
return results;
|
|
1644
|
+
}
|
|
1645
|
+
function parseJsonToolCallArray(jsonStr, nameKey = "name", argsKeys = ["arguments", "parameters"]) {
|
|
1646
|
+
const parsed = tryParseJson(jsonStr.trim());
|
|
1647
|
+
if (!parsed)
|
|
1648
|
+
return;
|
|
1649
|
+
const arr = Array.isArray(parsed) ? parsed : [parsed];
|
|
1650
|
+
const calls = arr.filter((c) => !!c && typeof c === "object" && !!c[nameKey]).map((c) => {
|
|
1651
|
+
const args = argsKeys.reduce((found, key) => found ?? c[key], undefined);
|
|
1652
|
+
return makeToolCall(c[nameKey], args ?? {}, c.id ?? null);
|
|
1653
|
+
});
|
|
1654
|
+
return calls.length > 0 ? calls : undefined;
|
|
1655
|
+
}
|
|
1656
|
+
function parseKeyValueArgs(argsStr) {
|
|
1657
|
+
const args = {};
|
|
1658
|
+
if (!argsStr)
|
|
1659
|
+
return args;
|
|
1660
|
+
const argRegex = /(?<!\w)(\w+)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s,]+))/g;
|
|
1661
|
+
let match;
|
|
1662
|
+
while ((match = argRegex.exec(argsStr)) !== null) {
|
|
1663
|
+
const key = match[1];
|
|
1664
|
+
const value = match[2] ?? match[3] ?? match[4];
|
|
1665
|
+
args[key] = coerceArgValue(value);
|
|
1666
|
+
}
|
|
1667
|
+
return args;
|
|
1668
|
+
}
|
|
1669
|
+
function coerceArgValue(value) {
|
|
1670
|
+
if (value === "true")
|
|
1671
|
+
return true;
|
|
1672
|
+
if (value === "false")
|
|
1673
|
+
return false;
|
|
1674
|
+
if (value !== "" && !isNaN(Number(value)))
|
|
1675
|
+
return Number(value);
|
|
1676
|
+
return value;
|
|
1677
|
+
}
|
|
1678
|
+
function parseFunctionGemmaArgumentValue(rawValue) {
|
|
1679
|
+
const trimmed = rawValue.trim();
|
|
1680
|
+
if (trimmed.length === 0)
|
|
1681
|
+
return "";
|
|
1682
|
+
if (trimmed === "true")
|
|
1683
|
+
return true;
|
|
1684
|
+
if (trimmed === "false")
|
|
1685
|
+
return false;
|
|
1686
|
+
if (trimmed === "null")
|
|
1687
|
+
return null;
|
|
1688
|
+
const numeric = Number(trimmed);
|
|
1689
|
+
if (!Number.isNaN(numeric) && /^-?\d+(?:\.\d+)?$/.test(trimmed)) {
|
|
1690
|
+
return numeric;
|
|
1691
|
+
}
|
|
1692
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
1693
|
+
try {
|
|
1694
|
+
return JSON.parse(trimmed);
|
|
1695
|
+
} catch {}
|
|
1696
|
+
}
|
|
1697
|
+
return trimmed;
|
|
1698
|
+
}
|
|
1699
|
+
function parseFunctionGemmaLooseObject(text) {
|
|
1700
|
+
const trimmed = text.trim();
|
|
1701
|
+
if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
|
|
1702
|
+
return;
|
|
1703
|
+
}
|
|
1704
|
+
const inner = trimmed.slice(1, -1).trim();
|
|
1705
|
+
if (inner.length === 0) {
|
|
1706
|
+
return {};
|
|
1707
|
+
}
|
|
1708
|
+
const result = {};
|
|
1709
|
+
const pairs = inner.matchAll(/([A-Za-z0-9_]+)\s*:\s*('[^']*'|"[^"]*"|[^,}]+)/g);
|
|
1710
|
+
for (const [_, rawKey, rawValue] of pairs) {
|
|
1711
|
+
const key = rawKey.trim();
|
|
1712
|
+
const valueText = rawValue.trim().replace(/^'([^']*)'$/, '"$1"');
|
|
1713
|
+
result[key] = parseFunctionGemmaArgumentValue(valueText);
|
|
1714
|
+
}
|
|
1715
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
1716
|
+
}
|
|
1717
|
+
var parseLlama = (text) => {
|
|
1718
|
+
const calls = [];
|
|
1719
|
+
let content = text;
|
|
1720
|
+
const pythonTagMatch = text.match(/<\|python_tag\|>((?:[^<]|<(?!\|eot_id\|>|\|eom_id\|>))*)(?:<\|eot_id\|>|<\|eom_id\|>|$)/);
|
|
1721
|
+
if (pythonTagMatch) {
|
|
1722
|
+
content = text.slice(0, text.indexOf("<|python_tag|>")).trim();
|
|
1723
|
+
const jsonSection = pythonTagMatch[1].trim();
|
|
1724
|
+
for (const line of jsonSection.split(`
|
|
1725
|
+
`)) {
|
|
1726
|
+
const trimmed = line.trim();
|
|
1727
|
+
if (!trimmed)
|
|
1728
|
+
continue;
|
|
1729
|
+
const parsed = tryParseJson(trimmed);
|
|
1730
|
+
if (parsed?.name) {
|
|
1731
|
+
calls.push(makeToolCall(parsed.name, parsed.parameters ?? parsed.arguments ?? {}, parsed.id ?? null));
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
if (calls.length === 0) {
|
|
1736
|
+
const funcTagRegex = /<function=(\w+)>((?:[^<]|<(?!\/function>))*)<\/function>/g;
|
|
1737
|
+
let funcMatch;
|
|
1738
|
+
while ((funcMatch = funcTagRegex.exec(text)) !== null) {
|
|
1739
|
+
const args = tryParseJson(funcMatch[2].trim());
|
|
1740
|
+
if (args) {
|
|
1741
|
+
calls.push(makeToolCall(funcMatch[1], args));
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
if (calls.length > 0) {
|
|
1745
|
+
content = text.replace(/<function=\w+>(?:[^<]|<(?!\/function>))*<\/function>/g, "").trim();
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
if (calls.length === 0) {
|
|
1749
|
+
const blocks = findBalancedBlocks(text, "{", "}");
|
|
1750
|
+
for (const block of blocks) {
|
|
1751
|
+
const parsed = tryParseJson(block.text);
|
|
1752
|
+
if (parsed?.name && (parsed.parameters !== undefined || parsed.arguments !== undefined)) {
|
|
1753
|
+
calls.push(makeToolCall(parsed.name, parsed.parameters ?? parsed.arguments ?? {}, parsed.id ?? null));
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
if (calls.length > 0) {
|
|
1757
|
+
content = text.slice(0, text.indexOf(calls[0].name) - '{"name": "'.length).trim();
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
return calls.length > 0 ? { tool_calls: calls, content, parser: "llama" } : null;
|
|
1761
|
+
};
|
|
1762
|
+
var parseMistral = (text) => {
|
|
1763
|
+
const marker = "[TOOL_CALLS]";
|
|
1764
|
+
const idx = text.indexOf(marker);
|
|
1765
|
+
if (idx === -1)
|
|
1766
|
+
return null;
|
|
1767
|
+
const content = text.slice(0, idx).trim();
|
|
1768
|
+
const jsonStr = text.slice(idx + marker.length).trim();
|
|
1769
|
+
const calls = parseJsonToolCallArray(jsonStr);
|
|
1770
|
+
return calls ? { tool_calls: calls, content, parser: "mistral" } : null;
|
|
1771
|
+
};
|
|
1772
|
+
var parseHermes = (text) => {
|
|
1773
|
+
const regex = /<tool_call>((?:[^<]|<(?!\/tool_call>))*)<\/tool_call>/g;
|
|
1774
|
+
const calls = [];
|
|
1775
|
+
let match;
|
|
1776
|
+
while ((match = regex.exec(text)) !== null) {
|
|
1777
|
+
const parsed = tryParseJson(match[1].trim());
|
|
1778
|
+
if (parsed) {
|
|
1779
|
+
calls.push(makeToolCall(parsed.name ?? "", parsed.arguments ?? parsed.parameters ?? {}, parsed.id ?? null));
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
if (calls.length === 0)
|
|
1783
|
+
return null;
|
|
1784
|
+
const content = text.replace(/<tool_call>(?:[^<]|<(?!\/tool_call>))*<\/tool_call>/g, "").trim();
|
|
1785
|
+
return { tool_calls: calls, content, parser: "hermes" };
|
|
1786
|
+
};
|
|
1787
|
+
var parseCohere = (text) => {
|
|
1788
|
+
const blockMatch = text.match(/Action:\s*```(?:json)?\n?((?:[^`]|`(?!``))*)\n?```/);
|
|
1789
|
+
let inlineJsonStr;
|
|
1790
|
+
if (!blockMatch) {
|
|
1791
|
+
const actionIdx2 = text.indexOf("Action:");
|
|
1792
|
+
if (actionIdx2 !== -1) {
|
|
1793
|
+
const afterAction = text.slice(actionIdx2 + "Action:".length).trimStart();
|
|
1794
|
+
if (afterAction.startsWith("[")) {
|
|
1795
|
+
const blocks = findBalancedBlocks(afterAction, "[", "]");
|
|
1796
|
+
if (blocks.length > 0) {
|
|
1797
|
+
inlineJsonStr = blocks[0].text;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
const jsonStr = blockMatch?.[1] ?? inlineJsonStr;
|
|
1803
|
+
if (!jsonStr)
|
|
1804
|
+
return null;
|
|
1805
|
+
const calls = parseJsonToolCallArray(jsonStr, "tool_name", ["parameters", "arguments"]);
|
|
1806
|
+
if (!calls) {
|
|
1807
|
+
const fallbackCalls = parseJsonToolCallArray(jsonStr);
|
|
1808
|
+
if (!fallbackCalls)
|
|
1809
|
+
return null;
|
|
1810
|
+
const actionIdx2 = text.indexOf("Action:");
|
|
1811
|
+
const content2 = text.slice(0, actionIdx2).trim();
|
|
1812
|
+
return { tool_calls: fallbackCalls, content: content2, parser: "cohere" };
|
|
1813
|
+
}
|
|
1814
|
+
const actionIdx = text.indexOf("Action:");
|
|
1815
|
+
const content = text.slice(0, actionIdx).trim();
|
|
1816
|
+
return { tool_calls: calls, content, parser: "cohere" };
|
|
1817
|
+
};
|
|
1818
|
+
var parseDeepSeek = (text) => {
|
|
1819
|
+
const calls = [];
|
|
1820
|
+
const bar = "(?:||\\|)";
|
|
1821
|
+
const sep = "[\\s▁]";
|
|
1822
|
+
const v31Regex = new RegExp(`<${bar}tool${sep}call${sep}begin${bar}>\\s*(\\w+)\\s*<${bar}tool${sep}sep${bar}>\\s*([^<]*(?:<(?!${bar}tool${sep}call${sep}end${bar}>)[^<]*)*)\\s*<${bar}tool${sep}call${sep}end${bar}>`, "g");
|
|
1823
|
+
let match;
|
|
1824
|
+
while ((match = v31Regex.exec(text)) !== null) {
|
|
1825
|
+
const args = tryParseJson(match[2].trim());
|
|
1826
|
+
if (args) {
|
|
1827
|
+
calls.push(makeToolCall(match[1], args));
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
if (calls.length === 0) {
|
|
1831
|
+
const v2Regex = new RegExp(`<${bar}tool${sep}call${sep}begin${bar}>\\s*(\\w+)\\s*\\n\`\`\`(?:json)?\\n([^\`]*(?:\`(?!\`\`)[^\`]*)*)\\n\`\`\`\\s*<${bar}tool${sep}call${sep}end${bar}>`, "g");
|
|
1832
|
+
while ((match = v2Regex.exec(text)) !== null) {
|
|
1833
|
+
const args = tryParseJson(match[2].trim());
|
|
1834
|
+
if (args) {
|
|
1835
|
+
calls.push(makeToolCall(match[1], args));
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
if (calls.length === 0)
|
|
1840
|
+
return null;
|
|
1841
|
+
const content = text.replace(new RegExp(`<${bar}tool${sep}calls?${sep}(?:begin|end)${bar}>`, "g"), "").replace(new RegExp(`<${bar}tool${sep}call${sep}(?:begin|end)${bar}>[^<]*(?:<(?!${bar}tool${sep}call${sep}end${bar}>)[^<]*)*<${bar}tool${sep}call${sep}end${bar}>`, "g"), "").replace(new RegExp(`<${bar}tool${sep}sep${bar}>`, "g"), "").trim();
|
|
1842
|
+
return { tool_calls: calls, content, parser: "deepseek" };
|
|
1843
|
+
};
|
|
1844
|
+
var parsePhi = (text) => {
|
|
1845
|
+
const match = text.match(/<\|tool_calls\|>((?:[^<]|<(?!\|\/tool_calls\|>))*)<\|\/tool_calls\|>/);
|
|
1846
|
+
if (!match)
|
|
1847
|
+
return null;
|
|
1848
|
+
const calls = parseJsonToolCallArray(match[1]);
|
|
1849
|
+
if (!calls)
|
|
1850
|
+
return null;
|
|
1851
|
+
const content = text.slice(0, text.indexOf("<|tool_calls|>")).trim();
|
|
1852
|
+
return { tool_calls: calls, content, parser: "phi" };
|
|
1853
|
+
};
|
|
1854
|
+
var parsePhiFunctools = (text) => {
|
|
1855
|
+
const idx = text.indexOf("functools");
|
|
1856
|
+
if (idx === -1)
|
|
1857
|
+
return null;
|
|
1858
|
+
let start = idx + "functools".length;
|
|
1859
|
+
while (start < text.length && /\s/.test(text[start]))
|
|
1860
|
+
start++;
|
|
1861
|
+
if (start >= text.length || text[start] !== "[")
|
|
1862
|
+
return null;
|
|
1863
|
+
const blocks = findBalancedBlocks(text, "[", "]", start);
|
|
1864
|
+
if (blocks.length === 0)
|
|
1865
|
+
return null;
|
|
1866
|
+
const calls = parseJsonToolCallArray(blocks[0].text);
|
|
1867
|
+
if (!calls)
|
|
1868
|
+
return null;
|
|
1869
|
+
const content = text.slice(0, idx).trim();
|
|
1870
|
+
return { tool_calls: calls, content, parser: "phi_functools" };
|
|
1871
|
+
};
|
|
1872
|
+
var parseInternLM = (text) => {
|
|
1873
|
+
const regex = /<\|action_start\|>\s*<\|plugin\|>((?:[^<]|<(?!\|action_end\|>))*)<\|action_end\|>/g;
|
|
1874
|
+
const calls = [];
|
|
1875
|
+
let match;
|
|
1876
|
+
while ((match = regex.exec(text)) !== null) {
|
|
1877
|
+
const parsed = tryParseJson(match[1].trim());
|
|
1878
|
+
if (parsed) {
|
|
1879
|
+
calls.push(makeToolCall(parsed.name ?? "", parsed.parameters ?? parsed.arguments ?? {}, parsed.id ?? null));
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
if (calls.length === 0)
|
|
1883
|
+
return null;
|
|
1884
|
+
const content = text.replace(/<\|action_start\|>\s*<\|plugin\|>(?:[^<]|<(?!\|action_end\|>))*<\|action_end\|>/g, "").trim();
|
|
1885
|
+
return { tool_calls: calls, content, parser: "internlm" };
|
|
1886
|
+
};
|
|
1887
|
+
var parseChatGLM = (text) => {
|
|
1888
|
+
const match = text.match(/^(\w+)\n(\{[\s\S]*\})\s*$/m);
|
|
1889
|
+
if (!match)
|
|
1890
|
+
return null;
|
|
1891
|
+
const args = tryParseJson(match[2].trim());
|
|
1892
|
+
if (!args)
|
|
1893
|
+
return null;
|
|
1894
|
+
return {
|
|
1895
|
+
tool_calls: [makeToolCall(match[1], args)],
|
|
1896
|
+
content: "",
|
|
1897
|
+
parser: "chatglm"
|
|
1898
|
+
};
|
|
1899
|
+
};
|
|
1900
|
+
var parseFunctionary = (text) => {
|
|
1901
|
+
const regex = />>>\s*(\w+)\s*\n((?:(?!>>>)[\s\S])*)/g;
|
|
1902
|
+
const calls = [];
|
|
1903
|
+
let content = "";
|
|
1904
|
+
let match;
|
|
1905
|
+
while ((match = regex.exec(text)) !== null) {
|
|
1906
|
+
const funcName = match[1].trim();
|
|
1907
|
+
const body = match[2].trim();
|
|
1908
|
+
if (funcName === "all") {
|
|
1909
|
+
content += body;
|
|
1910
|
+
continue;
|
|
1911
|
+
}
|
|
1912
|
+
const args = tryParseJson(body);
|
|
1913
|
+
calls.push(makeToolCall(funcName, args ?? { content: body }));
|
|
1914
|
+
}
|
|
1915
|
+
if (calls.length === 0)
|
|
1916
|
+
return null;
|
|
1917
|
+
return { tool_calls: calls, content: content.trim(), parser: "functionary" };
|
|
1918
|
+
};
|
|
1919
|
+
var parseGorilla = (text) => {
|
|
1920
|
+
const regex = /<<function>>\s{0,20}(\w+)\(([^)]*)\)/g;
|
|
1921
|
+
const calls = [];
|
|
1922
|
+
let match;
|
|
1923
|
+
while ((match = regex.exec(text)) !== null) {
|
|
1924
|
+
calls.push(makeToolCall(match[1], parseKeyValueArgs(match[2].trim())));
|
|
1925
|
+
}
|
|
1926
|
+
if (calls.length === 0)
|
|
1927
|
+
return null;
|
|
1928
|
+
const content = text.replace(/<<function>>\s{0,20}\w+\([^)]*\)/g, "").trim();
|
|
1929
|
+
return { tool_calls: calls, content, parser: "gorilla" };
|
|
1930
|
+
};
|
|
1931
|
+
var parseNexusRaven = (text) => {
|
|
1932
|
+
const regex = /Call:\s{0,20}(\w+)\(([^)]*)\)/g;
|
|
1933
|
+
const calls = [];
|
|
1934
|
+
let match;
|
|
1935
|
+
while ((match = regex.exec(text)) !== null) {
|
|
1936
|
+
calls.push(makeToolCall(match[1], parseKeyValueArgs(match[2].trim())));
|
|
1937
|
+
}
|
|
1938
|
+
if (calls.length === 0)
|
|
1939
|
+
return null;
|
|
1940
|
+
const thoughtMatch = text.match(/Thought:\s*((?:(?!Call:)[\s\S])*)/);
|
|
1941
|
+
const content = thoughtMatch?.[1]?.trim() ?? text.replace(/Call:\s{0,20}\w+\([^)]*\)/g, "").trim();
|
|
1942
|
+
return { tool_calls: calls, content, parser: "nexusraven" };
|
|
1943
|
+
};
|
|
1944
|
+
var parseXLAM = (text) => {
|
|
1945
|
+
const codeBlockMatch = text.match(/```(?:json)?\n?((?:[^`]|`(?!``))*)\n?```/);
|
|
1946
|
+
let jsonStr;
|
|
1947
|
+
let isCodeBlock = false;
|
|
1948
|
+
if (codeBlockMatch) {
|
|
1949
|
+
const inner = codeBlockMatch[1].trim();
|
|
1950
|
+
if (inner.startsWith("[")) {
|
|
1951
|
+
jsonStr = inner;
|
|
1952
|
+
isCodeBlock = true;
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
if (!jsonStr) {
|
|
1956
|
+
const trimmed = text.trim();
|
|
1957
|
+
if (!trimmed.startsWith("["))
|
|
1958
|
+
return null;
|
|
1959
|
+
jsonStr = trimmed;
|
|
1960
|
+
}
|
|
1961
|
+
const calls = parseJsonToolCallArray(jsonStr);
|
|
1962
|
+
if (!calls)
|
|
1963
|
+
return null;
|
|
1964
|
+
const content = isCodeBlock ? text.slice(0, text.indexOf("```")).trim() : "";
|
|
1965
|
+
return { tool_calls: calls, content, parser: "xlam" };
|
|
1966
|
+
};
|
|
1967
|
+
var parseFireFunction = (text) => {
|
|
1968
|
+
const toolCallsIdx = text.indexOf('"tool_calls"');
|
|
1969
|
+
if (toolCallsIdx === -1)
|
|
1970
|
+
return null;
|
|
1971
|
+
let bracketStart = text.indexOf("[", toolCallsIdx);
|
|
1972
|
+
if (bracketStart === -1)
|
|
1973
|
+
return null;
|
|
1974
|
+
const blocks = findBalancedBlocks(text, "[", "]", bracketStart);
|
|
1975
|
+
if (blocks.length === 0)
|
|
1976
|
+
return null;
|
|
1977
|
+
const parsed = tryParseJson(blocks[0].text);
|
|
1978
|
+
if (!parsed || !Array.isArray(parsed))
|
|
1979
|
+
return null;
|
|
1980
|
+
const calls = [];
|
|
1981
|
+
for (const c of parsed) {
|
|
1982
|
+
const fn = c.function;
|
|
1983
|
+
if (!fn?.name)
|
|
1984
|
+
continue;
|
|
1985
|
+
let args = fn.arguments ?? {};
|
|
1986
|
+
if (typeof args === "string") {
|
|
1987
|
+
args = tryParseJson(args) ?? {};
|
|
1988
|
+
}
|
|
1989
|
+
calls.push(makeToolCall(fn.name, args, c.id ?? null));
|
|
1990
|
+
}
|
|
1991
|
+
return calls.length > 0 ? { tool_calls: calls, content: "", parser: "firefunction" } : null;
|
|
1992
|
+
};
|
|
1993
|
+
var parseGranite = (text) => {
|
|
1994
|
+
const regex = /<\|tool_call\|>((?:[^<]|<(?!\|\/tool_call\|>|\|end_of_text\|>))*?)(?:<\|\/tool_call\|>|<\|end_of_text\|>|$)/g;
|
|
1995
|
+
const calls = [];
|
|
1996
|
+
let match;
|
|
1997
|
+
while ((match = regex.exec(text)) !== null) {
|
|
1998
|
+
const parsed = tryParseJson(match[1].trim());
|
|
1999
|
+
if (parsed) {
|
|
2000
|
+
calls.push(makeToolCall(parsed.name ?? "", parsed.arguments ?? parsed.parameters ?? {}, parsed.id ?? null));
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
if (calls.length === 0)
|
|
2004
|
+
return null;
|
|
2005
|
+
const content = text.replace(/<\|tool_call\|>(?:[^<]|<(?!\|\/tool_call\|>|\|end_of_text\|>))*(?:<\|\/tool_call\|>|$)/g, "").trim();
|
|
2006
|
+
return { tool_calls: calls, content, parser: "granite" };
|
|
2007
|
+
};
|
|
2008
|
+
var parseGemma = (text) => {
|
|
2009
|
+
const openMarker = "```tool_code";
|
|
2010
|
+
const openIdx = text.indexOf(openMarker);
|
|
2011
|
+
if (openIdx === -1)
|
|
2012
|
+
return null;
|
|
2013
|
+
const lineStart = text.indexOf(`
|
|
2014
|
+
`, openIdx + openMarker.length);
|
|
2015
|
+
if (lineStart === -1)
|
|
2016
|
+
return null;
|
|
2017
|
+
let closeIdx = -1;
|
|
2018
|
+
let searchFrom = lineStart + 1;
|
|
2019
|
+
while (searchFrom < text.length) {
|
|
2020
|
+
const candidate = text.indexOf("```", searchFrom);
|
|
2021
|
+
if (candidate === -1)
|
|
2022
|
+
break;
|
|
2023
|
+
const lineBegin = text.lastIndexOf(`
|
|
2024
|
+
`, candidate - 1);
|
|
2025
|
+
if (lineBegin >= lineStart && text.slice(lineBegin + 1, candidate).trim() === "") {
|
|
2026
|
+
closeIdx = candidate;
|
|
2027
|
+
break;
|
|
2028
|
+
}
|
|
2029
|
+
searchFrom = candidate + 3;
|
|
2030
|
+
}
|
|
2031
|
+
if (closeIdx === -1)
|
|
2032
|
+
return null;
|
|
2033
|
+
const rawCode = text.slice(lineStart + 1, closeIdx).replace(/\n[ \t]*$/, "");
|
|
2034
|
+
const code = rawCode.trim();
|
|
2035
|
+
const funcMatch = code.match(/^(\w+)\(([\s\S]*)\)$/);
|
|
2036
|
+
if (!funcMatch)
|
|
2037
|
+
return null;
|
|
2038
|
+
const blockEnd = closeIdx + 3;
|
|
2039
|
+
const content = (text.slice(0, openIdx) + text.slice(blockEnd)).trim();
|
|
2040
|
+
return {
|
|
2041
|
+
tool_calls: [makeToolCall(funcMatch[1], parseKeyValueArgs(funcMatch[2].trim()))],
|
|
2042
|
+
content,
|
|
2043
|
+
parser: "gemma"
|
|
2044
|
+
};
|
|
2045
|
+
};
|
|
2046
|
+
function parseFunctionGemmaArgs(argsStr) {
|
|
2047
|
+
const args = {};
|
|
2048
|
+
if (!argsStr.trim())
|
|
2049
|
+
return args;
|
|
2050
|
+
const escapeRegex = /(?<![A-Za-z0-9_])([A-Za-z0-9_]+)\s*:\s*<escape>((?:[^<]|<(?!escape>))*)<escape>/g;
|
|
2051
|
+
let escapeMatch;
|
|
2052
|
+
while ((escapeMatch = escapeRegex.exec(argsStr)) !== null) {
|
|
2053
|
+
args[escapeMatch[1]] = coerceArgValue(escapeMatch[2]);
|
|
2054
|
+
}
|
|
2055
|
+
if (Object.keys(args).length > 0)
|
|
2056
|
+
return args;
|
|
2057
|
+
const plainRegex = /(?<![A-Za-z0-9_])(?=([A-Za-z0-9_]+))\1\s*:\s*(?:'([^']*)'|"([^"]*)"|([^,}]+))/g;
|
|
2058
|
+
let plainMatch;
|
|
2059
|
+
while ((plainMatch = plainRegex.exec(argsStr)) !== null) {
|
|
2060
|
+
const key = plainMatch[1].trim();
|
|
2061
|
+
const value = (plainMatch[2] ?? plainMatch[3] ?? plainMatch[4] ?? "").replace(/<escape>/g, "").trim();
|
|
2062
|
+
args[key] = parseFunctionGemmaArgumentValue(value);
|
|
2063
|
+
}
|
|
2064
|
+
if (Object.keys(args).length > 0)
|
|
2065
|
+
return args;
|
|
2066
|
+
const jsonResult = tryParseJson(`{${argsStr}}`);
|
|
2067
|
+
if (jsonResult && typeof jsonResult === "object")
|
|
2068
|
+
return jsonResult;
|
|
2069
|
+
return args;
|
|
2070
|
+
}
|
|
2071
|
+
var parseFunctionGemma = (text) => {
|
|
2072
|
+
const regex = /(?:<start_function_call>\s*)?call:(?=([\w.]+))\1\s*\{([^}]*)\}(?:\s*<end_function_call>)?/g;
|
|
2073
|
+
const calls = [];
|
|
2074
|
+
let match;
|
|
2075
|
+
while ((match = regex.exec(text)) !== null) {
|
|
2076
|
+
calls.push(makeToolCall(match[1].trim(), parseFunctionGemmaArgs(match[2])));
|
|
2077
|
+
}
|
|
2078
|
+
if (calls.length === 0) {
|
|
2079
|
+
const fallbackRegex = /^:([A-Za-z_]\w*)\s*\{([^}]*)\}$/;
|
|
2080
|
+
const fallbackMatch = text.trim().match(fallbackRegex);
|
|
2081
|
+
if (fallbackMatch) {
|
|
2082
|
+
calls.push(makeToolCall(fallbackMatch[1].trim(), parseFunctionGemmaArgs(fallbackMatch[2])));
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
if (calls.length === 0)
|
|
2086
|
+
return null;
|
|
2087
|
+
const content = text.replace(/(?:<start_function_call>\s*)?(?:call)?:(?=([\w.]+))\1\s*\{[^}]*\}(?:\s*<end_function_call>)?/g, "").trim();
|
|
2088
|
+
return { tool_calls: calls, content, parser: "functiongemma" };
|
|
2089
|
+
};
|
|
2090
|
+
function parseLiquidArgs(argsStr) {
|
|
2091
|
+
const trimmed = argsStr.trim();
|
|
2092
|
+
const paramsMatch = trimmed.match(/^params\s*=\s*(\{[\s\S]*\})$/);
|
|
2093
|
+
if (paramsMatch) {
|
|
2094
|
+
const jsonStr = paramsMatch[1].replace(/'/g, '"');
|
|
2095
|
+
const parsed = tryParseJson(jsonStr);
|
|
2096
|
+
if (parsed && typeof parsed === "object") {
|
|
2097
|
+
return parsed;
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
2101
|
+
const jsonified = trimmed.replace(/([{,]\s*)(\w+)\s*:/g, '$1"$2":');
|
|
2102
|
+
const parsed = tryParseJson(jsonified);
|
|
2103
|
+
if (parsed && typeof parsed === "object") {
|
|
2104
|
+
return parsed;
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
return parseKeyValueArgs(argsStr);
|
|
2108
|
+
}
|
|
2109
|
+
function extractPythonicCalls(text) {
|
|
2110
|
+
const calls = [];
|
|
2111
|
+
const startRegex = /(?<!\w)(\w+)\(/g;
|
|
2112
|
+
let startMatch;
|
|
2113
|
+
while ((startMatch = startRegex.exec(text)) !== null) {
|
|
2114
|
+
const funcName = startMatch[1];
|
|
2115
|
+
const argsStart = startMatch.index + startMatch[0].length;
|
|
2116
|
+
let depth = 1;
|
|
2117
|
+
let i = argsStart;
|
|
2118
|
+
while (i < text.length && depth > 0) {
|
|
2119
|
+
if (text[i] === "(")
|
|
2120
|
+
depth++;
|
|
2121
|
+
else if (text[i] === ")")
|
|
2122
|
+
depth--;
|
|
2123
|
+
i++;
|
|
2124
|
+
}
|
|
2125
|
+
if (depth === 0) {
|
|
2126
|
+
const argsStr = text.slice(argsStart, i - 1);
|
|
2127
|
+
calls.push(makeToolCall(funcName, parseLiquidArgs(argsStr)));
|
|
2128
|
+
startRegex.lastIndex = i;
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
return calls;
|
|
2132
|
+
}
|
|
2133
|
+
var parseLiquid = (text) => {
|
|
2134
|
+
const specialMatch = text.match(/<\|tool_call_start\|>((?:[^<]|<(?!\|tool_call_end\|>))*)<\|tool_call_end\|>/);
|
|
2135
|
+
if (specialMatch) {
|
|
2136
|
+
const inner = specialMatch[1].trim();
|
|
2137
|
+
const unwrapped = inner.startsWith("[") && inner.endsWith("]") ? inner.slice(1, -1) : inner;
|
|
2138
|
+
const calls = extractPythonicCalls(unwrapped);
|
|
2139
|
+
if (calls.length > 0) {
|
|
2140
|
+
const content = stripModelArtifacts(text.replace(/<\|tool_call_start\|>(?:[^<]|<(?!\|tool_call_end\|>))*<\|tool_call_end\|>/g, ""));
|
|
2141
|
+
return { tool_calls: calls, content, parser: "liquid" };
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
const bracketCalls = [];
|
|
2145
|
+
const bracketSpans = [];
|
|
2146
|
+
{
|
|
2147
|
+
const bracketOpenRegex = /\[(?=\w+\()/g;
|
|
2148
|
+
let bm;
|
|
2149
|
+
while ((bm = bracketOpenRegex.exec(text)) !== null) {
|
|
2150
|
+
const innerStart = bm.index + 1;
|
|
2151
|
+
let depth = 0;
|
|
2152
|
+
let i = innerStart;
|
|
2153
|
+
let foundClose = false;
|
|
2154
|
+
while (i < text.length) {
|
|
2155
|
+
const ch = text[i];
|
|
2156
|
+
if (ch === "(")
|
|
2157
|
+
depth++;
|
|
2158
|
+
else if (ch === ")") {
|
|
2159
|
+
depth--;
|
|
2160
|
+
if (depth === 0 && i + 1 < text.length && text[i + 1] === "]") {
|
|
2161
|
+
const inner = text.slice(innerStart, i + 1);
|
|
2162
|
+
const calls = extractPythonicCalls(inner);
|
|
2163
|
+
bracketCalls.push(...calls);
|
|
2164
|
+
bracketSpans.push([bm.index, i + 2]);
|
|
2165
|
+
bracketOpenRegex.lastIndex = i + 2;
|
|
2166
|
+
foundClose = true;
|
|
2167
|
+
break;
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
i++;
|
|
2171
|
+
}
|
|
2172
|
+
if (!foundClose)
|
|
2173
|
+
break;
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
if (bracketCalls.length > 0) {
|
|
2177
|
+
let content = text;
|
|
2178
|
+
for (let k = bracketSpans.length - 1;k >= 0; k--) {
|
|
2179
|
+
content = content.slice(0, bracketSpans[k][0]) + content.slice(bracketSpans[k][1]);
|
|
2180
|
+
}
|
|
2181
|
+
return { tool_calls: bracketCalls, content: stripModelArtifacts(content), parser: "liquid" };
|
|
2182
|
+
}
|
|
2183
|
+
const callPrefixRegex = /\|?\|?Call:\s*/g;
|
|
2184
|
+
let callPrefixMatch;
|
|
2185
|
+
const callCalls = [];
|
|
2186
|
+
while ((callPrefixMatch = callPrefixRegex.exec(text)) !== null) {
|
|
2187
|
+
const afterPrefix = text.slice(callPrefixMatch.index + callPrefixMatch[0].length);
|
|
2188
|
+
const calls = extractPythonicCalls(afterPrefix);
|
|
2189
|
+
if (calls.length > 0) {
|
|
2190
|
+
callCalls.push(calls[0]);
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
if (callCalls.length > 0) {
|
|
2194
|
+
const content = stripModelArtifacts(text.replace(/\|?\|?Call:\s{0,20}\w+\([^)]*\)/g, ""));
|
|
2195
|
+
return { tool_calls: callCalls, content, parser: "liquid" };
|
|
2196
|
+
}
|
|
2197
|
+
return null;
|
|
2198
|
+
};
|
|
2199
|
+
var parseJamba = (text) => {
|
|
2200
|
+
const tagMatch = text.match(/<tool_calls>((?:[^<]|<(?!\/tool_calls>))*)<\/tool_calls>/);
|
|
2201
|
+
if (tagMatch) {
|
|
2202
|
+
const parsed = tryParseJson(tagMatch[1].trim());
|
|
2203
|
+
if (parsed) {
|
|
2204
|
+
const arr = Array.isArray(parsed) ? parsed : [parsed];
|
|
2205
|
+
const calls = [];
|
|
2206
|
+
for (const c of arr) {
|
|
2207
|
+
if (!c.name)
|
|
2208
|
+
continue;
|
|
2209
|
+
let args = c.arguments ?? c.parameters ?? {};
|
|
2210
|
+
if (typeof args === "string") {
|
|
2211
|
+
args = tryParseJson(args) ?? {};
|
|
2212
|
+
}
|
|
2213
|
+
calls.push(makeToolCall(c.name, args, c.id ?? null));
|
|
2214
|
+
}
|
|
2215
|
+
if (calls.length > 0) {
|
|
2216
|
+
const content = text.slice(0, text.indexOf("<tool_calls>")).trim();
|
|
2217
|
+
return { tool_calls: calls, content, parser: "jamba" };
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
return parseFireFunction(text);
|
|
2222
|
+
};
|
|
2223
|
+
var parseQwen35Xml = (text) => {
|
|
2224
|
+
const toolCallMatches = text.matchAll(/<tool_call>((?:[^<]|<(?!\/tool_call>))*)<\/tool_call>/g);
|
|
2225
|
+
const calls = [];
|
|
2226
|
+
for (const [_, toolCallBody] of toolCallMatches) {
|
|
2227
|
+
const functionMatch = toolCallBody.trim().match(/<function=([^>\n<]+)>((?:[^<]|<(?!\/function>))*)<\/function>/);
|
|
2228
|
+
if (!functionMatch) {
|
|
2229
|
+
continue;
|
|
2230
|
+
}
|
|
2231
|
+
const [, rawName, functionBody] = functionMatch;
|
|
2232
|
+
const parsedInput = {};
|
|
2233
|
+
const parameterMatches = functionBody.matchAll(/<parameter=([^>\n<]+)>((?:[^<]|<(?!\/parameter>))*)<\/parameter>/g);
|
|
2234
|
+
for (const [__, rawParamName, rawValue] of parameterMatches) {
|
|
2235
|
+
const paramName = rawParamName.trim();
|
|
2236
|
+
const valueText = rawValue.trim();
|
|
2237
|
+
if (paramName === "params") {
|
|
2238
|
+
try {
|
|
2239
|
+
const parsedValue = JSON.parse(valueText);
|
|
2240
|
+
if (parsedValue && typeof parsedValue === "object" && !Array.isArray(parsedValue)) {
|
|
2241
|
+
Object.assign(parsedInput, parsedValue);
|
|
2242
|
+
continue;
|
|
2243
|
+
}
|
|
2244
|
+
} catch {}
|
|
2245
|
+
}
|
|
2246
|
+
parsedInput[paramName] = valueText;
|
|
2247
|
+
}
|
|
2248
|
+
calls.push(makeToolCall(rawName.trim(), parsedInput));
|
|
2249
|
+
}
|
|
2250
|
+
if (calls.length === 0)
|
|
2251
|
+
return null;
|
|
2252
|
+
const content = text.replace(/<tool_call>(?:[^<]|<(?!\/tool_call>))*<\/tool_call>/g, "").trim();
|
|
2253
|
+
return { tool_calls: calls, content, parser: "qwen35xml" };
|
|
2254
|
+
};
|
|
2255
|
+
var MODEL_PARSERS = {
|
|
2256
|
+
llama: [parseLlama, parseHermes],
|
|
2257
|
+
mistral: [parseMistral, parseHermes],
|
|
2258
|
+
mixtral: [parseMistral, parseHermes],
|
|
2259
|
+
qwen: [parseHermes, parseLlama],
|
|
2260
|
+
qwen2: [parseHermes, parseLlama],
|
|
2261
|
+
qwen3: [parseHermes, parseQwen35Xml, parseLlama],
|
|
2262
|
+
qwen35: [parseQwen35Xml, parseHermes, parseLlama],
|
|
2263
|
+
cohere: [parseCohere, parseHermes],
|
|
2264
|
+
command: [parseCohere, parseHermes],
|
|
2265
|
+
deepseek: [parseDeepSeek, parseHermes],
|
|
2266
|
+
hermes: [parseHermes],
|
|
2267
|
+
phi: [parsePhi, parsePhiFunctools, parseHermes],
|
|
2268
|
+
internlm: [parseInternLM, parseHermes],
|
|
2269
|
+
chatglm: [parseChatGLM],
|
|
2270
|
+
glm: [parseChatGLM],
|
|
2271
|
+
functiongemma: [parseFunctionGemma, parseGemma, parseHermes],
|
|
2272
|
+
gemma: [parseFunctionGemma, parseGemma, parseHermes],
|
|
2273
|
+
functionary: [parseFunctionary],
|
|
2274
|
+
gorilla: [parseGorilla],
|
|
2275
|
+
nexusraven: [parseNexusRaven],
|
|
2276
|
+
xlam: [parseXLAM],
|
|
2277
|
+
firefunction: [parseFireFunction, parsePhiFunctools],
|
|
2278
|
+
granite: [parseGranite, parseHermes],
|
|
2279
|
+
solar: [parseHermes],
|
|
2280
|
+
jamba: [parseJamba, parseHermes],
|
|
2281
|
+
liquid: [parseLiquid, parseHermes],
|
|
2282
|
+
lfm: [parseLiquid, parseHermes],
|
|
2283
|
+
yi: [parseHermes, parseLlama],
|
|
2284
|
+
falcon: [parseHermes, parseLlama]
|
|
2285
|
+
};
|
|
2286
|
+
var DEFAULT_PARSER_CHAIN = [
|
|
2287
|
+
parsePhi,
|
|
2288
|
+
parseMistral,
|
|
2289
|
+
parseDeepSeek,
|
|
2290
|
+
parseInternLM,
|
|
2291
|
+
parseGranite,
|
|
2292
|
+
parseFunctionGemma,
|
|
2293
|
+
parseQwen35Xml,
|
|
2294
|
+
parseHermes,
|
|
2295
|
+
parseCohere,
|
|
2296
|
+
parseFunctionary,
|
|
2297
|
+
parseGorilla,
|
|
2298
|
+
parseNexusRaven,
|
|
2299
|
+
parseFireFunction,
|
|
2300
|
+
parsePhiFunctools,
|
|
2301
|
+
parseLiquid,
|
|
2302
|
+
parseLlama,
|
|
2303
|
+
parseGemma,
|
|
2304
|
+
parseXLAM
|
|
2305
|
+
];
|
|
2306
|
+
function detectModelFamily(tokenizerOrName) {
|
|
2307
|
+
let name = "";
|
|
2308
|
+
if (typeof tokenizerOrName === "string") {
|
|
2309
|
+
name = tokenizerOrName.toLowerCase();
|
|
2310
|
+
} else if (tokenizerOrName) {
|
|
2311
|
+
const config = tokenizerOrName.config ?? {};
|
|
2312
|
+
name = (config.name_or_path ?? config._name_or_path ?? config.model_type ?? tokenizerOrName.name_or_path ?? "").toLowerCase();
|
|
2313
|
+
}
|
|
2314
|
+
if (!name)
|
|
2315
|
+
return null;
|
|
2316
|
+
for (const family of Object.keys(MODEL_PARSERS)) {
|
|
2317
|
+
if (name.includes(family)) {
|
|
2318
|
+
return family;
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
return null;
|
|
2322
|
+
}
|
|
2323
|
+
function parseToolCalls(text, { tokenizer = null, model = null, parser = null } = {}) {
|
|
2324
|
+
if (!text || typeof text !== "string") {
|
|
2325
|
+
return { tool_calls: [], content: text ?? "", parser: "none" };
|
|
2326
|
+
}
|
|
2327
|
+
let parsersToTry;
|
|
2328
|
+
if (parser) {
|
|
2329
|
+
const key = parser.toLowerCase();
|
|
2330
|
+
const found = MODEL_PARSERS[key];
|
|
2331
|
+
if (!found) {
|
|
2332
|
+
throw new Error(`Unknown parser "${parser}". Available parsers: ${Object.keys(MODEL_PARSERS).join(", ")}`);
|
|
2333
|
+
}
|
|
2334
|
+
parsersToTry = found;
|
|
2335
|
+
} else {
|
|
2336
|
+
const family = detectModelFamily(tokenizer ?? model ?? null);
|
|
2337
|
+
parsersToTry = family ? MODEL_PARSERS[family] : DEFAULT_PARSER_CHAIN;
|
|
2338
|
+
}
|
|
2339
|
+
for (const parserFn of parsersToTry) {
|
|
2340
|
+
const result = parserFn(text);
|
|
2341
|
+
if (result)
|
|
2342
|
+
return result;
|
|
2343
|
+
}
|
|
2344
|
+
return { tool_calls: [], content: text, parser: "none" };
|
|
2345
|
+
}
|
|
2346
|
+
function hasToolCalls(text) {
|
|
2347
|
+
if (!text)
|
|
2348
|
+
return false;
|
|
2349
|
+
return text.includes("<tool_call>") || text.includes("[TOOL_CALLS]") || text.includes("<|python_tag|>") || text.includes("<function=") || text.includes("<|tool_calls|>") || text.includes("<tool_calls>") || text.includes("<|action_start|>") || text.includes("<<function>>") || text.includes(">>>") || text.includes("Call:") || text.includes("Action:") || text.includes("functools") || text.includes("<start_function_call>") || text.includes("<|tool_call|>") || text.includes("<|tool_call_start|>") || /tool[\s\u2581]call[\s\u2581]begin/.test(text);
|
|
2350
|
+
}
|
|
2351
|
+
function getAvailableParsers() {
|
|
2352
|
+
return Object.keys(MODEL_PARSERS);
|
|
2353
|
+
}
|
|
2354
|
+
function getGenerationPrefix(family, forcedToolName) {
|
|
2355
|
+
if (!family)
|
|
2356
|
+
return;
|
|
2357
|
+
switch (family) {
|
|
2358
|
+
case "functiongemma":
|
|
2359
|
+
return forcedToolName ? `<start_function_call>call:${forcedToolName}{` : "<start_function_call>call:";
|
|
2360
|
+
default:
|
|
2361
|
+
return;
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
function parseToolCallsFromText(responseText) {
|
|
2365
|
+
const functionGemmaResult = parseFunctionGemma(responseText);
|
|
2366
|
+
if (functionGemmaResult && functionGemmaResult.tool_calls.length > 0) {
|
|
2367
|
+
return {
|
|
2368
|
+
text: functionGemmaResult.content,
|
|
2369
|
+
toolCalls: functionGemmaResult.tool_calls.map((call, index) => ({
|
|
2370
|
+
id: call.id ?? `call_${index}`,
|
|
2371
|
+
name: call.name,
|
|
2372
|
+
input: call.arguments
|
|
2373
|
+
}))
|
|
2374
|
+
};
|
|
2375
|
+
}
|
|
2376
|
+
const looseObject = parseFunctionGemmaLooseObject(responseText);
|
|
2377
|
+
if (looseObject) {
|
|
2378
|
+
return {
|
|
2379
|
+
text: "",
|
|
2380
|
+
toolCalls: [{ id: "call_0", name: "", input: looseObject }]
|
|
2381
|
+
};
|
|
2382
|
+
}
|
|
2383
|
+
const hermesResult = parseHermes(responseText);
|
|
2384
|
+
if (hermesResult && hermesResult.tool_calls.length > 0) {
|
|
2385
|
+
return {
|
|
2386
|
+
text: hermesResult.content,
|
|
2387
|
+
toolCalls: hermesResult.tool_calls.map((call, index) => ({
|
|
2388
|
+
id: call.id ?? `call_${index}`,
|
|
2389
|
+
name: call.name,
|
|
2390
|
+
input: call.arguments
|
|
2391
|
+
}))
|
|
2392
|
+
};
|
|
2393
|
+
}
|
|
2394
|
+
const toolCalls = [];
|
|
2395
|
+
let callIndex = 0;
|
|
2396
|
+
const jsonCandidates = findBalancedBlocks(responseText, "{", "}");
|
|
2397
|
+
const matchedRanges = [];
|
|
2398
|
+
for (const candidate of jsonCandidates) {
|
|
2399
|
+
try {
|
|
2400
|
+
const parsed = JSON.parse(candidate.text);
|
|
2401
|
+
if (parsed.name && (parsed.arguments !== undefined || parsed.parameters !== undefined)) {
|
|
2402
|
+
const id = `call_${callIndex++}`;
|
|
2403
|
+
toolCalls.push({
|
|
2404
|
+
id,
|
|
2405
|
+
name: parsed.name,
|
|
2406
|
+
input: parsed.arguments ?? parsed.parameters ?? {}
|
|
2407
|
+
});
|
|
2408
|
+
matchedRanges.push({ start: candidate.start, end: candidate.end });
|
|
2409
|
+
} else if (parsed.function?.name) {
|
|
2410
|
+
let functionArgs = parsed.function.arguments ?? {};
|
|
2411
|
+
if (typeof functionArgs === "string") {
|
|
2412
|
+
try {
|
|
2413
|
+
functionArgs = JSON.parse(functionArgs);
|
|
2414
|
+
} catch {
|
|
2415
|
+
functionArgs = {};
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
const id = `call_${callIndex++}`;
|
|
2419
|
+
toolCalls.push({
|
|
2420
|
+
id,
|
|
2421
|
+
name: parsed.function.name,
|
|
2422
|
+
input: functionArgs ?? {}
|
|
2423
|
+
});
|
|
2424
|
+
matchedRanges.push({ start: candidate.start, end: candidate.end });
|
|
2425
|
+
}
|
|
2426
|
+
} catch {}
|
|
2427
|
+
}
|
|
2428
|
+
let cleanedText = responseText;
|
|
2429
|
+
if (toolCalls.length > 0) {
|
|
2430
|
+
let result = "";
|
|
2431
|
+
let lastIndex = 0;
|
|
2432
|
+
for (const range of matchedRanges) {
|
|
2433
|
+
result += responseText.slice(lastIndex, range.start);
|
|
2434
|
+
lastIndex = range.end;
|
|
2435
|
+
}
|
|
2436
|
+
result += responseText.slice(lastIndex);
|
|
2437
|
+
cleanedText = result.trim();
|
|
2438
|
+
}
|
|
2439
|
+
return { text: cleanedText, toolCalls };
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
// src/provider-hf-transformers/common/HFT_ToolCalling.ts
|
|
2443
|
+
function getModelTextCandidates(model) {
|
|
2444
|
+
return [model.model_id, model.title, model.description, model.provider_config.model_path].filter((value) => typeof value === "string" && value.length > 0).map((value) => value.toLowerCase());
|
|
2445
|
+
}
|
|
2446
|
+
function detectModelFamilyFromConfig(model) {
|
|
2447
|
+
const candidates = getModelTextCandidates(model);
|
|
2448
|
+
const families = getAvailableParsers();
|
|
2449
|
+
for (const candidate of candidates) {
|
|
2450
|
+
for (const family of families) {
|
|
2451
|
+
if (candidate.includes(family)) {
|
|
2452
|
+
return family;
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
|
+
return null;
|
|
2457
|
+
}
|
|
2458
|
+
function adaptParserResult(result) {
|
|
2459
|
+
return {
|
|
2460
|
+
text: stripModelArtifacts(result.content),
|
|
2461
|
+
toolCalls: result.tool_calls.map((call, index) => ({
|
|
2462
|
+
id: call.id ?? `call_${index}`,
|
|
2463
|
+
name: call.name,
|
|
2464
|
+
input: call.arguments
|
|
2465
|
+
}))
|
|
2466
|
+
};
|
|
2467
|
+
}
|
|
2468
|
+
function forcedToolSelection(input) {
|
|
2469
|
+
if (typeof input.toolChoice === "string" && input.toolChoice !== "auto" && input.toolChoice !== "none") {
|
|
2470
|
+
if (input.toolChoice !== "required") {
|
|
2471
|
+
return input.toolChoice;
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
if (input.toolChoice === "required" && input.tools.length === 1) {
|
|
2475
|
+
return input.tools[0]?.name;
|
|
2476
|
+
}
|
|
2477
|
+
return;
|
|
2478
|
+
}
|
|
2479
|
+
function normalizeParsedToolCalls(input, toolCalls) {
|
|
2480
|
+
const forcedToolName = forcedToolSelection(input);
|
|
2481
|
+
return toolCalls.map((toolCall) => toolCall.name ? toolCall : {
|
|
2482
|
+
...toolCall,
|
|
2483
|
+
name: forcedToolName ?? toolCall.name
|
|
2484
|
+
});
|
|
2485
|
+
}
|
|
2486
|
+
function mapHFTTools(tools) {
|
|
2487
|
+
return tools.map((t) => ({
|
|
2488
|
+
type: "function",
|
|
2489
|
+
function: {
|
|
2490
|
+
name: t.name,
|
|
2491
|
+
description: buildToolDescription(t),
|
|
2492
|
+
parameters: t.inputSchema
|
|
2493
|
+
}
|
|
2494
|
+
}));
|
|
2495
|
+
}
|
|
2496
|
+
function resolveHFTToolsAndMessages(input, messages) {
|
|
2497
|
+
if (input.toolChoice === "none") {
|
|
2498
|
+
return;
|
|
2499
|
+
}
|
|
2500
|
+
if (input.toolChoice === "required") {
|
|
2501
|
+
const requiredInstruction = "You must call at least one tool from the provided tool list when answering.";
|
|
2502
|
+
if (messages.length > 0 && messages[0].role === "system") {
|
|
2503
|
+
messages[0] = { ...messages[0], content: `${messages[0].content}
|
|
2504
|
+
|
|
2505
|
+
${requiredInstruction}` };
|
|
2506
|
+
} else {
|
|
2507
|
+
messages.unshift({ role: "system", content: requiredInstruction });
|
|
2508
|
+
}
|
|
2509
|
+
return mapHFTTools(input.tools);
|
|
2510
|
+
}
|
|
2511
|
+
if (typeof input.toolChoice === "string" && input.toolChoice !== "auto") {
|
|
2512
|
+
const selectedTools = input.tools?.filter((tool) => tool.name === input.toolChoice);
|
|
2513
|
+
const toolsToMap = selectedTools && selectedTools.length > 0 ? selectedTools : input.tools;
|
|
2514
|
+
return mapHFTTools(toolsToMap);
|
|
2515
|
+
}
|
|
2516
|
+
return mapHFTTools(input.tools);
|
|
2517
|
+
}
|
|
2518
|
+
function extractMessageText(content) {
|
|
2519
|
+
if (typeof content === "string") {
|
|
2520
|
+
return content;
|
|
2521
|
+
}
|
|
2522
|
+
if (!Array.isArray(content)) {
|
|
2523
|
+
return String(content ?? "");
|
|
2524
|
+
}
|
|
2525
|
+
return content.filter((block) => block && typeof block === "object" && block.type === "text").map((block) => String(block.text ?? "")).join("");
|
|
2526
|
+
}
|
|
2527
|
+
function parsePossibleToolResponse(content) {
|
|
2528
|
+
const trimmed = content.trim();
|
|
2529
|
+
if (!trimmed)
|
|
2530
|
+
return "";
|
|
2531
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]") || trimmed.startsWith('"') && trimmed.endsWith('"')) {
|
|
2532
|
+
try {
|
|
2533
|
+
return JSON.parse(trimmed);
|
|
2534
|
+
} catch {}
|
|
2535
|
+
}
|
|
2536
|
+
return content;
|
|
2537
|
+
}
|
|
2538
|
+
function buildHFTMessages(input) {
|
|
2539
|
+
const messages = [];
|
|
2540
|
+
if (input.systemPrompt) {
|
|
2541
|
+
messages.push({ role: "system", content: input.systemPrompt });
|
|
2542
|
+
}
|
|
2543
|
+
if (input.toolChoice === "required") {
|
|
2544
|
+
const instruction = "You must call at least one tool from the provided tool list when answering.";
|
|
2545
|
+
if (messages.length > 0 && messages[0].role === "system") {
|
|
2546
|
+
messages[0] = {
|
|
2547
|
+
...messages[0],
|
|
2548
|
+
content: `${messages[0].content}
|
|
2549
|
+
|
|
2550
|
+
${instruction}`
|
|
2551
|
+
};
|
|
2552
|
+
} else {
|
|
2553
|
+
messages.unshift({ role: "system", content: instruction });
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
const sourceMessages = input.messages && input.messages.length > 0 ? input.messages : [{ role: "user", content: input.prompt }];
|
|
2557
|
+
const toolNamesById = new Map;
|
|
2558
|
+
for (const message of sourceMessages) {
|
|
2559
|
+
if (message.role === "user") {
|
|
2560
|
+
messages.push({ role: "user", content: extractMessageText(message.content) });
|
|
2561
|
+
continue;
|
|
2562
|
+
}
|
|
2563
|
+
if (message.role === "assistant" && Array.isArray(message.content)) {
|
|
2564
|
+
const text = message.content.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
2565
|
+
const toolCalls = message.content.filter((block) => block.type === "tool_use").map((block) => {
|
|
2566
|
+
toolNamesById.set(block.id, block.name);
|
|
2567
|
+
return { function: { name: block.name, arguments: block.input } };
|
|
2568
|
+
});
|
|
2569
|
+
if (text || toolCalls.length > 0) {
|
|
2570
|
+
const assistantMsg = { role: "assistant" };
|
|
2571
|
+
if (text)
|
|
2572
|
+
assistantMsg.content = text;
|
|
2573
|
+
if (toolCalls.length > 0)
|
|
2574
|
+
assistantMsg.tool_calls = toolCalls;
|
|
2575
|
+
messages.push(assistantMsg);
|
|
2576
|
+
}
|
|
2577
|
+
continue;
|
|
2578
|
+
}
|
|
2579
|
+
if (message.role === "tool" && Array.isArray(message.content)) {
|
|
2580
|
+
for (const block of message.content) {
|
|
2581
|
+
const toolName = toolNamesById.get(block.tool_use_id);
|
|
2582
|
+
if (!toolName)
|
|
2583
|
+
continue;
|
|
2584
|
+
messages.push({
|
|
2585
|
+
role: "tool",
|
|
2586
|
+
name: toolName,
|
|
2587
|
+
content: parsePossibleToolResponse(extractMessageText(block.content))
|
|
2588
|
+
});
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
return messages;
|
|
2593
|
+
}
|
|
2594
|
+
function selectHFTTools(input) {
|
|
2595
|
+
if (input.toolChoice === "none")
|
|
2596
|
+
return;
|
|
2597
|
+
if (typeof input.toolChoice === "string" && input.toolChoice !== "auto" && input.toolChoice !== "required") {
|
|
2598
|
+
const selected = input.tools.filter((t) => t.name === input.toolChoice);
|
|
2599
|
+
return mapHFTTools(selected.length > 0 ? selected : input.tools);
|
|
2600
|
+
}
|
|
2601
|
+
return mapHFTTools(input.tools);
|
|
2602
|
+
}
|
|
2603
|
+
function hasToolMessages(input) {
|
|
2604
|
+
return input.messages?.some((m) => m.role === "tool") ?? false;
|
|
2605
|
+
}
|
|
2606
|
+
function buildPromptAndPrefix(tokenizer, input, modelFamily) {
|
|
2607
|
+
let basePrompt;
|
|
2608
|
+
if (hasToolMessages(input)) {
|
|
2609
|
+
const messages = buildHFTMessages(input);
|
|
2610
|
+
const tools = selectHFTTools(input);
|
|
2611
|
+
basePrompt = tokenizer.apply_chat_template(messages, {
|
|
2612
|
+
tools,
|
|
2613
|
+
tokenize: false,
|
|
2614
|
+
add_generation_prompt: true
|
|
2615
|
+
});
|
|
2616
|
+
} else {
|
|
2617
|
+
const messages = toTextFlatMessages(input);
|
|
2618
|
+
const tools = resolveHFTToolsAndMessages(input, messages);
|
|
2619
|
+
basePrompt = tokenizer.apply_chat_template(messages, {
|
|
2620
|
+
tools,
|
|
2621
|
+
tokenize: false,
|
|
2622
|
+
add_generation_prompt: true
|
|
2623
|
+
});
|
|
2624
|
+
}
|
|
2625
|
+
const responsePrefix = input.toolChoice === "none" || hasToolMessages(input) ? undefined : getGenerationPrefix(modelFamily, forcedToolSelection(input));
|
|
2626
|
+
return {
|
|
2627
|
+
prompt: responsePrefix ? `${basePrompt}${responsePrefix}` : basePrompt,
|
|
2628
|
+
responsePrefix
|
|
2629
|
+
};
|
|
2630
|
+
}
|
|
2631
|
+
var HFT_ToolCalling = async (input, model, onProgress, signal) => {
|
|
2632
|
+
const generateText = await getPipeline(model, onProgress, {}, signal);
|
|
2633
|
+
const { TextStreamer } = await loadTransformersSDK();
|
|
2634
|
+
const hfTokenizer = generateText.tokenizer;
|
|
2635
|
+
const hfModel = generateText.model;
|
|
2636
|
+
const streamer = createTextStreamer(hfTokenizer, onProgress, TextStreamer, signal);
|
|
2637
|
+
const modelFamily = detectModelFamilyFromConfig(model);
|
|
2638
|
+
const { prompt, responsePrefix } = buildPromptAndPrefix(hfTokenizer, input, modelFamily);
|
|
2639
|
+
const inputs = hfTokenizer(prompt, { return_tensors: "pt" });
|
|
2640
|
+
const output = await hfModel.generate({
|
|
2641
|
+
...inputs,
|
|
2642
|
+
max_new_tokens: input.maxTokens ?? 1024,
|
|
2643
|
+
streamer
|
|
2644
|
+
});
|
|
2645
|
+
const promptLen = inputs.input_ids.dims[1];
|
|
2646
|
+
const seqLen = output.dims[1];
|
|
2647
|
+
const newTokens = output.slice(0, [promptLen, seqLen], null);
|
|
2648
|
+
const decoded = hfTokenizer.decode(newTokens, {
|
|
2649
|
+
skip_special_tokens: false
|
|
2650
|
+
});
|
|
2651
|
+
const parseableText = responsePrefix ? `${responsePrefix}${decoded}` : decoded;
|
|
2652
|
+
const { text, toolCalls } = adaptParserResult(parseToolCalls(parseableText, { parser: modelFamily }));
|
|
2653
|
+
return {
|
|
2654
|
+
text,
|
|
2655
|
+
toolCalls: filterValidToolCalls(normalizeParsedToolCalls(input, toolCalls), input.tools)
|
|
2656
|
+
};
|
|
2657
|
+
};
|
|
2658
|
+
var HFT_ToolCalling_Stream = async function* (input, model, signal) {
|
|
2659
|
+
const noopProgress = () => {};
|
|
2660
|
+
const generateText = await getPipeline(model, noopProgress, {}, signal);
|
|
2661
|
+
const { TextStreamer } = await loadTransformersSDK();
|
|
2662
|
+
const modelFamily = detectModelFamilyFromConfig(model);
|
|
2663
|
+
const { prompt, responsePrefix } = buildPromptAndPrefix(generateText.tokenizer, input, modelFamily);
|
|
2664
|
+
const innerQueue = createStreamEventQueue();
|
|
2665
|
+
const outerQueue = createStreamEventQueue();
|
|
2666
|
+
const streamer = createStreamingTextStreamer(generateText.tokenizer, innerQueue, TextStreamer, signal);
|
|
2667
|
+
let fullText = "";
|
|
2668
|
+
const filter = createToolCallMarkupFilter((text) => {
|
|
2669
|
+
outerQueue.push({ type: "text-delta", port: "text", textDelta: text });
|
|
2670
|
+
});
|
|
2671
|
+
const originalPush = innerQueue.push;
|
|
2672
|
+
innerQueue.push = (event) => {
|
|
2673
|
+
if (event.type === "text-delta" && "textDelta" in event) {
|
|
2674
|
+
fullText += event.textDelta;
|
|
2675
|
+
filter.feed(event.textDelta);
|
|
2676
|
+
} else {
|
|
2677
|
+
outerQueue.push(event);
|
|
2678
|
+
}
|
|
2679
|
+
originalPush(event);
|
|
2680
|
+
};
|
|
2681
|
+
const originalDone = innerQueue.done;
|
|
2682
|
+
innerQueue.done = () => {
|
|
2683
|
+
filter.flush();
|
|
2684
|
+
outerQueue.done();
|
|
2685
|
+
originalDone();
|
|
2686
|
+
};
|
|
2687
|
+
const originalError = innerQueue.error;
|
|
2688
|
+
innerQueue.error = (e) => {
|
|
2689
|
+
filter.flush();
|
|
2690
|
+
outerQueue.error(e);
|
|
2691
|
+
originalError(e);
|
|
2692
|
+
};
|
|
2693
|
+
const pipelinePromise = generateText(prompt, {
|
|
2694
|
+
max_new_tokens: input.maxTokens ?? 1024,
|
|
2695
|
+
temperature: input.temperature ?? undefined,
|
|
2696
|
+
return_full_text: false,
|
|
2697
|
+
streamer
|
|
2698
|
+
}).then(() => innerQueue.done(), (err) => innerQueue.error(err));
|
|
2699
|
+
yield* outerQueue.iterable;
|
|
2700
|
+
await pipelinePromise;
|
|
2701
|
+
const parseableFullText = responsePrefix ? `${responsePrefix}${fullText}` : fullText;
|
|
2702
|
+
const { text: cleanedText, toolCalls } = adaptParserResult(parseToolCalls(parseableFullText, { parser: modelFamily }));
|
|
2703
|
+
const validToolCalls = filterValidToolCalls(normalizeParsedToolCalls(input, toolCalls), input.tools);
|
|
2704
|
+
if (validToolCalls.length > 0) {
|
|
2705
|
+
yield { type: "object-delta", port: "toolCalls", objectDelta: [...validToolCalls] };
|
|
2706
|
+
}
|
|
2707
|
+
yield {
|
|
2708
|
+
type: "finish",
|
|
2709
|
+
data: { text: cleanedText, toolCalls: validToolCalls }
|
|
2710
|
+
};
|
|
2711
|
+
};
|
|
2712
|
+
|
|
1483
2713
|
// src/provider-hf-transformers/common/HFT_Unload.ts
|
|
1484
2714
|
init_HFT_Pipeline();
|
|
1485
2715
|
function hasBrowserCacheStorage() {
|
|
@@ -1555,6 +2785,7 @@ var HFT_TASKS = {
|
|
|
1555
2785
|
ImageEmbeddingTask: HFT_ImageEmbedding,
|
|
1556
2786
|
ImageClassificationTask: HFT_ImageClassification,
|
|
1557
2787
|
ObjectDetectionTask: HFT_ObjectDetection,
|
|
2788
|
+
ToolCallingTask: HFT_ToolCalling,
|
|
1558
2789
|
StructuredGenerationTask: HFT_StructuredGeneration,
|
|
1559
2790
|
ModelSearchTask: HFT_ModelSearch
|
|
1560
2791
|
};
|
|
@@ -1564,6 +2795,7 @@ var HFT_STREAM_TASKS = {
|
|
|
1564
2795
|
TextSummaryTask: HFT_TextSummary_Stream,
|
|
1565
2796
|
TextQuestionAnswerTask: HFT_TextQuestionAnswer_Stream,
|
|
1566
2797
|
TextTranslationTask: HFT_TextTranslation_Stream,
|
|
2798
|
+
ToolCallingTask: HFT_ToolCalling_Stream,
|
|
1567
2799
|
StructuredGenerationTask: HFT_StructuredGeneration_Stream
|
|
1568
2800
|
};
|
|
1569
2801
|
var HFT_REACTIVE_TASKS = {
|
|
@@ -1571,9 +2803,7 @@ var HFT_REACTIVE_TASKS = {
|
|
|
1571
2803
|
};
|
|
1572
2804
|
|
|
1573
2805
|
// src/provider-hf-transformers/HuggingFaceTransformersQueuedProvider.ts
|
|
1574
|
-
import {
|
|
1575
|
-
QueuedAiProvider
|
|
1576
|
-
} from "@workglow/ai";
|
|
2806
|
+
import { QueuedAiProvider } from "@workglow/ai";
|
|
1577
2807
|
var GPU_DEVICES = new Set(["webgpu", "gpu", "metal"]);
|
|
1578
2808
|
var HFT_CPU_QUEUE_CONCURRENCY_PRODUCTION = 4;
|
|
1579
2809
|
function hftIsAutomatedTestEnvironment() {
|
|
@@ -1623,6 +2853,7 @@ class HuggingFaceTransformersQueuedProvider extends QueuedAiProvider {
|
|
|
1623
2853
|
"ImageEmbeddingTask",
|
|
1624
2854
|
"ImageClassificationTask",
|
|
1625
2855
|
"ObjectDetectionTask",
|
|
2856
|
+
"ToolCallingTask",
|
|
1626
2857
|
"ModelSearchTask"
|
|
1627
2858
|
];
|
|
1628
2859
|
constructor(tasks, streamTasks, reactiveTasks) {
|
|
@@ -1658,9 +2889,7 @@ async function registerHuggingFaceTransformersInline(options) {
|
|
|
1658
2889
|
import { getLogger as getLogger7, globalServiceRegistry, WORKER_SERVER } from "@workglow/util/worker";
|
|
1659
2890
|
|
|
1660
2891
|
// src/provider-hf-transformers/HuggingFaceTransformersProvider.ts
|
|
1661
|
-
import {
|
|
1662
|
-
AiProvider
|
|
1663
|
-
} from "@workglow/ai/worker";
|
|
2892
|
+
import { AiProvider } from "@workglow/ai/worker";
|
|
1664
2893
|
class HuggingFaceTransformersProvider extends AiProvider {
|
|
1665
2894
|
name = HF_TRANSFORMERS_ONNX;
|
|
1666
2895
|
displayName = "Hugging Face Transformers (ONNX)";
|
|
@@ -1687,6 +2916,7 @@ class HuggingFaceTransformersProvider extends AiProvider {
|
|
|
1687
2916
|
"ImageEmbeddingTask",
|
|
1688
2917
|
"ImageClassificationTask",
|
|
1689
2918
|
"ObjectDetectionTask",
|
|
2919
|
+
"ToolCallingTask",
|
|
1690
2920
|
"ModelSearchTask"
|
|
1691
2921
|
];
|
|
1692
2922
|
constructor(tasks, streamTasks, reactiveTasks) {
|
|
@@ -1719,6 +2949,7 @@ export {
|
|
|
1719
2949
|
hasCachedPipeline,
|
|
1720
2950
|
getPipelineCacheKey,
|
|
1721
2951
|
getPipeline,
|
|
2952
|
+
createToolCallMarkupFilter,
|
|
1722
2953
|
clearPipelineCache,
|
|
1723
2954
|
QuantizationDataType,
|
|
1724
2955
|
PipelineUseCase,
|
|
@@ -1733,4 +2964,4 @@ export {
|
|
|
1733
2964
|
HFT_NULL_PROCESSOR_PREFIX
|
|
1734
2965
|
};
|
|
1735
2966
|
|
|
1736
|
-
//# debugId=
|
|
2967
|
+
//# debugId=7D0C58F098156BAF64756E2164756E21
|