remdb 0.3.180__py3-none-any.whl → 0.3.258__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 (70) hide show
  1. rem/agentic/README.md +36 -2
  2. rem/agentic/__init__.py +10 -1
  3. rem/agentic/context.py +185 -1
  4. rem/agentic/context_builder.py +56 -35
  5. rem/agentic/mcp/tool_wrapper.py +2 -2
  6. rem/agentic/providers/pydantic_ai.py +303 -111
  7. rem/agentic/schema.py +2 -2
  8. rem/api/main.py +1 -1
  9. rem/api/mcp_router/resources.py +223 -0
  10. rem/api/mcp_router/server.py +4 -0
  11. rem/api/mcp_router/tools.py +608 -166
  12. rem/api/routers/admin.py +30 -4
  13. rem/api/routers/auth.py +219 -20
  14. rem/api/routers/chat/child_streaming.py +393 -0
  15. rem/api/routers/chat/completions.py +77 -40
  16. rem/api/routers/chat/sse_events.py +7 -3
  17. rem/api/routers/chat/streaming.py +381 -291
  18. rem/api/routers/chat/streaming_utils.py +325 -0
  19. rem/api/routers/common.py +18 -0
  20. rem/api/routers/dev.py +7 -1
  21. rem/api/routers/feedback.py +11 -3
  22. rem/api/routers/messages.py +176 -38
  23. rem/api/routers/models.py +9 -1
  24. rem/api/routers/query.py +17 -15
  25. rem/api/routers/shared_sessions.py +16 -0
  26. rem/auth/jwt.py +19 -4
  27. rem/auth/middleware.py +42 -28
  28. rem/cli/README.md +62 -0
  29. rem/cli/commands/ask.py +205 -114
  30. rem/cli/commands/db.py +55 -31
  31. rem/cli/commands/experiments.py +1 -1
  32. rem/cli/commands/process.py +179 -43
  33. rem/cli/commands/query.py +109 -0
  34. rem/cli/commands/session.py +117 -0
  35. rem/cli/main.py +2 -0
  36. rem/models/core/experiment.py +1 -1
  37. rem/models/entities/ontology.py +18 -20
  38. rem/models/entities/session.py +1 -0
  39. rem/schemas/agents/core/agent-builder.yaml +1 -1
  40. rem/schemas/agents/rem.yaml +1 -1
  41. rem/schemas/agents/test_orchestrator.yaml +42 -0
  42. rem/schemas/agents/test_structured_output.yaml +52 -0
  43. rem/services/content/providers.py +151 -49
  44. rem/services/content/service.py +18 -5
  45. rem/services/embeddings/worker.py +26 -12
  46. rem/services/postgres/__init__.py +28 -3
  47. rem/services/postgres/diff_service.py +57 -5
  48. rem/services/postgres/programmable_diff_service.py +635 -0
  49. rem/services/postgres/pydantic_to_sqlalchemy.py +2 -2
  50. rem/services/postgres/register_type.py +11 -10
  51. rem/services/postgres/repository.py +39 -28
  52. rem/services/postgres/schema_generator.py +5 -5
  53. rem/services/postgres/sql_builder.py +6 -5
  54. rem/services/rem/README.md +4 -3
  55. rem/services/rem/parser.py +7 -10
  56. rem/services/rem/service.py +47 -0
  57. rem/services/session/__init__.py +8 -1
  58. rem/services/session/compression.py +47 -5
  59. rem/services/session/pydantic_messages.py +310 -0
  60. rem/services/session/reload.py +2 -1
  61. rem/settings.py +92 -7
  62. rem/sql/migrations/001_install.sql +125 -7
  63. rem/sql/migrations/002_install_models.sql +159 -149
  64. rem/sql/migrations/004_cache_system.sql +10 -276
  65. rem/sql/migrations/migrate_session_id_to_uuid.sql +45 -0
  66. rem/utils/schema_loader.py +180 -120
  67. {remdb-0.3.180.dist-info → remdb-0.3.258.dist-info}/METADATA +7 -6
  68. {remdb-0.3.180.dist-info → remdb-0.3.258.dist-info}/RECORD +70 -61
  69. {remdb-0.3.180.dist-info → remdb-0.3.258.dist-info}/WHEEL +0 -0
  70. {remdb-0.3.180.dist-info → remdb-0.3.258.dist-info}/entry_points.txt +0 -0
