remdb 0.3.0__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.

Files changed (106) hide show
  1. rem/__init__.py +129 -2
  2. rem/agentic/README.md +76 -0
  3. rem/agentic/__init__.py +15 -0
  4. rem/agentic/agents/__init__.py +16 -2
  5. rem/agentic/agents/sse_simulator.py +502 -0
  6. rem/agentic/context.py +51 -25
  7. rem/agentic/llm_provider_models.py +301 -0
  8. rem/agentic/mcp/tool_wrapper.py +29 -3
  9. rem/agentic/otel/setup.py +93 -4
  10. rem/agentic/providers/phoenix.py +32 -43
  11. rem/agentic/providers/pydantic_ai.py +168 -24
  12. rem/agentic/schema.py +358 -21
  13. rem/agentic/tools/rem_tools.py +3 -3
  14. rem/api/README.md +238 -1
  15. rem/api/deps.py +255 -0
  16. rem/api/main.py +154 -37
  17. rem/api/mcp_router/resources.py +1 -1
  18. rem/api/mcp_router/server.py +26 -5
  19. rem/api/mcp_router/tools.py +465 -7
  20. rem/api/middleware/tracking.py +172 -0
  21. rem/api/routers/admin.py +494 -0
  22. rem/api/routers/auth.py +124 -0
  23. rem/api/routers/chat/completions.py +402 -20
  24. rem/api/routers/chat/models.py +88 -10
  25. rem/api/routers/chat/otel_utils.py +33 -0
  26. rem/api/routers/chat/sse_events.py +542 -0
  27. rem/api/routers/chat/streaming.py +642 -45
  28. rem/api/routers/dev.py +81 -0
  29. rem/api/routers/feedback.py +268 -0
  30. rem/api/routers/messages.py +473 -0
  31. rem/api/routers/models.py +78 -0
  32. rem/api/routers/query.py +360 -0
  33. rem/api/routers/shared_sessions.py +406 -0
  34. rem/auth/middleware.py +126 -27
  35. rem/cli/commands/README.md +237 -64
  36. rem/cli/commands/ask.py +13 -10
  37. rem/cli/commands/cluster.py +1808 -0
  38. rem/cli/commands/configure.py +5 -6
  39. rem/cli/commands/db.py +396 -139
  40. rem/cli/commands/experiments.py +293 -73
  41. rem/cli/commands/process.py +22 -15
  42. rem/cli/commands/scaffold.py +47 -0
  43. rem/cli/commands/schema.py +97 -50
  44. rem/cli/main.py +29 -6
  45. rem/config.py +10 -3
  46. rem/models/core/core_model.py +7 -1
  47. rem/models/core/rem_query.py +5 -2
  48. rem/models/entities/__init__.py +21 -0
  49. rem/models/entities/domain_resource.py +38 -0
  50. rem/models/entities/feedback.py +123 -0
  51. rem/models/entities/message.py +30 -1
  52. rem/models/entities/session.py +83 -0
  53. rem/models/entities/shared_session.py +180 -0
  54. rem/models/entities/user.py +10 -3
  55. rem/registry.py +373 -0
  56. rem/schemas/agents/rem.yaml +7 -3
  57. rem/services/content/providers.py +94 -140
  58. rem/services/content/service.py +92 -20
  59. rem/services/dreaming/affinity_service.py +2 -16
  60. rem/services/dreaming/moment_service.py +2 -15
  61. rem/services/embeddings/api.py +24 -17
  62. rem/services/embeddings/worker.py +16 -16
  63. rem/services/phoenix/EXPERIMENT_DESIGN.md +3 -3
  64. rem/services/phoenix/client.py +302 -28
  65. rem/services/postgres/README.md +159 -15
  66. rem/services/postgres/__init__.py +2 -1
  67. rem/services/postgres/diff_service.py +531 -0
  68. rem/services/postgres/pydantic_to_sqlalchemy.py +427 -129
  69. rem/services/postgres/repository.py +132 -0
  70. rem/services/postgres/schema_generator.py +291 -9
  71. rem/services/postgres/service.py +6 -6
  72. rem/services/rate_limit.py +113 -0
  73. rem/services/rem/README.md +14 -0
  74. rem/services/rem/parser.py +44 -9
  75. rem/services/rem/service.py +36 -2
  76. rem/services/session/compression.py +24 -1
  77. rem/services/session/reload.py +1 -1
  78. rem/services/user_service.py +98 -0
  79. rem/settings.py +313 -29
  80. rem/sql/background_indexes.sql +21 -16
  81. rem/sql/migrations/001_install.sql +387 -54
  82. rem/sql/migrations/002_install_models.sql +2320 -393
  83. rem/sql/migrations/003_optional_extensions.sql +326 -0
  84. rem/sql/migrations/004_cache_system.sql +548 -0
  85. rem/utils/__init__.py +18 -0
  86. rem/utils/constants.py +97 -0
  87. rem/utils/date_utils.py +228 -0
  88. rem/utils/embeddings.py +17 -4
  89. rem/utils/files.py +167 -0
  90. rem/utils/mime_types.py +158 -0
  91. rem/utils/model_helpers.py +156 -1
  92. rem/utils/schema_loader.py +282 -35
  93. rem/utils/sql_paths.py +146 -0
  94. rem/utils/sql_types.py +3 -1
  95. rem/utils/vision.py +9 -14
  96. rem/workers/README.md +14 -14
  97. rem/workers/__init__.py +3 -1
  98. rem/workers/db_listener.py +579 -0
  99. rem/workers/db_maintainer.py +74 -0
  100. rem/workers/unlogged_maintainer.py +463 -0
  101. {remdb-0.3.0.dist-info → remdb-0.3.127.dist-info}/METADATA +464 -289
  102. {remdb-0.3.0.dist-info → remdb-0.3.127.dist-info}/RECORD +104 -73
  103. {remdb-0.3.0.dist-info → remdb-0.3.127.dist-info}/WHEEL +1 -1
  104. rem/sql/002_install_models.sql +0 -1068
  105. rem/sql/install_models.sql +0 -1038
  106. {remdb-0.3.0.dist-info → remdb-0.3.127.dist-info}/entry_points.txt +0 -0
