The four layers
API layer
api.py and routers/ handle all inbound HTTP and WebSocket connections. The ODAPIApplication class wires together middleware, routers, and the WebSocket endpoint. CORS policy, authentication checks, and route registration all live here.Service layer
services/ contains the business logic that the API layer delegates to. AuthService validates Firebase tokens, ChatService manages Firestore chat documents and tracks analytics, and LocationService resolves IP addresses to geographic context.Integration layer
connectors/ houses the OpenAI agent definitions for each third-party service. The Orchestrator assembles a GPT-4o agent with 25+ specialist agents as handoff targets. When a request arrives, the orchestrator decides which agents to invoke, runs them in parallel where possible, and merges results.Data layer
firebase/models/ provides Firestore-backed model classes for users, chats, OAuth tokens, token usage, and error tracking. Each model inherits from FireStoreObject and abstracts all database operations behind a clean API.Core components
ODAPIApplication — api.py
The application entry point. The constructor initializes all services and wires them together through dependency injection:
/chats/{chat_id} and delegates the full connection lifecycle to WebSocketHandler:
ChatService — services/chat_service.py
Manages the lifecycle of a chat session in Firestore. Responsibilities include:
- Creating or retrieving a
Chatdocument by ID and user - Persisting message history and per-turn responses
- Recording OpenAI token usage for both the chat and the user
- Logging unhandled requests for future capability planning
- Emitting analytics events via Segment for every significant action
AuthService — services/auth_service.py
Validates Firebase ID tokens on every connection. In production mode it additionally enforces that:
- The user is not anonymous
- The user has accepted the terms of service
1008 before the chat loop begins.
ConnectionManager — websocket/connection_manager.py
Tracks all active WebSocket connections in memory. Provides methods to accept a new connection, remove a disconnected one, and send text or JSON messages to individual connections. The connection_count property is exposed by the /test health check endpoint.
WebSocketHandler — websocket/handlers.py
Orchestrates the full WebSocket request/response cycle after authentication:
- Resolves or creates the Firestore
Chatdocument viaChatService - Builds a per-user
Orchestratorinstance withbuild_dynamic_agents - Runs the main message loop: receive prompt → stream agent response → finalize
- Streams raw text deltas, tool call status, agent handoffs, and suggested prompts back to the client as JSON events
- Writes the completed conversation back to Firestore and records token usage
Runner.run_streamed from the OpenAI Agents SDK and iterates over the async event stream:
Orchestrator — connectors/orchestrator.py
Builds the root GPT-4o agent and populates its handoffs list with every specialist agent. When a user message arrives, the orchestrator applies the H.A.N.D.O.F.F. decision framework to route work to the right agent:
| Letter | Criterion |
|---|---|
| H | Has capability — does the agent explicitly solve this task? |
| A | Access — does it have the required data or API permissions? |
| N | Novelty/Need — is a tool call necessary vs. answering from context? |
| D | Delay/Cost — prefer fewer or cheaper calls when quality is unaffected |
| O | Output quality — will it return the needed format? |
| F | Failure fallback — choose alternates if the first is likely to fail |
| F | Fusion — orchestrate multiple agents and merge results |
TOOL_CALLS dictionary maps every registered function tool name to a human-readable progress string that is surfaced to the client as a tool_call WebSocket event:
Design patterns
Layered architecture
Each layer only calls downward. The API layer delegates to services, services delegate to connectors and Firebase models. This makes each layer independently testable and replaceable.Dependency injection
Services receive their dependencies through constructors rather than creating them directly.ODAPIApplication.__init__ composes the full object graph and passes the openai_client, settings, and connection_manager into WebSocketHandler.
Repository pattern
Firebase model classes (Chat, User, TokenUsage) abstract all Firestore operations. The service layer never writes Firestore queries directly — it calls methods like Chat.get_chat_by_id(), Chat.create_chat(), and chat.update_messages().
Agent-based architecture
Each third-party service is encapsulated as a standalone OpenAIAgent with its own system prompt, tool definitions, and optional sub-handoffs. The orchestrator composes these agents at runtime. Adding a new integration means creating a new agent file in connectors/ and appending the agent to the handoffs list in orchestrator.py.
Async/await throughout
All I/O — WebSocket communication, Firestore reads and writes, OpenAI streaming, and external API calls — uses Python’sasyncio. The server runs under uvicorn with optional uvloop for higher throughput.