@@ -84,6 +84,7 @@ Schema Caching Status:
84
84
  """
85
85
 
86
86
  import importlib.resources
87
+ import time
87
88
  from pathlib import Path
88
89
  from typing import Any, cast
89
90
 
@@ -104,10 +105,32 @@ SCHEMA_SEARCH_PATHS = [
104
105
  # In-memory cache for filesystem schemas (no TTL - immutable)
105
106
  _fs_schema_cache: dict[str, dict[str, Any]] = {}
106
107
 
107
- # Future: Database schema cache (with TTL - mutable)
108
- # Will be used when loading schemas from database (SchemaRepository)
109
- # _db_schema_cache: dict[tuple[str, str], tuple[dict[str, Any], float]] = {}
110
- # _db_schema_ttl: int = 300 # 5 minutes in seconds
108
+ # Database schema cache (with TTL - mutable, supports hot-reload)
109
+ # Cache key: (schema_name, user_id or "public") (schema_dict, timestamp)
110
+ _db_schema_cache: dict[tuple[str, str], tuple[dict[str, Any], float]] = {}
111
+ _db_schema_ttl: int = 300 # 5 minutes in seconds
112
+
113
+
114
+ def _get_cached_db_schema(schema_name: str, user_id: str | None) -> dict[str, Any] | None:
115
+ """Get schema from DB cache if exists and not expired."""
116
+ cache_key = (schema_name.lower(), user_id or "public")
117
+ if cache_key in _db_schema_cache:
118
+ schema, timestamp = _db_schema_cache[cache_key]
119
+ if time.time() - timestamp < _db_schema_ttl:
120
+ logger.debug(f"Schema cache hit: {schema_name} (age: {time.time() - timestamp:.0f}s)")
121
+ return schema
122
+ else:
123
+ # Expired, remove from cache
124
+ del _db_schema_cache[cache_key]
125
+ logger.debug(f"Schema cache expired: {schema_name}")
126
+ return None
127
+
128
+
129
+ def _cache_db_schema(schema_name: str, user_id: str | None, schema: dict[str, Any]) -> None:
130
+ """Add schema to DB cache with current timestamp."""
131
+ cache_key = (schema_name.lower(), user_id or "public")
132
+ _db_schema_cache[cache_key] = (schema, time.time())
133
+ logger.debug(f"Schema cached: {schema_name} (TTL: {_db_schema_ttl}s)")
111
134
 
112
135
 
113
136
  def _load_schema_from_database(schema_name: str, user_id: str) -> dict[str, Any] | None:
@@ -147,15 +170,25 @@ def _load_schema_from_database(schema_name: str, user_id: str) -> dict[str, Any]
147
170
  try:
148
171
  await db.connect()
149
172
 
150
- query = """
151
- SELECT spec FROM schemas
152
- WHERE LOWER(name) = LOWER($1)
153
- AND (user_id = $2 OR user_id = 'system' OR user_id IS NULL)
154
- LIMIT 1
155
- """
156
- logger.debug(f"Executing schema lookup: name={schema_name}, user_id={user_id}")
157
-
158
- row = await db.fetchrow(query, schema_name, user_id)
173
+ # Query for public schemas (user_id IS NULL) and optionally user-specific
174
+ if user_id:
175
+ query = """
176
+ SELECT spec FROM schemas
177
+ WHERE LOWER(name) = LOWER($1)
178
+ AND (user_id = $2 OR user_id = 'system' OR user_id IS NULL)
179
+ LIMIT 1
180
+ """
181
+ row = await db.fetchrow(query, schema_name, user_id)
182
+ else:
183
+ # No user_id - only search public schemas
184
+ query = """
185
+ SELECT spec FROM schemas
186
+ WHERE LOWER(name) = LOWER($1)
187
+ AND (user_id = 'system' OR user_id IS NULL)
188
+ LIMIT 1
189
+ """
190
+ row = await db.fetchrow(query, schema_name)
191
+ logger.debug(f"Executing schema lookup: name={schema_name}, user_id={user_id or 'public'}")
159
192
 
160
193
  if row:
161
194
  spec = row.get("spec")
@@ -193,17 +226,25 @@ def _load_schema_from_database(schema_name: str, user_id: str) -> dict[str, Any]
193
226
  try:
194
227
  await db.connect()
195
228
 
196
- # Query schemas table directly by name
197
- # Note: Schema name lookup is case-insensitive for user convenience
198
- query = """
199
- SELECT spec FROM schemas
200
- WHERE LOWER(name) = LOWER($1)
201
- AND (user_id = $2 OR user_id = 'system')
202
- LIMIT 1
203
- """
204
- logger.debug(f"Executing schema lookup: name={schema_name}, user_id={user_id}")
205
-
206
- row = await db.fetchrow(query, schema_name, user_id)
229
+ # Query for public schemas (user_id IS NULL) and optionally user-specific
230
+ if user_id:
231
+ query = """
232
+ SELECT spec FROM schemas
233
+ WHERE LOWER(name) = LOWER($1)
234
+ AND (user_id = $2 OR user_id = 'system' OR user_id IS NULL)
235
+ LIMIT 1
236
+ """
237
+ row = await db.fetchrow(query, schema_name, user_id)
238
+ else:
239
+ # No user_id - only search public schemas
240
+ query = """
241
+ SELECT spec FROM schemas
242
+ WHERE LOWER(name) = LOWER($1)
243
+ AND (user_id = 'system' OR user_id IS NULL)
244
+ LIMIT 1
245
+ """
246
+ row = await db.fetchrow(query, schema_name)
247
+ logger.debug(f"Executing schema lookup: name={schema_name}, user_id={user_id or 'public'}")
207
248
 
208
249
  if row:
209
250
  spec = row.get("spec")
@@ -231,74 +272,66 @@ def load_agent_schema(
231
272
  enable_db_fallback: bool = True,
232
273
  ) -> dict[str, Any]:
233
274
  """
