SAM AI API Infrastructure - Detailed Walkthrough
SAM AI API Infrastructure - Detailed Walkthrough
Purpose: Step-by-step explanation of data flow through SAM AI's API infrastructure
Prerequisite: Review sam_ai_api_infrastructure_DIAGRAM.md first
Overview
The SAM AI API infrastructure handles all communication between the frontend chat UI and AI providers (Claude, OpenAI, etc.). It follows a v2 Architecture (consolidated December 2025) with two core principles:
- WHAT SAM KNOWS - Built once per session (
system_prompt.py) - HOW SAM TALKS - Processed for every message (
sam_chat.py)
Architecture Layers
| Layer | Location | Purpose |
|---|---|---|
| HTTP Controllers | ai_sam_base/controllers/ |
Receive HTTP requests, route to processing |
| API Communications | ai_sam_base/api_communications/ |
Core business logic, session management, AI calls |
| Models | ai_sam_base/models/ |
Database persistence (ai.conversation, ai.message) |
| External | AI Provider APIs | Claude, OpenAI, etc. |
Step-by-Step: Streaming Request
Step 1: HTTP Request Arrives
What happens:
Browser sends a POST request with user message to the streaming endpoint.
Code location:
ai_sam_base/controllers/sam_ai_chat_controller.py:L133-L199
Route:
@http.route('/sam_ai/chat/send_streaming', type='http', auth='user', methods=['POST'], csrf=False)
def send_message_streaming(self, **kwargs):
Data transformation:
- Input: kwargs containing message, conversation_id, context_data (JSON string), environment (JSON string)
- Output: Parameters parsed and ready for processing
Step 2: Context Parsing
What happens:
The controller parses context_data to determine the user's location in Odoo (which model, record, canvas, etc.).
Code location:
ai_sam_base/controllers/sam_ai_chat_controller.py:L76-L102
Context types identified:
- Canvas/Workflow context: canvas_id or workflow_id present
- Node context: node_id present (specific workflow node)
- Record context: model + record_id present
- General context: No specific location
Data transformation:
- Input: Raw context_data dict
- Output: parsed_context with standardized fields
Step 3: Session Management
What happens:
SessionManager retrieves existing session or creates new one. Sessions are keyed by user_id:location_key.
Code location:
ai_sam_base/api_communications/session_manager.py:L63-L88
Location key examples:
- canvas:35 - Workflow canvas ID 35
- crm.lead:142 - CRM lead record 142
- sale.order:list - Sales order list view
- general - No specific location
Session lifecycle:
1. Check cache for {user_id}:{location_key}
2. If exists and not expired (30 min TTL), resume
3. If expired or new, create fresh session
Data transformation:
- Input: env, user_id, context_data
- Output: session_context dict containing:
- system_prompt: Full prompt built for this location
- tools: List of available tools
- location: Domain and context info
- user: User information
- conversation_history: Message history
Step 4: Session Context Build (New Session)
What happens:
If new session, SessionContextBuilder builds the complete session context including system prompt and tools.
Code location:
ai_sam_base/api_communications/session_context.py (via session_manager.py:L208-L236)
System prompt includes:
- SAM's personality and capabilities
- Current location context (what Odoo page/record user is viewing)
- Available tools for this context
- User preferences and history
Data transformation:
- Input: env, user_id, context_data
- Output: Full session_context with everything SAM needs
Step 5: SAMChat Initialization
What happens:
SAMChat class is instantiated with the session context. This is the main orchestrator for message processing.
Code location:
ai_sam_base/api_communications/sam_chat.py:L56-L86
class SAMChat:
def __init__(self, env, session_context):
self.env = env
self.session = session_context
self.system_prompt = session_context.get('system_prompt', '')
self.tools = session_context.get('tools', [])
self.sam_user = env.ref('ai_sam_base.sam_user', raise_if_not_found=False)
self.messages = session_context.get('conversation_history', [])
Step 6: Streaming Message Processing
What happens:
SAMChat.process_message_streaming() handles the actual message flow with streaming responses.
Code location:
ai_sam_base/api_communications/sam_chat.py:L145-L245
Flow:
1. Add user message to conversation history
2. Yield {'type': 'status', 'status': 'thinking'}
3. Call AI API with streaming
4. For each chunk:
- If content: Yield to frontend
- If tool_call: Execute tool, continue conversation
- If error: Yield error and return
5. Persist messages to database
6. Yield {'type': 'done'}
Step 7: AI API Call (Streaming)
What happens:
APIServices makes the actual HTTP call to the AI provider with streaming enabled.
Code location:
ai_sam_base/api_communications/api_services.py:L116-L168
Provider routing:
1. Determine API format from config (anthropic or openai)
2. Route to appropriate handler:
- _call_anthropic_api() for Claude
- _call_openai_api() for OpenAI-compatible providers
Data transformation:
- Input: messages, config, system_prompt, tools
- Output: Stream of response chunks
Step 8: Tool Execution (If Needed)
What happens:
If the AI requests tool calls, SAMChat._execute_single_tool() routes to the appropriate executor.
Code location:
ai_sam_base/api_communications/sam_chat.py:L445-L501
(Also: _execute_tools() at L437-L443)
Tool types:
| Tool | Executor | Purpose |
|------|----------|---------|
| odoo_read | core_tools.py | Read records by ID |
| odoo_search | core_tools.py | Search records by domain |
| odoo_create | core_tools.py | Create new record |
| odoo_write | core_tools.py | Update existing records |
| memory_recall | chat_tools.py | Search past conversations |
Important: Core tools execute as SAM user for proper audit trail.
Step 9: Message Persistence
What happens:
User and assistant messages are saved to ai.conversation and ai.message models.
Code location:
ai_sam_base/api_communications/sam_chat.py:L507-L533
def _persist_messages(self, conversation_id, user_message, assistant_message):
"""Save messages to ai.conversation/ai.message."""
try:
Message = self.env['ai.message']
message_env = Message.with_user(self.sam_user) if self.sam_user else Message
# User message
message_env.create({
'conversation_id': conversation_id,
'role': 'user',
'content': user_message,
})
# Assistant message
message_env.create({
'conversation_id': conversation_id,
'role': 'assistant',
'content': assistant_message,
})
except Exception as e:
_logger.warning(f"[SAM-CHAT] Failed to persist messages: {e}")
Step 10: SSE Response to Frontend
What happens:
The controller yields Server-Sent Events (SSE) to the browser for real-time streaming.
Code location:
ai_sam_base/controllers/sam_ai_chat_controller.py:L186-L250 (event_stream generator)
Event types:
| Event | Data | Purpose |
|-------|------|---------|
| status | {status, progress} | Progress updates |
| chunk | {text} | Response text chunk |
| activity | {activity, message} | Tool execution status |
| done | {success, conversation_id} | Completion signal |
| error | {error} | Error information |
Error Handling
| Error | Cause | Handling |
|---|---|---|
| Session expired | TTL exceeded (30 min) | Create new session |
| Tool execution failed | Invalid model/params | Return error in tool result, AI handles gracefully |
| AI API error | Network/provider issue | Yield error event, log exception |
| Database cursor closed | Transaction issue | Use context managers with new cursors |
Performance Considerations
Session Caching
- Sessions cached in-memory (class variable
_sessions) - TTL of 30 minutes prevents stale context
- Could be Redis-backed for multi-process scaling
Streaming
- Uses Server-Sent Events (SSE) for real-time updates
- Each database operation uses fresh cursor to avoid "cursor already closed" errors
- Transaction cleanup in
finallyblock handles client disconnections
Tool Execution
- Core tools execute as SAM user for consistent audit trail
- Tool results included in conversation context for AI to process
- Loop continues until AI stops requesting tools
Key Files Reference
| File | Location | Purpose | Key Classes/Functions |
|---|---|---|---|
sam_ai_chat_controller.py |
controllers/ |
HTTP endpoints | send_message(), send_message_streaming() |
sam_chat.py |
api_communications/ |
Message orchestration | SAMChat, process_chat_message(), process_chat_message_streaming() |
session_manager.py |
api_communications/ |
Session lifecycle | SessionManager, get_or_create_session() |
session_context.py |
api_communications/ |
Context builder | SessionContextBuilder |
system_prompt.py |
api_communications/ |
Prompt building | build_session_context() |
api_services.py |
api_communications/ |
AI provider calls | APIServices, send(), send_streaming() |
core_tools.py |
api_communications/ |
CRUD tool execution | execute_core_tool(), CORE_TOOLS |
chat_tools.py |
api_communications/ |
Memory tool execution | execute_chat_tool() |
location_insights.py |
api_communications/ |
Location analysis | Location-specific context building |
ai_conversation.py |
models/ |
Conversation model | ai.conversation |
ai_message.py |
models/ |
Message model | ai.message |
Related Flows
- Conversation Flow - Chat message handling
- Canvas/Workflow Flow - Workflow canvas context
- API Communications Architecture - V2 architecture details