gtm-autoresearch // feature/finetune-pipeline // Phase 5 of 6

OpenClaw
Client Brain

Routes every incoming request through OpenClaw gateway to the correct client-specialized model. Each client_id maps to an active model version. No 200k context dump at inference.

05
Phase 5 of 6
Request Routing Architecture
┌─────────────────────────────────────────────────────────────────────────────┐
  Incoming Request                                                            
  POST http://localhost:18789/v1/messages                                    
  Headers: x-client-id: hre  |  x-model-preference: client                   
└──────────────────────────────────┬──────────────────────────────────────────┘
                                   
                                   
┌──────────────────────────────────────────────────────────────────────────────┐
              OPENCLAW GATEWAY  :18789  (apps/openclaw-gateway/)             
├──────────────────────────────────────────────────────────────────────────────┤
  Layer 1: AuthMiddleware       Tailscale IP check + API key validation       
  Layer 2: ClientIDMiddleware   Extract x-client-id → lookup model_registry   
  Layer 3: ModelRouter         Resolve active model version + track          
  Layer 4: TelemetryMiddleware  Log client_id, model_version, latency         
└──────────────────────────────────┬───────────────────────────────────────────┘
                                   
            ModelRouter resolves → active model for client_id
                  ┌──────────────┴──────────────┐
                                               
┌─────────────────────────────┐   ┌────────────────────────────────────────────┐
 Track A — OpenAI                 Track B — Ollama (NoClaw :11434)           
 model: ft:gpt-4o-mini:hre-v3     model: hre-client:v3                       
 client: hre (active)             client: teleios (active, no egress)        
└──────────────┬──────────────┘   └──────────────────┬─────────────────────────┘
               └──────────────────┬──────────────────┘
                                  
┌──────────────────────────────────────────────────────────────────────────────┐
  Response  +  x-model-version: hre-v3  +  x-client-id: hre  response header 
