Luồng Middleware
Core Types
RequestMiddleware
Function type composable. Nhận map[string]any, sửa in-place, trả về error.
ComposeMiddlewares()
Chain nhiều middlewares thành một. Zero-allocation fast path khi danh sách nil.
ApplyMiddlewares()
Chạy toàn bộ chain theo thứ tự trên một request body cụ thể.
Cache Middleware
C CacheMiddleware
Inject OpenAI prompt caching headers vào request body. Tự động bật khi provider hỗ trợ — không cần cấu hình thủ công từ agent. Giúp giảm latency và cost với các nội dung system prompt lặp lại.
Service Tier Middleware
A Anthropic
O OpenAI
9 Error Categories
| Category | Ví dụ | Hành động |
|---|---|---|
| auth | Invalid API key | Tier 1: rotate profile |
| auth_permanent | Account suspended | Tier 2: skip model |
| format | Invalid request body | Tier 2: skip model |
| rate_limit | 429 Too Many Requests | Tier 1: rotate + wait |
| overloaded | Server overloaded | Tier 1: rotate (max 3) |
| billing | Quota exceeded | Tier 2: skip model |
| timeout | Request timeout | Tier 1: rotate |
| model_not_found | Model not available | Tier 2: skip model |
| unknown | Unrecognized error | Conservative: rotate |
D DefaultClassifier
Pattern matching trên HTTP status code + error body. Hai hàm đăng ký pattern provider-specific:
- RegisterOpenAIPatterns() — patterns cho OpenAI-compatible providers
- RegisterAnthropicPatterns() — patterns riêng cho Anthropic API
- Context overflow — trường hợp đặc biệt: trigger compaction, không phải failover
Two-Tier Failover
Transient?"} CLS --> T2{"Tier 2
Permanent?"} T1 -->|"rate_limit, overloaded
timeout, auth"| ROT["Rotate API Keys
/ Profiles"] ROT --> CHK{"Còn profile?"} CHK -->|"Còn, max 5"| RETRY(["Retry cùng model"]) CHK -->|"Overloaded ≥ 3"| ESC["Escalate"] CHK -->|"Hết profile"| ESC T2 -->|"auth_permanent, billing
format, model_not_found"| NEXT["Fall back
model tiếp theo"] ESC --> NEXT NEXT --> NOMORE{"Còn model?"} NOMORE -->|"Còn"| RETRY2(["Retry model mới"]) NOMORE -->|"Hết"| ERR(["Trả lỗi cuối cùng"]) style OK fill:#0d2818,stroke:#2ea85a,color:#6ee7b7 style ERR fill:#2d0a14,stroke:#d94040,color:#fca5a5 style RETRY fill:#0a1f2e,stroke:#e07030,color:#67e8f9 style RETRY2 fill:#0a1f2e,stroke:#e07030,color:#67e8f9 style T1 fill:#1a1a0d,stroke:#d4882a,color:#fde68a style T2 fill:#2d0a14,stroke:#d94040,color:#fca5a5 style ROT fill:#1f1c18,stroke:#3d3835,color:#f5f0eb style NEXT fill:#1f1c18,stroke:#3d3835,color:#f5f0eb
G RunWithFailover[T]()
Generic function orchestrate toàn bộ failover logic. Caller chỉ cần cung cấp danh sách candidates và function thực thi:
- ModelCandidate — gồm provider, model, profileID
- FailoverConfig — CooldownTracker (optional), MaxProfileRotations, OverloadRotationLimit
- Generic return type — không bị ràng buộc vào một response type cụ thể
ModelSpec Struct
Identity
ID, Provider — định danh duy nhất. Thread-safe storage qua sync.Map.
Capacity
ContextWindow, MaxTokens — giới hạn context. Dùng cho auto-summarization threshold.
Capabilities
Reasoning (bool), Vision (bool), TokenizerID — routing thông minh theo khả năng model.
Cost
Cost struct — input/output token pricing. Phục vụ báo cáo chi phí và tối ưu lựa chọn model.
Models Pre-seeded
Anthropic Claude
OpenAI GPT
Embedding Providers
- Model: text-embedding-3-small
- Dimensions: 1536 — khớp chính xác với cột embedding vector(1536) trong pgvector
- Batch max: 2048 texts mỗi request
- Interface: store.EmbeddingProvider với Embed(ctx, texts) [][]float32
- Model: voyage-3-large
- Dimensions: 1536 — cùng schema pgvector
- Implementation: thin wrapper bọc OpenAI provider interface
- Chỉ 20 lines — KISS principle cho Voyage integration
Cooldown Tracker
C Thiết kế in-memory
Per-provider:model tracking qua sync.Map. Khi provider lỗi, ghi nhận reason để tính duration phù hợp:
| Reason | Cooldown Duration |
|---|---|
| rate_limit | 30 giây |
| overloaded | 60 giây |
| billing | 5 phút |
| auth | 10 phút |
| auth_permanent | 1 giờ |
- Overload streak: 5 lỗi liên tiếp → nhân đôi cooldown duration
- Probe window: sau 30 giây cho phép 1 probe attempt
- TTL: 24h expiration, scan cleanup mỗi 5 phút
- Max keys: 512, LRU eviction để giới hạn memory
Markdown Chunking
M ChunkMarkdown(text, maxLen)
Chia văn bản Markdown dài thành chunks phù hợp giới hạn từng channel, tôn trọng cấu trúc nội dung:
- Không cắt giữa fenced code blocks — luôn giữ nguyên khối code
- Ưu tiên cắt: paragraph (\n\n) > line (\n) > space
- Force-split oversized code blocks với fence repair (thêm ``` trước/sau)
Session Recall — Hybrid Search
F FTS Search
Full-text search với ts_rank trên episodic summaries. Chính xác với từ khóa cụ thể.
V Vector Search
Cosine similarity với pgvector. Tìm ngữ nghĩa liên quan dù không khớp từ khóa.
Tìm trên episodic summaries (không phải raw messages) — 80/20 approach: ít nhiễu, đủ ngữ cảnh.
Dreaming Worker
D Episodic → Long-term Memory
Worker subscribe episodic.created event qua EventBus. Khi đủ ngưỡng, dùng LLM tổng hợp các episodic summaries thành insight dài hạn, lưu vào workspace agent:
- Debounce: tối thiểu 10 phút giữa các lần chạy per agent/user
- Threshold: cần ≥ 5 episodic summaries chưa được promoted
- LLM synthesis: gọi provider tóm tắt thành markdown có cấu trúc
- Output path: _system/dreaming/{YYYYMMDD}-consolidated.md
- Marks entries: set promoted_at timestamp để tránh xử lý lại
- Registered: trong workers.go line 54
Luồng Dreaming
≥ 10 phút?"} DB -->|"Chưa đủ"| SKIP(["Bỏ qua"]) DB -->|"Đủ"| CNT{"Unpromoted
≥ 5?"} CNT -->|"Không đủ"| SKIP2(["Chờ thêm"]) CNT -->|"Đủ"| FETCH["Fetch summaries"] FETCH --> LLM["LLM synthesize"] LLM --> STORE["Store markdown"] STORE --> MARK["Mark promoted_at"] MARK --> DONE(["Hoàn tất"]) style E fill:#1a1230,stroke:#c89030,color:#c4b5fd style DONE fill:#0d2818,stroke:#2ea85a,color:#6ee7b7 style SKIP fill:#262220,stroke:#3d3835,color:#6b6460 style SKIP2 fill:#262220,stroke:#3d3835,color:#6b6460 style LLM fill:#1f1c18,stroke:#d94040,color:#f5f0eb style DB fill:#1a1a0d,stroke:#d4882a,color:#fde68a style CNT fill:#1a1a0d,stroke:#d4882a,color:#fde68a