234
- Load agent schema from YAML file with unified search logic and caching.
275
+ Load agent schema with database-first priority for hot-reloading support.
235
276
 
236
277
  Schema names are case-invariant - "Rem", "rem", "REM" all resolve to the same schema.
237
278
 
238
- Filesystem schemas are cached indefinitely (immutable, versioned with code).
239
- Database schemas (future) will be cached with TTL for invalidation.
279
+ **IMPORTANT**: Database is checked FIRST (before filesystem) to enable hot-reloading
280
+ of schema updates without redeploying the application. This allows operators to
281
+ update schemas via `rem process ingest` and have changes take effect immediately.
240
282
 
241
283
  Handles path resolution automatically:
242
- - "rem" → searches schemas/agents/rem.yaml (top-level)
243
- - "moment-builder" → searches schemas/agents/core/moment-builder.yaml
244
- - "contract-analyzer" → searches schemas/agents/examples/contract-analyzer.yaml
245
- - "core/moment-builder" → searches schemas/agents/core/moment-builder.yaml
246
- - "/absolute/path.yaml" → loads directly
247
- - "relative/path.yaml" → loads relative to cwd
284
+ - "rem" → searches database, then schemas/agents/rem.yaml
285
+ - "moment-builder" → searches database, then schemas/agents/core/moment-builder.yaml
286
+ - "/absolute/path.yaml" → loads directly from filesystem (exact paths skip database)
287
+ - "relative/path.yaml" → loads relative to cwd (exact paths skip database)
248
288
 
249
289
  Search Order:
250
- 1. Check cache (if use_cache=True and schema found in FS cache)
251
- 2. Exact path if it exists (absolute or relative)
252
- 3. Custom paths from rem.register_schema_path() and SCHEMA__PATHS env var
253
- 4. Package resources: schemas/agents/{name}.yaml (top-level)
254
- 5. Package resources: schemas/agents/core/{name}.yaml
255
- 6. Package resources: schemas/agents/examples/{name}.yaml
256
- 7. Package resources: schemas/evaluators/{name}.yaml
257
- 8. Package resources: schemas/{name}.yaml
258
- 9. Database LOOKUP: schemas table (if enable_db_fallback=True and user_id provided)
290
+ 1. Exact path if it exists (absolute or relative) - skips database
291
+ 2. Database LOOKUP: schemas table (if enable_db_fallback=True) - PREFERRED for hot-reload
292
+ 3. Check cache (if use_cache=True and schema found in FS cache)
293
+ 4. Custom paths from rem.register_schema_path() and SCHEMA__PATHS env var
294
+ 5. Package resources: schemas/agents/{name}.yaml (top-level)
295
+ 6. Package resources: schemas/agents/core/{name}.yaml
296
+ 7. Package resources: schemas/agents/examples/{name}.yaml
297
+ 8. Package resources: schemas/evaluators/{name}.yaml
298
+ 9. Package resources: schemas/{name}.yaml
259
299
 
