remdb 0.3.114__py3-none-any.whl → 0.3.127__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 (41) hide show
  1. rem/agentic/agents/sse_simulator.py +2 -0
  2. rem/agentic/context.py +23 -3
  3. rem/agentic/mcp/tool_wrapper.py +29 -3
  4. rem/agentic/otel/setup.py +1 -0
  5. rem/agentic/providers/pydantic_ai.py +26 -2
  6. rem/api/main.py +4 -1
  7. rem/api/mcp_router/server.py +9 -3
  8. rem/api/mcp_router/tools.py +324 -2
  9. rem/api/routers/admin.py +218 -1
  10. rem/api/routers/chat/completions.py +250 -4
  11. rem/api/routers/chat/models.py +81 -7
  12. rem/api/routers/chat/otel_utils.py +33 -0
  13. rem/api/routers/chat/sse_events.py +17 -1
  14. rem/api/routers/chat/streaming.py +35 -1
  15. rem/api/routers/feedback.py +134 -14
  16. rem/api/routers/query.py +6 -3
  17. rem/cli/commands/README.md +42 -0
  18. rem/cli/commands/cluster.py +617 -168
  19. rem/cli/commands/configure.py +1 -3
  20. rem/cli/commands/db.py +66 -22
  21. rem/cli/commands/experiments.py +242 -26
  22. rem/cli/commands/schema.py +6 -5
  23. rem/config.py +8 -1
  24. rem/services/phoenix/client.py +59 -18
  25. rem/services/postgres/diff_service.py +108 -3
  26. rem/services/postgres/schema_generator.py +205 -4
  27. rem/services/session/compression.py +7 -0
  28. rem/settings.py +150 -18
  29. rem/sql/migrations/001_install.sql +156 -0
  30. rem/sql/migrations/002_install_models.sql +1864 -1
  31. rem/sql/migrations/004_cache_system.sql +548 -0
  32. rem/utils/__init__.py +18 -0
  33. rem/utils/schema_loader.py +94 -3
  34. rem/utils/sql_paths.py +146 -0
  35. rem/workers/__init__.py +3 -1
  36. rem/workers/db_listener.py +579 -0
  37. rem/workers/unlogged_maintainer.py +463 -0
  38. {remdb-0.3.114.dist-info → remdb-0.3.127.dist-info}/METADATA +213 -177
  39. {remdb-0.3.114.dist-info → remdb-0.3.127.dist-info}/RECORD +41 -36
  40. {remdb-0.3.114.dist-info → remdb-0.3.127.dist-info}/WHEEL +0 -0
  41. {remdb-0.3.114.dist-info → remdb-0.3.127.dist-info}/entry_points.txt +0 -0
