* fix(ci): resolve all CI failures — unversioned integrations, test retries - Move integration docs to separate unversioned docs plugin (docs-integrations/) so new integrations don't need to be duplicated across versioned_docs - Remove integration pages from versioned_docs (v0.3, v0.4) — sidebar entries now use links instead of doc refs - Add missing title/description SEO frontmatter to autogen.md - Add retry logic (2 attempts) to test-doc-examples.sh for transient LLM timeouts - Add pytest-rerunfailures to test-api with --reruns 2 for flaky Gemini-dependent integration tests * ci: retrigger * fix: graph entity inheritance, SyncTaskBackend error propagation, fact_type test regressions - Fix observation entity inheritance in get_graph_data: the unit_entities query only fetched entities for visible observation IDs, not their source memory IDs, so the inheritance loop always found an empty entity_map - Remove error swallowing in SyncTaskBackend._execute_task so test failures surface instead of being silently logged - Wrap remaining consolidation submission call sites with try/except since consolidation is non-critical for those operations - Fix test_sync_backend test to expect errors to propagate - Remove fact_type=["world"] filter from test_document_upsert_behavior and test_mentioned_at_from_context_string (same PR #848 regression) - Remove flaky marker from consolidation test (now deterministic)
167 lines
5.5 KiB
Markdown
167 lines
5.5 KiB
Markdown
---
|
||
sidebar_position: 5
|
||
title: "Vercel Chat SDK Persistent Memory with Hindsight | Integration"
|
||
description: "Give your Vercel Chat SDK bot persistent, per-user memory across Slack, Discord, Teams, and more. Single handler wrapper, no custom plumbing required."
|
||
---
|
||
|
||
# Vercel Chat SDK
|
||
|
||
We built `@vectorize-io/hindsight-chat` to give [Vercel Chat SDK](https://github.com/vercel/chat) bots persistent, per-user memory with a single handler wrapper. The integration works across Slack, Discord, Teams, Google Chat, GitHub, and Linear — no custom plumbing required.
|
||
|
||
[View Changelog →](/changelog/integrations/chat)
|
||
|
||
## Installation
|
||
|
||
```bash
|
||
npm install @vectorize-io/hindsight-chat
|
||
```
|
||
|
||
## Quick Start
|
||
|
||
```typescript
|
||
import { Chat } from 'chat';
|
||
import { HindsightClient } from '@vectorize-io/hindsight-client';
|
||
import { withHindsightChat } from '@vectorize-io/hindsight-chat';
|
||
import { streamText } from 'ai';
|
||
import { openai } from '@ai-sdk/openai';
|
||
|
||
const chat = new Chat({ connectors: [/* your connectors */] });
|
||
const hindsight = new HindsightClient({ apiKey: process.env.HINDSIGHT_API_KEY });
|
||
|
||
chat.onNewMention(
|
||
withHindsightChat(
|
||
{
|
||
client: hindsight,
|
||
bankId: (msg) => msg.author.userId, // per-user memory
|
||
},
|
||
async (thread, message, ctx) => {
|
||
await thread.subscribe();
|
||
|
||
const result = await streamText({
|
||
model: openai('gpt-4o'),
|
||
system: ctx.memoriesAsSystemPrompt(),
|
||
messages: [{ role: 'user', content: message.text }],
|
||
});
|
||
|
||
// Stream the response
|
||
const chunks: string[] = [];
|
||
for await (const chunk of result.textStream) {
|
||
chunks.push(chunk);
|
||
}
|
||
const fullResponse = chunks.join('');
|
||
await thread.post(fullResponse);
|
||
|
||
// Store the conversation in memory
|
||
await ctx.retain(
|
||
`User: ${message.text}\nAssistant: ${fullResponse}`
|
||
);
|
||
}
|
||
)
|
||
);
|
||
```
|
||
|
||
## Configuration
|
||
|
||
### `withHindsightChat(options, handler)`
|
||
|
||
`withHindsightChat` wraps your existing Chat SDK handler and injects memory context automatically. It returns a standard handler `(thread, message) => Promise<void>` so it drops in without changing your handler signature.
|
||
|
||
#### Options
|
||
|
||
| Option | Type | Default | Description |
|
||
|--------|------|---------|-------------|
|
||
| `client` | `HindsightClient` | *required* | Hindsight client instance |
|
||
| `bankId` | `string \| (msg) => string` | *required* | Memory bank ID or resolver function |
|
||
| `recall.enabled` | `boolean` | `true` | Auto-recall memories before handler |
|
||
| `recall.budget` | `'low' \| 'mid' \| 'high'` | `'mid'` | Processing budget for recall |
|
||
| `recall.maxTokens` | `number` | API default | Max tokens for recall results |
|
||
| `recall.types` | `FactType[]` | all | Filter to specific fact types |
|
||
| `recall.includeEntities` | `boolean` | `true` | Include entity observations |
|
||
| `retain.enabled` | `boolean` | `false` | Auto-retain inbound messages |
|
||
| `retain.async` | `boolean` | `true` | Fire-and-forget retain |
|
||
| `retain.tags` | `string[]` | – | Tags for retained memories |
|
||
| `retain.metadata` | `Record<string, string>` | – | Metadata for retained memories |
|
||
|
||
### Context (`ctx`)
|
||
|
||
We inject a third `ctx` argument into your handler that exposes the full Hindsight memory API scoped to the current user's bank:
|
||
|
||
| Property/Method | Description |
|
||
|----------------|-------------|
|
||
| `ctx.bankId` | Resolved bank ID |
|
||
| `ctx.memories` | Array of recalled memories |
|
||
| `ctx.entities` | Entity observations (or null) |
|
||
| `ctx.memoriesAsSystemPrompt(options?)` | Format memories for LLM system prompt |
|
||
| `ctx.retain(content, options?)` | Store content in memory |
|
||
| `ctx.recall(query, options?)` | Search memories |
|
||
| `ctx.reflect(query, options?)` | Reason over memories |
|
||
|
||
## Examples
|
||
|
||
### Subscribed Message Handler
|
||
|
||
```typescript
|
||
chat.onSubscribedMessage(
|
||
withHindsightChat(
|
||
{
|
||
client: hindsight,
|
||
bankId: (msg) => msg.author.userId,
|
||
recall: { budget: 'high', maxTokens: 1000 },
|
||
},
|
||
async (thread, message, ctx) => {
|
||
const result = await generateText({
|
||
model: openai('gpt-4o'),
|
||
system: ctx.memoriesAsSystemPrompt(),
|
||
messages: [{ role: 'user', content: message.text }],
|
||
});
|
||
await thread.post(result.text);
|
||
}
|
||
)
|
||
);
|
||
```
|
||
|
||
### Auto-Retain Inbound Messages
|
||
|
||
```typescript
|
||
chat.onNewMention(
|
||
withHindsightChat(
|
||
{
|
||
client: hindsight,
|
||
bankId: (msg) => msg.author.userId,
|
||
retain: { enabled: true, tags: ['slack', 'inbound'] },
|
||
},
|
||
async (thread, message, ctx) => {
|
||
// Inbound message is already being retained automatically
|
||
const result = await generateText({
|
||
model: openai('gpt-4o'),
|
||
system: ctx.memoriesAsSystemPrompt(),
|
||
messages: [{ role: 'user', content: message.text }],
|
||
});
|
||
await thread.post(result.text);
|
||
|
||
// Retain the assistant response separately
|
||
await ctx.retain(`Assistant: ${result.text}`, {
|
||
tags: ['slack', 'outbound'],
|
||
});
|
||
}
|
||
)
|
||
);
|
||
```
|
||
|
||
### Static Bank ID (Shared Memory)
|
||
|
||
```typescript
|
||
// All users share the same memory bank
|
||
chat.onNewMention(
|
||
withHindsightChat(
|
||
{ client: hindsight, bankId: 'shared-team-memory' },
|
||
async (thread, message, ctx) => {
|
||
// ...
|
||
}
|
||
)
|
||
);
|
||
```
|
||
|
||
## Error Handling
|
||
|
||
We designed the integration so that memory failures never break your bot. Auto-recall and auto-retain errors are caught internally, logged as warnings, and the handler continues with empty memories. Manual `ctx.retain()`, `ctx.recall()`, and `ctx.reflect()` calls propagate errors normally so you can handle them as needed.
|