remdb 0.2.6__py3-none-any.whl → 0.3.103__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.
Potentially problematic release.
This version of remdb might be problematic. Click here for more details.
- rem/__init__.py +129 -2
- rem/agentic/README.md +76 -0
- rem/agentic/__init__.py +15 -0
- rem/agentic/agents/__init__.py +16 -2
- rem/agentic/agents/sse_simulator.py +500 -0
- rem/agentic/context.py +7 -5
- rem/agentic/llm_provider_models.py +301 -0
- rem/agentic/providers/phoenix.py +32 -43
- rem/agentic/providers/pydantic_ai.py +84 -10
- rem/api/README.md +238 -1
- rem/api/deps.py +255 -0
- rem/api/main.py +70 -22
- rem/api/mcp_router/server.py +8 -1
- rem/api/mcp_router/tools.py +80 -0
- rem/api/middleware/tracking.py +172 -0
- rem/api/routers/admin.py +277 -0
- rem/api/routers/auth.py +124 -0
- rem/api/routers/chat/completions.py +123 -14
- rem/api/routers/chat/models.py +7 -3
- rem/api/routers/chat/sse_events.py +526 -0
- rem/api/routers/chat/streaming.py +468 -45
- rem/api/routers/dev.py +81 -0
- rem/api/routers/feedback.py +455 -0
- rem/api/routers/messages.py +473 -0
- rem/api/routers/models.py +78 -0
- rem/api/routers/shared_sessions.py +406 -0
- rem/auth/middleware.py +126 -27
- rem/cli/commands/ask.py +15 -11
- rem/cli/commands/configure.py +169 -94
- rem/cli/commands/db.py +53 -7
- rem/cli/commands/experiments.py +278 -96
- rem/cli/commands/process.py +8 -7
- rem/cli/commands/scaffold.py +47 -0
- rem/cli/commands/schema.py +9 -9
- rem/cli/main.py +10 -0
- rem/config.py +2 -2
- rem/models/core/core_model.py +7 -1
- rem/models/entities/__init__.py +21 -0
- rem/models/entities/domain_resource.py +38 -0
- rem/models/entities/feedback.py +123 -0
- rem/models/entities/message.py +30 -1
- rem/models/entities/session.py +83 -0
- rem/models/entities/shared_session.py +206 -0
- rem/models/entities/user.py +10 -3
- rem/registry.py +367 -0
- rem/schemas/agents/rem.yaml +7 -3
- rem/services/content/providers.py +94 -140
- rem/services/content/service.py +85 -16
- rem/services/dreaming/affinity_service.py +2 -16
- rem/services/dreaming/moment_service.py +2 -15
- rem/services/embeddings/api.py +20 -13
- rem/services/phoenix/EXPERIMENT_DESIGN.md +3 -3
- rem/services/phoenix/client.py +252 -19
- rem/services/postgres/README.md +29 -10
- rem/services/postgres/repository.py +132 -0
- rem/services/postgres/schema_generator.py +86 -5
- rem/services/rate_limit.py +113 -0
- rem/services/rem/README.md +14 -0
- rem/services/session/compression.py +17 -1
- rem/services/user_service.py +98 -0
- rem/settings.py +115 -17
- rem/sql/background_indexes.sql +10 -0
- rem/sql/migrations/001_install.sql +152 -2
- rem/sql/migrations/002_install_models.sql +580 -231
- rem/sql/migrations/003_seed_default_user.sql +48 -0
- rem/utils/constants.py +97 -0
- rem/utils/date_utils.py +228 -0
- rem/utils/embeddings.py +17 -4
- rem/utils/files.py +167 -0
- rem/utils/mime_types.py +158 -0
- rem/utils/model_helpers.py +156 -1
- rem/utils/schema_loader.py +273 -14
- rem/utils/sql_types.py +3 -1
- rem/utils/vision.py +9 -14
- rem/workers/README.md +14 -14
- rem/workers/db_maintainer.py +74 -0
- {remdb-0.2.6.dist-info → remdb-0.3.103.dist-info}/METADATA +486 -132
- {remdb-0.2.6.dist-info → remdb-0.3.103.dist-info}/RECORD +80 -57
- {remdb-0.2.6.dist-info → remdb-0.3.103.dist-info}/WHEEL +1 -1
- rem/sql/002_install_models.sql +0 -1068
- rem/sql/install_models.sql +0 -1038
- {remdb-0.2.6.dist-info → remdb-0.3.103.dist-info}/entry_points.txt +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
-- REM Model Schema (install_models.sql)
|
|
2
2
|
-- Generated from Pydantic models
|
|
3
|
-
-- Source directory: src/rem/models/entities
|
|
4
|
-
-- Generated at: 2025-11-
|
|
3
|
+
-- Source: directory: src/rem/models/entities
|
|
4
|
+
-- Generated at: 2025-11-28T08:13:28.661915
|
|
5
5
|
--
|
|
6
|
-
-- DO NOT EDIT MANUALLY - Regenerate with: rem schema generate
|
|
6
|
+
-- DO NOT EDIT MANUALLY - Regenerate with: rem db schema generate
|
|
7
7
|
--
|
|
8
8
|
-- This script creates:
|
|
9
9
|
-- 1. Primary entity tables
|
|
@@ -19,11 +19,11 @@ DO $$
|
|
|
19
19
|
BEGIN
|
|
20
20
|
-- Check that install.sql has been run
|
|
21
21
|
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'kv_store') THEN
|
|
22
|
-
RAISE EXCEPTION 'KV_STORE table not found. Run
|
|
22
|
+
RAISE EXCEPTION 'KV_STORE table not found. Run migrations/001_install.sql first.';
|
|
23
23
|
END IF;
|
|
24
24
|
|
|
25
25
|
IF NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector') THEN
|
|
26
|
-
RAISE EXCEPTION 'pgvector extension not found. Run
|
|
26
|
+
RAISE EXCEPTION 'pgvector extension not found. Run migrations/001_install.sql first.';
|
|
27
27
|
END IF;
|
|
28
28
|
|
|
29
29
|
RAISE NOTICE 'Prerequisites check passed';
|
|
@@ -41,6 +41,7 @@ CREATE TABLE IF NOT EXISTS users (
|
|
|
41
41
|
email VARCHAR(256),
|
|
42
42
|
role VARCHAR(256),
|
|
43
43
|
tier TEXT,
|
|
44
|
+
anonymous_ids TEXT[] DEFAULT ARRAY[]::TEXT[],
|
|
44
45
|
sec_policy JSONB DEFAULT '{}'::jsonb,
|
|
45
46
|
summary TEXT,
|
|
46
47
|
interests TEXT[] DEFAULT ARRAY[]::TEXT[],
|
|
@@ -55,11 +56,11 @@ CREATE TABLE IF NOT EXISTS users (
|
|
|
55
56
|
tags TEXT[] DEFAULT ARRAY[]::TEXT[]
|
|
56
57
|
);
|
|
57
58
|
|
|
58
|
-
CREATE INDEX
|
|
59
|
-
CREATE INDEX
|
|
60
|
-
CREATE INDEX
|
|
61
|
-
CREATE INDEX
|
|
62
|
-
CREATE INDEX
|
|
59
|
+
CREATE INDEX idx_users_tenant ON users (tenant_id);
|
|
60
|
+
CREATE INDEX idx_users_user ON users (user_id);
|
|
61
|
+
CREATE INDEX idx_users_graph_edges ON users USING GIN (graph_edges);
|
|
62
|
+
CREATE INDEX idx_users_metadata ON users USING GIN (metadata);
|
|
63
|
+
CREATE INDEX idx_users_tags ON users USING GIN (tags);
|
|
63
64
|
|
|
64
65
|
-- Embeddings for users
|
|
65
66
|
CREATE TABLE IF NOT EXISTS embeddings_users (
|
|
@@ -77,14 +78,14 @@ CREATE TABLE IF NOT EXISTS embeddings_users (
|
|
|
77
78
|
);
|
|
78
79
|
|
|
79
80
|
-- Index for entity lookup (get all embeddings for entity)
|
|
80
|
-
CREATE INDEX
|
|
81
|
+
CREATE INDEX idx_embeddings_users_entity ON embeddings_users (entity_id);
|
|
81
82
|
|
|
82
83
|
-- Index for field + provider lookup
|
|
83
|
-
CREATE INDEX
|
|
84
|
+
CREATE INDEX idx_embeddings_users_field_provider ON embeddings_users (field_name, provider);
|
|
84
85
|
|
|
85
86
|
-- HNSW index for vector similarity search (created in background)
|
|
86
87
|
-- Note: This will be created by background thread after data load
|
|
87
|
-
-- CREATE INDEX
|
|
88
|
+
-- CREATE INDEX idx_embeddings_users_vector_hnsw ON embeddings_users
|
|
88
89
|
-- USING hnsw (embedding vector_cosine_ops);
|
|
89
90
|
|
|
90
91
|
-- KV_STORE trigger for users
|
|
@@ -109,7 +110,7 @@ BEGIN
|
|
|
109
110
|
graph_edges,
|
|
110
111
|
updated_at
|
|
111
112
|
) VALUES (
|
|
112
|
-
NEW.name,
|
|
113
|
+
NEW.name::VARCHAR,
|
|
113
114
|
'users',
|
|
114
115
|
NEW.id,
|
|
115
116
|
NEW.tenant_id,
|
|
@@ -145,7 +146,7 @@ CREATE TABLE IF NOT EXISTS image_resources (
|
|
|
145
146
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
146
147
|
tenant_id VARCHAR(100) NOT NULL,
|
|
147
148
|
user_id VARCHAR(256),
|
|
148
|
-
name VARCHAR(256)
|
|
149
|
+
name VARCHAR(256),
|
|
149
150
|
uri VARCHAR(256),
|
|
150
151
|
ordinal INTEGER,
|
|
151
152
|
content TEXT,
|
|
@@ -168,11 +169,11 @@ CREATE TABLE IF NOT EXISTS image_resources (
|
|
|
168
169
|
tags TEXT[] DEFAULT ARRAY[]::TEXT[]
|
|
169
170
|
);
|
|
170
171
|
|
|
171
|
-
CREATE INDEX
|
|
172
|
-
CREATE INDEX
|
|
173
|
-
CREATE INDEX
|
|
174
|
-
CREATE INDEX
|
|
175
|
-
CREATE INDEX
|
|
172
|
+
CREATE INDEX idx_image_resources_tenant ON image_resources (tenant_id);
|
|
173
|
+
CREATE INDEX idx_image_resources_user ON image_resources (user_id);
|
|
174
|
+
CREATE INDEX idx_image_resources_graph_edges ON image_resources USING GIN (graph_edges);
|
|
175
|
+
CREATE INDEX idx_image_resources_metadata ON image_resources USING GIN (metadata);
|
|
176
|
+
CREATE INDEX idx_image_resources_tags ON image_resources USING GIN (tags);
|
|
176
177
|
|
|
177
178
|
-- Embeddings for image_resources
|
|
178
179
|
CREATE TABLE IF NOT EXISTS embeddings_image_resources (
|
|
@@ -190,14 +191,14 @@ CREATE TABLE IF NOT EXISTS embeddings_image_resources (
|
|
|
190
191
|
);
|
|
191
192
|
|
|
192
193
|
-- Index for entity lookup (get all embeddings for entity)
|
|
193
|
-
CREATE INDEX
|
|
194
|
+
CREATE INDEX idx_embeddings_image_resources_entity ON embeddings_image_resources (entity_id);
|
|
194
195
|
|
|
195
196
|
-- Index for field + provider lookup
|
|
196
|
-
CREATE INDEX
|
|
197
|
+
CREATE INDEX idx_embeddings_image_resources_field_provider ON embeddings_image_resources (field_name, provider);
|
|
197
198
|
|
|
198
199
|
-- HNSW index for vector similarity search (created in background)
|
|
199
200
|
-- Note: This will be created by background thread after data load
|
|
200
|
-
-- CREATE INDEX
|
|
201
|
+
-- CREATE INDEX idx_embeddings_image_resources_vector_hnsw ON embeddings_image_resources
|
|
201
202
|
-- USING hnsw (embedding vector_cosine_ops);
|
|
202
203
|
|
|
203
204
|
-- KV_STORE trigger for image_resources
|
|
@@ -222,7 +223,7 @@ BEGIN
|
|
|
222
223
|
graph_edges,
|
|
223
224
|
updated_at
|
|
224
225
|
) VALUES (
|
|
225
|
-
NEW.name,
|
|
226
|
+
NEW.name::VARCHAR,
|
|
226
227
|
'image_resources',
|
|
227
228
|
NEW.id,
|
|
228
229
|
NEW.tenant_id,
|
|
@@ -250,6 +251,88 @@ CREATE TRIGGER trg_image_resources_kv_store
|
|
|
250
251
|
AFTER INSERT OR UPDATE OR DELETE ON image_resources
|
|
251
252
|
FOR EACH ROW EXECUTE FUNCTION fn_image_resources_kv_store_upsert();
|
|
252
253
|
|
|
254
|
+
-- ======================================================================
|
|
255
|
+
-- FEEDBACKS (Model: Feedback)
|
|
256
|
+
-- ======================================================================
|
|
257
|
+
|
|
258
|
+
CREATE TABLE IF NOT EXISTS feedbacks (
|
|
259
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
260
|
+
tenant_id VARCHAR(100) NOT NULL,
|
|
261
|
+
user_id VARCHAR(256),
|
|
262
|
+
session_id VARCHAR(256) NOT NULL,
|
|
263
|
+
message_id VARCHAR(256),
|
|
264
|
+
rating INTEGER,
|
|
265
|
+
categories TEXT[] DEFAULT ARRAY[]::TEXT[],
|
|
266
|
+
comment TEXT,
|
|
267
|
+
trace_id VARCHAR(256),
|
|
268
|
+
span_id VARCHAR(256),
|
|
269
|
+
phoenix_synced BOOLEAN,
|
|
270
|
+
phoenix_annotation_id VARCHAR(256),
|
|
271
|
+
annotator_kind VARCHAR(256),
|
|
272
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
273
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
274
|
+
deleted_at TIMESTAMP,
|
|
275
|
+
graph_edges JSONB DEFAULT '[]'::jsonb,
|
|
276
|
+
metadata JSONB DEFAULT '{}'::jsonb,
|
|
277
|
+
tags TEXT[] DEFAULT ARRAY[]::TEXT[]
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
CREATE INDEX idx_feedbacks_tenant ON feedbacks (tenant_id);
|
|
281
|
+
CREATE INDEX idx_feedbacks_user ON feedbacks (user_id);
|
|
282
|
+
CREATE INDEX idx_feedbacks_graph_edges ON feedbacks USING GIN (graph_edges);
|
|
283
|
+
CREATE INDEX idx_feedbacks_metadata ON feedbacks USING GIN (metadata);
|
|
284
|
+
CREATE INDEX idx_feedbacks_tags ON feedbacks USING GIN (tags);
|
|
285
|
+
|
|
286
|
+
-- KV_STORE trigger for feedbacks
|
|
287
|
+
-- Trigger function to maintain KV_STORE for feedbacks
|
|
288
|
+
CREATE OR REPLACE FUNCTION fn_feedbacks_kv_store_upsert()
|
|
289
|
+
RETURNS TRIGGER AS $$
|
|
290
|
+
BEGIN
|
|
291
|
+
IF (TG_OP = 'DELETE') THEN
|
|
292
|
+
-- Remove from KV_STORE on delete
|
|
293
|
+
DELETE FROM kv_store
|
|
294
|
+
WHERE entity_id = OLD.id;
|
|
295
|
+
RETURN OLD;
|
|
296
|
+
ELSIF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
|
|
297
|
+
-- Upsert to KV_STORE (O(1) lookup by entity_key)
|
|
298
|
+
INSERT INTO kv_store (
|
|
299
|
+
entity_key,
|
|
300
|
+
entity_type,
|
|
301
|
+
entity_id,
|
|
302
|
+
tenant_id,
|
|
303
|
+
user_id,
|
|
304
|
+
metadata,
|
|
305
|
+
graph_edges,
|
|
306
|
+
updated_at
|
|
307
|
+
) VALUES (
|
|
308
|
+
NEW.id::VARCHAR,
|
|
309
|
+
'feedbacks',
|
|
310
|
+
NEW.id,
|
|
311
|
+
NEW.tenant_id,
|
|
312
|
+
NEW.user_id,
|
|
313
|
+
NEW.metadata,
|
|
314
|
+
COALESCE(NEW.graph_edges, '[]'::jsonb),
|
|
315
|
+
CURRENT_TIMESTAMP
|
|
316
|
+
)
|
|
317
|
+
ON CONFLICT (tenant_id, entity_key)
|
|
318
|
+
DO UPDATE SET
|
|
319
|
+
entity_id = EXCLUDED.entity_id,
|
|
320
|
+
user_id = EXCLUDED.user_id,
|
|
321
|
+
metadata = EXCLUDED.metadata,
|
|
322
|
+
graph_edges = EXCLUDED.graph_edges,
|
|
323
|
+
updated_at = CURRENT_TIMESTAMP;
|
|
324
|
+
|
|
325
|
+
RETURN NEW;
|
|
326
|
+
END IF;
|
|
327
|
+
END;
|
|
328
|
+
$$ LANGUAGE plpgsql;
|
|
329
|
+
|
|
330
|
+
-- Create trigger
|
|
331
|
+
DROP TRIGGER IF EXISTS trg_feedbacks_kv_store ON feedbacks;
|
|
332
|
+
CREATE TRIGGER trg_feedbacks_kv_store
|
|
333
|
+
AFTER INSERT OR UPDATE OR DELETE ON feedbacks
|
|
334
|
+
FOR EACH ROW EXECUTE FUNCTION fn_feedbacks_kv_store_upsert();
|
|
335
|
+
|
|
253
336
|
-- ======================================================================
|
|
254
337
|
-- MOMENTS (Model: Moment)
|
|
255
338
|
-- ======================================================================
|
|
@@ -258,7 +341,7 @@ CREATE TABLE IF NOT EXISTS moments (
|
|
|
258
341
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
259
342
|
tenant_id VARCHAR(100) NOT NULL,
|
|
260
343
|
user_id VARCHAR(256),
|
|
261
|
-
name VARCHAR(256)
|
|
344
|
+
name VARCHAR(256),
|
|
262
345
|
moment_type VARCHAR(256),
|
|
263
346
|
category VARCHAR(256),
|
|
264
347
|
starts_timestamp TIMESTAMP NOT NULL,
|
|
@@ -276,11 +359,11 @@ CREATE TABLE IF NOT EXISTS moments (
|
|
|
276
359
|
tags TEXT[] DEFAULT ARRAY[]::TEXT[]
|
|
277
360
|
);
|
|
278
361
|
|
|
279
|
-
CREATE INDEX
|
|
280
|
-
CREATE INDEX
|
|
281
|
-
CREATE INDEX
|
|
282
|
-
CREATE INDEX
|
|
283
|
-
CREATE INDEX
|
|
362
|
+
CREATE INDEX idx_moments_tenant ON moments (tenant_id);
|
|
363
|
+
CREATE INDEX idx_moments_user ON moments (user_id);
|
|
364
|
+
CREATE INDEX idx_moments_graph_edges ON moments USING GIN (graph_edges);
|
|
365
|
+
CREATE INDEX idx_moments_metadata ON moments USING GIN (metadata);
|
|
366
|
+
CREATE INDEX idx_moments_tags ON moments USING GIN (tags);
|
|
284
367
|
|
|
285
368
|
-- Embeddings for moments
|
|
286
369
|
CREATE TABLE IF NOT EXISTS embeddings_moments (
|
|
@@ -298,14 +381,14 @@ CREATE TABLE IF NOT EXISTS embeddings_moments (
|
|
|
298
381
|
);
|
|
299
382
|
|
|
300
383
|
-- Index for entity lookup (get all embeddings for entity)
|
|
301
|
-
CREATE INDEX
|
|
384
|
+
CREATE INDEX idx_embeddings_moments_entity ON embeddings_moments (entity_id);
|
|
302
385
|
|
|
303
386
|
-- Index for field + provider lookup
|
|
304
|
-
CREATE INDEX
|
|
387
|
+
CREATE INDEX idx_embeddings_moments_field_provider ON embeddings_moments (field_name, provider);
|
|
305
388
|
|
|
306
389
|
-- HNSW index for vector similarity search (created in background)
|
|
307
390
|
-- Note: This will be created by background thread after data load
|
|
308
|
-
-- CREATE INDEX
|
|
391
|
+
-- CREATE INDEX idx_embeddings_moments_vector_hnsw ON embeddings_moments
|
|
309
392
|
-- USING hnsw (embedding vector_cosine_ops);
|
|
310
393
|
|
|
311
394
|
-- KV_STORE trigger for moments
|
|
@@ -330,7 +413,7 @@ BEGIN
|
|
|
330
413
|
graph_edges,
|
|
331
414
|
updated_at
|
|
332
415
|
) VALUES (
|
|
333
|
-
NEW.name,
|
|
416
|
+
NEW.name::VARCHAR,
|
|
334
417
|
'moments',
|
|
335
418
|
NEW.id,
|
|
336
419
|
NEW.tenant_id,
|
|
@@ -376,11 +459,11 @@ CREATE TABLE IF NOT EXISTS persons (
|
|
|
376
459
|
tags TEXT[] DEFAULT ARRAY[]::TEXT[]
|
|
377
460
|
);
|
|
378
461
|
|
|
379
|
-
CREATE INDEX
|
|
380
|
-
CREATE INDEX
|
|
381
|
-
CREATE INDEX
|
|
382
|
-
CREATE INDEX
|
|
383
|
-
CREATE INDEX
|
|
462
|
+
CREATE INDEX idx_persons_tenant ON persons (tenant_id);
|
|
463
|
+
CREATE INDEX idx_persons_user ON persons (user_id);
|
|
464
|
+
CREATE INDEX idx_persons_graph_edges ON persons USING GIN (graph_edges);
|
|
465
|
+
CREATE INDEX idx_persons_metadata ON persons USING GIN (metadata);
|
|
466
|
+
CREATE INDEX idx_persons_tags ON persons USING GIN (tags);
|
|
384
467
|
|
|
385
468
|
-- KV_STORE trigger for persons
|
|
386
469
|
-- Trigger function to maintain KV_STORE for persons
|
|
@@ -404,7 +487,7 @@ BEGIN
|
|
|
404
487
|
graph_edges,
|
|
405
488
|
updated_at
|
|
406
489
|
) VALUES (
|
|
407
|
-
NEW.
|
|
490
|
+
NEW.id::VARCHAR,
|
|
408
491
|
'persons',
|
|
409
492
|
NEW.id,
|
|
410
493
|
NEW.tenant_id,
|
|
@@ -432,6 +515,113 @@ CREATE TRIGGER trg_persons_kv_store
|
|
|
432
515
|
AFTER INSERT OR UPDATE OR DELETE ON persons
|
|
433
516
|
FOR EACH ROW EXECUTE FUNCTION fn_persons_kv_store_upsert();
|
|
434
517
|
|
|
518
|
+
-- ======================================================================
|
|
519
|
+
-- SESSIONS (Model: Session)
|
|
520
|
+
-- ======================================================================
|
|
521
|
+
|
|
522
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
523
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
524
|
+
tenant_id VARCHAR(100) NOT NULL,
|
|
525
|
+
user_id VARCHAR(256),
|
|
526
|
+
name VARCHAR(256) NOT NULL,
|
|
527
|
+
mode TEXT,
|
|
528
|
+
description TEXT,
|
|
529
|
+
original_trace_id VARCHAR(256),
|
|
530
|
+
settings_overrides JSONB,
|
|
531
|
+
prompt TEXT,
|
|
532
|
+
agent_schema_uri VARCHAR(256),
|
|
533
|
+
message_count INTEGER,
|
|
534
|
+
total_tokens INTEGER,
|
|
535
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
536
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
537
|
+
deleted_at TIMESTAMP,
|
|
538
|
+
graph_edges JSONB DEFAULT '[]'::jsonb,
|
|
539
|
+
metadata JSONB DEFAULT '{}'::jsonb,
|
|
540
|
+
tags TEXT[] DEFAULT ARRAY[]::TEXT[]
|
|
541
|
+
);
|
|
542
|
+
|
|
543
|
+
CREATE INDEX idx_sessions_tenant ON sessions (tenant_id);
|
|
544
|
+
CREATE INDEX idx_sessions_user ON sessions (user_id);
|
|
545
|
+
CREATE INDEX idx_sessions_graph_edges ON sessions USING GIN (graph_edges);
|
|
546
|
+
CREATE INDEX idx_sessions_metadata ON sessions USING GIN (metadata);
|
|
547
|
+
CREATE INDEX idx_sessions_tags ON sessions USING GIN (tags);
|
|
548
|
+
|
|
549
|
+
-- Embeddings for sessions
|
|
550
|
+
CREATE TABLE IF NOT EXISTS embeddings_sessions (
|
|
551
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
552
|
+
entity_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
553
|
+
field_name VARCHAR(100) NOT NULL,
|
|
554
|
+
provider VARCHAR(50) NOT NULL DEFAULT 'openai',
|
|
555
|
+
model VARCHAR(100) NOT NULL DEFAULT 'text-embedding-3-small',
|
|
556
|
+
embedding vector(1536) NOT NULL,
|
|
557
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
558
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
559
|
+
|
|
560
|
+
-- Unique: one embedding per entity per field per provider
|
|
561
|
+
UNIQUE (entity_id, field_name, provider)
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
-- Index for entity lookup (get all embeddings for entity)
|
|
565
|
+
CREATE INDEX idx_embeddings_sessions_entity ON embeddings_sessions (entity_id);
|
|
566
|
+
|
|
567
|
+
-- Index for field + provider lookup
|
|
568
|
+
CREATE INDEX idx_embeddings_sessions_field_provider ON embeddings_sessions (field_name, provider);
|
|
569
|
+
|
|
570
|
+
-- HNSW index for vector similarity search (created in background)
|
|
571
|
+
-- Note: This will be created by background thread after data load
|
|
572
|
+
-- CREATE INDEX idx_embeddings_sessions_vector_hnsw ON embeddings_sessions
|
|
573
|
+
-- USING hnsw (embedding vector_cosine_ops);
|
|
574
|
+
|
|
575
|
+
-- KV_STORE trigger for sessions
|
|
576
|
+
-- Trigger function to maintain KV_STORE for sessions
|
|
577
|
+
CREATE OR REPLACE FUNCTION fn_sessions_kv_store_upsert()
|
|
578
|
+
RETURNS TRIGGER AS $$
|
|
579
|
+
BEGIN
|
|
580
|
+
IF (TG_OP = 'DELETE') THEN
|
|
581
|
+
-- Remove from KV_STORE on delete
|
|
582
|
+
DELETE FROM kv_store
|
|
583
|
+
WHERE entity_id = OLD.id;
|
|
584
|
+
RETURN OLD;
|
|
585
|
+
ELSIF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
|
|
586
|
+
-- Upsert to KV_STORE (O(1) lookup by entity_key)
|
|
587
|
+
INSERT INTO kv_store (
|
|
588
|
+
entity_key,
|
|
589
|
+
entity_type,
|
|
590
|
+
entity_id,
|
|
591
|
+
tenant_id,
|
|
592
|
+
user_id,
|
|
593
|
+
metadata,
|
|
594
|
+
graph_edges,
|
|
595
|
+
updated_at
|
|
596
|
+
) VALUES (
|
|
597
|
+
NEW.name::VARCHAR,
|
|
598
|
+
'sessions',
|
|
599
|
+
NEW.id,
|
|
600
|
+
NEW.tenant_id,
|
|
601
|
+
NEW.user_id,
|
|
602
|
+
NEW.metadata,
|
|
603
|
+
COALESCE(NEW.graph_edges, '[]'::jsonb),
|
|
604
|
+
CURRENT_TIMESTAMP
|
|
605
|
+
)
|
|
606
|
+
ON CONFLICT (tenant_id, entity_key)
|
|
607
|
+
DO UPDATE SET
|
|
608
|
+
entity_id = EXCLUDED.entity_id,
|
|
609
|
+
user_id = EXCLUDED.user_id,
|
|
610
|
+
metadata = EXCLUDED.metadata,
|
|
611
|
+
graph_edges = EXCLUDED.graph_edges,
|
|
612
|
+
updated_at = CURRENT_TIMESTAMP;
|
|
613
|
+
|
|
614
|
+
RETURN NEW;
|
|
615
|
+
END IF;
|
|
616
|
+
END;
|
|
617
|
+
$$ LANGUAGE plpgsql;
|
|
618
|
+
|
|
619
|
+
-- Create trigger
|
|
620
|
+
DROP TRIGGER IF EXISTS trg_sessions_kv_store ON sessions;
|
|
621
|
+
CREATE TRIGGER trg_sessions_kv_store
|
|
622
|
+
AFTER INSERT OR UPDATE OR DELETE ON sessions
|
|
623
|
+
FOR EACH ROW EXECUTE FUNCTION fn_sessions_kv_store_upsert();
|
|
624
|
+
|
|
435
625
|
-- ======================================================================
|
|
436
626
|
-- RESOURCES (Model: Resource)
|
|
437
627
|
-- ======================================================================
|
|
@@ -440,7 +630,7 @@ CREATE TABLE IF NOT EXISTS resources (
|
|
|
440
630
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
441
631
|
tenant_id VARCHAR(100) NOT NULL,
|
|
442
632
|
user_id VARCHAR(256),
|
|
443
|
-
name VARCHAR(256)
|
|
633
|
+
name VARCHAR(256),
|
|
444
634
|
uri VARCHAR(256),
|
|
445
635
|
ordinal INTEGER,
|
|
446
636
|
content TEXT,
|
|
@@ -455,11 +645,11 @@ CREATE TABLE IF NOT EXISTS resources (
|
|
|
455
645
|
tags TEXT[] DEFAULT ARRAY[]::TEXT[]
|
|
456
646
|
);
|
|
457
647
|
|
|
458
|
-
CREATE INDEX
|
|
459
|
-
CREATE INDEX
|
|
460
|
-
CREATE INDEX
|
|
461
|
-
CREATE INDEX
|
|
462
|
-
CREATE INDEX
|
|
648
|
+
CREATE INDEX idx_resources_tenant ON resources (tenant_id);
|
|
649
|
+
CREATE INDEX idx_resources_user ON resources (user_id);
|
|
650
|
+
CREATE INDEX idx_resources_graph_edges ON resources USING GIN (graph_edges);
|
|
651
|
+
CREATE INDEX idx_resources_metadata ON resources USING GIN (metadata);
|
|
652
|
+
CREATE INDEX idx_resources_tags ON resources USING GIN (tags);
|
|
463
653
|
|
|
464
654
|
-- Embeddings for resources
|
|
465
655
|
CREATE TABLE IF NOT EXISTS embeddings_resources (
|
|
@@ -477,14 +667,14 @@ CREATE TABLE IF NOT EXISTS embeddings_resources (
|
|
|
477
667
|
);
|
|
478
668
|
|
|
479
669
|
-- Index for entity lookup (get all embeddings for entity)
|
|
480
|
-
CREATE INDEX
|
|
670
|
+
CREATE INDEX idx_embeddings_resources_entity ON embeddings_resources (entity_id);
|
|
481
671
|
|
|
482
672
|
-- Index for field + provider lookup
|
|
483
|
-
CREATE INDEX
|
|
673
|
+
CREATE INDEX idx_embeddings_resources_field_provider ON embeddings_resources (field_name, provider);
|
|
484
674
|
|
|
485
675
|
-- HNSW index for vector similarity search (created in background)
|
|
486
676
|
-- Note: This will be created by background thread after data load
|
|
487
|
-
-- CREATE INDEX
|
|
677
|
+
-- CREATE INDEX idx_embeddings_resources_vector_hnsw ON embeddings_resources
|
|
488
678
|
-- USING hnsw (embedding vector_cosine_ops);
|
|
489
679
|
|
|
490
680
|
-- KV_STORE trigger for resources
|
|
@@ -509,7 +699,7 @@ BEGIN
|
|
|
509
699
|
graph_edges,
|
|
510
700
|
updated_at
|
|
511
701
|
) VALUES (
|
|
512
|
-
NEW.name,
|
|
702
|
+
NEW.name::VARCHAR,
|
|
513
703
|
'resources',
|
|
514
704
|
NEW.id,
|
|
515
705
|
NEW.tenant_id,
|
|
@@ -546,8 +736,13 @@ CREATE TABLE IF NOT EXISTS messages (
|
|
|
546
736
|
tenant_id VARCHAR(100) NOT NULL,
|
|
547
737
|
user_id VARCHAR(256),
|
|
548
738
|
content TEXT NOT NULL,
|
|
549
|
-
message_type
|
|
550
|
-
session_id
|
|
739
|
+
message_type VARCHAR(256),
|
|
740
|
+
session_id VARCHAR(256),
|
|
741
|
+
prompt TEXT,
|
|
742
|
+
model VARCHAR(256),
|
|
743
|
+
token_count INTEGER,
|
|
744
|
+
trace_id VARCHAR(256),
|
|
745
|
+
span_id VARCHAR(256),
|
|
551
746
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
552
747
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
553
748
|
deleted_at TIMESTAMP,
|
|
@@ -556,11 +751,11 @@ CREATE TABLE IF NOT EXISTS messages (
|
|
|
556
751
|
tags TEXT[] DEFAULT ARRAY[]::TEXT[]
|
|
557
752
|
);
|
|
558
753
|
|
|
559
|
-
CREATE INDEX
|
|
560
|
-
CREATE INDEX
|
|
561
|
-
CREATE INDEX
|
|
562
|
-
CREATE INDEX
|
|
563
|
-
CREATE INDEX
|
|
754
|
+
CREATE INDEX idx_messages_tenant ON messages (tenant_id);
|
|
755
|
+
CREATE INDEX idx_messages_user ON messages (user_id);
|
|
756
|
+
CREATE INDEX idx_messages_graph_edges ON messages USING GIN (graph_edges);
|
|
757
|
+
CREATE INDEX idx_messages_metadata ON messages USING GIN (metadata);
|
|
758
|
+
CREATE INDEX idx_messages_tags ON messages USING GIN (tags);
|
|
564
759
|
|
|
565
760
|
-- Embeddings for messages
|
|
566
761
|
CREATE TABLE IF NOT EXISTS embeddings_messages (
|
|
@@ -578,14 +773,14 @@ CREATE TABLE IF NOT EXISTS embeddings_messages (
|
|
|
578
773
|
);
|
|
579
774
|
|
|
580
775
|
-- Index for entity lookup (get all embeddings for entity)
|
|
581
|
-
CREATE INDEX
|
|
776
|
+
CREATE INDEX idx_embeddings_messages_entity ON embeddings_messages (entity_id);
|
|
582
777
|
|
|
583
778
|
-- Index for field + provider lookup
|
|
584
|
-
CREATE INDEX
|
|
779
|
+
CREATE INDEX idx_embeddings_messages_field_provider ON embeddings_messages (field_name, provider);
|
|
585
780
|
|
|
586
781
|
-- HNSW index for vector similarity search (created in background)
|
|
587
782
|
-- Note: This will be created by background thread after data load
|
|
588
|
-
-- CREATE INDEX
|
|
783
|
+
-- CREATE INDEX idx_embeddings_messages_vector_hnsw ON embeddings_messages
|
|
589
784
|
-- USING hnsw (embedding vector_cosine_ops);
|
|
590
785
|
|
|
591
786
|
-- KV_STORE trigger for messages
|
|
@@ -610,7 +805,7 @@ BEGIN
|
|
|
610
805
|
graph_edges,
|
|
611
806
|
updated_at
|
|
612
807
|
) VALUES (
|
|
613
|
-
NEW.id::VARCHAR,
|
|
808
|
+
NEW.id::VARCHAR,
|
|
614
809
|
'messages',
|
|
615
810
|
NEW.id,
|
|
616
811
|
NEW.tenant_id,
|
|
@@ -661,11 +856,11 @@ CREATE TABLE IF NOT EXISTS files (
|
|
|
661
856
|
tags TEXT[] DEFAULT ARRAY[]::TEXT[]
|
|
662
857
|
);
|
|
663
858
|
|
|
664
|
-
CREATE INDEX
|
|
665
|
-
CREATE INDEX
|
|
666
|
-
CREATE INDEX
|
|
667
|
-
CREATE INDEX
|
|
668
|
-
CREATE INDEX
|
|
859
|
+
CREATE INDEX idx_files_tenant ON files (tenant_id);
|
|
860
|
+
CREATE INDEX idx_files_user ON files (user_id);
|
|
861
|
+
CREATE INDEX idx_files_graph_edges ON files USING GIN (graph_edges);
|
|
862
|
+
CREATE INDEX idx_files_metadata ON files USING GIN (metadata);
|
|
863
|
+
CREATE INDEX idx_files_tags ON files USING GIN (tags);
|
|
669
864
|
|
|
670
865
|
-- Embeddings for files
|
|
671
866
|
CREATE TABLE IF NOT EXISTS embeddings_files (
|
|
@@ -683,14 +878,14 @@ CREATE TABLE IF NOT EXISTS embeddings_files (
|
|
|
683
878
|
);
|
|
684
879
|
|
|
685
880
|
-- Index for entity lookup (get all embeddings for entity)
|
|
686
|
-
CREATE INDEX
|
|
881
|
+
CREATE INDEX idx_embeddings_files_entity ON embeddings_files (entity_id);
|
|
687
882
|
|
|
688
883
|
-- Index for field + provider lookup
|
|
689
|
-
CREATE INDEX
|
|
884
|
+
CREATE INDEX idx_embeddings_files_field_provider ON embeddings_files (field_name, provider);
|
|
690
885
|
|
|
691
886
|
-- HNSW index for vector similarity search (created in background)
|
|
692
887
|
-- Note: This will be created by background thread after data load
|
|
693
|
-
-- CREATE INDEX
|
|
888
|
+
-- CREATE INDEX idx_embeddings_files_vector_hnsw ON embeddings_files
|
|
694
889
|
-- USING hnsw (embedding vector_cosine_ops);
|
|
695
890
|
|
|
696
891
|
-- KV_STORE trigger for files
|
|
@@ -715,7 +910,7 @@ BEGIN
|
|
|
715
910
|
graph_edges,
|
|
716
911
|
updated_at
|
|
717
912
|
) VALUES (
|
|
718
|
-
NEW.
|
|
913
|
+
NEW.id::VARCHAR,
|
|
719
914
|
'files',
|
|
720
915
|
NEW.id,
|
|
721
916
|
NEW.tenant_id,
|
|
@@ -752,7 +947,7 @@ CREATE TABLE IF NOT EXISTS ontologies (
|
|
|
752
947
|
tenant_id VARCHAR(100) NOT NULL,
|
|
753
948
|
user_id VARCHAR(256),
|
|
754
949
|
name VARCHAR(256) NOT NULL,
|
|
755
|
-
file_id
|
|
950
|
+
file_id UUID NOT NULL,
|
|
756
951
|
agent_schema_id VARCHAR(256) NOT NULL,
|
|
757
952
|
provider_name VARCHAR(256) NOT NULL,
|
|
758
953
|
model_name VARCHAR(256) NOT NULL,
|
|
@@ -768,11 +963,11 @@ CREATE TABLE IF NOT EXISTS ontologies (
|
|
|
768
963
|
tags TEXT[] DEFAULT ARRAY[]::TEXT[]
|
|
769
964
|
);
|
|
770
965
|
|
|
771
|
-
CREATE INDEX
|
|
772
|
-
CREATE INDEX
|
|
773
|
-
CREATE INDEX
|
|
774
|
-
CREATE INDEX
|
|
775
|
-
CREATE INDEX
|
|
966
|
+
CREATE INDEX idx_ontologies_tenant ON ontologies (tenant_id);
|
|
967
|
+
CREATE INDEX idx_ontologies_user ON ontologies (user_id);
|
|
968
|
+
CREATE INDEX idx_ontologies_graph_edges ON ontologies USING GIN (graph_edges);
|
|
969
|
+
CREATE INDEX idx_ontologies_metadata ON ontologies USING GIN (metadata);
|
|
970
|
+
CREATE INDEX idx_ontologies_tags ON ontologies USING GIN (tags);
|
|
776
971
|
|
|
777
972
|
-- KV_STORE trigger for ontologies
|
|
778
973
|
-- Trigger function to maintain KV_STORE for ontologies
|
|
@@ -796,7 +991,7 @@ BEGIN
|
|
|
796
991
|
graph_edges,
|
|
797
992
|
updated_at
|
|
798
993
|
) VALUES (
|
|
799
|
-
NEW.
|
|
994
|
+
NEW.id::VARCHAR,
|
|
800
995
|
'ontologies',
|
|
801
996
|
NEW.id,
|
|
802
997
|
NEW.tenant_id,
|
|
@@ -850,11 +1045,11 @@ CREATE TABLE IF NOT EXISTS ontology_configs (
|
|
|
850
1045
|
tags TEXT[] DEFAULT ARRAY[]::TEXT[]
|
|
851
1046
|
);
|
|
852
1047
|
|
|
853
|
-
CREATE INDEX
|
|
854
|
-
CREATE INDEX
|
|
855
|
-
CREATE INDEX
|
|
856
|
-
CREATE INDEX
|
|
857
|
-
CREATE INDEX
|
|
1048
|
+
CREATE INDEX idx_ontology_configs_tenant ON ontology_configs (tenant_id);
|
|
1049
|
+
CREATE INDEX idx_ontology_configs_user ON ontology_configs (user_id);
|
|
1050
|
+
CREATE INDEX idx_ontology_configs_graph_edges ON ontology_configs USING GIN (graph_edges);
|
|
1051
|
+
CREATE INDEX idx_ontology_configs_metadata ON ontology_configs USING GIN (metadata);
|
|
1052
|
+
CREATE INDEX idx_ontology_configs_tags ON ontology_configs USING GIN (tags);
|
|
858
1053
|
|
|
859
1054
|
-- Embeddings for ontology_configs
|
|
860
1055
|
CREATE TABLE IF NOT EXISTS embeddings_ontology_configs (
|
|
@@ -872,14 +1067,14 @@ CREATE TABLE IF NOT EXISTS embeddings_ontology_configs (
|
|
|
872
1067
|
);
|
|
873
1068
|
|
|
874
1069
|
-- Index for entity lookup (get all embeddings for entity)
|
|
875
|
-
CREATE INDEX
|
|
1070
|
+
CREATE INDEX idx_embeddings_ontology_configs_entity ON embeddings_ontology_configs (entity_id);
|
|
876
1071
|
|
|
877
1072
|
-- Index for field + provider lookup
|
|
878
|
-
CREATE INDEX
|
|
1073
|
+
CREATE INDEX idx_embeddings_ontology_configs_field_provider ON embeddings_ontology_configs (field_name, provider);
|
|
879
1074
|
|
|
880
1075
|
-- HNSW index for vector similarity search (created in background)
|
|
881
1076
|
-- Note: This will be created by background thread after data load
|
|
882
|
-
-- CREATE INDEX
|
|
1077
|
+
-- CREATE INDEX idx_embeddings_ontology_configs_vector_hnsw ON embeddings_ontology_configs
|
|
883
1078
|
-- USING hnsw (embedding vector_cosine_ops);
|
|
884
1079
|
|
|
885
1080
|
-- KV_STORE trigger for ontology_configs
|
|
@@ -904,7 +1099,7 @@ BEGIN
|
|
|
904
1099
|
graph_edges,
|
|
905
1100
|
updated_at
|
|
906
1101
|
) VALUES (
|
|
907
|
-
NEW.
|
|
1102
|
+
NEW.id::VARCHAR,
|
|
908
1103
|
'ontology_configs',
|
|
909
1104
|
NEW.id,
|
|
910
1105
|
NEW.tenant_id,
|
|
@@ -932,6 +1127,111 @@ CREATE TRIGGER trg_ontology_configs_kv_store
|
|
|
932
1127
|
AFTER INSERT OR UPDATE OR DELETE ON ontology_configs
|
|
933
1128
|
FOR EACH ROW EXECUTE FUNCTION fn_ontology_configs_kv_store_upsert();
|
|
934
1129
|
|
|
1130
|
+
-- ======================================================================
|
|
1131
|
+
-- DOMAIN_RESOURCES (Model: DomainResource)
|
|
1132
|
+
-- ======================================================================
|
|
1133
|
+
|
|
1134
|
+
CREATE TABLE IF NOT EXISTS domain_resources (
|
|
1135
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
1136
|
+
tenant_id VARCHAR(100) NOT NULL,
|
|
1137
|
+
user_id VARCHAR(256),
|
|
1138
|
+
name VARCHAR(256),
|
|
1139
|
+
uri VARCHAR(256),
|
|
1140
|
+
ordinal INTEGER,
|
|
1141
|
+
content TEXT,
|
|
1142
|
+
timestamp TIMESTAMP,
|
|
1143
|
+
category VARCHAR(256),
|
|
1144
|
+
related_entities JSONB DEFAULT '{}'::jsonb,
|
|
1145
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
1146
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
1147
|
+
deleted_at TIMESTAMP,
|
|
1148
|
+
graph_edges JSONB DEFAULT '[]'::jsonb,
|
|
1149
|
+
metadata JSONB DEFAULT '{}'::jsonb,
|
|
1150
|
+
tags TEXT[] DEFAULT ARRAY[]::TEXT[]
|
|
1151
|
+
);
|
|
1152
|
+
|
|
1153
|
+
CREATE INDEX idx_domain_resources_tenant ON domain_resources (tenant_id);
|
|
1154
|
+
CREATE INDEX idx_domain_resources_user ON domain_resources (user_id);
|
|
1155
|
+
CREATE INDEX idx_domain_resources_graph_edges ON domain_resources USING GIN (graph_edges);
|
|
1156
|
+
CREATE INDEX idx_domain_resources_metadata ON domain_resources USING GIN (metadata);
|
|
1157
|
+
CREATE INDEX idx_domain_resources_tags ON domain_resources USING GIN (tags);
|
|
1158
|
+
|
|
1159
|
+
-- Embeddings for domain_resources
|
|
1160
|
+
CREATE TABLE IF NOT EXISTS embeddings_domain_resources (
|
|
1161
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
1162
|
+
entity_id UUID NOT NULL REFERENCES domain_resources(id) ON DELETE CASCADE,
|
|
1163
|
+
field_name VARCHAR(100) NOT NULL,
|
|
1164
|
+
provider VARCHAR(50) NOT NULL DEFAULT 'openai',
|
|
1165
|
+
model VARCHAR(100) NOT NULL DEFAULT 'text-embedding-3-small',
|
|
1166
|
+
embedding vector(1536) NOT NULL,
|
|
1167
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
1168
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
1169
|
+
|
|
1170
|
+
-- Unique: one embedding per entity per field per provider
|
|
1171
|
+
UNIQUE (entity_id, field_name, provider)
|
|
1172
|
+
);
|
|
1173
|
+
|
|
1174
|
+
-- Index for entity lookup (get all embeddings for entity)
|
|
1175
|
+
CREATE INDEX idx_embeddings_domain_resources_entity ON embeddings_domain_resources (entity_id);
|
|
1176
|
+
|
|
1177
|
+
-- Index for field + provider lookup
|
|
1178
|
+
CREATE INDEX idx_embeddings_domain_resources_field_provider ON embeddings_domain_resources (field_name, provider);
|
|
1179
|
+
|
|
1180
|
+
-- HNSW index for vector similarity search (created in background)
|
|
1181
|
+
-- Note: This will be created by background thread after data load
|
|
1182
|
+
-- CREATE INDEX idx_embeddings_domain_resources_vector_hnsw ON embeddings_domain_resources
|
|
1183
|
+
-- USING hnsw (embedding vector_cosine_ops);
|
|
1184
|
+
|
|
1185
|
+
-- KV_STORE trigger for domain_resources
|
|
1186
|
+
-- Trigger function to maintain KV_STORE for domain_resources
|
|
1187
|
+
CREATE OR REPLACE FUNCTION fn_domain_resources_kv_store_upsert()
|
|
1188
|
+
RETURNS TRIGGER AS $$
|
|
1189
|
+
BEGIN
|
|
1190
|
+
IF (TG_OP = 'DELETE') THEN
|
|
1191
|
+
-- Remove from KV_STORE on delete
|
|
1192
|
+
DELETE FROM kv_store
|
|
1193
|
+
WHERE entity_id = OLD.id;
|
|
1194
|
+
RETURN OLD;
|
|
1195
|
+
ELSIF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
|
|
1196
|
+
-- Upsert to KV_STORE (O(1) lookup by entity_key)
|
|
1197
|
+
INSERT INTO kv_store (
|
|
1198
|
+
entity_key,
|
|
1199
|
+
entity_type,
|
|
1200
|
+
entity_id,
|
|
1201
|
+
tenant_id,
|
|
1202
|
+
user_id,
|
|
1203
|
+
metadata,
|
|
1204
|
+
graph_edges,
|
|
1205
|
+
updated_at
|
|
1206
|
+
) VALUES (
|
|
1207
|
+
NEW.name::VARCHAR,
|
|
1208
|
+
'domain_resources',
|
|
1209
|
+
NEW.id,
|
|
1210
|
+
NEW.tenant_id,
|
|
1211
|
+
NEW.user_id,
|
|
1212
|
+
NEW.metadata,
|
|
1213
|
+
COALESCE(NEW.graph_edges, '[]'::jsonb),
|
|
1214
|
+
CURRENT_TIMESTAMP
|
|
1215
|
+
)
|
|
1216
|
+
ON CONFLICT (tenant_id, entity_key)
|
|
1217
|
+
DO UPDATE SET
|
|
1218
|
+
entity_id = EXCLUDED.entity_id,
|
|
1219
|
+
user_id = EXCLUDED.user_id,
|
|
1220
|
+
metadata = EXCLUDED.metadata,
|
|
1221
|
+
graph_edges = EXCLUDED.graph_edges,
|
|
1222
|
+
updated_at = CURRENT_TIMESTAMP;
|
|
1223
|
+
|
|
1224
|
+
RETURN NEW;
|
|
1225
|
+
END IF;
|
|
1226
|
+
END;
|
|
1227
|
+
$$ LANGUAGE plpgsql;
|
|
1228
|
+
|
|
1229
|
+
-- Create trigger
|
|
1230
|
+
DROP TRIGGER IF EXISTS trg_domain_resources_kv_store ON domain_resources;
|
|
1231
|
+
CREATE TRIGGER trg_domain_resources_kv_store
|
|
1232
|
+
AFTER INSERT OR UPDATE OR DELETE ON domain_resources
|
|
1233
|
+
FOR EACH ROW EXECUTE FUNCTION fn_domain_resources_kv_store_upsert();
|
|
1234
|
+
|
|
935
1235
|
-- ======================================================================
|
|
936
1236
|
-- SCHEMAS (Model: Schema)
|
|
937
1237
|
-- ======================================================================
|
|
@@ -954,11 +1254,11 @@ CREATE TABLE IF NOT EXISTS schemas (
|
|
|
954
1254
|
tags TEXT[] DEFAULT ARRAY[]::TEXT[]
|
|
955
1255
|
);
|
|
956
1256
|
|
|
957
|
-
CREATE INDEX
|
|
958
|
-
CREATE INDEX
|
|
959
|
-
CREATE INDEX
|
|
960
|
-
CREATE INDEX
|
|
961
|
-
CREATE INDEX
|
|
1257
|
+
CREATE INDEX idx_schemas_tenant ON schemas (tenant_id);
|
|
1258
|
+
CREATE INDEX idx_schemas_user ON schemas (user_id);
|
|
1259
|
+
CREATE INDEX idx_schemas_graph_edges ON schemas USING GIN (graph_edges);
|
|
1260
|
+
CREATE INDEX idx_schemas_metadata ON schemas USING GIN (metadata);
|
|
1261
|
+
CREATE INDEX idx_schemas_tags ON schemas USING GIN (tags);
|
|
962
1262
|
|
|
963
1263
|
-- Embeddings for schemas
|
|
964
1264
|
CREATE TABLE IF NOT EXISTS embeddings_schemas (
|
|
@@ -976,14 +1276,14 @@ CREATE TABLE IF NOT EXISTS embeddings_schemas (
|
|
|
976
1276
|
);
|
|
977
1277
|
|
|
978
1278
|
-- Index for entity lookup (get all embeddings for entity)
|
|
979
|
-
CREATE INDEX
|
|
1279
|
+
CREATE INDEX idx_embeddings_schemas_entity ON embeddings_schemas (entity_id);
|
|
980
1280
|
|
|
981
1281
|
-- Index for field + provider lookup
|
|
982
|
-
CREATE INDEX
|
|
1282
|
+
CREATE INDEX idx_embeddings_schemas_field_provider ON embeddings_schemas (field_name, provider);
|
|
983
1283
|
|
|
984
1284
|
-- HNSW index for vector similarity search (created in background)
|
|
985
1285
|
-- Note: This will be created by background thread after data load
|
|
986
|
-
-- CREATE INDEX
|
|
1286
|
+
-- CREATE INDEX idx_embeddings_schemas_vector_hnsw ON embeddings_schemas
|
|
987
1287
|
-- USING hnsw (embedding vector_cosine_ops);
|
|
988
1288
|
|
|
989
1289
|
-- KV_STORE trigger for schemas
|
|
@@ -1008,7 +1308,7 @@ BEGIN
|
|
|
1008
1308
|
graph_edges,
|
|
1009
1309
|
updated_at
|
|
1010
1310
|
) VALUES (
|
|
1011
|
-
NEW.
|
|
1311
|
+
NEW.id::VARCHAR,
|
|
1012
1312
|
'schemas',
|
|
1013
1313
|
NEW.id,
|
|
1014
1314
|
NEW.tenant_id,
|
|
@@ -1036,6 +1336,185 @@ CREATE TRIGGER trg_schemas_kv_store
|
|
|
1036
1336
|
AFTER INSERT OR UPDATE OR DELETE ON schemas
|
|
1037
1337
|
FOR EACH ROW EXECUTE FUNCTION fn_schemas_kv_store_upsert();
|
|
1038
1338
|
|
|
1339
|
+
-- ======================================================================
|
|
1340
|
+
-- SHARED_SESSIONS (Session sharing between users)
|
|
1341
|
+
-- ======================================================================
|
|
1342
|
+
-- Lightweight linking table for session sharing. NOT a CoreModel - no
|
|
1343
|
+
-- graph edges, metadata, or embeddings. Just tracks who shared what with whom.
|
|
1344
|
+
--
|
|
1345
|
+
-- See: src/rem/models/entities/shared_session.py for full documentation
|
|
1346
|
+
|
|
1347
|
+
CREATE TABLE IF NOT EXISTS shared_sessions (
|
|
1348
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
1349
|
+
session_id VARCHAR(256) NOT NULL,
|
|
1350
|
+
owner_user_id VARCHAR(256) NOT NULL,
|
|
1351
|
+
shared_with_user_id VARCHAR(256) NOT NULL,
|
|
1352
|
+
tenant_id VARCHAR(100) NOT NULL DEFAULT 'default',
|
|
1353
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
1354
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
1355
|
+
deleted_at TIMESTAMP,
|
|
1356
|
+
|
|
1357
|
+
-- Prevent duplicate shares (same session, same recipient, active only)
|
|
1358
|
+
CONSTRAINT uq_active_share UNIQUE NULLS NOT DISTINCT (
|
|
1359
|
+
tenant_id, session_id, owner_user_id, shared_with_user_id, deleted_at
|
|
1360
|
+
)
|
|
1361
|
+
);
|
|
1362
|
+
|
|
1363
|
+
-- Index for finding shares by recipient (who is sharing WITH me)
|
|
1364
|
+
CREATE INDEX IF NOT EXISTS idx_shared_sessions_recipient
|
|
1365
|
+
ON shared_sessions (tenant_id, shared_with_user_id)
|
|
1366
|
+
WHERE deleted_at IS NULL;
|
|
1367
|
+
|
|
1368
|
+
-- Index for finding shares by owner (what have I shared)
|
|
1369
|
+
CREATE INDEX IF NOT EXISTS idx_shared_sessions_owner
|
|
1370
|
+
ON shared_sessions (tenant_id, owner_user_id)
|
|
1371
|
+
WHERE deleted_at IS NULL;
|
|
1372
|
+
|
|
1373
|
+
-- Index for finding shares by session
|
|
1374
|
+
CREATE INDEX IF NOT EXISTS idx_shared_sessions_session
|
|
1375
|
+
ON shared_sessions (tenant_id, session_id)
|
|
1376
|
+
WHERE deleted_at IS NULL;
|
|
1377
|
+
|
|
1378
|
+
-- Aggregation function: Get users sharing with me
|
|
1379
|
+
CREATE OR REPLACE FUNCTION fn_get_shared_with_me(
|
|
1380
|
+
p_tenant_id VARCHAR(100),
|
|
1381
|
+
p_user_id VARCHAR(256),
|
|
1382
|
+
p_limit INTEGER DEFAULT 50,
|
|
1383
|
+
p_offset INTEGER DEFAULT 0
|
|
1384
|
+
)
|
|
1385
|
+
RETURNS TABLE (
|
|
1386
|
+
user_id VARCHAR(256),
|
|
1387
|
+
name VARCHAR(256),
|
|
1388
|
+
email VARCHAR(256),
|
|
1389
|
+
message_count BIGINT,
|
|
1390
|
+
session_count BIGINT,
|
|
1391
|
+
first_message_at TIMESTAMP,
|
|
1392
|
+
last_message_at TIMESTAMP
|
|
1393
|
+
) AS $$
|
|
1394
|
+
BEGIN
|
|
1395
|
+
RETURN QUERY
|
|
1396
|
+
WITH shared_with_me AS (
|
|
1397
|
+
SELECT DISTINCT
|
|
1398
|
+
ss.session_id,
|
|
1399
|
+
ss.owner_user_id
|
|
1400
|
+
FROM shared_sessions ss
|
|
1401
|
+
WHERE ss.tenant_id = p_tenant_id
|
|
1402
|
+
AND ss.shared_with_user_id = p_user_id
|
|
1403
|
+
AND ss.deleted_at IS NULL
|
|
1404
|
+
),
|
|
1405
|
+
message_stats AS (
|
|
1406
|
+
SELECT
|
|
1407
|
+
swm.owner_user_id,
|
|
1408
|
+
COUNT(DISTINCT m.id) AS msg_count,
|
|
1409
|
+
COUNT(DISTINCT m.session_id) AS sess_count,
|
|
1410
|
+
MIN(m.created_at) AS first_msg,
|
|
1411
|
+
MAX(m.created_at) AS last_msg
|
|
1412
|
+
FROM shared_with_me swm
|
|
1413
|
+
LEFT JOIN messages m ON m.session_id = swm.session_id
|
|
1414
|
+
AND m.tenant_id = p_tenant_id
|
|
1415
|
+
AND m.deleted_at IS NULL
|
|
1416
|
+
GROUP BY swm.owner_user_id
|
|
1417
|
+
)
|
|
1418
|
+
SELECT
|
|
1419
|
+
ms.owner_user_id AS user_id,
|
|
1420
|
+
u.name,
|
|
1421
|
+
u.email,
|
|
1422
|
+
COALESCE(ms.msg_count, 0) AS message_count,
|
|
1423
|
+
COALESCE(ms.sess_count, 0) AS session_count,
|
|
1424
|
+
ms.first_msg AS first_message_at,
|
|
1425
|
+
ms.last_msg AS last_message_at
|
|
1426
|
+
FROM message_stats ms
|
|
1427
|
+
LEFT JOIN users u ON u.user_id = ms.owner_user_id
|
|
1428
|
+
AND u.tenant_id = p_tenant_id
|
|
1429
|
+
AND u.deleted_at IS NULL
|
|
1430
|
+
ORDER BY ms.last_msg DESC NULLS LAST, ms.msg_count DESC
|
|
1431
|
+
LIMIT p_limit
|
|
1432
|
+
OFFSET p_offset;
|
|
1433
|
+
END;
|
|
1434
|
+
$$ LANGUAGE plpgsql;
|
|
1435
|
+
|
|
1436
|
+
-- Count function for pagination
|
|
1437
|
+
CREATE OR REPLACE FUNCTION fn_count_shared_with_me(
|
|
1438
|
+
p_tenant_id VARCHAR(100),
|
|
1439
|
+
p_user_id VARCHAR(256)
|
|
1440
|
+
)
|
|
1441
|
+
RETURNS BIGINT AS $$
|
|
1442
|
+
BEGIN
|
|
1443
|
+
RETURN (
|
|
1444
|
+
SELECT COUNT(DISTINCT owner_user_id)
|
|
1445
|
+
FROM shared_sessions
|
|
1446
|
+
WHERE tenant_id = p_tenant_id
|
|
1447
|
+
AND shared_with_user_id = p_user_id
|
|
1448
|
+
AND deleted_at IS NULL
|
|
1449
|
+
);
|
|
1450
|
+
END;
|
|
1451
|
+
$$ LANGUAGE plpgsql;
|
|
1452
|
+
|
|
1453
|
+
-- Get messages from sessions shared by a specific owner
|
|
1454
|
+
CREATE OR REPLACE FUNCTION fn_get_shared_messages(
|
|
1455
|
+
p_tenant_id VARCHAR(100),
|
|
1456
|
+
p_recipient_user_id VARCHAR(256),
|
|
1457
|
+
p_owner_user_id VARCHAR(256),
|
|
1458
|
+
p_limit INTEGER DEFAULT 50,
|
|
1459
|
+
p_offset INTEGER DEFAULT 0
|
|
1460
|
+
)
|
|
1461
|
+
RETURNS TABLE (
|
|
1462
|
+
id UUID,
|
|
1463
|
+
content TEXT,
|
|
1464
|
+
message_type VARCHAR(256),
|
|
1465
|
+
session_id VARCHAR(256),
|
|
1466
|
+
model VARCHAR(256),
|
|
1467
|
+
token_count INTEGER,
|
|
1468
|
+
created_at TIMESTAMP,
|
|
1469
|
+
metadata JSONB
|
|
1470
|
+
) AS $$
|
|
1471
|
+
BEGIN
|
|
1472
|
+
RETURN QUERY
|
|
1473
|
+
SELECT
|
|
1474
|
+
m.id,
|
|
1475
|
+
m.content,
|
|
1476
|
+
m.message_type,
|
|
1477
|
+
m.session_id,
|
|
1478
|
+
m.model,
|
|
1479
|
+
m.token_count,
|
|
1480
|
+
m.created_at,
|
|
1481
|
+
m.metadata
|
|
1482
|
+
FROM messages m
|
|
1483
|
+
INNER JOIN shared_sessions ss ON ss.session_id = m.session_id
|
|
1484
|
+
AND ss.tenant_id = m.tenant_id
|
|
1485
|
+
AND ss.deleted_at IS NULL
|
|
1486
|
+
WHERE m.tenant_id = p_tenant_id
|
|
1487
|
+
AND ss.shared_with_user_id = p_recipient_user_id
|
|
1488
|
+
AND ss.owner_user_id = p_owner_user_id
|
|
1489
|
+
AND m.deleted_at IS NULL
|
|
1490
|
+
ORDER BY m.created_at DESC
|
|
1491
|
+
LIMIT p_limit
|
|
1492
|
+
OFFSET p_offset;
|
|
1493
|
+
END;
|
|
1494
|
+
$$ LANGUAGE plpgsql;
|
|
1495
|
+
|
|
1496
|
+
-- Count shared messages for pagination
|
|
1497
|
+
CREATE OR REPLACE FUNCTION fn_count_shared_messages(
|
|
1498
|
+
p_tenant_id VARCHAR(100),
|
|
1499
|
+
p_recipient_user_id VARCHAR(256),
|
|
1500
|
+
p_owner_user_id VARCHAR(256)
|
|
1501
|
+
)
|
|
1502
|
+
RETURNS BIGINT AS $$
|
|
1503
|
+
BEGIN
|
|
1504
|
+
RETURN (
|
|
1505
|
+
SELECT COUNT(m.id)
|
|
1506
|
+
FROM messages m
|
|
1507
|
+
INNER JOIN shared_sessions ss ON ss.session_id = m.session_id
|
|
1508
|
+
AND ss.tenant_id = m.tenant_id
|
|
1509
|
+
AND ss.deleted_at IS NULL
|
|
1510
|
+
WHERE m.tenant_id = p_tenant_id
|
|
1511
|
+
AND ss.shared_with_user_id = p_recipient_user_id
|
|
1512
|
+
AND ss.owner_user_id = p_owner_user_id
|
|
1513
|
+
AND m.deleted_at IS NULL
|
|
1514
|
+
);
|
|
1515
|
+
END;
|
|
1516
|
+
$$ LANGUAGE plpgsql;
|
|
1517
|
+
|
|
1039
1518
|
-- ============================================================================
|
|
1040
1519
|
-- RECORD MIGRATION
|
|
1041
1520
|
-- ============================================================================
|
|
@@ -1049,8 +1528,10 @@ SET applied_at = CURRENT_TIMESTAMP,
|
|
|
1049
1528
|
DO $$
|
|
1050
1529
|
BEGIN
|
|
1051
1530
|
RAISE NOTICE '============================================================';
|
|
1052
|
-
RAISE NOTICE 'REM Model Schema Applied:
|
|
1531
|
+
RAISE NOTICE 'REM Model Schema Applied: 14 tables';
|
|
1053
1532
|
RAISE NOTICE '============================================================';
|
|
1533
|
+
RAISE NOTICE ' ✓ domain_resources (1 embeddable fields)';
|
|
1534
|
+
RAISE NOTICE ' ✓ feedbacks';
|
|
1054
1535
|
RAISE NOTICE ' ✓ files (1 embeddable fields)';
|
|
1055
1536
|
RAISE NOTICE ' ✓ image_resources (1 embeddable fields)';
|
|
1056
1537
|
RAISE NOTICE ' ✓ messages (1 embeddable fields)';
|
|
@@ -1060,143 +1541,11 @@ BEGIN
|
|
|
1060
1541
|
RAISE NOTICE ' ✓ persons';
|
|
1061
1542
|
RAISE NOTICE ' ✓ resources (1 embeddable fields)';
|
|
1062
1543
|
RAISE NOTICE ' ✓ schemas (1 embeddable fields)';
|
|
1544
|
+
RAISE NOTICE ' ✓ sessions (1 embeddable fields)';
|
|
1545
|
+
RAISE NOTICE ' ✓ shared_sessions (session sharing)';
|
|
1063
1546
|
RAISE NOTICE ' ✓ users (1 embeddable fields)';
|
|
1064
1547
|
RAISE NOTICE '';
|
|
1065
1548
|
RAISE NOTICE 'Next: Run background indexes if needed';
|
|
1066
1549
|
RAISE NOTICE ' rem db migrate --background-indexes';
|
|
1067
1550
|
RAISE NOTICE '============================================================';
|
|
1068
|
-
END $$;
|
|
1069
|
-
-- ============================================================================
|
|
1070
|
-
-- REM TRAVERSE (Graph Traversal)
|
|
1071
|
-
-- ============================================================================
|
|
1072
|
-
|
|
1073
|
-
-- REM TRAVERSE: Recursive graph traversal following edges
|
|
1074
|
-
-- Explores graph_edges starting from entity_key up to max_depth
|
|
1075
|
-
-- Uses cached kv_store.graph_edges for fast traversal (no polymorphic view!)
|
|
1076
|
-
-- When keys_only=false, automatically fetches full entity records
|
|
1077
|
-
CREATE OR REPLACE FUNCTION rem_traverse(
|
|
1078
|
-
p_entity_key VARCHAR(255),
|
|
1079
|
-
p_tenant_id VARCHAR(100), -- Backward compat parameter (not used for filtering)
|
|
1080
|
-
p_user_id VARCHAR(100),
|
|
1081
|
-
p_max_depth INTEGER DEFAULT 1,
|
|
1082
|
-
p_rel_type VARCHAR(100) DEFAULT NULL,
|
|
1083
|
-
p_keys_only BOOLEAN DEFAULT FALSE
|
|
1084
|
-
)
|
|
1085
|
-
RETURNS TABLE(
|
|
1086
|
-
depth INTEGER,
|
|
1087
|
-
entity_key VARCHAR(255),
|
|
1088
|
-
entity_type VARCHAR(100),
|
|
1089
|
-
entity_id UUID,
|
|
1090
|
-
rel_type VARCHAR(100),
|
|
1091
|
-
rel_weight REAL,
|
|
1092
|
-
path TEXT[],
|
|
1093
|
-
entity_record JSONB
|
|
1094
|
-
) AS $$
|
|
1095
|
-
DECLARE
|
|
1096
|
-
graph_keys RECORD;
|
|
1097
|
-
entities_by_table JSONB := '{}'::jsonb;
|
|
1098
|
-
table_keys JSONB;
|
|
1099
|
-
BEGIN
|
|
1100
|
-
-- First, build graph structure from KV store
|
|
1101
|
-
FOR graph_keys IN
|
|
1102
|
-
WITH RECURSIVE graph_traversal AS (
|
|
1103
|
-
-- Base case: Find starting entity
|
|
1104
|
-
SELECT
|
|
1105
|
-
0 AS depth,
|
|
1106
|
-
kv.entity_key,
|
|
1107
|
-
kv.entity_type,
|
|
1108
|
-
kv.entity_id,
|
|
1109
|
-
NULL::VARCHAR(100) AS rel_type,
|
|
1110
|
-
NULL::REAL AS rel_weight,
|
|
1111
|
-
ARRAY[kv.entity_key]::TEXT[] AS path
|
|
1112
|
-
FROM kv_store kv
|
|
1113
|
-
WHERE kv.user_id = p_user_id
|
|
1114
|
-
AND kv.entity_key = p_entity_key
|
|
1115
|
-
|
|
1116
|
-
UNION ALL
|
|
1117
|
-
|
|
1118
|
-
-- Recursive case: Follow outbound edges from discovered entities
|
|
1119
|
-
SELECT
|
|
1120
|
-
gt.depth + 1,
|
|
1121
|
-
target_kv.entity_key,
|
|
1122
|
-
target_kv.entity_type,
|
|
1123
|
-
target_kv.entity_id,
|
|
1124
|
-
(edge->>'rel_type')::VARCHAR(100) AS rel_type,
|
|
1125
|
-
COALESCE((edge->>'weight')::REAL, 1.0) AS rel_weight,
|
|
1126
|
-
gt.path || target_kv.entity_key AS path
|
|
1127
|
-
FROM graph_traversal gt
|
|
1128
|
-
-- Join to KV store to get source entity (with cached graph_edges!)
|
|
1129
|
-
JOIN kv_store source_kv ON source_kv.entity_key = gt.entity_key
|
|
1130
|
-
AND source_kv.user_id = p_user_id
|
|
1131
|
-
-- Extract edges directly from cached kv_store.graph_edges (NO polymorphic view!)
|
|
1132
|
-
CROSS JOIN LATERAL jsonb_array_elements(COALESCE(source_kv.graph_edges, '[]'::jsonb)) AS edge
|
|
1133
|
-
-- Lookup target entity in KV store
|
|
1134
|
-
JOIN kv_store target_kv ON target_kv.entity_key = (edge->>'dst')::VARCHAR(255)
|
|
1135
|
-
AND target_kv.user_id = p_user_id
|
|
1136
|
-
WHERE gt.depth < p_max_depth
|
|
1137
|
-
-- Filter by relationship type if specified
|
|
1138
|
-
AND (p_rel_type IS NULL OR (edge->>'rel_type')::VARCHAR(100) = p_rel_type)
|
|
1139
|
-
-- Prevent cycles by checking path
|
|
1140
|
-
AND NOT (target_kv.entity_key = ANY(gt.path))
|
|
1141
|
-
)
|
|
1142
|
-
SELECT DISTINCT ON (entity_key)
|
|
1143
|
-
gt.depth,
|
|
1144
|
-
gt.entity_key,
|
|
1145
|
-
gt.entity_type,
|
|
1146
|
-
gt.entity_id,
|
|
1147
|
-
gt.rel_type,
|
|
1148
|
-
gt.rel_weight,
|
|
1149
|
-
gt.path
|
|
1150
|
-
FROM graph_traversal gt
|
|
1151
|
-
WHERE gt.depth > 0 -- Exclude starting entity
|
|
1152
|
-
ORDER BY gt.entity_key, gt.depth
|
|
1153
|
-
LOOP
|
|
1154
|
-
IF p_keys_only THEN
|
|
1155
|
-
-- Return just graph structure (no entity_record)
|
|
1156
|
-
depth := graph_keys.depth;
|
|
1157
|
-
entity_key := graph_keys.entity_key;
|
|
1158
|
-
entity_type := graph_keys.entity_type;
|
|
1159
|
-
entity_id := graph_keys.entity_id;
|
|
1160
|
-
rel_type := graph_keys.rel_type;
|
|
1161
|
-
rel_weight := graph_keys.rel_weight;
|
|
1162
|
-
path := graph_keys.path;
|
|
1163
|
-
entity_record := NULL;
|
|
1164
|
-
RETURN NEXT;
|
|
1165
|
-
ELSE
|
|
1166
|
-
-- Build JSONB mapping {table: [keys]} for batch fetch
|
|
1167
|
-
IF entities_by_table ? graph_keys.entity_type THEN
|
|
1168
|
-
table_keys := entities_by_table->graph_keys.entity_type;
|
|
1169
|
-
entities_by_table := jsonb_set(
|
|
1170
|
-
entities_by_table,
|
|
1171
|
-
ARRAY[graph_keys.entity_type],
|
|
1172
|
-
table_keys || jsonb_build_array(graph_keys.entity_key)
|
|
1173
|
-
);
|
|
1174
|
-
ELSE
|
|
1175
|
-
entities_by_table := jsonb_set(
|
|
1176
|
-
entities_by_table,
|
|
1177
|
-
ARRAY[graph_keys.entity_type],
|
|
1178
|
-
jsonb_build_array(graph_keys.entity_key)
|
|
1179
|
-
);
|
|
1180
|
-
END IF;
|
|
1181
|
-
END IF;
|
|
1182
|
-
END LOOP;
|
|
1183
|
-
|
|
1184
|
-
-- If keys_only=false, fetch full records using rem_fetch
|
|
1185
|
-
IF NOT p_keys_only AND entities_by_table != '{}'::jsonb THEN
|
|
1186
|
-
RETURN QUERY
|
|
1187
|
-
SELECT
|
|
1188
|
-
NULL::INTEGER AS depth,
|
|
1189
|
-
f.entity_key::VARCHAR(255),
|
|
1190
|
-
f.entity_type::VARCHAR(100),
|
|
1191
|
-
NULL::UUID AS entity_id,
|
|
1192
|
-
NULL::VARCHAR(100) AS rel_type,
|
|
1193
|
-
NULL::REAL AS rel_weight,
|
|
1194
|
-
NULL::TEXT[] AS path,
|
|
1195
|
-
f.entity_record
|
|
1196
|
-
FROM rem_fetch(entities_by_table, p_user_id) f;
|
|
1197
|
-
END IF;
|
|
1198
|
-
END;
|
|
1199
|
-
$$ LANGUAGE plpgsql STABLE;
|
|
1200
|
-
|
|
1201
|
-
COMMENT ON FUNCTION rem_traverse IS
|
|
1202
|
-
'REM TRAVERSE query: Recursive graph traversal using cached kv_store.graph_edges. When keys_only=false (default), automatically fetches full entity records via rem_fetch.';
|
|
1551
|
+
END $$;
|