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.
- rem/__init__.py +129 -0
- rem/agentic/README.md +760 -0
- rem/agentic/__init__.py +54 -0
- rem/agentic/agents/README.md +155 -0
- rem/agentic/agents/__init__.py +38 -0
- rem/agentic/agents/agent_manager.py +311 -0
- rem/agentic/agents/sse_simulator.py +502 -0
- rem/agentic/context.py +425 -0
- rem/agentic/context_builder.py +360 -0
- rem/agentic/llm_provider_models.py +301 -0
- rem/agentic/mcp/__init__.py +0 -0
- rem/agentic/mcp/tool_wrapper.py +273 -0
- rem/agentic/otel/__init__.py +5 -0
- rem/agentic/otel/setup.py +240 -0
- rem/agentic/providers/phoenix.py +926 -0
- rem/agentic/providers/pydantic_ai.py +854 -0
- rem/agentic/query.py +117 -0
- rem/agentic/query_helper.py +89 -0
- rem/agentic/schema.py +737 -0
- rem/agentic/serialization.py +245 -0
- rem/agentic/tools/__init__.py +5 -0
- rem/agentic/tools/rem_tools.py +242 -0
- rem/api/README.md +657 -0
- rem/api/deps.py +253 -0
- rem/api/main.py +460 -0
- rem/api/mcp_router/prompts.py +182 -0
- rem/api/mcp_router/resources.py +820 -0
- rem/api/mcp_router/server.py +243 -0
- rem/api/mcp_router/tools.py +1605 -0
- rem/api/middleware/tracking.py +172 -0
- rem/api/routers/admin.py +520 -0
- rem/api/routers/auth.py +898 -0
- rem/api/routers/chat/__init__.py +5 -0
- rem/api/routers/chat/child_streaming.py +394 -0
- rem/api/routers/chat/completions.py +702 -0
- rem/api/routers/chat/json_utils.py +76 -0
- rem/api/routers/chat/models.py +202 -0
- rem/api/routers/chat/otel_utils.py +33 -0
- rem/api/routers/chat/sse_events.py +546 -0
- rem/api/routers/chat/streaming.py +950 -0
- rem/api/routers/chat/streaming_utils.py +327 -0
- rem/api/routers/common.py +18 -0
- rem/api/routers/dev.py +87 -0
- rem/api/routers/feedback.py +276 -0
- rem/api/routers/messages.py +620 -0
- rem/api/routers/models.py +86 -0
- rem/api/routers/query.py +362 -0
- rem/api/routers/shared_sessions.py +422 -0
- rem/auth/README.md +258 -0
- rem/auth/__init__.py +36 -0
- rem/auth/jwt.py +367 -0
- rem/auth/middleware.py +318 -0
- rem/auth/providers/__init__.py +16 -0
- rem/auth/providers/base.py +376 -0
- rem/auth/providers/email.py +215 -0
- rem/auth/providers/google.py +163 -0
- rem/auth/providers/microsoft.py +237 -0
- rem/cli/README.md +517 -0
- rem/cli/__init__.py +8 -0
- rem/cli/commands/README.md +299 -0
- rem/cli/commands/__init__.py +3 -0
- rem/cli/commands/ask.py +549 -0
- rem/cli/commands/cluster.py +1808 -0
- rem/cli/commands/configure.py +495 -0
- rem/cli/commands/db.py +828 -0
- rem/cli/commands/dreaming.py +324 -0
- rem/cli/commands/experiments.py +1698 -0
- rem/cli/commands/mcp.py +66 -0
- rem/cli/commands/process.py +388 -0
- rem/cli/commands/query.py +109 -0
- rem/cli/commands/scaffold.py +47 -0
- rem/cli/commands/schema.py +230 -0
- rem/cli/commands/serve.py +106 -0
- rem/cli/commands/session.py +453 -0
- rem/cli/dreaming.py +363 -0
- rem/cli/main.py +123 -0
- rem/config.py +244 -0
- rem/mcp_server.py +41 -0
- rem/models/core/__init__.py +49 -0
- rem/models/core/core_model.py +70 -0
- rem/models/core/engram.py +333 -0
- rem/models/core/experiment.py +672 -0
- rem/models/core/inline_edge.py +132 -0
- rem/models/core/rem_query.py +246 -0
- rem/models/entities/__init__.py +68 -0
- rem/models/entities/domain_resource.py +38 -0
- rem/models/entities/feedback.py +123 -0
- rem/models/entities/file.py +57 -0
- rem/models/entities/image_resource.py +88 -0
- rem/models/entities/message.py +64 -0
- rem/models/entities/moment.py +123 -0
- rem/models/entities/ontology.py +181 -0
- rem/models/entities/ontology_config.py +131 -0
- rem/models/entities/resource.py +95 -0
- rem/models/entities/schema.py +87 -0
- rem/models/entities/session.py +84 -0
- rem/models/entities/shared_session.py +180 -0
- rem/models/entities/subscriber.py +175 -0
- rem/models/entities/user.py +93 -0
- rem/py.typed +0 -0
- rem/registry.py +373 -0
- rem/schemas/README.md +507 -0
- rem/schemas/__init__.py +6 -0
- rem/schemas/agents/README.md +92 -0
- rem/schemas/agents/core/agent-builder.yaml +235 -0
- rem/schemas/agents/core/moment-builder.yaml +178 -0
- rem/schemas/agents/core/rem-query-agent.yaml +226 -0
- rem/schemas/agents/core/resource-affinity-assessor.yaml +99 -0
- rem/schemas/agents/core/simple-assistant.yaml +19 -0
- rem/schemas/agents/core/user-profile-builder.yaml +163 -0
- rem/schemas/agents/examples/contract-analyzer.yaml +317 -0
- rem/schemas/agents/examples/contract-extractor.yaml +134 -0
- rem/schemas/agents/examples/cv-parser.yaml +263 -0
- rem/schemas/agents/examples/hello-world.yaml +37 -0
- rem/schemas/agents/examples/query.yaml +54 -0
- rem/schemas/agents/examples/simple.yaml +21 -0
- rem/schemas/agents/examples/test.yaml +29 -0
- rem/schemas/agents/rem.yaml +132 -0
- rem/schemas/evaluators/hello-world/default.yaml +77 -0
- rem/schemas/evaluators/rem/faithfulness.yaml +219 -0
- rem/schemas/evaluators/rem/lookup-correctness.yaml +182 -0
- rem/schemas/evaluators/rem/retrieval-precision.yaml +199 -0
- rem/schemas/evaluators/rem/retrieval-recall.yaml +211 -0
- rem/schemas/evaluators/rem/search-correctness.yaml +192 -0
- rem/services/__init__.py +18 -0
- rem/services/audio/INTEGRATION.md +308 -0
- rem/services/audio/README.md +376 -0
- rem/services/audio/__init__.py +15 -0
- rem/services/audio/chunker.py +354 -0
- rem/services/audio/transcriber.py +259 -0
- rem/services/content/README.md +1269 -0
- rem/services/content/__init__.py +5 -0
- rem/services/content/providers.py +760 -0
- rem/services/content/service.py +762 -0
- rem/services/dreaming/README.md +230 -0
- rem/services/dreaming/__init__.py +53 -0
- rem/services/dreaming/affinity_service.py +322 -0
- rem/services/dreaming/moment_service.py +251 -0
- rem/services/dreaming/ontology_service.py +54 -0
- rem/services/dreaming/user_model_service.py +297 -0
- rem/services/dreaming/utils.py +39 -0
- rem/services/email/__init__.py +10 -0
- rem/services/email/service.py +522 -0
- rem/services/email/templates.py +360 -0
- rem/services/embeddings/__init__.py +11 -0
- rem/services/embeddings/api.py +127 -0
- rem/services/embeddings/worker.py +435 -0
- rem/services/fs/README.md +662 -0
- rem/services/fs/__init__.py +62 -0
- rem/services/fs/examples.py +206 -0
- rem/services/fs/examples_paths.py +204 -0
- rem/services/fs/git_provider.py +935 -0
- rem/services/fs/local_provider.py +760 -0
- rem/services/fs/parsing-hooks-examples.md +172 -0
- rem/services/fs/paths.py +276 -0
- rem/services/fs/provider.py +460 -0
- rem/services/fs/s3_provider.py +1042 -0
- rem/services/fs/service.py +186 -0
- rem/services/git/README.md +1075 -0
- rem/services/git/__init__.py +17 -0
- rem/services/git/service.py +469 -0
- rem/services/phoenix/EXPERIMENT_DESIGN.md +1146 -0
- rem/services/phoenix/README.md +453 -0
- rem/services/phoenix/__init__.py +46 -0
- rem/services/phoenix/client.py +960 -0
- rem/services/phoenix/config.py +88 -0
- rem/services/phoenix/prompt_labels.py +477 -0
- rem/services/postgres/README.md +757 -0
- rem/services/postgres/__init__.py +49 -0
- rem/services/postgres/diff_service.py +599 -0
- rem/services/postgres/migration_service.py +427 -0
- rem/services/postgres/programmable_diff_service.py +635 -0
- rem/services/postgres/pydantic_to_sqlalchemy.py +562 -0
- rem/services/postgres/register_type.py +353 -0
- rem/services/postgres/repository.py +481 -0
- rem/services/postgres/schema_generator.py +661 -0
- rem/services/postgres/service.py +802 -0
- rem/services/postgres/sql_builder.py +355 -0
- rem/services/rate_limit.py +113 -0
- rem/services/rem/README.md +318 -0
- rem/services/rem/__init__.py +23 -0
- rem/services/rem/exceptions.py +71 -0
- rem/services/rem/executor.py +293 -0
- rem/services/rem/parser.py +180 -0
- rem/services/rem/queries.py +196 -0
- rem/services/rem/query.py +371 -0
- rem/services/rem/service.py +608 -0
- rem/services/session/README.md +374 -0
- rem/services/session/__init__.py +13 -0
- rem/services/session/compression.py +488 -0
- rem/services/session/pydantic_messages.py +310 -0
- rem/services/session/reload.py +85 -0
- rem/services/user_service.py +130 -0
- rem/settings.py +1877 -0
- rem/sql/background_indexes.sql +52 -0
- rem/sql/migrations/001_install.sql +983 -0
- rem/sql/migrations/002_install_models.sql +3157 -0
- rem/sql/migrations/003_optional_extensions.sql +326 -0
- rem/sql/migrations/004_cache_system.sql +282 -0
- rem/sql/migrations/005_schema_update.sql +145 -0
- rem/sql/migrations/migrate_session_id_to_uuid.sql +45 -0
- rem/utils/AGENTIC_CHUNKING.md +597 -0
- rem/utils/README.md +628 -0
- rem/utils/__init__.py +61 -0
- rem/utils/agentic_chunking.py +622 -0
- rem/utils/batch_ops.py +343 -0
- rem/utils/chunking.py +108 -0
- rem/utils/clip_embeddings.py +276 -0
- rem/utils/constants.py +97 -0
- rem/utils/date_utils.py +228 -0
- rem/utils/dict_utils.py +98 -0
- rem/utils/embeddings.py +436 -0
- rem/utils/examples/embeddings_example.py +305 -0
- rem/utils/examples/sql_types_example.py +202 -0
- rem/utils/files.py +323 -0
- rem/utils/markdown.py +16 -0
- rem/utils/mime_types.py +158 -0
- rem/utils/model_helpers.py +492 -0
- rem/utils/schema_loader.py +649 -0
- rem/utils/sql_paths.py +146 -0
- rem/utils/sql_types.py +350 -0
- rem/utils/user_id.py +81 -0
- rem/utils/vision.py +325 -0
- rem/workers/README.md +506 -0
- rem/workers/__init__.py +7 -0
- rem/workers/db_listener.py +579 -0
- rem/workers/db_maintainer.py +74 -0
- rem/workers/dreaming.py +502 -0
- rem/workers/engram_processor.py +312 -0
- rem/workers/sqs_file_processor.py +193 -0
- rem/workers/unlogged_maintainer.py +463 -0
- remdb-0.3.242.dist-info/METADATA +1632 -0
- remdb-0.3.242.dist-info/RECORD +235 -0
- remdb-0.3.242.dist-info/WHEEL +4 -0
- 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")
|