django-agent-runtime 0.3.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.
Files changed (55) hide show
  1. django_agent_runtime/__init__.py +25 -0
  2. django_agent_runtime/admin.py +155 -0
  3. django_agent_runtime/api/__init__.py +26 -0
  4. django_agent_runtime/api/permissions.py +109 -0
  5. django_agent_runtime/api/serializers.py +114 -0
  6. django_agent_runtime/api/views.py +472 -0
  7. django_agent_runtime/apps.py +26 -0
  8. django_agent_runtime/conf.py +241 -0
  9. django_agent_runtime/examples/__init__.py +10 -0
  10. django_agent_runtime/examples/langgraph_adapter.py +164 -0
  11. django_agent_runtime/examples/langgraph_tools.py +179 -0
  12. django_agent_runtime/examples/simple_chat.py +69 -0
  13. django_agent_runtime/examples/tool_agent.py +157 -0
  14. django_agent_runtime/management/__init__.py +2 -0
  15. django_agent_runtime/management/commands/__init__.py +2 -0
  16. django_agent_runtime/management/commands/runagent.py +419 -0
  17. django_agent_runtime/migrations/0001_initial.py +117 -0
  18. django_agent_runtime/migrations/0002_persistence_models.py +129 -0
  19. django_agent_runtime/migrations/0003_persistenceconversation_active_branch_id_and_more.py +212 -0
  20. django_agent_runtime/migrations/0004_add_anonymous_session_id.py +18 -0
  21. django_agent_runtime/migrations/__init__.py +2 -0
  22. django_agent_runtime/models/__init__.py +54 -0
  23. django_agent_runtime/models/base.py +450 -0
  24. django_agent_runtime/models/concrete.py +146 -0
  25. django_agent_runtime/persistence/__init__.py +60 -0
  26. django_agent_runtime/persistence/helpers.py +148 -0
  27. django_agent_runtime/persistence/models.py +506 -0
  28. django_agent_runtime/persistence/stores.py +1191 -0
  29. django_agent_runtime/runtime/__init__.py +23 -0
  30. django_agent_runtime/runtime/events/__init__.py +65 -0
  31. django_agent_runtime/runtime/events/base.py +135 -0
  32. django_agent_runtime/runtime/events/db.py +129 -0
  33. django_agent_runtime/runtime/events/redis.py +228 -0
  34. django_agent_runtime/runtime/events/sync.py +140 -0
  35. django_agent_runtime/runtime/interfaces.py +475 -0
  36. django_agent_runtime/runtime/llm/__init__.py +91 -0
  37. django_agent_runtime/runtime/llm/anthropic.py +249 -0
  38. django_agent_runtime/runtime/llm/litellm_adapter.py +173 -0
  39. django_agent_runtime/runtime/llm/openai.py +230 -0
  40. django_agent_runtime/runtime/queue/__init__.py +75 -0
  41. django_agent_runtime/runtime/queue/base.py +158 -0
  42. django_agent_runtime/runtime/queue/postgres.py +248 -0
  43. django_agent_runtime/runtime/queue/redis_streams.py +336 -0
  44. django_agent_runtime/runtime/queue/sync.py +277 -0
  45. django_agent_runtime/runtime/registry.py +186 -0
  46. django_agent_runtime/runtime/runner.py +540 -0
  47. django_agent_runtime/runtime/tracing/__init__.py +48 -0
  48. django_agent_runtime/runtime/tracing/langfuse.py +117 -0
  49. django_agent_runtime/runtime/tracing/noop.py +36 -0
  50. django_agent_runtime/urls.py +39 -0
  51. django_agent_runtime-0.3.6.dist-info/METADATA +723 -0
  52. django_agent_runtime-0.3.6.dist-info/RECORD +55 -0
  53. django_agent_runtime-0.3.6.dist-info/WHEEL +5 -0
  54. django_agent_runtime-0.3.6.dist-info/licenses/LICENSE +22 -0
  55. django_agent_runtime-0.3.6.dist-info/top_level.txt +1 -0
