Initial project scaffold: Open Brain self-hosted knowledge infrastructure

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>
This commit is contained in:
YOUNG
2026-03-06 14:53:41 -06:00
commit e90ea1591b
16 changed files with 3332 additions and 0 deletions

58
sql/schema.sql Normal file
View File

@@ -0,0 +1,58 @@
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;
$$;