409 lines
15 KiB
PL/PgSQL
409 lines
15 KiB
PL/PgSQL
-- ==========================================
|
|
-- 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();
|