Files
agentic-coding-workflow/migrations/0004_chat.up.sql
T

124 lines
5.7 KiB
SQL

-- ============ LLM Endpoints ============
CREATE TABLE llm_endpoints (
id UUID PRIMARY KEY,
provider TEXT NOT NULL CHECK (provider IN ('anthropic','openai','gemini')),
display_name TEXT NOT NULL,
base_url TEXT NOT NULL,
api_key_encrypted BYTEA NOT NULL,
created_by UUID NOT NULL REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX llm_endpoints_provider_idx ON llm_endpoints(provider);
-- ============ LLM Models ============
CREATE TABLE llm_models (
id UUID PRIMARY KEY,
endpoint_id UUID NOT NULL REFERENCES llm_endpoints(id) ON DELETE RESTRICT,
model_id TEXT NOT NULL,
display_name TEXT NOT NULL,
capabilities JSONB NOT NULL DEFAULT '{}'::jsonb,
context_window INT NOT NULL,
max_output_tokens INT NOT NULL,
prompt_price_per_million_usd NUMERIC(10,6) NOT NULL DEFAULT 0,
completion_price_per_million_usd NUMERIC(10,6) NOT NULL DEFAULT 0,
thinking_price_per_million_usd NUMERIC(10,6) NOT NULL DEFAULT 0,
is_title_generator BOOLEAN NOT NULL DEFAULT false,
enabled BOOLEAN NOT NULL DEFAULT true,
sort_order INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (endpoint_id, model_id)
);
CREATE UNIQUE INDEX llm_models_one_title_generator
ON llm_models ((1)) WHERE is_title_generator;
CREATE INDEX llm_models_enabled_sort_idx ON llm_models(enabled, sort_order);
-- ============ Prompt Templates ============
CREATE TABLE prompt_templates (
id UUID PRIMARY KEY,
name TEXT NOT NULL,
content TEXT NOT NULL,
scope TEXT NOT NULL CHECK (scope IN ('system','user')),
owner_id UUID REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CHECK ((scope='user' AND owner_id IS NOT NULL) OR (scope='system' AND owner_id IS NULL))
);
CREATE INDEX prompt_templates_scope_owner_idx ON prompt_templates(scope, owner_id);
-- ============ Conversations ============
CREATE TABLE conversations (
id UUID PRIMARY KEY,
user_id UUID NOT NULL REFERENCES users(id),
project_id UUID REFERENCES projects(id),
workspace_id UUID REFERENCES workspaces(id),
issue_id UUID REFERENCES issues(id),
model_id UUID NOT NULL REFERENCES llm_models(id) ON DELETE RESTRICT,
system_prompt TEXT NOT NULL DEFAULT '',
title TEXT,
title_generated_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ
);
CREATE INDEX conversations_user_list_idx ON conversations(user_id, deleted_at, updated_at DESC);
CREATE INDEX conversations_project_idx ON conversations(project_id) WHERE project_id IS NOT NULL;
CREATE INDEX conversations_workspace_idx ON conversations(workspace_id) WHERE workspace_id IS NOT NULL;
CREATE INDEX conversations_issue_idx ON conversations(issue_id) WHERE issue_id IS NOT NULL;
-- ============ Messages ============
CREATE TABLE messages (
id BIGSERIAL PRIMARY KEY,
conversation_id UUID NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
role TEXT NOT NULL CHECK (role IN ('user','assistant')),
content TEXT NOT NULL DEFAULT '',
thinking TEXT,
tool_calls JSONB,
status TEXT NOT NULL CHECK (status IN ('pending','ok','error','cancelled')),
error_message TEXT,
prompt_tokens INT,
completion_tokens INT,
thinking_tokens INT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX messages_conv_id_idx ON messages(conversation_id, id);
CREATE UNIQUE INDEX messages_one_pending_per_conv
ON messages(conversation_id) WHERE status='pending';
-- ============ Message Attachments ============
CREATE TABLE message_attachments (
id UUID PRIMARY KEY,
user_id UUID NOT NULL REFERENCES users(id),
message_id BIGINT REFERENCES messages(id) ON DELETE CASCADE,
filename TEXT NOT NULL,
mime_type TEXT NOT NULL,
size_bytes BIGINT NOT NULL,
sha256 TEXT NOT NULL,
storage_path TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX message_attachments_orphan_idx
ON message_attachments(user_id, created_at) WHERE message_id IS NULL;
CREATE INDEX message_attachments_msg_idx
ON message_attachments(message_id) WHERE message_id IS NOT NULL;
-- ============ LLM Usage ============
CREATE TABLE llm_usage (
id BIGSERIAL PRIMARY KEY,
conversation_id UUID NOT NULL,
message_id BIGINT NOT NULL,
user_id UUID NOT NULL,
endpoint_id UUID NOT NULL,
model_id UUID NOT NULL,
prompt_tokens INT NOT NULL,
completion_tokens INT NOT NULL,
thinking_tokens INT NOT NULL DEFAULT 0,
cost_usd NUMERIC(10,6) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX llm_usage_user_time_idx ON llm_usage(user_id, created_at);
CREATE INDEX llm_usage_model_time_idx ON llm_usage(model_id, created_at);
CREATE INDEX llm_usage_time_idx ON llm_usage(created_at);