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.
- rem/agentic/agents/sse_simulator.py +2 -0
- rem/agentic/context.py +23 -3
- rem/agentic/mcp/tool_wrapper.py +29 -3
- rem/agentic/otel/setup.py +1 -0
- rem/agentic/providers/pydantic_ai.py +26 -2
- rem/api/main.py +4 -1
- rem/api/mcp_router/server.py +9 -3
- rem/api/mcp_router/tools.py +324 -2
- rem/api/routers/admin.py +218 -1
- rem/api/routers/chat/completions.py +250 -4
- rem/api/routers/chat/models.py +81 -7
- rem/api/routers/chat/otel_utils.py +33 -0
- rem/api/routers/chat/sse_events.py +17 -1
- rem/api/routers/chat/streaming.py +35 -1
- rem/api/routers/feedback.py +134 -14
- rem/api/routers/query.py +6 -3
- rem/cli/commands/README.md +42 -0
- rem/cli/commands/cluster.py +617 -168
- rem/cli/commands/configure.py +1 -3
- rem/cli/commands/db.py +66 -22
- rem/cli/commands/experiments.py +242 -26
- rem/cli/commands/schema.py +6 -5
- rem/config.py +8 -1
- rem/services/phoenix/client.py +59 -18
- rem/services/postgres/diff_service.py +108 -3
- rem/services/postgres/schema_generator.py +205 -4
- rem/services/session/compression.py +7 -0
- rem/settings.py +150 -18
- rem/sql/migrations/001_install.sql +156 -0
- rem/sql/migrations/002_install_models.sql +1864 -1
- rem/sql/migrations/004_cache_system.sql +548 -0
- rem/utils/__init__.py +18 -0
- rem/utils/schema_loader.py +94 -3
- rem/utils/sql_paths.py +146 -0
- rem/workers/__init__.py +3 -1
- rem/workers/db_listener.py +579 -0
- rem/workers/unlogged_maintainer.py +463 -0
- {remdb-0.3.114.dist-info → remdb-0.3.127.dist-info}/METADATA +213 -177
- {remdb-0.3.114.dist-info → remdb-0.3.127.dist-info}/RECORD +41 -36
- {remdb-0.3.114.dist-info → remdb-0.3.127.dist-info}/WHEEL +0 -0
- {remdb-0.3.114.dist-info → remdb-0.3.127.dist-info}/entry_points.txt +0 -0
rem/utils/schema_loader.py
CHANGED
|
@@ -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 - "
|
|
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"]
|