260
300
  Args:
261
301
  schema_name_or_path: Schema name or file path (case-invariant for names)
262
302
  Examples: "rem-query-agent", "Contract-Analyzer", "./my-schema.yaml"
263
303
  use_cache: If True, uses in-memory cache for filesystem schemas
264
- user_id: User ID for database schema lookup (required for DB fallback)
265
- enable_db_fallback: If True, falls back to database LOOKUP when file not found
304
+ user_id: User ID for database schema lookup
305
+ enable_db_fallback: If True, checks database FIRST for schema (default: True)
266
306
 
267
307
  Returns:
268
308
  Agent schema as dictionary
269
309
 
270
310
  Raises:
271
- FileNotFoundError: If schema not found in any search location (filesystem + database)
311
+ FileNotFoundError: If schema not found in any search location (database + filesystem)
272
312
  yaml.YAMLError: If schema file is invalid YAML
273
313
 
274
314
  Examples:
275
- >>> # Load by short name (cached after first load) - case invariant
276
- >>> schema = load_agent_schema("Contract-Analyzer") # same as "contract-analyzer"
315
+ >>> # Load by short name - checks database first for hot-reload support
316
+ >>> schema = load_agent_schema("Contract-Analyzer") # case invariant
277
317
  >>>
278
- >>> # Load from custom path (not cached - custom paths may change)
318
+ >>> # Load from custom path (skips database - exact paths always use filesystem)
279
319
  >>> schema = load_agent_schema("./my-agent.yaml")
280
320
  >>>
281
- >>> # Load evaluator schema (cached)
321
+ >>> # Load evaluator schema
282
322
  >>> schema = load_agent_schema("rem-lookup-correctness")
