remdb 0.3.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. rem/__init__.py +2 -0
  2. rem/agentic/README.md +650 -0
  3. rem/agentic/__init__.py +39 -0
  4. rem/agentic/agents/README.md +155 -0
  5. rem/agentic/agents/__init__.py +8 -0
  6. rem/agentic/context.py +148 -0
  7. rem/agentic/context_builder.py +329 -0
  8. rem/agentic/mcp/__init__.py +0 -0
  9. rem/agentic/mcp/tool_wrapper.py +107 -0
  10. rem/agentic/otel/__init__.py +5 -0
  11. rem/agentic/otel/setup.py +151 -0
  12. rem/agentic/providers/phoenix.py +674 -0
  13. rem/agentic/providers/pydantic_ai.py +572 -0
  14. rem/agentic/query.py +117 -0
  15. rem/agentic/query_helper.py +89 -0
  16. rem/agentic/schema.py +396 -0
  17. rem/agentic/serialization.py +245 -0
  18. rem/agentic/tools/__init__.py +5 -0
  19. rem/agentic/tools/rem_tools.py +231 -0
  20. rem/api/README.md +420 -0
  21. rem/api/main.py +324 -0
  22. rem/api/mcp_router/prompts.py +182 -0
  23. rem/api/mcp_router/resources.py +536 -0
  24. rem/api/mcp_router/server.py +213 -0
  25. rem/api/mcp_router/tools.py +584 -0
  26. rem/api/routers/auth.py +229 -0
  27. rem/api/routers/chat/__init__.py +5 -0
  28. rem/api/routers/chat/completions.py +281 -0
  29. rem/api/routers/chat/json_utils.py +76 -0
  30. rem/api/routers/chat/models.py +124 -0
  31. rem/api/routers/chat/streaming.py +185 -0
  32. rem/auth/README.md +258 -0
  33. rem/auth/__init__.py +26 -0
  34. rem/auth/middleware.py +100 -0
  35. rem/auth/providers/__init__.py +13 -0
  36. rem/auth/providers/base.py +376 -0
  37. rem/auth/providers/google.py +163 -0
  38. rem/auth/providers/microsoft.py +237 -0
  39. rem/cli/README.md +455 -0
  40. rem/cli/__init__.py +8 -0
  41. rem/cli/commands/README.md +126 -0
  42. rem/cli/commands/__init__.py +3 -0
  43. rem/cli/commands/ask.py +566 -0
  44. rem/cli/commands/configure.py +497 -0
  45. rem/cli/commands/db.py +493 -0
  46. rem/cli/commands/dreaming.py +324 -0
  47. rem/cli/commands/experiments.py +1302 -0
  48. rem/cli/commands/mcp.py +66 -0
  49. rem/cli/commands/process.py +245 -0
  50. rem/cli/commands/schema.py +183 -0
  51. rem/cli/commands/serve.py +106 -0
  52. rem/cli/dreaming.py +363 -0
  53. rem/cli/main.py +96 -0
  54. rem/config.py +237 -0
  55. rem/mcp_server.py +41 -0
  56. rem/models/core/__init__.py +49 -0
  57. rem/models/core/core_model.py +64 -0
  58. rem/models/core/engram.py +333 -0
  59. rem/models/core/experiment.py +628 -0
  60. rem/models/core/inline_edge.py +132 -0
  61. rem/models/core/rem_query.py +243 -0
  62. rem/models/entities/__init__.py +43 -0
  63. rem/models/entities/file.py +57 -0
  64. rem/models/entities/image_resource.py +88 -0
  65. rem/models/entities/message.py +35 -0
  66. rem/models/entities/moment.py +123 -0
  67. rem/models/entities/ontology.py +191 -0
  68. rem/models/entities/ontology_config.py +131 -0
  69. rem/models/entities/resource.py +95 -0
  70. rem/models/entities/schema.py +87 -0
  71. rem/models/entities/user.py +85 -0
  72. rem/py.typed +0 -0
  73. rem/schemas/README.md +507 -0
  74. rem/schemas/__init__.py +6 -0
  75. rem/schemas/agents/README.md +92 -0
  76. rem/schemas/agents/core/moment-builder.yaml +178 -0
  77. rem/schemas/agents/core/rem-query-agent.yaml +226 -0
  78. rem/schemas/agents/core/resource-affinity-assessor.yaml +99 -0
  79. rem/schemas/agents/core/simple-assistant.yaml +19 -0
  80. rem/schemas/agents/core/user-profile-builder.yaml +163 -0
  81. rem/schemas/agents/examples/contract-analyzer.yaml +317 -0
  82. rem/schemas/agents/examples/contract-extractor.yaml +134 -0
  83. rem/schemas/agents/examples/cv-parser.yaml +263 -0
  84. rem/schemas/agents/examples/hello-world.yaml +37 -0
  85. rem/schemas/agents/examples/query.yaml +54 -0
  86. rem/schemas/agents/examples/simple.yaml +21 -0
  87. rem/schemas/agents/examples/test.yaml +29 -0
  88. rem/schemas/agents/rem.yaml +128 -0
  89. rem/schemas/evaluators/hello-world/default.yaml +77 -0
  90. rem/schemas/evaluators/rem/faithfulness.yaml +219 -0
  91. rem/schemas/evaluators/rem/lookup-correctness.yaml +182 -0
  92. rem/schemas/evaluators/rem/retrieval-precision.yaml +199 -0
  93. rem/schemas/evaluators/rem/retrieval-recall.yaml +211 -0
  94. rem/schemas/evaluators/rem/search-correctness.yaml +192 -0
  95. rem/services/__init__.py +16 -0
  96. rem/services/audio/INTEGRATION.md +308 -0
  97. rem/services/audio/README.md +376 -0
  98. rem/services/audio/__init__.py +15 -0
  99. rem/services/audio/chunker.py +354 -0
  100. rem/services/audio/transcriber.py +259 -0
  101. rem/services/content/README.md +1269 -0
  102. rem/services/content/__init__.py +5 -0
  103. rem/services/content/providers.py +801 -0
  104. rem/services/content/service.py +676 -0
  105. rem/services/dreaming/README.md +230 -0
  106. rem/services/dreaming/__init__.py +53 -0
  107. rem/services/dreaming/affinity_service.py +336 -0
  108. rem/services/dreaming/moment_service.py +264 -0
  109. rem/services/dreaming/ontology_service.py +54 -0
  110. rem/services/dreaming/user_model_service.py +297 -0
  111. rem/services/dreaming/utils.py +39 -0
  112. rem/services/embeddings/__init__.py +11 -0
  113. rem/services/embeddings/api.py +120 -0
  114. rem/services/embeddings/worker.py +421 -0
  115. rem/services/fs/README.md +662 -0
  116. rem/services/fs/__init__.py +62 -0
  117. rem/services/fs/examples.py +206 -0
  118. rem/services/fs/examples_paths.py +204 -0
  119. rem/services/fs/git_provider.py +935 -0
  120. rem/services/fs/local_provider.py +760 -0
  121. rem/services/fs/parsing-hooks-examples.md +172 -0
  122. rem/services/fs/paths.py +276 -0
  123. rem/services/fs/provider.py +460 -0
  124. rem/services/fs/s3_provider.py +1042 -0
  125. rem/services/fs/service.py +186 -0
  126. rem/services/git/README.md +1075 -0
  127. rem/services/git/__init__.py +17 -0
  128. rem/services/git/service.py +469 -0
  129. rem/services/phoenix/EXPERIMENT_DESIGN.md +1146 -0
  130. rem/services/phoenix/README.md +453 -0
  131. rem/services/phoenix/__init__.py +46 -0
  132. rem/services/phoenix/client.py +686 -0
  133. rem/services/phoenix/config.py +88 -0
  134. rem/services/phoenix/prompt_labels.py +477 -0
  135. rem/services/postgres/README.md +575 -0
  136. rem/services/postgres/__init__.py +23 -0
  137. rem/services/postgres/migration_service.py +427 -0
  138. rem/services/postgres/pydantic_to_sqlalchemy.py +232 -0
  139. rem/services/postgres/register_type.py +352 -0
  140. rem/services/postgres/repository.py +337 -0
  141. rem/services/postgres/schema_generator.py +379 -0
  142. rem/services/postgres/service.py +802 -0
  143. rem/services/postgres/sql_builder.py +354 -0
  144. rem/services/rem/README.md +304 -0
  145. rem/services/rem/__init__.py +23 -0
  146. rem/services/rem/exceptions.py +71 -0
  147. rem/services/rem/executor.py +293 -0
  148. rem/services/rem/parser.py +145 -0
  149. rem/services/rem/queries.py +196 -0
  150. rem/services/rem/query.py +371 -0
  151. rem/services/rem/service.py +527 -0
  152. rem/services/session/README.md +374 -0
  153. rem/services/session/__init__.py +6 -0
  154. rem/services/session/compression.py +360 -0
  155. rem/services/session/reload.py +77 -0
  156. rem/settings.py +1235 -0
  157. rem/sql/002_install_models.sql +1068 -0
  158. rem/sql/background_indexes.sql +42 -0
  159. rem/sql/install_models.sql +1038 -0
  160. rem/sql/migrations/001_install.sql +503 -0
  161. rem/sql/migrations/002_install_models.sql +1202 -0
  162. rem/utils/AGENTIC_CHUNKING.md +597 -0
  163. rem/utils/README.md +583 -0
  164. rem/utils/__init__.py +43 -0
  165. rem/utils/agentic_chunking.py +622 -0
  166. rem/utils/batch_ops.py +343 -0
  167. rem/utils/chunking.py +108 -0
  168. rem/utils/clip_embeddings.py +276 -0
  169. rem/utils/dict_utils.py +98 -0
  170. rem/utils/embeddings.py +423 -0
  171. rem/utils/examples/embeddings_example.py +305 -0
  172. rem/utils/examples/sql_types_example.py +202 -0
  173. rem/utils/markdown.py +16 -0
  174. rem/utils/model_helpers.py +236 -0
  175. rem/utils/schema_loader.py +336 -0
  176. rem/utils/sql_types.py +348 -0
  177. rem/utils/user_id.py +81 -0
  178. rem/utils/vision.py +330 -0
  179. rem/workers/README.md +506 -0
  180. rem/workers/__init__.py +5 -0
  181. rem/workers/dreaming.py +502 -0
  182. rem/workers/engram_processor.py +312 -0
  183. rem/workers/sqs_file_processor.py +193 -0
  184. remdb-0.3.7.dist-info/METADATA +1473 -0
  185. remdb-0.3.7.dist-info/RECORD +187 -0
  186. remdb-0.3.7.dist-info/WHEEL +4 -0
  187. remdb-0.3.7.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,1038 @@
