Add agent interface and model implementations
Implements Phase 1: Agent abstraction layer with multiple model support Features: - Abstract AgentInterface base class with standard contract - LoremTesterAgent: Full-featured mock agent with realistic behavior - Configurable reasoning effort levels (none to xhigh) - Random tool/function call generation - Error triggers for testing (rate_limit, context_overflow) - Temperature-based response variation - TatlockAgent: Placeholder for future PydanticAI integration - ModelRegistry: Centralized model management and discovery Testing: - 9 unit tests for lorem-tester agent behavior - 9 unit tests for registry operations - Coverage: Agent abstraction fully tested 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
"""
|
||||
Model registry for managing available models/agents.
|
||||
|
||||
This registry maintains the list of available models and their capabilities.
|
||||
It provides a central place to:
|
||||
- List all models for the /v1/models endpoint
|
||||
- Instantiate agents for specific models
|
||||
- Check model capabilities
|
||||
"""
|
||||
|
||||
import time
|
||||
from typing import Type
|
||||
|
||||
from src.agents.base import AgentInterface
|
||||
from src.agents.lorem_tester import LoremTesterAgent
|
||||
from src.agents.tatlock import TatlockAgent
|
||||
from src.core.exceptions import ModelNotFoundError
|
||||
|
||||
|
||||
class ModelRegistry:
|
||||
"""
|
||||
Central registry for all available models.
|
||||
|
||||
Each model maps to an agent implementation. The registry provides:
|
||||
- Model listing (for /v1/models endpoint)
|
||||
- Agent instantiation (for request handling)
|
||||
- Capability information (for client discovery)
|
||||
"""
|
||||
|
||||
# Model configurations
|
||||
# Add new models here as they're implemented
|
||||
MODELS: dict[str, dict] = {
|
||||
"lorem-tester": {
|
||||
"agent_class": LoremTesterAgent,
|
||||
"description": "Testing agent with mock Responses API features (Lorem Ipsum)",
|
||||
"created": 1733529600, # 2025-12-06
|
||||
"owned_by": "tatlock",
|
||||
# Capabilities are retrieved from agent instance
|
||||
},
|
||||
"tatlock": {
|
||||
"agent_class": TatlockAgent,
|
||||
"description": "Tatlock reasoning agent (placeholder - not yet implemented)",
|
||||
"created": 1733529600, # 2025-12-06
|
||||
"owned_by": "tatlock",
|
||||
# Capabilities are retrieved from agent instance
|
||||
},
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_agent(cls, model_id: str) -> AgentInterface:
|
||||
"""
|
||||
Instantiate agent for given model ID.
|
||||
|
||||
Args:
|
||||
model_id: Model identifier (e.g., "lorem-tester", "tatlock")
|
||||
|
||||
Returns:
|
||||
AgentInterface: Instance of the agent
|
||||
|
||||
Raises:
|
||||
ModelNotFoundError: If model_id not found in registry
|
||||
"""
|
||||
if model_id not in cls.MODELS:
|
||||
raise ModelNotFoundError(model_id)
|
||||
|
||||
agent_class: Type[AgentInterface] = cls.MODELS[model_id]["agent_class"]
|
||||
return agent_class()
|
||||
|
||||
@classmethod
|
||||
async def list_models(cls) -> list[dict]:
|
||||
"""
|
||||
Return all models in OpenAI-compatible format.
|
||||
|
||||
Returns:
|
||||
list[dict]: List of model objects with:
|
||||
- id: Model identifier
|
||||
- object: Always "model"
|
||||
- created: Unix timestamp
|
||||
- owned_by: Owner identifier
|
||||
- capabilities: Dict of capabilities
|
||||
- description: Human-readable description
|
||||
|
||||
Example:
|
||||
[
|
||||
{
|
||||
"id": "lorem-tester",
|
||||
"object": "model",
|
||||
"created": 1733529600,
|
||||
"owned_by": "tatlock",
|
||||
"capabilities": {
|
||||
"streaming": True,
|
||||
"reasoning": True,
|
||||
"tools": True,
|
||||
"vision": False,
|
||||
"audio": False
|
||||
},
|
||||
"description": "Testing agent..."
|
||||
},
|
||||
...
|
||||
]
|
||||
"""
|
||||
models = []
|
||||
|
||||
for model_id, config in cls.MODELS.items():
|
||||
# Instantiate agent to get capabilities
|
||||
agent = cls.get_agent(model_id)
|
||||
capabilities = await agent.get_capabilities()
|
||||
|
||||
models.append({
|
||||
"id": model_id,
|
||||
"object": "model",
|
||||
"created": config["created"],
|
||||
"owned_by": config["owned_by"],
|
||||
"capabilities": capabilities,
|
||||
"description": config["description"],
|
||||
})
|
||||
|
||||
return models
|
||||
|
||||
@classmethod
|
||||
def model_exists(cls, model_id: str) -> bool:
|
||||
"""
|
||||
Check if a model exists in the registry.
|
||||
|
||||
Args:
|
||||
model_id: Model identifier
|
||||
|
||||
Returns:
|
||||
bool: True if model exists
|
||||
"""
|
||||
return model_id in cls.MODELS
|
||||
|
||||
@classmethod
|
||||
async def get_model_info(cls, model_id: str) -> dict:
|
||||
"""
|
||||
Get detailed information about a specific model.
|
||||
|
||||
Args:
|
||||
model_id: Model identifier
|
||||
|
||||
Returns:
|
||||
dict: Model information
|
||||
|
||||
Raises:
|
||||
ModelNotFoundError: If model not found
|
||||
"""
|
||||
if not cls.model_exists(model_id):
|
||||
raise ModelNotFoundError(model_id)
|
||||
|
||||
config = cls.MODELS[model_id]
|
||||
agent = cls.get_agent(model_id)
|
||||
capabilities = await agent.get_capabilities()
|
||||
|
||||
return {
|
||||
"id": model_id,
|
||||
"object": "model",
|
||||
"created": config["created"],
|
||||
"owned_by": config["owned_by"],
|
||||
"capabilities": capabilities,
|
||||
"description": config["description"],
|
||||
}
|
||||
Reference in New Issue
Block a user