283
- >>>
284
- >>> # Load custom user schema from database (case invariant)
285
- >>> schema = load_agent_schema("My-Agent", user_id="user-123") # same as "my-agent"
286
323
  """
287
324
  # Normalize the name for cache key (lowercase for case-invariant lookups)
288
325
  cache_key = str(schema_name_or_path).replace('agents/', '').replace('schemas/', '').replace('evaluators/', '').replace('core/', '').replace('examples/', '').lower()
289
326
  if cache_key.endswith('.yaml') or cache_key.endswith('.yml'):
290
327
  cache_key = cache_key.rsplit('.', 1)[0]
291
328
 
292
- # Check cache first (only for package resources, not custom paths)
293
329
  path = Path(schema_name_or_path)
294
- is_custom_path = path.exists() or '/' in str(schema_name_or_path) or '\\' in str(schema_name_or_path)
295
-
296
- if use_cache and not is_custom_path and cache_key in _fs_schema_cache:
297
- logger.debug(f"Loading schema from cache: {cache_key}")
298
- return _fs_schema_cache[cache_key]
330
+ is_custom_path = (path.exists() and path.is_file()) or '/' in str(schema_name_or_path) or '\\' in str(schema_name_or_path)
299
331
 
300
- # 1. Try exact path first (absolute or relative to cwd)
301
- if path.exists():
332
+ # 1. Try exact path first (absolute or relative to cwd) - must be a file, not directory
333
+ # Exact paths skip database lookup (explicit file reference)
334
+ if path.exists() and path.is_file():
302
335
  logger.debug(f"Loading schema from exact path: {path}")
303
336
  with open(path, "r") as f:
304
337
  schema = yaml.safe_load(f)
@@ -306,10 +339,28 @@ def load_agent_schema(
306
339
  # Don't cache custom paths (they may change)
307
340
  return cast(dict[str, Any], schema)
308
341
 
309
- # 2. Normalize name for package resource search (lowercase)
342
+ # 2. Normalize name for lookups (lowercase)
310
343
  base_name = cache_key
311
344
 
312
- # 3. Try custom schema paths (from registry + SCHEMA__PATHS env var + auto-detected)
345
+ # 3. Try database FIRST (if enabled) - enables hot-reload without redeploy
346
+ # Database schemas are NOT cached to ensure hot-reload works immediately
347
+ if enable_db_fallback and not is_custom_path:
348
+ try:
349
+ logger.debug(f"Checking database for schema: {base_name} (user_id={user_id or 'public'})")
350
+ db_schema = _load_schema_from_database(base_name, user_id)
351
+ if db_schema:
352
+ logger.info(f"✅ Loaded schema from database: {base_name}")
353
+ return db_schema
354
+ except Exception as e:
355
+ logger.debug(f"Database schema lookup failed: {e}")
356
+ # Fall through to filesystem search
357
+
358
+ # 4. Check filesystem cache (only for package resources, not custom paths)
359
+ if use_cache and not is_custom_path and cache_key in _fs_schema_cache:
360
+ logger.debug(f"Loading schema from cache: {cache_key}")
361
+ return _fs_schema_cache[cache_key]
362
+
363
+ # 5. Try custom schema paths (from registry + SCHEMA__PATHS env var + auto-detected)
313
364
  from ..registry import get_schema_paths
314
365
 
315
366
  custom_paths = get_schema_paths()
@@ -340,7 +391,7 @@ def load_agent_schema(
340
391
  # Don't cache custom paths (they may change during development)
341
392
  return cast(dict[str, Any], schema)
342
393
 
343
- # 4. Try package resources with standard search paths
394
+ # 6. Try package resources with standard search paths
344
395
  for search_pattern in SCHEMA_SEARCH_PATHS:
345
396
  search_path = search_pattern.format(name=base_name)
346
397
 
@@ -365,19 +416,7 @@ def load_agent_schema(
365
416
  logger.debug(f"Could not load from {search_path}: {e}")
366
417
  continue
367
418
 
368
- # 5. Try database LOOKUP fallback (if enabled and user_id provided)
369
- if enable_db_fallback and user_id:
370
- try:
371
- logger.debug(f"Attempting database LOOKUP for schema: {base_name} (user_id={user_id})")
372
- db_schema = _load_schema_from_database(base_name, user_id)
373
- if db_schema:
374
- logger.info(f"✅ Loaded schema from database: {base_name} (user_id={user_id})")
375
- return db_schema
376
- except Exception as e:
377
- logger.debug(f"Database schema lookup failed: {e}")
378
- # Fall through to error below
379
-
380
- # 6. Schema not found in any location
419
+ # 7. Schema not found in any location
381
420
  searched_paths = [pattern.format(name=base_name) for pattern in SCHEMA_SEARCH_PATHS]
382
421
 
383
422
  custom_paths_note = ""
@@ -387,9 +426,9 @@ def load_agent_schema(
387
426
  db_search_note = ""
388
427
  if enable_db_fallback:
389
428
  if user_id:
390
- db_search_note = f"\n - Database: LOOKUP '{base_name}' FROM schemas WHERE user_id='{user_id}' (no match)"
429
+ db_search_note = f"\n - Database: LOOKUP '{base_name}' FROM schemas WHERE user_id IN ('{user_id}', 'system', NULL) (no match)"
391
430
  else:
392
- db_search_note = "\n - Database: (skipped - no user_id provided)"
431
+ db_search_note = f"\n - Database: LOOKUP '{base_name}' FROM schemas WHERE user_id IN ('system', NULL) (no match)"
393
432
 
394
433
  raise FileNotFoundError(
395
434
  f"Schema not found: {schema_name_or_path}\n"
@@ -405,18 +444,21 @@ async def load_agent_schema_async(
405
444
  schema_name_or_path: str,
406
445
  user_id: str | None = None,
407
446
  db=None,
447
+ enable_db_fallback: bool = True,
408
448
  ) -> dict[str, Any]:
409
449
  """
410
- Async version of load_agent_schema for use in async contexts.
450
+ Async version of load_agent_schema with database-first priority.
411
451
 
412
452
  Schema names are case-invariant - "MyAgent", "myagent", "MYAGENT" all resolve to the same schema.
413
453
 
414
- This version accepts an existing database connection to avoid creating new connections.
454
+ **IMPORTANT**: Database is checked FIRST (before filesystem) to enable hot-reloading
455
+ of schema updates without redeploying the application.
415
456
 
416
457
  Args:
417
458
  schema_name_or_path: Schema name or file path (case-invariant for names)
418
459
  user_id: User ID for database schema lookup
419
460
  db: Optional existing PostgresService connection (if None, will create one)
461
+ enable_db_fallback: If True, checks database FIRST for schema (default: True)
420
462
 
421
463
  Returns:
422
464
  Agent schema as dictionary