└──────────────────────────────────────────────────────────────────────────────┘
Middleware Stack Detail
Auth
Layer 1
Validate request originates from Tailscale network (IP range check) or carries valid x-api-key header. Reject with 401 if either fails. Part of existing OpenClaw 8-phase hardening plan for CVE-2026-25253.
existing
Client ID
Layer 2 — new
Extract x-client-id header. Load data/clients/{id}/model_registry.json. Find record where active: true. Attach resolved ClientContext (model_id, track, version) to request object.
new
Model Router
Layer 3 — new
Route to Track A (OpenAI client) or Track B (Ollama via http://100.86.248.8:11434) based on ClientContext.track. Falls back to RAG context injection if no active model exists for client.
new
Telemetry
Layer 4 — new
Log each request: client_id, model_version, track, latency_ms, token_count, timestamp. Write to SQLite telemetry table. Used for drift detection in Phase 6 flywheel.
new
Fallback
Layer 5 — new
If no fine-tuned model is active for client_id, inject full AccountState.system_prompt into the request messages array before forwarding to base model. Ensures graceful degradation for new clients.
fallback
Per-Client Config — data/clients/{id}/config.json
{ "client_id": "hre", "display_name": "HRE", "track_preference": "a", // "a" | "b" "data_egress_allowed": true, // false = track B only (Teleios) "min_examples_to_activate": 50, "score_threshold": 0.75, "openclaw_routing": { "header": "x-client-id", "fallback_on_missing_model": "rag_context", // "rag_context" | "base_model" | "error" "response_header_version": true // attach x-model-version to responses }, "integrations": { "gtm_container_id": "GTM-XXXXXXX", "google_ads_account_id": "123-456-7890", "meta_pixel_id": "HRE-pixel-id", "meta_ad_account_id": "act_12345" } }
Active Client Brain Status

HRE

TrackA (OpenAI)
Modelft:gpt-4o-mini:hre-v3
Versionv3
Eval score0.84
Status● active
Egressallowed

Teleios

TrackB (Ollama)
Modelteleios-client:v1
Versionv1
Eval scorepending
Status◐ eval
Egressblocked

RTT / Next

TrackTBD
Modelnone yet
Examples12 / 50
Statusaccumulating
FallbackRAG context
Egressallowed
Fallback Chain — When No Active Model Exists
1
Fine-Tuned Client Model (preferred)
Active model from model_registry.json routed directly. No context injection needed — account knowledge baked into weights. Fastest, most accurate.
2
RAG Context Injection
No fine-tuned model yet (client < 50 examples). Load AccountState.system_prompt + top-5 Chroma memories. Inject as system message. Forward to base model (Claude Sonnet / GPT-4o).
3
Base Model (last resort)
No account state available (collector hasn't run). Forward request to base model with no client context. Log warning — trigger account state collection.
CLI Output — pnpm openclaw register --client hre
$ pnpm openclaw register --client hre --model ft:gpt-4o-mini-2024-07-18:hre-v3 --track a

──────────────────────────────────────────────────────
  OpenClaw Client Brain — Registering HRE v3
──────────────────────────────────────────────────────
  Loading config...          ✓ data/clients/hre/config.json
  Validating model_id...     ✓ ft:gpt-4o-mini-2024-07-18:hre-v3 (reachable)
  Writing registry...        ✓ model_registry.json updated
  Reloading OpenClaw...      ✓ gateway :18789 hot-reloaded

  Testing routing:
    x-client-id: hre   →     ft:gpt-4o-mini-2024-07-18:hre-v3  
    x-client-id: teleios →   teleios-client:v1 (ollama)        
    x-client-id: unknown →   fallback: rag_context             
──────────────────────────────────────────────────────
  ✓ HRE Client Brain active on OpenClaw :18789
  x-client-id: hre → ft:gpt-4o-mini-2024-07-18:hre-v3
  Next: pnpm flywheel start --client hre  (Phase 6)
  
Claude Code Prompt
claude --dangerously-skip-permissions # Phase 5: OpenClaw Client Brain Integration # Branch: feature/finetune-pipeline # Repo: github.com/Organized-AI/gtm-autoresearch ## Context Phases 1–4 complete. Phase 5 wires the fine-tuned models into the OpenClaw gateway (:18789) via client-aware middleware. ## Task Read AGENT-HANDOFF/ and PLANNING/ first. Read existing apps/openclaw-gateway/ code before modifying. 1. Create packages/client-brain-router/ ClientIDMiddleware: - Extract x-client-id from request header - Load data/clients/{id}/config.json - Load data/clients/{id}/model_registry.json - Find active:true record → attach ClientContext to req - If no config: set fallback mode = "rag_context" ModelRouter: - ClientContext.track === "a" → route to OpenAI with model_id - ClientContext.track === "b" → route to Ollama URL: http://{OLLAMA_HOST}/api/chat, model: client model name - No active model → FallbackHandler FallbackHandler: - Load AccountState.system_prompt from data/clients/{id}/ - Query Chroma :37777 for top-5 memories by client_id - Prepend as system message to request - Forward to base model (FALLBACK_MODEL env var) TelemetryLogger: - On each request completion, write to SQLite: {timestamp, client_id, model_version, track, latency_ms, input_tokens, output_tokens, fallback_used} - Expose GET /telemetry?client_id=hre for Phase 6 drift query 2. Wire middleware into apps/openclaw-gateway/ request pipeline: AuthMiddleware → ClientIDMiddleware → ModelRouter → TelemetryLogger → response 3. CLI: pnpm openclaw register --client hre --model {id} --track a - Validates model is reachable before registering - Hot-reloads gateway config (no restart needed) - Tests all registered client routes 4. Add response headers: x-model-version: {version} x-client-id: {client_id} x-fallback-used: true|false 5. Unit tests: - ClientIDMiddleware resolves correct model for known client - FallbackHandler activates when no active model exists - TelemetryLogger writes correct record to SQLite - ModelRouter correctly distinguishes track A vs B ## Env vars: OPENCLAW_PORT=18789 OLLAMA_HOST=http://100.86.248.8:11434 FALLBACK_MODEL=claude-sonnet-4-6 CHROMA_URL=http://localhost:37777 CLIENT_DATA_DIR=./data/clients TELEMETRY_DB_PATH=./data/telemetry.sqlite ## Do NOT build Phase 6 (flywheel) yet.
← Phase 4: Fine-Tune Runner gtm-autoresearch-docs.pages.dev Phase 6: Flywheel →