remdb 0.3.7__py3-none-any.whl → 0.3.14__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 (43) hide show
  1. rem/__init__.py +129 -2
  2. rem/agentic/context.py +7 -5
  3. rem/agentic/providers/phoenix.py +32 -43
  4. rem/api/README.md +23 -0
  5. rem/api/main.py +27 -2
  6. rem/api/middleware/tracking.py +172 -0
  7. rem/api/routers/auth.py +54 -0
  8. rem/api/routers/chat/completions.py +1 -1
  9. rem/cli/commands/ask.py +13 -10
  10. rem/cli/commands/configure.py +4 -3
  11. rem/cli/commands/db.py +17 -3
  12. rem/cli/commands/experiments.py +76 -72
  13. rem/cli/commands/process.py +8 -7
  14. rem/cli/commands/scaffold.py +47 -0
  15. rem/cli/main.py +2 -0
  16. rem/models/entities/user.py +10 -3
  17. rem/registry.py +367 -0
  18. rem/services/content/providers.py +92 -133
  19. rem/services/dreaming/affinity_service.py +2 -16
  20. rem/services/dreaming/moment_service.py +2 -15
  21. rem/services/embeddings/api.py +20 -13
  22. rem/services/phoenix/EXPERIMENT_DESIGN.md +3 -3
  23. rem/services/phoenix/client.py +148 -14
  24. rem/services/postgres/schema_generator.py +86 -5
  25. rem/services/rate_limit.py +113 -0
  26. rem/services/rem/README.md +14 -0
  27. rem/services/user_service.py +98 -0
  28. rem/settings.py +79 -10
  29. rem/sql/install_models.sql +13 -0
  30. rem/sql/migrations/003_seed_default_user.sql +48 -0
  31. rem/utils/constants.py +97 -0
  32. rem/utils/date_utils.py +228 -0
  33. rem/utils/embeddings.py +17 -4
  34. rem/utils/files.py +167 -0
  35. rem/utils/mime_types.py +158 -0
  36. rem/utils/schema_loader.py +63 -14
  37. rem/utils/vision.py +9 -14
  38. rem/workers/README.md +14 -14
  39. rem/workers/db_maintainer.py +74 -0
  40. {remdb-0.3.7.dist-info → remdb-0.3.14.dist-info}/METADATA +169 -121
  41. {remdb-0.3.7.dist-info → remdb-0.3.14.dist-info}/RECORD +43 -32
  42. {remdb-0.3.7.dist-info → remdb-0.3.14.dist-info}/WHEEL +0 -0
  43. {remdb-0.3.7.dist-info → remdb-0.3.14.dist-info}/entry_points.txt +0 -0
rem/registry.py ADDED
@@ -0,0 +1,367 @@
1
+ """
2
+ REM Extension Registry - Register custom models and schema paths.
3
+
4
+ This module provides registration for downstream applications extending REM:
5
+ 1. Models - for database schema generation
6
+ 2. Schema paths - for agent/evaluator schema discovery
7
+
8
+ Usage:
9
+ import rem
10
+ from rem.models.core import CoreModel
11
+
12
+ # Register custom models
13
+ @rem.register_model
14
+ class CustomEntity(CoreModel):
15
+ name: str
16
+ custom_field: str
17
+
18
+ # Or register multiple at once
19
+ rem.register_models(ModelA, ModelB, ModelC)
20
+
21
+ # Register schema search paths
22
+ rem.register_schema_path("/app/custom-agents")
23
+ rem.register_schema_paths("/app/agents", "/app/evaluators")
24
+
25
+ # Then:
26
+ # - Schema generation includes your models: rem db schema generate
27
+ # - Schema loading finds your custom agents: load_agent_schema("my-agent")
28
+ """
29
+
30
+ from dataclasses import dataclass
31
+ from typing import Callable
32
+
33
+ from loguru import logger
34
+ from pydantic import BaseModel
35
+
36
+
37
+ @dataclass
38
+ class ModelExtension:
39
+ """Container for Pydantic model extension."""
40
+
41
+ model: type[BaseModel]
42
+ table_name: str | None = None # Optional: override inferred table name
43
+ entity_key_field: str | None = None # Optional: override inferred entity key
44
+
45
+
46
+ class ModelRegistry:
47
+ """
48
+ Registry for Pydantic models used in schema generation.
49
+
50
+ Models registered here are discovered by SchemaGenerator alongside
51
+ REM's core models.
52
+ """
53
+
54
+ def __init__(self) -> None:
55
+ self._models: dict[str, ModelExtension] = {}
56
+ self._core_models_registered: bool = False
57
+
58
+ def clear(self) -> None:
59
+ """Clear all registered models. Useful for testing."""
60
+ self._models.clear()
61
+ self._core_models_registered = False
62
+ logger.debug("Model registry cleared")
63
+
64
+ def register(
65
+ self,
66
+ model: type[BaseModel],
67
+ table_name: str | None = None,
68
+ entity_key_field: str | None = None,
69
+ ) -> type[BaseModel]:
70
+ """
71
+ Register a Pydantic model for database schema generation.
72
+
73
+ Args:
74
+ model: Pydantic model class (should inherit from CoreModel)
75
+ table_name: Optional table name override (inferred from class name if not provided)
76
+ entity_key_field: Optional entity key field override (inferred if not provided)
77
+
78
+ Returns:
79
+ The model class (allows use as decorator)
80
+
81
+ Example:
82
+ import rem
83
+ from rem.models.core import CoreModel
84
+
85
+ @rem.register_model
86
+ class CustomEntity(CoreModel):
87
+ name: str
88
+ custom_field: str
89
+
90
+ # Or with options:
91
+ rem.register_model(CustomEntity, table_name="custom_entities")
92
+ """
93
+ model_name = model.__name__
94
+ if model_name in self._models:
95
+ logger.warning(f"Model {model_name} already registered, overwriting")
96
+
97
+ self._models[model_name] = ModelExtension(
98
+ model=model,
99
+ table_name=table_name,
100
+ entity_key_field=entity_key_field,
101
+ )
102
+ logger.debug(f"Registered model: {model_name}")
103
+ return model
104
+
105
+ def register_many(self, *models: type[BaseModel]) -> None:
106
+ """
107
+ Register multiple models at once.
108
+
109
+ Example:
110
+ import rem
111
+ rem.register_models(ModelA, ModelB, ModelC)
112
+ """
113
+ for model in models:
114
+ self.register(model)
115
+
116
+ def register_core_models(self) -> None:
117
+ """
118
+ Register REM's built-in core models.
119
+
120
+ Called automatically by schema generator if not already done.
121
+ """
122
+ if self._core_models_registered:
123
+ return
124
+
125
+ from .models.entities import (
126
+ File,
127
+ ImageResource,
128
+ Message,
129
+ Moment,
130
+ Ontology,
131
+ OntologyConfig,
132
+ Resource,
133
+ Schema,
134
+ User,
135
+ )
136
+
137
+ core_models = [
138
+ Resource,
139
+ ImageResource,
140
+ Message,
141
+ User,
142
+ File,
143
+ Moment,
144
+ Schema,
145
+ Ontology,
146
+ OntologyConfig,
147
+ ]
148
+
149
+ for model in core_models:
150
+ if model.__name__ not in self._models:
151
+ self.register(model)
152
+
153
+ self._core_models_registered = True
154
+ logger.debug(f"Registered {len(core_models)} core models")
155
+
156
+ def get_models(self, include_core: bool = True) -> dict[str, ModelExtension]:
157
+ """
158
+ Get all registered models.
159
+
160
+ Args:
161
+ include_core: If True, ensures core models are registered first
162
+
163
+ Returns:
164
+ Dict mapping model name to ModelExtension
165
+ """
166
+ if include_core:
167
+ self.register_core_models()
168
+ return self._models.copy()
169
+
170
+ def get_model_classes(self, include_core: bool = True) -> dict[str, type[BaseModel]]:
171
+ """
172
+ Get all registered model classes (without extension metadata).
173
+
174
+ Args:
175
+ include_core: If True, ensures core models are registered first
176
+
177
+ Returns:
178
+ Dict mapping model name to model class
179
+ """
180
+ models = self.get_models(include_core)
181
+ return {name: ext.model for name, ext in models.items()}
182
+
183
+
184
+ # =============================================================================
185
+ # SCHEMA PATH REGISTRY
186
+ # =============================================================================
187
+
188
+
189
+ class SchemaPathRegistry:
190
+ """
191
+ Registry for custom schema search paths.
192
+
193
+ Paths registered here are searched BEFORE built-in package schemas
194
+ when loading agent/evaluator schemas.
195
+
196
+ Search order:
197
+ 1. Exact path (if file exists)
198
+ 2. Paths from this registry (in registration order)
199
+ 3. Paths from SCHEMA__PATHS env var
200
+ 4. Built-in package schemas
201
+ 5. Database LOOKUP
202
+ """
203
+
204
+ def __init__(self) -> None:
205
+ self._paths: list[str] = []
206
+
207
+ def clear(self) -> None:
208
+ """Clear all registered paths. Useful for testing."""
209
+ self._paths.clear()
210
+ logger.debug("Schema path registry cleared")
211
+
212
+ def register(self, path: str) -> None:
213
+ """
214
+ Register a schema search path.
215
+
216
+ Args:
217
+ path: Directory path to search for schemas
218
+
219
+ Example:
220
+ import rem
221
+ rem.register_schema_path("/app/custom-agents")
222
+ """
223
+ if path not in self._paths:
224
+ self._paths.append(path)
225
+ logger.debug(f"Registered schema path: {path}")
226
+
227
+ def register_many(self, *paths: str) -> None:
228
+ """
229
+ Register multiple schema paths at once.
230
+
231
+ Example:
232
+ import rem
233
+ rem.register_schema_paths("/app/agents", "/app/evaluators")
234
+ """
235
+ for path in paths:
236
+ self.register(path)
237
+
238
+ def get_paths(self) -> list[str]:
239
+ """
240
+ Get all registered paths (registry + settings).
241
+
242
+ Returns paths in search order:
243
+ 1. Programmatically registered paths (this registry)
244
+ 2. Paths from SCHEMA__PATHS environment variable
245
+
246
+ Returns:
247
+ List of directory paths to search
248
+ """
249
+ from .settings import settings
250
+
251
+ # Combine registry paths with settings paths
252
+ all_paths = self._paths.copy()
253
+
254
+ # Add paths from settings (SCHEMA__PATHS env var)
255
+ for path in settings.schema_search.path_list:
256
+ if path not in all_paths:
257
+ all_paths.append(path)
258
+
259
+ return all_paths
260
+
261
+
262
+ # =============================================================================
263
+ # MODULE-LEVEL SINGLETONS
264
+ # =============================================================================
265
+
266
+ _model_registry = ModelRegistry()
267
+ _schema_path_registry = SchemaPathRegistry()
268
+
269
+
270
+ def register_model(
271
+ model: type[BaseModel] | None = None,
272
+ *,
273
+ table_name: str | None = None,
274
+ entity_key_field: str | None = None,
275
+ ) -> type[BaseModel] | Callable[[type[BaseModel]], type[BaseModel]]:
276
+ """
277
+ Register a Pydantic model for database schema generation.
278
+
279
+ Can be used as a decorator or called directly.
280
+
281
+ Example:
282
+ import rem
283
+
284
+ @rem.register_model
285
+ class CustomEntity(CoreModel):
286
+ name: str
287
+
288
+ # Or with options:
289
+ @rem.register_model(table_name="custom_table")
290
+ class AnotherEntity(CoreModel):
291
+ name: str
292
+
293
+ # Or direct call:
294
+ rem.register_model(MyModel, table_name="my_table")
295
+ """
296
+ def decorator(m: type[BaseModel]) -> type[BaseModel]:
297
+ return _model_registry.register(m, table_name, entity_key_field)
298
+
299
+ if model is not None:
300
+ return decorator(model)
301
+ return decorator
302
+
303
+
304
+ def register_models(*models: type[BaseModel]) -> None:
305
+ """Register multiple models at once."""
306
+ _model_registry.register_many(*models)
307
+
308
+
309
+ def get_model_registry() -> ModelRegistry:
310
+ """Get the global model registry instance."""
311
+ return _model_registry
312
+
313
+
314
+ def clear_model_registry() -> None:
315
+ """Clear all model registrations. Useful for testing."""
316
+ _model_registry.clear()
317
+
318
+
319
+ # =============================================================================
320
+ # SCHEMA PATH FUNCTIONS
321
+ # =============================================================================
322
+
323
+
324
+ def register_schema_path(path: str) -> None:
325
+ """
326
+ Register a schema search path.
327
+
328
+ Paths registered here are searched BEFORE built-in package schemas.
329
+
330
+ Example:
331
+ import rem
332
+ rem.register_schema_path("/app/custom-agents")
333
+
334
+ # Now load_agent_schema("my-agent") will find /app/custom-agents/my-agent.yaml
335
+ """
336
+ _schema_path_registry.register(path)
337
+
338
+
339
+ def register_schema_paths(*paths: str) -> None:
340
+ """
341
+ Register multiple schema paths at once.
342
+
343
+ Example:
344
+ import rem
345
+ rem.register_schema_paths("/app/agents", "/app/evaluators")
346
+ """
347
+ _schema_path_registry.register_many(*paths)
348
+
349
+
350
+ def get_schema_path_registry() -> SchemaPathRegistry:
351
+ """Get the global schema path registry instance."""
352
+ return _schema_path_registry
353
+
354
+
355
+ def get_schema_paths() -> list[str]:
356
+ """
357
+ Get all registered schema paths (registry + SCHEMA__PATHS env var).
358
+
359
+ Returns:
360
+ List of directory paths to search for schemas
361
+ """
362
+ return _schema_path_registry.get_paths()
363
+
364
+
365
+ def clear_schema_path_registry() -> None:
366
+ """Clear all schema path registrations. Useful for testing."""
367
+ _schema_path_registry.clear()