fix(builtin): disable tools during continue/retry to force frontmatter output
Agent was using all continue turns to keep calling tools instead of outputting the required frontmatter. Now continue runs with noTools=true, forcing LLM to emit text-only response. Also supports null tools in chatCompletionWithTools to omit tools from the API request entirely.
This commit is contained in:
+1
-1
@@ -9,7 +9,7 @@
|
|||||||
"check": "bunx tsc --build && biome check . && bash scripts/lint-log-tags.sh",
|
"check": "bunx tsc --build && biome check . && bash scripts/lint-log-tags.sh",
|
||||||
"typecheck": "bunx tsc --build",
|
"typecheck": "bunx tsc --build",
|
||||||
"format": "biome format --write .",
|
"format": "biome format --write .",
|
||||||
"test": "bun run --filter '*' test",
|
"test": "bun run --filter './packages/*' test",
|
||||||
"changeset": "bunx changeset",
|
"changeset": "bunx changeset",
|
||||||
"version": "bunx changeset version",
|
"version": "bunx changeset version",
|
||||||
"release": "bun run build && bun test && node scripts/publish-all.mjs"
|
"release": "bun run build && bun test && node scripts/publish-all.mjs"
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ async function runBuiltinWithMessages(
|
|||||||
session: SessionRecord,
|
session: SessionRecord,
|
||||||
store: Store,
|
store: Store,
|
||||||
maxTurns: number,
|
maxTurns: number,
|
||||||
|
noTools: boolean,
|
||||||
): Promise<AgentRunResult> {
|
): Promise<AgentRunResult> {
|
||||||
const loopResult = await runBuiltinLoop({
|
const loopResult = await runBuiltinLoop({
|
||||||
provider,
|
provider,
|
||||||
@@ -74,6 +75,7 @@ async function runBuiltinWithMessages(
|
|||||||
maxTurns,
|
maxTurns,
|
||||||
storageRoot,
|
storageRoot,
|
||||||
sessionId: session.sessionId,
|
sessionId: session.sessionId,
|
||||||
|
noTools,
|
||||||
});
|
});
|
||||||
|
|
||||||
session.messages = loopResult.messages;
|
session.messages = loopResult.messages;
|
||||||
@@ -119,6 +121,7 @@ async function runBuiltin(ctx: AgentContext): Promise<AgentRunResult> {
|
|||||||
session,
|
session,
|
||||||
ctx.store,
|
ctx.store,
|
||||||
BUILTIN_MAX_TURNS,
|
BUILTIN_MAX_TURNS,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,6 +144,7 @@ async function continueBuiltin(
|
|||||||
session,
|
session,
|
||||||
store,
|
store,
|
||||||
BUILTIN_CONTINUE_MAX_TURNS,
|
BUILTIN_CONTINUE_MAX_TURNS,
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,8 +96,17 @@ function serializeMessage(message: ChatMessage): Record<string, unknown> {
|
|||||||
export async function chatCompletionWithTools(
|
export async function chatCompletionWithTools(
|
||||||
provider: ResolvedLlmProvider,
|
provider: ResolvedLlmProvider,
|
||||||
messages: ChatMessage[],
|
messages: ChatMessage[],
|
||||||
tools: OpenAiToolDefinition[],
|
tools: OpenAiToolDefinition[] | null,
|
||||||
): Promise<LlmAssistantResponse> {
|
): Promise<LlmAssistantResponse> {
|
||||||
|
const body: Record<string, unknown> = {
|
||||||
|
model: provider.model,
|
||||||
|
messages: messages.map(serializeMessage),
|
||||||
|
};
|
||||||
|
if (tools !== null && tools.length > 0) {
|
||||||
|
body.tools = tools;
|
||||||
|
body.tool_choice = "auto";
|
||||||
|
}
|
||||||
|
|
||||||
let response: Response;
|
let response: Response;
|
||||||
try {
|
try {
|
||||||
response = await fetch(chatUrl(provider.baseUrl), {
|
response = await fetch(chatUrl(provider.baseUrl), {
|
||||||
@@ -106,12 +115,7 @@ export async function chatCompletionWithTools(
|
|||||||
Authorization: `Bearer ${provider.apiKey}`,
|
Authorization: `Bearer ${provider.apiKey}`,
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify(body),
|
||||||
model: provider.model,
|
|
||||||
messages: messages.map(serializeMessage),
|
|
||||||
tools,
|
|
||||||
tool_choice: "auto",
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
const message = cause instanceof Error ? cause.message : String(cause);
|
const message = cause instanceof Error ? cause.message : String(cause);
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ export type RunBuiltinLoopOptions = {
|
|||||||
maxTurns: number;
|
maxTurns: number;
|
||||||
storageRoot: string;
|
storageRoot: string;
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
|
/** When true, do not provide tools — force LLM to emit text only. */
|
||||||
|
noTools: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RunBuiltinLoopResult = {
|
export type RunBuiltinLoopResult = {
|
||||||
@@ -73,13 +75,17 @@ export async function runBuiltinLoop(
|
|||||||
options: RunBuiltinLoopOptions,
|
options: RunBuiltinLoopOptions,
|
||||||
): Promise<RunBuiltinLoopResult> {
|
): Promise<RunBuiltinLoopResult> {
|
||||||
const messages = [...options.messages];
|
const messages = [...options.messages];
|
||||||
const openAiTools = builtinToolsToOpenAi(getBuiltinTools());
|
const openAiTools = options.noTools ? [] : builtinToolsToOpenAi(getBuiltinTools());
|
||||||
let finalText = "";
|
let finalText = "";
|
||||||
let turnCount = 0;
|
let turnCount = 0;
|
||||||
|
|
||||||
for (let turn = 0; turn < options.maxTurns; turn++) {
|
for (let turn = 0; turn < options.maxTurns; turn++) {
|
||||||
log("8K2M4N7P", `builtin loop turn ${turn + 1}/${options.maxTurns}`);
|
log("8K2M4N7P", `builtin loop turn ${turn + 1}/${options.maxTurns}`);
|
||||||
const response = await chatCompletionWithTools(options.provider, messages, openAiTools);
|
const response = await chatCompletionWithTools(
|
||||||
|
options.provider,
|
||||||
|
messages,
|
||||||
|
openAiTools.length > 0 ? openAiTools : null,
|
||||||
|
);
|
||||||
|
|
||||||
const assistantMessage: ChatMessage = {
|
const assistantMessage: ChatMessage = {
|
||||||
role: "assistant",
|
role: "assistant",
|
||||||
|
|||||||
Reference in New Issue
Block a user