rem/settings.py CHANGED
@@ -15,7 +15,7 @@ Example .env file:
15
15
  API__LOG_LEVEL=info
16
16
 
17
17
  # LLM
18
- LLM__DEFAULT_MODEL=anthropic:claude-sonnet-4-5-20250929
18
+ LLM__DEFAULT_MODEL=openai:gpt-4.1
19
19
  LLM__DEFAULT_TEMPERATURE=0.5
20
20
  LLM__MAX_RETRIES=10
21
21
  LLM__OPENAI_API_KEY=sk-...
@@ -33,14 +33,15 @@ Example .env file:
33
33
  AUTH__OIDC_CLIENT_ID=your-client-id
34
34
  AUTH__SESSION_SECRET=your-secret-key
35
35
 
36
- # OpenTelemetry (disabled by default)
36
+ # OpenTelemetry (disabled by default - enable via env var when collector available)
37
+ # Standard OTLP collector ports: 4317 (gRPC), 4318 (HTTP)
37
38
  OTEL__ENABLED=false
38
39
  OTEL__SERVICE_NAME=rem-api
39
- OTEL__COLLECTOR_ENDPOINT=http://localhost:4318
40
- OTEL__PROTOCOL=http
40
+ OTEL__COLLECTOR_ENDPOINT=http://localhost:4317
41
+ OTEL__PROTOCOL=grpc
41
42
 
42
- # Arize Phoenix (disabled by default)
43
- PHOENIX__ENABLED=false
43
+ # Arize Phoenix (enabled by default - can be disabled via env var)
44
+ PHOENIX__ENABLED=true
44
45
  PHOENIX__COLLECTOR_ENDPOINT=http://localhost:6006/v1/traces
45
46
  PHOENIX__PROJECT_NAME=rem
46
47
 
@@ -57,8 +58,10 @@ Example .env file:
57
58
  """
58
59
 
59
60
  import os
60
- from pydantic import Field, field_validator
61
+ import hashlib
62
+ from pydantic import Field, field_validator, ValidationInfo
61
63
  from pydantic_settings import BaseSettings, SettingsConfigDict
64
+ from loguru import logger
62
65
 
63
66
 
64
67
  class LLMSettings(BaseSettings):
@@ -72,7 +75,7 @@ class LLMSettings(BaseSettings):
72
75
  LLM__EVALUATOR_MODEL or EVALUATOR_MODEL - Model for LLM-as-judge evaluation
73
76
  LLM__OPENAI_API_KEY or OPENAI_API_KEY - OpenAI API key
74
77
  LLM__ANTHROPIC_API_KEY or ANTHROPIC_API_KEY - Anthropic API key
75
- LLM__EMBEDDING_PROVIDER or EMBEDDING_PROVIDER - Default embedding provider (openai, cohere, jina, etc.)
78
+ LLM__EMBEDDING_PROVIDER or EMBEDDING_PROVIDER - Default embedding provider (openai)
76
79
  LLM__EMBEDDING_MODEL or EMBEDDING_MODEL - Default embedding model name
77
80
  """
