@sema-agent/core
Version:
Stateless, task-oriented AI agent core
77 lines (76 loc) • 2.35 kB
JavaScript
import { mintFallbackToolCallId } from "./tool-call-id.js";
const TAG_RE = /<(tool_call|function_call|tool_calls|function_calls)>\s*([\s\S]*?)\s*<\/\1>/gi;
const FENCE_RE = /```(?:json|tool_call|tool|function)?\s*([\s\S]*?)```/gi;
function asRecord(v) {
return v && typeof v === "object" && !Array.isArray(v) ? v : undefined;
}
function extractCall(obj, toolNames, framed) {
const rec = asRecord(obj);
if (!rec) {
return undefined;
}
const fn = asRecord(rec.function);
const name = rec.name ?? rec.tool ?? rec.tool_name ?? fn?.name;
if (typeof name !== "string" || !toolNames.has(name)) {
return undefined;
}
const rawArgs = rec.arguments ?? rec.parameters ?? rec.input ?? rec.args ?? fn?.arguments;
let args;
if (rawArgs === undefined) {
if (!framed)
return undefined;
args = {};
}
else if (typeof rawArgs === "string") {
let parsed;
try {
parsed = JSON.parse(rawArgs);
}
catch {
return undefined;
}
const rec2 = asRecord(parsed);
if (!rec2)
return undefined;
args = rec2;
}
else {
const rec2 = asRecord(rawArgs);
if (!rec2)
return undefined;
args = rec2;
}
return {
type: "toolCall",
id: mintFallbackToolCallId("repair_"),
name,
arguments: args,
};
}
function parseBlock(raw, toolNames, out, framed) {
let parsed;
try {
parsed = JSON.parse(raw.trim());
}
catch {
return "none";
}
const items = Array.isArray(parsed) ? parsed : [parsed];
let promoted = 0;
for (const item of items) {
const call = extractCall(item, toolNames, framed);
if (call) {
out.push(call);
promoted++;
}
}
if (promoted === 0)
return "none";
return promoted === items.length ? "all" : "some";
}
export function repairTextToolCalls(text, toolNames) {
const toolCalls = [];
let cleaned = text.replace(TAG_RE, (m, _tag, inner) => parseBlock(inner, toolNames, toolCalls, true) === "all" ? "" : m);
cleaned = cleaned.replace(FENCE_RE, (m, inner) => parseBlock(inner, toolNames, toolCalls, false) === "all" ? "" : m);
return { toolCalls, cleanedText: cleaned.trim() };
}