Add AI Council architecture: Tier 2/3/Graph implementation + Integration Plan

Architecture (Agent 1):
- hermes_memory/tier2/{schema,facts,entities,relations,timeline}.py
- hermes_memory/tier3/{backend,chroma_backend,embedder}.py
- hermes_memory/graph/nx_store.py
- hermes_memory/api/memory_api.py (unified API)
- hermes_memory/cron/{consolidate,embed_queue,graph_refresh,prune}.py
- hermes_memory/config.py + pyproject.toml

Integration Plan (Agent 3):
- INTEGRATION_PLAN.md: Memory Provider Plugin strategy
- Hermes Core needs minimal changes
- sync_turn() + prefetch() hooks
- Skills integration via nextlevel_search/remember

Auto-Extraction (Agent 2):
- ARCHITECTURE.md: Full extraction pipeline docs
- Chunking, Pre-Filter, LLM Prompts, Classification
- Entity-Linking, Temporal Reasoning, Deduplication

All files: Python syntax checked, ECC standards applied.
This commit is contained in:
Florian Hartmann
2026-06-03 22:51:50 +00:00
parent 33fb180855
commit b83546d833
25 changed files with 2661 additions and 0 deletions
+196
View File
@@ -0,0 +1,196 @@
"""SQLite Schema & Migrationen für Tier 2.
Enthält alle CREATE TABLE / INDEX / TRIGGER Statements
mit Schema-Versionierung.
"""
import sqlite3
import time
from pathlib import Path
SCHEMA_VERSION = 1
SCHEMA_SQL = """
-- Schema-Version
CREATE TABLE IF NOT EXISTS memory_schema_version (
version INTEGER PRIMARY KEY,
applied_at REAL NOT NULL
);
-- Fakten
CREATE TABLE IF NOT EXISTS facts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT NOT NULL UNIQUE,
content TEXT NOT NULL,
content_hash TEXT NOT NULL,
category TEXT,
confidence REAL DEFAULT 1.0,
source_type TEXT NOT NULL,
source_id TEXT,
created_at REAL NOT NULL,
updated_at REAL NOT NULL,
expires_at REAL,
access_count INTEGER DEFAULT 0,
last_accessed REAL,
is_archived INTEGER DEFAULT 0
);
CREATE INDEX IF NOT EXISTS idx_facts_category ON facts(category);
CREATE INDEX IF NOT EXISTS idx_facts_source ON facts(source_type, source_id);
CREATE INDEX IF NOT EXISTS idx_facts_created ON facts(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_facts_hash ON facts(content_hash);
CREATE INDEX IF NOT EXISTS idx_facts_confidence ON facts(confidence DESC);
-- Entitäten
CREATE TABLE IF NOT EXISTS entities (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
aliases TEXT,
entity_type TEXT NOT NULL,
description TEXT,
first_seen REAL NOT NULL,
last_seen REAL NOT NULL,
occurrence_count INTEGER DEFAULT 1,
metadata TEXT
);
CREATE INDEX IF NOT EXISTS idx_entities_name ON entities(name);
CREATE INDEX IF NOT EXISTS idx_entities_type ON entities(entity_type);
-- Relationen
CREATE TABLE IF NOT EXISTS relations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT NOT NULL UNIQUE,
from_entity_id TEXT NOT NULL,
to_entity_id TEXT NOT NULL,
relation_type TEXT NOT NULL,
strength REAL DEFAULT 1.0,
evidence_fact_id TEXT,
created_at REAL NOT NULL,
updated_at REAL NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_relations_from ON relations(from_entity_id);
CREATE INDEX IF NOT EXISTS idx_relations_to ON relations(to_entity_id);
CREATE INDEX IF NOT EXISTS idx_relations_type ON relations(relation_type);
-- Timeline
CREATE TABLE IF NOT EXISTS timeline (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT NOT NULL UNIQUE,
event_type TEXT NOT NULL,
title TEXT NOT NULL,
description TEXT,
related_entities TEXT,
related_facts TEXT,
session_id TEXT,
timestamp REAL NOT NULL,
importance REAL DEFAULT 0.5
);
CREATE INDEX IF NOT EXISTS idx_timeline_time ON timeline(timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_timeline_type ON timeline(event_type);
-- Audit-Log
CREATE TABLE IF NOT EXISTS memory_audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp REAL NOT NULL,
action TEXT NOT NULL,
tier TEXT NOT NULL,
actor TEXT NOT NULL,
target_uuid TEXT,
diff TEXT,
success INTEGER DEFAULT 1
);
CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON memory_audit_log(timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_audit_actor ON memory_audit_log(actor);
-- Embedding-Queue
CREATE TABLE IF NOT EXISTS embedding_queue (
id INTEGER PRIMARY KEY AUTOINCREMENT,
fact_id TEXT,
content TEXT NOT NULL,
source_type TEXT NOT NULL,
session_id TEXT,
message_id INTEGER,
queued_at REAL NOT NULL,
processed INTEGER DEFAULT 0
);
CREATE INDEX IF NOT EXISTS idx_queue_processed ON embedding_queue(processed, queued_at);
"""
FTS_SQL = """
CREATE VIRTUAL TABLE IF NOT EXISTS facts_fts USING fts5(
content,
content_rowid='id',
tokenize='unicode61'
);
CREATE TRIGGER IF NOT EXISTS facts_fts_insert AFTER INSERT ON facts BEGIN
INSERT INTO facts_fts(rowid, content) VALUES (new.id, new.content);
END;
CREATE TRIGGER IF NOT EXISTS facts_fts_delete AFTER DELETE ON facts BEGIN
DELETE FROM facts_fts WHERE rowid = old.id;
END;
CREATE TRIGGER IF NOT EXISTS facts_fts_update AFTER UPDATE ON facts BEGIN
DELETE FROM facts_fts WHERE rowid = old.id;
INSERT INTO facts_fts(rowid, content) VALUES (new.id, new.content);
END;
CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
name,
content_rowid='id',
tokenize='unicode61'
);
CREATE TRIGGER IF NOT EXISTS entities_fts_insert AFTER INSERT ON entities BEGIN
INSERT INTO entities_fts(rowid, name) VALUES (new.id, new.name);
END;
CREATE TRIGGER IF NOT EXISTS entities_fts_delete AFTER DELETE ON entities BEGIN
DELETE FROM entities_fts WHERE rowid = old.id;
END;
CREATE TRIGGER IF NOT EXISTS entities_fts_update AFTER UPDATE ON entities BEGIN
DELETE FROM entities_fts WHERE rowid = old.id;
INSERT INTO entities_fts(rowid, name) VALUES (new.id, new.name);
END;
"""
def init_schema(conn: sqlite3.Connection) -> None:
"""Initialisiert alle Tabellen, Indizes und FTS5."""
conn.executescript(SCHEMA_SQL)
conn.executescript(FTS_SQL)
conn.execute(
"INSERT OR REPLACE INTO memory_schema_version (version, applied_at) VALUES (?, ?)",
(SCHEMA_VERSION, time.time()),
)
conn.commit()
def migrate(conn: sqlite3.Connection) -> None:
"""Führt Migrationen durch (aktuell: nur Schema-Version prüfen)."""
row = conn.execute("SELECT version FROM memory_schema_version ORDER BY version DESC LIMIT 1").fetchone()
current = row[0] if row else 0
if current < SCHEMA_VERSION:
init_schema(conn)
def connect(db_path: Path, wal_mode: bool = True) -> sqlite3.Connection:
"""Erstellt Verbindung mit WAL-Mode und Foreign Keys."""
db_path.parent.mkdir(parents=True, exist_ok=True)
conn = sqlite3.connect(str(db_path), check_same_thread=False, timeout=10.0)
conn.row_factory = sqlite3.Row
conn.execute("PRAGMA foreign_keys=ON")
if wal_mode:
try:
conn.execute("PRAGMA journal_mode=WAL")
except sqlite3.OperationalError:
conn.execute("PRAGMA journal_mode=DELETE")
return conn