78
81
 
@@ -84,7 +87,7 @@ class LLMSettings(BaseSettings):
84
87
  )
85
88
 
86
89
  default_model: str = Field(
87
- default="anthropic:claude-sonnet-4-5-20250929",
90
+ default="openai:gpt-4.1",
88
91
  description="Default LLM model (format: provider:model-id)",
89
92
  )
90
93
 
@@ -127,7 +130,7 @@ class LLMSettings(BaseSettings):
127
130
 
128
131
  embedding_provider: str = Field(
129
132
  default="openai",
130
- description="Default embedding provider (openai, cohere, jina, etc.)",
133
+ description="Default embedding provider (currently only openai supported)",
131
134
  )
132
135
 
133
136
  embedding_model: str = Field(
@@ -239,6 +242,11 @@ class OTELSettings(BaseSettings):
239
242
  description="Export timeout in milliseconds",
240
243
  )
241
244
 
245
+ insecure: bool = Field(
246
+ default=True,
247
+ description="Use insecure (non-TLS) gRPC connection (default: True for local dev)",
248
+ )
249
+
242
250
 
243
251
  class PhoenixSettings(BaseSettings):
244
252
  """
@@ -265,8 +273,8 @@ class PhoenixSettings(BaseSettings):
265
273
  )
266
274
 
267
275
  enabled: bool = Field(
268
- default=False,
269
- description="Enable Phoenix integration (disabled by default for local dev)",
276
+ default=True,
277
+ description="Enable Phoenix integration (enabled by default)",
270
278
  )
271
279
 
272
280
  base_url: str = Field(
@@ -359,10 +367,16 @@ class AuthSettings(BaseSettings):
359
367
  - Custom OIDC provider
360
368
 
361
369
  Environment variables:
362
- AUTH__ENABLED - Enable authentication (default: false)
370
+ AUTH__ENABLED - Enable authentication (default: true)
371
+ AUTH__ALLOW_ANONYMOUS - Allow rate-limited anonymous access (default: true)
363
372
  AUTH__SESSION_SECRET - Secret for session cookie signing
364
373
  AUTH__GOOGLE__* - Google OAuth settings
365
374
  AUTH__MICROSOFT__* - Microsoft OAuth settings
375
+
376
+ Access modes:
377
+ - enabled=true, allow_anonymous=true: Auth available, anonymous gets rate-limited access
378
+ - enabled=true, allow_anonymous=false: Auth required for all requests
379
+ - enabled=false: No auth, all requests treated as default user (dev mode)
366
380
  """
367
381
 
368
382
  model_config = SettingsConfigDict(
@@ -373,8 +387,26 @@ class AuthSettings(BaseSettings):
373
387
  )
374
388
 
375
389
  enabled: bool = Field(
376
- default=False,
377
- description="Enable authentication (disabled by default for testing)",
390
+ default=True,
391
+ description="Enable authentication (OAuth endpoints and middleware)",
392
+ )
393
+
394
+ allow_anonymous: bool = Field(
395
+ default=True,
396
+ description=(
397
+ "Allow anonymous (unauthenticated) access with rate limits. "
398
+ "When true, requests without auth get ANONYMOUS tier rate limits. "
399
+ "When false, all requests require authentication."
400
+ ),
401
+ )
402
+
403
+ mcp_requires_auth: bool = Field(
404
+ default=True,
405
+ description=(
406
+ "Require authentication for MCP endpoints. "
407
+ "MCP is a protected service and should always require login in production. "
408
+ "Set to false only for local development/testing."
409
+ ),
378
410
  )
379
411
 
380
412
  session_secret: str = Field(
@@ -386,6 +418,22 @@ class AuthSettings(BaseSettings):
386
418
  google: GoogleOAuthSettings = Field(default_factory=GoogleOAuthSettings)
387
419
  microsoft: MicrosoftOAuthSettings = Field(default_factory=MicrosoftOAuthSettings)
388
420
 
421
+ @field_validator("session_secret", mode="before")
422
+ @classmethod
423
+ def generate_dev_secret(cls, v: str | None, info: ValidationInfo) -> str:
424
+ # Only generate if not already set and not in production
425
+ if not v and info.data.get("environment") != "production":
426
+ # Deterministic secret for development
427
+ seed_string = f"{info.data.get('team', 'rem')}-{info.data.get('environment', 'development')}-auth-secret-salt"
428
+ logger.warning(
429
+ "AUTH__SESSION_SECRET not set. Generating deterministic secret for non-production environment. "
430
+ "DO NOT use in production."
431
+ )
432
+ return hashlib.sha256(seed_string.encode()).hexdigest()
433
+ elif not v and info.data.get("environment") == "production":
434
+ raise ValueError("AUTH__SESSION_SECRET must be set in production environment.")
435
+ return v
436
+
389
437
 
390
438
  class PostgresSettings(BaseSettings):
391
439
  """
@@ -962,6 +1010,123 @@ class APISettings(BaseSettings):
962
1010
  )
963
1011
 
964
1012
 
1013
+ class ModelsSettings(BaseSettings):
1014
+ """
1015
+ Custom model registration settings for downstream applications.
1016
+
1017
+ Allows downstream apps to specify Python modules containing custom models
1018
+ that should be imported (and thus registered) before schema generation.
1019
+
1020
+ This enables `rem db schema generate` to discover models registered with
1021
+ `@rem.register_model` in downstream applications.
1022
+
1023
+ Environment variables:
1024
+ MODELS__IMPORT_MODULES - Semicolon-separated list of Python modules to import
1025
+ Example: "models;myapp.entities;myapp.custom_models"
1026
+
1027
+ Example:
1028
+ # In downstream app's .env
1029
+ MODELS__IMPORT_MODULES=models
1030
+
1031
+ # In downstream app's models/__init__.py
1032
+ import rem
1033
+ from rem.models.core import CoreModel
1034
+
1035
+ @rem.register_model
1036
+ class MyCustomEntity(CoreModel):
1037
+ name: str
1038
+
1039
+ # Then run schema generation
1040
+ rem db schema generate # Includes MyCustomEntity
1041
+ """
1042
+
1043
+ model_config = SettingsConfigDict(
1044
+ env_prefix="MODELS__",
1045
+ extra="ignore",
1046
+ )
1047
+
1048
+ import_modules: str = Field(
1049
+ default="",
1050
+ description=(
1051
+ "Semicolon-separated list of Python modules to import for model registration. "
1052
+ "These modules are imported before schema generation to ensure custom models "
1053
+ "decorated with @rem.register_model are discovered. "
1054
+ "Example: 'models;myapp.entities'"
1055
+ ),
1056
+ )
1057
+
1058
+ @property
1059
+ def module_list(self) -> list[str]:
1060
+ """
1061
+ Get modules as a list, filtering empty strings.
1062
+
1063
+ Auto-detects ./models folder if it exists and is importable.
1064
+ """
1065
+ modules = []
1066
+ if self.import_modules:
1067
+ modules = [m.strip() for m in self.import_modules.split(";") if m.strip()]
1068
+
1069
+ # Auto-detect ./models if it exists and is a Python package (convention over configuration)
1070
+ from pathlib import Path
1071
+
1072
+ models_path = Path("./models")
1073
+ if models_path.exists() and models_path.is_dir():
1074
+ # Check if it's a Python package (has __init__.py)
1075
+ if (models_path / "__init__.py").exists():
1076
+ if "models" not in modules:
1077
+ modules.insert(0, "models")
1078
+
1079
+ return modules
1080
+
1081
+
1082
+ class SchemaSettings(BaseSettings):
1083
+ """
1084
+ Schema search path settings for agent and evaluator schemas.
1085
+
1086
+ Allows extending REM's schema search with custom directories.
1087
+ Custom paths are searched BEFORE built-in package schemas.
1088
+
1089
+ Environment variables:
1090
+ SCHEMA__PATHS - Semicolon-separated list of directories to search
1091
+ Example: "/app/schemas;/shared/agents;./local-schemas"
1092
+
1093
+ Search Order:
1094
+ 1. Exact path (if file exists)
1095
+ 2. Custom paths from SCHEMA__PATHS (in order)
1096
+ 3. Built-in package schemas (schemas/agents/, schemas/evaluators/, etc.)
1097
+ 4. Database LOOKUP (if enabled)
1098
+
1099
+ Example:
1100
+ # In .env or environment
1101
+ SCHEMA__PATHS=/app/custom-agents;/shared/evaluators
1102
+
1103
+ # Then in code
1104
+ from rem.utils.schema_loader import load_agent_schema
1105
+ schema = load_agent_schema("my-custom-agent") # Found in /app/custom-agents/
1106
+ """
1107
+
1108
+ model_config = SettingsConfigDict(
1109
+ env_prefix="SCHEMA__",
1110
+ extra="ignore",
1111
+ )
1112
+
1113
+ paths: str = Field(
1114
+ default="",
1115
+ description=(
1116
+ "Semicolon-separated list of directories to search for schemas. "
1117
+ "These paths are searched BEFORE built-in package schemas. "
1118
+ "Example: '/app/schemas;/shared/agents'"
1119
+ ),
1120
+ )
1121
+
1122
+ @property
1123
+ def path_list(self) -> list[str]:
1124
+ """Get paths as a list, filtering empty strings."""
1125
+ if not self.paths:
1126
+ return []
1127
+ return [p.strip() for p in self.paths.split(";") if p.strip()]
1128
+
1129
+
965
1130
  class GitSettings(BaseSettings):
966
1131
  """
967
1132
  Git repository provider settings for versioned schema/experiment syncing.
@@ -1097,6 +1262,110 @@ class GitSettings(BaseSettings):
1097
1262
  )
