fix: parse tagged local-model tool calls
This commit is contained in:
@@ -91,6 +91,10 @@ export class TerminalAgent {
|
||||
const decision = parseDecision(response?.text);
|
||||
if (!decision) {
|
||||
const answer = String(response?.text || '').trim() || 'The agent returned no usable response.';
|
||||
if (looksLikeProtocolPayload(answer)) {
|
||||
working += '\n\nPROTOCOL ERROR: The previous tool syntax was malformed. Return the documented JSON tool_call or final object only.';
|
||||
continue;
|
||||
}
|
||||
this.lastTraceByChat.set(key, trace);
|
||||
return { answer, trace };
|
||||
}
|
||||
@@ -272,6 +276,8 @@ Output exactly one JSON object without markdown:
|
||||
|
||||
function parseDecision(text) {
|
||||
let value = String(text || '').trim().replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/, '');
|
||||
const tagged = parseTaggedToolCall(value);
|
||||
if (tagged) return tagged;
|
||||
const match = value.match(/\{[\s\S]*\}/);
|
||||
if (match) value = match[0];
|
||||
try {
|
||||
@@ -282,9 +288,53 @@ function parseDecision(text) {
|
||||
}
|
||||
}
|
||||
|
||||
function parseTaggedToolCall(text) {
|
||||
const callMatch = String(text || '').match(/<\|?tool_call\|?>\s*call:([a-z0-9_]+)\s*(\{[\s\S]*?\})\s*<(?:\/tool_call|tool_call\|)>/i);
|
||||
if (callMatch) {
|
||||
const argumentsValue = parseTaggedArguments(callMatch[2]);
|
||||
if (argumentsValue) {
|
||||
return {
|
||||
type: 'tool_call',
|
||||
tool: callMatch[1],
|
||||
arguments: argumentsValue,
|
||||
rationale: 'Model requested an allowlisted tool through tagged protocol.',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const jsonMatch = String(text || '').match(/<\|?tool_call\|?>\s*(\{[\s\S]*\})\s*<(?:\/tool_call|tool_call\|)>/i);
|
||||
if (!jsonMatch) return null;
|
||||
try {
|
||||
const value = JSON.parse(jsonMatch[1]);
|
||||
const tool = value.name || value.tool;
|
||||
const args = value.arguments || value.parameters || {};
|
||||
if (!/^[a-z0-9_]+$/i.test(String(tool || '')) || !args || Array.isArray(args) || typeof args !== 'object') return null;
|
||||
return { type: 'tool_call', tool: String(tool), arguments: args, rationale: 'Model requested an allowlisted tool through tagged protocol.' };
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function parseTaggedArguments(value) {
|
||||
const input = String(value || '').trim().slice(0, 4000);
|
||||
try {
|
||||
const parsed = JSON.parse(input);
|
||||
return parsed && !Array.isArray(parsed) && typeof parsed === 'object' ? parsed : null;
|
||||
} catch {
|
||||
try {
|
||||
const normalized = input.replace(/([{,]\s*)([a-z_][a-z0-9_]*)\s*:/gi, '$1"$2":');
|
||||
const parsed = JSON.parse(normalized);
|
||||
return parsed && !Array.isArray(parsed) && typeof parsed === 'object' ? parsed : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function looksLikeProtocolPayload(text) {
|
||||
return /"?type"?\s*:\s*"?(?:tool_call|final)\b/i.test(text)
|
||||
|| /"?tool"?\s*:\s*"?[a-z0-9_]+/i.test(text);
|
||||
|| /"?tool"?\s*:\s*"?[a-z0-9_]+/i.test(text)
|
||||
|| /<\|?tool_call\|?>|<tool_call\|>|call:[a-z0-9_]+\s*\{/i.test(text);
|
||||
}
|
||||
|
||||
function finalizationFailureMessage(input) {
|
||||
|
||||
Reference in New Issue
Block a user