strands-dakera 0.1.0__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.
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Strands Dakera — persistent, decay-weighted memory for Strands agents.
|
|
2
|
+
|
|
3
|
+
Backed by a self-hosted `Dakera <https://github.com/dakera-ai/dakera-deploy>`_
|
|
4
|
+
memory server. Exposes the ``dakera_memory`` tool for store / retrieve / get /
|
|
5
|
+
update / delete operations with importance-weighted, decay-aware recall.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from strands_dakera.memory import (
|
|
9
|
+
TOOL_SPEC,
|
|
10
|
+
DakeraServiceClient,
|
|
11
|
+
dakera_memory,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"dakera_memory",
|
|
16
|
+
"DakeraServiceClient",
|
|
17
|
+
"TOOL_SPEC",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
__version__ = "0.1.0"
|
strands_dakera/memory.py
ADDED
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool for managing agent memories using Dakera (store, retrieve, get, update, delete).
|
|
3
|
+
|
|
4
|
+
This module provides persistent, decay-weighted vector memory for Strands agents,
|
|
5
|
+
backed by a self-hosted Dakera memory server. It mirrors the structure of the
|
|
6
|
+
``mem0_memory`` tool so it slots naturally into the Strands tools ecosystem.
|
|
7
|
+
|
|
8
|
+
Dakera is a self-hosted AI agent memory server. Run it locally with the
|
|
9
|
+
``dakera-ai/dakera-deploy`` docker-compose stack (server + MinIO); the REST API
|
|
10
|
+
listens on port 3000 by default. See https://github.com/dakera-ai/dakera-deploy.
|
|
11
|
+
|
|
12
|
+
Key Features:
|
|
13
|
+
------------
|
|
14
|
+
1. Memory Management:
|
|
15
|
+
- store: Add a new memory for an agent, with importance and metadata
|
|
16
|
+
- retrieve: Semantic (decay-weighted) recall across an agent's memories
|
|
17
|
+
- get: Fetch a specific memory by its ID
|
|
18
|
+
- update: Update the content/metadata of an existing memory
|
|
19
|
+
- delete: Remove a memory by its ID
|
|
20
|
+
|
|
21
|
+
2. Safety Features:
|
|
22
|
+
- User confirmation for mutative operations (store, update, delete)
|
|
23
|
+
- Content previews before storage
|
|
24
|
+
- Warning messages before deletion
|
|
25
|
+
- BYPASS_TOOL_CONSENT mode for bypassing confirmations in tests
|
|
26
|
+
|
|
27
|
+
3. Advanced Capabilities:
|
|
28
|
+
- Decay-weighted, access-aware ranking (recall favors important, fresh memories)
|
|
29
|
+
- Importance scoring (0.0-1.0) and typed memories (episodic/semantic/procedural)
|
|
30
|
+
- Structured metadata storage
|
|
31
|
+
- Rich output formatting
|
|
32
|
+
|
|
33
|
+
Configuration (environment variables):
|
|
34
|
+
- DAKERA_BASE_URL: Dakera server URL (default: http://localhost:3000)
|
|
35
|
+
- DAKERA_API_KEY: API key for the Dakera server (dk-...)
|
|
36
|
+
|
|
37
|
+
Usage Examples:
|
|
38
|
+
--------------
|
|
39
|
+
```python
|
|
40
|
+
from strands import Agent
|
|
41
|
+
from strands_dakera import dakera_memory
|
|
42
|
+
|
|
43
|
+
agent = Agent(tools=[dakera_memory])
|
|
44
|
+
|
|
45
|
+
# Store a memory
|
|
46
|
+
agent.tool.dakera_memory(
|
|
47
|
+
action="store",
|
|
48
|
+
agent_id="alex",
|
|
49
|
+
content="Important information to remember",
|
|
50
|
+
importance=0.8,
|
|
51
|
+
metadata={"category": "meeting_notes"},
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Retrieve memories with decay-weighted semantic search
|
|
55
|
+
agent.tool.dakera_memory(
|
|
56
|
+
action="retrieve",
|
|
57
|
+
agent_id="alex",
|
|
58
|
+
query="meeting information",
|
|
59
|
+
top_k=5,
|
|
60
|
+
)
|
|
61
|
+
```
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
import json
|
|
65
|
+
import logging
|
|
66
|
+
import os
|
|
67
|
+
from typing import Any
|
|
68
|
+
|
|
69
|
+
from rich.console import Console
|
|
70
|
+
from rich.panel import Panel
|
|
71
|
+
from rich.table import Table
|
|
72
|
+
from rich.text import Text
|
|
73
|
+
from strands.types.tools import ToolResult, ToolResultContent, ToolUse
|
|
74
|
+
|
|
75
|
+
logger = logging.getLogger(__name__)
|
|
76
|
+
console = Console()
|
|
77
|
+
|
|
78
|
+
TOOL_SPEC = {
|
|
79
|
+
"name": "dakera_memory",
|
|
80
|
+
"description": (
|
|
81
|
+
"Persistent, decay-weighted memory for agents, backed by a self-hosted Dakera server.\n\n"
|
|
82
|
+
"Actions:\n"
|
|
83
|
+
"- store: Store a new memory (requires agent_id and content)\n"
|
|
84
|
+
"- retrieve: Decay-weighted semantic search (requires agent_id and query)\n"
|
|
85
|
+
"- get: Get a memory by ID (requires agent_id and memory_id)\n"
|
|
86
|
+
"- update: Update a memory's content/metadata (requires agent_id and memory_id)\n"
|
|
87
|
+
"- delete: Delete a memory by ID (requires agent_id and memory_id)\n\n"
|
|
88
|
+
"Configure the server via DAKERA_BASE_URL (default http://localhost:3000) and DAKERA_API_KEY."
|
|
89
|
+
),
|
|
90
|
+
"inputSchema": {
|
|
91
|
+
"json": {
|
|
92
|
+
"type": "object",
|
|
93
|
+
"properties": {
|
|
94
|
+
"action": {
|
|
95
|
+
"type": "string",
|
|
96
|
+
"description": "Action to perform (store, retrieve, get, update, delete)",
|
|
97
|
+
"enum": ["store", "retrieve", "get", "update", "delete"],
|
|
98
|
+
},
|
|
99
|
+
"agent_id": {
|
|
100
|
+
"type": "string",
|
|
101
|
+
"description": "Agent identifier that owns the memories (required for all actions)",
|
|
102
|
+
},
|
|
103
|
+
"content": {
|
|
104
|
+
"type": "string",
|
|
105
|
+
"description": "Memory content (required for store; optional for update)",
|
|
106
|
+
},
|
|
107
|
+
"memory_id": {
|
|
108
|
+
"type": "string",
|
|
109
|
+
"description": "Memory ID (required for get, update, delete actions)",
|
|
110
|
+
},
|
|
111
|
+
"query": {
|
|
112
|
+
"type": "string",
|
|
113
|
+
"description": "Search query (required for retrieve action)",
|
|
114
|
+
},
|
|
115
|
+
"top_k": {
|
|
116
|
+
"type": "integer",
|
|
117
|
+
"description": "Number of results to return for retrieve (default: 5)",
|
|
118
|
+
},
|
|
119
|
+
"importance": {
|
|
120
|
+
"type": "number",
|
|
121
|
+
"description": "Importance score 0.0-1.0 for store action",
|
|
122
|
+
},
|
|
123
|
+
"memory_type": {
|
|
124
|
+
"type": "string",
|
|
125
|
+
"description": "Memory type for store/update (episodic, semantic, procedural, working)",
|
|
126
|
+
"enum": ["episodic", "semantic", "procedural", "working"],
|
|
127
|
+
},
|
|
128
|
+
"metadata": {
|
|
129
|
+
"type": "object",
|
|
130
|
+
"description": "Optional metadata to store with the memory",
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
"required": ["action", "agent_id"],
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class DakeraServiceClient:
|
|
140
|
+
"""Thin wrapper around the Dakera Python SDK for the memory tool."""
|
|
141
|
+
|
|
142
|
+
def __init__(self) -> None:
|
|
143
|
+
"""Initialize the Dakera client from environment configuration.
|
|
144
|
+
|
|
145
|
+
Reads DAKERA_BASE_URL (default http://localhost:3000) and DAKERA_API_KEY.
|
|
146
|
+
"""
|
|
147
|
+
try:
|
|
148
|
+
from dakera import DakeraClient
|
|
149
|
+
except ImportError as err:
|
|
150
|
+
raise ImportError(
|
|
151
|
+
"The dakera package is required for the dakera_memory tool. "
|
|
152
|
+
"Install it with: pip install 'strands-dakera'"
|
|
153
|
+
) from err
|
|
154
|
+
|
|
155
|
+
base_url = os.environ.get("DAKERA_BASE_URL", "http://localhost:3000")
|
|
156
|
+
api_key = os.environ.get("DAKERA_API_KEY")
|
|
157
|
+
self.client = DakeraClient(base_url=base_url, api_key=api_key)
|
|
158
|
+
|
|
159
|
+
def store_memory(
|
|
160
|
+
self,
|
|
161
|
+
agent_id: str,
|
|
162
|
+
content: str,
|
|
163
|
+
importance: float | None = None,
|
|
164
|
+
memory_type: str = "episodic",
|
|
165
|
+
metadata: dict[str, Any] | None = None,
|
|
166
|
+
) -> dict[str, Any]:
|
|
167
|
+
"""Store a memory for an agent."""
|
|
168
|
+
result: dict[str, Any] = self.client.store_memory(
|
|
169
|
+
agent_id=agent_id,
|
|
170
|
+
content=content,
|
|
171
|
+
memory_type=memory_type,
|
|
172
|
+
importance=importance,
|
|
173
|
+
metadata=metadata,
|
|
174
|
+
)
|
|
175
|
+
return result
|
|
176
|
+
|
|
177
|
+
def get_memory(self, agent_id: str, memory_id: str) -> dict[str, Any]:
|
|
178
|
+
"""Get a memory by ID."""
|
|
179
|
+
result: dict[str, Any] = self.client.get_memory(agent_id, memory_id)
|
|
180
|
+
return result
|
|
181
|
+
|
|
182
|
+
def update_memory(
|
|
183
|
+
self,
|
|
184
|
+
agent_id: str,
|
|
185
|
+
memory_id: str,
|
|
186
|
+
content: str | None = None,
|
|
187
|
+
metadata: dict[str, Any] | None = None,
|
|
188
|
+
memory_type: str | None = None,
|
|
189
|
+
) -> dict[str, Any]:
|
|
190
|
+
"""Update an existing memory."""
|
|
191
|
+
result: dict[str, Any] = self.client.update_memory(
|
|
192
|
+
agent_id=agent_id,
|
|
193
|
+
memory_id=memory_id,
|
|
194
|
+
content=content,
|
|
195
|
+
metadata=metadata,
|
|
196
|
+
memory_type=memory_type,
|
|
197
|
+
)
|
|
198
|
+
return result
|
|
199
|
+
|
|
200
|
+
def search_memories(self, agent_id: str, query: str, top_k: int = 5) -> list[dict[str, Any]]:
|
|
201
|
+
"""Decay-weighted semantic recall for an agent."""
|
|
202
|
+
# `recall` returns a RecallResponse (with `.memories`); we defensively also
|
|
203
|
+
# accept a raw iterable, so treat the result as untyped for duck-typing.
|
|
204
|
+
response: Any = self.client.recall(agent_id=agent_id, query=query, top_k=top_k)
|
|
205
|
+
memories = getattr(response, "memories", response)
|
|
206
|
+
return [_memory_to_dict(m) for m in memories]
|
|
207
|
+
|
|
208
|
+
def delete_memory(self, agent_id: str, memory_id: str) -> dict[str, Any]:
|
|
209
|
+
"""Delete a memory by ID."""
|
|
210
|
+
result: dict[str, Any] = self.client.forget(agent_id, memory_id)
|
|
211
|
+
return result
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _memory_to_dict(memory: Any) -> dict[str, Any]:
|
|
215
|
+
"""Normalize an SDK memory object (or dict) into a plain dict."""
|
|
216
|
+
if isinstance(memory, dict):
|
|
217
|
+
return memory
|
|
218
|
+
return {
|
|
219
|
+
"id": getattr(memory, "id", None),
|
|
220
|
+
"content": getattr(memory, "content", None),
|
|
221
|
+
"importance": getattr(memory, "importance", None),
|
|
222
|
+
"score": getattr(memory, "score", None),
|
|
223
|
+
"memory_type": getattr(memory, "memory_type", None),
|
|
224
|
+
"metadata": getattr(memory, "metadata", None),
|
|
225
|
+
"created_at": getattr(memory, "created_at", None),
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def format_store_response(memory: dict[str, Any]) -> Panel:
|
|
230
|
+
"""Format a store response."""
|
|
231
|
+
content = [
|
|
232
|
+
"✅ Memory stored successfully:",
|
|
233
|
+
f"🔑 Memory ID: {memory.get('id', 'unknown')}",
|
|
234
|
+
f"📊 Importance: {memory.get('importance', 'default')}",
|
|
235
|
+
]
|
|
236
|
+
text = memory.get("content")
|
|
237
|
+
if text:
|
|
238
|
+
preview = text[:100] + "..." if len(text) > 100 else text
|
|
239
|
+
content.append(f"\n📄 Content: {preview}")
|
|
240
|
+
return Panel("\n".join(content), title="[bold green]Memory Stored", border_style="green")
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def format_get_response(memory: dict[str, Any]) -> Panel:
|
|
244
|
+
"""Format a get/update response."""
|
|
245
|
+
result = [
|
|
246
|
+
"✅ Memory retrieved successfully:",
|
|
247
|
+
f"🔑 Memory ID: {memory.get('id', 'unknown')}",
|
|
248
|
+
f"📊 Importance: {memory.get('importance', 'Unknown')}",
|
|
249
|
+
f"🕒 Created: {memory.get('created_at', 'Unknown')}",
|
|
250
|
+
]
|
|
251
|
+
metadata = memory.get("metadata")
|
|
252
|
+
if metadata:
|
|
253
|
+
result.append(f"📋 Metadata: {json.dumps(metadata, indent=2)}")
|
|
254
|
+
result.append(f"\n📄 Memory: {memory.get('content', 'No content available')}")
|
|
255
|
+
return Panel("\n".join(result), title="[bold green]Memory Retrieved", border_style="green")
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def format_retrieve_response(memories: list[dict[str, Any]]) -> Panel:
|
|
259
|
+
"""Format a retrieve (semantic search) response."""
|
|
260
|
+
if not memories:
|
|
261
|
+
return Panel(
|
|
262
|
+
"No memories found matching the query.",
|
|
263
|
+
title="[bold yellow]No Matches",
|
|
264
|
+
border_style="yellow",
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
table = Table(title="Search Results", show_header=True, header_style="bold magenta")
|
|
268
|
+
table.add_column("ID", style="cyan")
|
|
269
|
+
table.add_column("Memory", style="yellow", width=50)
|
|
270
|
+
table.add_column("Score", style="green")
|
|
271
|
+
table.add_column("Importance", style="blue")
|
|
272
|
+
|
|
273
|
+
for memory in memories:
|
|
274
|
+
content = memory.get("content") or "No content available"
|
|
275
|
+
preview = content[:100] + "..." if len(content) > 100 else content
|
|
276
|
+
table.add_row(
|
|
277
|
+
str(memory.get("id", "unknown")),
|
|
278
|
+
preview,
|
|
279
|
+
str(memory.get("score", "N/A")),
|
|
280
|
+
str(memory.get("importance", "N/A")),
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
return Panel(table, title="[bold green]Search Results", border_style="green")
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def format_delete_response(memory_id: str) -> Panel:
|
|
287
|
+
"""Format a delete response."""
|
|
288
|
+
content = [
|
|
289
|
+
"✅ Memory deleted successfully:",
|
|
290
|
+
f"🔑 Memory ID: {memory_id}",
|
|
291
|
+
]
|
|
292
|
+
return Panel("\n".join(content), title="[bold green]Memory Deleted", border_style="green")
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def dakera_memory(tool: ToolUse, **kwargs: Any) -> ToolResult:
|
|
296
|
+
"""Persistent decay-weighted memory management for agents, backed by Dakera.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
tool: ToolUse object containing the input fields (action, agent_id, content,
|
|
300
|
+
memory_id, query, top_k, importance, memory_type, metadata).
|
|
301
|
+
**kwargs: Additional keyword arguments.
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
ToolResult containing status and response content.
|
|
305
|
+
"""
|
|
306
|
+
tool_use_id = tool.get("toolUseId", "default-id")
|
|
307
|
+
try:
|
|
308
|
+
tool_input = tool.get("input", {})
|
|
309
|
+
|
|
310
|
+
action = tool_input.get("action")
|
|
311
|
+
if not action:
|
|
312
|
+
raise ValueError("action parameter is required")
|
|
313
|
+
|
|
314
|
+
agent_id = tool_input.get("agent_id")
|
|
315
|
+
if not agent_id:
|
|
316
|
+
raise ValueError("agent_id parameter is required")
|
|
317
|
+
|
|
318
|
+
client = DakeraServiceClient()
|
|
319
|
+
bypass_consent = os.environ.get("BYPASS_TOOL_CONSENT", "").lower() == "true"
|
|
320
|
+
|
|
321
|
+
mutative_actions = {"store", "update", "delete"}
|
|
322
|
+
needs_confirmation = action in mutative_actions and not bypass_consent
|
|
323
|
+
|
|
324
|
+
if needs_confirmation:
|
|
325
|
+
if action == "store":
|
|
326
|
+
if not tool_input.get("content"):
|
|
327
|
+
raise ValueError("content is required for store action")
|
|
328
|
+
preview = tool_input["content"][:15000]
|
|
329
|
+
console.print(Panel(preview, title=f"[bold green]Memory for agent {agent_id}", border_style="green"))
|
|
330
|
+
elif action in {"update", "delete"}:
|
|
331
|
+
if not tool_input.get("memory_id"):
|
|
332
|
+
raise ValueError(f"memory_id is required for {action} action")
|
|
333
|
+
console.print(
|
|
334
|
+
Panel(
|
|
335
|
+
f"Memory ID: {tool_input['memory_id']}",
|
|
336
|
+
title=f"[bold red]⚠️ Memory to be {action}d",
|
|
337
|
+
border_style="red",
|
|
338
|
+
)
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
if action == "store":
|
|
342
|
+
if not tool_input.get("content"):
|
|
343
|
+
raise ValueError("content is required for store action")
|
|
344
|
+
memory = client.store_memory(
|
|
345
|
+
agent_id=agent_id,
|
|
346
|
+
content=tool_input["content"],
|
|
347
|
+
importance=tool_input.get("importance"),
|
|
348
|
+
memory_type=tool_input.get("memory_type", "episodic"),
|
|
349
|
+
metadata=tool_input.get("metadata"),
|
|
350
|
+
)
|
|
351
|
+
console.print(format_store_response(memory))
|
|
352
|
+
return ToolResult(
|
|
353
|
+
toolUseId=tool_use_id,
|
|
354
|
+
status="success",
|
|
355
|
+
content=[ToolResultContent(text=json.dumps(memory, indent=2, default=str))],
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
if action == "retrieve":
|
|
359
|
+
if not tool_input.get("query"):
|
|
360
|
+
raise ValueError("query is required for retrieve action")
|
|
361
|
+
memories = client.search_memories(
|
|
362
|
+
agent_id=agent_id,
|
|
363
|
+
query=tool_input["query"],
|
|
364
|
+
top_k=tool_input.get("top_k", 5),
|
|
365
|
+
)
|
|
366
|
+
console.print(format_retrieve_response(memories))
|
|
367
|
+
return ToolResult(
|
|
368
|
+
toolUseId=tool_use_id,
|
|
369
|
+
status="success",
|
|
370
|
+
content=[ToolResultContent(text=json.dumps(memories, indent=2, default=str))],
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
if action == "get":
|
|
374
|
+
if not tool_input.get("memory_id"):
|
|
375
|
+
raise ValueError("memory_id is required for get action")
|
|
376
|
+
memory = client.get_memory(agent_id, tool_input["memory_id"])
|
|
377
|
+
console.print(format_get_response(memory))
|
|
378
|
+
return ToolResult(
|
|
379
|
+
toolUseId=tool_use_id,
|
|
380
|
+
status="success",
|
|
381
|
+
content=[ToolResultContent(text=json.dumps(memory, indent=2, default=str))],
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
if action == "update":
|
|
385
|
+
if not tool_input.get("memory_id"):
|
|
386
|
+
raise ValueError("memory_id is required for update action")
|
|
387
|
+
memory = client.update_memory(
|
|
388
|
+
agent_id=agent_id,
|
|
389
|
+
memory_id=tool_input["memory_id"],
|
|
390
|
+
content=tool_input.get("content"),
|
|
391
|
+
metadata=tool_input.get("metadata"),
|
|
392
|
+
memory_type=tool_input.get("memory_type"),
|
|
393
|
+
)
|
|
394
|
+
console.print(format_get_response(memory))
|
|
395
|
+
return ToolResult(
|
|
396
|
+
toolUseId=tool_use_id,
|
|
397
|
+
status="success",
|
|
398
|
+
content=[ToolResultContent(text=json.dumps(memory, indent=2, default=str))],
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
if action == "delete":
|
|
402
|
+
if not tool_input.get("memory_id"):
|
|
403
|
+
raise ValueError("memory_id is required for delete action")
|
|
404
|
+
client.delete_memory(agent_id, tool_input["memory_id"])
|
|
405
|
+
console.print(format_delete_response(tool_input["memory_id"]))
|
|
406
|
+
return ToolResult(
|
|
407
|
+
toolUseId=tool_use_id,
|
|
408
|
+
status="success",
|
|
409
|
+
content=[ToolResultContent(text=f"Memory {tool_input['memory_id']} deleted successfully")],
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
raise ValueError(f"Invalid action: {action}")
|
|
413
|
+
|
|
414
|
+
except Exception as e:
|
|
415
|
+
console.print(Panel(Text(str(e), style="red"), title="❌ Memory Operation Error", border_style="red"))
|
|
416
|
+
return ToolResult(
|
|
417
|
+
toolUseId=tool_use_id,
|
|
418
|
+
status="error",
|
|
419
|
+
content=[ToolResultContent(text=f"Error: {str(e)}")],
|
|
420
|
+
)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: strands-dakera
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Persistent, decay-weighted memory for Strands agents, backed by a self-hosted Dakera server.
|
|
5
|
+
Project-URL: Homepage, https://dakera.ai
|
|
6
|
+
Project-URL: Documentation, https://github.com/dakera-ai/strands-dakera#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/dakera-ai/strands-dakera
|
|
8
|
+
Project-URL: Issues, https://github.com/dakera-ai/strands-dakera/issues
|
|
9
|
+
Author-email: Dakera <hello@dakera.ai>
|
|
10
|
+
License: Apache-2.0
|
|
11
|
+
Keywords: agents,ai,dakera,decay-weighted,memory,strands,strands-agents,vector-search
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: dakera>=0.12.0
|
|
23
|
+
Requires-Dist: rich>=13.0.0
|
|
24
|
+
Requires-Dist: strands-agents>=1.0.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: hatch; extra == 'dev'
|
|
27
|
+
Requires-Dist: mypy<2.0.0,>=1.15.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-asyncio<1.0.0,>=0.25.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest<9.0.0,>=8.0.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff<1.0.0,>=0.11.0; extra == 'dev'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# strands-dakera (Python)
|
|
34
|
+
|
|
35
|
+
Persistent, decay-weighted memory for [Strands Agents](https://github.com/strands-agents/sdk-python),
|
|
36
|
+
backed by a self-hosted [Dakera](https://github.com/dakera-ai/dakera-deploy) server.
|
|
37
|
+
|
|
38
|
+
See the [repository README](../README.md) for full usage. Quick start:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install strands-dakera
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from strands import Agent
|
|
46
|
+
from strands_dakera import dakera_memory
|
|
47
|
+
|
|
48
|
+
agent = Agent(tools=[dakera_memory])
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Local development
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install hatch
|
|
55
|
+
hatch run test # run the test suite (mocked client, no live server)
|
|
56
|
+
hatch run lint # ruff
|
|
57
|
+
hatch run typecheck # mypy
|
|
58
|
+
hatch run prepare # format + lint + typecheck + test
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Release
|
|
62
|
+
|
|
63
|
+
Tag a GitHub release with a `python-v*` tag (e.g. `python-v0.1.0`); the
|
|
64
|
+
`publish-python` workflow builds the wheel and publishes it to PyPI via trusted
|
|
65
|
+
publishing.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
strands_dakera/__init__.py,sha256=OLL17uvk-fXHbAQacHRzDFiK-1P4yAQdC83daUye_NM,513
|
|
2
|
+
strands_dakera/memory.py,sha256=zv_YrgAXxQfxVZbBa1Q43yCgNEEE6L0J9JbHQaq0vqA,16334
|
|
3
|
+
strands_dakera-0.1.0.dist-info/METADATA,sha256=SKUGZVYdiYFgwgaz715LtB5bW8xjnQ_1A2ORDu9kSQk,2337
|
|
4
|
+
strands_dakera-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
5
|
+
strands_dakera-0.1.0.dist-info/RECORD,,
|