remdb 0.2.6__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 (187) hide show
  1. rem/__init__.py +2 -0
  2. rem/agentic/README.md +650 -0
  3. rem/agentic/__init__.py +39 -0
  4. rem/agentic/agents/README.md +155 -0
  5. rem/agentic/agents/__init__.py +8 -0
  6. rem/agentic/context.py +148 -0
  7. rem/agentic/context_builder.py +329 -0
  8. rem/agentic/mcp/__init__.py +0 -0
  9. rem/agentic/mcp/tool_wrapper.py +107 -0
  10. rem/agentic/otel/__init__.py +5 -0
  11. rem/agentic/otel/setup.py +151 -0
  12. rem/agentic/providers/phoenix.py +674 -0
  13. rem/agentic/providers/pydantic_ai.py +572 -0
  14. rem/agentic/query.py +117 -0
  15. rem/agentic/query_helper.py +89 -0
  16. rem/agentic/schema.py +396 -0
  17. rem/agentic/serialization.py +245 -0
  18. rem/agentic/tools/__init__.py +5 -0
  19. rem/agentic/tools/rem_tools.py +231 -0
  20. rem/api/README.md +420 -0
  21. rem/api/main.py +324 -0
  22. rem/api/mcp_router/prompts.py +182 -0
  23. rem/api/mcp_router/resources.py +536 -0
  24. rem/api/mcp_router/server.py +213 -0
  25. rem/api/mcp_router/tools.py +584 -0
  26. rem/api/routers/auth.py +229 -0
  27. rem/api/routers/chat/__init__.py +5 -0
  28. rem/api/routers/chat/completions.py +281 -0
  29. rem/api/routers/chat/json_utils.py +76 -0
  30. rem/api/routers/chat/models.py +124 -0
  31. rem/api/routers/chat/streaming.py +185 -0
  32. rem/auth/README.md +258 -0
  33. rem/auth/__init__.py +26 -0
  34. rem/auth/middleware.py +100 -0
  35. rem/auth/providers/__init__.py +13 -0
  36. rem/auth/providers/base.py +376 -0
  37. rem/auth/providers/google.py +163 -0
  38. rem/auth/providers/microsoft.py +237 -0
  39. rem/cli/README.md +455 -0
  40. rem/cli/__init__.py +8 -0
  41. rem/cli/commands/README.md +126 -0
  42. rem/cli/commands/__init__.py +3 -0
  43. rem/cli/commands/ask.py +565 -0
  44. rem/cli/commands/configure.py +423 -0
  45. rem/cli/commands/db.py +493 -0
  46. rem/cli/commands/dreaming.py +324 -0
  47. rem/cli/commands/experiments.py +1124 -0
  48. rem/cli/commands/mcp.py +66 -0
  49. rem/cli/commands/process.py +245 -0
  50. rem/cli/commands/schema.py +183 -0
  51. rem/cli/commands/serve.py +106 -0
  52. rem/cli/dreaming.py +363 -0
  53. rem/cli/main.py +88 -0
  54. rem/config.py +237 -0
  55. rem/mcp_server.py +41 -0
  56. rem/models/core/__init__.py +49 -0
  57. rem/models/core/core_model.py +64 -0
  58. rem/models/core/engram.py +333 -0
  59. rem/models/core/experiment.py +628 -0
  60. rem/models/core/inline_edge.py +132 -0
  61. rem/models/core/rem_query.py +243 -0
  62. rem/models/entities/__init__.py +43 -0
  63. rem/models/entities/file.py +57 -0
  64. rem/models/entities/image_resource.py +88 -0
  65. rem/models/entities/message.py +35 -0
  66. rem/models/entities/moment.py +123 -0
  67. rem/models/entities/ontology.py +191 -0
  68. rem/models/entities/ontology_config.py +131 -0
  69. rem/models/entities/resource.py +95 -0
  70. rem/models/entities/schema.py +87 -0
  71. rem/models/entities/user.py +85 -0
  72. rem/py.typed +0 -0
  73. rem/schemas/README.md +507 -0
  74. rem/schemas/__init__.py +6 -0
  75. rem/schemas/agents/README.md +92 -0
  76. rem/schemas/agents/core/moment-builder.yaml +178 -0
  77. rem/schemas/agents/core/rem-query-agent.yaml +226 -0
  78. rem/schemas/agents/core/resource-affinity-assessor.yaml +99 -0
  79. rem/schemas/agents/core/simple-assistant.yaml +19 -0
  80. rem/schemas/agents/core/user-profile-builder.yaml +163 -0
  81. rem/schemas/agents/examples/contract-analyzer.yaml +317 -0
  82. rem/schemas/agents/examples/contract-extractor.yaml +134 -0
  83. rem/schemas/agents/examples/cv-parser.yaml +263 -0
  84. rem/schemas/agents/examples/hello-world.yaml +37 -0
  85. rem/schemas/agents/examples/query.yaml +54 -0
  86. rem/schemas/agents/examples/simple.yaml +21 -0
  87. rem/schemas/agents/examples/test.yaml +29 -0
  88. rem/schemas/agents/rem.yaml +128 -0
  89. rem/schemas/evaluators/hello-world/default.yaml +77 -0
  90. rem/schemas/evaluators/rem/faithfulness.yaml +219 -0
  91. rem/schemas/evaluators/rem/lookup-correctness.yaml +182 -0
  92. rem/schemas/evaluators/rem/retrieval-precision.yaml +199 -0
  93. rem/schemas/evaluators/rem/retrieval-recall.yaml +211 -0
  94. rem/schemas/evaluators/rem/search-correctness.yaml +192 -0
  95. rem/services/__init__.py +16 -0
  96. rem/services/audio/INTEGRATION.md +308 -0
  97. rem/services/audio/README.md +376 -0
  98. rem/services/audio/__init__.py +15 -0
  99. rem/services/audio/chunker.py +354 -0
  100. rem/services/audio/transcriber.py +259 -0
  101. rem/services/content/README.md +1269 -0
  102. rem/services/content/__init__.py +5 -0
  103. rem/services/content/providers.py +806 -0
  104. rem/services/content/service.py +657 -0
  105. rem/services/dreaming/README.md +230 -0
  106. rem/services/dreaming/__init__.py +53 -0
  107. rem/services/dreaming/affinity_service.py +336 -0
  108. rem/services/dreaming/moment_service.py +264 -0
  109. rem/services/dreaming/ontology_service.py +54 -0
  110. rem/services/dreaming/user_model_service.py +297 -0
  111. rem/services/dreaming/utils.py +39 -0
  112. rem/services/embeddings/__init__.py +11 -0
  113. rem/services/embeddings/api.py +120 -0
  114. rem/services/embeddings/worker.py +421 -0
  115. rem/services/fs/README.md +662 -0
  116. rem/services/fs/__init__.py +62 -0
  117. rem/services/fs/examples.py +206 -0
  118. rem/services/fs/examples_paths.py +204 -0
  119. rem/services/fs/git_provider.py +935 -0
  120. rem/services/fs/local_provider.py +760 -0
  121. rem/services/fs/parsing-hooks-examples.md +172 -0
  122. rem/services/fs/paths.py +276 -0
  123. rem/services/fs/provider.py +460 -0
  124. rem/services/fs/s3_provider.py +1042 -0
  125. rem/services/fs/service.py +186 -0
  126. rem/services/git/README.md +1075 -0
  127. rem/services/git/__init__.py +17 -0
  128. rem/services/git/service.py +469 -0
  129. rem/services/phoenix/EXPERIMENT_DESIGN.md +1146 -0
  130. rem/services/phoenix/README.md +453 -0
  131. rem/services/phoenix/__init__.py +46 -0
  132. rem/services/phoenix/client.py +686 -0
  133. rem/services/phoenix/config.py +88 -0
  134. rem/services/phoenix/prompt_labels.py +477 -0
  135. rem/services/postgres/README.md +575 -0
  136. rem/services/postgres/__init__.py +23 -0
  137. rem/services/postgres/migration_service.py +427 -0
  138. rem/services/postgres/pydantic_to_sqlalchemy.py +232 -0
  139. rem/services/postgres/register_type.py +352 -0
  140. rem/services/postgres/repository.py +337 -0
  141. rem/services/postgres/schema_generator.py +379 -0
  142. rem/services/postgres/service.py +802 -0
  143. rem/services/postgres/sql_builder.py +354 -0
  144. rem/services/rem/README.md +304 -0
  145. rem/services/rem/__init__.py +23 -0
  146. rem/services/rem/exceptions.py +71 -0
  147. rem/services/rem/executor.py +293 -0
  148. rem/services/rem/parser.py +145 -0
  149. rem/services/rem/queries.py +196 -0
  150. rem/services/rem/query.py +371 -0
  151. rem/services/rem/service.py +527 -0
  152. rem/services/session/README.md +374 -0
  153. rem/services/session/__init__.py +6 -0
  154. rem/services/session/compression.py +360 -0
  155. rem/services/session/reload.py +77 -0
  156. rem/settings.py +1235 -0
  157. rem/sql/002_install_models.sql +1068 -0
  158. rem/sql/background_indexes.sql +42 -0
  159. rem/sql/install_models.sql +1038 -0
  160. rem/sql/migrations/001_install.sql +503 -0
  161. rem/sql/migrations/002_install_models.sql +1202 -0
  162. rem/utils/AGENTIC_CHUNKING.md +597 -0
  163. rem/utils/README.md +583 -0
  164. rem/utils/__init__.py +43 -0
  165. rem/utils/agentic_chunking.py +622 -0
  166. rem/utils/batch_ops.py +343 -0
  167. rem/utils/chunking.py +108 -0
  168. rem/utils/clip_embeddings.py +276 -0
  169. rem/utils/dict_utils.py +98 -0
  170. rem/utils/embeddings.py +423 -0
  171. rem/utils/examples/embeddings_example.py +305 -0
  172. rem/utils/examples/sql_types_example.py +202 -0
  173. rem/utils/markdown.py +16 -0
  174. rem/utils/model_helpers.py +236 -0
  175. rem/utils/schema_loader.py +229 -0
  176. rem/utils/sql_types.py +348 -0
  177. rem/utils/user_id.py +81 -0
  178. rem/utils/vision.py +330 -0
  179. rem/workers/README.md +506 -0
  180. rem/workers/__init__.py +5 -0
  181. rem/workers/dreaming.py +502 -0
  182. rem/workers/engram_processor.py +312 -0
  183. rem/workers/sqs_file_processor.py +193 -0
  184. remdb-0.2.6.dist-info/METADATA +1191 -0
  185. remdb-0.2.6.dist-info/RECORD +187 -0
  186. remdb-0.2.6.dist-info/WHEEL +4 -0
  187. remdb-0.2.6.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,502 @@
1
+ """
2
+ Dreaming Worker - REM memory indexing and insight extraction.
3
+
4
+ The dreaming worker processes user content to build the REM knowledge graph
5
+ through three core operations:
6
+
7
+ 1. **User Model Updates**: Extract and update user profiles from activity
8
+ 2. **Moment Construction**: Identify temporal narratives from resources
9
+ 3. **Resource Affinity**: Build semantic relationships between resources
10
+
11
+ Design Philosophy:
12
+ - Lean implementation: Push complex utilities to services/repositories
13
+ - REM-first: Use REM system for all reads and writes
14
+ - Modular: Each operation is independent and composable
15
+ - Observable: Rich logging and metrics
16
+ - Cloud-native: Designed for Kubernetes CronJob execution
17
+
18
+ Architecture:
19
+ ```
20
+ ┌─────────────────────────────────────────────────────────────┐
21
+ │ Dreaming Worker │
22
+ ├─────────────────────────────────────────────────────────────┤
23
+ │ │
24
+ │ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
25
+ │ │ User Model │ │ Moment │ │ Resource │ │
26
+ │ │ Updater │ │ Constructor │ │ Affinity │ │
27
+ │ └───────┬───────┘ └───────┬───────┘ └───────┬───────┘ │
28
+ │ │ │ │ │
29
+ │ └──────────────────┼──────────────────┘ │
30
+ │ │ │
31
+ │ ┌───────▼───────┐ │
32
+ │ │ REM Services │ │
33
+ │ │ - Repository │ │
34
+ │ │ - Query │ │
35
+ │ │ - Embedding │ │
36
+ │ └───────────────┘ │
37
+ └─────────────────────────────────────────────────────────────┘
38
+ ```
39
+
40
+ User Model Updates:
41
+ - Reads recent sessions, moments, resources, files
42
+ - Generates user summary using LLM
43
+ - Updates User entity with latest profile information
44
+ - Adds graph edges to key resources and moments
45
+
46
+ Moment Construction:
47
+ - Queries recent resources (lookback window)
48
+ - Uses LLM to extract temporal narratives
49
+ - Creates Moment entities with temporal boundaries
50
+ - Links moments to source resources via graph edges
51
+ - Generates embeddings for moment content
52
+
53
+ Resource Affinity:
54
+ - Semantic similarity mode (fast, vector-based)
55
+ - LLM mode (intelligent, context-aware)
56
+ - Creates graph edges between related resources
57
+ - Updates resource entities with affinity edges
58
+
59
+ CLI Usage:
60
+ ```bash
61
+ # Update user models
62
+ rem-dreaming user-model --user-id=user-123
63
+
64
+ # Extract moments for user
65
+ rem-dreaming moments --user-id=user-123 --lookback-hours=24
66
+
67
+ # Build resource affinity (semantic mode)
68
+ rem-dreaming affinity --user-id=user-123 --lookback-hours=168
69
+
70
+ # Build resource affinity (LLM mode)
71
+ rem-dreaming affinity --user-id=user-123 --use-llm --limit=100
72
+
73
+ # Run all operations (recommended for daily cron)
74
+ rem-dreaming full --user-id=user-123
75
+
76
+ # Process all active users
77
+ rem-dreaming full --all-users
78
+ ```
79
+
80
+ Environment Variables:
81
+ - REM_API_URL: REM API endpoint (default: http://rem-api:8000)
82
+ - REM_EMBEDDING_PROVIDER: Embedding provider (default: text-embedding-3-small)
83
+ - REM_DEFAULT_MODEL: LLM model (default: gpt-4o)
84
+ - REM_LOOKBACK_HOURS: Default lookback window (default: 24)
85
+ - OPENAI_API_KEY: OpenAI API key for embeddings/LLM
86
+
87
+ Kubernetes CronJob:
88
+ - Daily execution (3 AM): Full indexing for all tenants
89
+ - Resource limits: 512Mi memory, 1 CPU
90
+ - Spot instances: Tolerate node affinity
91
+ - Completion tracking: Save job results to database
92
+
93
+ Best Practices:
94
+ - Start with small lookback windows (24-48 hours)
95
+ - Use semantic mode for frequent updates (cheap, fast)
96
+ - Use LLM mode sparingly (expensive, slow)
97
+ - Always use --limit with LLM mode to control costs
98
+ - Monitor embedding/LLM costs in provider dashboard
99
+
100
+ Error Handling:
101
+ - Graceful degradation: Continue on partial failures
102
+ - Retry logic: Exponential backoff for transient errors
103
+ - Error reporting: Log errors with context for debugging
104
+ - Job status: Save success/failure status to database
105
+
106
+ Performance:
107
+ - Batch operations: Minimize round trips to REM API
108
+ - Streaming: Process large result sets incrementally
109
+ - Parallelization: Use asyncio for concurrent operations
110
+ - Caching: Cache embeddings and LLM responses when possible
111
+
112
+ Observability:
113
+ - Structured logging: JSON logs for parsing
114
+ - Metrics: Count processed resources, moments, edges
115
+ - Tracing: OpenTelemetry traces for distributed tracing
116
+ - Alerts: Notify on job failures or anomalies
117
+ """
118
+
119
+ import asyncio
120
+ from datetime import datetime, timedelta, timezone
121
+ from enum import Enum
122
+ from typing import Any, Optional, TYPE_CHECKING
123
+ from uuid import uuid4
124
+
125
+ import httpx
126
+ from loguru import logger
127
+ from pydantic import BaseModel, Field
128
+
129
+ if TYPE_CHECKING:
130
+ from ..services.postgres import PostgresService
131
+ from ..services.dreaming.affinity_service import AffinityMode
132
+
133
+
134
+ class TaskType(str, Enum):
135
+ """Dreaming task types."""
136
+
137
+ USER_MODEL = "user_model"
138
+ MOMENTS = "moments"
139
+ AFFINITY = "affinity"
140
+ ONTOLOGY = "ontology" # Extract domain-specific knowledge from files
141
+ FULL = "full"
142
+
143
+
144
+ class DreamingJob(BaseModel):
145
+ """Dreaming job execution record."""
146
+
147
+ id: str = Field(default_factory=lambda: str(uuid4()))
148
+ user_id: str
149
+ task_type: TaskType
150
+ status: str = "pending" # pending, running, completed, failed
151
+ started_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
152
+ completed_at: Optional[datetime] = None
153
+ result: dict[str, Any] = Field(default_factory=dict)
154
+ error: Optional[str] = None
155
+
156
+
157
+ class DreamingWorker:
158
+ """
159
+ REM dreaming worker for memory indexing.
160
+
161
+ Processes user content to build the REM knowledge graph through
162
+ user model updates, moment construction, and resource affinity.
163
+
164
+ This is a lean implementation that delegates complex operations
165
+ to REM services and repositories, keeping the worker focused on
166
+ orchestration and coordination.
167
+
168
+ User-ID First Approach:
169
+ - All operations are scoped by user_id (primary identifier)
170
+ - tenant_id field is set equal to user_id in entities (backward compatibility)
171
+ - In single-user deployments, user_id is the only identifier needed
172
+ - In future multi-tenant SaaS, tenant_id could group users (e.g., "acme-corp")
173
+ enabling org-wide dreaming workflows and cross-user knowledge graphs
174
+ - For now, all filtering and isolation is done via user_id
175
+ """
176
+
177
+ def __init__(
178
+ self,
179
+ rem_api_url: str = "http://rem-api:8000",
180
+ embedding_provider: str = "text-embedding-3-small",
181
+ default_model: str = "gpt-4o",
182
+ lookback_hours: int = 24,
183
+ ):
184
+ """
185
+ Initialize dreaming worker.
186
+
187
+ Args:
188
+ rem_api_url: REM API endpoint
189
+ embedding_provider: Embedding provider for vector search
190
+ default_model: Default LLM model for analysis
191
+ lookback_hours: Default lookback window in hours
192
+ """
193
+ self.rem_api_url = rem_api_url
194
+ self.embedding_provider = embedding_provider
195
+ self.default_model = default_model
196
+ self.lookback_hours = lookback_hours
197
+ self.client = httpx.AsyncClient(base_url=rem_api_url, timeout=300.0)
198
+ self._db: "PostgresService | None" = None # Lazy-loaded database connection
199
+
200
+ async def _ensure_db(self):
201
+ """
202
+ Ensure database connection is established.
203
+
204
+ Lazy-loads and caches the database connection for reuse across
205
+ multiple operations. Connection is shared for the lifetime of
206
+ the worker instance.
207
+
208
+ Returns:
209
+ PostgresService instance
210
+ """
211
+ if not self._db:
212
+ from rem.services.postgres import get_postgres_service
213
+ self._db = get_postgres_service()
214
+ if not self._db:
215
+ raise RuntimeError("PostgreSQL service not available")
216
+ await self._db.connect()
217
+ return self._db
218
+
219
+ async def close(self):
220
+ """Close HTTP client and database connection."""
221
+ await self.client.aclose()
222
+ if self._db:
223
+ await self._db.disconnect()
224
+ self._db = None
225
+
226
+ async def update_user_model(
227
+ self,
228
+ user_id: str,
229
+ time_window_days: int = 30,
230
+ max_sessions: int = 100,
231
+ max_moments: int = 20,
232
+ max_resources: int = 20,
233
+ ) -> dict[str, Any]:
234
+ """
235
+ Update user model from recent activity.
236
+
237
+ Delegates to user_model_service for implementation.
238
+
239
+ Args:
240
+ user_id: User to process
241
+ time_window_days: Days to look back for activity (default: 30)
242
+ max_sessions: Max sessions to analyze
243
+ max_moments: Max moments to include
244
+ max_resources: Max resources to include
245
+
246
+ Returns:
247
+ Statistics about user model update
248
+ """
249
+ from rem.services.dreaming import update_user_model as _update_user_model
250
+
251
+ db = await self._ensure_db()
252
+
253
+ return await _update_user_model(
254
+ user_id=user_id,
255
+ db=db,
256
+ default_model=self.default_model,
257
+ time_window_days=time_window_days,
258
+ max_messages=max_sessions, # Map max_sessions to max_messages parameter
259
+ max_moments=max_moments,
260
+ max_resources=max_resources,
261
+ )
262
+
263
+ async def construct_moments(
264
+ self,
265
+ user_id: str,
266
+ lookback_hours: Optional[int] = None,
267
+ limit: Optional[int] = None,
268
+ ) -> dict[str, Any]:
269
+ """
270
+ Extract moments from resources.
271
+
272
+ Delegates to moment_service for implementation.
273
+
274
+ Args:
275
+ user_id: User to process
276
+ lookback_hours: Hours to look back (default: self.lookback_hours)
277
+ limit: Max resources to process
278
+
279
+ Returns:
280
+ Statistics about moment construction
281
+ """
282
+ from rem.services.dreaming import construct_moments as _construct_moments
283
+
284
+ lookback = lookback_hours or self.lookback_hours
285
+ db = await self._ensure_db()
286
+
287
+ return await _construct_moments(
288
+ user_id=user_id,
289
+ db=db,
290
+ default_model=self.default_model,
291
+ lookback_hours=lookback,
292
+ limit=limit,
293
+ )
294
+
295
+ async def build_affinity(
296
+ self,
297
+ user_id: str,
298
+ mode: Optional["AffinityMode"] = None,
299
+ lookback_hours: Optional[int] = None,
300
+ limit: Optional[int] = None,
301
+ similarity_threshold: float = 0.7,
302
+ top_k: int = 3,
303
+ ) -> dict[str, Any]:
304
+ """
305
+ Build resource affinity graph.
306
+
307
+ Delegates to affinity_service for implementation.
308
+
309
+ Args:
310
+ user_id: User to process
311
+ mode: Affinity mode (semantic or llm)
312
+ lookback_hours: Hours to look back (default: self.lookback_hours)
313
+ limit: Max resources to process (REQUIRED for LLM mode)
314
+ similarity_threshold: Minimum similarity score for semantic mode (default: 0.7)
315
+ top_k: Number of similar resources to find per resource (default: 3)
316
+
317
+ Returns:
318
+ Statistics about affinity construction
319
+ """
320
+ from rem.services.dreaming import build_affinity as _build_affinity
321
+ from rem.services.dreaming.affinity_service import AffinityMode
322
+
323
+ # Default to SEMANTIC mode if not provided
324
+ if mode is None:
325
+ mode = AffinityMode.SEMANTIC
326
+
327
+ lookback = lookback_hours or self.lookback_hours
328
+ db = await self._ensure_db()
329
+
330
+ return await _build_affinity(
331
+ user_id=user_id,
332
+ db=db,
333
+ mode=mode, # Pass enum member, handled by service
334
+ default_model=self.default_model,
335
+ lookback_hours=lookback,
336
+ limit=limit,
337
+ similarity_threshold=similarity_threshold,
338
+ top_k=top_k,
339
+ )
340
+
341
+ async def extract_ontologies(
342
+ self,
343
+ user_id: str,
344
+ lookback_hours: Optional[int] = None,
345
+ limit: Optional[int] = None,
346
+ ) -> dict[str, Any]:
347
+ """
348
+ Extract domain-specific knowledge from files using custom agents.
349
+
350
+ Delegates to ontology_service for implementation.
351
+
352
+ Args:
353
+ user_id: User to process
354
+ lookback_hours: Hours to look back (default: self.lookback_hours)
355
+ limit: Max files to process
356
+
357
+ Returns:
358
+ Statistics about ontology extraction
359
+ """
360
+ from rem.services.dreaming import extract_ontologies as _extract_ontologies
361
+
362
+ lookback = lookback_hours or self.lookback_hours
363
+ return await _extract_ontologies(
364
+ user_id=user_id,
365
+ lookback_hours=lookback,
366
+ limit=limit,
367
+ )
368
+
369
+ async def process_full(
370
+ self,
371
+ user_id: str,
372
+ use_llm_affinity: bool = False,
373
+ lookback_hours: Optional[int] = None,
374
+ extract_ontologies: bool = True,
375
+ ) -> dict[str, Any]:
376
+ """
377
+ Run complete dreaming workflow.
378
+
379
+ Executes all dreaming operations in sequence:
380
+ 1. Extract ontologies from files (if enabled)
381
+ 2. Update user model
382
+ 3. Construct moments
383
+ 4. Build resource affinity
384
+
385
+ Recommended for daily cron execution.
386
+
387
+ Args:
388
+ user_id: User to process
389
+ use_llm_affinity: Use LLM mode for affinity (expensive)
390
+ lookback_hours: Hours to look back
391
+ extract_ontologies: Whether to run ontology extraction (default: True)
392
+
393
+ Returns:
394
+ Aggregated statistics from all operations
395
+ """
396
+ lookback = lookback_hours or self.lookback_hours
397
+ results = {
398
+ "user_id": user_id,
399
+ "lookback_hours": lookback,
400
+ "ontologies": {},
401
+ "user_model": {},
402
+ "moments": {},
403
+ "affinity": {},
404
+ }
405
+
406
+ # Ontology extraction (runs first to extract knowledge before moments)
407
+ if extract_ontologies:
408
+ try:
409
+ results["ontologies"] = await self.extract_ontologies(
410
+ user_id=user_id,
411
+ lookback_hours=lookback,
412
+ )
413
+ except Exception as e:
414
+ logger.exception("Ontology extraction failed")
415
+ results["ontologies"] = {"error": str(e)}
416
+
417
+ # User model update
418
+ try:
419
+ results["user_model"] = await self.update_user_model(
420
+ user_id=user_id,
421
+ )
422
+ except Exception as e:
423
+ logger.exception("User model update failed")
424
+ results["user_model"] = {"error": str(e)}
425
+
426
+ # Moment construction
427
+ try:
428
+ results["moments"] = await self.construct_moments(
429
+ user_id=user_id,
430
+ lookback_hours=lookback,
431
+ )
432
+ except Exception as e:
433
+ logger.exception("Moment construction failed")
434
+ results["moments"] = {"error": str(e)}
435
+
436
+ # Resource affinity
437
+ from rem.services.dreaming.affinity_service import AffinityMode as _AffinityMode
438
+ affinity_mode = _AffinityMode.LLM if use_llm_affinity else _AffinityMode.SEMANTIC
439
+ try:
440
+ results["affinity"] = await self.build_affinity(
441
+ user_id=user_id,
442
+ mode=affinity_mode,
443
+ lookback_hours=lookback,
444
+ )
445
+ except Exception as e:
446
+ logger.exception("Resource affinity building failed")
447
+ results["affinity"] = {"error": str(e)}
448
+
449
+ return results
450
+
451
+ async def process_all_users(
452
+ self,
453
+ task_type: TaskType = TaskType.FULL,
454
+ use_llm_affinity: bool = False,
455
+ lookback_hours: Optional[int] = None,
456
+ ) -> list[dict[str, Any]]:
457
+ """
458
+ Process all active users.
459
+
460
+ Queries REM for users with recent activity and processes
461
+ each user according to task_type.
462
+
463
+ Args:
464
+ task_type: Task to run for each user
465
+ use_llm_affinity: Use LLM mode for affinity
466
+ lookback_hours: Hours to look back
467
+
468
+ Returns:
469
+ List of results for each user
470
+ """
471
+ lookback = lookback_hours or self.lookback_hours
472
+
473
+ # TODO: Query REM for active users
474
+ # Filter by recent activity (resources with timestamp > cutoff)
475
+ # Process each user according to task_type
476
+
477
+ # Stub implementation
478
+ return [
479
+ {
480
+ "status": "stub_not_implemented",
481
+ "message": "Query REM API for users with recent activity",
482
+ }
483
+ ]
484
+
485
+
486
+ async def main():
487
+ """Main entry point (for testing)."""
488
+ worker = DreamingWorker()
489
+ try:
490
+ # Example: Process single user
491
+ result = await worker.process_full(
492
+ user_id="user-123",
493
+ use_llm_affinity=False,
494
+ lookback_hours=24,
495
+ )
496
+ print(result)
497
+ finally:
498
+ await worker.close()
499
+
500
+
501
+ if __name__ == "__main__":
502
+ asyncio.run(main())