Node.js/TypeScript server with MCP endpoint for AI assistant integration, Discord bot for thought capture and slash commands, PostgreSQL + pgvector for semantic search, and OpenRouter for embeddings/metadata extraction. Dockerized for deployment behind Caddy. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
59 lines
1.7 KiB
PL/PgSQL
59 lines
1.7 KiB
PL/PgSQL
CREATE EXTENSION IF NOT EXISTS vector;
|
|
|
|
CREATE TABLE IF NOT EXISTS thoughts (
|
|
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
|
user_id TEXT NOT NULL DEFAULT 'default',
|
|
content TEXT NOT NULL,
|
|
embedding VECTOR(1536),
|
|
metadata JSONB DEFAULT '{}'::jsonb,
|
|
source TEXT NOT NULL DEFAULT 'mcp',
|
|
created_at TIMESTAMPTZ DEFAULT now(),
|
|
updated_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS thoughts_embedding_idx ON thoughts USING hnsw (embedding vector_cosine_ops);
|
|
CREATE INDEX IF NOT EXISTS thoughts_metadata_idx ON thoughts USING gin (metadata);
|
|
CREATE INDEX IF NOT EXISTS thoughts_created_at_idx ON thoughts (created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS thoughts_user_id_idx ON thoughts (user_id);
|
|
|
|
-- Auto-update trigger for updated_at
|
|
CREATE OR REPLACE FUNCTION update_updated_at() RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = now();
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
DROP TRIGGER IF EXISTS thoughts_updated_at ON thoughts;
|
|
CREATE TRIGGER thoughts_updated_at
|
|
BEFORE UPDATE ON thoughts
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
|
|
-- Semantic search function
|
|
CREATE OR REPLACE FUNCTION match_thoughts(
|
|
query_embedding VECTOR(1536),
|
|
match_threshold FLOAT DEFAULT 0.7,
|
|
match_count INT DEFAULT 10,
|
|
filter JSONB DEFAULT '{}'::jsonb
|
|
) RETURNS TABLE (
|
|
id UUID,
|
|
content TEXT,
|
|
metadata JSONB,
|
|
similarity FLOAT,
|
|
created_at TIMESTAMPTZ
|
|
) LANGUAGE plpgsql AS $$
|
|
BEGIN
|
|
RETURN QUERY
|
|
SELECT
|
|
t.id,
|
|
t.content,
|
|
t.metadata,
|
|
(1 - (t.embedding <=> query_embedding))::FLOAT AS similarity,
|
|
t.created_at
|
|
FROM thoughts t
|
|
WHERE 1 - (t.embedding <=> query_embedding) > match_threshold
|
|
ORDER BY t.embedding <=> query_embedding
|
|
LIMIT match_count;
|
|
END;
|
|
$$;
|