memcode-sdk 2.3.1__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.
@@ -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 &mdash; 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` | &mdash; | `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` | &mdash; | Network timeout, DNS failure, connection refused |