@@ -424,7 +466,6 @@ async def load_agent_schema_async(
424
466
  Raises:
425
467
  FileNotFoundError: If schema not found
426
468
  """
427
- # First try filesystem search (sync operations are fine)
428
469
  path = Path(schema_name_or_path)
429
470
 
430
471
  # Normalize the name for cache key (lowercase for case-invariant lookups)
@@ -432,15 +473,10 @@ async def load_agent_schema_async(
432
473
  if cache_key.endswith('.yaml') or cache_key.endswith('.yml'):
433
474
  cache_key = cache_key.rsplit('.', 1)[0]
434
475
 
435
- is_custom_path = path.exists() or '/' in str(schema_name_or_path) or '\\' in str(schema_name_or_path)
436
-
437
- # Check cache
438
- if not is_custom_path and cache_key in _fs_schema_cache:
439
- logger.debug(f"Loading schema from cache: {cache_key}")
440
- return _fs_schema_cache[cache_key]
476
+ is_custom_path = (path.exists() and path.is_file()) or '/' in str(schema_name_or_path) or '\\' in str(schema_name_or_path)
441
477
 
442
- # Try exact path
443
- if path.exists():
478
+ # 1. Try exact path first (skips database - explicit file reference)
479
+ if path.exists() and path.is_file():
444
480
  logger.debug(f"Loading schema from exact path: {path}")
445
481
  with open(path, "r") as f:
446
482
  schema = yaml.safe_load(f)
@@ -448,7 +484,60 @@ async def load_agent_schema_async(
448
484
 
449
485
  base_name = cache_key
450
486
 
451
- # Try custom schema paths (from registry + SCHEMA__PATHS env var + auto-detected)
487
+ # 2. Try database FIRST (if enabled) - enables hot-reload without redeploy
488
+ if enable_db_fallback and not is_custom_path:
489
+ # Check DB schema cache first (TTL-based)
490
+ cached_schema = _get_cached_db_schema(base_name, user_id)
491
+ if cached_schema is not None:
492
+ logger.info(f"✅ Loaded schema from cache: {base_name}")
493
+ return cached_schema
494
+
495
+ # Cache miss - query database
496
+ from rem.services.postgres import get_postgres_service
497
+
498
+ should_disconnect = False
499
+ if db is None:
500
+ db = get_postgres_service()
501
+ if db:
502
+ await db.connect()
503
+ should_disconnect = True
504
+
505
+ if db:
506
+ try:
507
+ if user_id:
508
+ query = """
509
+ SELECT spec FROM schemas
510
+ WHERE LOWER(name) = LOWER($1)
511
+ AND (user_id = $2 OR user_id = 'system' OR user_id IS NULL)
512
+ LIMIT 1
513
+ """
514
+ row = await db.fetchrow(query, base_name, user_id)
515
+ else:
516
+ # No user_id - only search public schemas
517
+ query = """
518
+ SELECT spec FROM schemas
519
+ WHERE LOWER(name) = LOWER($1)
520
+ AND (user_id = 'system' OR user_id IS NULL)
521
+ LIMIT 1
522
+ """
523
+ row = await db.fetchrow(query, base_name)
524
+ if row:
525
+ spec = row.get("spec")
526
+ if spec and isinstance(spec, dict):
527
+ # Cache the schema for future requests
528
+ _cache_db_schema(base_name, user_id, spec)
529
+ logger.info(f"✅ Loaded schema from database: {base_name}")
530
+ return spec
531
+ finally:
532
+ if should_disconnect:
533
+ await db.disconnect()
534
+
535
+ # 3. Check filesystem cache
536
+ if not is_custom_path and cache_key in _fs_schema_cache:
537
+ logger.debug(f"Loading schema from cache: {cache_key}")
538
+ return _fs_schema_cache[cache_key]
539
+
540
+ # 4. Try custom schema paths (from registry + SCHEMA__PATHS env var + auto-detected)
452
541
  from ..registry import get_schema_paths
453
542
  custom_paths = get_schema_paths()
454
543
 
@@ -470,7 +559,7 @@ async def load_agent_schema_async(
470
559
  schema = yaml.safe_load(f)
471
560
  return cast(dict[str, Any], schema)
472
561
 
473
- # Try package resources
562
+ # 5. Try package resources
474
563
  for search_pattern in SCHEMA_SEARCH_PATHS:
475
564
  search_path = search_pattern.format(name=base_name)
476
565
  try:
@@ -484,35 +573,6 @@ async def load_agent_schema_async(
484
573
  except Exception:
485
574
  continue
486
575
 
487
- # Try database lookup
488
- if user_id:
489
- from rem.services.postgres import get_postgres_service
490
-
491
- should_disconnect = False
492
- if db is None:
493
- db = get_postgres_service()
494
- if db:
495
- await db.connect()
496
- should_disconnect = True
497
-
498
- if db:
499
- try:
500
- query = """
501
- SELECT spec FROM schemas
502
- WHERE LOWER(name) = LOWER($1)
503
- AND (user_id = $2 OR user_id = 'system' OR user_id IS NULL)
504
- LIMIT 1
505
- """
506
- row = await db.fetchrow(query, base_name, user_id)
507
- if row:
508
- spec = row.get("spec")
509
- if spec and isinstance(spec, dict):
510
- logger.info(f"✅ Loaded schema from database: {base_name}")
511
- return spec
512
- finally:
513
- if should_disconnect:
514
- await db.disconnect()
515
-
516
576
  # Not found
517
577
  raise FileNotFoundError(f"Schema not found: {schema_name_or_path}")
518
578
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remdb
3
- Version: 0.3.180
3
+ Version: 0.3.258
4
4
  Summary: Resources Entities Moments - Bio-inspired memory system for agentic AI workloads
5
5
  Project-URL: Homepage, https://github.com/Percolation-Labs/reminiscent
6
6
  Project-URL: Documentation, https://github.com/Percolation-Labs/reminiscent/blob/main/README.md
@@ -28,7 +28,7 @@ Requires-Dist: gitpython>=3.1.45
28
28
  Requires-Dist: hypercorn>=0.17.0
29
29
  Requires-Dist: itsdangerous>=2.0.0
30
30
  Requires-Dist: json-schema-to-pydantic>=0.2.0
31
- Requires-Dist: kreuzberg<4.0.0,>=3.21.0
31
+ Requires-Dist: kreuzberg>=4.0.5
32
32
  Requires-Dist: loguru>=0.7.0
33
33
  Requires-Dist: openinference-instrumentation-pydantic-ai>=0.1.0
34
34
  Requires-Dist: opentelemetry-api>=1.28.0
@@ -39,7 +39,7 @@ Requires-Dist: opentelemetry-instrumentation-fastapi>=0.49b0
39
39
  Requires-Dist: opentelemetry-instrumentation>=0.49b0
40
40
  Requires-Dist: opentelemetry-sdk>=1.28.0
41
41
  Requires-Dist: psycopg[binary]>=3.2.0
42
- Requires-Dist: pydantic-ai>=0.0.14
42
+ Requires-Dist: pydantic-ai>=1.0.0
43
43
  Requires-Dist: pydantic-settings>=2.6.0
44
44
  Requires-Dist: pydantic>=2.10.0
45
45
  Requires-Dist: pydub>=0.25.0
@@ -1300,15 +1300,16 @@ FuzzyQuery ::= FUZZY <text:string> [THRESHOLD <t:float>] [LIMIT <n:int>]
1300
1300
  available : Stage 1+
1301
1301
  example : FUZZY "sara" THRESHOLD 0.5 LIMIT 10
1302
1302
 
1303
- SearchQuery ::= SEARCH <text:string> [TABLE <table:string>] [WHERE <clause:string>] [LIMIT <n:int>]
1303
+ SearchQuery ::= SEARCH <text:string> [IN|TABLE <table:string>] [WHERE <clause:string>] [LIMIT <n:int>]
1304
1304
  text : Semantic query text
1305
- table : Target table (default: "resources")
1305
+ table : Target table (default: "resources"). Use IN or TABLE keyword.
1306
1306
  clause : Optional PostgreSQL WHERE clause for hybrid filtering (combines vector + structured)
1307
1307
  limit : Max results (default: 10)
1308
1308
  performance : Indexed (pgvector)
1309
1309
  available : Stage 3+
1310
1310
  examples :
1311
- - SEARCH "database migration" TABLE resources LIMIT 10
1311
+ - SEARCH "database migration" IN resources LIMIT 10
1312
+ - SEARCH "parcel delivery" IN ontologies
1312
1313
  - SEARCH "team discussion" TABLE moments WHERE "moment_type='meeting'" LIMIT 5
1313
1314
  - SEARCH "project updates" WHERE "created_at >= '2024-01-01'" LIMIT 20
1314
1315
  - SEARCH "AI research" WHERE "tags @> ARRAY['machine-learning']" LIMIT 10