1
+ -- REM Model Schema (install_models.sql)
2
+ -- Generated from Pydantic models
3
+ -- Source directory: src/rem/models/entities
4
+ -- Generated at: 2025-11-22T20:03:37.573943
5
+ --
6
+ -- DO NOT EDIT MANUALLY - Regenerate with: rem db schema generate
7
+ --
8
+ -- This script creates:
9
+ -- 1. Primary entity tables
10
+ -- 2. Embeddings tables (embeddings_<table>)
11
+ -- 3. KV_STORE triggers for cache maintenance
12
+ -- 4. Indexes (foreground only, background indexes separate)
13
+
14
+ -- ============================================================================
15
+ -- PREREQUISITES CHECK
16
+ -- ============================================================================
17
+
18
+ DO $$
19
+ BEGIN
20
+ -- Check that install.sql has been run
21
+ IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'kv_store') THEN
22
+ RAISE EXCEPTION 'KV_STORE table not found. Run migrations/001_install.sql first.';
23
+ END IF;
24
+
25
+ IF NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector') THEN
26
+ RAISE EXCEPTION 'pgvector extension not found. Run migrations/001_install.sql first.';
27
+ END IF;
28
+
29
+ RAISE NOTICE 'Prerequisites check passed';
30
+ END $$;
31
+
32
+ -- ======================================================================
33
+ -- USERS (Model: User)
34
+ -- ======================================================================
35
+
36
+ CREATE TABLE IF NOT EXISTS users (
37
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
38
+ tenant_id VARCHAR(100) NOT NULL,
39
+ user_id VARCHAR(256),
40
+ name VARCHAR(256) NOT NULL,
41
+ email VARCHAR(256),
42
+ role VARCHAR(256),
43
+ tier TEXT,
44
+ sec_policy JSONB DEFAULT '{}'::jsonb,
45
+ summary TEXT,
46
+ interests TEXT[] DEFAULT ARRAY[]::TEXT[],
47
+ preferred_topics TEXT[] DEFAULT ARRAY[]::TEXT[],
48
+ activity_level VARCHAR(256),
49
+ last_active_at TIMESTAMP,
50
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
51
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
52
+ deleted_at TIMESTAMP,
53
+ graph_edges JSONB DEFAULT '[]'::jsonb,
54
+ metadata JSONB DEFAULT '{}'::jsonb,
55
+ tags TEXT[] DEFAULT ARRAY[]::TEXT[]
56
+ );
57
+
58
+ CREATE INDEX idx_users_tenant ON users (tenant_id);
59
+ CREATE INDEX idx_users_user ON users (user_id);
60
+ CREATE INDEX idx_users_graph_edges ON users USING GIN (graph_edges);
61
+ CREATE INDEX idx_users_metadata ON users USING GIN (metadata);
62
+ CREATE INDEX idx_users_tags ON users USING GIN (tags);
63
+
64
+ -- Embeddings for users
65
+ CREATE TABLE IF NOT EXISTS embeddings_users (
66
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
67
+ entity_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
68
+ field_name VARCHAR(100) NOT NULL,
69
+ provider VARCHAR(50) NOT NULL DEFAULT 'openai',
70
+ model VARCHAR(100) NOT NULL DEFAULT 'text-embedding-3-small',
71
+ embedding vector(1536) NOT NULL,
72
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
73
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
74
+
75
+ -- Unique: one embedding per entity per field per provider
76
+ UNIQUE (entity_id, field_name, provider)
77
+ );
78
+
79
+ -- Index for entity lookup (get all embeddings for entity)
80
+ CREATE INDEX idx_embeddings_users_entity ON embeddings_users (entity_id);
81
+
82
+ -- Index for field + provider lookup
83
+ CREATE INDEX idx_embeddings_users_field_provider ON embeddings_users (field_name, provider);
84
+
85
+ -- HNSW index for vector similarity search (created in background)
86
+ -- Note: This will be created by background thread after data load
87
+ -- CREATE INDEX idx_embeddings_users_vector_hnsw ON embeddings_users
88
+ -- USING hnsw (embedding vector_cosine_ops);
89
+
90
+ -- KV_STORE trigger for users
91
+ -- Trigger function to maintain KV_STORE for users
92
+ CREATE OR REPLACE FUNCTION fn_users_kv_store_upsert()
93
+ RETURNS TRIGGER AS $$
94
+ BEGIN
95
+ IF (TG_OP = 'DELETE') THEN
96
+ -- Remove from KV_STORE on delete
97
+ DELETE FROM kv_store
98
+ WHERE entity_id = OLD.id;
99
+ RETURN OLD;
100
+ ELSIF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
101
+ -- Upsert to KV_STORE (O(1) lookup by entity_key)
102
+ INSERT INTO kv_store (
103
+ entity_key,
104
+ entity_type,
105
+ entity_id,
106
+ tenant_id,
107
+ user_id,
108
+ metadata,
109
+ updated_at
110
+ ) VALUES (
111
+ NEW.name,
112
+ 'users',
113
+ NEW.id,
114
+ NEW.tenant_id,
115
+ NEW.user_id,
116
+ NEW.metadata,
117
+ CURRENT_TIMESTAMP
118
+ )
119
+ ON CONFLICT (tenant_id, entity_key)
120
+ DO UPDATE SET
121
+ entity_id = EXCLUDED.entity_id,
122
+ user_id = EXCLUDED.user_id,
123
+ metadata = EXCLUDED.metadata,
124
+ updated_at = CURRENT_TIMESTAMP;
125
+
126
+ RETURN NEW;
127
+ END IF;
128
+ END;
129
+ $$ LANGUAGE plpgsql;
130
+
131
+ -- Create trigger
132
+ DROP TRIGGER IF EXISTS trg_users_kv_store ON users;
133
+ CREATE TRIGGER trg_users_kv_store
134
+ AFTER INSERT OR UPDATE OR DELETE ON users
135
+ FOR EACH ROW EXECUTE FUNCTION fn_users_kv_store_upsert();
136
+
137
+ -- ======================================================================
138
+ -- IMAGE_RESOURCES (Model: ImageResource)
139
+ -- ======================================================================
140
+
141
+ CREATE TABLE IF NOT EXISTS image_resources (
142
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
143
+ tenant_id VARCHAR(100) NOT NULL,
144
+ user_id VARCHAR(256),
145
+ name VARCHAR(256),
146
+ uri VARCHAR(256),
147
+ ordinal INTEGER,
148
+ content TEXT,
149
+ timestamp TIMESTAMP,
150
+ category VARCHAR(256),
151
+ related_entities JSONB DEFAULT '{}'::jsonb,
152
+ image_width INTEGER,
153
+ image_height INTEGER,
154
+ image_format VARCHAR(256),
155
+ vision_description TEXT,
156
+ vision_provider VARCHAR(256),
157
+ vision_model VARCHAR(256),
158
+ clip_embedding JSONB,
159
+ clip_dimensions INTEGER,
160
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
161
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
162
+ deleted_at TIMESTAMP,
163
+ graph_edges JSONB DEFAULT '[]'::jsonb,
164
+ metadata JSONB DEFAULT '{}'::jsonb,
165
+ tags TEXT[] DEFAULT ARRAY[]::TEXT[]
166
+ );
167
+
168
+ CREATE INDEX idx_image_resources_tenant ON image_resources (tenant_id);
169
+ CREATE INDEX idx_image_resources_user ON image_resources (user_id);
170
+ CREATE INDEX idx_image_resources_graph_edges ON image_resources USING GIN (graph_edges);
171
+ CREATE INDEX idx_image_resources_metadata ON image_resources USING GIN (metadata);
172
+ CREATE INDEX idx_image_resources_tags ON image_resources USING GIN (tags);
173
+
174
+ -- Embeddings for image_resources
175
+ CREATE TABLE IF NOT EXISTS embeddings_image_resources (
176
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
177
+ entity_id UUID NOT NULL REFERENCES image_resources(id) ON DELETE CASCADE,
178
+ field_name VARCHAR(100) NOT NULL,
179
+ provider VARCHAR(50) NOT NULL DEFAULT 'openai',
180
+ model VARCHAR(100) NOT NULL DEFAULT 'text-embedding-3-small',
181
+ embedding vector(1536) NOT NULL,
182
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
183
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
184
+
185
+ -- Unique: one embedding per entity per field per provider
186
+ UNIQUE (entity_id, field_name, provider)
187
+ );
188
+
189
+ -- Index for entity lookup (get all embeddings for entity)
190
+ CREATE INDEX idx_embeddings_image_resources_entity ON embeddings_image_resources (entity_id);
191
+
192
+ -- Index for field + provider lookup
193
+ CREATE INDEX idx_embeddings_image_resources_field_provider ON embeddings_image_resources (field_name, provider);
194
+
195
+ -- HNSW index for vector similarity search (created in background)
196
+ -- Note: This will be created by background thread after data load
197
+ -- CREATE INDEX idx_embeddings_image_resources_vector_hnsw ON embeddings_image_resources
198
+ -- USING hnsw (embedding vector_cosine_ops);
199
+
200
+ -- KV_STORE trigger for image_resources
201
+ -- Trigger function to maintain KV_STORE for image_resources
202
+ CREATE OR REPLACE FUNCTION fn_image_resources_kv_store_upsert()
203
+ RETURNS TRIGGER AS $$
204
+ BEGIN
205
+ IF (TG_OP = 'DELETE') THEN
206
+ -- Remove from KV_STORE on delete
207
+ DELETE FROM kv_store
208
+ WHERE entity_id = OLD.id;
209
+ RETURN OLD;
210
+ ELSIF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
211
+ -- Upsert to KV_STORE (O(1) lookup by entity_key)
212
+ INSERT INTO kv_store (
213
+ entity_key,
214
+ entity_type,
215
+ entity_id,
216
+ tenant_id,
217
+ user_id,
218
+ metadata,
219
+ updated_at
220
+ ) VALUES (
221
+ NEW.name,
222
+ 'image_resources',
223
+ NEW.id,
224
+ NEW.tenant_id,
225
+ NEW.user_id,
226
+ NEW.metadata,
227
+ CURRENT_TIMESTAMP
228
+ )
229
+ ON CONFLICT (tenant_id, entity_key)
230
+ DO UPDATE SET
231
+ entity_id = EXCLUDED.entity_id,
232
+ user_id = EXCLUDED.user_id,
233
+ metadata = EXCLUDED.metadata,
234
+ updated_at = CURRENT_TIMESTAMP;
235
+
236
+ RETURN NEW;
237
+ END IF;
238
+ END;
239
+ $$ LANGUAGE plpgsql;
240
+
241
+ -- Create trigger
242
+ DROP TRIGGER IF EXISTS trg_image_resources_kv_store ON image_resources;
243
+ CREATE TRIGGER trg_image_resources_kv_store
244
+ AFTER INSERT OR UPDATE OR DELETE ON image_resources
245
+ FOR EACH ROW EXECUTE FUNCTION fn_image_resources_kv_store_upsert();
246
+
247
+ -- ======================================================================
248
+ -- MOMENTS (Model: Moment)
249
+ -- ======================================================================
250
+
251
+ CREATE TABLE IF NOT EXISTS moments (
252
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
253
+ tenant_id VARCHAR(100) NOT NULL,
254
+ user_id VARCHAR(256),
255
+ name VARCHAR(256),
256
+ moment_type VARCHAR(256),
257
+ category VARCHAR(256),
258
+ starts_timestamp TIMESTAMP NOT NULL,
259
+ ends_timestamp TIMESTAMP,
260
+ present_persons JSONB DEFAULT '{}'::jsonb,
261
+ emotion_tags TEXT[] DEFAULT ARRAY[]::TEXT[],
262
+ topic_tags TEXT[] DEFAULT ARRAY[]::TEXT[],
263
+ summary TEXT,
264
+ source_resource_ids TEXT[] DEFAULT ARRAY[]::TEXT[],
265
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
266
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
267
+ deleted_at TIMESTAMP,
268
+ graph_edges JSONB DEFAULT '[]'::jsonb,
269
+ metadata JSONB DEFAULT '{}'::jsonb,
270
+ tags TEXT[] DEFAULT ARRAY[]::TEXT[]
271
+ );
272
+
273
+ CREATE INDEX idx_moments_tenant ON moments (tenant_id);
274
+ CREATE INDEX idx_moments_user ON moments (user_id);
275
+ CREATE INDEX idx_moments_graph_edges ON moments USING GIN (graph_edges);
276
+ CREATE INDEX idx_moments_metadata ON moments USING GIN (metadata);
277
+ CREATE INDEX idx_moments_tags ON moments USING GIN (tags);
278
+
279
+ -- Embeddings for moments
280
+ CREATE TABLE IF NOT EXISTS embeddings_moments (
281
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
282
+ entity_id UUID NOT NULL REFERENCES moments(id) ON DELETE CASCADE,
283
+ field_name VARCHAR(100) NOT NULL,
284
+ provider VARCHAR(50) NOT NULL DEFAULT 'openai',
285
+ model VARCHAR(100) NOT NULL DEFAULT 'text-embedding-3-small',
286
+ embedding vector(1536) NOT NULL,
287
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
288
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
289
+
290
+ -- Unique: one embedding per entity per field per provider
291
+ UNIQUE (entity_id, field_name, provider)
292
+ );
293
+
294
+ -- Index for entity lookup (get all embeddings for entity)
295
+ CREATE INDEX idx_embeddings_moments_entity ON embeddings_moments (entity_id);
296
+
297
+ -- Index for field + provider lookup
298
+ CREATE INDEX idx_embeddings_moments_field_provider ON embeddings_moments (field_name, provider);
299
+
300
+ -- HNSW index for vector similarity search (created in background)
301
+ -- Note: This will be created by background thread after data load
302
+ -- CREATE INDEX idx_embeddings_moments_vector_hnsw ON embeddings_moments
303
+ -- USING hnsw (embedding vector_cosine_ops);
304
+
305
+ -- KV_STORE trigger for moments
306
+ -- Trigger function to maintain KV_STORE for moments
307
+ CREATE OR REPLACE FUNCTION fn_moments_kv_store_upsert()
308
+ RETURNS TRIGGER AS $$
309
+ BEGIN
310
+ IF (TG_OP = 'DELETE') THEN
311
+ -- Remove from KV_STORE on delete
312
+ DELETE FROM kv_store
313
+ WHERE entity_id = OLD.id;
314
+ RETURN OLD;
315
+ ELSIF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
316
+ -- Upsert to KV_STORE (O(1) lookup by entity_key)
317
+ INSERT INTO kv_store (
318
+ entity_key,
319
+ entity_type,
320
+ entity_id,
321
+ tenant_id,
322
+ user_id,
323
+ metadata,
324
+ updated_at
325
+ ) VALUES (
326
+ NEW.name,
327
+ 'moments',
328
+ NEW.id,
329
+ NEW.tenant_id,
330
+ NEW.user_id,
331
+ NEW.metadata,
332
+ CURRENT_TIMESTAMP
333
+ )
334
+ ON CONFLICT (tenant_id, entity_key)
335
+ DO UPDATE SET
336
+ entity_id = EXCLUDED.entity_id,
337
+ user_id = EXCLUDED.user_id,
338
+ metadata = EXCLUDED.metadata,
339
+ updated_at = CURRENT_TIMESTAMP;
340
+
341
+ RETURN NEW;
342
+ END IF;
343
+ END;
344
+ $$ LANGUAGE plpgsql;
345
+
346
+ -- Create trigger
347
+ DROP TRIGGER IF EXISTS trg_moments_kv_store ON moments;
348
+ CREATE TRIGGER trg_moments_kv_store
349
+ AFTER INSERT OR UPDATE OR DELETE ON moments
350
+ FOR EACH ROW EXECUTE FUNCTION fn_moments_kv_store_upsert();
351
+
352
+ -- ======================================================================
353
+ -- PERSONS (Model: Person)
354
+ -- ======================================================================
355
+
356
+ CREATE TABLE IF NOT EXISTS persons (
357
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
358
+ tenant_id VARCHAR(100) NOT NULL,
359
+ user_id VARCHAR(256),
360
+ name VARCHAR(256) NOT NULL,
361
+ role VARCHAR(256),
362
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
363
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
364
+ deleted_at TIMESTAMP,
365
+ graph_edges JSONB DEFAULT '[]'::jsonb,
366
+ metadata JSONB DEFAULT '{}'::jsonb,
367
+ tags TEXT[] DEFAULT ARRAY[]::TEXT[]
368
+ );
369
+
370
+ CREATE INDEX idx_persons_tenant ON persons (tenant_id);
371
+ CREATE INDEX idx_persons_user ON persons (user_id);
372
+ CREATE INDEX idx_persons_graph_edges ON persons USING GIN (graph_edges);
373
+ CREATE INDEX idx_persons_metadata ON persons USING GIN (metadata);
374
+ CREATE INDEX idx_persons_tags ON persons USING GIN (tags);
375
+
376
+ -- KV_STORE trigger for persons
377
+ -- Trigger function to maintain KV_STORE for persons
378
+ CREATE OR REPLACE FUNCTION fn_persons_kv_store_upsert()
379
+ RETURNS TRIGGER AS $$
380
+ BEGIN
381
+ IF (TG_OP = 'DELETE') THEN
382
+ -- Remove from KV_STORE on delete
383
+ DELETE FROM kv_store
384
+ WHERE entity_id = OLD.id;
385
+ RETURN OLD;
386
+ ELSIF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
387
+ -- Upsert to KV_STORE (O(1) lookup by entity_key)
388
+ INSERT INTO kv_store (
389
+ entity_key,
390
+ entity_type,
391
+ entity_id,
392
+ tenant_id,
393
+ user_id,
394
+ metadata,
395
+ updated_at
396
+ ) VALUES (
397
+ NEW.id,
398
+ 'persons',
399
+ NEW.id,
400
+ NEW.tenant_id,
401
+ NEW.user_id,
402
+ NEW.metadata,
403
+ CURRENT_TIMESTAMP
404
+ )
405
+ ON CONFLICT (tenant_id, entity_key)
406
+ DO UPDATE SET
407
+ entity_id = EXCLUDED.entity_id,
408
+ user_id = EXCLUDED.user_id,
409
+ metadata = EXCLUDED.metadata,
410
+ updated_at = CURRENT_TIMESTAMP;
411
+
412
+ RETURN NEW;
413
+ END IF;
414
+ END;
415
+ $$ LANGUAGE plpgsql;
416
+
417
+ -- Create trigger
418
+ DROP TRIGGER IF EXISTS trg_persons_kv_store ON persons;
419
+ CREATE TRIGGER trg_persons_kv_store
420
+ AFTER INSERT OR UPDATE OR DELETE ON persons
421
+ FOR EACH ROW EXECUTE FUNCTION fn_persons_kv_store_upsert();
422
+
423
+ -- ======================================================================
424
+ -- RESOURCES (Model: Resource)
425
+ -- ======================================================================
426
+
427
+ CREATE TABLE IF NOT EXISTS resources (
428
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
429
+ tenant_id VARCHAR(100) NOT NULL,
430
+ user_id VARCHAR(256),
431
+ name VARCHAR(256),
432
+ uri VARCHAR(256),
433
+ ordinal INTEGER,
434
+ content TEXT,
435
+ timestamp TIMESTAMP,
436
+ category VARCHAR(256),
437
+ related_entities JSONB DEFAULT '{}'::jsonb,
438
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
439
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
440
+ deleted_at TIMESTAMP,
441
+ graph_edges JSONB DEFAULT '[]'::jsonb,
442
+ metadata JSONB DEFAULT '{}'::jsonb,
443
+ tags TEXT[] DEFAULT ARRAY[]::TEXT[]
444
+ );
445
+
446
+ CREATE INDEX idx_resources_tenant ON resources (tenant_id);
447
+ CREATE INDEX idx_resources_user ON resources (user_id);
448
+ CREATE INDEX idx_resources_graph_edges ON resources USING GIN (graph_edges);
449
+ CREATE INDEX idx_resources_metadata ON resources USING GIN (metadata);
450
+ CREATE INDEX idx_resources_tags ON resources USING GIN (tags);
451
+
452
+ -- Embeddings for resources
453
+ CREATE TABLE IF NOT EXISTS embeddings_resources (
454
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
455
+ entity_id UUID NOT NULL REFERENCES resources(id) ON DELETE CASCADE,
456
+ field_name VARCHAR(100) NOT NULL,
457
+ provider VARCHAR(50) NOT NULL DEFAULT 'openai',
458
+ model VARCHAR(100) NOT NULL DEFAULT 'text-embedding-3-small',
459
+ embedding vector(1536) NOT NULL,
460
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
461
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
462
+
463
+ -- Unique: one embedding per entity per field per provider
464
+ UNIQUE (entity_id, field_name, provider)
465
+ );
466
+
467
+ -- Index for entity lookup (get all embeddings for entity)
468
+ CREATE INDEX idx_embeddings_resources_entity ON embeddings_resources (entity_id);
469
+
470
+ -- Index for field + provider lookup
471
+ CREATE INDEX idx_embeddings_resources_field_provider ON embeddings_resources (field_name, provider);
472
+
473
+ -- HNSW index for vector similarity search (created in background)
474
+ -- Note: This will be created by background thread after data load
475
+ -- CREATE INDEX idx_embeddings_resources_vector_hnsw ON embeddings_resources
476
+ -- USING hnsw (embedding vector_cosine_ops);
477
+
478
+ -- KV_STORE trigger for resources
479
+ -- Trigger function to maintain KV_STORE for resources
480
+ CREATE OR REPLACE FUNCTION fn_resources_kv_store_upsert()
481
+ RETURNS TRIGGER AS $$
482
+ BEGIN
483
+ IF (TG_OP = 'DELETE') THEN
484
+ -- Remove from KV_STORE on delete
485
+ DELETE FROM kv_store
486
+ WHERE entity_id = OLD.id;
487
+ RETURN OLD;
488
+ ELSIF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
489
+ -- Upsert to KV_STORE (O(1) lookup by entity_key)
490
+ INSERT INTO kv_store (
491
+ entity_key,
492
+ entity_type,
493
+ entity_id,
494
+ tenant_id,
495
+ user_id,
496
+ metadata,
497
+ updated_at
498
+ ) VALUES (
499
+ NEW.name,
500
+ 'resources',
501
+ NEW.id,
502
+ NEW.tenant_id,
503
+ NEW.user_id,
504
+ NEW.metadata,
505
+ CURRENT_TIMESTAMP
506
+ )
507
+ ON CONFLICT (tenant_id, entity_key)
508
+ DO UPDATE SET
509
+ entity_id = EXCLUDED.entity_id,
510
+ user_id = EXCLUDED.user_id,
511
+ metadata = EXCLUDED.metadata,
512
+ updated_at = CURRENT_TIMESTAMP;
513
+
514
+ RETURN NEW;
515
+ END IF;
516
+ END;
517
+ $$ LANGUAGE plpgsql;
518
+
519
+ -- Create trigger
520
+ DROP TRIGGER IF EXISTS trg_resources_kv_store ON resources;
521
+ CREATE TRIGGER trg_resources_kv_store
522
+ AFTER INSERT OR UPDATE OR DELETE ON resources
523
+ FOR EACH ROW EXECUTE FUNCTION fn_resources_kv_store_upsert();
524
+
525
+ -- ======================================================================
526
+ -- MESSAGES (Model: Message)
527
+ -- ======================================================================
528
+
529
+ CREATE TABLE IF NOT EXISTS messages (
530
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
531
+ tenant_id VARCHAR(100) NOT NULL,
532
+ user_id VARCHAR(256),
533
+ content TEXT NOT NULL,
534
+ message_type TEXT,
535
+ session_id TEXT,
536
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
537
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
538
+ deleted_at TIMESTAMP,
539
+ graph_edges JSONB DEFAULT '[]'::jsonb,
540
+ metadata JSONB DEFAULT '{}'::jsonb,
541
+ tags TEXT[] DEFAULT ARRAY[]::TEXT[]
542
+ );
543
+
544
+ CREATE INDEX idx_messages_tenant ON messages (tenant_id);
545
+ CREATE INDEX idx_messages_user ON messages (user_id);
546
+ CREATE INDEX idx_messages_graph_edges ON messages USING GIN (graph_edges);
547
+ CREATE INDEX idx_messages_metadata ON messages USING GIN (metadata);
548
+ CREATE INDEX idx_messages_tags ON messages USING GIN (tags);
549
+
550
+ -- Embeddings for messages
551
+ CREATE TABLE IF NOT EXISTS embeddings_messages (
552
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
553
+ entity_id UUID NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
554
+ field_name VARCHAR(100) NOT NULL,
555
+ provider VARCHAR(50) NOT NULL DEFAULT 'openai',
556
+ model VARCHAR(100) NOT NULL DEFAULT 'text-embedding-3-small',
557
+ embedding vector(1536) NOT NULL,
558
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
559
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
560
+
561
+ -- Unique: one embedding per entity per field per provider
562
+ UNIQUE (entity_id, field_name, provider)
563
+ );
564
+
565
+ -- Index for entity lookup (get all embeddings for entity)
566
+ CREATE INDEX idx_embeddings_messages_entity ON embeddings_messages (entity_id);
567
+
568
+ -- Index for field + provider lookup
569
+ CREATE INDEX idx_embeddings_messages_field_provider ON embeddings_messages (field_name, provider);
570
+
571
+ -- HNSW index for vector similarity search (created in background)
572
+ -- Note: This will be created by background thread after data load
573
+ -- CREATE INDEX idx_embeddings_messages_vector_hnsw ON embeddings_messages
574
+ -- USING hnsw (embedding vector_cosine_ops);
575
+
576
+ -- KV_STORE trigger for messages
577
+ -- Trigger function to maintain KV_STORE for messages
578
+ CREATE OR REPLACE FUNCTION fn_messages_kv_store_upsert()
579
+ RETURNS TRIGGER AS $$
580
+ BEGIN
581
+ IF (TG_OP = 'DELETE') THEN
582
+ -- Remove from KV_STORE on delete
583
+ DELETE FROM kv_store
584
+ WHERE entity_id = OLD.id;
585
+ RETURN OLD;
586
+ ELSIF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
587
+ -- Upsert to KV_STORE (O(1) lookup by entity_key)
588
+ INSERT INTO kv_store (
589
+ entity_key,
590
+ entity_type,
591
+ entity_id,
592
+ tenant_id,
593
+ user_id,
594
+ metadata,
595
+ updated_at
596
+ ) VALUES (
597
+ NEW.id,
598
+ 'messages',
599
+ NEW.id,
600
+ NEW.tenant_id,
601
+ NEW.user_id,
602
+ NEW.metadata,
603
+ CURRENT_TIMESTAMP
604
+ )
605
+ ON CONFLICT (tenant_id, entity_key)
606
+ DO UPDATE SET
607
+ entity_id = EXCLUDED.entity_id,
608
+ user_id = EXCLUDED.user_id,
609
+ metadata = EXCLUDED.metadata,
610
+ updated_at = CURRENT_TIMESTAMP;
611
+
612
+ RETURN NEW;
613
+ END IF;
614
+ END;
615
+ $$ LANGUAGE plpgsql;
616
+
617
+ -- Create trigger
618
+ DROP TRIGGER IF EXISTS trg_messages_kv_store ON messages;
619
+ CREATE TRIGGER trg_messages_kv_store
620
+ AFTER INSERT OR UPDATE OR DELETE ON messages
621
+ FOR EACH ROW EXECUTE FUNCTION fn_messages_kv_store_upsert();
622
+
623
+ -- ======================================================================
624
+ -- FILES (Model: File)
625
+ -- ======================================================================
626
+
627
+ CREATE TABLE IF NOT EXISTS files (
628
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
629
+ tenant_id VARCHAR(100) NOT NULL,
630
+ user_id VARCHAR(256),
631
+ name VARCHAR(256) NOT NULL,
632
+ uri VARCHAR(256) NOT NULL,
633
+ content TEXT,
634
+ timestamp VARCHAR(256),
635
+ size_bytes INTEGER,
636
+ mime_type VARCHAR(256),
637
+ processing_status VARCHAR(256),
638
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
639
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
640
+ deleted_at TIMESTAMP,
641
+ graph_edges JSONB DEFAULT '[]'::jsonb,
642
+ metadata JSONB DEFAULT '{}'::jsonb,
643
+ tags TEXT[] DEFAULT ARRAY[]::TEXT[]
644
+ );
645
+
646
+ CREATE INDEX idx_files_tenant ON files (tenant_id);
647
+ CREATE INDEX idx_files_user ON files (user_id);
648
+ CREATE INDEX idx_files_graph_edges ON files USING GIN (graph_edges);
649
+ CREATE INDEX idx_files_metadata ON files USING GIN (metadata);
650
+ CREATE INDEX idx_files_tags ON files USING GIN (tags);
651
+
652
+ -- Embeddings for files
653
+ CREATE TABLE IF NOT EXISTS embeddings_files (
654
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
655
+ entity_id UUID NOT NULL REFERENCES files(id) ON DELETE CASCADE,
656
+ field_name VARCHAR(100) NOT NULL,
657
+ provider VARCHAR(50) NOT NULL DEFAULT 'openai',
658
+ model VARCHAR(100) NOT NULL DEFAULT 'text-embedding-3-small',
659
+ embedding vector(1536) NOT NULL,
660
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
661
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
662
+
663
+ -- Unique: one embedding per entity per field per provider
664
+ UNIQUE (entity_id, field_name, provider)
665
+ );
666
+
667
+ -- Index for entity lookup (get all embeddings for entity)
668
+ CREATE INDEX idx_embeddings_files_entity ON embeddings_files (entity_id);
669
+
670
+ -- Index for field + provider lookup
671
+ CREATE INDEX idx_embeddings_files_field_provider ON embeddings_files (field_name, provider);
672
+
673
+ -- HNSW index for vector similarity search (created in background)
674
+ -- Note: This will be created by background thread after data load
675
+ -- CREATE INDEX idx_embeddings_files_vector_hnsw ON embeddings_files
676
+ -- USING hnsw (embedding vector_cosine_ops);
677
+
678
+ -- KV_STORE trigger for files
679
+ -- Trigger function to maintain KV_STORE for files
680
+ CREATE OR REPLACE FUNCTION fn_files_kv_store_upsert()
681
+ RETURNS TRIGGER AS $$
682
+ BEGIN
683
+ IF (TG_OP = 'DELETE') THEN
684
+ -- Remove from KV_STORE on delete
685
+ DELETE FROM kv_store
686
+ WHERE entity_id = OLD.id;
687
+ RETURN OLD;
688
+ ELSIF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
689
+ -- Upsert to KV_STORE (O(1) lookup by entity_key)
690
+ INSERT INTO kv_store (
691
+ entity_key,
692
+ entity_type,
693
+ entity_id,
694
+ tenant_id,
695
+ user_id,
696
+ metadata,
697
+ updated_at
698
+ ) VALUES (
699
+ NEW.id,
700
+ 'files',
701
+ NEW.id,
702
+ NEW.tenant_id,
703
+ NEW.user_id,
704
+ NEW.metadata,
705
+ CURRENT_TIMESTAMP
706
+ )
707
+ ON CONFLICT (tenant_id, entity_key)
708
+ DO UPDATE SET
709
+ entity_id = EXCLUDED.entity_id,
710
+ user_id = EXCLUDED.user_id,
711
+ metadata = EXCLUDED.metadata,
712
+ updated_at = CURRENT_TIMESTAMP;
713
+
714
+ RETURN NEW;
715
+ END IF;
716
+ END;
717
+ $$ LANGUAGE plpgsql;
718
+
719
+ -- Create trigger
720
+ DROP TRIGGER IF EXISTS trg_files_kv_store ON files;
721
+ CREATE TRIGGER trg_files_kv_store
722
+ AFTER INSERT OR UPDATE OR DELETE ON files
723
+ FOR EACH ROW EXECUTE FUNCTION fn_files_kv_store_upsert();
724
+
725
+ -- ======================================================================
726
+ -- ONTOLOGIES (Model: Ontology)
727
+ -- ======================================================================
728
+
729
+ CREATE TABLE IF NOT EXISTS ontologies (
730
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
731
+ tenant_id VARCHAR(100) NOT NULL,
732
+ user_id VARCHAR(256),
733
+ name VARCHAR(256) NOT NULL,
734
+ file_id TEXT NOT NULL,
735
+ agent_schema_id VARCHAR(256) NOT NULL,
736
+ provider_name VARCHAR(256) NOT NULL,
737
+ model_name VARCHAR(256) NOT NULL,
738
+ extracted_data JSONB NOT NULL,
739
+ confidence_score DOUBLE PRECISION,
740
+ extraction_timestamp VARCHAR(256),
741
+ embedding_text TEXT,
742
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
743
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
744
+ deleted_at TIMESTAMP,
745
+ graph_edges JSONB DEFAULT '[]'::jsonb,
746
+ metadata JSONB DEFAULT '{}'::jsonb,
747
+ tags TEXT[] DEFAULT ARRAY[]::TEXT[]
748
+ );
749
+
750
+ CREATE INDEX idx_ontologies_tenant ON ontologies (tenant_id);
751
+ CREATE INDEX idx_ontologies_user ON ontologies (user_id);
752
+ CREATE INDEX idx_ontologies_graph_edges ON ontologies USING GIN (graph_edges);
753
+ CREATE INDEX idx_ontologies_metadata ON ontologies USING GIN (metadata);
754
+ CREATE INDEX idx_ontologies_tags ON ontologies USING GIN (tags);
755
+
756
+ -- KV_STORE trigger for ontologies
757
+ -- Trigger function to maintain KV_STORE for ontologies
758
+ CREATE OR REPLACE FUNCTION fn_ontologies_kv_store_upsert()
759
+ RETURNS TRIGGER AS $$
760
+ BEGIN
761
+ IF (TG_OP = 'DELETE') THEN
762
+ -- Remove from KV_STORE on delete
763
+ DELETE FROM kv_store
764
+ WHERE entity_id = OLD.id;
765
+ RETURN OLD;
766
+ ELSIF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
767
+ -- Upsert to KV_STORE (O(1) lookup by entity_key)
768
+ INSERT INTO kv_store (
769
+ entity_key,
770
+ entity_type,
771
+ entity_id,
772
+ tenant_id,
773
+ user_id,
774
+ metadata,
775
+ updated_at
776
+ ) VALUES (
777
+ NEW.id,
778
+ 'ontologies',
779
+ NEW.id,
780
+ NEW.tenant_id,
781
+ NEW.user_id,
782
+ NEW.metadata,
783
+ CURRENT_TIMESTAMP
784
+ )
785
+ ON CONFLICT (tenant_id, entity_key)
786
+ DO UPDATE SET
787
+ entity_id = EXCLUDED.entity_id,
788
+ user_id = EXCLUDED.user_id,
789
+ metadata = EXCLUDED.metadata,
790
+ updated_at = CURRENT_TIMESTAMP;
791
+
792
+ RETURN NEW;
793
+ END IF;
794
+ END;
795
+ $$ LANGUAGE plpgsql;
796
+
797
+ -- Create trigger
798
+ DROP TRIGGER IF EXISTS trg_ontologies_kv_store ON ontologies;
799
+ CREATE TRIGGER trg_ontologies_kv_store
800
+ AFTER INSERT OR UPDATE OR DELETE ON ontologies
801
+ FOR EACH ROW EXECUTE FUNCTION fn_ontologies_kv_store_upsert();
802
+
803
+ -- ======================================================================
804
+ -- ONTOLOGY_CONFIGS (Model: OntologyConfig)
805
+ -- ======================================================================
806
+
807
+ CREATE TABLE IF NOT EXISTS ontology_configs (
808
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
809
+ tenant_id VARCHAR(100) NOT NULL,
810
+ user_id VARCHAR(256),
811
+ name VARCHAR(256) NOT NULL,
812
+ agent_schema_id VARCHAR(256) NOT NULL,
813
+ description TEXT,
814
+ mime_type_pattern VARCHAR(256),
815
+ uri_pattern VARCHAR(256),
816
+ tag_filter TEXT[],
817
+ priority INTEGER,
818
+ enabled BOOLEAN,
819
+ provider_name VARCHAR(256),
820
+ model_name VARCHAR(256),
821
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
822
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
823
+ deleted_at TIMESTAMP,
824
+ graph_edges JSONB DEFAULT '[]'::jsonb,
825
+ metadata JSONB DEFAULT '{}'::jsonb,
826
+ tags TEXT[] DEFAULT ARRAY[]::TEXT[]
827
+ );
828
+
829
+ CREATE INDEX idx_ontology_configs_tenant ON ontology_configs (tenant_id);
830
+ CREATE INDEX idx_ontology_configs_user ON ontology_configs (user_id);
831
+ CREATE INDEX idx_ontology_configs_graph_edges ON ontology_configs USING GIN (graph_edges);
832
+ CREATE INDEX idx_ontology_configs_metadata ON ontology_configs USING GIN (metadata);
833
+ CREATE INDEX idx_ontology_configs_tags ON ontology_configs USING GIN (tags);
834
+
835
+ -- Embeddings for ontology_configs
836
+ CREATE TABLE IF NOT EXISTS embeddings_ontology_configs (
837
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
838
+ entity_id UUID NOT NULL REFERENCES ontology_configs(id) ON DELETE CASCADE,
839
+ field_name VARCHAR(100) NOT NULL,
840
+ provider VARCHAR(50) NOT NULL DEFAULT 'openai',
841
+ model VARCHAR(100) NOT NULL DEFAULT 'text-embedding-3-small',
842
+ embedding vector(1536) NOT NULL,
843
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
844
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
845
+
846
+ -- Unique: one embedding per entity per field per provider
847
+ UNIQUE (entity_id, field_name, provider)
848
+ );
849
+
850
+ -- Index for entity lookup (get all embeddings for entity)
851
+ CREATE INDEX idx_embeddings_ontology_configs_entity ON embeddings_ontology_configs (entity_id);
852
+
853
+ -- Index for field + provider lookup
854
+ CREATE INDEX idx_embeddings_ontology_configs_field_provider ON embeddings_ontology_configs (field_name, provider);
855
+
856
+ -- HNSW index for vector similarity search (created in background)
857
+ -- Note: This will be created by background thread after data load
858
+ -- CREATE INDEX idx_embeddings_ontology_configs_vector_hnsw ON embeddings_ontology_configs
859
+ -- USING hnsw (embedding vector_cosine_ops);
860
+
861
+ -- KV_STORE trigger for ontology_configs
862
+ -- Trigger function to maintain KV_STORE for ontology_configs
863
+ CREATE OR REPLACE FUNCTION fn_ontology_configs_kv_store_upsert()
864
+ RETURNS TRIGGER AS $$
865
+ BEGIN
866
+ IF (TG_OP = 'DELETE') THEN
867
+ -- Remove from KV_STORE on delete
868
+ DELETE FROM kv_store
869
+ WHERE entity_id = OLD.id;
870
+ RETURN OLD;
871
+ ELSIF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
872
+ -- Upsert to KV_STORE (O(1) lookup by entity_key)
873
+ INSERT INTO kv_store (
874
+ entity_key,
875
+ entity_type,
876
+ entity_id,
877
+ tenant_id,
878
+ user_id,
879
+ metadata,
880
+ updated_at
881
+ ) VALUES (
882
+ NEW.id,
883
+ 'ontology_configs',
884
+ NEW.id,
885
+ NEW.tenant_id,
886
+ NEW.user_id,
887
+ NEW.metadata,
888
+ CURRENT_TIMESTAMP
889
+ )
890
+ ON CONFLICT (tenant_id, entity_key)
891
+ DO UPDATE SET
892
+ entity_id = EXCLUDED.entity_id,
893
+ user_id = EXCLUDED.user_id,
894
+ metadata = EXCLUDED.metadata,
895
+ updated_at = CURRENT_TIMESTAMP;
896
+
897
+ RETURN NEW;
898
+ END IF;
899
+ END;
900
+ $$ LANGUAGE plpgsql;
901
+
902
+ -- Create trigger
903
+ DROP TRIGGER IF EXISTS trg_ontology_configs_kv_store ON ontology_configs;
904
+ CREATE TRIGGER trg_ontology_configs_kv_store
905
+ AFTER INSERT OR UPDATE OR DELETE ON ontology_configs
906
+ FOR EACH ROW EXECUTE FUNCTION fn_ontology_configs_kv_store_upsert();
907
+
908
+ -- ======================================================================
909
+ -- SCHEMAS (Model: Schema)
910
+ -- ======================================================================
911
+
912
+ CREATE TABLE IF NOT EXISTS schemas (
913
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
914
+ tenant_id VARCHAR(100) NOT NULL,
915
+ user_id VARCHAR(256),
916
+ name VARCHAR(256) NOT NULL,
917
+ content TEXT,
918
+ spec JSONB NOT NULL,
919
+ category VARCHAR(256),
920
+ provider_configs JSONB DEFAULT '{}'::jsonb,
921
+ embedding_fields TEXT[] DEFAULT ARRAY[]::TEXT[],
922
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
923
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
924
+ deleted_at TIMESTAMP,
925
+ graph_edges JSONB DEFAULT '[]'::jsonb,
926
+ metadata JSONB DEFAULT '{}'::jsonb,
927
+ tags TEXT[] DEFAULT ARRAY[]::TEXT[]
928
+ );
929
+
930
+ CREATE INDEX idx_schemas_tenant ON schemas (tenant_id);
931
+ CREATE INDEX idx_schemas_user ON schemas (user_id);
932
+ CREATE INDEX idx_schemas_graph_edges ON schemas USING GIN (graph_edges);
933
+ CREATE INDEX idx_schemas_metadata ON schemas USING GIN (metadata);
934
+ CREATE INDEX idx_schemas_tags ON schemas USING GIN (tags);
935
+
936
+ -- Embeddings for schemas
937
+ CREATE TABLE IF NOT EXISTS embeddings_schemas (
938
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
939
+ entity_id UUID NOT NULL REFERENCES schemas(id) ON DELETE CASCADE,
940
+ field_name VARCHAR(100) NOT NULL,
941
+ provider VARCHAR(50) NOT NULL DEFAULT 'openai',
942
+ model VARCHAR(100) NOT NULL DEFAULT 'text-embedding-3-small',
943
+ embedding vector(1536) NOT NULL,
944
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
945
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
946
+
947
+ -- Unique: one embedding per entity per field per provider
948
+ UNIQUE (entity_id, field_name, provider)
949
+ );
950
+
951
+ -- Index for entity lookup (get all embeddings for entity)
952
+ CREATE INDEX idx_embeddings_schemas_entity ON embeddings_schemas (entity_id);
953
+
954
+ -- Index for field + provider lookup
955
+ CREATE INDEX idx_embeddings_schemas_field_provider ON embeddings_schemas (field_name, provider);
956
+
957
+ -- HNSW index for vector similarity search (created in background)
958
+ -- Note: This will be created by background thread after data load
959
+ -- CREATE INDEX idx_embeddings_schemas_vector_hnsw ON embeddings_schemas
960
+ -- USING hnsw (embedding vector_cosine_ops);
961
+
962
+ -- KV_STORE trigger for schemas
963
+ -- Trigger function to maintain KV_STORE for schemas
964
+ CREATE OR REPLACE FUNCTION fn_schemas_kv_store_upsert()
965
+ RETURNS TRIGGER AS $$
966
+ BEGIN
967
+ IF (TG_OP = 'DELETE') THEN
968
+ -- Remove from KV_STORE on delete
969
+ DELETE FROM kv_store
970
+ WHERE entity_id = OLD.id;
971
+ RETURN OLD;
972
+ ELSIF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
973
+ -- Upsert to KV_STORE (O(1) lookup by entity_key)
974
+ INSERT INTO kv_store (
975
+ entity_key,
976
+ entity_type,
977
+ entity_id,
978
+ tenant_id,
979
+ user_id,
980
+ metadata,
981
+ updated_at
982
+ ) VALUES (
983
+ NEW.id,
984
+ 'schemas',
985
+ NEW.id,
986
+ NEW.tenant_id,
987
+ NEW.user_id,
988
+ NEW.metadata,
989
+ CURRENT_TIMESTAMP
990
+ )
991
+ ON CONFLICT (tenant_id, entity_key)
992
+ DO UPDATE SET
993
+ entity_id = EXCLUDED.entity_id,
994
+ user_id = EXCLUDED.user_id,
995
+ metadata = EXCLUDED.metadata,
996
+ updated_at = CURRENT_TIMESTAMP;
997
+
998
+ RETURN NEW;
999
+ END IF;
1000
+ END;
1001
+ $$ LANGUAGE plpgsql;
1002
+
1003
+ -- Create trigger
1004
+ DROP TRIGGER IF EXISTS trg_schemas_kv_store ON schemas;
1005
+ CREATE TRIGGER trg_schemas_kv_store
1006
+ AFTER INSERT OR UPDATE OR DELETE ON schemas
1007
+ FOR EACH ROW EXECUTE FUNCTION fn_schemas_kv_store_upsert();
1008
+
1009
+ -- ============================================================================
1010
+ -- RECORD MIGRATION
1011
+ -- ============================================================================
1012
+
1013
+ INSERT INTO rem_migrations (name, type, version)
1014
+ VALUES ('install_models.sql', 'models', '1.0.0')
1015
+ ON CONFLICT (name) DO UPDATE
1016
+ SET applied_at = CURRENT_TIMESTAMP,
1017
+ applied_by = CURRENT_USER;
1018
+
1019
+ DO $$
1020
+ BEGIN
1021
+ RAISE NOTICE '============================================================';
1022
+ RAISE NOTICE 'REM Model Schema Applied: 10 tables';
1023
+ RAISE NOTICE '============================================================';
1024
+ RAISE NOTICE ' ✓ files (1 embeddable fields)';
1025
+ RAISE NOTICE ' ✓ image_resources (1 embeddable fields)';
1026
+ RAISE NOTICE ' ✓ messages (1 embeddable fields)';
1027
+ RAISE NOTICE ' ✓ moments (1 embeddable fields)';
1028
+ RAISE NOTICE ' ✓ ontologies';
1029
+ RAISE NOTICE ' ✓ ontology_configs (1 embeddable fields)';
1030
+ RAISE NOTICE ' ✓ persons';
1031
+ RAISE NOTICE ' ✓ resources (1 embeddable fields)';
1032
+ RAISE NOTICE ' ✓ schemas (1 embeddable fields)';
1033
+ RAISE NOTICE ' ✓ users (1 embeddable fields)';
1034
+ RAISE NOTICE '';
1035
+ RAISE NOTICE 'Next: Run background indexes if needed';
1036
+ RAISE NOTICE ' rem db migrate --background-indexes';
1037
+ RAISE NOTICE '============================================================';
1038
+ END $$;