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
rem/workers/dreaming.py
ADDED
|
@@ -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())
|