From 311a834228aa45db8eba95b727c000f983ac49a2 Mon Sep 17 00:00:00 2001 From: Max Mayfield Date: Sun, 1 Mar 2026 02:29:23 +0000 Subject: [PATCH] Add dd0c/route project scaffolding: migrations, docker-compose, README - PostgreSQL schema: orgs, users, api_keys, provider_configs, routing_rules, cost_tables, feature_flags - TimescaleDB schema: request_events hypertable, hourly/daily continuous aggregates, compression, retention - docker-compose.yml: postgres, timescaledb, redis for local dev - README with quick start, architecture overview, pricing tiers - .env.example, .gitignore --- products/01-llm-cost-router/.env.example | 13 +++ products/01-llm-cost-router/.gitignore | 4 + products/01-llm-cost-router/README.md | 53 +++++++++ .../01-llm-cost-router/docker-compose.yml | 50 ++++++++ .../migrations/001_init.sql | 108 ++++++++++++++++++ .../migrations/002_timescale.sql | 88 ++++++++++++++ 6 files changed, 316 insertions(+) create mode 100644 products/01-llm-cost-router/.env.example create mode 100644 products/01-llm-cost-router/.gitignore create mode 100644 products/01-llm-cost-router/README.md create mode 100644 products/01-llm-cost-router/docker-compose.yml create mode 100644 products/01-llm-cost-router/migrations/001_init.sql create mode 100644 products/01-llm-cost-router/migrations/002_timescale.sql diff --git a/products/01-llm-cost-router/.env.example b/products/01-llm-cost-router/.env.example new file mode 100644 index 0000000..9c01010 --- /dev/null +++ b/products/01-llm-cost-router/.env.example @@ -0,0 +1,13 @@ +DATABASE_URL=postgres://dd0c:dd0c@localhost:5432/dd0c +TIMESCALE_URL=postgres://dd0c:dd0c@localhost:5433/dd0c_telemetry +REDIS_URL=redis://localhost:6379 +JWT_SECRET=dev-secret-change-me-in-production +PROXY_PORT=8080 +API_PORT=3000 +AUTH_MODE=local +GOVERNANCE_MODE=audit +TELEMETRY_CHANNEL_SIZE=1000 + +# Provider keys (add yours) +# OPENAI_API_KEY=sk-... +# ANTHROPIC_API_KEY=sk-ant-... diff --git a/products/01-llm-cost-router/.gitignore b/products/01-llm-cost-router/.gitignore new file mode 100644 index 0000000..b054f58 --- /dev/null +++ b/products/01-llm-cost-router/.gitignore @@ -0,0 +1,4 @@ +/target +.env +*.pem +*.key diff --git a/products/01-llm-cost-router/README.md b/products/01-llm-cost-router/README.md new file mode 100644 index 0000000..36a3921 --- /dev/null +++ b/products/01-llm-cost-router/README.md @@ -0,0 +1,53 @@ +# dd0c/route — LLM Cost Router & Optimization Dashboard + +Drop-in OpenAI-compatible proxy that routes AI requests to the cheapest capable model. + +## Quick Start + +```bash +# Start local infra +docker compose up -d + +# Run the proxy +OPENAI_API_KEY=sk-your-key cargo run --bin dd0c-proxy + +# Test it +curl http://localhost:8080/v1/chat/completions \ + -H "Authorization: Bearer your-dd0c-key" \ + -H "Content-Type: application/json" \ + -d '{"model":"gpt-4o","messages":[{"role":"user","content":"Hello"}]}' +``` + +## Architecture + +- **Proxy Engine** (Rust/Axum) — <5ms overhead, SSE streaming, async telemetry +- **Router Brain** — Complexity classification, cost-based routing, fallback chains +- **Dashboard API** — REST API for config, analytics, team management +- **TimescaleDB** — Time-series telemetry with continuous aggregates +- **PostgreSQL** — Config, auth, routing rules +- **Redis** — API key cache, rate limiting + +## Project Structure + +``` +src/ + proxy/ — Proxy server (main entry point) + api/ — Dashboard API server + worker/ — Background jobs (digests, alerts) + router/ — Routing logic & complexity classifier + auth/ — API key validation, JWT, OAuth + config/ — App configuration + data/ — Data layer traits (EventQueue, ObjectStore) + analytics/ — PostHog PLG instrumentation +migrations/ — PostgreSQL + TimescaleDB schemas +tests/ — Unit, integration, E2E tests +infra/ — CDK / deployment configs +``` + +## Pricing + +| Tier | Price | Requests/mo | Retention | +|------|-------|-------------|-----------| +| Free | $0 | 10K | 7 days | +| Pro | $49/mo | 1M | 90 days | +| Enterprise | Custom | Unlimited | 1 year | diff --git a/products/01-llm-cost-router/docker-compose.yml b/products/01-llm-cost-router/docker-compose.yml new file mode 100644 index 0000000..02fcf62 --- /dev/null +++ b/products/01-llm-cost-router/docker-compose.yml @@ -0,0 +1,50 @@ +# dd0c/route — Local Development + +services: + postgres: + image: postgres:16-alpine + environment: + POSTGRES_USER: dd0c + POSTGRES_PASSWORD: dd0c + POSTGRES_DB: dd0c + ports: + - "5432:5432" + volumes: + - pg_data:/var/lib/postgresql/data + - ./migrations/001_init.sql:/docker-entrypoint-initdb.d/001_init.sql + healthcheck: + test: ["CMD-SHELL", "pg_isready -U dd0c"] + interval: 5s + timeout: 3s + retries: 5 + + timescaledb: + image: timescale/timescaledb:latest-pg16 + environment: + POSTGRES_USER: dd0c + POSTGRES_PASSWORD: dd0c + POSTGRES_DB: dd0c_telemetry + ports: + - "5433:5432" + volumes: + - ts_data:/var/lib/postgresql/data + - ./migrations/002_timescale.sql:/docker-entrypoint-initdb.d/002_timescale.sql + healthcheck: + test: ["CMD-SHELL", "pg_isready -U dd0c"] + interval: 5s + timeout: 3s + retries: 5 + + redis: + image: redis:7-alpine + ports: + - "6379:6379" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 3s + retries: 5 + +volumes: + pg_data: + ts_data: diff --git a/products/01-llm-cost-router/migrations/001_init.sql b/products/01-llm-cost-router/migrations/001_init.sql new file mode 100644 index 0000000..7bf19b0 --- /dev/null +++ b/products/01-llm-cost-router/migrations/001_init.sql @@ -0,0 +1,108 @@ +-- dd0c/route V1 schema — PostgreSQL (config + auth) + +-- Organizations +CREATE TABLE organizations ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name TEXT NOT NULL, + slug TEXT NOT NULL UNIQUE, + tier TEXT NOT NULL DEFAULT 'free' CHECK (tier IN ('free', 'pro', 'enterprise')), + stripe_customer_id TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +-- Users (GitHub OAuth) +CREATE TABLE users ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + org_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE, + github_id BIGINT UNIQUE, + email TEXT NOT NULL, + name TEXT, + role TEXT NOT NULL DEFAULT 'member' CHECK (role IN ('owner', 'member', 'viewer')), + avatar_url TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); +CREATE INDEX idx_users_org ON users(org_id); +CREATE INDEX idx_users_github ON users(github_id); + +-- API Keys (proxy auth) +CREATE TABLE api_keys ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + org_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE, + key_prefix CHAR(8) NOT NULL, -- first 8 chars for fast lookup + key_hash TEXT NOT NULL, -- bcrypt hash of full key + name TEXT NOT NULL DEFAULT 'Default', + scopes TEXT[] NOT NULL DEFAULT '{"proxy"}', + last_used_at TIMESTAMPTZ, + revoked_at TIMESTAMPTZ, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); +CREATE UNIQUE INDEX idx_api_keys_prefix ON api_keys(key_prefix) WHERE revoked_at IS NULL; +CREATE INDEX idx_api_keys_org ON api_keys(org_id); + +-- Provider Configs (encrypted API keys for upstream providers) +CREATE TABLE provider_configs ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + org_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE, + provider TEXT NOT NULL, -- 'openai', 'anthropic', etc. + encrypted_api_key BYTEA NOT NULL, -- AES-256-GCM encrypted + base_url TEXT, -- custom endpoint override + is_default BOOLEAN NOT NULL DEFAULT false, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + UNIQUE(org_id, provider) +); + +-- Routing Rules +CREATE TABLE routing_rules ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + org_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE, + priority INT NOT NULL DEFAULT 0, + name TEXT NOT NULL, + -- Match conditions + match_model TEXT, -- e.g. 'gpt-4o' + match_feature TEXT, -- X-DD0C-Feature header + match_team TEXT, -- X-DD0C-Team header + match_complexity TEXT CHECK (match_complexity IN ('low', 'medium', 'high')), + -- Routing action + strategy TEXT NOT NULL DEFAULT 'passthrough' CHECK (strategy IN ('passthrough', 'cheapest', 'quality-first', 'cascading')), + target_model TEXT, -- override model + target_provider TEXT, -- override provider + fallback_models TEXT[], -- for cascading strategy + enabled BOOLEAN NOT NULL DEFAULT true, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); +CREATE INDEX idx_routing_rules_org ON routing_rules(org_id, priority); + +-- Cost Tables (provider pricing, refreshed daily) +CREATE TABLE cost_tables ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + provider TEXT NOT NULL, + model TEXT NOT NULL, + input_cost_per_1k NUMERIC(10, 6) NOT NULL, + output_cost_per_1k NUMERIC(10, 6) NOT NULL, + effective_date DATE NOT NULL DEFAULT CURRENT_DATE, + UNIQUE(provider, model, effective_date) +); + +-- Seed current pricing +INSERT INTO cost_tables (provider, model, input_cost_per_1k, output_cost_per_1k) VALUES + ('openai', 'gpt-4o', 0.005000, 0.015000), + ('openai', 'gpt-4o-mini', 0.000150, 0.000600), + ('openai', 'gpt-4-turbo', 0.010000, 0.030000), + ('openai', 'gpt-3.5-turbo', 0.000500, 0.001500), + ('anthropic', 'claude-3-opus', 0.015000, 0.075000), + ('anthropic', 'claude-3-sonnet', 0.003000, 0.015000), + ('anthropic', 'claude-3-haiku', 0.000250, 0.001250), + ('anthropic', 'claude-3.5-sonnet', 0.003000, 0.015000); + +-- Feature flags (Transparent Factory: Atomic Flagging) +CREATE TABLE feature_flags ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + org_id UUID REFERENCES organizations(id) ON DELETE CASCADE, -- NULL = global + flag_key TEXT NOT NULL, + enabled BOOLEAN NOT NULL DEFAULT false, + rollout_pct INT NOT NULL DEFAULT 0 CHECK (rollout_pct BETWEEN 0 AND 100), + metadata JSONB NOT NULL DEFAULT '{}', + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + UNIQUE(org_id, flag_key) +); diff --git a/products/01-llm-cost-router/migrations/002_timescale.sql b/products/01-llm-cost-router/migrations/002_timescale.sql new file mode 100644 index 0000000..a183923 --- /dev/null +++ b/products/01-llm-cost-router/migrations/002_timescale.sql @@ -0,0 +1,88 @@ +-- dd0c/route V1 — TimescaleDB schema (telemetry) + +-- Request events (the core telemetry table) +CREATE TABLE request_events ( + time TIMESTAMPTZ NOT NULL DEFAULT now(), + org_id UUID NOT NULL, + original_model TEXT NOT NULL, + routed_model TEXT NOT NULL, + provider TEXT NOT NULL, + strategy TEXT NOT NULL, + latency_ms INT NOT NULL, + status_code SMALLINT NOT NULL, + is_streaming BOOLEAN NOT NULL DEFAULT false, + prompt_tokens INT NOT NULL DEFAULT 0, + completion_tokens INT NOT NULL DEFAULT 0, + cost_original NUMERIC(10, 6), + cost_actual NUMERIC(10, 6), + cost_saved NUMERIC(10, 6) +); + +-- Convert to hypertable (7-day chunks) +SELECT create_hypertable('request_events', 'time', chunk_time_interval => INTERVAL '7 days'); + +-- Indexes for dashboard queries +CREATE INDEX idx_events_org_time ON request_events (org_id, time DESC); +CREATE INDEX idx_events_model ON request_events (org_id, original_model, time DESC); + +-- Continuous aggregates for dashboard rollups + +-- Hourly rollup +CREATE MATERIALIZED VIEW request_events_hourly +WITH (timescaledb.continuous) AS +SELECT + time_bucket('1 hour', time) AS bucket, + org_id, + original_model, + routed_model, + provider, + strategy, + COUNT(*) AS request_count, + AVG(latency_ms)::INT AS avg_latency_ms, + PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY latency_ms)::INT AS p99_latency_ms, + SUM(prompt_tokens) AS total_prompt_tokens, + SUM(completion_tokens) AS total_completion_tokens, + SUM(cost_original) AS total_cost_original, + SUM(cost_actual) AS total_cost_actual, + SUM(cost_saved) AS total_cost_saved +FROM request_events +GROUP BY bucket, org_id, original_model, routed_model, provider, strategy; + +-- Refresh policy: every hour, lag 10 minutes +SELECT add_continuous_aggregate_policy('request_events_hourly', + start_offset => INTERVAL '2 hours', + end_offset => INTERVAL '10 minutes', + schedule_interval => INTERVAL '1 hour'); + +-- Daily rollup +CREATE MATERIALIZED VIEW request_events_daily +WITH (timescaledb.continuous) AS +SELECT + time_bucket('1 day', time) AS bucket, + org_id, + original_model, + routed_model, + COUNT(*) AS request_count, + SUM(prompt_tokens) AS total_prompt_tokens, + SUM(completion_tokens) AS total_completion_tokens, + SUM(cost_original) AS total_cost_original, + SUM(cost_actual) AS total_cost_actual, + SUM(cost_saved) AS total_cost_saved +FROM request_events +GROUP BY bucket, org_id, original_model, routed_model; + +SELECT add_continuous_aggregate_policy('request_events_daily', + start_offset => INTERVAL '3 days', + end_offset => INTERVAL '1 hour', + schedule_interval => INTERVAL '1 day'); + +-- Compression policy: compress chunks older than 30 days +ALTER TABLE request_events SET ( + timescaledb.compress, + timescaledb.compress_segmentby = 'org_id', + timescaledb.compress_orderby = 'time DESC' +); +SELECT add_compression_policy('request_events', INTERVAL '30 days'); + +-- Retention policy: drop data older than 1 year (free tier: 30 days via app logic) +SELECT add_retention_policy('request_events', INTERVAL '365 days');