@@ -195,7 +195,7 @@ def load_agent_schema(
195
195
  """
196
196
  Load agent schema from YAML file with unified search logic and caching.
197
197
 
198
- Schema names are case-invariant - "Siggy", "siggy", "SIGGY" all resolve to the same schema.
198
+ Schema names are case-invariant - "Rem", "rem", "REM" all resolve to the same schema.
199
199
 
200
200
  Filesystem schemas are cached indefinitely (immutable, versioned with code).
201
201
  Database schemas (future) will be cached with TTL for invalidation.
@@ -271,10 +271,20 @@ def load_agent_schema(
271
271
  # 2. Normalize name for package resource search (lowercase)
272
272
  base_name = cache_key
273
273
 
274
- # 3. Try custom schema paths (from registry + SCHEMA__PATHS env var)
274
+ # 3. Try custom schema paths (from registry + SCHEMA__PATHS env var + auto-detected)
275
275
  from ..registry import get_schema_paths
276
276
 
277
277
  custom_paths = get_schema_paths()
278
+
279
+ # Auto-detect local folders if they exist (convention over configuration)
280
+ auto_detect_folders = ["./agents", "./schemas", "./evaluators"]
281
+ for auto_folder in auto_detect_folders:
282
+ auto_path = Path(auto_folder)
283
+ if auto_path.exists() and auto_path.is_dir():
284
+ resolved = str(auto_path.resolve())
285
+ if resolved not in custom_paths:
286
+ custom_paths.insert(0, resolved)
287
+ logger.debug(f"Auto-detected schema directory: {auto_folder}")
278
288
  for custom_dir in custom_paths:
279
289
  # Try various patterns within each custom directory
280
290
  for pattern in [
@@ -400,9 +410,20 @@ async def load_agent_schema_async(
400
410
 
401
411
  base_name = cache_key
402
412
 
403
- # Try custom schema paths
413
+ # Try custom schema paths (from registry + SCHEMA__PATHS env var + auto-detected)
404
414
  from ..registry import get_schema_paths
405
415
  custom_paths = get_schema_paths()
416
+
417
+ # Auto-detect local folders if they exist (convention over configuration)
418
+ auto_detect_folders = ["./agents", "./schemas", "./evaluators"]
419
+ for auto_folder in auto_detect_folders:
420
+ auto_path = Path(auto_folder)
421
+ if auto_path.exists() and auto_path.is_dir():
422
+ resolved = str(auto_path.resolve())
423
+ if resolved not in custom_paths:
424
+ custom_paths.insert(0, resolved)
425
+ logger.debug(f"Auto-detected schema directory: {auto_folder}")
426
+
406
427
  for custom_dir in custom_paths:
407
428
  for pattern in [f"{base_name}.yaml", f"{base_name}.yml", f"agents/{base_name}.yaml"]:
408
429
  custom_path = Path(custom_dir) / pattern
@@ -490,3 +511,73 @@ def validate_agent_schema(schema: dict[str, Any]) -> bool:
490
511
 
491
512
  logger.debug("Schema validation passed")
492
513
  return True
514
+
515
+
516
+ def get_evaluator_schema_path(evaluator_name: str) -> Path | None:
517
+ """
518
+ Find the file path to an evaluator schema.
519
+
520
+ Searches standard locations for the evaluator schema YAML file:
521
+ - ./evaluators/{name}.yaml (local project)
522
+ - Custom schema paths from registry
523
+ - Package resources: schemas/evaluators/{name}.yaml
524
+
525
+ Args:
526
+ evaluator_name: Name of the evaluator (e.g., "mental-health-classifier")
527
+
528
+ Returns:
529
+ Path to the evaluator schema file, or None if not found
530
+
531
+ Example:
532
+ >>> path = get_evaluator_schema_path("mental-health-classifier")
533
+ >>> if path:
534
+ ... print(f"Found evaluator at: {path}")
535
+ """
536
+ from ..registry import get_schema_paths
537
+
538
+ base_name = evaluator_name.lower().replace('.yaml', '').replace('.yml', '')
539
+
540
+ # 1. Try custom schema paths (from registry + auto-detected)
541
+ custom_paths = get_schema_paths()
542
+
543
+ # Auto-detect local folders
544
+ auto_detect_folders = ["./evaluators", "./schemas", "./agents"]
545
+ for auto_folder in auto_detect_folders:
546
+ auto_path = Path(auto_folder)
547
+ if auto_path.exists() and auto_path.is_dir():
548
+ resolved = str(auto_path.resolve())
549
+ if resolved not in custom_paths:
550
+ custom_paths.insert(0, resolved)
551
+
552
+ for custom_dir in custom_paths:
553
+ # Try various patterns within each custom directory
554
+ for pattern in [
555
+ f"{base_name}.yaml",
556
+ f"{base_name}.yml",
557
+ f"evaluators/{base_name}.yaml",
558
+ ]:
559
+ custom_path = Path(custom_dir) / pattern
560
+ if custom_path.exists():
561
+ logger.debug(f"Found evaluator schema: {custom_path}")
562
+ return custom_path
563
+
564
+ # 2. Try package resources
565
+ evaluator_search_paths = [
566
+ f"schemas/evaluators/{base_name}.yaml",
567
+ f"schemas/evaluators/rem/{base_name}.yaml",
568
+ ]
569
+
570
+ for search_path in evaluator_search_paths:
571
+ try:
572
+ schema_ref = importlib.resources.files("rem") / search_path
573
+ schema_path = Path(str(schema_ref))
574
+
575
+ if schema_path.exists():
576
+ logger.debug(f"Found evaluator schema in package: {schema_path}")
577
+ return schema_path
578
+ except Exception as e:
579
+ logger.debug(f"Could not check {search_path}: {e}")
580
+ continue
581
+
582
+ logger.warning(f"Evaluator schema not found: {evaluator_name}")
583
+ return None
rem/utils/sql_paths.py ADDED
@@ -0,0 +1,146 @@
1
+ """Utilities for resolving SQL file paths.
2
+
3
+ Handles package SQL directory resolution and user migrations.
4
+
5
+ Convention for user migrations:
6
+ Place custom SQL files in `./sql/migrations/` relative to your project root.
7
+ Files should be numbered (e.g., `100_custom_table.sql`) to control execution order.
8
+ Package migrations (001-099) run first, then user migrations (100+).
9
+ """
10
+
11
+ from pathlib import Path
12
+ from typing import List, Optional
13
+ import importlib.resources
14
+
15
+ # Convention: Default location for user-maintained migrations
16
+ USER_SQL_DIR_CONVENTION = "sql"
17
+
18
+
19
+ def get_package_sql_dir() -> Path:
20
+ """Get the SQL directory from the installed rem package.
21
+
22
+ Returns:
23
+ Path to the package's sql directory
24
+
25
+ Raises:
26
+ FileNotFoundError: If the SQL directory cannot be found
27
+ """
28
+ try:
29
+ # Use importlib.resources for Python 3.9+
30
+ sql_ref = importlib.resources.files("rem") / "sql"
31
+ package_sql = Path(str(sql_ref))
32
+ if package_sql.exists():
33
+ return package_sql
34
+ except (AttributeError, TypeError):
35
+ pass
36
+
37
+ # Fallback: use __file__ to find package location
38
+ try:
39
+ import rem
40
+ package_sql = Path(rem.__file__).parent / "sql"
41
+ if package_sql.exists():
42
+ return package_sql
43
+ except (ImportError, AttributeError):
44
+ pass
45
+
46
+ # Development fallback: check relative to cwd
47
+ dev_sql = Path("src/rem/sql")
48
+ if dev_sql.exists():
49
+ return dev_sql
50
+
51
+ raise FileNotFoundError(
52
+ "Could not locate rem SQL directory. "
53
+ "Ensure remdb is properly installed or run from the source directory."
54
+ )
55
+
56
+
57
+ def get_package_migrations_dir() -> Path:
58
+ """Get the migrations directory from the installed rem package.
59
+
60
+ Returns:
61
+ Path to the package's migrations directory
62
+ """
63
+ return get_package_sql_dir() / "migrations"
64
+
65
+
66
+ def get_user_sql_dir() -> Optional[Path]:
67
+ """Get the conventional user SQL directory if it exists.
68
+
69
+ Looks for `./sql/` relative to the current working directory.
70
+ This follows the convention for user-maintained migrations.
71
+
72
+ Returns:
73
+ Path to user sql directory if it exists, None otherwise
74
+ """
75
+ user_sql = Path.cwd() / USER_SQL_DIR_CONVENTION
76
+ if user_sql.exists() and user_sql.is_dir():
77
+ return user_sql
78
+ return None
79
+
80
+
81
+ def list_package_migrations() -> List[Path]:
82
+ """List all migration files in the package.
83
+
84
+ Returns:
85
+ Sorted list of migration file paths
86
+ """
87
+ try:
88
+ migrations_dir = get_package_migrations_dir()
89
+ if migrations_dir.exists():
90
+ return sorted(
91
+ f for f in migrations_dir.glob("*.sql")
92
+ if f.name[0].isdigit() # Only numbered migrations
93
+ )
94
+ except FileNotFoundError:
95
+ pass
96
+
97
+ return []
98
+
99
+
100
+ def list_user_migrations() -> List[Path]:
101
+ """List all migration files in the user's sql/migrations directory.
102
+
103
+ Returns:
104
+ Sorted list of user migration file paths
105
+ """
106
+ user_sql = get_user_sql_dir()
107
+ if user_sql:
108
+ migrations_dir = user_sql / "migrations"
109
+ if migrations_dir.exists():
110
+ return sorted(
111
+ f for f in migrations_dir.glob("*.sql")
112
+ if f.name[0].isdigit() # Only numbered migrations
113
+ )
114
+ return []
115
+
116
+
117
+ def list_all_migrations() -> List[Path]:
118
+ """List all migration files from package and user directories.
119
+
120
+ Collects migrations from:
121
+ 1. Package migrations directory
122
+ 2. User directory (./sql/migrations/) if it exists
123
+
124
+ Files are sorted by name, so use numbered prefixes to control order:
125
+ - 001-099: Reserved for package migrations
126
+ - 100+: Recommended for user migrations
127
+
128
+ Returns:
129
+ Sorted list of all migration file paths (by filename)
130
+ """
131
+ all_migrations = []
132
+ seen_names = set()
133
+
134
+ # Package migrations first
135
+ for f in list_package_migrations():
136
+ if f.name not in seen_names:
137
+ all_migrations.append(f)
138
+ seen_names.add(f.name)
139
+
140
+ # User migrations second
141
+ for f in list_user_migrations():
142
+ if f.name not in seen_names:
143
+ all_migrations.append(f)
144
+ seen_names.add(f.name)
145
+
146
+ return sorted(all_migrations, key=lambda p: p.name)
rem/workers/__init__.py CHANGED
@@ -1,5 +1,7 @@
1
1
  """Background workers for processing tasks."""
2
2
 
3
+ from .db_listener import DBListener
3
4
  from .sqs_file_processor import SQSFileProcessor
5
+ from .unlogged_maintainer import UnloggedMaintainer
4
6
 
5
- __all__ = ["SQSFileProcessor"]
7
+ __all__ = ["DBListener", "SQSFileProcessor", "UnloggedMaintainer"]