remdb 0.3.181__py3-none-any.whl → 0.3.223__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.

Files changed (48) hide show
  1. rem/agentic/README.md +262 -2
  2. rem/agentic/context.py +173 -0
  3. rem/agentic/context_builder.py +12 -2
  4. rem/agentic/mcp/tool_wrapper.py +2 -2
  5. rem/agentic/providers/pydantic_ai.py +1 -1
  6. rem/agentic/schema.py +2 -2
  7. rem/api/main.py +1 -1
  8. rem/api/mcp_router/server.py +4 -0
  9. rem/api/mcp_router/tools.py +542 -170
  10. rem/api/routers/admin.py +30 -4
  11. rem/api/routers/auth.py +106 -10
  12. rem/api/routers/chat/completions.py +66 -18
  13. rem/api/routers/chat/sse_events.py +7 -3
  14. rem/api/routers/chat/streaming.py +254 -22
  15. rem/api/routers/common.py +18 -0
  16. rem/api/routers/dev.py +7 -1
  17. rem/api/routers/feedback.py +9 -1
  18. rem/api/routers/messages.py +176 -38
  19. rem/api/routers/models.py +9 -1
  20. rem/api/routers/query.py +12 -1
  21. rem/api/routers/shared_sessions.py +16 -0
  22. rem/auth/jwt.py +19 -4
  23. rem/auth/middleware.py +42 -28
  24. rem/cli/README.md +62 -0
  25. rem/cli/commands/db.py +33 -19
  26. rem/cli/commands/process.py +171 -43
  27. rem/models/entities/ontology.py +18 -20
  28. rem/schemas/agents/rem.yaml +1 -1
  29. rem/services/content/service.py +18 -5
  30. rem/services/postgres/__init__.py +28 -3
  31. rem/services/postgres/diff_service.py +57 -5
  32. rem/services/postgres/programmable_diff_service.py +635 -0
  33. rem/services/postgres/pydantic_to_sqlalchemy.py +2 -2
  34. rem/services/postgres/register_type.py +11 -10
  35. rem/services/postgres/repository.py +14 -4
  36. rem/services/session/__init__.py +8 -1
  37. rem/services/session/compression.py +40 -2
  38. rem/services/session/pydantic_messages.py +276 -0
  39. rem/settings.py +28 -0
  40. rem/sql/migrations/001_install.sql +125 -7
  41. rem/sql/migrations/002_install_models.sql +136 -126
  42. rem/sql/migrations/004_cache_system.sql +7 -275
  43. rem/sql/migrations/migrate_session_id_to_uuid.sql +45 -0
  44. rem/utils/schema_loader.py +6 -6
  45. {remdb-0.3.181.dist-info → remdb-0.3.223.dist-info}/METADATA +1 -1
  46. {remdb-0.3.181.dist-info → remdb-0.3.223.dist-info}/RECORD +48 -44
  47. {remdb-0.3.181.dist-info → remdb-0.3.223.dist-info}/WHEEL +0 -0
  48. {remdb-0.3.181.dist-info → remdb-0.3.223.dist-info}/entry_points.txt +0 -0
@@ -121,18 +121,18 @@ CREATE UNLOGGED TABLE IF NOT EXISTS kv_store (
121
121
  entity_key VARCHAR(255) NOT NULL,
122
122
  entity_type VARCHAR(100) NOT NULL,
123
123
  entity_id UUID NOT NULL,
124
- tenant_id VARCHAR(100) NOT NULL,
124
+ tenant_id VARCHAR(100), -- NULL = public/shared data
125
125
  user_id VARCHAR(100),
126
126
  content_summary TEXT,
127
127
  metadata JSONB DEFAULT '{}',
128
128
  graph_edges JSONB DEFAULT '[]'::jsonb, -- Cached edges for fast graph traversal
129
129
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
130
- updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
131
-
132
- -- Composite primary key: entity_key unique per tenant
133
- PRIMARY KEY (tenant_id, entity_key)
130
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
134
131
  );
135
132
 
133
+ -- Unique constraint on (tenant_id, entity_key) using COALESCE to handle NULL tenant_id
134
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_kv_store_tenant_key ON kv_store (COALESCE(tenant_id, ''), entity_key);
135
+
136
136
  -- Index for user-scoped lookups (when user_id IS NOT NULL)
