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/utils/sql_paths.py ADDED
@@ -0,0 +1,146 @@
1
+ """Utilities for resolving SQL file paths.
2
+
3
+ Handles package SQL directory resolution and user migrations.
4
+
5
+ Convention for user migrations:
6
+ Place custom SQL files in `./sql/migrations/` relative to your project root.
7
+ Files should be numbered (e.g., `100_custom_table.sql`) to control execution order.
8
+ Package migrations (001-099) run first, then user migrations (100+).
9
+ """
10
+
11
+ from pathlib import Path
12
+ from typing import List, Optional
13
+ import importlib.resources
14
+
15
+ # Convention: Default location for user-maintained migrations
16
+ USER_SQL_DIR_CONVENTION = "sql"
17
+
18
+
19
+ def get_package_sql_dir() -> Path:
20
+ """Get the SQL directory from the installed rem package.
21
+
22
+ Returns:
23
+ Path to the package's sql directory
24
+
25
+ Raises:
26
+ FileNotFoundError: If the SQL directory cannot be found
27
+ """
28
+ try:
29
+ # Use importlib.resources for Python 3.9+
30
+ sql_ref = importlib.resources.files("rem") / "sql"
31
+ package_sql = Path(str(sql_ref))
32
+ if package_sql.exists():
33
+ return package_sql
34
+ except (AttributeError, TypeError):
35
+ pass
36
+
37
+ # Fallback: use __file__ to find package location
38
+ try:
39
+ import rem
40
+ package_sql = Path(rem.__file__).parent / "sql"
41
+ if package_sql.exists():
42
+ return package_sql
43
+ except (ImportError, AttributeError):
44
+ pass
45
+
46
+ # Development fallback: check relative to cwd
47
+ dev_sql = Path("src/rem/sql")
48
+ if dev_sql.exists():
49
+ return dev_sql
50
+
51
+ raise FileNotFoundError(
52
+ "Could not locate rem SQL directory. "
53
+ "Ensure remdb is properly installed or run from the source directory."
54
+ )
55
+
56
+
57
+ def get_package_migrations_dir() -> Path:
58
+ """Get the migrations directory from the installed rem package.
59
+
60
+ Returns:
61
+ Path to the package's migrations directory
62
+ """
63
+ return get_package_sql_dir() / "migrations"
64
+
65
+
66
+ def get_user_sql_dir() -> Optional[Path]:
67
+ """Get the conventional user SQL directory if it exists.
68
+
69
+ Looks for `./sql/` relative to the current working directory.
70
+ This follows the convention for user-maintained migrations.
71
+
72
+ Returns:
73
+ Path to user sql directory if it exists, None otherwise
74
+ """
75
+ user_sql = Path.cwd() / USER_SQL_DIR_CONVENTION
76
+ if user_sql.exists() and user_sql.is_dir():
77
+ return user_sql
78
+ return None
79
+
80
+
81
+ def list_package_migrations() -> List[Path]:
82
+ """List all migration files in the package.
83
+
84
+ Returns:
85
+ Sorted list of migration file paths
86
+ """
87
+ try:
88
+ migrations_dir = get_package_migrations_dir()
89
+ if migrations_dir.exists():
90
+ return sorted(
91
+ f for f in migrations_dir.glob("*.sql")
92
+ if f.name[0].isdigit() # Only numbered migrations
93
+ )
94
+ except FileNotFoundError:
95
+ pass
96
+
97
+ return []
98
+
99
+
100
+ def list_user_migrations() -> List[Path]:
101
+ """List all migration files in the user's sql/migrations directory.
102
+
103
+ Returns:
104
+ Sorted list of user migration file paths
105
+ """
106
+ user_sql = get_user_sql_dir()
107
+ if user_sql:
108
+ migrations_dir = user_sql / "migrations"
109
+ if migrations_dir.exists():
110
+ return sorted(
111
+ f for f in migrations_dir.glob("*.sql")
112
+ if f.name[0].isdigit() # Only numbered migrations
113
+ )
114
+ return []
115
+
116
+
117
+ def list_all_migrations() -> List[Path]:
118
+ """List all migration files from package and user directories.
119
+
120
+ Collects migrations from:
121
+ 1. Package migrations directory
122
+ 2. User directory (./sql/migrations/) if it exists
123
+
124
+ Files are sorted by name, so use numbered prefixes to control order:
125
+ - 001-099: Reserved for package migrations
126
+ - 100+: Recommended for user migrations
127
+
128
+ Returns:
129
+ Sorted list of all migration file paths (by filename)
130
+ """
131
+ all_migrations = []
132
+ seen_names = set()
133
+
134
+ # Package migrations first
135
+ for f in list_package_migrations():
136
+ if f.name not in seen_names:
137
+ all_migrations.append(f)
138
+ seen_names.add(f.name)
139
+
140
+ # User migrations second
141
+ for f in list_user_migrations():
142
+ if f.name not in seen_names:
143
+ all_migrations.append(f)
144
+ seen_names.add(f.name)
145
+
146
+ return sorted(all_migrations, key=lambda p: p.name)
rem/utils/sql_types.py CHANGED
@@ -16,6 +16,7 @@ Best Practices:
16
16
  - UUID for identifiers in Union types
17
17
  """
18
18
 
19
+ import types
19
20
  from datetime import date, datetime, time
20
21
  from typing import Any, Union, get_args, get_origin
21
22
  from uuid import UUID
@@ -78,8 +79,9 @@ def get_sql_type(field_info: FieldInfo, field_name: str) -> str:
78
79
  return "TEXT"
79
80
 
80
81
  # Handle Union types (including Optional[T] which is Union[T, None])
82
+ # Also handles Python 3.10+ `X | None` syntax which uses types.UnionType
81
83
  origin = get_origin(annotation)
82
- if origin is Union:
84
+ if origin is Union or isinstance(annotation, types.UnionType):
83
85
  args = get_args(annotation)
84
86
  # Filter out NoneType
85
87
  non_none_args = [arg for arg in args if arg is not type(None)]
rem/utils/vision.py CHANGED
@@ -11,7 +11,6 @@ markdown descriptions of images.
11
11
  """
12
12
 
13
13
  import base64
14
- import os
15
14
  from enum import Enum
16
15
  from pathlib import Path
17
16
  from typing import Optional
@@ -19,6 +18,9 @@ from typing import Optional
19
18
  import requests
20
19
  from loguru import logger
21
20
 
21
+ from rem.utils.constants import HTTP_TIMEOUT_LONG, VISION_MAX_TOKENS
22
+ from rem.utils.mime_types import EXTENSION_TO_MIME
23
+
22
24
 
23
25
  class VisionProvider(str, Enum):
24
26
  """Supported vision providers."""
@@ -141,14 +143,7 @@ class ImageAnalyzer:
141
143
 
142
144
  # Detect media type
143
145
  suffix = image_path.suffix.lower()
144
- media_type_map = {
145
- ".png": "image/png",
146
- ".jpg": "image/jpeg",
147
- ".jpeg": "image/jpeg",
148
- ".gif": "image/gif",
149
- ".webp": "image/webp",
150
- }
151
- media_type = media_type_map.get(suffix, "image/png")
146
+ media_type = EXTENSION_TO_MIME.get(suffix, "image/png")
152
147
 
153
148
  logger.info(f"Analyzing {image_path.name} with {self.provider.value} ({self.model})")
154
149
 
@@ -190,7 +185,7 @@ class ImageAnalyzer:
190
185
 
191
186
  body = {
192
187
  "model": self.model,
193
- "max_tokens": 2048,
188
+ "max_tokens": VISION_MAX_TOKENS,
194
189
  "messages": [
195
190
  {
196
191
  "role": "user",
@@ -216,7 +211,7 @@ class ImageAnalyzer:
216
211
  "https://api.anthropic.com/v1/messages",
217
212
  headers=headers,
218
213
  json=body,
219
- timeout=60.0,
214
+ timeout=HTTP_TIMEOUT_LONG,
220
215
  )
221
216
 
222
217
  if response.status_code != 200:
@@ -261,7 +256,7 @@ class ImageAnalyzer:
261
256
  url,
262
257
  params=params,
263
258
  json=body,
264
- timeout=60.0,
259
+ timeout=HTTP_TIMEOUT_LONG,
265
260
  )
266
261
 
267
262
  if response.status_code != 200:
@@ -311,14 +306,14 @@ class ImageAnalyzer:
311
306
  ],
312
307
  }
313
308
  ],
314
- "max_tokens": 2048,
309
+ "max_tokens": VISION_MAX_TOKENS,
315
310
  }
316
311
 
317
312
  response = requests.post(
318
313
  url,
319
314
  headers=headers,
320
315
  json=body,
321
- timeout=60.0,
316
+ timeout=HTTP_TIMEOUT_LONG,
322
317
  )
323
318
 
324
319
  if response.status_code != 200:
rem/workers/README.md CHANGED
@@ -207,7 +207,7 @@ Reads recent activity to generate comprehensive user profiles.
207
207
 
208
208
  **CLI:**
209
209
  ```bash
210
- rem-dreaming user-model --tenant-id=tenant-123
210
+ rem-dreaming user-model
211
211
  ```
212
212
 
213
213
  **Frequency:** Daily (runs as part of full workflow)
@@ -235,13 +235,13 @@ Extracts temporal narratives from resources.
235
235
  **CLI:**
236
236
  ```bash
237
237
  # Process last 24 hours
238
- rem-dreaming moments --tenant-id=tenant-123
238
+ rem-dreaming moments
239
239
 
240
240
  # Custom lookback
241
- rem-dreaming moments --tenant-id=tenant-123 --lookback-hours=48
241
+ rem-dreaming moments --lookback-hours=48
242
242
 
243
243
  # Limit resources processed
244
- rem-dreaming moments --tenant-id=tenant-123 --limit=100
244
+ rem-dreaming moments --limit=100
245
245
  ```
246
246
 
247
247
  **Frequency:** Daily or on-demand
@@ -283,13 +283,13 @@ Builds semantic relationships between resources.
283
283
  **CLI:**
284
284
  ```bash
285
285
  # Semantic mode (fast, cheap)
286
- rem-dreaming affinity --tenant-id=tenant-123
286
+ rem-dreaming affinity
287
287
 
288
288
  # LLM mode (intelligent, expensive)
289
- rem-dreaming affinity --tenant-id=tenant-123 --use-llm --limit=100
289
+ rem-dreaming affinity --use-llm --limit=100
290
290
 
291
291
  # Custom lookback
292
- rem-dreaming affinity --tenant-id=tenant-123 --lookback-hours=168
292
+ rem-dreaming affinity --lookback-hours=168
293
293
  ```
294
294
 
295
295
  **Frequency:**
@@ -308,13 +308,13 @@ Runs all operations in sequence.
308
308
  **CLI:**
309
309
  ```bash
310
310
  # Single tenant
311
- rem-dreaming full --tenant-id=tenant-123
311
+ rem-dreaming full
312
312
 
313
313
  # All active tenants (daily cron)
314
314
  rem-dreaming full --all-tenants
315
315
 
316
316
  # Use LLM affinity mode
317
- rem-dreaming full --tenant-id=tenant-123 --use-llm-affinity
317
+ rem-dreaming full --use-llm-affinity
318
318
  ```
319
319
 
320
320
  **Frequency:** Daily at 3 AM UTC
@@ -455,16 +455,16 @@ export REM_API_URL=http://localhost:8000
455
455
  export OPENAI_API_KEY=sk-...
456
456
 
457
457
  # Run user model update
458
- python -m rem.cli.dreaming user-model --tenant-id=tenant-test
458
+ python -m rem.cli.dreaming user-model
459
459
 
460
460
  # Run moment construction
461
- python -m rem.cli.dreaming moments --tenant-id=tenant-test --lookback-hours=24
461
+ python -m rem.cli.dreaming moments --lookback-hours=24
462
462
 
463
463
  # Run affinity (semantic mode)
464
- python -m rem.cli.dreaming affinity --tenant-id=tenant-test
464
+ python -m rem.cli.dreaming affinity
465
465
 
466
466
  # Run full workflow
467
- python -m rem.cli.dreaming full --tenant-id=tenant-test
467
+ python -m rem.cli.dreaming full
468
468
  ```
469
469
 
470
470
  ### Testing with Docker
@@ -478,7 +478,7 @@ docker run --rm \
478
478
  -e REM_API_URL=http://host.docker.internal:8000 \
479
479
  -e OPENAI_API_KEY=$OPENAI_API_KEY \
480
480
  rem-stack:latest \
481
- python -m rem.cli.dreaming full --tenant-id=tenant-test
481
+ python -m rem.cli.dreaming full
482
482
  ```
483
483
 
484
484
  ## Architecture Decisions
rem/workers/__init__.py CHANGED
@@ -1,5 +1,7 @@
1
1
  """Background workers for processing tasks."""
2
2
 
3
+ from .db_listener import DBListener
3
4
  from .sqs_file_processor import SQSFileProcessor
5
+ from .unlogged_maintainer import UnloggedMaintainer
4
6
 
5
- __all__ = ["SQSFileProcessor"]
7
+ __all__ = ["DBListener", "SQSFileProcessor", "UnloggedMaintainer"]