Agent Execution API Contract¶
Feature: 007-interactive-chat
Contract Type: Internal Python API
Date: 2025-11-22
Overview¶
This contract defines the internal API for the Agent Execution Runtime, which orchestrates LLM interactions, tool execution, and conversation management. This API is consumed by the holodeck chat CLI command and will be reused by the holodeck test command.
Module: holodeck.agent.executor¶
Class: AgentExecutor¶
Orchestrates agent execution using Semantic Kernel.
Purpose: Provides a high-level interface for executing agents with conversation context.
Constructor¶
def __init__(
self,
agent_config: AgentConfig,
enable_observability: bool = False
) -> None:
"""
Initialize agent executor.
Args:
agent_config: Loaded agent configuration
enable_observability: Enable OpenTelemetry instrumentation
Raises:
AgentInitializationError: If agent cannot be initialized
InvalidToolError: If tool configuration is invalid
LLMConnectionError: If LLM provider is unreachable
"""
Methods¶
async def execute_turn(message: str) -> AgentResponse
Execute a single conversation turn.
async def execute_turn(self, message: str) -> AgentResponse:
"""
Process user message and generate agent response.
Args:
message: User message (already validated)
Returns:
AgentResponse containing response text, tool executions, and token usage
Raises:
MessageProcessingError: If message processing fails
ToolExecutionError: If tool execution fails critically
LLMError: If LLM call fails
"""
def get_history() -> ChatHistory
Retrieve current conversation history.
def get_history(self) -> ChatHistory:
"""
Get conversation history.
Returns:
Semantic Kernel ChatHistory instance
"""
def clear_history() -> None
Clear conversation history (keep system message).
def clear_history(self) -> None:
"""Clear conversation history but preserve system message."""
async def shutdown() -> None
Clean up resources.
async def shutdown(self) -> None:
"""Gracefully shutdown agent executor and clean up resources."""
Module: holodeck.agent.session¶
Class: ChatSessionManager¶
Manages chat session lifecycle and state.
Purpose: Handles session initialization, message tracking, and context management.
Constructor¶
def __init__(
self,
agent_config: AgentConfig,
config: ChatConfig
) -> None:
"""
Initialize chat session manager.
Args:
agent_config: Loaded agent configuration
config: Chat runtime configuration
Raises:
SessionInitializationError: If session cannot be initialized
"""
Methods¶
async def start() -> ChatSession
Start new chat session.
async def start(self) -> ChatSession:
"""
Initialize and start chat session.
Returns:
ChatSession instance with initialized state
Raises:
SessionInitializationError: If initialization fails
"""
async def process_message(message: str) -> AgentResponse
Process user message through agent.
async def process_message(self, message: str) -> AgentResponse:
"""
Validate, process, and respond to user message.
Args:
message: Raw user input
Returns:
AgentResponse with response and metadata
Raises:
ValidationError: If message validation fails
MessageProcessingError: If processing fails
"""
def get_session() -> ChatSession
Retrieve current session state.
def get_session(self) -> ChatSession:
"""
Get current session.
Returns:
ChatSession instance
"""
def should_warn_context_limit() -> bool
Check if approaching context limit.
def should_warn_context_limit(self) -> bool:
"""
Check if conversation approaching message limit.
Returns:
True if at 80% of max_messages threshold
"""
async def terminate() -> None
Terminate session and cleanup.
async def terminate(self) -> None:
"""Gracefully terminate session and release resources."""
Module: holodeck.agent.message¶
Class: MessageValidator¶
Validates user messages.
Purpose: Implements validation pipeline for input sanitization.
Methods¶
def validate(message: str) -> tuple[bool, str | None]
def validate(self, message: str) -> tuple[bool, str | None]:
"""
Validate user message.
Args:
message: Raw user input
Returns:
Tuple of (is_valid, error_message)
- (True, None) if valid
- (False, "error reason") if invalid
"""
Module: holodeck.agent.streaming¶
Class: ToolExecutionStream¶
Streams tool execution events.
Purpose: Provides real-time tool execution updates for display.
Methods¶
async def stream_execution(tool_call: ToolExecution) -> AsyncIterator[ToolEvent]
async def stream_execution(
self,
tool_call: ToolExecution
) -> AsyncIterator[ToolEvent]:
"""
Stream tool execution events.
Args:
tool_call: Tool execution metadata
Yields:
ToolEvent instances (started, progress, completed, failed)
"""
Data Transfer Objects¶
AgentResponse¶
Response from agent execution.
@dataclass
class AgentResponse:
"""Agent response container."""
content: str
"""Agent response text"""
tool_executions: list[ToolExecution]
"""Tools executed during this turn"""
tokens_used: TokenUsage | None
"""Token usage for this turn"""
execution_time: float
"""Total execution time in seconds"""
ToolEvent¶
Tool execution event for streaming.
@dataclass
class ToolEvent:
"""Tool execution event."""
event_type: ToolEventType
"""Event type (started, progress, completed, failed)"""
tool_name: str
"""Tool name"""
timestamp: datetime
"""Event timestamp"""
data: dict[str, Any]
"""Event-specific data"""
ToolEventType Enum:
class ToolEventType(str, Enum):
STARTED = "started"
PROGRESS = "progress"
COMPLETED = "completed"
FAILED = "failed"
Error Hierarchy¶
All agent execution errors inherit from AgentError (defined in holodeck.lib.errors).
class AgentError(HoloDeckError):
"""Base class for agent execution errors."""
pass
class AgentInitializationError(AgentError):
"""Agent initialization failed."""
pass
class MessageProcessingError(AgentError):
"""Message processing failed."""
pass
class ToolExecutionError(AgentError):
"""Tool execution failed."""
pass
class LLMError(AgentError):
"""LLM provider error."""
pass
class LLMConnectionError(LLMError):
"""Cannot connect to LLM provider."""
pass
class SessionInitializationError(AgentError):
"""Session initialization failed."""
pass
class ValidationError(AgentError):
"""Input validation failed."""
pass
Usage Example¶
from holodeck.config.loader import ConfigLoader
from holodeck.agent.executor import AgentExecutor
from holodeck.agent.session import ChatSessionManager
from holodeck.models.chat import ChatConfig
# Load agent configuration
config_loader = ConfigLoader()
agent_config = config_loader.load_agent("examples/weather-agent.yaml")
# Create chat configuration
chat_config = ChatConfig(
agent_config_path=Path("examples/weather-agent.yaml"),
verbose=True,
enable_observability=False,
max_messages=50
)
# Initialize session
session_manager = ChatSessionManager(agent_config, chat_config)
session = await session_manager.start()
# Interactive loop
while True:
user_input = input("You: ").strip()
if user_input.lower() in ['exit', 'quit']:
break
try:
response = await session_manager.process_message(user_input)
print(f"Agent: {response.content}")
# Display tool executions (if verbose)
for tool in response.tool_executions:
print(f"[Tool] {tool.tool_name} → {tool.status} ({tool.execution_time}s)")
except ValidationError as e:
print(f"Error: {e}")
# Cleanup
await session_manager.terminate()
Thread Safety¶
AgentExecutor: NOT thread-safe - Single session per instanceChatSessionManager: NOT thread-safe - Single interactive session- Concurrent sessions require separate instances
Testing Requirements¶
All public methods must have: - Unit tests with mocked Semantic Kernel components - Integration tests with real LLM providers (test accounts) - Error case coverage (invalid configs, LLM failures, tool errors)
Test Markers:
- @pytest.mark.unit for isolated tests
- @pytest.mark.integration for end-to-end tests
- @pytest.mark.slow for LLM-dependent tests