memcode-sdk 2.3.1__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.
- memcode_sdk/__init__.py +104 -0
- memcode_sdk/_http.py +162 -0
- memcode_sdk/async_client.py +277 -0
- memcode_sdk/async_v2_client.py +147 -0
- memcode_sdk/client.py +576 -0
- memcode_sdk/errors.py +64 -0
- memcode_sdk/py.typed +1 -0
- memcode_sdk/types.py +172 -0
- memcode_sdk/v2_client.py +214 -0
- memcode_sdk/v2_types.py +116 -0
- memcode_sdk-2.3.1.dist-info/METADATA +387 -0
- memcode_sdk-2.3.1.dist-info/RECORD +14 -0
- memcode_sdk-2.3.1.dist-info/WHEEL +5 -0
- memcode_sdk-2.3.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: memcode-sdk
|
|
3
|
+
Version: 2.3.1
|
|
4
|
+
Summary: Python SDK for the Memcode long-term memory API
|
|
5
|
+
Author: Memcode
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://memcode.in
|
|
8
|
+
Project-URL: Repository, https://gitlab.com/xortex1/memcode-sdk
|
|
9
|
+
Project-URL: Issues, https://gitlab.com/xortex1/memcode-sdk/-/issues
|
|
10
|
+
Keywords: memory,long-term-memory,llm,rag,ai-agent
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: httpx<1,>=0.24
|
|
23
|
+
|
|
24
|
+
<h1 align="center">Memcode Client SDKs</h1>
|
|
25
|
+
|
|
26
|
+
<p align="center">
|
|
27
|
+
Official client libraries for the <strong>Memcode long-term memory API</strong>.<br>
|
|
28
|
+
Available in Python, TypeScript, and Go.
|
|
29
|
+
</p>
|
|
30
|
+
|
|
31
|
+
All three SDKs share the same design principles:
|
|
32
|
+
|
|
33
|
+
- Existing v1 clients keep three 1:1 methods: **ingest**, **retrieve**, **search**
|
|
34
|
+
- Bearer-token authentication via constructor arg or `MEMCODE_API_KEY` env var
|
|
35
|
+
- Typed error hierarchy so callers can handle auth, rate-limit, and server errors distinctly
|
|
36
|
+
- Zero config defaults — point at `localhost:8000` with no key and it just works in dev
|
|
37
|
+
|
|
38
|
+
The existing `MemcodeClient`/`Client` APIs remain v1-compatible and now expose an
|
|
39
|
+
additive advanced personal v2 surface for authenticated personal callers:
|
|
40
|
+
|
|
41
|
+
- Python: `ingest_v2`, `get_ingest_status_v2`, `search_v2`, `retrieve_v2`
|
|
42
|
+
- TypeScript: `ingestV2`, `getIngestStatusV2`, `searchV2`, `retrieveV2`
|
|
43
|
+
- Go: `IngestV2`, `GetIngestStatusV2`, `SearchV2`,
|
|
44
|
+
`SearchV2WithOptions`, `RetrieveV2`
|
|
45
|
+
|
|
46
|
+
Personal v2 derives the user from the API key or JWT, so no `user_id` is needed.
|
|
47
|
+
Ingestion returns a durable job receipt, retrieval includes
|
|
48
|
+
attribution/connection-learning metadata, and personal v2 search uses the
|
|
49
|
+
unified `/v2/memory/search` route.
|
|
50
|
+
The deprecated `user_id` argument remains accepted and is sent only when supplied.
|
|
51
|
+
The API may return `409` for personal v2 ingest when raw original storage is
|
|
52
|
+
globally enabled; that backend safety gate remains unchanged.
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from memcode_sdk import MemcodeClient
|
|
56
|
+
|
|
57
|
+
client = MemcodeClient(api_url="https://memory.example.com", api_key="sk-...")
|
|
58
|
+
job = client.ingest_v2(
|
|
59
|
+
user_query="The launch is Friday",
|
|
60
|
+
)
|
|
61
|
+
status = client.get_ingest_status_v2(job.job_id)
|
|
62
|
+
hits = client.search_v2(query="launch date")
|
|
63
|
+
answer = client.retrieve_v2(query="When is launch?")
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Prerequisites
|
|
67
|
+
|
|
68
|
+
A running Memcode API server:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
uvicorn src.api.app:create_app --factory --host 0.0.0.0 --port 8000
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Python
|
|
75
|
+
|
|
76
|
+
**Location:** `memcode_sdk/`
|
|
77
|
+
|
|
78
|
+
### Install
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install memcode-sdk
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Sync usage
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from memcode_sdk import MemcodeClient
|
|
88
|
+
|
|
89
|
+
client = MemcodeClient(api_url="http://localhost:8000", api_key="sk-...")
|
|
90
|
+
|
|
91
|
+
# Check health
|
|
92
|
+
health = client.ping()
|
|
93
|
+
print(health.status, health.pipelines_ready)
|
|
94
|
+
|
|
95
|
+
# Ingest a conversation turn
|
|
96
|
+
result = client.ingest(
|
|
97
|
+
user_query="I just got promoted to senior engineer at Google!",
|
|
98
|
+
agent_response="Congratulations on your promotion!",
|
|
99
|
+
user_id="user_42",
|
|
100
|
+
)
|
|
101
|
+
print(result.model, result.profile, result.temporal)
|
|
102
|
+
|
|
103
|
+
# Retrieve an LLM-generated answer backed by memory
|
|
104
|
+
answer = client.retrieve(query="What is my job title?", user_id="user_42")
|
|
105
|
+
print(answer.answer)
|
|
106
|
+
print(answer.sources) # list of SourceRecord
|
|
107
|
+
print(answer.confidence)
|
|
108
|
+
|
|
109
|
+
# Raw semantic search (no LLM answer)
|
|
110
|
+
hits = client.search(
|
|
111
|
+
query="work history",
|
|
112
|
+
user_id="user_42",
|
|
113
|
+
domains=["profile", "temporal"],
|
|
114
|
+
top_k=10,
|
|
115
|
+
)
|
|
116
|
+
for r in hits.results:
|
|
117
|
+
print(f"[{r.domain}] {r.content} (score={r.score:.2f})")
|
|
118
|
+
|
|
119
|
+
job = client.ingest_v2(
|
|
120
|
+
user_query="I now lead the platform team",
|
|
121
|
+
effort_level="high",
|
|
122
|
+
)
|
|
123
|
+
status = client.get_ingest_status_v2(job.job_id)
|
|
124
|
+
hybrid = client.search_v2(
|
|
125
|
+
query="work history",
|
|
126
|
+
top_k=10,
|
|
127
|
+
original_top_k=5,
|
|
128
|
+
)
|
|
129
|
+
advanced_answer = client.retrieve_v2(
|
|
130
|
+
query="What team do I lead?",
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
client.close()
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Async usage
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
from memcode_sdk import AsyncMemcodeClient
|
|
140
|
+
|
|
141
|
+
async with AsyncMemcodeClient(api_url="http://localhost:8000") as client:
|
|
142
|
+
result = await client.ingest(
|
|
143
|
+
user_query="I love hiking in the Rockies.",
|
|
144
|
+
user_id="user_42",
|
|
145
|
+
)
|
|
146
|
+
answer = await client.retrieve(query="hobbies", user_id="user_42")
|
|
147
|
+
print(answer.answer)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Error handling
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from memcode_sdk import MemcodeClient, AuthenticationError, RateLimitError, NotReadyError
|
|
154
|
+
|
|
155
|
+
client = MemcodeClient(api_key="bad-key")
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
client.ingest(user_query="test", user_id="u1")
|
|
159
|
+
except AuthenticationError as e:
|
|
160
|
+
print(f"Auth failed (HTTP {e.status_code}): {e.message}")
|
|
161
|
+
except RateLimitError as e:
|
|
162
|
+
print(f"Throttled, retry after {e.retry_after}s")
|
|
163
|
+
except NotReadyError:
|
|
164
|
+
print("Pipelines still loading, try again shortly")
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Configuration
|
|
168
|
+
|
|
169
|
+
| Parameter | Env var | Default |
|
|
170
|
+
|-----------|---------|---------|
|
|
171
|
+
| `api_url` | `MEMCODE_API_URL` | `http://localhost:8000` |
|
|
172
|
+
| `api_key` | `MEMCODE_API_KEY` | _(empty, no auth)_ |
|
|
173
|
+
| `timeout` | — | `120` seconds |
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## TypeScript
|
|
178
|
+
|
|
179
|
+
**Location:** `memcode-ts/`
|
|
180
|
+
**Package name:** `memcode-sdk`
|
|
181
|
+
|
|
182
|
+
### Install
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
npm install memcode-sdk
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Usage
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
import { MemcodeClient } from "memcode-sdk";
|
|
192
|
+
|
|
193
|
+
const client = new MemcodeClient("http://localhost:8000", "sk-...");
|
|
194
|
+
|
|
195
|
+
// Health
|
|
196
|
+
const ready = await client.isReady();
|
|
197
|
+
|
|
198
|
+
// Ingest
|
|
199
|
+
const result = await client.ingest({
|
|
200
|
+
user_query: "I just adopted a golden retriever named Max!",
|
|
201
|
+
agent_response: "That's wonderful!",
|
|
202
|
+
user_id: "user_42",
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Retrieve
|
|
206
|
+
const answer = await client.retrieve({
|
|
207
|
+
query: "Do I have any pets?",
|
|
208
|
+
user_id: "user_42",
|
|
209
|
+
});
|
|
210
|
+
console.log(answer.answer);
|
|
211
|
+
|
|
212
|
+
// Search
|
|
213
|
+
const hits = await client.search({
|
|
214
|
+
query: "pets",
|
|
215
|
+
user_id: "user_42",
|
|
216
|
+
domains: ["profile", "summary"],
|
|
217
|
+
top_k: 5,
|
|
218
|
+
});
|
|
219
|
+
hits.results.forEach((r) => console.log(`[${r.domain}] ${r.content}`));
|
|
220
|
+
|
|
221
|
+
const job = await client.ingestV2({
|
|
222
|
+
user_query: "I now lead the platform team",
|
|
223
|
+
effort_level: "high",
|
|
224
|
+
});
|
|
225
|
+
const status = await client.getIngestStatusV2(job.job_id);
|
|
226
|
+
const hybrid = await client.searchV2({
|
|
227
|
+
query: "pets",
|
|
228
|
+
top_k: 10,
|
|
229
|
+
original_top_k: 5,
|
|
230
|
+
});
|
|
231
|
+
const advancedAnswer = await client.retrieveV2({
|
|
232
|
+
query: "Do I have pets?",
|
|
233
|
+
});
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Error handling
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
import { MemcodeClient, AuthenticationError, RateLimitError } from "memcode-sdk";
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
await client.ingest({ user_query: "test", user_id: "u1" });
|
|
243
|
+
} catch (e) {
|
|
244
|
+
if (e instanceof AuthenticationError) {
|
|
245
|
+
console.error(`Auth failed: ${e.message}`);
|
|
246
|
+
} else if (e instanceof RateLimitError) {
|
|
247
|
+
console.error(`Rate limited, retry after ${e.retryAfter}s`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Go
|
|
255
|
+
|
|
256
|
+
**Location:** `memcode-go/`
|
|
257
|
+
**Module:** `gitlab.com/xortex1/memcode-sdk/memcode-go/v2`
|
|
258
|
+
|
|
259
|
+
### Install
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
go get gitlab.com/xortex1/memcode-sdk/memcode-go/v2
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Usage
|
|
266
|
+
|
|
267
|
+
```go
|
|
268
|
+
package main
|
|
269
|
+
|
|
270
|
+
import (
|
|
271
|
+
"fmt"
|
|
272
|
+
memcode "gitlab.com/xortex1/memcode-sdk/memcode-go/v2"
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
func main() {
|
|
276
|
+
client := memcode.NewClient("http://localhost:8000", "sk-...")
|
|
277
|
+
|
|
278
|
+
// Health
|
|
279
|
+
if client.IsReady() {
|
|
280
|
+
fmt.Println("Memcode API is ready")
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Ingest
|
|
284
|
+
result, err := client.Ingest(memcode.IngestParams{
|
|
285
|
+
UserQuery: "I'm moving to Seattle next month.",
|
|
286
|
+
AgentResponse: "Good luck with your move!",
|
|
287
|
+
UserID: "user_42",
|
|
288
|
+
})
|
|
289
|
+
if err != nil {
|
|
290
|
+
panic(err)
|
|
291
|
+
}
|
|
292
|
+
fmt.Println("Model:", result.Model)
|
|
293
|
+
|
|
294
|
+
// Retrieve
|
|
295
|
+
answer, err := client.Retrieve(memcode.RetrieveParams{
|
|
296
|
+
Query: "Where am I moving?",
|
|
297
|
+
UserID: "user_42",
|
|
298
|
+
})
|
|
299
|
+
if err != nil {
|
|
300
|
+
panic(err)
|
|
301
|
+
}
|
|
302
|
+
fmt.Println("Answer:", answer.Answer)
|
|
303
|
+
|
|
304
|
+
// Search
|
|
305
|
+
hits, err := client.Search(memcode.SearchParams{
|
|
306
|
+
Query: "location",
|
|
307
|
+
UserID: "user_42",
|
|
308
|
+
Domains: []string{"profile", "temporal"},
|
|
309
|
+
TopK: 10,
|
|
310
|
+
})
|
|
311
|
+
if err != nil {
|
|
312
|
+
panic(err)
|
|
313
|
+
}
|
|
314
|
+
for _, r := range hits.Results {
|
|
315
|
+
fmt.Printf("[%s] %s (%.2f)\n", r.Domain, r.Content, r.Score)
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
job, err := client.IngestV2(memcode.PersonalV2IngestParams{
|
|
319
|
+
UserQuery: "I'm moving to Seattle next month.",
|
|
320
|
+
EffortLevel: "high",
|
|
321
|
+
}, "move-1")
|
|
322
|
+
if err != nil {
|
|
323
|
+
panic(err)
|
|
324
|
+
}
|
|
325
|
+
status, err := client.GetIngestStatusV2(job.JobID)
|
|
326
|
+
|
|
327
|
+
hybrid, err := client.SearchV2WithOptions(memcode.HybridSearchParams{
|
|
328
|
+
Query: "location",
|
|
329
|
+
OriginalTopK: 5,
|
|
330
|
+
}, memcode.PersonalV2SearchOptions{
|
|
331
|
+
TopK: 10,
|
|
332
|
+
})
|
|
333
|
+
if err != nil {
|
|
334
|
+
panic(err)
|
|
335
|
+
}
|
|
336
|
+
advancedAnswer, err := client.RetrieveV2(memcode.RetrieveParams{
|
|
337
|
+
Query: "Where am I moving?",
|
|
338
|
+
})
|
|
339
|
+
fmt.Println("V2:", status.Status, hybrid.Total, advancedAnswer.Answer)
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Error handling
|
|
344
|
+
|
|
345
|
+
```go
|
|
346
|
+
result, err := client.Ingest(params)
|
|
347
|
+
if err != nil {
|
|
348
|
+
switch e := err.(type) {
|
|
349
|
+
case *memcode.AuthenticationError:
|
|
350
|
+
fmt.Println("Bad API key:", e.Message)
|
|
351
|
+
case *memcode.RateLimitError:
|
|
352
|
+
fmt.Printf("Throttled, retry after %ds\n", e.RetryAfter)
|
|
353
|
+
case *memcode.NotReadyError:
|
|
354
|
+
fmt.Println("Pipelines loading, retry shortly")
|
|
355
|
+
default:
|
|
356
|
+
fmt.Println("Error:", err)
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## API Reference
|
|
364
|
+
|
|
365
|
+
All three SDKs expose backwards-compatible v1 methods plus advanced personal v2 methods:
|
|
366
|
+
|
|
367
|
+
| Method | Endpoint | Description |
|
|
368
|
+
|--------|----------|-------------|
|
|
369
|
+
| **ingest** | `POST /v1/memory/ingest` | Store a conversation turn. Memcode classifies the input and extracts profile facts, temporal events, and summaries automatically. |
|
|
370
|
+
| **retrieve** | `POST /v1/memory/retrieve` | Answer a question using stored memories. Returns an LLM-generated answer with source citations and a confidence score. |
|
|
371
|
+
| **search** | `POST /v1/memory/search` | Raw semantic search across memory domains (`profile`, `temporal`, `summary`). Returns matching records without an LLM answer. |
|
|
372
|
+
| **personal v2 ingest** | `POST /v2/memory/ingest` | Start a durable normal-user ingest job. |
|
|
373
|
+
| **personal v2 status** | `GET /v2/memory/ingest/{job_id}/status` | Poll durable ingest progress. |
|
|
374
|
+
| **personal v2 search** | `POST /v2/memory/search` | Search extracted memories and original chunks for the user derived from the credential. |
|
|
375
|
+
| **personal v2 retrieve** | `POST /v2/memory/retrieve` | Advanced attributed retrieval with connection-learning metadata. |
|
|
376
|
+
| **ping** | `GET /health` | Health/readiness check. Never raises on a valid HTTP response. |
|
|
377
|
+
|
|
378
|
+
## Error Types
|
|
379
|
+
|
|
380
|
+
| Error | HTTP status | When |
|
|
381
|
+
|-------|-------------|------|
|
|
382
|
+
| `AuthenticationError` | 401 / 403 | Missing or invalid API key |
|
|
383
|
+
| `RateLimitError` | 429 | Per-key rate limit exceeded |
|
|
384
|
+
| `ValidationError` | 422 | Request body failed validation |
|
|
385
|
+
| `NotReadyError` | 503 | Pipelines still initializing |
|
|
386
|
+
| `ServerError` | 5xx | Server-side failure |
|
|
387
|
+
| `ConnectionError` | — | Network timeout, DNS failure, connection refused |
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
memcode_sdk/__init__.py,sha256=GmcdPUufFaIUkPQmBR0kgMTkJb9BBtwsatcCxBOJUoA,2402
|
|
2
|
+
memcode_sdk/_http.py,sha256=pElyduXCHYCY-lwlB2RHJKHlYHJaCPMeA49t08XI-lw,6452
|
|
3
|
+
memcode_sdk/async_client.py,sha256=uP_1xNIvnoNj3zJtA7vj1Zn7dzDgUNYO_uTxTdrSalw,9724
|
|
4
|
+
memcode_sdk/async_v2_client.py,sha256=hMd6IXqCklCDxsED2tdpgqmUnspET_JwkFl4vI44ZFE,5509
|
|
5
|
+
memcode_sdk/client.py,sha256=u7Eu5aiAhD3S5MvRMJ8u191hHbQ7MiM3CIW0hZPZvGQ,20617
|
|
6
|
+
memcode_sdk/errors.py,sha256=M7TEOkSJUbeWL3Df1FSlC8MW-Hw9Fsfxltn11r3OLRg,1863
|
|
7
|
+
memcode_sdk/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
8
|
+
memcode_sdk/types.py,sha256=EeF5wJRsPZ7OlBbrgT_WWJZttX4S24SH_9CRo5gNKzM,5589
|
|
9
|
+
memcode_sdk/v2_client.py,sha256=NCKjZ3xpJwBvnzYZ3eFuCC2UyJQD2MANl6lUdWRPpso,8052
|
|
10
|
+
memcode_sdk/v2_types.py,sha256=dlsXzezeHx9uDiopQV4c5ZzWwDNEpq7XR_yYm-11jis,3026
|
|
11
|
+
memcode_sdk-2.3.1.dist-info/METADATA,sha256=S1r3tmD0a-P-mnzdPlbd1SNvHdEvdhWdSjFAk1_nezw,11134
|
|
12
|
+
memcode_sdk-2.3.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
13
|
+
memcode_sdk-2.3.1.dist-info/top_level.txt,sha256=1kJSxZHjhEYBiqN_EbogL1VcI2mwAskU6mokg862KLk,12
|
|
14
|
+
memcode_sdk-2.3.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
memcode_sdk
|