137
137
  CREATE INDEX IF NOT EXISTS idx_kv_store_user ON kv_store (tenant_id, user_id)
138
138
  WHERE user_id IS NOT NULL;
@@ -173,7 +173,7 @@ COMMENT ON COLUMN kv_store.entity_id IS
173
173
  'UUID from primary table for reverse lookup';
174
174
 
175
175
  COMMENT ON COLUMN kv_store.tenant_id IS
176
- 'Tenant identifier for multi-tenancy isolation';
176
+ 'Tenant identifier for multi-tenancy isolation. NULL = public/shared data visible to all.';
177
177
 
178
178
  COMMENT ON COLUMN kv_store.user_id IS
179
179
  'Optional user scoping. NULL = system-level entity, visible to all users in tenant';
@@ -271,8 +271,12 @@ BEGIN
271
271
  AND kv.entity_key = normalize_key(p_entity_key)
272
272
  LIMIT 1;
273
273
 
274
- -- If not found, return empty
274
+ -- If not found, check if cache is empty and maybe trigger rebuild
275
275
  IF entity_table IS NULL THEN
276
+ -- SELF-HEALING: Check if this is because cache is empty
277
+ IF rem_kv_store_empty(effective_user_id) THEN
278
+ PERFORM maybe_trigger_kv_rebuild(effective_user_id, 'rem_lookup');
279
+ END IF;
276
280
  RETURN;
277
281
  END IF;
278
282
 
@@ -357,6 +361,7 @@ DECLARE
357
361
  entities_by_table JSONB := '{}'::jsonb;
358
362
  table_keys JSONB;
359
363
  effective_user_id VARCHAR(100);
364
+ v_found_any BOOLEAN := FALSE;
360
365
  BEGIN
361
366
  effective_user_id := COALESCE(p_user_id, p_tenant_id);
362
367
 
@@ -373,6 +378,7 @@ BEGIN
373
378
  ORDER BY sim_score DESC
374
379
  LIMIT p_limit
375
380
  LOOP
381
+ v_found_any := TRUE;
376
382
  -- Build JSONB mapping {table: [keys]}
377
383
  IF entities_by_table ? kv_matches.entity_type THEN
378
384
  table_keys := entities_by_table->kv_matches.entity_type;
@@ -390,6 +396,11 @@ BEGIN
390
396
  END IF;
391
397
  END LOOP;
392
398
 
399
+ -- SELF-HEALING: If no matches and cache is empty, trigger rebuild
400
+ IF NOT v_found_any AND rem_kv_store_empty(effective_user_id) THEN
401
+ PERFORM maybe_trigger_kv_rebuild(effective_user_id, 'rem_fuzzy');
402
+ END IF;
403
+
393
404
  -- Fetch full records using rem_fetch (which now supports NULL user_id)
394
405
  RETURN QUERY
395
406
  SELECT
@@ -436,9 +447,25 @@ DECLARE
436
447
  entities_by_table JSONB := '{}'::jsonb;
437
448
  table_keys JSONB;
438
449
  effective_user_id VARCHAR(100);
450
+ v_found_start BOOLEAN := FALSE;
439
451
  BEGIN
440
452
  effective_user_id := COALESCE(p_user_id, p_tenant_id);
441
453
 
454
+ -- Check if start entity exists in kv_store
455
+ SELECT TRUE INTO v_found_start
456
+ FROM kv_store kv
457
+ WHERE (kv.user_id = effective_user_id OR kv.user_id IS NULL)
458
+ AND kv.entity_key = normalize_key(p_entity_key)
459
+ LIMIT 1;
460
+
461
+ -- SELF-HEALING: If start not found and cache is empty, trigger rebuild
462
+ IF NOT COALESCE(v_found_start, FALSE) THEN
463
+ IF rem_kv_store_empty(effective_user_id) THEN
464
+ PERFORM maybe_trigger_kv_rebuild(effective_user_id, 'rem_traverse');
465
+ END IF;
466
+ RETURN;
467
+ END IF;
468
+
442
469
  FOR graph_keys IN
