anona 0.6.0__tar.gz
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.
- anona-0.6.0/LICENSE +21 -0
- anona-0.6.0/PKG-INFO +285 -0
- anona-0.6.0/README.md +239 -0
- anona-0.6.0/anona/__init__.py +4 -0
- anona-0.6.0/anona/client.py +743 -0
- anona-0.6.0/anona/integrations/__init__.py +16 -0
- anona-0.6.0/anona/integrations/_core.py +309 -0
- anona-0.6.0/anona/integrations/crewai.py +236 -0
- anona-0.6.0/anona/integrations/google_adk.py +309 -0
- anona-0.6.0/anona/integrations/langchain.py +229 -0
- anona-0.6.0/anona/integrations/litellm.py +155 -0
- anona-0.6.0/anona/integrations/llamaindex.py +252 -0
- anona-0.6.0/anona/integrations/mcp.py +167 -0
- anona-0.6.0/anona/integrations/ms_agent.py +234 -0
- anona-0.6.0/anona/integrations/strands.py +170 -0
- anona-0.6.0/anona.egg-info/PKG-INFO +285 -0
- anona-0.6.0/anona.egg-info/SOURCES.txt +34 -0
- anona-0.6.0/anona.egg-info/dependency_links.txt +1 -0
- anona-0.6.0/anona.egg-info/entry_points.txt +2 -0
- anona-0.6.0/anona.egg-info/requires.txt +33 -0
- anona-0.6.0/anona.egg-info/top_level.txt +1 -0
- anona-0.6.0/pyproject.toml +128 -0
- anona-0.6.0/setup.cfg +4 -0
- anona-0.6.0/tests/test_adapter_crewai.py +479 -0
- anona-0.6.0/tests/test_adapter_google_adk.py +641 -0
- anona-0.6.0/tests/test_adapter_langchain.py +567 -0
- anona-0.6.0/tests/test_adapter_llamaindex.py +429 -0
- anona-0.6.0/tests/test_adapter_ms_agent.py +376 -0
- anona-0.6.0/tests/test_adapter_strands.py +504 -0
- anona-0.6.0/tests/test_async_context.py +102 -0
- anona-0.6.0/tests/test_client.py +272 -0
- anona-0.6.0/tests/test_client_lifecycle.py +265 -0
- anona-0.6.0/tests/test_examples.py +243 -0
- anona-0.6.0/tests/test_mcp.py +193 -0
- anona-0.6.0/tests/test_memory_bridge.py +471 -0
- anona-0.6.0/tests/test_path_encoding.py +106 -0
anona-0.6.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Anona Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
anona-0.6.0/PKG-INFO
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: anona
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: Anona Memory SDK — managed AI memory for intelligent agents
|
|
5
|
+
Author: Anona Labs
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/anonalabs/Anona-Memory-SDK
|
|
8
|
+
Project-URL: Repository, https://github.com/anonalabs/Anona-Memory-SDK
|
|
9
|
+
Keywords: ai,memory,agents,llm,rag,mcp
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: httpx>=0.24
|
|
22
|
+
Provides-Extra: litellm
|
|
23
|
+
Requires-Dist: litellm>=1.0; extra == "litellm"
|
|
24
|
+
Provides-Extra: mcp
|
|
25
|
+
Requires-Dist: mcp<2,>=1.2; extra == "mcp"
|
|
26
|
+
Provides-Extra: langchain
|
|
27
|
+
Requires-Dist: langchain>=1.3; extra == "langchain"
|
|
28
|
+
Requires-Dist: langchain-core>=1.4; extra == "langchain"
|
|
29
|
+
Provides-Extra: crewai
|
|
30
|
+
Requires-Dist: crewai>=1.0; extra == "crewai"
|
|
31
|
+
Provides-Extra: llamaindex
|
|
32
|
+
Requires-Dist: llama-index-core>=0.14; extra == "llamaindex"
|
|
33
|
+
Provides-Extra: adk
|
|
34
|
+
Requires-Dist: google-adk>=1.21; extra == "adk"
|
|
35
|
+
Provides-Extra: msagent
|
|
36
|
+
Requires-Dist: agent-framework-core>=1.0; extra == "msagent"
|
|
37
|
+
Provides-Extra: strands
|
|
38
|
+
Requires-Dist: strands-agents>=1.0; extra == "strands"
|
|
39
|
+
Provides-Extra: dev
|
|
40
|
+
Requires-Dist: pytest; extra == "dev"
|
|
41
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
42
|
+
Requires-Dist: respx; extra == "dev"
|
|
43
|
+
Requires-Dist: anyio; extra == "dev"
|
|
44
|
+
Requires-Dist: anona[mcp]; extra == "dev"
|
|
45
|
+
Dynamic: license-file
|
|
46
|
+
|
|
47
|
+
# Anona Memory SDK
|
|
48
|
+
|
|
49
|
+
Official SDKs for [Anona Memory](https://memory.anonalabs.com) — managed AI memory for intelligent agents. Record, retrieve, and reason over memories per user/space via a simple client, or auto-inject memory into LiteLLM calls with one line.
|
|
50
|
+
|
|
51
|
+
- **Python** — this repository root. Documented below.
|
|
52
|
+
- **TypeScript** — [`typescript/`](typescript/). Zero dependencies, runs on Node 18+, Bun, Deno, Cloudflare Workers and the browser, with adapters for the Vercel AI SDK and the OpenAI Agents SDK. See [`typescript/README.md`](typescript/README.md).
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { Anona } from "@anona-labs/memory";
|
|
56
|
+
|
|
57
|
+
const anona = new Anona({ apiKey: process.env.ANONA_API_KEY! });
|
|
58
|
+
await anona.record({ spaceId: "support", content: "Alice prefers email" });
|
|
59
|
+
const hits = await anona.retrieve({ spaceId: "support", query: "how to contact Alice" });
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npm install @anona-labs/memory
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Install
|
|
67
|
+
|
|
68
|
+
Install directly from GitHub:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install git+https://github.com/anonalabs/Anona-Memory-SDK.git
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
With the LiteLLM integration (or `mcp` for the MCP server):
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip install "anona[litellm] @ git+https://github.com/anonalabs/Anona-Memory-SDK.git"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
> The package is not yet on PyPI, so install from the Git URL above. Once it's
|
|
81
|
+
> published, `pip install anona` will also work.
|
|
82
|
+
|
|
83
|
+
## Quickstart
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from anona import AnonaClient
|
|
87
|
+
|
|
88
|
+
# base_url defaults to https://api.anonalabs.com — pass it only to override.
|
|
89
|
+
client = AnonaClient(api_key="anona_live_...")
|
|
90
|
+
|
|
91
|
+
# Record a memory
|
|
92
|
+
client.record(space_id="space_123", content="User prefers dark mode.")
|
|
93
|
+
|
|
94
|
+
# Retrieve memories
|
|
95
|
+
results = client.retrieve(space_id="space_123", query="UI preferences", limit=5)
|
|
96
|
+
for r in results:
|
|
97
|
+
print(r["relevance_score"], r["content"])
|
|
98
|
+
|
|
99
|
+
# Reason: a synthesized insight across memories
|
|
100
|
+
summary = client.reason(space_id="space_123", query="What do we know about this user?")
|
|
101
|
+
print(summary)
|
|
102
|
+
|
|
103
|
+
client.close()
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Async ingestion (don't block on a write)
|
|
107
|
+
|
|
108
|
+
Recording runs fact extraction, so a normal `record()` takes a moment. In a
|
|
109
|
+
chat loop or any latency-sensitive path, queue the write with `background=True`
|
|
110
|
+
and poll the returned job instead:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
import time
|
|
114
|
+
|
|
115
|
+
job = client.record(
|
|
116
|
+
space_id="space_123",
|
|
117
|
+
content="User prefers dark mode.",
|
|
118
|
+
background=True, # returns a job_id, doesn't wait
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
while True:
|
|
122
|
+
status = client.get_job(space_id="space_123", job_id=job["job_id"])
|
|
123
|
+
if status["status"] in ("completed", "failed", "cancelled", "not_found"):
|
|
124
|
+
break
|
|
125
|
+
time.sleep(2)
|
|
126
|
+
|
|
127
|
+
# Backfill many memories at once (always queued, up to 100 per call):
|
|
128
|
+
batch = client.record_batch(
|
|
129
|
+
space_id="space_123",
|
|
130
|
+
items=[
|
|
131
|
+
{"content": "User is on the Pro plan."},
|
|
132
|
+
{"content": "Signed up in 2024.", "timestamp": "2024-03-01T00:00:00Z"},
|
|
133
|
+
],
|
|
134
|
+
)
|
|
135
|
+
print(batch["accepted"], "queued as job", batch["job_id"])
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Async variants (`async_record`, `async_retrieve`, `async_reason`) are available on the same client, or use it as a context manager:
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
async with AnonaClient(api_key="...") as client:
|
|
142
|
+
await client.async_record(space_id="space_123", content="...")
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## API
|
|
146
|
+
|
|
147
|
+
### `AnonaClient(api_key, base_url="https://api.anonalabs.com")`
|
|
148
|
+
|
|
149
|
+
- `record(space_id, content, metadata=None, background=False) -> dict` — store a memory; `background=True` queues it and returns a `job_id`
|
|
150
|
+
- `record_batch(space_id, items) -> dict` — bulk-ingest up to 100 items (always queued); returns a `job_id`
|
|
151
|
+
- `get_job(space_id, job_id) -> dict` — poll a queued job's status (free); `status` is one of pending / processing / completed / failed / cancelled / not_found
|
|
152
|
+
- `retrieve(space_id, query, limit=10) -> list[dict]`
|
|
153
|
+
- `reason(space_id, query) -> str | None`
|
|
154
|
+
- `list_spaces() -> list[dict]`
|
|
155
|
+
- `upload_file(space_id, file, *, filename=None, strategy=None, tags=None) -> dict` — upload a file (path / bytes / file-like) so retrieval can draw on its content; ingested asynchronously, returns `job_ids`. PDF, DOCX, PPTX, XLSX, images (OCR), HTML, TXT/MD, CSV, audio. Files over 25 MB are rejected client-side.
|
|
156
|
+
- `list_documents(space_id, limit=100, offset=0) -> list[dict]`
|
|
157
|
+
- `delete_document(space_id, document_id) -> None` — remove a document and the memories extracted from it
|
|
158
|
+
- `get_graph(space_id, limit=500, min_count=1) -> dict` — entity relationship graph (nodes + co-occurrence edges)
|
|
159
|
+
- `list_entities(space_id, limit=100, offset=0) -> list[dict]`
|
|
160
|
+
- `get_entity(space_id, entity_id) -> dict` — one entity + its observations
|
|
161
|
+
- `async_record(...)`, `async_record_batch(...)`, `async_get_job(...)`, `async_retrieve(...)`, `async_reason(...)`, `async_list_spaces(...)`, `async_upload_file(...)`, `async_list_documents(...)`, `async_delete_document(...)`, `async_get_graph(...)`, `async_list_entities(...)`, `async_get_entity(...)` — async equivalents
|
|
162
|
+
- `close()` / `aclose()` — release underlying HTTP clients
|
|
163
|
+
|
|
164
|
+
Errors raise `AnonaError(status_code, detail)`.
|
|
165
|
+
|
|
166
|
+
## Framework adapters
|
|
167
|
+
|
|
168
|
+
Anona plugs into the Python agent frameworks through optional extras. Every
|
|
169
|
+
adapter handles recall and storage for you, and scopes memories per end user.
|
|
170
|
+
|
|
171
|
+
| Framework | Install | Import |
|
|
172
|
+
| --- | --- | --- |
|
|
173
|
+
| LangChain / LangGraph | `pip install 'anona[langchain]'` | `anona.integrations.langchain` |
|
|
174
|
+
| CrewAI | `pip install 'anona[crewai]'` | `anona.integrations.crewai` |
|
|
175
|
+
| LlamaIndex | `pip install 'anona[llamaindex]'` | `anona.integrations.llamaindex` |
|
|
176
|
+
| Google ADK | `pip install 'anona[adk]'` | `anona.integrations.google_adk` |
|
|
177
|
+
| Microsoft Agent Framework | `pip install 'anona[msagent]'` | `anona.integrations.ms_agent` |
|
|
178
|
+
| AWS Strands | `pip install 'anona[strands]'` | `anona.integrations.strands` |
|
|
179
|
+
|
|
180
|
+
All six are built on one `MemoryBridge`, which owns scope resolution and the
|
|
181
|
+
failure contract:
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
from anona.integrations import MemoryBridge
|
|
185
|
+
from anona.integrations.langchain import AnonaMemory
|
|
186
|
+
|
|
187
|
+
bridge = MemoryBridge(
|
|
188
|
+
api_key="anona_live_...",
|
|
189
|
+
space_id="my-space",
|
|
190
|
+
user_id="customer-42", # optional scope: this user's memories only
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
agent = create_agent(model="gpt-4o-mini", middleware=[AnonaMemory(bridge=bridge)])
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Memory failures never raise into your agent.** A failed recall or store is
|
|
197
|
+
logged and the agent runs on without memory, rather than taking your
|
|
198
|
+
application down.
|
|
199
|
+
|
|
200
|
+
Each adapter's own module docstring documents its scoping, failure behaviour
|
|
201
|
+
and per-call cost. Full docs: https://docs.anonalabs.com/integrations/langchain
|
|
202
|
+
|
|
203
|
+
Runnable end-to-end scripts for all six live in [`examples/`](examples/).
|
|
204
|
+
|
|
205
|
+
## LiteLLM integration
|
|
206
|
+
|
|
207
|
+
Auto-inject relevant memories into every `litellm.completion()` call, and auto-store the resulting Q&A pair:
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
from anona.integrations.litellm import AnonaMemory
|
|
211
|
+
|
|
212
|
+
mem = AnonaMemory(
|
|
213
|
+
api_key="anona_live_...",
|
|
214
|
+
space_id="space_123",
|
|
215
|
+
recall_limit=5, # how many memories to retrieve per call
|
|
216
|
+
inject_mode="system", # "system" or "user"
|
|
217
|
+
store_after=True, # auto-store the exchange after each call
|
|
218
|
+
)
|
|
219
|
+
mem.enable()
|
|
220
|
+
|
|
221
|
+
# All subsequent litellm.completion() calls now auto-recall + auto-store.
|
|
222
|
+
import litellm
|
|
223
|
+
litellm.completion(model="gpt-4o", messages=[{"role": "user", "content": "..."}])
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## MCP server
|
|
227
|
+
|
|
228
|
+
The SDK ships an [MCP](https://modelcontextprotocol.io) server so any MCP client
|
|
229
|
+
— Claude Desktop, Claude Code, Cursor — can read and write Anona memory as native
|
|
230
|
+
tools: `record`, `retrieve`, `list_spaces`, and `reason`.
|
|
231
|
+
|
|
232
|
+
Install the extra:
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
pip install "anona[mcp] @ git+https://github.com/anonalabs/Anona-Memory-SDK.git"
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**Claude Desktop / Cursor** — add to `claude_desktop_config.json` (or
|
|
239
|
+
`~/.cursor/mcp.json`), then restart:
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"mcpServers": {
|
|
244
|
+
"anona": {
|
|
245
|
+
"command": "uvx",
|
|
246
|
+
"args": [
|
|
247
|
+
"--from",
|
|
248
|
+
"anona[mcp] @ git+https://github.com/anonalabs/Anona-Memory-SDK.git",
|
|
249
|
+
"anona-mcp"
|
|
250
|
+
],
|
|
251
|
+
"env": {
|
|
252
|
+
"ANONA_API_KEY": "anona_live_...",
|
|
253
|
+
"ANONA_SPACE_ID": "space_123"
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**Claude Code** — one command:
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
claude mcp add anona \
|
|
264
|
+
--env ANONA_API_KEY=anona_live_... \
|
|
265
|
+
--env ANONA_SPACE_ID=space_123 \
|
|
266
|
+
-- uvx --from "anona[mcp] @ git+https://github.com/anonalabs/Anona-Memory-SDK.git" anona-mcp
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
`ANONA_SPACE_ID` sets the default space so you can just say "remember this"
|
|
270
|
+
without naming one; override it per call with the `space_id` argument. The key
|
|
271
|
+
is personal — the server only reaches spaces you are a member of.
|
|
272
|
+
|
|
273
|
+
## Requirements
|
|
274
|
+
|
|
275
|
+
- Python >= 3.10
|
|
276
|
+
- `httpx >= 0.24`
|
|
277
|
+
- `litellm >= 1.0` (optional, only for the LiteLLM integration)
|
|
278
|
+
- `mcp >= 1.2` (optional, only for the MCP server)
|
|
279
|
+
- one of `langchain`, `crewai`, `llama-index-core`, `google-adk`,
|
|
280
|
+
`agent-framework-core`, `strands-agents` (optional, only for the matching
|
|
281
|
+
framework adapter — see the extras above for the verified version floors)
|
|
282
|
+
|
|
283
|
+
## License
|
|
284
|
+
|
|
285
|
+
MIT
|
anona-0.6.0/README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Anona Memory SDK
|
|
2
|
+
|
|
3
|
+
Official SDKs for [Anona Memory](https://memory.anonalabs.com) — managed AI memory for intelligent agents. Record, retrieve, and reason over memories per user/space via a simple client, or auto-inject memory into LiteLLM calls with one line.
|
|
4
|
+
|
|
5
|
+
- **Python** — this repository root. Documented below.
|
|
6
|
+
- **TypeScript** — [`typescript/`](typescript/). Zero dependencies, runs on Node 18+, Bun, Deno, Cloudflare Workers and the browser, with adapters for the Vercel AI SDK and the OpenAI Agents SDK. See [`typescript/README.md`](typescript/README.md).
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
import { Anona } from "@anona-labs/memory";
|
|
10
|
+
|
|
11
|
+
const anona = new Anona({ apiKey: process.env.ANONA_API_KEY! });
|
|
12
|
+
await anona.record({ spaceId: "support", content: "Alice prefers email" });
|
|
13
|
+
const hits = await anona.retrieve({ spaceId: "support", query: "how to contact Alice" });
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @anona-labs/memory
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
Install directly from GitHub:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install git+https://github.com/anonalabs/Anona-Memory-SDK.git
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
With the LiteLLM integration (or `mcp` for the MCP server):
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install "anona[litellm] @ git+https://github.com/anonalabs/Anona-Memory-SDK.git"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
> The package is not yet on PyPI, so install from the Git URL above. Once it's
|
|
35
|
+
> published, `pip install anona` will also work.
|
|
36
|
+
|
|
37
|
+
## Quickstart
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from anona import AnonaClient
|
|
41
|
+
|
|
42
|
+
# base_url defaults to https://api.anonalabs.com — pass it only to override.
|
|
43
|
+
client = AnonaClient(api_key="anona_live_...")
|
|
44
|
+
|
|
45
|
+
# Record a memory
|
|
46
|
+
client.record(space_id="space_123", content="User prefers dark mode.")
|
|
47
|
+
|
|
48
|
+
# Retrieve memories
|
|
49
|
+
results = client.retrieve(space_id="space_123", query="UI preferences", limit=5)
|
|
50
|
+
for r in results:
|
|
51
|
+
print(r["relevance_score"], r["content"])
|
|
52
|
+
|
|
53
|
+
# Reason: a synthesized insight across memories
|
|
54
|
+
summary = client.reason(space_id="space_123", query="What do we know about this user?")
|
|
55
|
+
print(summary)
|
|
56
|
+
|
|
57
|
+
client.close()
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Async ingestion (don't block on a write)
|
|
61
|
+
|
|
62
|
+
Recording runs fact extraction, so a normal `record()` takes a moment. In a
|
|
63
|
+
chat loop or any latency-sensitive path, queue the write with `background=True`
|
|
64
|
+
and poll the returned job instead:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
import time
|
|
68
|
+
|
|
69
|
+
job = client.record(
|
|
70
|
+
space_id="space_123",
|
|
71
|
+
content="User prefers dark mode.",
|
|
72
|
+
background=True, # returns a job_id, doesn't wait
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
while True:
|
|
76
|
+
status = client.get_job(space_id="space_123", job_id=job["job_id"])
|
|
77
|
+
if status["status"] in ("completed", "failed", "cancelled", "not_found"):
|
|
78
|
+
break
|
|
79
|
+
time.sleep(2)
|
|
80
|
+
|
|
81
|
+
# Backfill many memories at once (always queued, up to 100 per call):
|
|
82
|
+
batch = client.record_batch(
|
|
83
|
+
space_id="space_123",
|
|
84
|
+
items=[
|
|
85
|
+
{"content": "User is on the Pro plan."},
|
|
86
|
+
{"content": "Signed up in 2024.", "timestamp": "2024-03-01T00:00:00Z"},
|
|
87
|
+
],
|
|
88
|
+
)
|
|
89
|
+
print(batch["accepted"], "queued as job", batch["job_id"])
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Async variants (`async_record`, `async_retrieve`, `async_reason`) are available on the same client, or use it as a context manager:
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
async with AnonaClient(api_key="...") as client:
|
|
96
|
+
await client.async_record(space_id="space_123", content="...")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## API
|
|
100
|
+
|
|
101
|
+
### `AnonaClient(api_key, base_url="https://api.anonalabs.com")`
|
|
102
|
+
|
|
103
|
+
- `record(space_id, content, metadata=None, background=False) -> dict` — store a memory; `background=True` queues it and returns a `job_id`
|
|
104
|
+
- `record_batch(space_id, items) -> dict` — bulk-ingest up to 100 items (always queued); returns a `job_id`
|
|
105
|
+
- `get_job(space_id, job_id) -> dict` — poll a queued job's status (free); `status` is one of pending / processing / completed / failed / cancelled / not_found
|
|
106
|
+
- `retrieve(space_id, query, limit=10) -> list[dict]`
|
|
107
|
+
- `reason(space_id, query) -> str | None`
|
|
108
|
+
- `list_spaces() -> list[dict]`
|
|
109
|
+
- `upload_file(space_id, file, *, filename=None, strategy=None, tags=None) -> dict` — upload a file (path / bytes / file-like) so retrieval can draw on its content; ingested asynchronously, returns `job_ids`. PDF, DOCX, PPTX, XLSX, images (OCR), HTML, TXT/MD, CSV, audio. Files over 25 MB are rejected client-side.
|
|
110
|
+
- `list_documents(space_id, limit=100, offset=0) -> list[dict]`
|
|
111
|
+
- `delete_document(space_id, document_id) -> None` — remove a document and the memories extracted from it
|
|
112
|
+
- `get_graph(space_id, limit=500, min_count=1) -> dict` — entity relationship graph (nodes + co-occurrence edges)
|
|
113
|
+
- `list_entities(space_id, limit=100, offset=0) -> list[dict]`
|
|
114
|
+
- `get_entity(space_id, entity_id) -> dict` — one entity + its observations
|
|
115
|
+
- `async_record(...)`, `async_record_batch(...)`, `async_get_job(...)`, `async_retrieve(...)`, `async_reason(...)`, `async_list_spaces(...)`, `async_upload_file(...)`, `async_list_documents(...)`, `async_delete_document(...)`, `async_get_graph(...)`, `async_list_entities(...)`, `async_get_entity(...)` — async equivalents
|
|
116
|
+
- `close()` / `aclose()` — release underlying HTTP clients
|
|
117
|
+
|
|
118
|
+
Errors raise `AnonaError(status_code, detail)`.
|
|
119
|
+
|
|
120
|
+
## Framework adapters
|
|
121
|
+
|
|
122
|
+
Anona plugs into the Python agent frameworks through optional extras. Every
|
|
123
|
+
adapter handles recall and storage for you, and scopes memories per end user.
|
|
124
|
+
|
|
125
|
+
| Framework | Install | Import |
|
|
126
|
+
| --- | --- | --- |
|
|
127
|
+
| LangChain / LangGraph | `pip install 'anona[langchain]'` | `anona.integrations.langchain` |
|
|
128
|
+
| CrewAI | `pip install 'anona[crewai]'` | `anona.integrations.crewai` |
|
|
129
|
+
| LlamaIndex | `pip install 'anona[llamaindex]'` | `anona.integrations.llamaindex` |
|
|
130
|
+
| Google ADK | `pip install 'anona[adk]'` | `anona.integrations.google_adk` |
|
|
131
|
+
| Microsoft Agent Framework | `pip install 'anona[msagent]'` | `anona.integrations.ms_agent` |
|
|
132
|
+
| AWS Strands | `pip install 'anona[strands]'` | `anona.integrations.strands` |
|
|
133
|
+
|
|
134
|
+
All six are built on one `MemoryBridge`, which owns scope resolution and the
|
|
135
|
+
failure contract:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from anona.integrations import MemoryBridge
|
|
139
|
+
from anona.integrations.langchain import AnonaMemory
|
|
140
|
+
|
|
141
|
+
bridge = MemoryBridge(
|
|
142
|
+
api_key="anona_live_...",
|
|
143
|
+
space_id="my-space",
|
|
144
|
+
user_id="customer-42", # optional scope: this user's memories only
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
agent = create_agent(model="gpt-4o-mini", middleware=[AnonaMemory(bridge=bridge)])
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Memory failures never raise into your agent.** A failed recall or store is
|
|
151
|
+
logged and the agent runs on without memory, rather than taking your
|
|
152
|
+
application down.
|
|
153
|
+
|
|
154
|
+
Each adapter's own module docstring documents its scoping, failure behaviour
|
|
155
|
+
and per-call cost. Full docs: https://docs.anonalabs.com/integrations/langchain
|
|
156
|
+
|
|
157
|
+
Runnable end-to-end scripts for all six live in [`examples/`](examples/).
|
|
158
|
+
|
|
159
|
+
## LiteLLM integration
|
|
160
|
+
|
|
161
|
+
Auto-inject relevant memories into every `litellm.completion()` call, and auto-store the resulting Q&A pair:
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
from anona.integrations.litellm import AnonaMemory
|
|
165
|
+
|
|
166
|
+
mem = AnonaMemory(
|
|
167
|
+
api_key="anona_live_...",
|
|
168
|
+
space_id="space_123",
|
|
169
|
+
recall_limit=5, # how many memories to retrieve per call
|
|
170
|
+
inject_mode="system", # "system" or "user"
|
|
171
|
+
store_after=True, # auto-store the exchange after each call
|
|
172
|
+
)
|
|
173
|
+
mem.enable()
|
|
174
|
+
|
|
175
|
+
# All subsequent litellm.completion() calls now auto-recall + auto-store.
|
|
176
|
+
import litellm
|
|
177
|
+
litellm.completion(model="gpt-4o", messages=[{"role": "user", "content": "..."}])
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## MCP server
|
|
181
|
+
|
|
182
|
+
The SDK ships an [MCP](https://modelcontextprotocol.io) server so any MCP client
|
|
183
|
+
— Claude Desktop, Claude Code, Cursor — can read and write Anona memory as native
|
|
184
|
+
tools: `record`, `retrieve`, `list_spaces`, and `reason`.
|
|
185
|
+
|
|
186
|
+
Install the extra:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
pip install "anona[mcp] @ git+https://github.com/anonalabs/Anona-Memory-SDK.git"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Claude Desktop / Cursor** — add to `claude_desktop_config.json` (or
|
|
193
|
+
`~/.cursor/mcp.json`), then restart:
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"mcpServers": {
|
|
198
|
+
"anona": {
|
|
199
|
+
"command": "uvx",
|
|
200
|
+
"args": [
|
|
201
|
+
"--from",
|
|
202
|
+
"anona[mcp] @ git+https://github.com/anonalabs/Anona-Memory-SDK.git",
|
|
203
|
+
"anona-mcp"
|
|
204
|
+
],
|
|
205
|
+
"env": {
|
|
206
|
+
"ANONA_API_KEY": "anona_live_...",
|
|
207
|
+
"ANONA_SPACE_ID": "space_123"
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Claude Code** — one command:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
claude mcp add anona \
|
|
218
|
+
--env ANONA_API_KEY=anona_live_... \
|
|
219
|
+
--env ANONA_SPACE_ID=space_123 \
|
|
220
|
+
-- uvx --from "anona[mcp] @ git+https://github.com/anonalabs/Anona-Memory-SDK.git" anona-mcp
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
`ANONA_SPACE_ID` sets the default space so you can just say "remember this"
|
|
224
|
+
without naming one; override it per call with the `space_id` argument. The key
|
|
225
|
+
is personal — the server only reaches spaces you are a member of.
|
|
226
|
+
|
|
227
|
+
## Requirements
|
|
228
|
+
|
|
229
|
+
- Python >= 3.10
|
|
230
|
+
- `httpx >= 0.24`
|
|
231
|
+
- `litellm >= 1.0` (optional, only for the LiteLLM integration)
|
|
232
|
+
- `mcp >= 1.2` (optional, only for the MCP server)
|
|
233
|
+
- one of `langchain`, `crewai`, `llama-index-core`, `google-adk`,
|
|
234
|
+
`agent-framework-core`, `strands-agents` (optional, only for the matching
|
|
235
|
+
framework adapter — see the extras above for the verified version floors)
|
|
236
|
+
|
|
237
|
+
## License
|
|
238
|
+
|
|
239
|
+
MIT
|