remdb 0.3.242__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 (235) hide show
  1. rem/__init__.py +129 -0
  2. rem/agentic/README.md +760 -0
  3. rem/agentic/__init__.py +54 -0
  4. rem/agentic/agents/README.md +155 -0
  5. rem/agentic/agents/__init__.py +38 -0
  6. rem/agentic/agents/agent_manager.py +311 -0
  7. rem/agentic/agents/sse_simulator.py +502 -0
  8. rem/agentic/context.py +425 -0
  9. rem/agentic/context_builder.py +360 -0
  10. rem/agentic/llm_provider_models.py +301 -0
  11. rem/agentic/mcp/__init__.py +0 -0
  12. rem/agentic/mcp/tool_wrapper.py +273 -0
  13. rem/agentic/otel/__init__.py +5 -0
  14. rem/agentic/otel/setup.py +240 -0
  15. rem/agentic/providers/phoenix.py +926 -0
  16. rem/agentic/providers/pydantic_ai.py +854 -0
  17. rem/agentic/query.py +117 -0
  18. rem/agentic/query_helper.py +89 -0
  19. rem/agentic/schema.py +737 -0
  20. rem/agentic/serialization.py +245 -0
  21. rem/agentic/tools/__init__.py +5 -0
  22. rem/agentic/tools/rem_tools.py +242 -0
  23. rem/api/README.md +657 -0
  24. rem/api/deps.py +253 -0
  25. rem/api/main.py +460 -0
  26. rem/api/mcp_router/prompts.py +182 -0
  27. rem/api/mcp_router/resources.py +820 -0
  28. rem/api/mcp_router/server.py +243 -0
  29. rem/api/mcp_router/tools.py +1605 -0
  30. rem/api/middleware/tracking.py +172 -0
  31. rem/api/routers/admin.py +520 -0
  32. rem/api/routers/auth.py +898 -0
  33. rem/api/routers/chat/__init__.py +5 -0
  34. rem/api/routers/chat/child_streaming.py +394 -0
  35. rem/api/routers/chat/completions.py +702 -0
  36. rem/api/routers/chat/json_utils.py +76 -0
  37. rem/api/routers/chat/models.py +202 -0
  38. rem/api/routers/chat/otel_utils.py +33 -0
  39. rem/api/routers/chat/sse_events.py +546 -0
  40. rem/api/routers/chat/streaming.py +950 -0
  41. rem/api/routers/chat/streaming_utils.py +327 -0
  42. rem/api/routers/common.py +18 -0
  43. rem/api/routers/dev.py +87 -0
  44. rem/api/routers/feedback.py +276 -0
  45. rem/api/routers/messages.py +620 -0
  46. rem/api/routers/models.py +86 -0
  47. rem/api/routers/query.py +362 -0
  48. rem/api/routers/shared_sessions.py +422 -0
  49. rem/auth/README.md +258 -0
  50. rem/auth/__init__.py +36 -0
  51. rem/auth/jwt.py +367 -0
  52. rem/auth/middleware.py +318 -0
  53. rem/auth/providers/__init__.py +16 -0
  54. rem/auth/providers/base.py +376 -0
  55. rem/auth/providers/email.py +215 -0
  56. rem/auth/providers/google.py +163 -0
  57. rem/auth/providers/microsoft.py +237 -0
  58. rem/cli/README.md +517 -0
  59. rem/cli/__init__.py +8 -0
  60. rem/cli/commands/README.md +299 -0
  61. rem/cli/commands/__init__.py +3 -0
  62. rem/cli/commands/ask.py +549 -0
  63. rem/cli/commands/cluster.py +1808 -0
  64. rem/cli/commands/configure.py +495 -0
  65. rem/cli/commands/db.py +828 -0
  66. rem/cli/commands/dreaming.py +324 -0
  67. rem/cli/commands/experiments.py +1698 -0
  68. rem/cli/commands/mcp.py +66 -0
  69. rem/cli/commands/process.py +388 -0
  70. rem/cli/commands/query.py +109 -0
  71. rem/cli/commands/scaffold.py +47 -0
  72. rem/cli/commands/schema.py +230 -0
  73. rem/cli/commands/serve.py +106 -0
  74. rem/cli/commands/session.py +453 -0
  75. rem/cli/dreaming.py +363 -0
  76. rem/cli/main.py +123 -0
  77. rem/config.py +244 -0
  78. rem/mcp_server.py +41 -0
  79. rem/models/core/__init__.py +49 -0
  80. rem/models/core/core_model.py +70 -0
  81. rem/models/core/engram.py +333 -0
  82. rem/models/core/experiment.py +672 -0
  83. rem/models/core/inline_edge.py +132 -0
  84. rem/models/core/rem_query.py +246 -0
  85. rem/models/entities/__init__.py +68 -0
  86. rem/models/entities/domain_resource.py +38 -0
  87. rem/models/entities/feedback.py +123 -0
  88. rem/models/entities/file.py +57 -0
  89. rem/models/entities/image_resource.py +88 -0
  90. rem/models/entities/message.py +64 -0
  91. rem/models/entities/moment.py +123 -0
  92. rem/models/entities/ontology.py +181 -0
  93. rem/models/entities/ontology_config.py +131 -0
  94. rem/models/entities/resource.py +95 -0
  95. rem/models/entities/schema.py +87 -0
  96. rem/models/entities/session.py +84 -0
  97. rem/models/entities/shared_session.py +180 -0
  98. rem/models/entities/subscriber.py +175 -0
  99. rem/models/entities/user.py +93 -0
  100. rem/py.typed +0 -0
  101. rem/registry.py +373 -0
  102. rem/schemas/README.md +507 -0
  103. rem/schemas/__init__.py +6 -0
  104. rem/schemas/agents/README.md +92 -0
  105. rem/schemas/agents/core/agent-builder.yaml +235 -0
  106. rem/schemas/agents/core/moment-builder.yaml +178 -0
  107. rem/schemas/agents/core/rem-query-agent.yaml +226 -0
  108. rem/schemas/agents/core/resource-affinity-assessor.yaml +99 -0
  109. rem/schemas/agents/core/simple-assistant.yaml +19 -0
  110. rem/schemas/agents/core/user-profile-builder.yaml +163 -0
  111. rem/schemas/agents/examples/contract-analyzer.yaml +317 -0
  112. rem/schemas/agents/examples/contract-extractor.yaml +134 -0
  113. rem/schemas/agents/examples/cv-parser.yaml +263 -0
  114. rem/schemas/agents/examples/hello-world.yaml +37 -0
  115. rem/schemas/agents/examples/query.yaml +54 -0
  116. rem/schemas/agents/examples/simple.yaml +21 -0
  117. rem/schemas/agents/examples/test.yaml +29 -0
  118. rem/schemas/agents/rem.yaml +132 -0
  119. rem/schemas/evaluators/hello-world/default.yaml +77 -0
  120. rem/schemas/evaluators/rem/faithfulness.yaml +219 -0
  121. rem/schemas/evaluators/rem/lookup-correctness.yaml +182 -0
  122. rem/schemas/evaluators/rem/retrieval-precision.yaml +199 -0
  123. rem/schemas/evaluators/rem/retrieval-recall.yaml +211 -0
  124. rem/schemas/evaluators/rem/search-correctness.yaml +192 -0
  125. rem/services/__init__.py +18 -0
  126. rem/services/audio/INTEGRATION.md +308 -0
  127. rem/services/audio/README.md +376 -0
  128. rem/services/audio/__init__.py +15 -0
  129. rem/services/audio/chunker.py +354 -0
  130. rem/services/audio/transcriber.py +259 -0
  131. rem/services/content/README.md +1269 -0
  132. rem/services/content/__init__.py +5 -0
  133. rem/services/content/providers.py +760 -0
  134. rem/services/content/service.py +762 -0
  135. rem/services/dreaming/README.md +230 -0
  136. rem/services/dreaming/__init__.py +53 -0
  137. rem/services/dreaming/affinity_service.py +322 -0
  138. rem/services/dreaming/moment_service.py +251 -0
  139. rem/services/dreaming/ontology_service.py +54 -0
  140. rem/services/dreaming/user_model_service.py +297 -0
  141. rem/services/dreaming/utils.py +39 -0
  142. rem/services/email/__init__.py +10 -0
  143. rem/services/email/service.py +522 -0
  144. rem/services/email/templates.py +360 -0
  145. rem/services/embeddings/__init__.py +11 -0
  146. rem/services/embeddings/api.py +127 -0
  147. rem/services/embeddings/worker.py +435 -0
  148. rem/services/fs/README.md +662 -0
  149. rem/services/fs/__init__.py +62 -0
  150. rem/services/fs/examples.py +206 -0
  151. rem/services/fs/examples_paths.py +204 -0
  152. rem/services/fs/git_provider.py +935 -0
  153. rem/services/fs/local_provider.py +760 -0
  154. rem/services/fs/parsing-hooks-examples.md +172 -0
  155. rem/services/fs/paths.py +276 -0
  156. rem/services/fs/provider.py +460 -0
  157. rem/services/fs/s3_provider.py +1042 -0
  158. rem/services/fs/service.py +186 -0
  159. rem/services/git/README.md +1075 -0
  160. rem/services/git/__init__.py +17 -0
  161. rem/services/git/service.py +469 -0
  162. rem/services/phoenix/EXPERIMENT_DESIGN.md +1146 -0
  163. rem/services/phoenix/README.md +453 -0
  164. rem/services/phoenix/__init__.py +46 -0
  165. rem/services/phoenix/client.py +960 -0
  166. rem/services/phoenix/config.py +88 -0
  167. rem/services/phoenix/prompt_labels.py +477 -0
  168. rem/services/postgres/README.md +757 -0
  169. rem/services/postgres/__init__.py +49 -0
  170. rem/services/postgres/diff_service.py +599 -0
  171. rem/services/postgres/migration_service.py +427 -0
  172. rem/services/postgres/programmable_diff_service.py +635 -0
  173. rem/services/postgres/pydantic_to_sqlalchemy.py +562 -0
  174. rem/services/postgres/register_type.py +353 -0
  175. rem/services/postgres/repository.py +481 -0
  176. rem/services/postgres/schema_generator.py +661 -0
  177. rem/services/postgres/service.py +802 -0
  178. rem/services/postgres/sql_builder.py +355 -0
  179. rem/services/rate_limit.py +113 -0
  180. rem/services/rem/README.md +318 -0
  181. rem/services/rem/__init__.py +23 -0
  182. rem/services/rem/exceptions.py +71 -0
  183. rem/services/rem/executor.py +293 -0
  184. rem/services/rem/parser.py +180 -0
  185. rem/services/rem/queries.py +196 -0
  186. rem/services/rem/query.py +371 -0
  187. rem/services/rem/service.py +608 -0
  188. rem/services/session/README.md +374 -0
  189. rem/services/session/__init__.py +13 -0
  190. rem/services/session/compression.py +488 -0
  191. rem/services/session/pydantic_messages.py +310 -0
  192. rem/services/session/reload.py +85 -0
  193. rem/services/user_service.py +130 -0
  194. rem/settings.py +1877 -0
  195. rem/sql/background_indexes.sql +52 -0
  196. rem/sql/migrations/001_install.sql +983 -0
  197. rem/sql/migrations/002_install_models.sql +3157 -0
  198. rem/sql/migrations/003_optional_extensions.sql +326 -0
  199. rem/sql/migrations/004_cache_system.sql +282 -0
  200. rem/sql/migrations/005_schema_update.sql +145 -0
  201. rem/sql/migrations/migrate_session_id_to_uuid.sql +45 -0
  202. rem/utils/AGENTIC_CHUNKING.md +597 -0
  203. rem/utils/README.md +628 -0
  204. rem/utils/__init__.py +61 -0
  205. rem/utils/agentic_chunking.py +622 -0
  206. rem/utils/batch_ops.py +343 -0
  207. rem/utils/chunking.py +108 -0
  208. rem/utils/clip_embeddings.py +276 -0
  209. rem/utils/constants.py +97 -0
  210. rem/utils/date_utils.py +228 -0
  211. rem/utils/dict_utils.py +98 -0
  212. rem/utils/embeddings.py +436 -0
  213. rem/utils/examples/embeddings_example.py +305 -0
  214. rem/utils/examples/sql_types_example.py +202 -0
  215. rem/utils/files.py +323 -0
  216. rem/utils/markdown.py +16 -0
  217. rem/utils/mime_types.py +158 -0
  218. rem/utils/model_helpers.py +492 -0
  219. rem/utils/schema_loader.py +649 -0
  220. rem/utils/sql_paths.py +146 -0
  221. rem/utils/sql_types.py +350 -0
  222. rem/utils/user_id.py +81 -0
  223. rem/utils/vision.py +325 -0
  224. rem/workers/README.md +506 -0
  225. rem/workers/__init__.py +7 -0
  226. rem/workers/db_listener.py +579 -0
  227. rem/workers/db_maintainer.py +74 -0
  228. rem/workers/dreaming.py +502 -0
  229. rem/workers/engram_processor.py +312 -0
  230. rem/workers/sqs_file_processor.py +193 -0
  231. rem/workers/unlogged_maintainer.py +463 -0
  232. remdb-0.3.242.dist-info/METADATA +1632 -0
  233. remdb-0.3.242.dist-info/RECORD +235 -0
  234. remdb-0.3.242.dist-info/WHEEL +4 -0
  235. remdb-0.3.242.dist-info/entry_points.txt +2 -0
rem/config.py ADDED
@@ -0,0 +1,244 @@
1
+ """
2
+ REM Configuration Management.
3
+
4
+ Provides persistent configuration in ~/.rem/config.yaml with environment variable overrides.
5
+
6
+ Configuration Precedence (highest to lowest):
7
+ 1. Environment variables (POSTGRES__CONNECTION_STRING, etc.)
8
+ 2. ~/.rem/config.yaml (user configuration)
9
+ 3. Default values (from settings.py)
10
+
11
+ File Format (~/.rem/config.yaml):
12
+ postgres:
13
+ connection_string: postgresql://user:pass@localhost:5432/rem
14
+ pool_min_size: 5
15
+ pool_max_size: 20
16
+
17
+ llm:
18
+ default_model: openai:gpt-4.1
19
+ openai_api_key: sk-...
20
+ anthropic_api_key: sk-ant-...
21
+
22
+ s3:
23
+ bucket_name: rem-storage
24
+ region: us-east-1
25
+ endpoint_url: http://localhost:9000
26
+
27
+ # Additional custom environment variables
28
+ env:
29
+ MY_CUSTOM_VAR: value
30
+
31
+ Usage:
32
+ from rem.config import load_config, get_config_path, ensure_config_dir
33
+
34
+ # Load configuration and merge with environment
35
+ config = load_config()
36
+
37
+ # Get configuration file path
38
+ config_path = get_config_path()
39
+
40
+ # Ensure ~/.rem directory exists
41
+ ensure_config_dir()
42
+ """
43
+
44
+ import os
45
+ from pathlib import Path
46
+ from typing import Any
47
+
48
+ import yaml
49
+ from loguru import logger
50
+
51
+
52
+ def get_rem_home() -> Path:
53
+ """
54
+ Get REM home directory (~/.rem).
55
+
56
+ Returns:
57
+ Path to ~/.rem directory
58
+ """
59
+ return Path.home() / ".rem"
60
+
61
+
62
+ def ensure_config_dir() -> Path:
63
+ """
64
+ Ensure ~/.rem directory exists.
65
+
66
+ Returns:
67
+ Path to ~/.rem directory
68
+ """
69
+ rem_home = get_rem_home()
70
+ rem_home.mkdir(exist_ok=True, mode=0o700) # User-only permissions
71
+ return rem_home
72
+
73
+
74
+ def get_config_path() -> Path:
75
+ """
76
+ Get path to configuration file (~/.rem/config.yaml).
77
+
78
+ Returns:
79
+ Path to configuration file
80
+ """
81
+ return get_rem_home() / "config.yaml"
82
+
83
+
84
+ def config_exists() -> bool:
85
+ """
86
+ Check if configuration file exists.
87
+
88
+ Returns:
89
+ True if ~/.rem/config.yaml exists
90
+ """
91
+ return get_config_path().exists()
92
+
93
+
94
+ def load_config() -> dict[str, Any]:
95
+ """
96
+ Load configuration from ~/.rem/config.yaml.
97
+
98
+ Set REM_SKIP_CONFIG=1 to skip loading the config file (useful when using .env files).
99
+
100
+ Returns:
101
+ Configuration dictionary (empty if file doesn't exist or skipped)
102
+ """
103
+ # Allow skipping config file via environment variable
104
+ if os.environ.get("REM_SKIP_CONFIG", "").lower() in ("1", "true", "yes"):
105
+ logger.debug("Skipping config file (REM_SKIP_CONFIG is set)")
106
+ return {}
107
+
108
+ config_path = get_config_path()
109
+
110
+ if not config_path.exists():
111
+ logger.debug(f"Configuration file not found: {config_path}")
112
+ return {}
113
+
114
+ try:
115
+ with open(config_path, "r") as f:
116
+ config = yaml.safe_load(f) or {}
117
+ logger.debug(f"Loaded configuration from {config_path}")
118
+ return config
119
+ except Exception as e:
120
+ logger.warning(f"Failed to load configuration from {config_path}: {e}")
121
+ return {}
122
+
123
+
124
+ def save_config(config: dict[str, Any]) -> None:
125
+ """
126
+ Save configuration to ~/.rem/config.yaml.
127
+
128
+ Args:
129
+ config: Configuration dictionary to save
130
+ """
131
+ ensure_config_dir()
132
+ config_path = get_config_path()
133
+
134
+ try:
135
+ with open(config_path, "w") as f:
136
+ yaml.dump(config, f, default_flow_style=False, sort_keys=False)
137
+ logger.info(f"Configuration saved to {config_path}")
138
+ except Exception as e:
139
+ logger.error(f"Failed to save configuration to {config_path}: {e}")
140
+ raise
141
+
142
+
143
+ def merge_config_to_env(config: dict[str, Any]) -> None:
144
+ """
145
+ Merge configuration file into environment variables.
146
+
147
+ This allows Pydantic Settings to pick up values from the config file
148
+ by setting environment variables before settings initialization.
149
+
150
+ Precedence:
151
+ - Existing environment variables are NOT overwritten
152
+ - Only sets env vars if they don't already exist
153
+
154
+ Args:
155
+ config: Configuration dictionary from ~/.rem/config.yaml
156
+
157
+ Example:
158
+ config = {"postgres": {"connection_string": "postgresql://..."}}
159
+ merge_config_to_env(config)
160
+ # Sets POSTGRES__CONNECTION_STRING if not already set
161
+ """
162
+ # Handle custom env vars first
163
+ if "env" in config:
164
+ for key, value in config["env"].items():
165
+ if key not in os.environ:
166
+ os.environ[key] = str(value)
167
+ logger.debug(f"Set env var from config: {key}")
168
+
169
+ # Convert nested config to environment variables
170
+ for section, values in config.items():
171
+ if section == "env":
172
+ continue # Already handled
173
+
174
+ if not isinstance(values, dict):
175
+ continue
176
+
177
+ for key, value in values.items():
178
+ # Convert to environment variable format (SECTION__KEY)
179
+ env_key = f"{section.upper()}__{key.upper()}"
180
+
181
+ # Only set if not already in environment
182
+ if env_key not in os.environ:
183
+ os.environ[env_key] = str(value)
184
+ logger.debug(f"Set env var from config: {env_key}")
185
+
186
+
187
+ def validate_config(config: dict[str, Any]) -> list[str]:
188
+ """
189
+ Validate configuration for required fields.
190
+
191
+ Args:
192
+ config: Configuration dictionary
193
+
194
+ Returns:
195
+ List of validation error messages (empty if valid)
196
+ """
197
+ errors = []
198
+
199
+ # Postgres connection is required
200
+ postgres = config.get("postgres", {})
201
+ if not postgres.get("connection_string"):
202
+ errors.append("PostgreSQL connection string is required (postgres.connection_string)")
203
+
204
+ # Validate connection string format
205
+ conn_str = postgres.get("connection_string", "")
206
+ if conn_str and not conn_str.startswith("postgresql://"):
207
+ errors.append("PostgreSQL connection string must start with 'postgresql://'")
208
+
209
+ return errors
210
+
211
+
212
+ def get_default_config() -> dict[str, Any]:
213
+ """
214
+ Get default configuration template for new installations.
215
+
216
+ Returns:
217
+ Default configuration dictionary
218
+ """
219
+ return {
220
+ "postgres": {
221
+ "connection_string": "postgresql://rem:rem@localhost:5432/rem",
222
+ "pool_min_size": 5,
223
+ "pool_max_size": 20,
224
+ },
225
+ "llm": {
226
+ "default_model": "openai:gpt-4.1",
227
+ "default_temperature": 0.5,
228
+ # API keys will be prompted for in wizard
229
+ # "openai_api_key": "",
230
+ # "anthropic_api_key": "",
231
+ },
232
+ "s3": {
233
+ "bucket_name": "rem-storage",
234
+ "region": "us-east-1",
235
+ # Optional fields
236
+ # "endpoint_url": "http://localhost:9000", # For MinIO
237
+ # "access_key_id": "",
238
+ # "secret_access_key": "",
239
+ },
240
+ "env": {
241
+ # Custom environment variables
242
+ # "MY_VAR": "value",
243
+ },
244
+ }
rem/mcp_server.py ADDED
@@ -0,0 +1,41 @@
1
+ """
2
+ In-process MCP server for agent tool loading.
3
+
4
+ This module creates an MCP server instance that is imported directly by agents.
5
+ NO subprocess, NO stdio transport - just direct function calls in the same process.
6
+
7
+ The server exposes REM tools and resources that agents can call directly.
8
+ Services are initialized lazily on first tool call (see tools.py).
9
+ """
10
+
11
+ import asyncio
12
+
13
+ from rem.api.mcp_router.server import create_mcp_server
14
+
15
+ # Create MCP server instance (is_local=True for local/in-process usage)
16
+ # No initialization needed - tools handle lazy init of services
17
+ mcp = create_mcp_server(is_local=True)
18
+
19
+
20
+ if __name__ == "__main__":
21
+ # When run directly via CLI, start in stdio mode with service initialization
22
+ from rem.api.mcp_router.tools import init_services
23
+ from rem.services.postgres import get_postgres_service
24
+ from rem.services.rem import RemService
25
+
26
+ async def run_stdio():
27
+ """Run MCP server in stdio mode with services."""
28
+ db = get_postgres_service()
29
+ if not db:
30
+ raise RuntimeError("PostgreSQL service not available")
31
+ await db.connect()
32
+ rem_service = RemService(postgres_service=db)
33
+ init_services(postgres_service=db, rem_service=rem_service)
34
+
35
+ # Run server
36
+ await mcp.run_async(transport="stdio")
37
+
38
+ # Cleanup
39
+ await db.disconnect()
40
+
41
+ asyncio.run(run_stdio())
@@ -0,0 +1,49 @@
1
+ """
2
+ REM Core Models
3
+
4
+ Core types and base models for the REM (Resource-Entity-Moment) system.
5
+
6
+ REM is a unified memory infrastructure that enables LLM-augmented iterated retrieval
7
+ through natural language interfaces. Unlike traditional databases that assume single-shot
8
+ queries with known schemas, REM is architected for multi-turn conversations where:
9
+
10
+ 1. LLMs don't know internal IDs - they work with natural language labels
11
+ 2. Information needs emerge incrementally through exploration
12
+ 3. Multi-stage exploration is essential: find entity → explore neighborhood → traverse relationships
13
+
14
+ Key Design Principles:
15
+ - Graph edges reference entity LABELS (natural language), not UUIDs
16
+ - Natural language surface area for all queries
17
+ - Schema-agnostic operations (LOOKUP, FUZZY, TRAVERSE)
18
+ - O(1) performance guarantees for entity resolution
19
+ - Iterated retrieval with stage tracking and memos
20
+ """
21
+
22
+ from .core_model import CoreModel
23
+ from .inline_edge import InlineEdge, InlineEdges
24
+ from .rem_query import (
25
+ FuzzyParameters,
26
+ LookupParameters,
27
+ QueryType,
28
+ RemQuery,
29
+ SearchParameters,
30
+ SQLParameters,
31
+ TraverseParameters,
32
+ TraverseResponse,
33
+ TraverseStage,
34
+ )
35
+
36
+ __all__ = [
37
+ "CoreModel",
38
+ "InlineEdge",
39
+ "InlineEdges",
40
+ "QueryType",
41
+ "LookupParameters",
42
+ "FuzzyParameters",
43
+ "SearchParameters",
44
+ "SQLParameters",
45
+ "TraverseParameters",
46
+ "RemQuery",
47
+ "TraverseStage",
48
+ "TraverseResponse",
49
+ ]
@@ -0,0 +1,70 @@
1
+ """
2
+ CoreModel - Base model for all REM entities.
3
+
4
+ All REM entities (Resources, Messages, Users, Files, Moments) inherit from CoreModel,
5
+ which provides:
6
+ - Identity (id - UUID or string, generated per model type)
7
+ - Temporal tracking (created_at, updated_at, deleted_at)
8
+ - Multi-tenancy (tenant_id)
9
+ - Ownership (user_id)
10
+ - Graph connectivity (graph_edges)
11
+ - Flexible metadata (metadata dict)
12
+ - Tagging (tags list)
13
+ """
14
+
15
+ from datetime import datetime, timezone
16
+ from typing import Optional, Union
17
+ from uuid import UUID
18
+
19
+ from pydantic import BaseModel, ConfigDict, Field
20
+
21
+
22
+ class CoreModel(BaseModel):
23
+ """
24
+ Base model for all REM entities.
25
+
26
+ Provides system-level fields for:
27
+ - Identity management (id)
28
+ - Temporal tracking (created_at, updated_at, deleted_at)
29
+ - Multi-tenancy isolation (tenant_id)
30
+ - Ownership tracking (user_id)
31
+ - Graph connectivity (graph_edges)
32
+ - Flexible metadata storage (metadata, tags)
33
+
34
+ Note: ID generation is handled per model type, not by CoreModel.
35
+ Each entity model should generate IDs with appropriate prefixes or labels.
36
+ """
37
+
38
+ id: Union[UUID, str, None] = Field(
39
+ default=None,
40
+ description="Unique identifier (UUID or string, generated per model type). Generated automatically if not provided."
41
+ )
42
+ created_at: datetime = Field(
43
+ default_factory=lambda: datetime.now(timezone.utc).replace(tzinfo=None), description="Entity creation timestamp"
44
+ )
45
+ updated_at: datetime = Field(
46
+ default_factory=lambda: datetime.now(timezone.utc).replace(tzinfo=None), description="Last update timestamp"
47
+ )
48
+ deleted_at: Optional[datetime] = Field(
49
+ default=None, description="Soft deletion timestamp"
50
+ )
51
+ tenant_id: Optional[str] = Field(
52
+ default=None, description="Tenant identifier for multi-tenancy isolation"
53
+ )
54
+ user_id: Optional[str] = Field(
55
+ default=None,
56
+ description=(
57
+ "Owner user identifier (tenant-scoped). This is a VARCHAR(256), not a UUID, "
58
+ "to allow flexibility for external identity providers. Typically generated as "
59
+ "a hash of the user's email address. In future, other strong unique claims "
60
+ "(e.g., OAuth sub, verified phone) could also be used for generation."
61
+ ),
62
+ )
63
+ graph_edges: list[dict] = Field(
64
+ default_factory=list,
65
+ description="Knowledge graph edges stored as InlineEdge dicts",
66
+ )
67
+ metadata: dict = Field(
68
+ default_factory=dict, description="Flexible metadata storage"
69
+ )
70
+ tags: list[str] = Field(default_factory=list, description="Entity tags")