feat(cross-cutting): added multi-syntax in-band tool-call support for runtime tool conversion
- Added optional Agent and SDK tool-call syntax controls (`toolCallSyntax`, `PI_OWNED_TOOLS`) for owned calls. - Added in-band grammar scanners and renderers for Anthropic, DeepSeek, GLM, Hermes, Kimi, PI, and Qwen3. - Added supportsTools propagation and model schema updates to route unsupported models to fallback syntax. - Replaced stream-markup parsing with syntax-specific in-band scanners and event conversion.
This commit is contained in:
@@ -639,6 +639,11 @@ class LiveProgress {
|
||||
const metaParts = [op, target].filter((v): v is string => Boolean(v));
|
||||
const meta = metaParts.length > 0 ? paint(ANSI.dim, metaParts.join(" ")) : "";
|
||||
console.log(` ${tag}${meta ? ` ${meta}` : ""} ${clipped}`);
|
||||
if (failure.rawBlock) {
|
||||
const rawLine = failure.rawBlock.replace(/\s+/g, " ").trim();
|
||||
const clippedRaw = rawLine.length > 240 ? `${rawLine.slice(0, 237)}...` : rawLine;
|
||||
console.log(` ${paint(ANSI.dim, "raw")} ${clippedRaw}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -780,10 +780,16 @@ export interface ToolCallStats {
|
||||
totalInputChars: number;
|
||||
}
|
||||
|
||||
interface PendingEditCall {
|
||||
args: unknown;
|
||||
rawBlock?: string;
|
||||
}
|
||||
|
||||
export interface EditFailure {
|
||||
toolCallId: string;
|
||||
args: unknown;
|
||||
error: string;
|
||||
rawBlock?: string;
|
||||
category?: EditFailureCategory;
|
||||
}
|
||||
|
||||
@@ -1196,9 +1202,16 @@ async function runSingleTask(
|
||||
});
|
||||
break;
|
||||
}
|
||||
const pendingEdits = new Map<string, unknown>();
|
||||
|
||||
const pendingEdits = new Map<string, PendingEditCall>();
|
||||
const rawToolBlocks = new Map<string, string>();
|
||||
for (const event of events) {
|
||||
if (event.type === "message_end") {
|
||||
for (const raw of extractAssistantToolRawBlocks(event)) {
|
||||
rawToolBlocks.set(raw.id, raw.rawBlock);
|
||||
const pending = pendingEdits.get(raw.id);
|
||||
if (pending) pending.rawBlock = raw.rawBlock;
|
||||
}
|
||||
}
|
||||
if (event.type === "tool_execution_start") {
|
||||
const e = event as { toolName?: string; toolCallId?: string; args?: unknown };
|
||||
const toolName = e.toolName;
|
||||
@@ -1206,7 +1219,8 @@ async function runSingleTask(
|
||||
toolStats.read++;
|
||||
} else if (isEditTool(toolName)) {
|
||||
toolStats.edit++;
|
||||
if (e.toolCallId) pendingEdits.set(e.toolCallId, e.args);
|
||||
if (e.toolCallId)
|
||||
pendingEdits.set(e.toolCallId, { args: e.args, rawBlock: rawToolBlocks.get(e.toolCallId) });
|
||||
} else if (toolName === "write") {
|
||||
toolStats.write++;
|
||||
}
|
||||
@@ -1218,7 +1232,8 @@ async function runSingleTask(
|
||||
} else if (event.type === "tool_execution_end") {
|
||||
const e = event as { toolName?: string; toolCallId?: string; isError?: boolean; result?: unknown };
|
||||
if (isEditTool(e.toolName) && e.toolCallId && pendingEdits.has(e.toolCallId)) {
|
||||
const args = pendingEdits.get(e.toolCallId) ?? null;
|
||||
const pendingEdit = pendingEdits.get(e.toolCallId) ?? { args: null };
|
||||
const args = pendingEdit.args;
|
||||
pendingEdits.delete(e.toolCallId);
|
||||
if (config.editVariant === "hashline" && args) {
|
||||
const counts = countHashlineEditSubtypes(args);
|
||||
@@ -1238,6 +1253,7 @@ async function runSingleTask(
|
||||
toolCallId: e.toolCallId,
|
||||
args,
|
||||
error,
|
||||
rawBlock: pendingEdit.rawBlock,
|
||||
category: categorizeEditFailure(error, args),
|
||||
});
|
||||
} else {
|
||||
@@ -1410,6 +1426,27 @@ function extractToolErrorMessage(result: unknown): string {
|
||||
}
|
||||
}
|
||||
|
||||
function extractAssistantToolRawBlocks(event: {
|
||||
type: string;
|
||||
[key: string]: unknown;
|
||||
}): Array<{ id: string; rawBlock: string }> {
|
||||
const message = event.message;
|
||||
if (message === null || typeof message !== "object") return [];
|
||||
const role = (message as { role?: unknown }).role;
|
||||
if (role !== "assistant") return [];
|
||||
const content = (message as { content?: unknown }).content;
|
||||
if (!Array.isArray(content)) return [];
|
||||
const rawBlocks: Array<{ id: string; rawBlock: string }> = [];
|
||||
for (const block of content) {
|
||||
if (block === null || typeof block !== "object") continue;
|
||||
const typedBlock = block as { type?: unknown; id?: unknown; rawBlock?: unknown };
|
||||
if (typedBlock.type !== "toolCall") continue;
|
||||
if (typeof typedBlock.id !== "string" || typeof typedBlock.rawBlock !== "string") continue;
|
||||
rawBlocks.push({ id: typedBlock.id, rawBlock: typedBlock.rawBlock });
|
||||
}
|
||||
return rawBlocks;
|
||||
}
|
||||
|
||||
function shuffle<T>(items: T[]): T[] {
|
||||
const copy = items.slice();
|
||||
for (let i = copy.length - 1; i > 0; i--) {
|
||||
|
||||
Reference in New Issue
Block a user