remdb 0.3.7__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.
- rem/__init__.py +2 -0
- rem/agentic/README.md +650 -0
- rem/agentic/__init__.py +39 -0
- rem/agentic/agents/README.md +155 -0
- rem/agentic/agents/__init__.py +8 -0
- rem/agentic/context.py +148 -0
- rem/agentic/context_builder.py +329 -0
- rem/agentic/mcp/__init__.py +0 -0
- rem/agentic/mcp/tool_wrapper.py +107 -0
- rem/agentic/otel/__init__.py +5 -0
- rem/agentic/otel/setup.py +151 -0
- rem/agentic/providers/phoenix.py +674 -0
- rem/agentic/providers/pydantic_ai.py +572 -0
- rem/agentic/query.py +117 -0
- rem/agentic/query_helper.py +89 -0
- rem/agentic/schema.py +396 -0
- rem/agentic/serialization.py +245 -0
- rem/agentic/tools/__init__.py +5 -0
- rem/agentic/tools/rem_tools.py +231 -0
- rem/api/README.md +420 -0
- rem/api/main.py +324 -0
- rem/api/mcp_router/prompts.py +182 -0
- rem/api/mcp_router/resources.py +536 -0
- rem/api/mcp_router/server.py +213 -0
- rem/api/mcp_router/tools.py +584 -0
- rem/api/routers/auth.py +229 -0
- rem/api/routers/chat/__init__.py +5 -0
- rem/api/routers/chat/completions.py +281 -0
- rem/api/routers/chat/json_utils.py +76 -0
- rem/api/routers/chat/models.py +124 -0
- rem/api/routers/chat/streaming.py +185 -0
- rem/auth/README.md +258 -0
- rem/auth/__init__.py +26 -0
- rem/auth/middleware.py +100 -0
- rem/auth/providers/__init__.py +13 -0
- rem/auth/providers/base.py +376 -0
- rem/auth/providers/google.py +163 -0
- rem/auth/providers/microsoft.py +237 -0
- rem/cli/README.md +455 -0
- rem/cli/__init__.py +8 -0
- rem/cli/commands/README.md +126 -0
- rem/cli/commands/__init__.py +3 -0
- rem/cli/commands/ask.py +566 -0
- rem/cli/commands/configure.py +497 -0
- rem/cli/commands/db.py +493 -0
- rem/cli/commands/dreaming.py +324 -0
- rem/cli/commands/experiments.py +1302 -0
- rem/cli/commands/mcp.py +66 -0
- rem/cli/commands/process.py +245 -0
- rem/cli/commands/schema.py +183 -0
- rem/cli/commands/serve.py +106 -0
- rem/cli/dreaming.py +363 -0
- rem/cli/main.py +96 -0
- rem/config.py +237 -0
- rem/mcp_server.py +41 -0
- rem/models/core/__init__.py +49 -0
- rem/models/core/core_model.py +64 -0
- rem/models/core/engram.py +333 -0
- rem/models/core/experiment.py +628 -0
- rem/models/core/inline_edge.py +132 -0
- rem/models/core/rem_query.py +243 -0
- rem/models/entities/__init__.py +43 -0
- rem/models/entities/file.py +57 -0
- rem/models/entities/image_resource.py +88 -0
- rem/models/entities/message.py +35 -0
- rem/models/entities/moment.py +123 -0
- rem/models/entities/ontology.py +191 -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/user.py +85 -0
- rem/py.typed +0 -0
- rem/schemas/README.md +507 -0
- rem/schemas/__init__.py +6 -0
- rem/schemas/agents/README.md +92 -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 +128 -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 +16 -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 +801 -0
- rem/services/content/service.py +676 -0
- rem/services/dreaming/README.md +230 -0
- rem/services/dreaming/__init__.py +53 -0
- rem/services/dreaming/affinity_service.py +336 -0
- rem/services/dreaming/moment_service.py +264 -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/embeddings/__init__.py +11 -0
- rem/services/embeddings/api.py +120 -0
- rem/services/embeddings/worker.py +421 -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 +686 -0
- rem/services/phoenix/config.py +88 -0
- rem/services/phoenix/prompt_labels.py +477 -0
- rem/services/postgres/README.md +575 -0
- rem/services/postgres/__init__.py +23 -0
- rem/services/postgres/migration_service.py +427 -0
- rem/services/postgres/pydantic_to_sqlalchemy.py +232 -0
- rem/services/postgres/register_type.py +352 -0
- rem/services/postgres/repository.py +337 -0
- rem/services/postgres/schema_generator.py +379 -0
- rem/services/postgres/service.py +802 -0
- rem/services/postgres/sql_builder.py +354 -0
- rem/services/rem/README.md +304 -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 +145 -0
- rem/services/rem/queries.py +196 -0
- rem/services/rem/query.py +371 -0
- rem/services/rem/service.py +527 -0
- rem/services/session/README.md +374 -0
- rem/services/session/__init__.py +6 -0
- rem/services/session/compression.py +360 -0
- rem/services/session/reload.py +77 -0
- rem/settings.py +1235 -0
- rem/sql/002_install_models.sql +1068 -0
- rem/sql/background_indexes.sql +42 -0
- rem/sql/install_models.sql +1038 -0
- rem/sql/migrations/001_install.sql +503 -0
- rem/sql/migrations/002_install_models.sql +1202 -0
- rem/utils/AGENTIC_CHUNKING.md +597 -0
- rem/utils/README.md +583 -0
- rem/utils/__init__.py +43 -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/dict_utils.py +98 -0
- rem/utils/embeddings.py +423 -0
- rem/utils/examples/embeddings_example.py +305 -0
- rem/utils/examples/sql_types_example.py +202 -0
- rem/utils/markdown.py +16 -0
- rem/utils/model_helpers.py +236 -0
- rem/utils/schema_loader.py +336 -0
- rem/utils/sql_types.py +348 -0
- rem/utils/user_id.py +81 -0
- rem/utils/vision.py +330 -0
- rem/workers/README.md +506 -0
- rem/workers/__init__.py +5 -0
- rem/workers/dreaming.py +502 -0
- rem/workers/engram_processor.py +312 -0
- rem/workers/sqs_file_processor.py +193 -0
- remdb-0.3.7.dist-info/METADATA +1473 -0
- remdb-0.3.7.dist-info/RECORD +187 -0
- remdb-0.3.7.dist-info/WHEEL +4 -0
- remdb-0.3.7.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
"""
|
|
2
|
+
REM configuration wizard.
|
|
3
|
+
|
|
4
|
+
Interactive setup wizard for configuring REM on a new installation.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
rem configure # Interactive wizard
|
|
8
|
+
rem configure --install # Run wizard + install database tables
|
|
9
|
+
rem configure --show # Show current configuration
|
|
10
|
+
rem configure --edit # Open config file in editor
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
import subprocess
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
import click
|
|
18
|
+
from loguru import logger
|
|
19
|
+
|
|
20
|
+
from rem.config import (
|
|
21
|
+
config_exists,
|
|
22
|
+
ensure_config_dir,
|
|
23
|
+
get_config_path,
|
|
24
|
+
get_default_config,
|
|
25
|
+
load_config,
|
|
26
|
+
save_config,
|
|
27
|
+
validate_config,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def prompt_postgres_config(use_defaults: bool = False) -> dict:
|
|
32
|
+
"""
|
|
33
|
+
Prompt user for PostgreSQL configuration.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
use_defaults: If True, use all default values without prompting
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
PostgreSQL configuration dictionary
|
|
40
|
+
"""
|
|
41
|
+
click.echo("\n" + "=" * 60)
|
|
42
|
+
click.echo("PostgreSQL Configuration")
|
|
43
|
+
click.echo("=" * 60)
|
|
44
|
+
|
|
45
|
+
# Parse existing config if available
|
|
46
|
+
existing_conn = os.environ.get(
|
|
47
|
+
"POSTGRES__CONNECTION_STRING", "postgresql://rem:rem@localhost:5051/rem"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Default values
|
|
51
|
+
host = "localhost"
|
|
52
|
+
port = 5050
|
|
53
|
+
database = "rem"
|
|
54
|
+
username = "rem"
|
|
55
|
+
password = "rem"
|
|
56
|
+
pool_min_size = 5
|
|
57
|
+
pool_max_size = 20
|
|
58
|
+
|
|
59
|
+
if use_defaults:
|
|
60
|
+
click.echo("\nUsing default PostgreSQL configuration:")
|
|
61
|
+
click.echo(f" Host: {host}")
|
|
62
|
+
click.echo(f" Port: {port}")
|
|
63
|
+
click.echo(f" Database: {database}")
|
|
64
|
+
click.echo(f" Username: {username}")
|
|
65
|
+
click.echo(f" Pool: {pool_min_size}-{pool_max_size} connections")
|
|
66
|
+
else:
|
|
67
|
+
# Prompt for components
|
|
68
|
+
click.echo(
|
|
69
|
+
"\nEnter PostgreSQL connection details (press Enter to use default):"
|
|
70
|
+
)
|
|
71
|
+
click.echo("Default: Package users on port 5051 (docker compose -f docker-compose.prebuilt.yml up -d)")
|
|
72
|
+
click.echo("Developers: Change port to 5050 if using docker-compose.yml (local build)")
|
|
73
|
+
click.echo("Custom DB: Enter your own host/port below")
|
|
74
|
+
|
|
75
|
+
host = click.prompt("Host", default=host)
|
|
76
|
+
port = click.prompt("Port", default=port, type=int)
|
|
77
|
+
database = click.prompt("Database name", default=database)
|
|
78
|
+
username = click.prompt("Username", default=username)
|
|
79
|
+
password = click.prompt("Password", default=password, hide_input=True)
|
|
80
|
+
|
|
81
|
+
# Additional pool settings
|
|
82
|
+
click.echo("\nConnection pool settings:")
|
|
83
|
+
pool_min_size = click.prompt("Pool minimum size", default=pool_min_size, type=int)
|
|
84
|
+
pool_max_size = click.prompt("Pool maximum size", default=pool_max_size, type=int)
|
|
85
|
+
|
|
86
|
+
# Build connection string
|
|
87
|
+
connection_string = f"postgresql://{username}:{password}@{host}:{port}/{database}"
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
"connection_string": connection_string,
|
|
91
|
+
"pool_min_size": pool_min_size,
|
|
92
|
+
"pool_max_size": pool_max_size,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def prompt_llm_config(use_defaults: bool = False) -> dict:
|
|
97
|
+
"""
|
|
98
|
+
Prompt user for LLM provider configuration.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
use_defaults: If True, use all default values without prompting
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
LLM configuration dictionary
|
|
105
|
+
"""
|
|
106
|
+
click.echo("\n" + "=" * 60)
|
|
107
|
+
click.echo("LLM Provider Configuration")
|
|
108
|
+
click.echo("=" * 60)
|
|
109
|
+
|
|
110
|
+
config = {}
|
|
111
|
+
|
|
112
|
+
# Default values
|
|
113
|
+
default_model = "anthropic:claude-sonnet-4-5-20250929"
|
|
114
|
+
default_temperature = 0.5
|
|
115
|
+
|
|
116
|
+
if use_defaults:
|
|
117
|
+
click.echo("\nUsing default LLM configuration:")
|
|
118
|
+
click.echo(f" Model: {default_model}")
|
|
119
|
+
click.echo(f" Temperature: {default_temperature}")
|
|
120
|
+
click.echo(" API Keys: Not configured (set via environment variables)")
|
|
121
|
+
config["default_model"] = default_model
|
|
122
|
+
config["default_temperature"] = default_temperature
|
|
123
|
+
else:
|
|
124
|
+
# Default model
|
|
125
|
+
click.echo("\nDefault LLM model (format: provider:model-id)")
|
|
126
|
+
click.echo("Examples:")
|
|
127
|
+
click.echo(" - anthropic:claude-sonnet-4-5-20250929")
|
|
128
|
+
click.echo(" - openai:gpt-4o")
|
|
129
|
+
click.echo(" - openai:gpt-4o-mini")
|
|
130
|
+
|
|
131
|
+
config["default_model"] = click.prompt(
|
|
132
|
+
"Default model", default=default_model
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Temperature
|
|
136
|
+
config["default_temperature"] = click.prompt(
|
|
137
|
+
"Default temperature (0.0-1.0)", default=default_temperature, type=float
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# API keys
|
|
141
|
+
click.echo("\nAPI Keys (optional - leave empty to skip):")
|
|
142
|
+
|
|
143
|
+
openai_key = click.prompt("OpenAI API key", default="", show_default=False)
|
|
144
|
+
if openai_key:
|
|
145
|
+
config["openai_api_key"] = openai_key
|
|
146
|
+
|
|
147
|
+
anthropic_key = click.prompt("Anthropic API key", default="", show_default=False)
|
|
148
|
+
if anthropic_key:
|
|
149
|
+
config["anthropic_api_key"] = anthropic_key
|
|
150
|
+
|
|
151
|
+
return config
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def prompt_s3_config(use_defaults: bool = False) -> dict:
|
|
155
|
+
"""
|
|
156
|
+
Prompt user for S3 storage configuration.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
use_defaults: If True, skip S3 configuration (optional feature)
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
S3 configuration dictionary
|
|
163
|
+
"""
|
|
164
|
+
click.echo("\n" + "=" * 60)
|
|
165
|
+
click.echo("S3 Storage Configuration")
|
|
166
|
+
click.echo("=" * 60)
|
|
167
|
+
|
|
168
|
+
config = {}
|
|
169
|
+
|
|
170
|
+
if use_defaults:
|
|
171
|
+
click.echo("\nSkipping S3 configuration (optional - configure later if needed)")
|
|
172
|
+
return {}
|
|
173
|
+
|
|
174
|
+
click.echo("\nS3 storage is used for file uploads and processed content.")
|
|
175
|
+
use_s3 = click.confirm("Configure S3 storage?", default=False)
|
|
176
|
+
|
|
177
|
+
if not use_s3:
|
|
178
|
+
return {}
|
|
179
|
+
|
|
180
|
+
config["bucket_name"] = click.prompt("S3 bucket name", default="rem-storage")
|
|
181
|
+
config["region"] = click.prompt("AWS region", default="us-east-1")
|
|
182
|
+
|
|
183
|
+
# Optional: MinIO or LocalStack endpoint
|
|
184
|
+
use_custom_endpoint = click.confirm(
|
|
185
|
+
"Use custom S3 endpoint (MinIO, LocalStack)?", default=False
|
|
186
|
+
)
|
|
187
|
+
if use_custom_endpoint:
|
|
188
|
+
config["endpoint_url"] = click.prompt(
|
|
189
|
+
"Endpoint URL", default="http://localhost:9000"
|
|
190
|
+
)
|
|
191
|
+
config["access_key_id"] = click.prompt("Access key ID", default="minioadmin")
|
|
192
|
+
config["secret_access_key"] = click.prompt(
|
|
193
|
+
"Secret access key", default="minioadmin", hide_input=True
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
return config
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def prompt_additional_env_vars(use_defaults: bool = False) -> dict:
|
|
200
|
+
"""
|
|
201
|
+
Prompt user for additional environment variables.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
use_defaults: If True, skip additional env vars (optional feature)
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
Dictionary of custom environment variables
|
|
208
|
+
"""
|
|
209
|
+
click.echo("\n" + "=" * 60)
|
|
210
|
+
click.echo("Additional Environment Variables")
|
|
211
|
+
click.echo("=" * 60)
|
|
212
|
+
|
|
213
|
+
env_vars: dict[str, str] = {}
|
|
214
|
+
|
|
215
|
+
if use_defaults:
|
|
216
|
+
click.echo("\nSkipping additional environment variables (configure later if needed)")
|
|
217
|
+
return env_vars
|
|
218
|
+
|
|
219
|
+
add_env = click.confirm(
|
|
220
|
+
"Add custom environment variables?", default=False
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
if not add_env:
|
|
224
|
+
return env_vars
|
|
225
|
+
|
|
226
|
+
click.echo("\nEnter environment variables (empty name to finish):")
|
|
227
|
+
while True:
|
|
228
|
+
name = click.prompt("Variable name", default="", show_default=False)
|
|
229
|
+
if not name:
|
|
230
|
+
break
|
|
231
|
+
|
|
232
|
+
value = click.prompt(f"Value for {name}")
|
|
233
|
+
env_vars[name] = value
|
|
234
|
+
|
|
235
|
+
return env_vars
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@click.command("configure")
|
|
239
|
+
@click.option(
|
|
240
|
+
"--install",
|
|
241
|
+
is_flag=True,
|
|
242
|
+
help="Install database tables after configuration",
|
|
243
|
+
)
|
|
244
|
+
@click.option(
|
|
245
|
+
"--claude-desktop",
|
|
246
|
+
is_flag=True,
|
|
247
|
+
help="Register REM MCP server with Claude Desktop",
|
|
248
|
+
)
|
|
249
|
+
@click.option(
|
|
250
|
+
"--show",
|
|
251
|
+
is_flag=True,
|
|
252
|
+
help="Show current configuration",
|
|
253
|
+
)
|
|
254
|
+
@click.option(
|
|
255
|
+
"--edit",
|
|
256
|
+
is_flag=True,
|
|
257
|
+
help="Open configuration file in editor",
|
|
258
|
+
)
|
|
259
|
+
@click.option(
|
|
260
|
+
"--yes",
|
|
261
|
+
"-y",
|
|
262
|
+
is_flag=True,
|
|
263
|
+
help="Accept all defaults without prompting (non-interactive mode)",
|
|
264
|
+
)
|
|
265
|
+
def configure_command(install: bool, claude_desktop: bool, show: bool, edit: bool, yes: bool):
|
|
266
|
+
"""
|
|
267
|
+
Configure REM installation.
|
|
268
|
+
|
|
269
|
+
Interactive wizard for setting up PostgreSQL, LLM providers, S3, and more.
|
|
270
|
+
Configuration is saved to ~/.rem/config.yaml and merged with environment variables.
|
|
271
|
+
|
|
272
|
+
Examples:
|
|
273
|
+
rem configure # Run interactive wizard
|
|
274
|
+
rem configure --yes # Accept all defaults (non-interactive)
|
|
275
|
+
rem configure --yes --install # Quick setup with defaults + install tables
|
|
276
|
+
rem configure --install # Run wizard + install database tables
|
|
277
|
+
rem configure --show # Show current configuration
|
|
278
|
+
rem configure --edit # Open config in $EDITOR
|
|
279
|
+
"""
|
|
280
|
+
config_path = get_config_path()
|
|
281
|
+
|
|
282
|
+
# Show current configuration
|
|
283
|
+
if show:
|
|
284
|
+
if not config_exists():
|
|
285
|
+
click.echo(f"No configuration file found at {config_path}")
|
|
286
|
+
click.echo("Run 'rem configure' to create one.")
|
|
287
|
+
return
|
|
288
|
+
|
|
289
|
+
config = load_config()
|
|
290
|
+
click.echo(f"\nConfiguration file: {config_path}")
|
|
291
|
+
click.echo("=" * 60)
|
|
292
|
+
|
|
293
|
+
import yaml
|
|
294
|
+
|
|
295
|
+
click.echo(yaml.dump(config, default_flow_style=False, sort_keys=False))
|
|
296
|
+
return
|
|
297
|
+
|
|
298
|
+
# Edit configuration file
|
|
299
|
+
if edit:
|
|
300
|
+
if not config_exists():
|
|
301
|
+
click.echo(f"No configuration file found at {config_path}")
|
|
302
|
+
click.echo("Run 'rem configure' to create one first.")
|
|
303
|
+
return
|
|
304
|
+
|
|
305
|
+
editor = os.environ.get("EDITOR", "vim")
|
|
306
|
+
try:
|
|
307
|
+
subprocess.run([editor, str(config_path)], check=True)
|
|
308
|
+
click.echo(f"\nConfiguration saved to {config_path}")
|
|
309
|
+
except Exception as e:
|
|
310
|
+
click.echo(f"Error opening editor: {e}", err=True)
|
|
311
|
+
click.echo(f"Manually edit: {config_path}")
|
|
312
|
+
return
|
|
313
|
+
|
|
314
|
+
# Run interactive wizard
|
|
315
|
+
click.echo("\n" + "=" * 60)
|
|
316
|
+
click.echo("REM Configuration Wizard")
|
|
317
|
+
click.echo("=" * 60)
|
|
318
|
+
|
|
319
|
+
if yes:
|
|
320
|
+
click.echo("\nRunning in non-interactive mode (--yes flag)")
|
|
321
|
+
click.echo("Using default configuration values...")
|
|
322
|
+
else:
|
|
323
|
+
click.echo("\nThis wizard will help you configure REM for first-time use.")
|
|
324
|
+
|
|
325
|
+
click.echo(f"Configuration will be saved to: {config_path}")
|
|
326
|
+
|
|
327
|
+
# Check if config already exists
|
|
328
|
+
if config_exists():
|
|
329
|
+
click.echo(f"\nConfiguration file already exists at {config_path}")
|
|
330
|
+
if yes:
|
|
331
|
+
# In non-interactive mode, skip configuration creation
|
|
332
|
+
click.echo("Skipping configuration creation (file already exists)")
|
|
333
|
+
config = None # Will not save/validate
|
|
334
|
+
elif not click.confirm("Overwrite existing configuration?", default=False):
|
|
335
|
+
click.echo("Configuration unchanged.")
|
|
336
|
+
config = None # Will not save/validate
|
|
337
|
+
else:
|
|
338
|
+
# User confirmed overwrite - create new config
|
|
339
|
+
config = {}
|
|
340
|
+
config["postgres"] = prompt_postgres_config(use_defaults=yes)
|
|
341
|
+
config["llm"] = prompt_llm_config(use_defaults=yes)
|
|
342
|
+
s3_config = prompt_s3_config(use_defaults=yes)
|
|
343
|
+
if s3_config:
|
|
344
|
+
config["s3"] = s3_config
|
|
345
|
+
env_vars = prompt_additional_env_vars(use_defaults=yes)
|
|
346
|
+
if env_vars:
|
|
347
|
+
config["env"] = env_vars
|
|
348
|
+
else:
|
|
349
|
+
# No existing config - create new one
|
|
350
|
+
config = {}
|
|
351
|
+
config["postgres"] = prompt_postgres_config(use_defaults=yes)
|
|
352
|
+
config["llm"] = prompt_llm_config(use_defaults=yes)
|
|
353
|
+
s3_config = prompt_s3_config(use_defaults=yes)
|
|
354
|
+
if s3_config:
|
|
355
|
+
config["s3"] = s3_config
|
|
356
|
+
env_vars = prompt_additional_env_vars(use_defaults=yes)
|
|
357
|
+
if env_vars:
|
|
358
|
+
config["env"] = env_vars
|
|
359
|
+
|
|
360
|
+
# Validate and save configuration (only if we created one)
|
|
361
|
+
if config is not None:
|
|
362
|
+
click.echo("\n" + "=" * 60)
|
|
363
|
+
click.echo("Validating Configuration")
|
|
364
|
+
click.echo("=" * 60)
|
|
365
|
+
|
|
366
|
+
errors = validate_config(config)
|
|
367
|
+
if errors:
|
|
368
|
+
click.echo("\nConfiguration validation failed:")
|
|
369
|
+
for error in errors:
|
|
370
|
+
click.echo(f" ❌ {error}", err=True)
|
|
371
|
+
click.echo("\nPlease fix these errors and try again.")
|
|
372
|
+
return
|
|
373
|
+
|
|
374
|
+
click.echo("✅ Configuration is valid")
|
|
375
|
+
|
|
376
|
+
# Save configuration
|
|
377
|
+
try:
|
|
378
|
+
save_config(config)
|
|
379
|
+
click.echo(f"\n✅ Configuration saved to {config_path}")
|
|
380
|
+
except Exception as e:
|
|
381
|
+
click.echo(f"\n❌ Failed to save configuration: {e}", err=True)
|
|
382
|
+
return
|
|
383
|
+
else:
|
|
384
|
+
# Load existing config for use in install step
|
|
385
|
+
config = load_config() if config_exists() else {}
|
|
386
|
+
|
|
387
|
+
# Install database tables if requested
|
|
388
|
+
if install:
|
|
389
|
+
click.echo("\n" + "=" * 60)
|
|
390
|
+
click.echo("Database Installation")
|
|
391
|
+
click.echo("=" * 60)
|
|
392
|
+
|
|
393
|
+
if yes or click.confirm("\nInstall database tables?", default=True):
|
|
394
|
+
try:
|
|
395
|
+
# Import here to ensure config is loaded first
|
|
396
|
+
from rem.config import merge_config_to_env
|
|
397
|
+
|
|
398
|
+
# Merge config into environment before running migrations
|
|
399
|
+
merge_config_to_env(config)
|
|
400
|
+
|
|
401
|
+
click.echo("\nRunning database migrations...")
|
|
402
|
+
|
|
403
|
+
# Import and run migrations
|
|
404
|
+
from rem.cli.commands.db import migrate
|
|
405
|
+
|
|
406
|
+
# Create a context for the command and invoke it
|
|
407
|
+
ctx = click.Context(migrate)
|
|
408
|
+
ctx.invoke(migrate, install_only=False, models_only=False,
|
|
409
|
+
background_indexes=False, connection=None,
|
|
410
|
+
sql_dir=Path("rem/sql"))
|
|
411
|
+
|
|
412
|
+
click.echo("✅ Database installation complete")
|
|
413
|
+
|
|
414
|
+
except Exception as e:
|
|
415
|
+
click.echo(f"\n❌ Database installation failed: {e}", err=True)
|
|
416
|
+
click.echo("\nYou can manually install tables later with:")
|
|
417
|
+
click.echo(" rem db migrate")
|
|
418
|
+
|
|
419
|
+
# Register with Claude Desktop if requested
|
|
420
|
+
if claude_desktop:
|
|
421
|
+
click.echo("\n" + "=" * 60)
|
|
422
|
+
click.echo("Claude Desktop Integration")
|
|
423
|
+
click.echo("=" * 60)
|
|
424
|
+
|
|
425
|
+
try:
|
|
426
|
+
import shutil
|
|
427
|
+
from pathlib import Path
|
|
428
|
+
from fastmcp.mcp_config import update_config_file, StdioMCPServer
|
|
429
|
+
|
|
430
|
+
# Find Claude Desktop config path
|
|
431
|
+
if os.name == "nt": # Windows
|
|
432
|
+
config_dir = Path.home() / "AppData/Roaming/Claude"
|
|
433
|
+
elif os.name == "posix":
|
|
434
|
+
if Path.home() / "Library/Application Support/Claude":
|
|
435
|
+
config_dir = Path.home() / "Library/Application Support/Claude"
|
|
436
|
+
else:
|
|
437
|
+
config_dir = Path.home() / ".config/Claude"
|
|
438
|
+
else:
|
|
439
|
+
config_dir = Path.home() / ".config/Claude"
|
|
440
|
+
|
|
441
|
+
config_path = config_dir / "claude_desktop_config.json"
|
|
442
|
+
|
|
443
|
+
# Find rem executable
|
|
444
|
+
rem_executable = shutil.which("rem")
|
|
445
|
+
if not rem_executable:
|
|
446
|
+
click.echo("❌ 'rem' command not found in PATH", err=True)
|
|
447
|
+
return
|
|
448
|
+
|
|
449
|
+
# Create server config with all necessary env vars
|
|
450
|
+
env_vars = {
|
|
451
|
+
"POSTGRES__CONNECTION_STRING": config.get("postgres", {}).get("connection_string", "")
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
# Add LLM API keys if present
|
|
455
|
+
llm_config = config.get("llm", {})
|
|
456
|
+
if llm_config.get("openai_api_key"):
|
|
457
|
+
env_vars["LLM__OPENAI_API_KEY"] = llm_config["openai_api_key"]
|
|
458
|
+
if llm_config.get("anthropic_api_key"):
|
|
459
|
+
env_vars["LLM__ANTHROPIC_API_KEY"] = llm_config["anthropic_api_key"]
|
|
460
|
+
|
|
461
|
+
server_config = StdioMCPServer(
|
|
462
|
+
command=rem_executable,
|
|
463
|
+
args=["mcp"],
|
|
464
|
+
env=env_vars
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
# Update config file using FastMCP utility
|
|
468
|
+
update_config_file(config_path, "rem", server_config)
|
|
469
|
+
|
|
470
|
+
click.echo(f"✅ Registered REM MCP server with Claude Desktop")
|
|
471
|
+
click.echo(f"Config: {config_path}")
|
|
472
|
+
click.echo("\nRestart Claude Desktop to use the REM server.")
|
|
473
|
+
|
|
474
|
+
except Exception as e:
|
|
475
|
+
click.echo(f"❌ Failed: {e}", err=True)
|
|
476
|
+
|
|
477
|
+
# Next steps
|
|
478
|
+
click.echo("\n" + "=" * 60)
|
|
479
|
+
click.echo("Next Steps")
|
|
480
|
+
click.echo("=" * 60)
|
|
481
|
+
click.echo("\n1. Verify configuration:")
|
|
482
|
+
click.echo(" rem configure --show")
|
|
483
|
+
click.echo("\n2. Edit configuration (if needed):")
|
|
484
|
+
click.echo(" rem configure --edit")
|
|
485
|
+
if not install:
|
|
486
|
+
click.echo("\n3. Install database tables:")
|
|
487
|
+
click.echo(" rem db migrate")
|
|
488
|
+
click.echo("\n4. Start the API server:")
|
|
489
|
+
click.echo(" rem dev run-server")
|
|
490
|
+
click.echo("\n5. Test the installation:")
|
|
491
|
+
click.echo(" rem ask 'Hello, REM!'")
|
|
492
|
+
click.echo()
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def register_command(cli_group):
|
|
496
|
+
"""Register the configure command."""
|
|
497
|
+
cli_group.add_command(configure_command)
|