-- Migration 000003: Routing rules — intelligent routing engine (Sprint 5) -- Stores per-tenant routing rules with JSONB conditions and actions. -- Evaluated in priority order (ASC); first fully matching rule wins. CREATE TABLE routing_rules ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, name TEXT NOT NULL, description TEXT, -- conditions: JSON array of condition objects, ALL must match (AND logic). -- Each condition: {"field":"request.sensitivity","operator":"gte","value":"high"} -- Supported fields: user.role, user.department, request.sensitivity, -- request.model, request.use_case, request.token_estimate -- Supported operators: eq, neq, in, nin, gte, lte, contains, matches -- Empty array [] means the rule matches all requests (catch-all). conditions JSONB NOT NULL DEFAULT '[]', -- action: routing decision when rule matches. -- {"provider":"ollama","model":"llama3","fallback_providers":["openai","anthropic"]} action JSONB NOT NULL, -- priority: lower value = higher priority. Evaluated in ASC order. priority INT NOT NULL DEFAULT 100, is_enabled BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- Fast lookup: active rules for a tenant, sorted by priority. CREATE INDEX idx_routing_rules_tenant_active ON routing_rules (tenant_id, is_enabled, priority); -- Validate that the 'provider' field in action is one of the known providers. ALTER TABLE routing_rules ADD CONSTRAINT chk_routing_rules_provider CHECK ((action->>'provider') IN ('openai', 'anthropic', 'azure', 'mistral', 'ollama')); -- Auto-update updated_at on row modification. CREATE TRIGGER routing_rules_updated_at BEFORE UPDATE ON routing_rules FOR EACH ROW EXECUTE FUNCTION set_updated_at(); -- Row-Level Security: tenants can only access their own rules. ALTER TABLE routing_rules ENABLE ROW LEVEL SECURITY; CREATE POLICY routing_rules_tenant_isolation ON routing_rules USING (tenant_id = current_setting('app.current_tenant_id', TRUE)::UUID);