mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-08-28 05:25:33 +00:00
### TL;DR
Adds documentation for the Memory Review endpoints and Profile Buckets feature.
### What changed?
**Memory Review (`memory-review.mdx`)**
- Added a new documentation page covering the two inferred memory review endpoints: `GET /v3/container-tags/{containerTag}/inferred` and `POST /v3/container-tags/{containerTag}/inferred/{memoryId}/review`.
- Documents the three review actions (`approve`, `decline`, `undo`) and how each affects search ranking and memory state (`isInference`, `isForgotten`, `reviewStatus`).
- Includes request/response examples in both `fetch` and cURL, a field reference table, error codes, and a collapsible React Query hooks example for building a review UI.
- Registered the new page in `docs.json` under the "Manage Content" group and linked to it from the Memory Operations next steps.
**Profile Buckets (`user-profiles.mdx`)**
- Added a "Profile Buckets" section explaining custom topical categories (`preferences`, `goals`, `work`, etc.) as a complement to `static`/`dynamic` profile sections.
- Documents the `include`, `buckets`, and `filters` query parameters on the profile endpoint.
- Covers the `GET /v4/profile/buckets` endpoint for listing configured bucket definitions, with request/response examples and a field reference.
- Explains the `[Recent]` / `[Summary]` label convention used in bucket and dynamic profile entries.
- Updated the `ProfileResponse` TypeScript interface to mark `static` and `dynamic` as optional and add the `buckets` field.
### How to test?
- Navigate to the docs site and confirm "Memory Review" appears in the sidebar under "Manage Content".
- Verify all code examples render correctly and tabs switch between `fetch` and cURL variants.
- Confirm the React Query accordion expands and displays the TypeScript snippet.
- Check that the Profile Buckets section renders inline within the User Profiles page, including the response JSON blocks and the tip/note callouts.
### Why make this change?
Inferred (derived) graph memories are down-weighted in search until reviewed, but there was no documentation explaining how to surface or act on them. Similarly, profile buckets were a shipped feature with no public-facing docs. These additions give developers the reference material needed to build review UIs and use topical bucket filtering in their integrations.
409 lines
11 KiB
Text
409 lines
11 KiB
Text
---
|
||
title: "User Profiles"
|
||
sidebarTitle: "User Profiles"
|
||
description: "Fetch and use automatically maintained user context"
|
||
icon: "user"
|
||
---
|
||
|
||
User profiles are extremely short summaries of context about an entity (Usually a user, but can be anything) which includes both the *static* facts about them, as well as a few recent episodes.
|
||
|
||
> You can think of these as a dynamic compaction that's done by supermemory in real-time.
|
||
|
||
This profile should be injected into the agent context for truly personalized experiences. To read more, visit [User profiles - Concept](/concepts/user-profiles)
|
||
|
||
Get a user's profile — their static facts and dynamic context — with a single API call.
|
||
|
||
<Tip>
|
||
Profiles are built automatically as you [ingest content](/add-memories). No setup required.
|
||
</Tip>
|
||
|
||
## Quick Start
|
||
|
||
<Tabs>
|
||
<Tab title="TypeScript">
|
||
```typescript
|
||
import Supermemory from 'supermemory';
|
||
|
||
const client = new Supermemory();
|
||
|
||
const { profile } = await client.profile({
|
||
containerTag: "user_123"
|
||
});
|
||
|
||
console.log(profile.static); // Long-term facts
|
||
console.log(profile.dynamic); // Recent context
|
||
```
|
||
</Tab>
|
||
<Tab title="Python">
|
||
```python
|
||
from supermemory import Supermemory
|
||
|
||
client = Supermemory()
|
||
|
||
result = client.profile(container_tag="user_123")
|
||
|
||
print(result.profile.static) # Long-term facts
|
||
print(result.profile.dynamic) # Recent context
|
||
```
|
||
</Tab>
|
||
<Tab title="cURL">
|
||
```bash
|
||
curl -X POST "https://api.supermemory.ai/v4/profile" \
|
||
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"containerTag": "user_123"}'
|
||
```
|
||
</Tab>
|
||
</Tabs>
|
||
|
||
**Response:**
|
||
```json
|
||
{
|
||
"profile": {
|
||
"static": [
|
||
"User is a software engineer",
|
||
"User specializes in Python and React",
|
||
"User prefers dark mode interfaces"
|
||
],
|
||
"dynamic": [
|
||
"User is working on Project Alpha",
|
||
"User recently started learning Rust",
|
||
"User is debugging authentication issues"
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Profile + Search
|
||
|
||
Get profile and search results in one call by adding the `q` parameter:
|
||
|
||
<Tabs>
|
||
<Tab title="TypeScript">
|
||
```typescript
|
||
const result = await client.profile({
|
||
containerTag: "user_123",
|
||
q: "deployment errors"
|
||
});
|
||
|
||
// Profile data
|
||
const { static: facts, dynamic: context } = result.profile;
|
||
|
||
// Search results (only if q was provided)
|
||
const memories = result.searchResults?.results || [];
|
||
```
|
||
</Tab>
|
||
<Tab title="Python">
|
||
```python
|
||
result = client.profile(
|
||
container_tag="user_123",
|
||
q="deployment errors"
|
||
)
|
||
|
||
# Profile data
|
||
facts = result.profile.static
|
||
context = result.profile.dynamic
|
||
|
||
# Search results
|
||
memories = result.search_results.results if result.search_results else []
|
||
```
|
||
</Tab>
|
||
</Tabs>
|
||
|
||
---
|
||
|
||
## Parameters
|
||
|
||
| Parameter | Type | Required | Description |
|
||
|-----------|------|----------|-------------|
|
||
| `containerTag` | string | Yes | User/project identifier |
|
||
| `q` | string | No | Search query (includes search results in response) |
|
||
| `threshold` | 0-1 | No | Filter search results by relevance score |
|
||
| `filters` | object | No | Metadata filters applied to profile and search results |
|
||
| `include` | string[] | No | Sections to return — any of `"static"`, `"dynamic"`, `"buckets"`. Omit to return all |
|
||
| `buckets` | string[] | No | Restrict the `buckets` section to specific keys. Omit for all configured buckets |
|
||
|
||
---
|
||
|
||
## Building Prompts
|
||
|
||
The most common pattern — inject profile into your LLM's system prompt:
|
||
|
||
```typescript
|
||
async function chat(userId: string, message: string) {
|
||
const { profile } = await client.profile({ containerTag: userId });
|
||
|
||
const systemPrompt = `You are assisting a user.
|
||
|
||
ABOUT THE USER:
|
||
${profile.static?.join('\n') || 'No profile yet.'}
|
||
|
||
CURRENT CONTEXT:
|
||
${profile.dynamic?.join('\n') || 'No recent activity.'}
|
||
|
||
Personalize responses to their expertise and preferences.`;
|
||
|
||
return llm.chat({
|
||
messages: [
|
||
{ role: "system", content: systemPrompt },
|
||
{ role: "user", content: message }
|
||
]
|
||
});
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Full Context Pattern
|
||
|
||
Get profile + query-specific memories in one call:
|
||
|
||
```typescript
|
||
async function getContext(userId: string, query: string) {
|
||
const result = await client.profile({
|
||
containerTag: userId,
|
||
q: query,
|
||
threshold: 0.6
|
||
});
|
||
|
||
return `
|
||
User Background:
|
||
${result.profile.static.join('\n')}
|
||
|
||
Current Context:
|
||
${result.profile.dynamic.join('\n')}
|
||
|
||
Relevant Memories:
|
||
${result.searchResults?.results.map(m => m.memory).join('\n') || 'None'}
|
||
`;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Profile Buckets
|
||
|
||
Buckets are **custom topical categories** for a profile — an axis that sits alongside
|
||
`static` and `dynamic`. Where static/dynamic split facts by how long-lived they are,
|
||
buckets group them by subject (e.g. `preferences`, `goals`, `work`). As content is
|
||
ingested, a classifier assigns each memory to the buckets it matches, so you can pull
|
||
just the slice of context a given surface needs.
|
||
|
||
Every org starts with a built-in `preferences` bucket. You can define your own at the
|
||
organization or space level in your console settings; space-level buckets are
|
||
**add-only** — a container tag inherits all org buckets and may add more, but cannot
|
||
disable them.
|
||
|
||
### Requesting buckets
|
||
|
||
Pass `include: ["buckets"]` to return bucket-organized memories, and optionally
|
||
`buckets` to limit the response to specific keys. `include` also lets you skip
|
||
sections you don't need — `["buckets"]` alone omits `static` and `dynamic`.
|
||
|
||
<Tabs>
|
||
<Tab title="fetch">
|
||
```typescript
|
||
const res = await fetch("https://api.supermemory.ai/v4/profile", {
|
||
method: "POST",
|
||
headers: {
|
||
"Authorization": `Bearer ${API_KEY}`,
|
||
"Content-Type": "application/json"
|
||
},
|
||
body: JSON.stringify({
|
||
containerTag: "user_123",
|
||
include: ["buckets"],
|
||
buckets: ["preferences", "goals"] // optional — omit for all buckets
|
||
})
|
||
});
|
||
|
||
const { profile } = await res.json();
|
||
console.log(profile.buckets.preferences);
|
||
console.log(profile.buckets.goals);
|
||
```
|
||
</Tab>
|
||
<Tab title="cURL">
|
||
```bash
|
||
curl -X POST "https://api.supermemory.ai/v4/profile" \
|
||
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"containerTag": "user_123",
|
||
"include": ["buckets"],
|
||
"buckets": ["preferences", "goals"]
|
||
}'
|
||
```
|
||
</Tab>
|
||
</Tabs>
|
||
|
||
**Response:**
|
||
```json
|
||
{
|
||
"profile": {
|
||
"buckets": {
|
||
"preferences": [
|
||
"[Summary] Prefers concise, technical answers and dark-mode tooling",
|
||
"[Recent] Switched their editor to Zed"
|
||
],
|
||
"goals": [
|
||
"[Recent] Wants to ship the billing revamp this quarter"
|
||
]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
<Note>
|
||
**`[Recent]` and `[Summary]` labels.** To keep profiles dense, an entity's older
|
||
memories are periodically aggregated into a short synthesis. Entries prefixed
|
||
`[Summary]` are that aggregated context; entries prefixed `[Recent]` were ingested
|
||
since the last aggregation and aren't summarized yet. The `dynamic` section uses the
|
||
same `[Recent]` prefix (plus a `[YYYY-MM-DD]` date). Strip the prefixes if you only
|
||
want raw text, or keep them to signal recency to your model.
|
||
</Note>
|
||
|
||
### List bucket definitions
|
||
|
||
To see which buckets are configured for a container tag (org buckets merged with any
|
||
space-level additions), call `/v4/profile/buckets`:
|
||
|
||
<Tabs>
|
||
<Tab title="fetch">
|
||
```typescript
|
||
const res = await fetch("https://api.supermemory.ai/v4/profile/buckets", {
|
||
method: "POST",
|
||
headers: {
|
||
"Authorization": `Bearer ${API_KEY}`,
|
||
"Content-Type": "application/json"
|
||
},
|
||
body: JSON.stringify({ containerTag: "user_123" })
|
||
});
|
||
|
||
const { buckets } = await res.json();
|
||
// [{ key: "preferences", description: "..." }, ...]
|
||
```
|
||
</Tab>
|
||
<Tab title="cURL">
|
||
```bash
|
||
curl -X POST "https://api.supermemory.ai/v4/profile/buckets" \
|
||
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"containerTag": "user_123"}'
|
||
```
|
||
</Tab>
|
||
</Tabs>
|
||
|
||
**Response:**
|
||
```json
|
||
{
|
||
"buckets": [
|
||
{
|
||
"key": "preferences",
|
||
"description": "Explicit first-person preferences the person directly stated."
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
| Field | Type | Description |
|
||
|-------|------|-------------|
|
||
| `buckets[].key` | string | Stable slug, also stored on each memory. Lowercase alphanumeric with `-`/`_`, 1–64 chars |
|
||
| `buckets[].description` | string | What belongs in the bucket — guides the ingestion classifier |
|
||
|
||
<Tip>
|
||
Bucket descriptions steer classification. A precise description ("Explicit
|
||
first-person preferences only — exclude inferred traits") yields cleaner buckets than
|
||
a vague one. `static` and `dynamic` are reserved and can't be used as bucket keys.
|
||
</Tip>
|
||
|
||
---
|
||
|
||
## Framework Examples
|
||
|
||
<Accordion title="Express.js Middleware">
|
||
```typescript
|
||
async function withProfile(req, res, next) {
|
||
if (!req.user?.id) return next();
|
||
|
||
try {
|
||
const { profile } = await client.profile({
|
||
containerTag: req.user.id
|
||
});
|
||
req.userProfile = profile;
|
||
} catch (e) {
|
||
req.userProfile = null;
|
||
}
|
||
next();
|
||
}
|
||
|
||
app.use(withProfile);
|
||
|
||
app.post('/chat', (req, res) => {
|
||
// req.userProfile available in all routes
|
||
});
|
||
```
|
||
</Accordion>
|
||
|
||
<Accordion title="Next.js API Route">
|
||
```typescript
|
||
// app/api/chat/route.ts
|
||
export async function POST(req: NextRequest) {
|
||
const { userId, message } = await req.json();
|
||
|
||
const { profile } = await client.profile({
|
||
containerTag: userId
|
||
});
|
||
|
||
const response = await generateResponse(message, profile);
|
||
return NextResponse.json({ response });
|
||
}
|
||
```
|
||
</Accordion>
|
||
|
||
<Accordion title="AI SDK Integration">
|
||
```typescript
|
||
import { withSupermemory } from "@supermemory/tools/ai-sdk"
|
||
import { openai } from "@ai-sdk/openai"
|
||
|
||
// Profiles automatically injected
|
||
const model = withSupermemory(openai("gpt-4"), {
|
||
containerTag: "user-123",
|
||
customId: "conv-1",
|
||
})
|
||
|
||
const result = await generateText({
|
||
model,
|
||
messages: [{ role: "user", content: "Help with my project" }]
|
||
});
|
||
```
|
||
|
||
See [AI SDK Integration](/integrations/ai-sdk) for details.
|
||
</Accordion>
|
||
|
||
---
|
||
|
||
## Response Schema
|
||
|
||
```typescript
|
||
interface ProfileResponse {
|
||
profile: {
|
||
static?: string[]; // Long-term facts
|
||
dynamic?: string[]; // Recent context
|
||
buckets?: Record<string, string[]>; // Topical buckets, keyed by bucket key
|
||
};
|
||
searchResults?: { // Only if q parameter provided
|
||
results: SearchResult[];
|
||
total: number;
|
||
timing: number;
|
||
};
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Next Steps
|
||
|
||
- [User Profiles Concept](/concepts/user-profiles) — Understand static vs dynamic
|
||
- [Ingesting Content](/add-memories) — Build profiles by adding content
|
||
- [AI SDK Integration](/integrations/ai-sdk) — Automatic profile injection
|