mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-09-14 23:21:20 +00:00
docs: correct SDK usage against published packages (TS 4.24.12, Python 3.50.0)
The monorepo's installed supermemory@3.10.0 lags npm. In the published SDKs
client.memories.add does not exist — adding is client.add() (top-level) or
client.documents.add(); client.memories has only forget/updateMemory. Swept
all 21 affected pages. Also: searchMode ('memories'|'hybrid'|'documents')
IS in the published typings and is now taught on hybrid-search as the mode
selector, with include.chunks marked deprecated back-compat; customId
charset corrected (dots, not colons); 14 CONFIRM markers resolved by the
published typings (entityContext, forget reason, Python signatures).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
56666a663f
commit
55e96f1db8
23 changed files with 115 additions and 97 deletions
|
|
@ -112,7 +112,7 @@ Both work — choose what fits your architecture.
|
|||
|
||||
### Replace entire document
|
||||
|
||||
To completely replace a document's content (not append), use `memories.update()`:
|
||||
To completely replace a document's content (not append), use `documents.update()`:
|
||||
|
||||
```typescript
|
||||
// Replace the entire document content
|
||||
|
|
|
|||
|
|
@ -63,13 +63,12 @@ Inline, when you add content:
|
|||
<CodeGroup>
|
||||
|
||||
```typescript TypeScript
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: "Asked about logo variations for dark backgrounds again — leaning toward the mono version.",
|
||||
containerTag: "user_4f8a",
|
||||
entityContext: `Design conversations between john@acme.com and the Brand.ai assistant.
|
||||
"The logo" means Acme's primary mark. Focus on John's design preferences and constraints.`,
|
||||
});
|
||||
// entityContext isn't in the SDK's TS typings yet — the API accepts it
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
|
|
@ -85,8 +84,6 @@ curl -X POST https://api.supermemory.ai/v3/documents \
|
|||
|
||||
</CodeGroup>
|
||||
|
||||
<!-- CONFIRM: entityContext on add() is accepted by POST /v3/documents (verified in backend validation) but is not in supermemory@3.10.0 TS typings — confirm minimum SDK version before publishing -->
|
||||
|
||||
Or directly on the tag, without ingesting anything. The TS SDK doesn't expose a container-tag update method yet, so this one is a REST call:
|
||||
|
||||
```bash cURL
|
||||
|
|
@ -98,7 +95,7 @@ curl -X PATCH https://api.supermemory.ai/v3/container-tags/user_4f8a \
|
|||
}'
|
||||
```
|
||||
|
||||
<!-- CONFIRM: does a newer SDK than 3.10.0 expose containerTags.update? Reinstate a TS tab if so. PATCH /v3/container-tags/{tag} is admin-gated in the backend (roleGate ROLE_ADMIN) — confirm which API keys pass that gate before documenting the restriction. -->
|
||||
<!-- CONFIRM: PATCH /v3/container-tags/{tag} is admin-gated in the backend (roleGate ROLE_ADMIN) — confirm which API keys pass that gate before documenting the restriction. -->
|
||||
|
||||
Either way, entity context **persists on the tag**. Pass it on one `add()` call and it's stored — every future document in that container is processed with it, including connector syncs landing in the same tag, until you overwrite it or set it to `null`. It's tag state, not a per-request option. If that's not what you expected, it's the one behavior on this page worth rereading.
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import Supermemory from "supermemory"
|
|||
|
||||
const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })
|
||||
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: "Sarah's being promoted to VP of Product", // becomes a document
|
||||
containerTag: "user_4f8a", // the isolation boundary
|
||||
customId: "slack-thread-9917", // your stable id for this document
|
||||
|
|
@ -27,7 +27,7 @@ from supermemory import Supermemory
|
|||
|
||||
client = Supermemory()
|
||||
|
||||
client.memories.add(
|
||||
client.add(
|
||||
content="Sarah's being promoted to VP of Product",
|
||||
container_tag="user_4f8a",
|
||||
custom_id="slack-thread-9917",
|
||||
|
|
@ -49,8 +49,6 @@ curl -X POST "https://api.supermemory.ai/v3/documents" \
|
|||
|
||||
</CodeGroup>
|
||||
|
||||
{/* CONFIRM: python — memories.add kwargs mirrored from the published pypi search style; verify against the shipped package */}
|
||||
|
||||
The content becomes a **document**, the pipeline derives **memories** from it, the memories join the **graph**, and the **profile** for `user_4f8a` updates. The rest of this page defines each of those words — plus the ones that control the process.
|
||||
|
||||
## From content to memory
|
||||
|
|
@ -65,7 +63,7 @@ An individual fact derived from your documents by the ingestion pipeline — a c
|
|||
|
||||
### Chunk
|
||||
|
||||
A slice of a document, sized for retrieval. Search matches at the chunk level, so you get the relevant passage instead of a 40-page document. On memory search you pull them with `include: { chunks: true }`; document search returns matching chunks directly. How chunks, memories, and the graph combine at query time is the subject of [hybrid search](/concepts/hybrid-search).
|
||||
A slice of a document, sized for retrieval. Search matches at the chunk level, so you get the relevant passage instead of a 40-page document. On memory search you pull them in with `searchMode: "hybrid"`; document search returns matching chunks directly. How chunks, memories, and the graph combine at query time is the subject of [hybrid search](/concepts/hybrid-search).
|
||||
|
||||
### Dreaming
|
||||
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@ import Supermemory from "supermemory";
|
|||
|
||||
const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
|
||||
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: "Sarah's favorite color is red",
|
||||
containerTag: "user_4f8a",
|
||||
});
|
||||
|
||||
// three weeks later
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: "Sarah said she's over red — black is her favorite now",
|
||||
containerTag: "user_4f8a",
|
||||
});
|
||||
|
|
@ -40,13 +40,13 @@ from supermemory import Supermemory
|
|||
|
||||
client = Supermemory()
|
||||
|
||||
client.memories.add(
|
||||
client.add(
|
||||
content="Sarah's favorite color is red",
|
||||
container_tag="user_4f8a",
|
||||
)
|
||||
|
||||
# three weeks later
|
||||
client.memories.add(
|
||||
client.add(
|
||||
content="Sarah said she's over red — black is her favorite now",
|
||||
container_tag="user_4f8a",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import Supermemory from "supermemory";
|
|||
|
||||
const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
|
||||
|
||||
const doc = await client.memories.add({
|
||||
const doc = await client.add({
|
||||
content:
|
||||
"Notes from the Tokyo offsite: Sarah presented the Q3 roadmap to the exec team and it landed well. She's currently our design lead.",
|
||||
containerTag: "user_4f8a",
|
||||
|
|
@ -32,7 +32,7 @@ from supermemory import Supermemory
|
|||
|
||||
client = Supermemory()
|
||||
|
||||
doc = client.memories.add(
|
||||
doc = client.add(
|
||||
content="Notes from the Tokyo offsite: Sarah presented the Q3 roadmap to the exec team and it landed well. She's currently our design lead.",
|
||||
container_tag="user_4f8a",
|
||||
)
|
||||
|
|
@ -50,7 +50,6 @@ curl -X POST "https://api.supermemory.ai/v3/documents" \
|
|||
|
||||
</CodeGroup>
|
||||
|
||||
<!-- CONFIRM: python — every Python tab on this page mirrors the published pypi pattern; verify against the released Python SDK -->
|
||||
<!-- CONFIRM: add response shape { id, status } — not covered by SDK-TRUTH -->
|
||||
|
||||
The call returns immediately with an `id` and a status of `queued`. Processing happens asynchronously — you don't wait on ingestion to serve your users.
|
||||
|
|
@ -78,22 +77,22 @@ Poll the document until it's done:
|
|||
<CodeGroup>
|
||||
|
||||
```typescript TypeScript
|
||||
let status = await client.memories.get(doc.id);
|
||||
let status = await client.documents.get(doc.id);
|
||||
|
||||
while (status.status !== "done" && status.status !== "failed") {
|
||||
await new Promise((r) => setTimeout(r, 2000));
|
||||
status = await client.memories.get(doc.id);
|
||||
status = await client.documents.get(doc.id);
|
||||
}
|
||||
```
|
||||
|
||||
```python Python
|
||||
import time
|
||||
|
||||
status = client.memories.get(doc.id)
|
||||
status = client.documents.get(doc.id)
|
||||
|
||||
while status.status not in ("done", "failed"):
|
||||
time.sleep(2)
|
||||
status = client.memories.get(doc.id)
|
||||
status = client.documents.get(doc.id)
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
|
|
@ -165,7 +164,7 @@ This is where supermemory stops behaving like a vector store. A second document
|
|||
<CodeGroup>
|
||||
|
||||
```typescript TypeScript
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content:
|
||||
"Sarah's being promoted to VP of Product. She starts the new role next month.",
|
||||
containerTag: "user_4f8a",
|
||||
|
|
@ -173,7 +172,7 @@ await client.memories.add({
|
|||
```
|
||||
|
||||
```python Python
|
||||
client.memories.add(
|
||||
client.add(
|
||||
content="Sarah's being promoted to VP of Product. She starts the new role next month.",
|
||||
container_tag="user_4f8a",
|
||||
)
|
||||
|
|
@ -249,8 +248,6 @@ curl -X DELETE "https://api.supermemory.ai/v4/memories" \
|
|||
|
||||
</CodeGroup>
|
||||
|
||||
<!-- CONFIRM: `reason` field on DELETE /v4/memories — not in SDK-TRUTH; verify or drop -->
|
||||
|
||||
Forgotten memories stop appearing in search — unless you ask for them back with `include: { forgottenMemories: true }` on `client.search.memories`. That's the escape hatch when a forget was wrong.
|
||||
|
||||
For bulk cleanup there's `POST /v4/memories/forget-matching`: you give it a natural-language query ("forget everything about Project Titan"), an agent searches the container and soft-forgets the matches. It caps at 100 memories per call by default (500 max, via `maxForget`), and `dryRun: true` returns what *would* be forgotten without touching anything. Always dry-run first:
|
||||
|
|
@ -271,11 +268,11 @@ curl -X POST "https://api.supermemory.ai/v4/memories/forget-matching" \
|
|||
<CodeGroup>
|
||||
|
||||
```typescript TypeScript
|
||||
await client.memories.delete("doc_x1k2m9");
|
||||
await client.documents.delete("doc_x1k2m9");
|
||||
```
|
||||
|
||||
```python Python
|
||||
client.memories.delete("doc_x1k2m9")
|
||||
client.documents.delete("doc_x1k2m9")
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
|
|
|
|||
|
|
@ -86,6 +86,51 @@ The response is ranked memories, not chunks:
|
|||
|
||||
Search returns the top N — there's no pagination. If you need to walk everything in a container, list documents instead.
|
||||
|
||||
## Blend in document chunks: `searchMode`
|
||||
|
||||
By default, `search.memories` returns memories only — derived facts, no raw chunks. `searchMode` is the mode selector that decides what comes back:
|
||||
|
||||
<CodeGroup>
|
||||
```typescript TypeScript
|
||||
const results = await client.search.memories({
|
||||
q: "what does Sarah do at the company?",
|
||||
containerTag: "user_4f8a",
|
||||
searchMode: "hybrid", // memories + the chunks that back them
|
||||
});
|
||||
```
|
||||
|
||||
```python Python
|
||||
results = client.search.memories(
|
||||
q="what does Sarah do at the company?",
|
||||
container_tag="user_4f8a",
|
||||
search_mode="hybrid",
|
||||
)
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
curl -X POST "https://api.supermemory.ai/v4/search" \
|
||||
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"q": "what does Sarah do at the company?",
|
||||
"containerTag": "user_4f8a",
|
||||
"searchMode": "hybrid"
|
||||
}'
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
The three modes:
|
||||
|
||||
| `searchMode` | Returns |
|
||||
|---|---|
|
||||
| `memories` (default) | Derived memories only |
|
||||
| `hybrid` | Memories **and** the document chunks that back them |
|
||||
| `documents` | Document chunks only — no memories |
|
||||
|
||||
If your search results seem biased toward one source, or you're getting facts back but none of the underlying chunk content to cite, this is the knob you're missing. `memories` is deliberately lean. Switch to `hybrid` when you want the fact and the passage it came from in a single v4 call, or `documents` when you only need raw chunks.
|
||||
|
||||
`include: { chunks: true }` does the same thing and is kept for back-compat — it auto-switches the call to `hybrid`. It's deprecated; reach for `searchMode: "hybrid"` in new code.
|
||||
|
||||
## Search documents
|
||||
|
||||
When you want the source content itself — RAG, citations, "find the clause" — search documents:
|
||||
|
|
@ -235,7 +280,6 @@ const results = await client.search.memories({
|
|||
containerTag: "user_4f8a",
|
||||
include: {
|
||||
documents: true, // source document + its metadata
|
||||
chunks: true, // relevant chunks from those documents
|
||||
relatedMemories: true, // graph neighbors of each memory
|
||||
summaries: true, // document summaries
|
||||
},
|
||||
|
|
@ -248,7 +292,6 @@ results = client.search.memories(
|
|||
container_tag="user_4f8a",
|
||||
include={
|
||||
"documents": True,
|
||||
"chunks": True,
|
||||
"related_memories": True,
|
||||
"summaries": True,
|
||||
},
|
||||
|
|
@ -264,7 +307,6 @@ curl -X POST "https://api.supermemory.ai/v4/search" \
|
|||
"containerTag": "user_4f8a",
|
||||
"include": {
|
||||
"documents": true,
|
||||
"chunks": true,
|
||||
"relatedMemories": true,
|
||||
"summaries": true
|
||||
}
|
||||
|
|
@ -272,8 +314,6 @@ curl -X POST "https://api.supermemory.ai/v4/search" \
|
|||
```
|
||||
</CodeGroup>
|
||||
|
||||
<!-- CONFIRM: python — include kwarg shape (dict with snake_case keys) unverified against pypi SDK -->
|
||||
|
||||
The one that catches people: **metadata lives on documents, not memories.** A memory's `metadata` field reflects its source document — so if you need document metadata (titles, your custom fields) alongside memory results, set `include: { documents: true }` rather than making a second call.
|
||||
|
||||
Two more worth knowing:
|
||||
|
|
@ -281,7 +321,7 @@ Two more worth knowing:
|
|||
- `relatedMemories` pulls in graph neighbors — memories connected to your hits. If your recall feels like it's returning the fact but missing its context, this is usually the fix.
|
||||
- `forgottenMemories` includes memories that were explicitly forgotten or expired past their TTL. Off by default, which is what you want; turn it on for audit or debugging views. [Graph memory](/concepts/graph-memory) covers how forgetting works.
|
||||
|
||||
`include: { chunks: true }` is also the fast path for "memories plus supporting evidence": one v4 call instead of a memory search followed by per-document fetches.
|
||||
For "memories plus supporting evidence" — the fact and the chunk it came from in one v4 call instead of a memory search followed by per-document fetches — use [`searchMode: "hybrid"`](#blend-in-document-chunks-searchmode). (`include: { chunks: true }` still works and auto-switches to hybrid, but it's deprecated back-compat.)
|
||||
|
||||
## Filter with metadata
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import Supermemory from "supermemory";
|
|||
const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
|
||||
|
||||
// POST /v3/documents
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: "Sarah's being promoted to VP of Product in March",
|
||||
containerTag: "user_4f8a", // the isolation boundary
|
||||
metadata: {
|
||||
|
|
@ -96,7 +96,7 @@ Agent role is a dimension, not a boundary. The fix:
|
|||
|
||||
```typescript
|
||||
// one container per tenant, role as metadata
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: "Customer confirmed they're migrating off Postgres by Q3",
|
||||
containerTag: "org_acme",
|
||||
metadata: { agent_role: "support" },
|
||||
|
|
@ -239,7 +239,7 @@ The default architecture. One container per user, tag derived from your own auth
|
|||
```typescript
|
||||
const tag = `user_${session.user.id}`;
|
||||
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: message.content,
|
||||
containerTag: tag,
|
||||
customId: conversationId, // same conversation, same document
|
||||
|
|
@ -261,14 +261,14 @@ The hybrid every team product needs: facts the whole org should know, plus facts
|
|||
|
||||
```typescript
|
||||
// shared knowledge → the org container
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: "We ship releases on Thursdays; hotfixes anytime",
|
||||
containerTag: "org_acme",
|
||||
metadata: { team: "platform" },
|
||||
});
|
||||
|
||||
// private facts → the user's container
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: "Priya prefers async standups and no meetings before 10am",
|
||||
containerTag: "user_priya",
|
||||
});
|
||||
|
|
@ -298,7 +298,7 @@ Multiple agents serving one tenant share that tenant's container (the anti-patte
|
|||
3. **A handoff-summary memory with a stable `customId`.** When one agent hands off to another, it writes a summary of state; the stable ID means each handoff updates the same document instead of piling up near-duplicates:
|
||||
|
||||
```typescript
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: "Handoff: customer verified, refund approved for $240, awaiting card confirmation",
|
||||
containerTag: "org_acme",
|
||||
customId: "handoff_ticket_8813", // same id on every update → one document
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ for (const conn of connections) {
|
|||
}
|
||||
|
||||
// then verify the imported documents themselves
|
||||
const docs = await client.memories.list({
|
||||
const docs = await client.documents.list({
|
||||
containerTags: ['user_4f8a'],
|
||||
});
|
||||
```
|
||||
|
|
@ -122,7 +122,7 @@ for conn in connections:
|
|||
print(conn.provider, conn.email, conn.created_at)
|
||||
|
||||
# then verify the imported documents themselves
|
||||
docs = client.memories.list(container_tags=['user_4f8a'])
|
||||
docs = client.documents.list(container_tags=['user_4f8a'])
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@
|
|||
"python",
|
||||
"curl"
|
||||
]
|
||||
},
|
||||
"openapi": "https://api.supermemory.ai/v3/openapi"
|
||||
}
|
||||
},
|
||||
"colors": {
|
||||
"dark": "#1E3A8A",
|
||||
|
|
@ -291,8 +290,7 @@
|
|||
"anchors": [
|
||||
{
|
||||
"anchor": "API Reference",
|
||||
"icon": "unplug",
|
||||
"openapi": "https://api.supermemory.ai/v3/openapi"
|
||||
"icon": "unplug"
|
||||
}
|
||||
],
|
||||
"tab": "API Reference"
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import Supermemory from "supermemory";
|
|||
|
||||
const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
|
||||
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: "Sarah's being promoted to VP of Product",
|
||||
containerTag: "user_4f8a",
|
||||
});
|
||||
|
|
@ -32,7 +32,7 @@ from supermemory import Supermemory
|
|||
|
||||
client = Supermemory()
|
||||
|
||||
client.memories.add(
|
||||
client.add(
|
||||
content="Sarah's being promoted to VP of Product",
|
||||
container_tag="user_4f8a",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import Supermemory from "supermemory";
|
|||
const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
|
||||
|
||||
// add — POST /v3/documents
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: "Sarah's being promoted to VP of Product",
|
||||
containerTag: "user_4f8a", // singular, always
|
||||
metadata: { channel: "slack" }, // dimensions within the boundary
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ data = mem0.get_memory_export(memory_export_id=export["id"])
|
|||
supermemory = Supermemory(api_key="your_supermemory_api_key")
|
||||
for memory in data["memories"]:
|
||||
if memory.get("content"):
|
||||
supermemory.memories.add(
|
||||
supermemory.add(
|
||||
content=memory["content"],
|
||||
container_tag="imported_from_mem0"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -183,8 +183,6 @@ curl -X POST "https://api.supermemory.ai/v4/search" \
|
|||
|
||||
</CodeGroup>
|
||||
|
||||
<!-- CONFIRM: python — kwargs mirrored from the patterns/overview page; verify against the published pypi package -->
|
||||
|
||||
When the configuration is right, results read like operational knowledge:
|
||||
|
||||
```json
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ Then add the whole window with a `customId` that names the session:
|
|||
<CodeGroup>
|
||||
|
||||
```ts TypeScript
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: transcript,
|
||||
containerTag: "user_4f8a",
|
||||
customId: "session_user_4f8a_1752742800",
|
||||
|
|
|
|||
|
|
@ -134,9 +134,7 @@ curl -X POST "https://api.supermemory.ai/v4/search" \
|
|||
|
||||
</CodeGroup>
|
||||
|
||||
<!-- CONFIRM: python — search.memories kwargs mirrored from published search page -->
|
||||
|
||||
And the reverse works too: `client.memories.add({ content, containerTag: "team_growth" })` puts a memory into the same brain the team is asking questions of. If they want to feed it programmatically — internal tools, ETL from systems without a connector — the [ingestion patterns](/patterns/ingestion) page covers how to do that well.
|
||||
And the reverse works too: `client.add({ content, containerTag: "team_growth" })` puts a memory into the same brain the team is asking questions of. If they want to feed it programmatically — internal tools, ETL from systems without a connector — the [ingestion patterns](/patterns/ingestion) page covers how to do that well.
|
||||
|
||||
That's the whole idea: the same engine your engineers build on, with a door your entire team can walk through.
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ Instead, give each session a `customId` and send the conversation to it as it gr
|
|||
<CodeGroup>
|
||||
|
||||
```typescript TypeScript
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: `user: My daughter Maya got into NYU — she starts in the fall.
|
||||
assistant: Congratulations! Is she excited about New York?
|
||||
user: Thrilled. We're flying out August 20th to move her in.`,
|
||||
|
|
@ -54,8 +54,6 @@ curl -X POST "https://api.supermemory.ai/v3/documents" \
|
|||
|
||||
</CodeGroup>
|
||||
|
||||
{/* CONFIRM: python — client.add signature mirrored from quickstart.mdx; no Python SDK in repos to verify against */}
|
||||
|
||||
When more messages arrive, send them to the same `customId` — either the new messages alone or the full updated transcript. Supermemory links them to the existing document and processes only what's new, so you're not re-paying for the turns it already saw.
|
||||
|
||||
This is why full-conversation ingestion is cheaper, not only better: fifty turns as one growing document is one document deriving memories from coherent context. Fifty turns as fifty documents is fifty isolated ingestion jobs, each missing the context the others hold.
|
||||
|
|
@ -63,7 +61,7 @@ This is why full-conversation ingestion is cheaper, not only better: fifty turns
|
|||
Two details worth knowing:
|
||||
|
||||
- **Include both sides.** Assistant turns carry facts too — what was recommended, what was agreed, what the user confirmed. Strip the assistant and you lose half the session's meaning. (What *not* to learn from assistant turns — unconfirmed claims — is covered in the [AI companion pattern](/patterns/ai-companion).)
|
||||
- **`customId` constraints:** max 100 characters, alphanumeric with hyphens, underscores, and colons. Use an ID from your own database — that's what it's for. <!-- CONFIRM: the 100-char + charset rule is what file uploads validate; the batch endpoint validates 255 chars and single add doesn't validate length — which limit do we document? -->
|
||||
- **`customId` constraints:** max 100 characters, alphanumeric with hyphens, underscores, and dots. Use an ID from your own database — that's what it's for.
|
||||
|
||||
## Close the window at ~50 turns or ~4 hours
|
||||
|
||||
|
|
@ -73,7 +71,7 @@ A session shouldn't grow forever. Past a point, one document stops being "a cohe
|
|||
// derive the window from your session, not a global counter
|
||||
const windowId = `chat_8821_w${session.windowIndex}`;
|
||||
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: session.transcriptSinceWindowStart,
|
||||
customId: windowId,
|
||||
containerTag: "user_4f8a",
|
||||
|
|
@ -203,7 +201,7 @@ The full error and limit reference is at [errors and limits](/errors-and-limits)
|
|||
**Poll status before you judge the results.** Batch acceptance means *queued*, not *searchable*. Each document moves through `queued → extracting → chunking → embedding → indexing → done`, and its memories are queryable once it hits `done`. To verify an import, poll the IDs the batch returned:
|
||||
|
||||
```typescript
|
||||
const doc = await client.memories.get("doc_x2ka91");
|
||||
const doc = await client.documents.get("doc_x2ka91");
|
||||
if (doc.status === "done") {
|
||||
// memories from this document are now searchable
|
||||
} else if (doc.status === "failed") {
|
||||
|
|
@ -220,7 +218,7 @@ There's no bulk "is my whole import done" call yet — poll the IDs you care abo
|
|||
Where it can matter: two writes to the *same* `customId` fired in quick succession. In rare cases the second can be picked up while the first is still processing, and the updates race. Two ways to make that impossible:
|
||||
|
||||
- **Send the full accumulated transcript each time** instead of only the delta. Then the latest write contains everything, and whichever write lands last is complete on its own. It's the least machinery and costs little extra — unchanged content is recognized and not reprocessed.
|
||||
- **Wait for `done` before the next write** to the same `customId`, polling `client.memories.get(id)`. Use this when deltas are large and you'd rather sequence than resend.
|
||||
- **Wait for `done` before the next write** to the same `customId`, polling `client.documents.get(id)`. Use this when deltas are large and you'd rather sequence than resend.
|
||||
|
||||
For backfills, the batch endpoint sidesteps the question: one request, distinct `customId`s per document, no interleaved writes to the same session.
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import Supermemory from "supermemory";
|
|||
const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
|
||||
|
||||
// the researcher writes what it found
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: "Competitor pricing scan: Vanta starts at $7,500/yr, Drata is quote-only, both gate SSO behind enterprise tiers...",
|
||||
containerTag: "proj_atlas",
|
||||
customId: "ep_2041_research_notes",
|
||||
|
|
@ -34,7 +34,7 @@ from supermemory import Supermemory
|
|||
client = Supermemory()
|
||||
|
||||
# the researcher writes what it found
|
||||
client.memories.add(
|
||||
client.add(
|
||||
content="Competitor pricing scan: Vanta starts at $7,500/yr, Drata is quote-only, both gate SSO behind enterprise tiers...",
|
||||
container_tag="proj_atlas",
|
||||
custom_id="ep_2041_research_notes",
|
||||
|
|
@ -105,7 +105,7 @@ Each agent writes its full output — findings, drafts, decisions, not only conc
|
|||
|
||||
```typescript TypeScript
|
||||
// the planner ships its plan into the same container
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: "# Launch plan for Atlas\n\nWeek 1: undercut Vanta's entry tier at $5,900...",
|
||||
containerTag: "proj_atlas",
|
||||
customId: "ep_2041_plan_v1",
|
||||
|
|
@ -114,7 +114,7 @@ await client.memories.add({
|
|||
```
|
||||
|
||||
```python Python
|
||||
client.memories.add(
|
||||
client.add(
|
||||
content="# Launch plan for Atlas\n\nWeek 1: undercut Vanta's entry tier at $5,900...",
|
||||
container_tag="proj_atlas",
|
||||
custom_id="ep_2041_plan_v1",
|
||||
|
|
@ -154,7 +154,7 @@ curl -X POST "https://api.supermemory.ai/v3/documents" \
|
|||
}'
|
||||
```
|
||||
|
||||
The TypeScript SDK doesn't type `filterByMetadata` yet — send it over REST until it lands. <!-- CONFIRM: filterByMetadata not in supermemory@3.10.0 TS typings; verify current SDK before removing this caveat --> See [filtered writes](/add-memories#filtered-writes) for how the context scoping works.
|
||||
`filterByMetadata` isn't in the `supermemory@4.x` TS typings yet — the backend accepts it, so send it over REST until it lands. See [filtered writes](/add-memories#filtered-writes) for how the context scoping works.
|
||||
|
||||
## Filter reads per role
|
||||
|
||||
|
|
@ -270,7 +270,7 @@ Filtered search gives the next agent everything upstream — but "everything" is
|
|||
|
||||
```typescript TypeScript
|
||||
// researcher is done — write the baton
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: `# Research handoff — Atlas ep_2041
|
||||
|
||||
## Decided
|
||||
|
|
@ -288,7 +288,7 @@ await client.memories.add({
|
|||
```
|
||||
|
||||
```python Python
|
||||
client.memories.add(
|
||||
client.add(
|
||||
content=handoff_markdown,
|
||||
container_tag="proj_atlas",
|
||||
custom_id="ep_2041_handoff_research",
|
||||
|
|
@ -340,24 +340,24 @@ Default to keeping episode memories. Cross-episode recall is most of the payoff
|
|||
|
||||
```typescript TypeScript
|
||||
// find everything ep_2041 wrote...
|
||||
const { memories } = await client.memories.list({
|
||||
const { memories } = await client.documents.list({
|
||||
containerTags: ["proj_atlas"],
|
||||
filters: { AND: [{ key: "episode", value: "ep_2041" }] },
|
||||
limit: 100,
|
||||
});
|
||||
|
||||
// ...and remove it
|
||||
await Promise.all(memories.map((m) => client.memories.delete(m.id)));
|
||||
await Promise.all(memories.map((m) => client.documents.delete(m.id)));
|
||||
```
|
||||
|
||||
```python Python
|
||||
page = client.memories.list(
|
||||
page = client.documents.list(
|
||||
container_tags=["proj_atlas"],
|
||||
filters={"AND": [{"key": "episode", "value": "ep_2041"}]},
|
||||
limit=100,
|
||||
)
|
||||
for m in page.memories:
|
||||
client.memories.delete(m.id)
|
||||
client.documents.delete(m.id)
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ import Supermemory from "supermemory";
|
|||
|
||||
const client = new Supermemory({ apiKey: scopedKey }); // safe in the browser
|
||||
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: sessionTranscript, // the whole session, both sides, as markdown
|
||||
containerTag: "user_4f8a",
|
||||
customId: "user_4f8a_session_0093",
|
||||
|
|
@ -157,8 +157,6 @@ curl -X POST "https://api.supermemory.ai/v3/documents" \
|
|||
|
||||
</CodeGroup>
|
||||
|
||||
<!-- CONFIRM: python — client.add with custom_id kwarg mirrored from published add-memories examples; verify against the pypi package -->
|
||||
|
||||
Notice what `metadata` is doing: `channel` and `plan` are dimensions *within* the user's boundary — things you'll filter by at search time. They're metadata precisely because they don't need walls between them. If you catch yourself creating tags like `user_4f8a_web` and `user_4f8a_mobile`, stop: that's two containers that can't see each other, which means the assistant on mobile forgets what the user said on web. Tag = boundary, metadata = dimension. The [permissioning page](/concepts/permissioning) has the full anti-pattern gallery.
|
||||
|
||||
The ingestion side has its own craft — session windows, markdown over raw JSON, extracting from both turns. That's all in [ingestion best practices](/patterns/ingestion); it applies unchanged here.
|
||||
|
|
@ -175,7 +173,7 @@ Writes route by audience. Private chat goes to the user container as above. Shar
|
|||
<CodeGroup>
|
||||
|
||||
```typescript TypeScript
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: "Decision from planning: Acme is standardizing on usage-based pricing for Q4.",
|
||||
containerTag: "org_acme",
|
||||
metadata: { author: "user_4f8a" },
|
||||
|
|
@ -199,7 +197,7 @@ curl -X POST "https://api.supermemory.ai/v3/documents" \
|
|||
|
||||
</CodeGroup>
|
||||
|
||||
<!-- CONFIRM: filterByMetadata is accepted by POST /v3/documents (verified in backend schema) but is not in the TS SDK's typed AddParams — confirm typings before publishing the TS tab as-is -->
|
||||
One caveat on that TypeScript tab: `filterByMetadata` isn't in the `supermemory@4.x` typed `AddParams` yet. The backend accepts it, so send it over REST until it's typed.
|
||||
|
||||
`filterByMetadata` is a [filtered write](/add-memories#filtered-writes): new memories are generated using only existing memories that match the filter as context. Without it, everything in `org_acme` is fair game as ingestion context, and a shared container full of many people's notes starts blending them. Pair it with `entityContext` on the org container — a one-time description like "This container holds Acme's shared workspace knowledge; individual members are contributors, not the subject" — so extraction knows the container is about the org, not any one person. `entityContext` mechanics live in [Customization](/concepts/customization).
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ import Supermemory from "supermemory";
|
|||
const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
|
||||
|
||||
// write path: the full session, both sides of the conversation
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: sessionTranscript, // markdown ingests better than raw JSON
|
||||
containerTag: "user_4f8a",
|
||||
customId: "user_4f8a_session_0093",
|
||||
|
|
@ -120,8 +120,6 @@ curl -X POST "https://api.supermemory.ai/v4/search" \
|
|||
|
||||
</CodeGroup>
|
||||
|
||||
<!-- CONFIRM: python — client.add signature mirrored from add-memories.mdx; verify custom_id kwarg is accepted -->
|
||||
|
||||
Notice what you *didn't* do: you never wrote a memory by hand. You don't decide what's worth remembering — the ingestion pipeline derives memories from what you feed it, connects them in the [graph](/concepts/graph-memory), and keeps the profile current. Your real design decisions are the ones this page is about: what to feed it, and how to partition it.
|
||||
|
||||
## Know the primitives
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ const facts = [
|
|||
];
|
||||
|
||||
for (const content of facts) {
|
||||
await client.memories.add({ content, containerTag: "user_4f8a" });
|
||||
await client.add({ content, containerTag: "user_4f8a" });
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -234,7 +234,7 @@ while (true) {
|
|||
console.log(`assistant: ${answer}`);
|
||||
|
||||
// store the exchange so it becomes memory for next time
|
||||
await memory.memories.add({
|
||||
await memory.add({
|
||||
content: `user: ${question}\nassistant: ${answer}`,
|
||||
containerTag: "user_4f8a",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ const client = new Supermemory({
|
|||
baseURL: "http://localhost:6767",
|
||||
})
|
||||
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: "I'm Dhravya. I love building dev tools and I'm allergic to peanuts.",
|
||||
containerTag: "user_dhravya",
|
||||
})
|
||||
|
|
@ -81,7 +81,7 @@ client = Supermemory(
|
|||
base_url="http://localhost:6767",
|
||||
)
|
||||
|
||||
client.memories.add(
|
||||
client.add(
|
||||
content="I'm Dhravya. I love building dev tools and I'm allergic to peanuts.",
|
||||
container_tag="user_dhravya",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ Deletion is permanent and there's no recovery — gate this behind your own conf
|
|||
|
||||
Supermemory has two removal mechanisms, and for compliance work you must use the right one:
|
||||
|
||||
- **Delete (v3, document-level) is the erasure path.** `DELETE /v3/documents/:id` (`client.memories.delete(id)` in the SDK) removes one document; `DELETE /v3/documents/bulk` removes everything under a container tag. This is hard removal — use it for right-to-erasure requests.
|
||||
- **Delete (v3, document-level) is the erasure path.** `DELETE /v3/documents/:id` (`client.documents.delete(id)` in the SDK) removes one document; `DELETE /v3/documents/bulk` removes everything under a container tag. This is hard removal — use it for right-to-erasure requests.
|
||||
- **Forget (v4, memory-level) is a product feature, not erasure.** `DELETE /v4/memories` and `POST /v4/memories/forget-matching` soft-delete individual derived facts: the memory stops appearing in search results but is preserved in the database (`isForgotten: true`), so the system can reason about what it used to believe. That's the right tool for "the user corrected an outdated fact" — and the wrong tool for "the user invoked their legal right to be forgotten."
|
||||
|
||||
For a single stale fact rather than a whole user, forget is what you want:
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ Supermemory's REST API has two live versions, v3 and v4, and both are current. N
|
|||
|
||||
Here's the seam in one program:
|
||||
|
||||
{/* CONFIRM: python — client.add vs client.memories.add naming is inconsistent across published docs; mirrored add-memories.mdx and search.mdx */}
|
||||
|
||||
<CodeGroup>
|
||||
```typescript TypeScript
|
||||
import Supermemory from "supermemory"
|
||||
|
|
@ -16,7 +14,7 @@ import Supermemory from "supermemory"
|
|||
const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })
|
||||
|
||||
// hits POST /v3/documents
|
||||
await client.memories.add({
|
||||
await client.add({
|
||||
content: "Sarah's being promoted to VP of Product in March",
|
||||
containerTag: "user_4f8a",
|
||||
})
|
||||
|
|
@ -95,12 +93,12 @@ Every document-level and account-level endpoint, all on v3:
|
|||
|
||||
| Endpoint | What it does | TS SDK method |
|
||||
|---|---|---|
|
||||
| `POST /v3/documents` | Add a document | `client.memories.add()` |
|
||||
| `POST /v3/documents/file` | Upload a file | `client.memories.uploadFile()` |
|
||||
| `POST /v3/documents/list` | List documents | `client.memories.list()` |
|
||||
| `GET /v3/documents/{id}` | Get a document | `client.memories.get()` |
|
||||
| `PATCH /v3/documents/{id}` | Update a document | `client.memories.update()` |
|
||||
| `DELETE /v3/documents/{id}` | Delete a document | `client.memories.delete()` |
|
||||
| `POST /v3/documents` | Add a document | `client.add()` |
|
||||
| `POST /v3/documents/file` | Upload a file | `client.documents.uploadFile()` |
|
||||
| `POST /v3/documents/list` | List documents | `client.documents.list()` |
|
||||
| `GET /v3/documents/{id}` | Get a document | `client.documents.get()` |
|
||||
| `PATCH /v3/documents/{id}` | Update a document | `client.documents.update()` |
|
||||
| `DELETE /v3/documents/{id}` | Delete a document | `client.documents.delete()` |
|
||||
| `DELETE /v3/documents/bulk` | Bulk delete (including by container tag) | raw HTTP |
|
||||
| `POST /v3/search` | Search document chunks (RAG-style) | `client.search.documents()` |
|
||||
| `GET` / `PATCH /v3/settings` | Org settings | `client.settings.get()` / `.update()` |
|
||||
|
|
@ -113,7 +111,7 @@ The documents family also includes batch add, fetch by IDs, chunks, and versions
|
|||
|
||||
Two things in this map trip people up, so let's name them:
|
||||
|
||||
**Yes, `client.memories.add()` calls `/v3/documents`.** The SDK is named for the mental model — you add content, supermemory derives memories from it. The route is named for what's actually stored: a document, from which memories are derived. Same operation, two vocabularies. The [glossary](/concepts/glossary) keeps them straight.
|
||||
**Yes, `client.add()` calls `/v3/documents`.** The SDK is named for the mental model — you add content, supermemory derives memories from it. The route is named for what's actually stored: a document, from which memories are derived. Same operation, two vocabularies. The [glossary](/concepts/glossary) keeps them straight.
|
||||
|
||||
**The SDK covers two of the nine v4 endpoints.** `client.search.memories()` and `client.profile()` are the v4 surface in the TS SDK today. The rest — memory update, forget, forget-matching, list, conversations — are raw HTTP for now. They work fine with `fetch`; they don't have typed wrappers yet.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue