Architecture 2 Agents - djvolz/coda-code-assistant GitHub Wiki
- Primary implementation:
coda/agents/agent.py - Tool decorator:
coda/agents/decorators.py - Function tools:
coda/agents/function_tool.py - MCP adapter:
coda/agents/tool_adapter.py - Built-in tools:
coda/agents/builtin_tools.py - Type definitions:
coda/agents/types.py - Tests:
tests/test_agent.py,tests/test_tool_calling.py
The Agents module provides an autonomous AI agent framework with tool-calling capabilities. It allows AI models to execute functions and interact with external systems through a unified interface. The main entry point is the Agent class in coda/agents/agent.py.
coda/agents/
├── __init__.py # Module exports (verified)
├── agent.py # Core Agent implementation (verified)
├── decorators.py # @tool decorator (verified)
├── function_tool.py # FunctionTool wrapper class (verified)
├── tool_adapter.py # MCP tool integration (verified)
├── builtin_tools.py # Pre-built tool functions (verified)
└── types.py # Type definitions (verified)
Location: coda/agents/agent.py
Purpose: Main agent class that orchestrates AI conversations with tool execution
Key Methods:
-
__init__(): Initializes agent with provider, model, and tools -
run_async(): Executes agent with tool calling support -
run_async_streaming(): Streaming execution with real-time output -
_handle_tool_calls(): Orchestrates tool execution
Location: coda/agents/decorators.py
Purpose: Decorator that marks functions as agent-callable tools
Implementation:
def tool(func=None, *, name=None, description=None):
"""Decorator to mark a function as a tool."""
def decorator(f):
f._is_tool = True
f._tool_name = name or f.__name__
f._tool_description = description or f.__doc__ or ""Location: coda/agents/function_tool.py
Purpose: Wrapper that converts Python functions into tool specifications
Key Methods:
-
from_callable(): Factory method to create from decorated functions -
execute(): Executes the wrapped function with arguments -
_build_parameters(): Generates JSON Schema from function signature
Location: coda/agents/tool_adapter.py
Purpose: Adapts MCP (Model Context Protocol) tools for use with agents
Key Methods:
-
convert_mcp_tool(): Converts MCP tool to FunctionTool -
get_all_tools(): Retrieves all registered MCP tools
Implementation: coda/agents/decorators.py
The @tool decorator adds metadata to functions:
@tool(description="Read contents of a file")
def read_file(path: str) -> str:
"""Read and return the contents of a file."""
with open(path, 'r') as f:
return f.read()Implementation: coda/agents/tool_adapter.py
Adapts MCP tools to the agent's FunctionTool interface:
def convert_mcp_tool(self, mcp_tool: Type[BaseTool]) -> FunctionTool:
"""Convert an MCP tool to a FunctionTool."""
# Creates wrapper function that calls MCP tool
async def tool_wrapper(**kwargs):
return await tool_instance.execute(**kwargs)Implementation: coda/agents/function_tool.py
@classmethod
def from_callable(cls, func: Callable) -> "FunctionTool":
"""Create a FunctionTool from a callable."""
if not hasattr(func, "_is_tool"):
raise ValueError(f"Function {func.__name__} is not decorated with @tool")sequenceDiagram
participant User
participant Agent
participant Provider
participant FunctionTool
participant Tool Function
User->>Agent: run_async(messages)
Agent->>Provider: Send messages with tools
Provider->>Provider: Generate response
alt Tool calls requested
Provider->>Agent: Response with tool_calls
loop For each tool call
Agent->>Agent: Create RequiredAction
Agent->>FunctionTool: execute(arguments)
FunctionTool->>Tool Function: Call with args
Tool Function->>FunctionTool: Return result
FunctionTool->>Agent: Return PerformedAction
end
Agent->>Agent: Add tool results to messages
Agent->>Provider: Continue with results
end
Provider->>Agent: Final response
Agent->>User: Return RunResponse
classDiagram
BaseModel <|-- FunctionTool
BaseModel <|-- RequiredAction
BaseModel <|-- PerformedAction
BaseModel <|-- RunResponse
class Agent {
-provider: BaseProvider
-model: str
-tools: List[FunctionTool]
+run_async(messages)
+run_async_streaming(messages)
-_handle_tool_calls(tool_calls)
}
class FunctionTool {
+name: str
+description: str
+parameters: dict
+callable: Callable
+from_callable(func)
+execute(arguments)
}
class MCPToolAdapter {
+convert_mcp_tool(mcp_tool)
+get_all_tools()
}
note for Agent "Defined in agent.py"
note for FunctionTool "Defined in function_tool.py"
Location: coda/agents/decorators.py
Purpose: Marks a function as an agent-callable tool
Usage:
@tool # Simple usage
def my_tool(): ...
@tool(name="custom", description="Custom tool") # With metadata
def another_tool(): ...Location: coda/agents/agent.py
Purpose: Autonomous AI agent with tool-calling capabilities
Constructor:
def __init__(
self,
provider: BaseProvider,
model: str,
instructions: str = "",
tools: List[Union[Callable, FunctionTool]] = None,
name: str = None
)Key Methods:
-
run_async(messages, instructions): Async execution -
run_async_streaming(messages, instructions): Streaming execution -
run(messages, instructions): Sync wrapper -
as_tool(): Convert agent to tool
Location: coda/agents/function_tool.py
Purpose: Wrapper for Python functions with tool metadata
Fields:
-
name(str): Tool name -
description(str): Tool description -
parameters(dict): JSON Schema parameters -
callable(Callable): Wrapped function
Tools are configured through:
-
Decorator parameters:
@tool(name="...", description="...") - Function docstrings: Used as default descriptions
- Type hints: Used to generate parameter schemas
-
coda.providers.base: Provider interface -
coda.tools.base: Tool registry for MCP tools -
coda.tools.executor: Tool execution permissions
-
pydantic>=2.0: Data validation and models -
rich>=13.7.0: Console output formatting -
typing_extensions: Enhanced typing support
- Unit tests:
tests/test_agent.py - Tool calling tests:
tests/test_tool_calling.py - Integration tests: Various provider-specific tests
- Basic Agent Execution: Tests agent without tools
- Tool Calling: Tests tool execution flow
- Streaming Support: Tests streaming responses
Tool execution errors are captured and returned to the model:
try:
result = tool.execute(filtered_args)
except Exception as e:
logger.error(f"Tool execution error: {e}")
result = f"Error: {str(e)}"Implementation: agent.py
Prevents infinite tool-calling loops by tracking call patterns and breaking after repeated sequences.
- Async Execution: All operations are async-first
- Streaming Support: Reduces time-to-first-token
- Tool Map Caching: O(1) tool lookup
- Lazy Tool Processing: Tools converted only when needed
- Tool Permissions: Integrated with permission system
- Argument Filtering: Only declared parameters passed to tools
- Error Isolation: Tool errors don't crash agent
from coda.agents import Agent, tool
@tool
def get_weather(location: str) -> str:
"""Get weather for a location."""
return f"Sunny in {location}"
agent = Agent(
provider=provider,
model="gpt-4",
tools=[get_weather]
)
response = await agent.run_async([
{"role": "user", "content": "What's the weather in Paris?"}
])@tool(description="Fetch data from a URL asynchronously")
async def fetch_data(url: str) -> str:
"""Fetch and return data from a URL."""
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()- Provider Module: Uses provider for AI completions
- Tools Module: Integrates MCP tools via adapter
-
CLI Module: Used in
agent_chat.pyfor interactive sessions
-
Custom Tools: Create with
@tooldecorator - Tool Adapters: Implement adapters for other tool protocols
- Agent Subclassing: Extend Agent for specialized behavior
- Tool Discovery: Tools must be explicitly passed to agent
- Parameter Types: Complex types may not serialize correctly
All files referenced in this document: