-- Chat Switchboard — 003 Providers & Routing (SQLite) CREATE TABLE IF NOT EXISTS provider_configs ( id TEXT PRIMARY KEY, scope TEXT NOT NULL CHECK (scope IN ('global', 'team', 'personal')), owner_id TEXT, name TEXT NOT NULL, provider TEXT NOT NULL, endpoint TEXT NOT NULL, api_key_enc BLOB, key_nonce BLOB, key_scope TEXT NOT NULL DEFAULT 'global', model_default TEXT, config TEXT DEFAULT '{}', headers TEXT DEFAULT '{}', settings TEXT DEFAULT '{}', is_active INTEGER DEFAULT 1, is_private INTEGER DEFAULT 0, proxy_mode TEXT NOT NULL DEFAULT 'system' CHECK (proxy_mode IN ('system', 'direct', 'custom')), proxy_url TEXT, created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')), CHECK (proxy_mode != 'custom' OR proxy_url IS NOT NULL) ); CREATE INDEX IF NOT EXISTS idx_provider_configs_scope ON provider_configs(scope); CREATE INDEX IF NOT EXISTS idx_provider_configs_owner ON provider_configs(owner_id); CREATE INDEX IF NOT EXISTS idx_provider_configs_active ON provider_configs(is_active); CREATE TABLE IF NOT EXISTS model_catalog ( id TEXT PRIMARY KEY, provider_config_id TEXT NOT NULL REFERENCES provider_configs(id) ON DELETE CASCADE, model_id TEXT NOT NULL, display_name TEXT, model_type TEXT DEFAULT 'chat', capabilities TEXT NOT NULL DEFAULT '{}', pricing TEXT, visibility TEXT DEFAULT 'disabled' CHECK (visibility IN ('enabled', 'disabled', 'team')), last_synced_at TEXT, created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')), UNIQUE(provider_config_id, model_id) ); CREATE INDEX IF NOT EXISTS idx_model_catalog_provider ON model_catalog(provider_config_id); CREATE INDEX IF NOT EXISTS idx_model_catalog_enabled ON model_catalog(visibility); CREATE INDEX IF NOT EXISTS idx_model_catalog_type ON model_catalog(model_type); CREATE TABLE IF NOT EXISTS model_pricing ( id TEXT PRIMARY KEY, provider_config_id TEXT NOT NULL REFERENCES provider_configs(id) ON DELETE CASCADE, model_id TEXT NOT NULL, input_per_m REAL, output_per_m REAL, cache_create_per_m REAL, cache_read_per_m REAL, currency TEXT NOT NULL DEFAULT 'USD', source TEXT NOT NULL DEFAULT 'manual', updated_at TEXT DEFAULT (datetime('now')), updated_by TEXT REFERENCES users(id), UNIQUE(provider_config_id, model_id) ); CREATE TABLE IF NOT EXISTS provider_health ( id TEXT PRIMARY KEY, provider_config_id TEXT NOT NULL REFERENCES provider_configs(id) ON DELETE CASCADE, window_start TEXT NOT NULL, request_count INTEGER NOT NULL DEFAULT 0, error_count INTEGER NOT NULL DEFAULT 0, timeout_count INTEGER NOT NULL DEFAULT 0, total_latency_ms INTEGER NOT NULL DEFAULT 0, max_latency_ms INTEGER NOT NULL DEFAULT 0, rate_limit_count INTEGER NOT NULL DEFAULT 0, last_error TEXT, last_error_at TEXT, updated_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE (provider_config_id, window_start) ); CREATE INDEX IF NOT EXISTS idx_provider_health_window ON provider_health(provider_config_id, window_start); CREATE TABLE IF NOT EXISTS capability_overrides ( id TEXT PRIMARY KEY, provider_config_id TEXT REFERENCES provider_configs(id) ON DELETE CASCADE, model_id TEXT NOT NULL, field TEXT NOT NULL, value TEXT NOT NULL, set_by TEXT REFERENCES users(id) ON DELETE SET NULL, created_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE (provider_config_id, model_id, field) ); CREATE INDEX IF NOT EXISTS idx_capability_overrides_model ON capability_overrides(model_id); CREATE TABLE IF NOT EXISTS routing_policies ( id TEXT PRIMARY KEY, name TEXT NOT NULL, scope TEXT NOT NULL DEFAULT 'global' CHECK (scope IN ('global', 'team')), team_id TEXT REFERENCES teams(id) ON DELETE CASCADE, priority INTEGER NOT NULL DEFAULT 100, policy_type TEXT NOT NULL CHECK (policy_type IN ('provider_prefer', 'team_route', 'cost_limit', 'model_alias')), config TEXT NOT NULL DEFAULT '{}', is_active INTEGER NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX IF NOT EXISTS idx_routing_policies_active ON routing_policies(is_active, priority); CREATE INDEX IF NOT EXISTS idx_routing_policies_team ON routing_policies(team_id);