remdb 0.3.242__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.
- rem/__init__.py +129 -0
- rem/agentic/README.md +760 -0
- rem/agentic/__init__.py +54 -0
- rem/agentic/agents/README.md +155 -0
- rem/agentic/agents/__init__.py +38 -0
- rem/agentic/agents/agent_manager.py +311 -0
- rem/agentic/agents/sse_simulator.py +502 -0
- rem/agentic/context.py +425 -0
- rem/agentic/context_builder.py +360 -0
- rem/agentic/llm_provider_models.py +301 -0
- rem/agentic/mcp/__init__.py +0 -0
- rem/agentic/mcp/tool_wrapper.py +273 -0
- rem/agentic/otel/__init__.py +5 -0
- rem/agentic/otel/setup.py +240 -0
- rem/agentic/providers/phoenix.py +926 -0
- rem/agentic/providers/pydantic_ai.py +854 -0
- rem/agentic/query.py +117 -0
- rem/agentic/query_helper.py +89 -0
- rem/agentic/schema.py +737 -0
- rem/agentic/serialization.py +245 -0
- rem/agentic/tools/__init__.py +5 -0
- rem/agentic/tools/rem_tools.py +242 -0
- rem/api/README.md +657 -0
- rem/api/deps.py +253 -0
- rem/api/main.py +460 -0
- rem/api/mcp_router/prompts.py +182 -0
- rem/api/mcp_router/resources.py +820 -0
- rem/api/mcp_router/server.py +243 -0
- rem/api/mcp_router/tools.py +1605 -0
- rem/api/middleware/tracking.py +172 -0
- rem/api/routers/admin.py +520 -0
- rem/api/routers/auth.py +898 -0
- rem/api/routers/chat/__init__.py +5 -0
- rem/api/routers/chat/child_streaming.py +394 -0
- rem/api/routers/chat/completions.py +702 -0
- rem/api/routers/chat/json_utils.py +76 -0
- rem/api/routers/chat/models.py +202 -0
- rem/api/routers/chat/otel_utils.py +33 -0
- rem/api/routers/chat/sse_events.py +546 -0
- rem/api/routers/chat/streaming.py +950 -0
- rem/api/routers/chat/streaming_utils.py +327 -0
- rem/api/routers/common.py +18 -0
- rem/api/routers/dev.py +87 -0
- rem/api/routers/feedback.py +276 -0
- rem/api/routers/messages.py +620 -0
- rem/api/routers/models.py +86 -0
- rem/api/routers/query.py +362 -0
- rem/api/routers/shared_sessions.py +422 -0
- rem/auth/README.md +258 -0
- rem/auth/__init__.py +36 -0
- rem/auth/jwt.py +367 -0
- rem/auth/middleware.py +318 -0
- rem/auth/providers/__init__.py +16 -0
- rem/auth/providers/base.py +376 -0
- rem/auth/providers/email.py +215 -0
- rem/auth/providers/google.py +163 -0
- rem/auth/providers/microsoft.py +237 -0
- rem/cli/README.md +517 -0
- rem/cli/__init__.py +8 -0
- rem/cli/commands/README.md +299 -0
- rem/cli/commands/__init__.py +3 -0
- rem/cli/commands/ask.py +549 -0
- rem/cli/commands/cluster.py +1808 -0
- rem/cli/commands/configure.py +495 -0
- rem/cli/commands/db.py +828 -0
- rem/cli/commands/dreaming.py +324 -0
- rem/cli/commands/experiments.py +1698 -0
- rem/cli/commands/mcp.py +66 -0
- rem/cli/commands/process.py +388 -0
- rem/cli/commands/query.py +109 -0
- rem/cli/commands/scaffold.py +47 -0
- rem/cli/commands/schema.py +230 -0
- rem/cli/commands/serve.py +106 -0
- rem/cli/commands/session.py +453 -0
- rem/cli/dreaming.py +363 -0
- rem/cli/main.py +123 -0
- rem/config.py +244 -0
- rem/mcp_server.py +41 -0
- rem/models/core/__init__.py +49 -0
- rem/models/core/core_model.py +70 -0
- rem/models/core/engram.py +333 -0
- rem/models/core/experiment.py +672 -0
- rem/models/core/inline_edge.py +132 -0
- rem/models/core/rem_query.py +246 -0
- rem/models/entities/__init__.py +68 -0
- rem/models/entities/domain_resource.py +38 -0
- rem/models/entities/feedback.py +123 -0
- rem/models/entities/file.py +57 -0
- rem/models/entities/image_resource.py +88 -0
- rem/models/entities/message.py +64 -0
- rem/models/entities/moment.py +123 -0
- rem/models/entities/ontology.py +181 -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/session.py +84 -0
- rem/models/entities/shared_session.py +180 -0
- rem/models/entities/subscriber.py +175 -0
- rem/models/entities/user.py +93 -0
- rem/py.typed +0 -0
- rem/registry.py +373 -0
- rem/schemas/README.md +507 -0
- rem/schemas/__init__.py +6 -0
- rem/schemas/agents/README.md +92 -0
- rem/schemas/agents/core/agent-builder.yaml +235 -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 +132 -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 +18 -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 +760 -0
- rem/services/content/service.py +762 -0
- rem/services/dreaming/README.md +230 -0
- rem/services/dreaming/__init__.py +53 -0
- rem/services/dreaming/affinity_service.py +322 -0
- rem/services/dreaming/moment_service.py +251 -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/email/__init__.py +10 -0
- rem/services/email/service.py +522 -0
- rem/services/email/templates.py +360 -0
- rem/services/embeddings/__init__.py +11 -0
- rem/services/embeddings/api.py +127 -0
- rem/services/embeddings/worker.py +435 -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 +960 -0
- rem/services/phoenix/config.py +88 -0
- rem/services/phoenix/prompt_labels.py +477 -0
- rem/services/postgres/README.md +757 -0
- rem/services/postgres/__init__.py +49 -0
- rem/services/postgres/diff_service.py +599 -0
- rem/services/postgres/migration_service.py +427 -0
- rem/services/postgres/programmable_diff_service.py +635 -0
- rem/services/postgres/pydantic_to_sqlalchemy.py +562 -0
- rem/services/postgres/register_type.py +353 -0
- rem/services/postgres/repository.py +481 -0
- rem/services/postgres/schema_generator.py +661 -0
- rem/services/postgres/service.py +802 -0
- rem/services/postgres/sql_builder.py +355 -0
- rem/services/rate_limit.py +113 -0
- rem/services/rem/README.md +318 -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 +180 -0
- rem/services/rem/queries.py +196 -0
- rem/services/rem/query.py +371 -0
- rem/services/rem/service.py +608 -0
- rem/services/session/README.md +374 -0
- rem/services/session/__init__.py +13 -0
- rem/services/session/compression.py +488 -0
- rem/services/session/pydantic_messages.py +310 -0
- rem/services/session/reload.py +85 -0
- rem/services/user_service.py +130 -0
- rem/settings.py +1877 -0
- rem/sql/background_indexes.sql +52 -0
- rem/sql/migrations/001_install.sql +983 -0
- rem/sql/migrations/002_install_models.sql +3157 -0
- rem/sql/migrations/003_optional_extensions.sql +326 -0
- rem/sql/migrations/004_cache_system.sql +282 -0
- rem/sql/migrations/005_schema_update.sql +145 -0
- rem/sql/migrations/migrate_session_id_to_uuid.sql +45 -0
- rem/utils/AGENTIC_CHUNKING.md +597 -0
- rem/utils/README.md +628 -0
- rem/utils/__init__.py +61 -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/constants.py +97 -0
- rem/utils/date_utils.py +228 -0
- rem/utils/dict_utils.py +98 -0
- rem/utils/embeddings.py +436 -0
- rem/utils/examples/embeddings_example.py +305 -0
- rem/utils/examples/sql_types_example.py +202 -0
- rem/utils/files.py +323 -0
- rem/utils/markdown.py +16 -0
- rem/utils/mime_types.py +158 -0
- rem/utils/model_helpers.py +492 -0
- rem/utils/schema_loader.py +649 -0
- rem/utils/sql_paths.py +146 -0
- rem/utils/sql_types.py +350 -0
- rem/utils/user_id.py +81 -0
- rem/utils/vision.py +325 -0
- rem/workers/README.md +506 -0
- rem/workers/__init__.py +7 -0
- rem/workers/db_listener.py +579 -0
- rem/workers/db_maintainer.py +74 -0
- rem/workers/dreaming.py +502 -0
- rem/workers/engram_processor.py +312 -0
- rem/workers/sqs_file_processor.py +193 -0
- rem/workers/unlogged_maintainer.py +463 -0
- remdb-0.3.242.dist-info/METADATA +1632 -0
- remdb-0.3.242.dist-info/RECORD +235 -0
- remdb-0.3.242.dist-info/WHEEL +4 -0
- remdb-0.3.242.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Engram - Core memory model for REM.
|
|
3
|
+
|
|
4
|
+
Engrams are structured memory documents that represent captured experiences,
|
|
5
|
+
observations, or insights. They are fundamentally Resources with optional
|
|
6
|
+
attached Moments, following the unified schema from the engram specification.
|
|
7
|
+
|
|
8
|
+
Key Design Principles:
|
|
9
|
+
- Engrams ARE Resources (category="engram")
|
|
10
|
+
- Human-friendly labels in graph edges (not UUIDs)
|
|
11
|
+
- Upsert with JSON merge behavior (never overwrite)
|
|
12
|
+
- Dual indexing: SQL + vector embeddings handled by repository
|
|
13
|
+
- YAML-first for human readability
|
|
14
|
+
|
|
15
|
+
Data Flow:
|
|
16
|
+
1. Upload YAML/JSON engram (API or S3)
|
|
17
|
+
2. Parse into Resource model
|
|
18
|
+
3. Call repository.upsert() - automatically handles:
|
|
19
|
+
- SQL persistence
|
|
20
|
+
- Vector embedding generation
|
|
21
|
+
- Entity key index population
|
|
22
|
+
4. Create attached Moments (if present)
|
|
23
|
+
5. Link moments to parent engram via graph edges
|
|
24
|
+
|
|
25
|
+
Example Engram Structure:
|
|
26
|
+
```yaml
|
|
27
|
+
kind: engram
|
|
28
|
+
name: "Daily Team Standup"
|
|
29
|
+
category: "meeting"
|
|
30
|
+
summary: "Daily standup discussing sprint progress"
|
|
31
|
+
timestamp: "2025-11-16T09:00:00Z"
|
|
32
|
+
uri: "s3://recordings/2025/11/16/standup.m4a"
|
|
33
|
+
content: |
|
|
34
|
+
Daily standup meeting with engineering team...
|
|
35
|
+
|
|
36
|
+
graph_edges:
|
|
37
|
+
- dst: "Q4 Roadmap Discussion"
|
|
38
|
+
rel_type: "semantic_similar"
|
|
39
|
+
weight: 0.75
|
|
40
|
+
properties:
|
|
41
|
+
dst_name: "Q4 Roadmap Discussion"
|
|
42
|
+
dst_entity_type: "resource/meeting"
|
|
43
|
+
confidence: 0.75
|
|
44
|
+
|
|
45
|
+
moments:
|
|
46
|
+
- name: "Sprint Progress Review"
|
|
47
|
+
content: "Sarah reviewed completed tickets"
|
|
48
|
+
summary: "Sprint progress update from Sarah"
|
|
49
|
+
starts_timestamp: "2025-11-16T09:00:00Z"
|
|
50
|
+
ends_timestamp: "2025-11-16T09:05:00Z"
|
|
51
|
+
moment_type: "meeting"
|
|
52
|
+
emotion_tags: ["focused", "productive"]
|
|
53
|
+
topic_tags: ["sprint-progress", "velocity"]
|
|
54
|
+
present_persons:
|
|
55
|
+
- id: "sarah-chen"
|
|
56
|
+
name: "Sarah Chen"
|
|
57
|
+
role: "VP Engineering"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Graph Edge Labels:
|
|
61
|
+
- Use natural, human-friendly labels: "Sarah Chen", "Q4 Roadmap"
|
|
62
|
+
- NOT kebab-case unless it's a file path: "docs/api-spec.md"
|
|
63
|
+
- NOT UUIDs: "550e8400-e29b-41d4-a716-446655440000"
|
|
64
|
+
- Enables conversational queries: "LOOKUP Sarah Chen"
|
|
65
|
+
|
|
66
|
+
JSON Merge Behavior:
|
|
67
|
+
- Graph edges are MERGED, not replaced
|
|
68
|
+
- Metadata is MERGED, not replaced
|
|
69
|
+
- Arrays (tags) are MERGED and deduplicated
|
|
70
|
+
- Content/summary updated if provided
|
|
71
|
+
- Timestamps preserved if not provided
|
|
72
|
+
|
|
73
|
+
Best Practices:
|
|
74
|
+
- Use natural, descriptive names
|
|
75
|
+
- Include device metadata when available
|
|
76
|
+
- Use standard categories: diary, meeting, note, observation, conversation, media
|
|
77
|
+
- Attach moments for temporal segments
|
|
78
|
+
- Use appropriate relationship types in graph edges
|
|
79
|
+
- Always include timestamps in ISO 8601 format
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
from datetime import datetime, timezone
|
|
83
|
+
from typing import Optional
|
|
84
|
+
|
|
85
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
86
|
+
|
|
87
|
+
from .inline_edge import InlineEdge
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class DeviceMetadata(BaseModel):
|
|
91
|
+
"""Device metadata for engram capture context."""
|
|
92
|
+
|
|
93
|
+
imei: Optional[str] = Field(
|
|
94
|
+
default=None,
|
|
95
|
+
description="Device IMEI identifier",
|
|
96
|
+
)
|
|
97
|
+
model: Optional[str] = Field(
|
|
98
|
+
default=None,
|
|
99
|
+
description="Device model (e.g., 'iPhone 15 Pro', 'MacBook Pro')",
|
|
100
|
+
)
|
|
101
|
+
os: Optional[str] = Field(
|
|
102
|
+
default=None,
|
|
103
|
+
description="Operating system (e.g., 'iOS 18.1', 'macOS 14.2')",
|
|
104
|
+
)
|
|
105
|
+
app: Optional[str] = Field(
|
|
106
|
+
default=None,
|
|
107
|
+
description="Application name (e.g., 'Percolate Voice', 'Percolate Desktop')",
|
|
108
|
+
)
|
|
109
|
+
version: Optional[str] = Field(
|
|
110
|
+
default=None,
|
|
111
|
+
description="Application version",
|
|
112
|
+
)
|
|
113
|
+
location: Optional[dict] = Field(
|
|
114
|
+
default=None,
|
|
115
|
+
description="GPS location data (latitude, longitude, accuracy, altitude, etc.)",
|
|
116
|
+
)
|
|
117
|
+
network: Optional[dict] = Field(
|
|
118
|
+
default=None,
|
|
119
|
+
description="Network information (type, carrier, signal_strength)",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class EngramMoment(BaseModel):
|
|
124
|
+
"""
|
|
125
|
+
Moment attached to an engram.
|
|
126
|
+
|
|
127
|
+
Represents a temporal segment within an engram with specific
|
|
128
|
+
temporal boundaries, present persons, and contextual metadata.
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
name: str = Field(
|
|
132
|
+
...,
|
|
133
|
+
description="Moment name (human-readable)",
|
|
134
|
+
)
|
|
135
|
+
content: str = Field(
|
|
136
|
+
...,
|
|
137
|
+
description="Moment content/description",
|
|
138
|
+
)
|
|
139
|
+
summary: Optional[str] = Field(
|
|
140
|
+
default=None,
|
|
141
|
+
description="Brief summary of the moment",
|
|
142
|
+
)
|
|
143
|
+
moment_type: Optional[str] = Field(
|
|
144
|
+
default=None,
|
|
145
|
+
description="Moment type (meeting, conversation, reflection, etc.)",
|
|
146
|
+
)
|
|
147
|
+
category: Optional[str] = Field(
|
|
148
|
+
default=None,
|
|
149
|
+
description="Moment category for grouping",
|
|
150
|
+
)
|
|
151
|
+
uri: Optional[str] = Field(
|
|
152
|
+
default=None,
|
|
153
|
+
description="Source URI (can include time fragment, e.g., 's3://file.m4a#t=0,300')",
|
|
154
|
+
)
|
|
155
|
+
starts_timestamp: Optional[datetime] = Field(
|
|
156
|
+
default=None,
|
|
157
|
+
description="Moment start time",
|
|
158
|
+
)
|
|
159
|
+
ends_timestamp: Optional[datetime] = Field(
|
|
160
|
+
default=None,
|
|
161
|
+
description="Moment end time",
|
|
162
|
+
)
|
|
163
|
+
emotion_tags: list[str] = Field(
|
|
164
|
+
default_factory=list,
|
|
165
|
+
description="Emotional context tags (focused, excited, concerned, etc.)",
|
|
166
|
+
)
|
|
167
|
+
topic_tags: list[str] = Field(
|
|
168
|
+
default_factory=list,
|
|
169
|
+
description="Topic tags in kebab-case (sprint-progress, api-design, etc.)",
|
|
170
|
+
)
|
|
171
|
+
present_persons: list[dict] = Field(
|
|
172
|
+
default_factory=list,
|
|
173
|
+
description="People present (Person objects with id, name, role)",
|
|
174
|
+
)
|
|
175
|
+
speakers: Optional[list[dict]] = Field(
|
|
176
|
+
default=None,
|
|
177
|
+
description="Speaker segments with text, speaker_identifier, timestamp, emotion",
|
|
178
|
+
)
|
|
179
|
+
location: Optional[str] = Field(
|
|
180
|
+
default=None,
|
|
181
|
+
description="GPS coordinates or descriptive location",
|
|
182
|
+
)
|
|
183
|
+
background_sounds: Optional[str] = Field(
|
|
184
|
+
default=None,
|
|
185
|
+
description="Ambient sounds description",
|
|
186
|
+
)
|
|
187
|
+
metadata: dict = Field(
|
|
188
|
+
default_factory=dict,
|
|
189
|
+
description="Additional moment metadata",
|
|
190
|
+
)
|
|
191
|
+
graph_edges: list[InlineEdge] = Field(
|
|
192
|
+
default_factory=list,
|
|
193
|
+
description="Knowledge graph edges for this moment",
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class Engram(BaseModel):
|
|
198
|
+
"""
|
|
199
|
+
Structured memory document for REM.
|
|
200
|
+
|
|
201
|
+
Engrams are Resources with category="engram", optionally containing
|
|
202
|
+
attached Moments. They represent captured experiences, observations,
|
|
203
|
+
or insights with rich contextual metadata.
|
|
204
|
+
|
|
205
|
+
Processing:
|
|
206
|
+
- Upsert as Resource via repository.upsert()
|
|
207
|
+
- Create attached Moments as separate entities
|
|
208
|
+
- Link moments to parent via graph edges (rel_type="part_of")
|
|
209
|
+
|
|
210
|
+
Chunking:
|
|
211
|
+
- Summary chunking (if summary exists)
|
|
212
|
+
- Content chunking (semantic-text-splitter for long content)
|
|
213
|
+
- Moment chunking (each moment is a separate chunk)
|
|
214
|
+
|
|
215
|
+
Embeddings:
|
|
216
|
+
- Generated for resource summary
|
|
217
|
+
- Generated for resource content (chunked if needed)
|
|
218
|
+
- Generated for each attached moment content
|
|
219
|
+
"""
|
|
220
|
+
|
|
221
|
+
kind: str = Field(
|
|
222
|
+
default="engram",
|
|
223
|
+
description="Resource kind (always 'engram')",
|
|
224
|
+
)
|
|
225
|
+
name: str = Field(
|
|
226
|
+
...,
|
|
227
|
+
description="Engram name (human-readable, used as graph label)",
|
|
228
|
+
)
|
|
229
|
+
category: str = Field(
|
|
230
|
+
default="engram",
|
|
231
|
+
description="Engram category (diary, meeting, note, observation, conversation, media)",
|
|
232
|
+
)
|
|
233
|
+
summary: Optional[str] = Field(
|
|
234
|
+
default=None,
|
|
235
|
+
description="Brief summary for semantic search (1-3 sentences)",
|
|
236
|
+
)
|
|
237
|
+
content: str = Field(
|
|
238
|
+
default="",
|
|
239
|
+
description="Full engram content",
|
|
240
|
+
)
|
|
241
|
+
uri: Optional[str] = Field(
|
|
242
|
+
default=None,
|
|
243
|
+
description="Resource URI (s3://, seaweedfs://, etc.)",
|
|
244
|
+
)
|
|
245
|
+
timestamp: datetime = Field(
|
|
246
|
+
default_factory=lambda: datetime.now(timezone.utc).replace(tzinfo=None),
|
|
247
|
+
description="Engram timestamp (content creation time)",
|
|
248
|
+
)
|
|
249
|
+
metadata: dict = Field(
|
|
250
|
+
default_factory=dict,
|
|
251
|
+
description="Engram metadata (device info, etc.)",
|
|
252
|
+
)
|
|
253
|
+
graph_edges: list[InlineEdge] = Field(
|
|
254
|
+
default_factory=list,
|
|
255
|
+
description="Knowledge graph edges",
|
|
256
|
+
)
|
|
257
|
+
moments: list[EngramMoment] = Field(
|
|
258
|
+
default_factory=list,
|
|
259
|
+
description="Attached moments (temporal segments)",
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
@property
|
|
263
|
+
def device(self) -> Optional[DeviceMetadata]:
|
|
264
|
+
"""Get device metadata if present."""
|
|
265
|
+
device_data = self.metadata.get("device")
|
|
266
|
+
if device_data:
|
|
267
|
+
return DeviceMetadata(**device_data)
|
|
268
|
+
return None
|
|
269
|
+
|
|
270
|
+
def add_moment(
|
|
271
|
+
self,
|
|
272
|
+
name: str,
|
|
273
|
+
content: str,
|
|
274
|
+
summary: Optional[str] = None,
|
|
275
|
+
moment_type: Optional[str] = None,
|
|
276
|
+
starts_timestamp: Optional[datetime] = None,
|
|
277
|
+
ends_timestamp: Optional[datetime] = None,
|
|
278
|
+
**kwargs,
|
|
279
|
+
) -> EngramMoment:
|
|
280
|
+
"""
|
|
281
|
+
Add a moment to this engram.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
name: Moment name
|
|
285
|
+
content: Moment content
|
|
286
|
+
summary: Brief summary
|
|
287
|
+
moment_type: Moment type
|
|
288
|
+
starts_timestamp: Start time
|
|
289
|
+
ends_timestamp: End time
|
|
290
|
+
**kwargs: Additional moment fields
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
Created EngramMoment
|
|
294
|
+
"""
|
|
295
|
+
moment = EngramMoment(
|
|
296
|
+
name=name,
|
|
297
|
+
content=content,
|
|
298
|
+
summary=summary,
|
|
299
|
+
moment_type=moment_type,
|
|
300
|
+
starts_timestamp=starts_timestamp,
|
|
301
|
+
ends_timestamp=ends_timestamp,
|
|
302
|
+
**kwargs,
|
|
303
|
+
)
|
|
304
|
+
self.moments.append(moment)
|
|
305
|
+
return moment
|
|
306
|
+
|
|
307
|
+
def add_graph_edge(
|
|
308
|
+
self,
|
|
309
|
+
dst: str,
|
|
310
|
+
rel_type: str,
|
|
311
|
+
weight: float = 0.5,
|
|
312
|
+
properties: Optional[dict] = None,
|
|
313
|
+
) -> InlineEdge:
|
|
314
|
+
"""
|
|
315
|
+
Add a graph edge to this engram.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
dst: Human-friendly destination label
|
|
319
|
+
rel_type: Relationship type
|
|
320
|
+
weight: Edge weight (0.0-1.0)
|
|
321
|
+
properties: Edge properties
|
|
322
|
+
|
|
323
|
+
Returns:
|
|
324
|
+
Created InlineEdge
|
|
325
|
+
"""
|
|
326
|
+
edge = InlineEdge(
|
|
327
|
+
dst=dst,
|
|
328
|
+
rel_type=rel_type,
|
|
329
|
+
weight=weight,
|
|
330
|
+
properties=properties or {},
|
|
331
|
+
)
|
|
332
|
+
self.graph_edges.append(edge)
|
|
333
|
+
return edge
|