Skip to content

Commit ed88109

Browse files
elemdosclaude
andcommitted
fix: auto-extract code when Gemini writes tool calls as text
Some models (particularly Gemini) occasionally write write_code() as text instead of using the function calling API. This detects and auto-applies the code as a fallback. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 33269d9 commit ed88109

File tree

1 file changed

+32
-0
lines changed

1 file changed

+32
-0
lines changed

src/lib/ai/sdk-agent.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,25 @@ For file fields, use placeholder images for demo data: "placeholder.jpg" (square
455455
return tools
456456
}
457457

458+
// Extract code from failed tool calls (when model writes tool call as text instead of using API)
459+
function extract_failed_tool_call(text: string): string | null {
460+
// Pattern 1: write_code({ code: "..." }) or write_code({ code: `...` })
461+
const fn_match = text.match(/write_code\s*\(\s*\{\s*code:\s*[`"']([\s\S]+?)[`"']\s*\}\s*\)/)
462+
if (fn_match) return fn_match[1]
463+
464+
// Pattern 2: JSON-style { "name": "write_code", ... "code": "..." }
465+
const json_match = text.match(/"name":\s*"write_code"[\s\S]*?"code":\s*[`"']([\s\S]+?)[`"']/)
466+
if (json_match) return json_match[1]
467+
468+
// Pattern 3: ```svelte block after mentioning write_code
469+
if (text.includes('write_code')) {
470+
const svelte_match = text.match(/```svelte\n([\s\S]+?)```/)
471+
if (svelte_match && svelte_match[1].length > 100) return svelte_match[1]
472+
}
473+
474+
return null
475+
}
476+
458477
export async function run_agent(
459478
config: AgentConfig,
460479
project_id: string,
@@ -516,6 +535,7 @@ export async function run_agent(
516535
// Stream both text and tool calls using fullStream
517536
let full_text = ''
518537
const seen_tool_calls = new Set<string>()
538+
let had_write_code_call = false
519539

520540
for await (const part of result.fullStream) {
521541
if (part.type === 'text-delta') {
@@ -529,6 +549,7 @@ export async function run_agent(
529549
const toolCallId = (part as any).toolCallId
530550
if (toolCallId && seen_tool_calls.has(toolCallId)) continue
531551
if (toolCallId) seen_tool_calls.add(toolCallId)
552+
if (part.toolName === 'write_code') had_write_code_call = true
532553
callbacks?.onToolCall?.(toolCallId, part.toolName, (part as any).input || {})
533554
} else if (part.type === 'tool-result') {
534555
// AI SDK 5.x uses 'output' property for tool results
@@ -548,6 +569,17 @@ export async function run_agent(
548569
}
549570
}
550571

572+
// Fallback: if model wrote code as text instead of calling write_code tool, extract and apply it
573+
if (!had_write_code_call) {
574+
const missed_code = extract_failed_tool_call(full_text)
575+
if (missed_code) {
576+
console.log('[Agent] Detected failed write_code call in text, auto-applying code')
577+
await updateProject(project_id, { frontend_code: missed_code })
578+
callbacks?.onToolCall?.('auto-extract', 'write_code', { code: '[extracted from response]' })
579+
callbacks?.onToolResult?.('auto-extract', 'write_code', 'Auto-applied code from response')
580+
}
581+
}
582+
551583
const usage = await result.usage
552584

553585
return {

0 commit comments

Comments
 (0)