Rene Fichtmueller 3a00ff4d33 feat: initial llm-gateway implementation
- Complete Fastify gateway with 8-stage pipeline
- Circuit breaker (opossum) per model tier
- Rate limiting per caller
- Ban list validation (EN/DE/auto-detected)
- TIP validator (SFF-8024, part numbers, wavelengths)
- Prometheus metrics
- pg-boss async queue
- PostgreSQL audit log + review queue
- 9 prompt templates (TIP, LinkedIn, ShieldX)
- Learning engine scaffolding
- Auto-learning: ban-list, few-shot, routing, prompt optimizer
2026-04-02 22:48:55 +02:00

130 lines
6.7 KiB
SQL

-- LLM Gateway Learning Engine Schema
-- Run after 001_initial.sql
-- psql -U llm -d llm_gateway -f 002_learning.sql
-- ─── BAN CANDIDATES ────────────────────────────────────────────────────────
-- Auto-detected suspicious phrases waiting for promotion to banlist
CREATE TABLE IF NOT EXISTS ban_candidates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
term VARCHAR(256) NOT NULL,
language VARCHAR(4) NOT NULL CHECK (language IN ('en', 'de', 'auto')),
category VARCHAR(32) NOT NULL CHECK (category IN ('buzzword', 'filler', 'opener', 'closer', 'transition')),
occurrence_count INTEGER NOT NULL DEFAULT 1,
source_task_types TEXT[] NOT NULL DEFAULT '{}',
example_contexts TEXT[],
promoted BOOLEAN NOT NULL DEFAULT false,
promoted_at TIMESTAMPTZ,
rejected BOOLEAN NOT NULL DEFAULT false,
rejected_at TIMESTAMPTZ,
rejected_reason TEXT,
UNIQUE(term, language)
);
CREATE INDEX IF NOT EXISTS idx_ban_candidates_term ON ban_candidates (term, language);
CREATE INDEX IF NOT EXISTS idx_ban_candidates_count ON ban_candidates (occurrence_count DESC) WHERE promoted = false AND rejected = false;
-- ─── FEW-SHOT CANDIDATES ────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS few_shot_candidates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
task_type VARCHAR(64) NOT NULL,
llm_call_id UUID REFERENCES llm_calls(id) ON DELETE SET NULL,
input_text TEXT NOT NULL,
output_text TEXT NOT NULL,
confidence NUMERIC(3,1) NOT NULL,
similarity_to_existing NUMERIC(4,3),
promoted BOOLEAN NOT NULL DEFAULT false,
promoted_at TIMESTAMPTZ,
template_version VARCHAR(16),
is_negative BOOLEAN NOT NULL DEFAULT false,
negative_reason TEXT
);
CREATE INDEX IF NOT EXISTS idx_few_shot_candidates_task ON few_shot_candidates (task_type, confidence DESC);
CREATE INDEX IF NOT EXISTS idx_few_shot_candidates_pending ON few_shot_candidates (task_type) WHERE promoted = false;
-- ─── ROUTING CANDIDATES ─────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS routing_candidates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
task_type VARCHAR(64) NOT NULL,
current_model VARCHAR(128) NOT NULL,
candidate_model VARCHAR(128) NOT NULL,
current_avg_confidence NUMERIC(4,2),
candidate_avg_confidence NUMERIC(4,2),
current_p95_latency_ms INTEGER,
candidate_p95_latency_ms INTEGER,
sample_size INTEGER NOT NULL,
auto_applied BOOLEAN NOT NULL DEFAULT false,
applied_at TIMESTAMPTZ,
rollback_at TIMESTAMPTZ,
rollback_reason TEXT
);
CREATE INDEX IF NOT EXISTS idx_routing_candidates_task ON routing_candidates (task_type, created_at DESC);
-- ─── PROMPT CANDIDATES ──────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS prompt_candidates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
template_id VARCHAR(128) NOT NULL,
current_version VARCHAR(16) NOT NULL,
candidate_version VARCHAR(16) NOT NULL,
current_system_prompt TEXT NOT NULL,
candidate_system_prompt TEXT NOT NULL,
improvement_rationale TEXT NOT NULL,
changes_made TEXT[] NOT NULL DEFAULT '{}',
expected_improvements TEXT[] NOT NULL DEFAULT '{}',
test_confidence_delta NUMERIC(4,2),
auto_applied BOOLEAN NOT NULL DEFAULT false,
human_approved BOOLEAN,
applied_at TIMESTAMPTZ,
review_queue_id UUID REFERENCES review_queue(id) ON DELETE SET NULL
);
CREATE INDEX IF NOT EXISTS idx_prompt_candidates_template ON prompt_candidates (template_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_prompt_candidates_pending ON prompt_candidates (template_id) WHERE auto_applied = false AND human_approved IS NULL;
-- ─── LEARNING REPORTS ───────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS learning_reports (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
period_from TIMESTAMPTZ NOT NULL,
period_to TIMESTAMPTZ NOT NULL,
report_data JSONB NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_learning_reports_period ON learning_reports (period_from DESC);
-- ─── A/B TEST TRACKING ──────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS ab_tests (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
completed_at TIMESTAMPTZ,
task_type VARCHAR(64) NOT NULL,
control_model VARCHAR(128) NOT NULL,
challenger_model VARCHAR(128) NOT NULL,
traffic_percent INTEGER NOT NULL DEFAULT 10,
control_calls INTEGER NOT NULL DEFAULT 0,
challenger_calls INTEGER NOT NULL DEFAULT 0,
control_avg_conf NUMERIC(4,2),
challenger_avg_conf NUMERIC(4,2),
winner VARCHAR(128),
auto_promoted BOOLEAN NOT NULL DEFAULT false,
status VARCHAR(16) NOT NULL DEFAULT 'running'
);
CREATE INDEX IF NOT EXISTS idx_ab_tests_task ON ab_tests (task_type, status);
-- ─── ADDITIONAL INDEXES ON EXISTING TABLES ──────────────────────────────────
-- Safe to run even if already exist
CREATE INDEX IF NOT EXISTS idx_routing_metrics_lookup
ON routing_metrics (task_type, model_used, recorded_at DESC);
CREATE INDEX IF NOT EXISTS idx_learning_corpus_task
ON learning_corpus (task_type, quality_score DESC, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_ban_analytics_task_term
ON ban_analytics (task_type, term, created_at DESC);