Add comprehensive PostgreSQL schema for all features

This commit is contained in:
2026-02-03 23:04:44 +00:00
parent ad13c08f92
commit 8f5ed23dd7

View File

@@ -0,0 +1,408 @@
-- ==========================================
-- Chat Switchboard - PostgreSQL Schema
-- ==========================================
-- Enable extensions
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
CREATE EXTENSION IF NOT EXISTS "vector"; -- For embeddings (pgvector)
-- ==========================================
-- Core Tables
-- ==========================================
-- Users
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
display_name VARCHAR(100),
avatar_url TEXT,
role VARCHAR(20) DEFAULT 'user', -- user, admin, moderator
is_active BOOLEAN DEFAULT true,
settings JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
last_login_at TIMESTAMP WITH TIME ZONE
);
CREATE INDEX idx_users_username ON users(username);
CREATE INDEX idx_users_email ON users(email);
-- API Configurations (user's API keys)
CREATE TABLE api_configs (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
name VARCHAR(100) NOT NULL, -- "OpenAI", "Claude", "Local Ollama"
provider VARCHAR(50) NOT NULL, -- openai, anthropic, ollama, openrouter
endpoint TEXT NOT NULL,
api_key_encrypted TEXT, -- Encrypted at rest
model_default VARCHAR(100),
config JSONB DEFAULT '{}'::jsonb, -- Custom settings per provider
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_api_configs_user ON api_configs(user_id);
-- ==========================================
-- Feature 1: CHATS (User to LLM)
-- ==========================================
CREATE TABLE chats (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
title VARCHAR(500) NOT NULL,
model VARCHAR(100), -- Current model for this chat
api_config_id UUID REFERENCES api_configs(id) ON DELETE SET NULL,
system_prompt TEXT,
settings JSONB DEFAULT '{}'::jsonb, -- temperature, max_tokens, etc
is_archived BOOLEAN DEFAULT false,
is_pinned BOOLEAN DEFAULT false,
folder VARCHAR(100), -- For organization
tags TEXT[], -- For filtering
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_chats_user ON chats(user_id);
CREATE INDEX idx_chats_updated ON chats(updated_at DESC);
CREATE INDEX idx_chats_tags ON chats USING GIN(tags);
CREATE TABLE chat_messages (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
chat_id UUID REFERENCES chats(id) ON DELETE CASCADE,
role VARCHAR(20) NOT NULL, -- user, assistant, system, tool
content TEXT NOT NULL,
model VARCHAR(100), -- Which model generated this (for assistant)
tokens_used INTEGER,
tool_calls JSONB, -- Function calls made
metadata JSONB DEFAULT '{}'::jsonb, -- thinking_blocks, attachments, etc
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_chat_messages_chat ON chat_messages(chat_id, created_at);
-- Model routing history (for analytics/debugging)
CREATE TABLE model_routing_log (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
chat_id UUID REFERENCES chats(id) ON DELETE CASCADE,
message_id UUID REFERENCES chat_messages(id) ON DELETE CASCADE,
requested_model VARCHAR(100),
routed_model VARCHAR(100),
reason TEXT, -- "cost_optimization", "context_length", "manual", "fallback"
latency_ms INTEGER,
cost_usd NUMERIC(10, 6),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_routing_log_chat ON model_routing_log(chat_id);
-- ==========================================
-- Feature 2: CHANNELS (User to User + AI)
-- ==========================================
CREATE TABLE channels (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name VARCHAR(100) UNIQUE NOT NULL,
description TEXT,
type VARCHAR(20) DEFAULT 'public', -- public, private, dm
owner_id UUID REFERENCES users(id) ON DELETE SET NULL,
settings JSONB DEFAULT '{}'::jsonb, -- ai_participants, webhooks, etc
is_archived BOOLEAN DEFAULT false,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_channels_name ON channels(name);
CREATE INDEX idx_channels_type ON channels(type);
-- Channel memberships
CREATE TABLE channel_members (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
channel_id UUID REFERENCES channels(id) ON DELETE CASCADE,
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
role VARCHAR(20) DEFAULT 'member', -- owner, admin, member
joined_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
last_read_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(channel_id, user_id)
);
CREATE INDEX idx_channel_members_channel ON channel_members(channel_id);
CREATE INDEX idx_channel_members_user ON channel_members(user_id);
-- Channel messages
CREATE TABLE channel_messages (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
channel_id UUID REFERENCES channels(id) ON DELETE CASCADE,
user_id UUID REFERENCES users(id) ON DELETE SET NULL, -- NULL for AI messages
content TEXT NOT NULL,
mentions JSONB, -- {users: [uuid], models: [name]}
thread_id UUID REFERENCES channel_messages(id) ON DELETE CASCADE, -- For threading
attachments JSONB, -- Files, images, etc
reactions JSONB DEFAULT '{}'::jsonb, -- {emoji: [user_ids]}
is_ai_message BOOLEAN DEFAULT false,
ai_model VARCHAR(100), -- If AI generated
edited_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_channel_messages_channel ON channel_messages(channel_id, created_at DESC);
CREATE INDEX idx_channel_messages_thread ON channel_messages(thread_id);
CREATE INDEX idx_channel_messages_mentions ON channel_messages USING GIN(mentions);
-- ==========================================
-- Feature 3: NOTES & KNOWLEDGE BASES
-- ==========================================
CREATE TABLE notes (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
title VARCHAR(500) NOT NULL,
content TEXT NOT NULL,
content_type VARCHAR(20) DEFAULT 'markdown', -- markdown, html, plain
folder VARCHAR(100),
tags TEXT[],
is_pinned BOOLEAN DEFAULT false,
is_shared BOOLEAN DEFAULT false,
share_token UUID UNIQUE DEFAULT uuid_generate_v4(),
parent_note_id UUID REFERENCES notes(id) ON DELETE SET NULL, -- For hierarchies
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_notes_user ON notes(user_id);
CREATE INDEX idx_notes_updated ON notes(updated_at DESC);
CREATE INDEX idx_notes_tags ON notes USING GIN(tags);
CREATE INDEX idx_notes_share_token ON notes(share_token) WHERE is_shared = true;
-- Knowledge Bases (Collections of documents)
CREATE TABLE knowledge_bases (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
name VARCHAR(200) NOT NULL,
description TEXT,
embedding_model VARCHAR(100) DEFAULT 'text-embedding-ada-002',
settings JSONB DEFAULT '{}'::jsonb, -- chunk_size, overlap, etc
is_public BOOLEAN DEFAULT false,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_knowledge_bases_user ON knowledge_bases(user_id);
-- Documents in knowledge bases
CREATE TABLE kb_documents (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
kb_id UUID REFERENCES knowledge_bases(id) ON DELETE CASCADE,
filename VARCHAR(500) NOT NULL,
content TEXT NOT NULL,
content_type VARCHAR(50), -- application/pdf, text/markdown, etc
file_size INTEGER,
metadata JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_kb_documents_kb ON kb_documents(kb_id);
-- Document chunks (for RAG)
CREATE TABLE kb_chunks (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
document_id UUID REFERENCES kb_documents(id) ON DELETE CASCADE,
kb_id UUID REFERENCES knowledge_bases(id) ON DELETE CASCADE,
content TEXT NOT NULL,
embedding vector(1536), -- For pgvector similarity search
chunk_index INTEGER,
metadata JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_kb_chunks_document ON kb_chunks(document_id);
CREATE INDEX idx_kb_chunks_embedding ON kb_chunks USING ivfflat (embedding vector_cosine_ops);
-- ==========================================
-- Feature 4: PLUGIN ORCHESTRATION
-- ==========================================
-- Installed extensions/plugins
CREATE TABLE extensions (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name VARCHAR(100) UNIQUE NOT NULL,
version VARCHAR(20) NOT NULL,
author VARCHAR(100),
description TEXT,
runtime VARCHAR(20), -- python, go, node, rust
entry_point TEXT NOT NULL,
port INTEGER,
manifest JSONB NOT NULL, -- Full extension.json
is_enabled BOOLEAN DEFAULT true,
is_system BOOLEAN DEFAULT false, -- Core extensions
install_source VARCHAR(200), -- URL or marketplace ID
installed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_extensions_enabled ON extensions(is_enabled);
-- Extension tools/functions
CREATE TABLE extension_tools (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
extension_id UUID REFERENCES extensions(id) ON DELETE CASCADE,
name VARCHAR(100) NOT NULL,
description TEXT,
parameters_schema JSONB NOT NULL, -- JSON Schema
response_schema JSONB,
is_enabled BOOLEAN DEFAULT true,
UNIQUE(extension_id, name)
);
CREATE INDEX idx_extension_tools_extension ON extension_tools(extension_id);
-- Tool usage log (for analytics)
CREATE TABLE tool_usage_log (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
tool_id UUID REFERENCES extension_tools(id) ON DELETE SET NULL,
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
chat_id UUID REFERENCES chats(id) ON DELETE SET NULL,
channel_id UUID REFERENCES channels(id) ON DELETE SET NULL,
input_params JSONB,
output_result JSONB,
execution_time_ms INTEGER,
success BOOLEAN,
error_message TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_tool_usage_tool ON tool_usage_log(tool_id);
CREATE INDEX idx_tool_usage_user ON tool_usage_log(user_id);
-- ==========================================
-- Feature 5: WORKFLOWS (Unique Feature!)
-- ==========================================
-- Workflow definitions (DAG of AI operations)
CREATE TABLE workflows (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
name VARCHAR(200) NOT NULL,
description TEXT,
graph JSONB NOT NULL, -- Node-edge DAG structure
input_schema JSONB, -- Expected inputs
output_schema JSONB, -- Expected outputs
is_public BOOLEAN DEFAULT false,
is_template BOOLEAN DEFAULT false,
tags TEXT[],
usage_count INTEGER DEFAULT 0,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_workflows_user ON workflows(user_id);
CREATE INDEX idx_workflows_public ON workflows(is_public) WHERE is_public = true;
CREATE INDEX idx_workflows_tags ON workflows USING GIN(tags);
-- Workflow executions
CREATE TABLE workflow_executions (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
workflow_id UUID REFERENCES workflows(id) ON DELETE CASCADE,
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
input_data JSONB,
output_data JSONB,
status VARCHAR(20), -- running, completed, failed
steps JSONB, -- Execution trace
total_cost_usd NUMERIC(10, 6),
total_time_ms INTEGER,
error_message TEXT,
started_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
completed_at TIMESTAMP WITH TIME ZONE
);
CREATE INDEX idx_workflow_executions_workflow ON workflow_executions(workflow_id);
CREATE INDEX idx_workflow_executions_user ON workflow_executions(user_id);
-- ==========================================
-- Shared/Utility Tables
-- ==========================================
-- File uploads
CREATE TABLE files (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
filename VARCHAR(500) NOT NULL,
content_type VARCHAR(100),
file_size INTEGER,
storage_path TEXT NOT NULL, -- S3/local path
is_public BOOLEAN DEFAULT false,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_files_user ON files(user_id);
-- Webhooks
CREATE TABLE webhooks (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
name VARCHAR(100),
url TEXT NOT NULL,
events TEXT[] NOT NULL, -- chat.message, channel.message, etc
secret VARCHAR(100),
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_webhooks_user ON webhooks(user_id);
-- Audit log
CREATE TABLE audit_log (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
action VARCHAR(100) NOT NULL,
resource_type VARCHAR(50),
resource_id UUID,
metadata JSONB,
ip_address INET,
user_agent TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_audit_log_user ON audit_log(user_id);
CREATE INDEX idx_audit_log_created ON audit_log(created_at DESC);
-- ==========================================
-- Functions & Triggers
-- ==========================================
-- Auto-update updated_at timestamps
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER users_updated_at BEFORE UPDATE ON users
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
CREATE TRIGGER chats_updated_at BEFORE UPDATE ON chats
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
CREATE TRIGGER channels_updated_at BEFORE UPDATE ON channels
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
CREATE TRIGGER notes_updated_at BEFORE UPDATE ON notes
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
CREATE TRIGGER knowledge_bases_updated_at BEFORE UPDATE ON knowledge_bases
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
CREATE TRIGGER workflows_updated_at BEFORE UPDATE ON workflows
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
-- Increment workflow usage count
CREATE OR REPLACE FUNCTION increment_workflow_usage()
RETURNS TRIGGER AS $$
BEGIN
UPDATE workflows SET usage_count = usage_count + 1 WHERE id = NEW.workflow_id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER workflow_executions_insert AFTER INSERT ON workflow_executions
FOR EACH ROW EXECUTE FUNCTION increment_workflow_usage();