-- 0007_ai_config.sql -- AI-administrasjon: modellaliaser, leverandører, jobbruting og tokenlogging. -- PG som source of truth for LiteLLM config-generering. BEGIN; -- === Modellaliaser (globale, ikke per workspace) === CREATE TABLE ai_model_aliases ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), alias TEXT NOT NULL UNIQUE, -- f.eks. "sidelinja/rutine" description TEXT, -- kort beskrivelse av aliaset is_active BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- === Leverandør-modeller per alias med prioritet === CREATE TABLE ai_model_providers ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), alias_id UUID NOT NULL REFERENCES ai_model_aliases(id) ON DELETE CASCADE, priority INT NOT NULL DEFAULT 1, -- lavere = høyere prioritet i LiteLLM litellm_model TEXT NOT NULL, -- f.eks. "gemini/gemini-2.5-flash-lite" api_key_env TEXT NOT NULL, -- f.eks. "GEMINI_API_KEY" is_active BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (alias_id, priority) ); -- === Jobbtype → alias mapping === CREATE TABLE ai_job_routing ( job_type TEXT PRIMARY KEY, -- f.eks. "ai_text_process" alias_id UUID NOT NULL REFERENCES ai_model_aliases(id) ON DELETE RESTRICT, description TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- === Per-kall token-logging (workspace-scopet) === CREATE TABLE ai_usage_log ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), workspace_id UUID NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE, job_id UUID REFERENCES job_queue(id) ON DELETE SET NULL, job_type TEXT NOT NULL, model_alias TEXT NOT NULL, -- alias brukt (snapshot) model_actual TEXT, -- faktisk modell fra respons prompt_tokens INT NOT NULL DEFAULT 0, completion_tokens INT NOT NULL DEFAULT 0, total_tokens INT NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_ai_usage_log_workspace ON ai_usage_log (workspace_id, created_at DESC); CREATE INDEX idx_ai_usage_log_alias ON ai_usage_log (model_alias, created_at DESC); -- === Seed-data: matcher nåværende config.yaml === -- Aliaser INSERT INTO ai_model_aliases (alias, description) VALUES ('sidelinja/rutine', 'Billig, høyt volum — tekstrensing, faktauthenting, oversettelse'), ('sidelinja/resonering', 'Presis, lav volum — kompleks analyse, research'); -- Leverandører for sidelinja/rutine INSERT INTO ai_model_providers (alias_id, priority, litellm_model, api_key_env) VALUES ((SELECT id FROM ai_model_aliases WHERE alias = 'sidelinja/rutine'), 1, 'gemini/gemini-2.5-flash-lite', 'GEMINI_API_KEY'), ((SELECT id FROM ai_model_aliases WHERE alias = 'sidelinja/rutine'), 2, 'gemini/gemini-2.5-flash', 'GEMINI_API_KEY'), ((SELECT id FROM ai_model_aliases WHERE alias = 'sidelinja/rutine'), 3, 'openrouter/google/gemini-2.5-flash-preview', 'OPENROUTER_API_KEY'); -- Leverandører for sidelinja/resonering INSERT INTO ai_model_providers (alias_id, priority, litellm_model, api_key_env) VALUES ((SELECT id FROM ai_model_aliases WHERE alias = 'sidelinja/resonering'), 1, 'openrouter/anthropic/claude-sonnet-4', 'OPENROUTER_API_KEY'), ((SELECT id FROM ai_model_aliases WHERE alias = 'sidelinja/resonering'), 2, 'gemini/gemini-2.5-flash', 'GEMINI_API_KEY'); -- Jobbruting INSERT INTO ai_job_routing (job_type, alias_id, description) VALUES ('ai_text_process', (SELECT id FROM ai_model_aliases WHERE alias = 'sidelinja/rutine'), 'Tekstrensing og AI-behandling via ✨-knappen'); COMMIT;