1098
1263
 
1099
1264
 
1265
+ class DBListenerSettings(BaseSettings):
1266
+ """
1267
+ PostgreSQL LISTEN/NOTIFY database listener settings.
1268
+
1269
+ The DB Listener is a lightweight worker that subscribes to PostgreSQL
1270
+ NOTIFY events and dispatches them to external systems (SQS, REST, custom).
1271
+
1272
+ Architecture:
1273
+ - Single-replica deployment (to avoid duplicate processing)
1274
+ - Dedicated connection for LISTEN (not from connection pool)
1275
+ - Automatic reconnection with exponential backoff
1276
+ - Graceful shutdown on SIGTERM
1277
+
1278
+ Use Cases:
1279
+ - Sync data changes to external systems (Phoenix, webhooks)
1280
+ - Trigger async jobs without polling
1281
+ - Event-driven architectures with PostgreSQL as event source
1282
+
1283
+ Example PostgreSQL trigger:
1284
+ CREATE OR REPLACE FUNCTION notify_feedback_insert()
1285
+ RETURNS TRIGGER AS $$
1286
+ BEGIN
1287
+ PERFORM pg_notify('feedback_sync', json_build_object(
1288
+ 'id', NEW.id,
1289
+ 'table', 'feedbacks',
1290
+ 'action', 'insert'
1291
+ )::text);
1292
+ RETURN NEW;
1293
+ END;
1294
+ $$ LANGUAGE plpgsql;
1295
+
1296
+ Environment variables:
1297
+ DB_LISTENER__ENABLED - Enable the listener worker (default: false)
1298
+ DB_LISTENER__CHANNELS - Comma-separated PostgreSQL channels to listen on
1299
+ DB_LISTENER__HANDLER_TYPE - Handler type: 'sqs', 'rest', or 'custom'
1300
+ DB_LISTENER__SQS_QUEUE_URL - SQS queue URL (for handler_type=sqs)
1301
+ DB_LISTENER__REST_ENDPOINT - REST endpoint URL (for handler_type=rest)
1302
+ DB_LISTENER__RECONNECT_DELAY - Initial reconnect delay in seconds
1303
+ DB_LISTENER__MAX_RECONNECT_DELAY - Maximum reconnect delay in seconds
1304
+
1305
+ References:
1306
+ - PostgreSQL NOTIFY: https://www.postgresql.org/docs/current/sql-notify.html
1307
+ - Brandur's Notifier: https://brandur.org/notifier
1308
+ """
1309
+
1310
+ model_config = SettingsConfigDict(
1311
+ env_prefix="DB_LISTENER__",
1312
+ env_file=".env",
1313
+ env_file_encoding="utf-8",
1314
+ extra="ignore",
1315
+ )
1316
+
1317
+ enabled: bool = Field(
1318
+ default=False,
1319
+ description="Enable the DB Listener worker (disabled by default)",
1320
+ )
1321
+
1322
+ channels: str = Field(
1323
+ default="",
1324
+ description=(
1325
+ "Comma-separated list of PostgreSQL channels to LISTEN on. "
1326
+ "Example: 'feedback_sync,entity_update,user_events'"
1327
+ ),
1328
+ )
1329
+
1330
+ handler_type: str = Field(
1331
+ default="rest",
1332
+ description=(
1333
+ "Handler type for dispatching notifications. Options: "
1334
+ "'sqs' (publish to SQS), 'rest' (POST to endpoint), 'custom' (Python handlers)"
1335
+ ),
1336
+ )
1337
+
1338
+ sqs_queue_url: str = Field(
1339
+ default="",
1340
+ description="SQS queue URL for handler_type='sqs'",
1341
+ )
1342
+
1343
+ rest_endpoint: str = Field(
1344
+ default="http://localhost:8000/api/v1/internal/events",
1345
+ description=(
1346
+ "REST endpoint URL for handler_type='rest'. "
1347
+ "Receives POST with {channel, payload, source} JSON body."
1348
+ ),
1349
+ )
1350
+
1351
+ reconnect_delay: float = Field(
1352
+ default=1.0,
1353
+ description="Initial delay (seconds) between reconnection attempts",
1354
+ )
1355
+
1356
+ max_reconnect_delay: float = Field(
1357
+ default=60.0,
1358
+ description="Maximum delay (seconds) between reconnection attempts (exponential backoff cap)",
1359
+ )
1360
+
1361
+ @property
1362
+ def channel_list(self) -> list[str]:
1363
+ """Get channels as a list, filtering empty strings."""
1364
+ if not self.channels:
1365
+ return []
1366
+ return [c.strip() for c in self.channels.split(",") if c.strip()]
1367
+
1368
+
1100
1369
  class TestSettings(BaseSettings):
