Screenshot to Code Agent Tools Architecture: How create_file and edit_file Work
The create_file and edit_file tools in the screenshot-to-code repository follow a declarative schema-driven architecture where AgentToolRuntime dispatches LLM requests to type-safe handlers that manipulate shared AgentFileState, validating inputs and returning structured ToolExecutionResult objects with human-readable summaries.
The abi/screenshot-to-code repository implements a clean, extensible tool framework that lets Large Language Models safely manipulate HTML files through canonical definitions and runtime validation. Understanding this screenshot to code agent tools architecture reveals how the system prevents common LLM coding errors while maintaining state across multi-step file operations.
Core Components of the Tool Framework
The architecture separates concerns across five key modules, each handling a distinct layer of the tool execution pipeline.
Canonical Tool Definitions
All tool specifications live in backend/agent/tools/definitions.py. This file exposes JSON-schema descriptions for each available tool, defining the exact name, human-readable description, and required input parameters. These schemas drive validation for both the LLM (via OpenAI, Gemini, or Anthropic tool descriptions) and the runtime argument parsing.
Typed Data Structures
The backend/agent/tools/types.py file defines the data contracts that govern tool communication:
ToolCall– Represents an incoming request from the LLM, containing the tool name and arguments dictionaryToolExecutionResult– Wraps the outcome withok(boolean status),result(payload),summary(concise description), and optional updated file contentCanonicalToolDefinition– The schema definition used to register tools with the LLM provider
Runtime Dispatcher
backend/agent/tools/runtime.py contains the AgentToolRuntime class, which serves as the central dispatcher. It receives a ToolCall, validates the JSON payload against the expected schema, and forwards execution to the concrete implementation (_create_file, _edit_file, etc.). The runtime also injects helper state and optional services like image generation or background removal.
File State Container
Mutable state management lives in backend/agent/state.py via the AgentFileState class. This container holds the current file path and HTML content, shared across tool invocations so that edit_file operates on the file produced by an earlier create_file call. The overall agent controller populates this state from prior tool results.
Summarization Helpers
Before returning results to the LLM, the system generates human-readable descriptions of what occurred. The backend/agent/tools/summaries.py module builds concise summaries—such as previews of created content or the number of replacements applied—enabling the LLM to understand context without parsing raw HTML.
Execution Flow: How create_file Works
When the LLM decides to create a new HTML file, the system processes the request through five distinct stages:
-
Tool invocation. The LLM emits a tool call named
"create_file"with a JSON arguments object containingpath(defaulting toindex.html) andcontent. This maps to theToolCalldataclass defined inbackend/agent/tools/types.py. -
Runtime routing. The
AgentToolRuntime.execute()method inbackend/agent/tools/runtime.pyvalidates the payload and dispatches to the private_create_fileimplementation. -
Content extraction and validation. The handler extracts the
pathandcontentparameters, validates that content is non-empty, and processes the HTML throughextract_html_contentto isolate the body. It then stores both the path and content in the sharedAgentFileStateinstance. -
Result construction. The method builds a
ToolExecutionResultwithok=True, a success message in theresultfield, and asummarycontaining the file path, content length, and a preview generated viasummarize_text(lines 84-88 inruntime.py). -
LLM feedback. The populated
ToolExecutionResultreturns to the LLM, which can now reference the newly created file in subsequent tool calls.
Execution Flow: How edit_file Works
The editing tool supports both single replacements and batch operations through a more complex validation pipeline:
-
Tool invocation. The LLM emits
"edit_file"with either a single edit object (old_text,new_text,count) or aneditsarray containing multiple such objects. -
Runtime routing.
AgentToolRuntime.execute()routes the call to_edit_fileinbackend/agent/tools/runtime.py. -
State validation. The handler first verifies that a file already exists by checking
file_state.content. If no file exists, it immediately returns aToolExecutionResultwithok=Falseand an error summary (lines 11-17). -
Payload normalization. The runtime normalizes the input into a list of edit dictionaries, validating that each contains the required
old_textfield (lines 19-25). -
Safe string replacement. For each edit,
_apply_single_edit(lines 91-108) performs a safe replacement:- Locates
old_textin the current content - Determines how many occurrences to replace (default
1,-1for all) - Executes
str.replacewith the specified count
- Locates
-
Error handling. If any
old_textis not found, the tool aborts immediately with an error indicating the missing snippet (lines 46-55), preventing partial edits or accidental content loss. -
State update and summarization. After all edits succeed, the updated HTML stores back into
AgentFileState. TheToolExecutionResultincludes a summary previewing each edit's old/new text and the count replaced (lines 66-79). -
Continuation. The LLM receives the updated file content and can issue further edits or proceed to other actions.
Key Architectural Design Principles
The screenshot to code agent tools architecture adheres to several critical design patterns that ensure reliability and extensibility:
-
Declarative schema-driven contracts. Both
create_fileandedit_fileexpose strict JSON schemas.create_fileexpectscontent(required) and optionalpath, whileedit_filesupports either a single edit or a list of edits, each withold_text,new_text, and optionalcountparameters. -
Stateless dispatcher with shared state.
AgentToolRuntimeholds no historical context; it only maintains the currentAgentFileStateand service flags. This design prevents memory leaks while ensuring file operations remain atomic and traceable. -
Error-first validation. Every tool validates arguments early and returns a
ToolExecutionResultwithok=Falseand a concisesummarydescribing the problem. This feedback loop allows the LLM to self-correct in subsequent calls. -
Safety guardrails. The
edit_filetool refuses to replace text that does not exist, preventing silent failures. Additionally, the system prompt inbackend/prompts/system_prompt.pyenforces that the LLM must use these tools rather than outputting raw HTML. -
Extensible plugin model. New capabilities (such as image generation) are added by defining a new
CanonicalToolDefinitionindefinitions.pyand implementing a corresponding private method (e.g.,_generate_images) inAgentToolRuntime. The dispatcher automatically exposes new tools to the LLM without modifying core routing logic.
Practical Code Examples
Building a ToolCall for File Creation
from agent.tools.types import ToolCall
create_call = ToolCall(
id="1",
name="create_file",
arguments={
"path": "index.html",
"content": "<!DOCTYPE html><html><body><h1>Hello</h1></body></html>"
},
)
The schema for this call is defined in backend/agent/tools/definitions.py under name="create_file".
Dispatching Calls Through the Runtime
from agent.state import AgentFileState
from agent.tools.runtime import AgentToolRuntime
state = AgentFileState() # starts empty
runtime = AgentToolRuntime(
file_state=state,
should_generate_images=False,
openai_api_key=None,
openai_base_url=None,
)
result = await runtime.execute(create_call)
print(result.ok) # True
print(result.result) # {"content": "Successfully created file at index.html.", ...}
print(result.summary) # {"path": "index.html", "contentLength": 57, "preview": "..."}
All heavy lifting occurs inside runtime._create_file (lines 58-84 in backend/agent/tools/runtime.py).
Executing a Single Text Replacement
edit_call = ToolCall(
id="2",
name="edit_file",
arguments={
"old_text": "Hello",
"new_text": "Welcome",
"count": 1, # replace only the first occurrence
},
)
edit_result = await runtime.execute(edit_call)
print(edit_result.ok) # True
print(edit_result.result["content"]) # Updated HTML with "Welcome"
The edit logic lives in runtime._edit_file (lines 111-180) and runtime._apply_single_edit (lines 91-108).
Batch Editing with the edits Array
batch_edit = ToolCall(
id="3",
name="edit_file",
arguments={
"edits": [
{"old_text": "<h1>", "new_text": "<h2>", "count": -1},
{"old_text": "</h1>", "new_text": "</h2>", "count": -1},
]
},
)
batch_result = await runtime.execute(batch_edit)
The runtime automatically normalizes the payload and runs each edit sequentially.
Generating Tool Summaries for LLM Feedback
from agent.tools.summaries import summarize_tool_input
summary = summarize_tool_input(edit_call, state)
# → {"path": "index.html", "edits": [{"old_text": "Hello", "new_text": "Welcome", "count": 1}]}
Summaries are defined in backend/agent/tools/summaries.py.
Summary
The screenshot to code agent tools architecture provides a robust framework for LLM-driven file manipulation:
- Schema definitions in
definitions.pyestablish strict contracts that both the LLM and runtime must follow - Type-safe data classes (
ToolCall,ToolExecutionResult) ensure reliable communication between components AgentToolRuntimeserves as a stateless dispatcher that routes requests to specific handlers like_create_fileand_edit_fileAgentFileStatemaintains mutable file content across tool invocations, enabling iterative editing workflows- Safety validations prevent common errors by verifying file existence before edits and requiring exact text matches for replacements
- Summarization layers convert technical results into human-readable feedback that the LLM can interpret for subsequent actions
Frequently Asked Questions
What happens if edit_file cannot find the old_text snippet?
If _apply_single_edit cannot locate the specified old_text in the current file content, the tool immediately aborts and returns a ToolExecutionResult with ok=False. The result includes a concise error message indicating which snippet was missing, allowing the LLM to adjust its next edit call. This safety guard prevents partial replacements or accidental content deletion.
How does the runtime handle multiple edits in a single call?
When the LLM provides an edits array rather than a single edit object, AgentToolRuntime normalizes the payload into a list of edit dictionaries. It then iterates through each edit sequentially, applying _apply_single_edit for every item. If any individual edit fails validation or cannot find its target text, the entire operation aborts without applying partial changes.
Where is the file content stored between tool calls?
Mutable file state resides in the AgentFileState class defined in backend/agent/state.py. This object holds both the file path and the current HTML content. The AgentToolRuntime receives a reference to this state during initialization, allowing create_file to establish the initial content and edit_file to mutate it across multiple invocations.
Can new tools be added without modifying the core runtime?
Yes. The architecture supports extensibility through the CanonicalToolDefinition system. To add a new tool, define its schema in definitions.py and implement a corresponding private method in AgentToolRuntime (e.g., _generate_images). The dispatcher automatically exposes the new capability to the LLM, provided the method name matches the tool definition's name field.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →