Skip to main content

miku/
constant.rs

1// Environment Variables
2pub const ENV_DATABASE_URL: &str = "MIKU_DATABASE_URL";
3pub const ENV_OBSERVATION_LIMIT: &str = "MIKU_OBSERVATION_LIMIT";
4
5// Pragmas
6pub const PRAGMA_FOREIGN_KEYS_ON: &str = "PRAGMA foreign_keys = ON";
7
8// Table schema statements
9pub const SCHEMA_CREATE_TOPICS: &str = "CREATE TABLE IF NOT EXISTS topics (
10            id        TEXT PRIMARY KEY,
11            title     TEXT NOT NULL,
12            file_path TEXT NOT NULL,
13            body      TEXT NOT NULL DEFAULT '',
14            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
15            updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
16        )";
17
18pub const SCHEMA_CREATE_TOPICS_FTS: &str = "CREATE VIRTUAL TABLE IF NOT EXISTS topics_fts
19          USING fts5(title, body, content='topics', content_rowid='rowid', tokenize='porter unicode61')";
20
21pub const SCHEMA_CREATE_SESSIONS: &str = "CREATE TABLE IF NOT EXISTS sessions (
22            id        TEXT PRIMARY KEY,
23            summary   TEXT NOT NULL,
24            file_path TEXT NOT NULL,
25            created_at DATETIME DEFAULT CURRENT_TIMESTAMP
26        )";
27
28pub const SCHEMA_CREATE_MCP_ENTITIES: &str = "CREATE TABLE IF NOT EXISTS mcp_entities (
29            name        TEXT PRIMARY KEY,
30            entity_type TEXT NOT NULL,
31            created_at  DATETIME DEFAULT CURRENT_TIMESTAMP,
32            updated_at  DATETIME DEFAULT CURRENT_TIMESTAMP
33        )";
34
35pub const SCHEMA_CREATE_MCP_OBSERVATIONS: &str = "CREATE TABLE IF NOT EXISTS mcp_observations (
36            id          TEXT PRIMARY KEY,
37            entity_name TEXT NOT NULL,
38            content     TEXT NOT NULL,
39            created_at  DATETIME DEFAULT CURRENT_TIMESTAMP,
40            FOREIGN KEY (entity_name) REFERENCES mcp_entities(name) ON DELETE CASCADE
41        )";
42
43pub const SCHEMA_CREATE_IDX_MCP_OBSERVATIONS: &str =
44    "CREATE INDEX IF NOT EXISTS idx_mcp_observations_entity_name
45          ON mcp_observations(entity_name)";
46
47pub const SCHEMA_CREATE_MCP_RELATIONS: &str = "CREATE TABLE IF NOT EXISTS mcp_relations (
48            from_entity   TEXT NOT NULL,
49            to_entity     TEXT NOT NULL,
50            relation_type TEXT NOT NULL,
51            created_at    DATETIME DEFAULT CURRENT_TIMESTAMP,
52            PRIMARY KEY (from_entity, to_entity, relation_type),
53            FOREIGN KEY (from_entity) REFERENCES mcp_entities(name) ON DELETE CASCADE,
54            FOREIGN KEY (to_entity)   REFERENCES mcp_entities(name) ON DELETE CASCADE
55        )";
56
57pub const SCHEMA_CREATE_MCP_TRUTHS: &str = "CREATE TABLE IF NOT EXISTS mcp_truths (
58            entity_name TEXT NOT NULL,
59            key         TEXT NOT NULL,
60            value       TEXT NOT NULL,
61            updated_at  DATETIME DEFAULT CURRENT_TIMESTAMP,
62            PRIMARY KEY (entity_name, key),
63            FOREIGN KEY (entity_name) REFERENCES mcp_entities(name) ON DELETE CASCADE
64        )";
65
66pub const SCHEMA_CREATE_MCP_SKILLS: &str = "CREATE TABLE IF NOT EXISTS mcp_skills (
67            entity_name  TEXT PRIMARY KEY,
68            body         TEXT NOT NULL,
69            source       TEXT NOT NULL,
70            version      TEXT NOT NULL,
71            installed_at DATETIME DEFAULT CURRENT_TIMESTAMP,
72            FOREIGN KEY (entity_name) REFERENCES mcp_entities(name) ON DELETE CASCADE
73        )";
74
75pub const SCHEMA_CREATE_CHUNKS: &str = "CREATE TABLE IF NOT EXISTS chunks (
76            id        TEXT PRIMARY KEY,
77            topic_id  TEXT NOT NULL,
78            chunk_idx INTEGER NOT NULL,
79            text      TEXT NOT NULL,
80            source    TEXT NOT NULL,
81            embedding F32_BLOB(384) NOT NULL
82        )";
83
84pub const SCHEMA_CREATE_IDX_CHUNKS_TOPIC_ID: &str =
85    "CREATE INDEX IF NOT EXISTS idx_chunks_topic_id ON chunks(topic_id)";
86
87pub const SCHEMA_CREATE_IDX_CHUNKS_VECTOR: &str = "CREATE INDEX IF NOT EXISTS idx_chunks_vector ON chunks(libsql_vector_idx(embedding, 'metric=cosine'))";
88
89pub const SCHEMA_CREATE_TRIGGER_TOPICS_AI: &str =
90    "CREATE TRIGGER IF NOT EXISTS topics_ai AFTER INSERT ON topics BEGIN
91            INSERT INTO topics_fts(rowid, title, body) VALUES (new.rowid, new.title, new.body);
92        END";
93
94pub const SCHEMA_CREATE_TRIGGER_TOPICS_AD: &str = "CREATE TRIGGER IF NOT EXISTS topics_ad AFTER DELETE ON topics BEGIN
95            INSERT INTO topics_fts(topics_fts, rowid, title, body) VALUES('delete', old.rowid, old.title, old.body);
96        END";
97
98pub const SCHEMA_CREATE_TRIGGER_TOPICS_AU: &str = "CREATE TRIGGER IF NOT EXISTS topics_au AFTER UPDATE ON topics BEGIN
99            INSERT INTO topics_fts(topics_fts, rowid, title, body) VALUES('delete', old.rowid, old.title, old.body);
100            INSERT INTO topics_fts(rowid, title, body) VALUES (new.rowid, new.title, new.body);
101        END";
102
103pub const SCHEMA_CREATE_MCP_OBS_FTS: &str = "CREATE VIRTUAL TABLE IF NOT EXISTS mcp_obs_fts
104          USING fts5(content, content='mcp_observations', content_rowid='rowid', tokenize='porter unicode61')";
105
106pub const SCHEMA_CREATE_TRIGGER_MCP_OBS_AI: &str =
107    "CREATE TRIGGER IF NOT EXISTS mcp_obs_ai AFTER INSERT ON mcp_observations BEGIN
108            INSERT INTO mcp_obs_fts(rowid, content) VALUES (new.rowid, new.content);
109        END";
110
111pub const SCHEMA_CREATE_TRIGGER_MCP_OBS_AD: &str = "CREATE TRIGGER IF NOT EXISTS mcp_obs_ad AFTER DELETE ON mcp_observations BEGIN
112            INSERT INTO mcp_obs_fts(mcp_obs_fts, rowid, content) VALUES('delete', old.rowid, old.content);
113        END";
114
115pub const SCHEMA_CREATE_TRIGGER_MCP_OBS_AU: &str = "CREATE TRIGGER IF NOT EXISTS mcp_obs_au AFTER UPDATE ON mcp_observations BEGIN
116            INSERT INTO mcp_obs_fts(mcp_obs_fts, rowid, content) VALUES('delete', old.rowid, old.content);
117            INSERT INTO mcp_obs_fts(rowid, content) VALUES (new.rowid, new.content);
118        END";
119
120// Queries - Topics
121pub const SQL_SEARCH_FTS: &str = "SELECT t.id, t.title, t.file_path, bm25(topics_fts) AS score
122               FROM topics_fts
123               JOIN topics t ON topics_fts.rowid = t.rowid
124               WHERE topics_fts MATCH ?1
125               ORDER BY score
126               LIMIT ?2";
127
128pub const SQL_UPSERT_TOPIC: &str = "INSERT INTO topics (id, title, file_path, body) VALUES (?1, ?2, ?3, ?4) \
129     ON CONFLICT(id) DO UPDATE SET title=excluded.title, file_path=excluded.file_path, body=excluded.body, updated_at=CURRENT_TIMESTAMP";
130
131// Queries - MCP Entities / Observations / Relations
132pub const SQL_INSERT_ENTITY: &str =
133    "INSERT OR IGNORE INTO mcp_entities (name, entity_type) VALUES (?1, ?2)";
134pub const SQL_INSERT_OBSERVATION: &str =
135    "INSERT INTO mcp_observations (id, entity_name, content) VALUES (?1, ?2, ?3)";
136pub const SQL_INSERT_RELATION: &str = "INSERT OR REPLACE INTO mcp_relations (from_entity, to_entity, relation_type) VALUES (?1, ?2, ?3)";
137pub const SQL_DELETE_ENTITY: &str = "DELETE FROM mcp_entities WHERE name = ?1";
138pub const SQL_DELETE_OBSERVATION: &str =
139    "DELETE FROM mcp_observations WHERE entity_name = ?1 AND content = ?2";
140pub const SQL_DELETE_RELATION: &str =
141    "DELETE FROM mcp_relations WHERE from_entity = ?1 AND to_entity = ?2 AND relation_type = ?3";
142pub const SQL_EVICT_OBSERVATIONS: &str = "DELETE FROM mcp_observations WHERE entity_name = ?1 AND rowid NOT IN \
143     (SELECT rowid FROM mcp_observations WHERE entity_name = ?1 ORDER BY rowid DESC LIMIT ?2)";
144
145pub const SQL_UPSERT_TRUTH: &str = "INSERT INTO mcp_truths (entity_name, key, value) VALUES (?1, ?2, ?3) \
146     ON CONFLICT(entity_name, key) DO UPDATE SET value=excluded.value, updated_at=CURRENT_TIMESTAMP";
147pub const SQL_DELETE_TRUTH: &str = "DELETE FROM mcp_truths WHERE entity_name = ?1 AND key = ?2";
148
149pub const SQL_UPSERT_SKILL: &str = "INSERT INTO mcp_skills (entity_name, body, source, version) VALUES (?1, ?2, ?3, ?4) \
150     ON CONFLICT(entity_name) DO UPDATE SET body=excluded.body, source=excluded.source, version=excluded.version, installed_at=CURRENT_TIMESTAMP";
151pub const SQL_SELECT_SKILL_BODY: &str = "SELECT body FROM mcp_skills WHERE entity_name = ?1";
152pub const SQL_SELECT_SKILL_BODIES_IN_TEMPLATE: &str =
153    "SELECT entity_name, body FROM mcp_skills WHERE entity_name IN ({})";
154pub const SQL_LIST_SKILLS: &str = "SELECT s.entity_name, COALESCE(t.value, '') AS description, s.version, s.source, s.installed_at \
155     FROM mcp_skills s \
156     LEFT JOIN mcp_truths t ON t.entity_name = s.entity_name AND t.key = 'description' \
157     ORDER BY s.source, s.entity_name";
158
159pub const SQL_SELECT_ALL_ENTITIES: &str = "SELECT name, entity_type FROM mcp_entities";
160pub const SQL_SELECT_ALL_TOPIC_IDS: &str = "SELECT id FROM topics";
161pub const SQL_SELECT_OBSERVATIONS_FOR_ENTITY: &str =
162    "SELECT content FROM mcp_observations WHERE entity_name = ?1";
163pub const SQL_SELECT_ALL_RELATIONS: &str =
164    "SELECT from_entity, to_entity, relation_type FROM mcp_relations";
165
166// Graph Search
167pub const SQL_SEARCH_OBSERVATIONS_FTS: &str = "SELECT o.entity_name
168                   FROM mcp_obs_fts
169                   JOIN mcp_observations o ON mcp_obs_fts.rowid = o.rowid
170                   WHERE mcp_obs_fts MATCH ?1
171                   ORDER BY bm25(mcp_obs_fts)
172                   LIMIT ?2";
173
174pub const SQL_SEARCH_ENTITIES_LIKE: &str = "SELECT name FROM mcp_entities
175              WHERE name LIKE ?1 OR entity_type LIKE ?1
176              ORDER BY name
177              LIMIT ?2";
178
179pub const SQL_SELECT_RELATIONS_IN_TEMPLATE: &str = "SELECT from_entity, to_entity, relation_type FROM mcp_relations \
180             WHERE from_entity IN ({0}) OR to_entity IN ({0})";
181
182pub const SQL_SELECT_ENTITIES_IN_TEMPLATE: &str =
183    "SELECT name, entity_type FROM mcp_entities WHERE name IN ({})";
184
185pub const SQL_SELECT_OBSERVATIONS_IN_TEMPLATE: &str = "SELECT entity_name, content FROM mcp_observations \
186             WHERE entity_name IN ({}) \
187             ORDER BY created_at, id";
188
189pub const SQL_SELECT_TRUTHS_FOR_ENTITIES: &str = "SELECT entity_name, key, value FROM mcp_truths \
190             WHERE entity_name IN ({}) \
191             ORDER BY key";
192
193pub const SQL_COUNT_ENTITIES: &str = "SELECT COUNT(*) FROM mcp_entities";
194pub const SQL_COUNT_RELATIONS: &str = "SELECT COUNT(*) FROM mcp_relations";
195pub const SQL_COUNT_OBSERVATIONS: &str = "SELECT COUNT(*) FROM mcp_observations";
196
197pub const SQL_DELETE_ALL_RELATIONS: &str = "DELETE FROM mcp_relations";
198pub const SQL_DELETE_ALL_OBSERVATIONS: &str = "DELETE FROM mcp_observations";
199pub const SQL_DELETE_ALL_ENTITIES: &str = "DELETE FROM mcp_entities";
200
201// Chunks
202pub const SQL_INSERT_CHUNK: &str = "INSERT INTO chunks (id, topic_id, chunk_idx, text, source, embedding) \
203             VALUES (?1, ?2, ?3, ?4, ?5, vector32(?6))";
204
205// COALESCE guards against NULL distance: vector_distance_cos is undefined for a
206// zero-magnitude vector, and a NULL would panic the f64 column read. Treat it as
207// maximally distant (1.0 → similarity 0.0).
208pub const SQL_SEARCH_CHUNKS: &str = "SELECT c.id, c.topic_id, c.text, c.source, \
209             COALESCE(vector_distance_cos(c.embedding, vector32(?1)), 1.0) AS score \
210             FROM vector_top_k('idx_chunks_vector', vector32(?1), ?2) AS v \
211             JOIN chunks c ON c.rowid = v.id \
212             ORDER BY score";
213
214pub const SQL_DELETE_CHUNKS_BY_TOPIC: &str = "DELETE FROM chunks WHERE topic_id = ?1";
215
216// Backup / restore
217// VACUUM INTO cannot bind parameters, so the destination is embedded as an
218// escaped SQL string literal at the call site (single quotes doubled).
219pub const SQL_VACUUM_INTO_TEMPLATE: &str = "VACUUM INTO '{}'";
220pub const SQL_TABLE_EXISTS: &str =
221    "SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?1";
222pub const SQL_INTEGRITY_CHECK: &str = "PRAGMA integrity_check";