443
470
  WITH RECURSIVE graph_traversal AS (
444
471
  -- Base case: Find starting entity (user-owned OR public)
@@ -789,6 +816,97 @@ $$ LANGUAGE plpgsql STABLE;
789
816
  COMMENT ON FUNCTION fn_get_shared_messages IS
790
817
  'Get messages from sessions shared by a specific user with the recipient.';
791
818
 
819
+ -- ============================================================================
820
+ -- SESSIONS WITH USER INFO
821
+ -- ============================================================================
822
+ -- Function to list sessions with user details (name, email) for admin views
823
+
824
+ -- List sessions with user info, CTE pagination
825
+ -- Note: messages.session_id stores the session UUID (sessions.id)
826
+ CREATE OR REPLACE FUNCTION fn_list_sessions_with_user(
827
+ p_user_id VARCHAR(256) DEFAULT NULL, -- Filter by user_id (NULL = all users, admin only)
828
+ p_user_name VARCHAR(256) DEFAULT NULL, -- Filter by user name (partial match, admin only)
829
+ p_user_email VARCHAR(256) DEFAULT NULL, -- Filter by user email (partial match, admin only)
830
+ p_mode VARCHAR(50) DEFAULT NULL, -- Filter by session mode
831
+ p_page INTEGER DEFAULT 1,
832
+ p_page_size INTEGER DEFAULT 50
833
+ )
834
+ RETURNS TABLE(
835
+ id UUID,
836
+ name VARCHAR(256),
837
+ mode TEXT,
838
+ description TEXT,
839
+ user_id VARCHAR(256),
840
+ user_name VARCHAR(256),
841
+ user_email VARCHAR(256),
842
+ message_count INTEGER,
843
+ total_tokens INTEGER,
844
+ created_at TIMESTAMP,
845
+ updated_at TIMESTAMP,
846
+ metadata JSONB,
847
+ total_count BIGINT
848
+ ) AS $$
849
+ BEGIN
850
+ RETURN QUERY
851
+ WITH session_msg_counts AS (
852
+ -- Count messages per session (joining on session UUID)
853
+ SELECT
854
+ m.session_id,
855
+ COUNT(*)::INTEGER as actual_message_count
856
+ FROM messages m
857
+ GROUP BY m.session_id
858
+ ),
859
+ filtered_sessions AS (
860
+ SELECT
861
+ s.id,
862
+ s.name,
863
+ s.mode,
864
+ s.description,
865
+ s.user_id,
866
+ COALESCE(u.name, s.user_id)::VARCHAR(256) AS user_name,
867
+ u.email::VARCHAR(256) AS user_email,
868
+ COALESCE(mc.actual_message_count, 0) AS message_count,
869
+ s.total_tokens,
870
+ s.created_at,
871
+ s.updated_at,
872
+ s.metadata
873
+ FROM sessions s
874
+ LEFT JOIN users u ON u.id::text = s.user_id
875
+ LEFT JOIN session_msg_counts mc ON mc.session_id = s.id::text
876
+ WHERE s.deleted_at IS NULL
877
+ AND (p_user_id IS NULL OR s.user_id = p_user_id)
878
+ AND (p_user_name IS NULL OR u.name ILIKE '%' || p_user_name || '%')
879
+ AND (p_user_email IS NULL OR u.email ILIKE '%' || p_user_email || '%')
880
+ AND (p_mode IS NULL OR s.mode = p_mode)
881
+ ),
882
+ counted AS (
883
+ SELECT *, COUNT(*) OVER () AS total_count
884
+ FROM filtered_sessions
885
+ )
886
+ SELECT
887
+ c.id,
888
+ c.name,
889
+ c.mode,
890
+ c.description,
891
+ c.user_id,
892
+ c.user_name,
893
+ c.user_email,
894
+ c.message_count,
895
+ c.total_tokens,
896
+ c.created_at,
897
+ c.updated_at,
898
+ c.metadata,
899
+ c.total_count
900
+ FROM counted c
901
+ ORDER BY c.created_at DESC
902
+ LIMIT p_page_size
903
+ OFFSET (p_page - 1) * p_page_size;
904
+ END;
905
+ $$ LANGUAGE plpgsql STABLE;
906
+
907
+ COMMENT ON FUNCTION fn_list_sessions_with_user IS
908
+ 'List sessions with user details and computed message counts. Joins messages on session name.';
909
+
792
910
  -- ============================================================================
793
911
  -- RECORD INSTALLATION
794
912
  -- ============================================================================