@@ -0,0 +1,450 @@
1
+ """
2
+ Abstract base models for the Agent Runtime.
3
+
4
+ These can be extended by host projects for customization.
5
+ Use Pattern A (concrete models) by default, Pattern B (swappable) for advanced use.
6
+ """
7
+
8
+ import uuid
9
+ from django.db import models
10
+ from django.conf import settings
11
+
12
+
13
+ class RunStatus(models.TextChoices):
14
+ """Status choices for agent runs."""
15
+
16
+ QUEUED = "queued", "Queued"
17
+ RUNNING = "running", "Running"
18
+ SUCCEEDED = "succeeded", "Succeeded"
19
+ FAILED = "failed", "Failed"
20
+ CANCELLED = "cancelled", "Cancelled"
21
+ TIMED_OUT = "timed_out", "Timed Out"
22
+
23
+
24
+ class AbstractAgentConversation(models.Model):
25
+ """
26
+ Abstract model for grouping related agent runs.
27
+
28
+ A conversation represents a multi-turn interaction with an agent.
29
+ Supports both authenticated users and anonymous sessions.
30
+
31
+ Anonymous Session Support:
32
+ The abstract model stores anonymous_session_id as a UUID field.
33
+ This allows the runtime to work without requiring a specific session model.
34
+
35
+ To enable anonymous sessions:
36
+ 1. Set ANONYMOUS_SESSION_MODEL in DJANGO_AGENT_RUNTIME settings
37
+ 2. The model must have a 'token' field and optionally 'is_expired' property
38
+
39
+ For a proper FK relationship, create a custom conversation model::
40
+
41
+ class MyAgentConversation(AbstractAgentConversation):
42
+ anonymous_session = models.ForeignKey(
43
+ "myapp.AnonymousSession",
44
+ on_delete=models.SET_NULL,
45
+ null=True, blank=True,
46
+ )
47
+ """
48
+
49
+ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
50
+
51
+ # Optional user association (nullable for system-initiated conversations)
52
+ user = models.ForeignKey(
53
+ settings.AUTH_USER_MODEL,
54
+ on_delete=models.SET_NULL,
55
+ null=True,
56
+ blank=True,
57
+ related_name="agent_conversations",
58
+ )
59
+
60
+ # Optional anonymous session association (stores session ID as UUID)
61
+ # This allows anonymous sessions without requiring a specific model FK
62
+ anonymous_session_id = models.UUIDField(
63
+ null=True,
64
+ blank=True,
65
+ db_index=True,
66
+ help_text="UUID of the anonymous session (if using anonymous sessions)",
67
+ )
68
+
69
+ # Agent identification
70
+ agent_key = models.CharField(
71
+ max_length=100,
72
+ db_index=True,
73
+ help_text="Identifier for the agent runtime to use",
74
+ )
75
+
76
+ # Conversation state
77
+ title = models.CharField(max_length=255, blank=True)
78
+ metadata = models.JSONField(default=dict, blank=True)
79
+
80
+ # Timestamps
81
+ created_at = models.DateTimeField(auto_now_add=True, db_index=True)
82
+ updated_at = models.DateTimeField(auto_now=True)
83
+
84
+ class Meta:
85
+ abstract = True
86
+ ordering = ["-created_at"]
87
+ verbose_name = "Agent Conversation"
88
+ verbose_name_plural = "Agent Conversations"
89
+
90
+ def __str__(self):
91
+ return f"{self.agent_key} - {self.id}"
92
+
93
+ @property
94
+ def owner(self):
95
+ """Return the owner (User or AnonymousSession) of this conversation."""
96
+ if self.user:
97
+ return self.user
98
+ # Try to get anonymous_session FK if it exists (custom model)
99
+ if hasattr(self, 'anonymous_session') and self.anonymous_session:
100
+ return self.anonymous_session
101
+ # Fall back to resolving from anonymous_session_id
102
+ return self.get_anonymous_session()
103
+
104
+ def get_anonymous_session(self):
105
+ """
106
+ Get the anonymous session object if configured and available.
107
+
108
+ Returns the session object or None if:
109
+ - No anonymous_session_id is set
110
+ - ANONYMOUS_SESSION_MODEL is not configured
111
+ - Session doesn't exist or is expired
112
+ """
113
+ if not self.anonymous_session_id:
114
+ return None
115
+
116
+ # Check if we have a direct FK (custom model)
117
+ if hasattr(self, 'anonymous_session'):
118
+ return self.anonymous_session
119
+
120
+ # Resolve from configured model
121
+ from django_agent_runtime.conf import runtime_settings
122
+
123
+ settings_obj = runtime_settings()
124
+ model_path = settings_obj.ANONYMOUS_SESSION_MODEL
125
+
126
+ if not model_path:
127
+ return None
128
+
129
+ try:
130
+ from django.apps import apps
131
+ app_label, model_name = model_path.rsplit('.', 1)
132
+ AnonymousSession = apps.get_model(app_label, model_name)
133
+ session = AnonymousSession.objects.get(id=self.anonymous_session_id)
134
+
135
+ # Check if expired
136
+ if hasattr(session, 'is_expired') and session.is_expired:
137
+ return None
138
+
139
+ return session
140
+ except Exception:
141
+ return None
142
+
143
+ def get_message_history(self, include_failed_runs: bool = False) -> list[dict]:
144
+ """
145
+ Get the full message history across all runs in this conversation.
146
+
147
+ Returns messages in chronological order, including:
148
+ - Input messages from each run
149
+ - Assistant responses (including tool calls)
150
+ - Tool results
151
+
152
+ Args:
153
+ include_failed_runs: If True, include messages from failed runs.
154
+ Default is False (only successful runs).
155
+
156
+ Returns:
157
+ List of Message dicts in the framework-neutral format:
158
+ [
159
+ {"role": "user", "content": "..."},
160
+ {"role": "assistant", "content": "...", "tool_calls": [...]},
161
+ {"role": "tool", "content": "...", "tool_call_id": "..."},
162
+ ...
163
+ ]
164
+ """
165
+ from django_agent_runtime.models.base import RunStatus
166
+
167
+ # Get runs in chronological order
168
+ runs_qs = self.runs.order_by("created_at")
169
+
170
+ if not include_failed_runs:
171
+ runs_qs = runs_qs.filter(status=RunStatus.SUCCEEDED)
172
+
173
+ messages = []
174
+ seen_message_hashes = set() # Avoid duplicates from overlapping input
175
+
176
+ for run in runs_qs:
177
+ # Get input messages (user messages that started this run)
178
+ input_data = run.input or {}
179
+ input_messages = input_data.get("messages", [])
180
+
181
+ # Add input messages (avoiding duplicates)
182
+ for msg in input_messages:
183
+ # Create a hash to detect duplicates
184
+ msg_hash = _message_hash(msg)
185
+ if msg_hash not in seen_message_hashes:
186
+ messages.append(_normalize_message(msg))
187
+ seen_message_hashes.add(msg_hash)
188
+
189
+ # Get output messages (assistant responses, tool calls, etc.)
190
+ output_data = run.output or {}
191
+ output_messages = output_data.get("final_messages", [])
192
+
193
+ for msg in output_messages:
194
+ msg_hash = _message_hash(msg)
195
+ if msg_hash not in seen_message_hashes:
196
+ messages.append(_normalize_message(msg))
197
+ seen_message_hashes.add(msg_hash)
198
+
199
+ return messages
200
+
201
+ def get_last_assistant_message(self) -> dict | None:
202
+ """
203
+ Get the most recent assistant message from the conversation.
204
+
205
+ Returns:
206
+ The last assistant message dict, or None if no assistant messages exist.
207
+ """
208
+ messages = self.get_message_history()
209
+ for msg in reversed(messages):
210
+ if msg.get("role") == "assistant":
211
+ return msg
212
+ return None
213
+
214
+
215
+ def _message_hash(msg: dict) -> str:
216
+ """Create a hash for deduplication of messages."""
217
+ import hashlib
218
+ import json
219
+
220
+ # Use role + content + tool_call_id for uniqueness
221
+ key_parts = [
222
+ msg.get("role", ""),
223
+ str(msg.get("content", "")),
224
+ msg.get("tool_call_id", ""),
225
+ ]
226
+ # Include tool_calls if present
227
+ if msg.get("tool_calls"):
228
+ key_parts.append(json.dumps(msg["tool_calls"], sort_keys=True))
229
+
230
+ key = "|".join(key_parts)
231
+ return hashlib.md5(key.encode()).hexdigest()
232
+
233
+
234
+ def _normalize_message(msg: dict) -> dict:
235
+ """
236
+ Normalize a message to the framework-neutral Message format.
237
+
238
+ Ensures consistent structure regardless of how it was stored.
239
+ """
240
+ normalized = {
241
+ "role": msg.get("role", "user"),
242
+ }
243
+
244
+ # Handle content (can be string, dict, or list)
245
+ content = msg.get("content")
246
+ if content is not None:
247
+ normalized["content"] = content
248
+
249
+ # Optional fields - only include if present
250
+ if msg.get("name"):
251
+ normalized["name"] = msg["name"]
252
+
253
+ if msg.get("tool_call_id"):
254
+ normalized["tool_call_id"] = msg["tool_call_id"]
255
+
256
+ if msg.get("tool_calls"):
257
+ normalized["tool_calls"] = msg["tool_calls"]
258
+
259
+ if msg.get("metadata"):
260
+ normalized["metadata"] = msg["metadata"]
261
+
262
+ return normalized
263
+
264
+
265
+ class AbstractAgentRun(models.Model):
266
+ """
267
+ Abstract model for a single agent execution.
268
+
269
+ This is the core model - tracks status, input/output, retries, and leasing.
270
+ """
271
+
272
+ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
273
+
274
+ # Relationship to conversation (optional)
275
+ # Note: concrete model defines the FK to avoid circular imports
276
+ # conversation = models.ForeignKey(...)
277
+
278
+ # Agent identification
279
+ agent_key = models.CharField(
280
+ max_length=100,
281
+ db_index=True,
282
+ help_text="Identifier for the agent runtime to use",
283
+ )
284
+
285
+ # Status tracking
286
+ status = models.CharField(
287
+ max_length=20,
288
+ choices=RunStatus.choices,
289
+ default=RunStatus.QUEUED,
290
+ db_index=True,
291
+ )
292
+
293
+ # Input/Output (the canonical schema)
294
+ input = models.JSONField(
295
+ default=dict,
296
+ help_text='{"messages": [...], "params": {...}}',
297
+ )
298
+ output = models.JSONField(
299
+ default=dict,
300
+ blank=True,
301
+ help_text="Final output from the agent",
302
+ )
303
+
304
+ # Error tracking
305
+ error = models.JSONField(
306
+ default=dict,
307
+ blank=True,
308
+ help_text='{"type": "", "message": "", "stack": "", "retriable": true}',
309
+ )
310
+
311
+ # Retry configuration
312
+ attempt = models.PositiveIntegerField(default=1)
313
+ max_attempts = models.PositiveIntegerField(default=3)
314
+
315
+ # Lease management (for distributed workers)
316
+ lease_owner = models.CharField(
317
+ max_length=100,
318
+ blank=True,
319
+ db_index=True,
320
+ help_text="Worker ID that owns this run",
321
+ )
322
+ lease_expires_at = models.DateTimeField(
323
+ null=True,
324
+ blank=True,
325
+ db_index=True,
326
+ help_text="When the lease expires",
327
+ )
328
+
329
+ # Idempotency
330
+ idempotency_key = models.CharField(
331
+ max_length=255,
332
+ null=True,
333
+ blank=True,
334
+ unique=True,
335
+ help_text="Client-provided key for idempotent requests",
336
+ )
337
+
338
+ # Cancellation
339
+ cancel_requested_at = models.DateTimeField(
340
+ null=True,
341
+ blank=True,
342
+ help_text="When cancellation was requested",
343
+ )
344
+
345
+ # Timestamps
346
+ created_at = models.DateTimeField(auto_now_add=True, db_index=True)
347
+ started_at = models.DateTimeField(null=True, blank=True)
348
+ finished_at = models.DateTimeField(null=True, blank=True)
349
+
350
+ # Extensibility
351
+ metadata = models.JSONField(default=dict, blank=True)
352
+
353
+ class Meta:
354
+ abstract = True
355
+ ordering = ["-created_at"]
356
+ verbose_name = "Agent Run"
357
+ verbose_name_plural = "Agent Runs"
358
+ indexes = [
359
+ models.Index(fields=["status", "lease_expires_at"]),
360
+ models.Index(fields=["agent_key", "status"]),
361
+ ]
362
+
363
+ def __str__(self):
364
+ return f"{self.agent_key} - {self.status} - {self.id}"
365
+
366
+ @property
367
+ def is_terminal(self) -> bool:
368
+ """Check if the run is in a terminal state."""
369
+ return self.status in {
370
+ RunStatus.SUCCEEDED,
371
+ RunStatus.FAILED,
372
+ RunStatus.CANCELLED,
373
+ RunStatus.TIMED_OUT,
374
+ }
375
+
376
+
377
+ class AbstractAgentEvent(models.Model):
378
+ """
379
+ Abstract model for agent events (append-only log).
380
+
381
+ Events are the communication channel between workers and UI.
382
+ Strictly increasing seq per run, exactly one terminal event.
383
+ """
384
+
385
+ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
386
+
387
+ # Relationship to run (concrete model defines FK)
388
+ # run = models.ForeignKey(...)
389
+
390
+ # Event ordering
391
+ seq = models.PositiveIntegerField(
392
+ db_index=True,
393
+ help_text="Strictly increasing sequence number per run",
394
+ )
395
+
396
+ # Event data
397
+ event_type = models.CharField(
398
+ max_length=50,
399
+ db_index=True,
400
+ help_text="Event type (e.g., run.started, assistant.message)",
401
+ )
402
+ payload = models.JSONField(default=dict)
403
+
404
+ # Timestamp
405
+ timestamp = models.DateTimeField(auto_now_add=True, db_index=True)
406
+
407
+ class Meta:
408
+ abstract = True
409
+ ordering = ["seq"]
410
+ verbose_name = "Agent Event"
411
+ verbose_name_plural = "Agent Events"
412
+
413
+ def __str__(self):
414
+ return f"{self.event_type} (seq={self.seq})"
415
+
416
+
417
+ class AbstractAgentCheckpoint(models.Model):
418
+ """
419
+ Abstract model for state checkpoints.
420
+
421
+ Checkpoints allow recovery from failures mid-run.
422
+ """
423
+
424
+ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
425
+
426
+ # Relationship to run (concrete model defines FK)
427
+ # run = models.ForeignKey(...)
428
+
429
+ # Checkpoint data
430
+ state = models.JSONField(
431
+ help_text="Serialized agent state for recovery",
432
+ )
433
+
434
+ # Ordering
435
+ seq = models.PositiveIntegerField(
436
+ help_text="Checkpoint sequence number",
437
+ )
438
+
439
+ # Timestamp
440
+ created_at = models.DateTimeField(auto_now_add=True)
441
+
442
+ class Meta:
443
+ abstract = True
444
+ ordering = ["-seq"]
445
+ verbose_name = "Agent Checkpoint"
446
+ verbose_name_plural = "Agent Checkpoints"
447
+
448
+ def __str__(self):
449
+ return f"Checkpoint {self.seq}"
450
+
@@ -0,0 +1,146 @@
1
+ """
2
+ Concrete model implementations for the Agent Runtime.
3
+
4
+ These are the default models used when no custom models are configured.
5
+ Host projects can use these directly or create their own by extending the abstract models.
6
+ """
7
+
8
+ from django.db import models
9
+
10
+ from django_agent_runtime.models.base import (
11
+ AbstractAgentConversation,
12
+ AbstractAgentRun,
13
+ AbstractAgentEvent,
14
+ AbstractAgentCheckpoint,
15
+ )
16
+
17
+
18
+ class AgentConversation(AbstractAgentConversation):
19
+ """
20
+ Default concrete implementation of AgentConversation.
21
+
22
+ Groups related agent runs into a conversation.
23
+
24
+ Anonymous Session Support:
25
+ This model uses the anonymous_session_id UUID field from the abstract model.
26
+ When ANONYMOUS_SESSION_MODEL is configured, the get_anonymous_session() method
27
+ will resolve the session object.
28
+
29
+ The anonymous_session property provides convenient access to the session object.
30
+
31
+ For a proper FK relationship with database-level integrity, create your own model::
32
+
33
+ from django.db import models
34
+ from django_agent_runtime.models.base import AbstractAgentConversation
35
+
36
+ class MyAgentConversation(AbstractAgentConversation):
37
+ anonymous_session = models.ForeignKey(
38
+ "myapp.MySession",
39
+ on_delete=models.SET_NULL,
40
+ null=True,
41
+ blank=True,
42
+ related_name="agent_conversations",
43
+ )
44
+
45
+ class Meta(AbstractAgentConversation.Meta):
46
+ abstract = False
47
+
48
+ Then configure in settings::
49
+
50
+ DJANGO_AGENT_RUNTIME = {
51
+ 'CONVERSATION_MODEL': 'myapp.MyAgentConversation',
52
+ }
53
+ """
54
+
55
+ class Meta(AbstractAgentConversation.Meta):
56
+ abstract = False
57
+ db_table = "agent_runtime_conversation"
58
+
59
+ @property
60
+ def anonymous_session(self):
61
+ """
62
+ Get the anonymous session object.
63
+
64
+ This property resolves the session from anonymous_session_id using
65
+ the configured ANONYMOUS_SESSION_MODEL.
66
+
67
+ Returns None if:
68
+ - No anonymous_session_id is set
69
+ - ANONYMOUS_SESSION_MODEL is not configured
70
+ - Session doesn't exist or is expired
71
+ """
72
+ return self.get_anonymous_session()
73
+
74
+ @anonymous_session.setter
75
+ def anonymous_session(self, session):
76
+ """
77
+ Set the anonymous session.
78
+
79
+ Accepts either a session object (with an 'id' attribute) or a UUID.
80
+ """
81
+ if session is None:
82
+ self.anonymous_session_id = None
83
+ elif hasattr(session, 'id'):
84
+ self.anonymous_session_id = session.id
85
+ else:
86
+ # Assume it's a UUID
87
+ self.anonymous_session_id = session
88
+
89
+
90
+ class AgentRun(AbstractAgentRun):
91
+ """
92
+ Default concrete implementation of AgentRun.
93
+
94
+ Tracks individual agent executions with full lifecycle management.
95
+ """
96
+
97
+ conversation = models.ForeignKey(
98
+ AgentConversation,
99
+ on_delete=models.CASCADE,
100
+ null=True,
101
+ blank=True,
102
+ related_name="runs",
103
+ )
104
+
105
+ class Meta(AbstractAgentRun.Meta):
106
+ abstract = False
107
+ db_table = "agent_runtime_run"
108
+
109
+
110
+ class AgentEvent(AbstractAgentEvent):
111
+ """
112
+ Default concrete implementation of AgentEvent.
113
+
114
+ Append-only event log for streaming to UI.
115
+ """
116
+
117
+ run = models.ForeignKey(
118
+ AgentRun,
119
+ on_delete=models.CASCADE,
120
+ related_name="events",
121
+ )
122
+
123
+ class Meta(AbstractAgentEvent.Meta):
124
+ abstract = False
125
+ db_table = "agent_runtime_event"
126
+ unique_together = [("run", "seq")]
127
+
128
+
129
+ class AgentCheckpoint(AbstractAgentCheckpoint):
130
+ """
131
+ Default concrete implementation of AgentCheckpoint.
132
+
133
+ State snapshots for recovery from failures.
134
+ """
135
+
136
+ run = models.ForeignKey(
137
+ AgentRun,
138
+ on_delete=models.CASCADE,
139
+ related_name="checkpoints",
140
+ )
141
+
142
+ class Meta(AbstractAgentCheckpoint.Meta):
143
+ abstract = False
144
+ db_table = "agent_runtime_checkpoint"
145
+ unique_together = [("run", "seq")]
146
+
@@ -0,0 +1,60 @@
1
+ """
2
+ Django persistence layer for agent-runtime-core.
3
+
4
+ This module provides Django-backed implementations of the persistence stores
5
+ defined in agent_runtime_core.persistence.
6
+
7
+ Usage:
8
+ from django_agent_runtime.persistence import (
9
+ DjangoMemoryStore,
10
+ DjangoConversationStore,
11
+ DjangoTaskStore,
12
+ DjangoPreferencesStore,
13
+ DjangoKnowledgeStore,
14
+ DjangoAuditStore,
15
+ get_persistence_manager,
16
+ )
17
+
18
+ # In a view or middleware
19
+ manager = get_persistence_manager(request.user)
20
+
21
+ # Or configure manually
22
+ from agent_runtime_core.persistence import PersistenceConfig, PersistenceManager
23
+
24
+ config = PersistenceConfig(
25
+ memory_store=DjangoMemoryStore(user=request.user),
26
+ conversation_store=DjangoConversationStore(user=request.user),
27
+ task_store=DjangoTaskStore(user=request.user),
28
+ preferences_store=DjangoPreferencesStore(user=request.user),
29
+ knowledge_store=DjangoKnowledgeStore(user=request.user),
30
+ audit_store=DjangoAuditStore(user=request.user),
31
+ )
32
+ manager = PersistenceManager(config)
33
+ """
34
+
35
+ from django_agent_runtime.persistence.stores import (
36
+ DjangoMemoryStore,
37
+ DjangoConversationStore,
38
+ DjangoTaskStore,
39
+ DjangoPreferencesStore,
40
+ DjangoKnowledgeStore,
41
+ DjangoAuditStore,
42
+ )
43
+ from django_agent_runtime.persistence.helpers import (
44
+ get_persistence_manager,
45
+ get_persistence_config,
46
+ )
47
+
48
+ __all__ = [
49
+ # Store implementations
50
+ "DjangoMemoryStore",
51
+ "DjangoConversationStore",
52
+ "DjangoTaskStore",
53
+ "DjangoPreferencesStore",
54
+ "DjangoKnowledgeStore",
55
+ "DjangoAuditStore",
56
+ # Helpers
57
+ "get_persistence_manager",
58
+ "get_persistence_config",
59
+ ]
60
+