1101
1370
  """
1102
1371
  Test environment settings.
@@ -1166,6 +1435,11 @@ class Settings(BaseSettings):
1166
1435
  extra="ignore",
1167
1436
  )
1168
1437
 
1438
+ app_name: str = Field(
1439
+ default="REM",
1440
+ description="Application/API name used in docs, titles, and user-facing text",
1441
+ )
1442
+
1169
1443
  team: str = Field(
1170
1444
  default="rem",
1171
1445
  description="Team or project name for observability",
@@ -1186,16 +1460,12 @@ class Settings(BaseSettings):
1186
1460
  description="Root path for reverse proxy (e.g., /rem for ALB routing)",
1187
1461
  )
1188
1462
 
1189
- sql_dir: str = Field(
1190
- default="src/rem/sql",
1191
- description="Directory for SQL files and migrations",
1192
- )
1193
-
1194
1463
  # Nested settings groups
1195
1464
  api: APISettings = Field(default_factory=APISettings)
1196
1465
  chat: ChatSettings = Field(default_factory=ChatSettings)
1197
1466
  llm: LLMSettings = Field(default_factory=LLMSettings)
1198
1467
  mcp: MCPSettings = Field(default_factory=MCPSettings)
1468
+ models: ModelsSettings = Field(default_factory=ModelsSettings)
1199
1469
  otel: OTELSettings = Field(default_factory=OTELSettings)
1200
1470
  phoenix: PhoenixSettings = Field(default_factory=PhoenixSettings)
1201
1471
  auth: AuthSettings = Field(default_factory=AuthSettings)
@@ -1205,22 +1475,36 @@ class Settings(BaseSettings):
1205
1475
  s3: S3Settings = Field(default_factory=S3Settings)
1206
1476
  git: GitSettings = Field(default_factory=GitSettings)
1207
1477
  sqs: SQSSettings = Field(default_factory=SQSSettings)
1478
+ db_listener: DBListenerSettings = Field(default_factory=DBListenerSettings)
1208
1479
  chunking: ChunkingSettings = Field(default_factory=ChunkingSettings)
1209
1480
  content: ContentSettings = Field(default_factory=ContentSettings)
1481
+ schema_search: SchemaSettings = Field(default_factory=SchemaSettings)
1210
1482
  test: TestSettings = Field(default_factory=TestSettings)
1211
1483
 
1212
1484
 
1485
+ # Auto-load .env file from current directory if it exists
1486
+ # This happens BEFORE config file loading, so .env takes precedence
1487
+ from pathlib import Path
1488
+ from dotenv import load_dotenv
1489
+
1490
+ _dotenv_path = Path(".env")
1491
+ if _dotenv_path.exists():
1492
+ load_dotenv(_dotenv_path, override=False) # Don't override existing env vars
1493
+ logger.debug(f"Loaded environment from {_dotenv_path.resolve()}")
1494
+
1213
1495
  # Load configuration from ~/.rem/config.yaml before initializing settings
1214
1496
  # This allows user configuration to be merged with environment variables
1215
- try:
1216
- from rem.config import load_config, merge_config_to_env
1217
-
1218
- _config = load_config()
1219
- if _config:
1220
- merge_config_to_env(_config)
1221
- except ImportError:
1222
- # config module not available (e.g., during initial setup)
1223
- pass
1497
+ # Set REM_SKIP_CONFIG=1 to disable (useful for development with .env)
1498
+ if not os.getenv("REM_SKIP_CONFIG", "").lower() in ("true", "1", "yes"):
1499
+ try:
1500
+ from rem.config import load_config, merge_config_to_env
1501
+
1502
+ _config = load_config()
1503
+ if _config:
1504
+ merge_config_to_env(_config)
1505
+ except ImportError:
1506
+ # config module not available (e.g., during initial setup)
1507
+ pass
1224
1508
 
1225
1509
  # Global settings singleton
1226
1510
  settings = Settings()
@@ -1,9 +1,9 @@
1
1
  -- Background index creation
2
2
  -- Run AFTER initial data load to avoid blocking writes
3
3
 
4
- -- HNSW vector index for embeddings_users
5
- CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_embeddings_users_vector_hnsw
6
- ON embeddings_users
4
+ -- HNSW vector index for embeddings_files
5
+ CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_embeddings_files_vector_hnsw
6
+ ON embeddings_files
7
7
  USING hnsw (embedding vector_cosine_ops);
8
8
 
9
9
  -- HNSW vector index for embeddings_image_resources
@@ -11,24 +11,14 @@ CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_embeddings_image_resources_vector_hn
11
11
  ON embeddings_image_resources
12
12
  USING hnsw (embedding vector_cosine_ops);
13
13
 
14
- -- HNSW vector index for embeddings_moments
15
- CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_embeddings_moments_vector_hnsw
16
- ON embeddings_moments
17
- USING hnsw (embedding vector_cosine_ops);
18
-
19
- -- HNSW vector index for embeddings_resources
20
- CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_embeddings_resources_vector_hnsw
21
- ON embeddings_resources
22
- USING hnsw (embedding vector_cosine_ops);
23
-
24
14
  -- HNSW vector index for embeddings_messages
25
15
  CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_embeddings_messages_vector_hnsw
26
16
  ON embeddings_messages
27
17
  USING hnsw (embedding vector_cosine_ops);
28
18
 
29
- -- HNSW vector index for embeddings_files
30
- CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_embeddings_files_vector_hnsw
31
- ON embeddings_files
19
+ -- HNSW vector index for embeddings_moments
20
+ CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_embeddings_moments_vector_hnsw
21
+ ON embeddings_moments
32
22
  USING hnsw (embedding vector_cosine_ops);
33
23
 
34
24
  -- HNSW vector index for embeddings_ontology_configs
@@ -36,7 +26,22 @@ CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_embeddings_ontology_configs_vector_h
36
26
  ON embeddings_ontology_configs
37
27
  USING hnsw (embedding vector_cosine_ops);
38
28
 
29
+ -- HNSW vector index for embeddings_resources
30
+ CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_embeddings_resources_vector_hnsw
31
+ ON embeddings_resources
32
+ USING hnsw (embedding vector_cosine_ops);
33
+
39
34
  -- HNSW vector index for embeddings_schemas
40
35
  CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_embeddings_schemas_vector_hnsw
41
36
  ON embeddings_schemas
42
37
  USING hnsw (embedding vector_cosine_ops);
38
+
39
+ -- HNSW vector index for embeddings_sessions
40
+ CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_embeddings_sessions_vector_hnsw
41
+ ON embeddings_sessions
42
+ USING hnsw (embedding vector_cosine_ops);
43
+
44
+ -- HNSW vector index for embeddings_users
45
+ CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_embeddings_users_vector_hnsw
46
+ ON embeddings_users
47
+ USING hnsw (embedding vector_cosine_ops);