-
Notifications
You must be signed in to change notification settings - Fork 1
Architecture
Amadeus follows Clean Architecture with four strict layers. Dependencies only flow inward — the Core layer has zero external dependencies.
┌──────────────────────────────────────────────┐
│ CLIENT LAYER │
│ HTTP / REST · WebSocket · Telegram │
│ WhatsApp · Email │
└────────────────────┬─────────────────────────┘
│
┌────────────────────▼─────────────────────────┐
│ API LAYER (src/api/) │
│ JWT Auth · Rate Limiter · Audit Logger │
│ /chat /tasks /voice /llm /webhooks │
└────────────────────┬─────────────────────────┘
│ Depends()
┌────────────────────▼─────────────────────────┐
│ APPLICATION LAYER (src/app/) │
│ AmadeusService · SemanticToolRouter │
│ AgentOrchestrator · VoiceService │
└────────────────────┬─────────────────────────┘
│
┌────────────┴────────────┐
│ │
┌───────▼──────┐ ┌───────────▼──────────────┐
│ CORE │ │ INFRASTRUCTURE │
│ (src/core/) │ │ (src/infra/) │
│ │ │ │
│ Domain │ │ LLM Adapters │
│ Interfaces │ │ Qdrant + Flash Cache │
│ Exceptions │ │ Redis · PostgreSQL │
│ Config │ │ Whisper STT · Edge TTS │
│ │ │ Tools (60+) │
│ (no external│ │ WorkspaceIndexer │
│ imports) │ │ Messaging adapters │
└──────────────┘ └───────────────────────────┘
Dependency Injection is handled by dependency-injector in src/container.py. The Container class wires all singletons — LLM router, cache, tool registry, conversation repo, and the main AmadeusService — at startup.
A POST /api/v1/chat request passes through six stages:
Client
│
▼ 1. JWT verification (HS256, requires exp claim in production)
│
▼ 2. Rate limiting (SlowAPI — keyed by JWT sub, falls back to IP)
│
▼ 3. AmadeusService.handle_command()
│ ├─ Check Redis LLM response cache (1h TTL)
│ ├─ Store user message in PostgreSQL + Qdrant
│ └─ Multi-step? → AgentOrchestrator
│ Single-step? → _process_command_internal()
│
▼ 4. SemanticToolRouter.route() ← Stage 1: cosine sim (<10ms)
│ └─ Confidence < 0.50? → LLM triage ← Stage 2: LlamaCpp/Groq
│
▼ 5. Tool execution (ToolExecutor, with HITL gate for destructive ops)
│ └─ LLMRouter.generate() composes a natural response
│
▼ 6. Store response in PostgreSQL + Qdrant → return ChatResponse
Every generation request flows through LLMRouter, which checks Redis daily-quota counters before dispatching:
Incoming Request
│
▼
LlamaCpp ──(SLM_MODEL_PATH set? offline GGUF)──▶ ✅ Response
(local, unlimited)
│ not configured
▼
Ollama ──(running locally?)──▶ ✅ Response
(local, unlimited)
│ not running
▼
Groq ──(quota < 14,400/day?)──▶ ✅ Response
(free tier, Llama 3.3 70B)
│ exhausted
▼
Gemini ──(quota < 1,500/day?)──▶ ✅ Response
(free tier, Gemini 2.5 Flash)
│ exhausted
▼
OpenAI ──(key configured?)──▶ ✅ Response
(paid, GPT-4o-mini, emergency only)
│ no key
▼
🚫 LLMRateLimitError → HTTP 503
The router accepts a complexity hint:
| Value | Behaviour |
|---|---|
"auto" |
Score the prompt and choose the best tier |
"simple" |
Local-only (LlamaCpp / Ollama) |
"normal" |
Local first, cloud fallback |
"high" |
Cloud-first (Groq → Gemini → OpenAI), local as last resort |
Audio Bytes (WebSocket /api/v1/ws/voice)
│
▼ faster-whisper (CPU / CUDA, int8, non-blocking via executor)
Transcribed Text
│
▼ LLMRouter (local-first)
LLM Response Text
│
▼ Edge TTS (en-US-JennyNeural, async streaming, Redis-cached)
Audio Bytes ──▶ Client
The voice WebSocket protocol sends three frames per turn:
-
{"type": "transcription", "text": "..."}— what was heard -
{"type": "response_text", "text": "..."}— the AI reply - Binary frame — TTS audio bytes (MP3)
Amadeus uses a three-tier memory system:
| Tier | Technology | Purpose | Latency |
|---|---|---|---|
| L1 Flash Cache | NumPy float32 ring buffer (100 entries, ~307 KB RAM) | Intercepts Qdrant for recently-accessed memories | ~1 µs |
| L2 Qdrant (Semantic) |
all-mpnet-base-v2 768-dim vectors + cosine similarity |
Long-term cross-session recall with recency/importance weighting | ~5 ms |
| L3 Knowledge Graph | SQLite via SQLAlchemy (EntityORM + RelationshipORM SPO triples) | Structured episodic memory — relationships between entities | ~2 ms |
The L1 cache is invalidated on clear_conversation(). Identity memories (subtype "identity", importance 1.0) never decay in the L2 ranking formula.
← Home | Core-Systems →
Amadeus-AI · v6.0.0· Apache License 2.0 · Report a Bug
Getting Started
Architecture
Reference
Integrations
Operations
Development
Project
Links