@vectorize-io/hindsight-chat 0.4.14
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.
- package/README.md +161 -0
- package/dist/format.d.ts +8 -0
- package/dist/format.js +45 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/types.d.ts +227 -0
- package/dist/types.js +1 -0
- package/dist/wrapper.d.ts +30 -0
- package/dist/wrapper.js +93 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# @vectorize-io/hindsight-chat
|
|
2
|
+
|
|
3
|
+
Give your [Vercel Chat SDK](https://github.com/vercel/chat) bots persistent, per-user memory with a single handler wrapper. Works with Slack, Discord, Teams, Google Chat, GitHub, and Linear.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @vectorize-io/hindsight-chat
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { Chat } from 'chat';
|
|
13
|
+
import { HindsightClient } from '@vectorize-io/hindsight-client';
|
|
14
|
+
import { withHindsightChat } from '@vectorize-io/hindsight-chat';
|
|
15
|
+
import { streamText } from 'ai';
|
|
16
|
+
import { openai } from '@ai-sdk/openai';
|
|
17
|
+
|
|
18
|
+
const chat = new Chat({ connectors: [/* your connectors */] });
|
|
19
|
+
const hindsight = new HindsightClient({ apiKey: process.env.HINDSIGHT_API_KEY });
|
|
20
|
+
|
|
21
|
+
chat.onNewMention(
|
|
22
|
+
withHindsightChat(
|
|
23
|
+
{
|
|
24
|
+
client: hindsight,
|
|
25
|
+
bankId: (msg) => msg.author.userId, // per-user memory
|
|
26
|
+
},
|
|
27
|
+
async (thread, message, ctx) => {
|
|
28
|
+
await thread.subscribe();
|
|
29
|
+
|
|
30
|
+
const result = await streamText({
|
|
31
|
+
model: openai('gpt-4o'),
|
|
32
|
+
system: ctx.memoriesAsSystemPrompt(),
|
|
33
|
+
messages: [{ role: 'user', content: message.text }],
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Stream the response
|
|
37
|
+
const chunks: string[] = [];
|
|
38
|
+
for await (const chunk of result.textStream) {
|
|
39
|
+
chunks.push(chunk);
|
|
40
|
+
}
|
|
41
|
+
const fullResponse = chunks.join('');
|
|
42
|
+
await thread.post(fullResponse);
|
|
43
|
+
|
|
44
|
+
// Store the conversation in memory
|
|
45
|
+
await ctx.retain(
|
|
46
|
+
`User: ${message.text}\nAssistant: ${fullResponse}`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Configuration
|
|
54
|
+
|
|
55
|
+
### `withHindsightChat(options, handler)`
|
|
56
|
+
|
|
57
|
+
Returns a standard Chat SDK handler `(thread, message) => Promise<void>`.
|
|
58
|
+
|
|
59
|
+
#### Options
|
|
60
|
+
|
|
61
|
+
| Option | Type | Default | Description |
|
|
62
|
+
|--------|------|---------|-------------|
|
|
63
|
+
| `client` | `HindsightClient` | *required* | Hindsight client instance |
|
|
64
|
+
| `bankId` | `string \| (msg) => string` | *required* | Memory bank ID or resolver function |
|
|
65
|
+
| `recall.enabled` | `boolean` | `true` | Auto-recall memories before handler |
|
|
66
|
+
| `recall.budget` | `'low' \| 'mid' \| 'high'` | `'mid'` | Processing budget for recall |
|
|
67
|
+
| `recall.maxTokens` | `number` | API default | Max tokens for recall results |
|
|
68
|
+
| `recall.types` | `FactType[]` | all | Filter to specific fact types |
|
|
69
|
+
| `recall.includeEntities` | `boolean` | `true` | Include entity observations |
|
|
70
|
+
| `retain.enabled` | `boolean` | `false` | Auto-retain inbound messages |
|
|
71
|
+
| `retain.async` | `boolean` | `true` | Fire-and-forget retain |
|
|
72
|
+
| `retain.tags` | `string[]` | – | Tags for retained memories |
|
|
73
|
+
| `retain.metadata` | `Record<string, string>` | – | Metadata for retained memories |
|
|
74
|
+
|
|
75
|
+
### Context (`ctx`)
|
|
76
|
+
|
|
77
|
+
The third argument passed to your handler:
|
|
78
|
+
|
|
79
|
+
| Property/Method | Description |
|
|
80
|
+
|----------------|-------------|
|
|
81
|
+
| `ctx.bankId` | Resolved bank ID |
|
|
82
|
+
| `ctx.memories` | Array of recalled memories |
|
|
83
|
+
| `ctx.entities` | Entity observations (or null) |
|
|
84
|
+
| `ctx.memoriesAsSystemPrompt(options?)` | Format memories for LLM system prompt |
|
|
85
|
+
| `ctx.retain(content, options?)` | Store content in memory |
|
|
86
|
+
| `ctx.recall(query, options?)` | Search memories |
|
|
87
|
+
| `ctx.reflect(query, options?)` | Reason over memories |
|
|
88
|
+
|
|
89
|
+
## Examples
|
|
90
|
+
|
|
91
|
+
### Subscribed Message Handler
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
chat.onSubscribedMessage(
|
|
95
|
+
withHindsightChat(
|
|
96
|
+
{
|
|
97
|
+
client: hindsight,
|
|
98
|
+
bankId: (msg) => msg.author.userId,
|
|
99
|
+
recall: { budget: 'high', maxTokens: 1000 },
|
|
100
|
+
},
|
|
101
|
+
async (thread, message, ctx) => {
|
|
102
|
+
const result = await generateText({
|
|
103
|
+
model: openai('gpt-4o'),
|
|
104
|
+
system: ctx.memoriesAsSystemPrompt(),
|
|
105
|
+
messages: [{ role: 'user', content: message.text }],
|
|
106
|
+
});
|
|
107
|
+
await thread.post(result.text);
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Auto-Retain Inbound Messages
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
chat.onNewMention(
|
|
117
|
+
withHindsightChat(
|
|
118
|
+
{
|
|
119
|
+
client: hindsight,
|
|
120
|
+
bankId: (msg) => msg.author.userId,
|
|
121
|
+
retain: { enabled: true, tags: ['slack', 'inbound'] },
|
|
122
|
+
},
|
|
123
|
+
async (thread, message, ctx) => {
|
|
124
|
+
// Inbound message is already being retained automatically
|
|
125
|
+
const result = await generateText({
|
|
126
|
+
model: openai('gpt-4o'),
|
|
127
|
+
system: ctx.memoriesAsSystemPrompt(),
|
|
128
|
+
messages: [{ role: 'user', content: message.text }],
|
|
129
|
+
});
|
|
130
|
+
await thread.post(result.text);
|
|
131
|
+
|
|
132
|
+
// Retain the assistant response separately
|
|
133
|
+
await ctx.retain(`Assistant: ${result.text}`, {
|
|
134
|
+
tags: ['slack', 'outbound'],
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Static Bank ID (Shared Memory)
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
// All users share the same memory bank
|
|
145
|
+
chat.onNewMention(
|
|
146
|
+
withHindsightChat(
|
|
147
|
+
{ client: hindsight, bankId: 'shared-team-memory' },
|
|
148
|
+
async (thread, message, ctx) => {
|
|
149
|
+
// ...
|
|
150
|
+
}
|
|
151
|
+
)
|
|
152
|
+
);
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Error Handling
|
|
156
|
+
|
|
157
|
+
Memory failures never break your bot. Auto-recall and auto-retain errors are 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.
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
MIT
|
package/dist/format.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RecallResult, EntityState, MemoryPromptOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Formats recalled memories and entity observations into a system prompt string.
|
|
4
|
+
*
|
|
5
|
+
* Returns an empty string when there are no memories or entities to include,
|
|
6
|
+
* so callers can safely concatenate or conditionally append.
|
|
7
|
+
*/
|
|
8
|
+
export declare function formatMemoriesAsSystemPrompt(memories: RecallResult[], entities: Record<string, EntityState> | null | undefined, options?: MemoryPromptOptions): string;
|
package/dist/format.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const DEFAULT_PREAMBLE = 'You have access to the following memories about this user from previous interactions:';
|
|
2
|
+
/**
|
|
3
|
+
* Formats recalled memories and entity observations into a system prompt string.
|
|
4
|
+
*
|
|
5
|
+
* Returns an empty string when there are no memories or entities to include,
|
|
6
|
+
* so callers can safely concatenate or conditionally append.
|
|
7
|
+
*/
|
|
8
|
+
export function formatMemoriesAsSystemPrompt(memories, entities, options) {
|
|
9
|
+
const { preamble = DEFAULT_PREAMBLE, maxMemories, includeTypes, includeEntities = true, } = options ?? {};
|
|
10
|
+
let filtered = memories;
|
|
11
|
+
if (includeTypes && includeTypes.length > 0) {
|
|
12
|
+
filtered = filtered.filter((m) => m.type != null && includeTypes.includes(m.type));
|
|
13
|
+
}
|
|
14
|
+
if (maxMemories != null && maxMemories > 0) {
|
|
15
|
+
filtered = filtered.slice(0, maxMemories);
|
|
16
|
+
}
|
|
17
|
+
const hasMemories = filtered.length > 0;
|
|
18
|
+
const entityEntries = entities ? Object.values(entities) : [];
|
|
19
|
+
const hasEntities = includeEntities && entityEntries.length > 0;
|
|
20
|
+
if (!hasMemories && !hasEntities) {
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
const parts = [preamble, ''];
|
|
24
|
+
if (hasMemories) {
|
|
25
|
+
parts.push('<memories>');
|
|
26
|
+
for (const memory of filtered) {
|
|
27
|
+
const typeSuffix = memory.type ? ` [${memory.type}]` : '';
|
|
28
|
+
parts.push(`- ${memory.text}${typeSuffix}`);
|
|
29
|
+
}
|
|
30
|
+
parts.push('</memories>');
|
|
31
|
+
}
|
|
32
|
+
if (hasEntities) {
|
|
33
|
+
if (hasMemories)
|
|
34
|
+
parts.push('');
|
|
35
|
+
parts.push('<entity_observations>');
|
|
36
|
+
for (const entity of entityEntries) {
|
|
37
|
+
parts.push(`## ${entity.canonical_name}`);
|
|
38
|
+
for (const obs of entity.observations) {
|
|
39
|
+
parts.push(`- ${obs.text}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
parts.push('</entity_observations>');
|
|
43
|
+
}
|
|
44
|
+
return parts.join('\n');
|
|
45
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { withHindsightChat } from './wrapper.js';
|
|
2
|
+
export { formatMemoriesAsSystemPrompt } from './format.js';
|
|
3
|
+
export type { Budget, FactType, RecallResult, EntityState, RecallResponse, ReflectFact, ReflectResponse, RetainResponse, HindsightClient, BankIdResolver, ChatMessage, ChatThread, MemoryPromptOptions, RecallOptions, RetainOptions, HindsightChatOptions, HindsightChatContext, HindsightChatHandler, } from './types.js';
|
package/dist/index.js
ADDED
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Budget levels for recall/reflect operations.
|
|
3
|
+
*/
|
|
4
|
+
export type Budget = 'low' | 'mid' | 'high';
|
|
5
|
+
/**
|
|
6
|
+
* Fact types for filtering recall results.
|
|
7
|
+
*/
|
|
8
|
+
export type FactType = 'world' | 'experience' | 'observation';
|
|
9
|
+
/**
|
|
10
|
+
* Recall result item from Hindsight.
|
|
11
|
+
*/
|
|
12
|
+
export interface RecallResult {
|
|
13
|
+
id: string;
|
|
14
|
+
text: string;
|
|
15
|
+
type?: string | null;
|
|
16
|
+
entities?: string[] | null;
|
|
17
|
+
context?: string | null;
|
|
18
|
+
occurred_start?: string | null;
|
|
19
|
+
occurred_end?: string | null;
|
|
20
|
+
mentioned_at?: string | null;
|
|
21
|
+
document_id?: string | null;
|
|
22
|
+
metadata?: Record<string, string> | null;
|
|
23
|
+
chunk_id?: string | null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Entity state with observations.
|
|
27
|
+
*/
|
|
28
|
+
export interface EntityState {
|
|
29
|
+
entity_id: string;
|
|
30
|
+
canonical_name: string;
|
|
31
|
+
observations: Array<{
|
|
32
|
+
text: string;
|
|
33
|
+
mentioned_at?: string | null;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Recall response from Hindsight.
|
|
38
|
+
*/
|
|
39
|
+
export interface RecallResponse {
|
|
40
|
+
results: RecallResult[];
|
|
41
|
+
trace?: Record<string, unknown> | null;
|
|
42
|
+
entities?: Record<string, EntityState> | null;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Reflect fact.
|
|
46
|
+
*/
|
|
47
|
+
export interface ReflectFact {
|
|
48
|
+
id?: string | null;
|
|
49
|
+
text: string;
|
|
50
|
+
type?: string | null;
|
|
51
|
+
context?: string | null;
|
|
52
|
+
occurred_start?: string | null;
|
|
53
|
+
occurred_end?: string | null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Reflect response from Hindsight.
|
|
57
|
+
*/
|
|
58
|
+
export interface ReflectResponse {
|
|
59
|
+
text: string;
|
|
60
|
+
based_on?: ReflectFact[];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Retain response from Hindsight.
|
|
64
|
+
*/
|
|
65
|
+
export interface RetainResponse {
|
|
66
|
+
success: boolean;
|
|
67
|
+
bank_id: string;
|
|
68
|
+
items_count: number;
|
|
69
|
+
async: boolean;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Hindsight client interface — matches @vectorize-io/hindsight-client.
|
|
73
|
+
*/
|
|
74
|
+
export interface HindsightClient {
|
|
75
|
+
retain(bankId: string, content: string, options?: {
|
|
76
|
+
timestamp?: Date | string;
|
|
77
|
+
context?: string;
|
|
78
|
+
metadata?: Record<string, string>;
|
|
79
|
+
documentId?: string;
|
|
80
|
+
tags?: string[];
|
|
81
|
+
async?: boolean;
|
|
82
|
+
}): Promise<RetainResponse>;
|
|
83
|
+
recall(bankId: string, query: string, options?: {
|
|
84
|
+
types?: FactType[];
|
|
85
|
+
maxTokens?: number;
|
|
86
|
+
budget?: Budget;
|
|
87
|
+
trace?: boolean;
|
|
88
|
+
queryTimestamp?: string;
|
|
89
|
+
includeEntities?: boolean;
|
|
90
|
+
maxEntityTokens?: number;
|
|
91
|
+
includeChunks?: boolean;
|
|
92
|
+
maxChunkTokens?: number;
|
|
93
|
+
}): Promise<RecallResponse>;
|
|
94
|
+
reflect(bankId: string, query: string, options?: {
|
|
95
|
+
context?: string;
|
|
96
|
+
budget?: Budget;
|
|
97
|
+
maxTokens?: number;
|
|
98
|
+
}): Promise<ReflectResponse>;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Resolves a bank ID from a message. Can be a static string or a function
|
|
102
|
+
* that derives the bank ID from the message (e.g. per-user memory).
|
|
103
|
+
*/
|
|
104
|
+
export type BankIdResolver = string | ((message: ChatMessage) => string);
|
|
105
|
+
/**
|
|
106
|
+
* Minimal message shape expected from the Chat SDK.
|
|
107
|
+
* We declare our own interface so `chat` stays a peer dep only.
|
|
108
|
+
*/
|
|
109
|
+
export interface ChatMessage {
|
|
110
|
+
author: {
|
|
111
|
+
userId: string;
|
|
112
|
+
name?: string;
|
|
113
|
+
isMe?: boolean;
|
|
114
|
+
};
|
|
115
|
+
text: string;
|
|
116
|
+
threadId: string;
|
|
117
|
+
isMention?: boolean;
|
|
118
|
+
metadata?: {
|
|
119
|
+
timestamp?: Date;
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Minimal thread shape expected from the Chat SDK.
|
|
124
|
+
*/
|
|
125
|
+
export interface ChatThread<TState = unknown> {
|
|
126
|
+
post(message: unknown): Promise<unknown>;
|
|
127
|
+
subscribe(): Promise<void>;
|
|
128
|
+
unsubscribe(): Promise<void>;
|
|
129
|
+
isSubscribed(): Promise<boolean>;
|
|
130
|
+
startTyping(status?: string): Promise<void>;
|
|
131
|
+
state: Promise<TState | null>;
|
|
132
|
+
setState(state: TState): Promise<void>;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Options for formatting memories as a system prompt.
|
|
136
|
+
*/
|
|
137
|
+
export interface MemoryPromptOptions {
|
|
138
|
+
/** Custom preamble text before the memories section. */
|
|
139
|
+
preamble?: string;
|
|
140
|
+
/** Maximum number of memories to include. */
|
|
141
|
+
maxMemories?: number;
|
|
142
|
+
/** Filter to specific fact types. */
|
|
143
|
+
includeTypes?: FactType[];
|
|
144
|
+
/** Include entity observations section. */
|
|
145
|
+
includeEntities?: boolean;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Options for the auto-recall step.
|
|
149
|
+
*/
|
|
150
|
+
export interface RecallOptions {
|
|
151
|
+
/** Enable auto-recall before the handler runs (default: true). */
|
|
152
|
+
enabled?: boolean;
|
|
153
|
+
/** Processing budget (default: 'mid'). */
|
|
154
|
+
budget?: Budget;
|
|
155
|
+
/** Maximum tokens for recall results. */
|
|
156
|
+
maxTokens?: number;
|
|
157
|
+
/** Filter to specific fact types. */
|
|
158
|
+
types?: FactType[];
|
|
159
|
+
/** Include entity observations (default: true). */
|
|
160
|
+
includeEntities?: boolean;
|
|
161
|
+
/** Tags to filter recall results. */
|
|
162
|
+
tags?: string[];
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Options for the auto-retain step.
|
|
166
|
+
*/
|
|
167
|
+
export interface RetainOptions {
|
|
168
|
+
/** Enable auto-retain of inbound messages (default: false). */
|
|
169
|
+
enabled?: boolean;
|
|
170
|
+
/** Fire-and-forget retain (default: true when enabled). */
|
|
171
|
+
async?: boolean;
|
|
172
|
+
/** Tags to attach to retained memories. */
|
|
173
|
+
tags?: string[];
|
|
174
|
+
/** Metadata to attach to retained memories. */
|
|
175
|
+
metadata?: Record<string, string>;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Configuration for withHindsightChat.
|
|
179
|
+
*/
|
|
180
|
+
export interface HindsightChatOptions {
|
|
181
|
+
/** Hindsight client instance. */
|
|
182
|
+
client: HindsightClient;
|
|
183
|
+
/** Bank ID — static string or function deriving it from the message. */
|
|
184
|
+
bankId: BankIdResolver;
|
|
185
|
+
/** Auto-recall options (default: enabled). */
|
|
186
|
+
recall?: RecallOptions;
|
|
187
|
+
/** Auto-retain options for inbound messages (default: disabled). */
|
|
188
|
+
retain?: RetainOptions;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Context object passed to the wrapped handler, providing memory operations.
|
|
192
|
+
*/
|
|
193
|
+
export interface HindsightChatContext {
|
|
194
|
+
/** Resolved bank ID for this message. */
|
|
195
|
+
bankId: string;
|
|
196
|
+
/** Recalled memories (empty array if recall disabled or failed). */
|
|
197
|
+
memories: RecallResult[];
|
|
198
|
+
/** Recalled entity observations (null if entities not included). */
|
|
199
|
+
entities: Record<string, EntityState> | null;
|
|
200
|
+
/** Format memories as a system prompt string. */
|
|
201
|
+
memoriesAsSystemPrompt(options?: MemoryPromptOptions): string;
|
|
202
|
+
/** Store content in memory. */
|
|
203
|
+
retain(content: string, options?: {
|
|
204
|
+
timestamp?: Date | string;
|
|
205
|
+
context?: string;
|
|
206
|
+
metadata?: Record<string, string>;
|
|
207
|
+
tags?: string[];
|
|
208
|
+
async?: boolean;
|
|
209
|
+
}): Promise<RetainResponse>;
|
|
210
|
+
/** Search memories. */
|
|
211
|
+
recall(query: string, options?: {
|
|
212
|
+
types?: FactType[];
|
|
213
|
+
maxTokens?: number;
|
|
214
|
+
budget?: Budget;
|
|
215
|
+
includeEntities?: boolean;
|
|
216
|
+
}): Promise<RecallResponse>;
|
|
217
|
+
/** Reflect on memories to form insights. */
|
|
218
|
+
reflect(query: string, options?: {
|
|
219
|
+
context?: string;
|
|
220
|
+
budget?: Budget;
|
|
221
|
+
maxTokens?: number;
|
|
222
|
+
}): Promise<ReflectResponse>;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* The handler signature that withHindsightChat wraps.
|
|
226
|
+
*/
|
|
227
|
+
export type HindsightChatHandler<TState = unknown> = (thread: ChatThread<TState>, message: ChatMessage, ctx: HindsightChatContext) => void | Promise<void>;
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { HindsightChatOptions, HindsightChatHandler, ChatThread, ChatMessage } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Wraps a Chat SDK handler to automatically provide Hindsight memory context.
|
|
4
|
+
*
|
|
5
|
+
* Before the handler runs:
|
|
6
|
+
* 1. Resolves the bank ID from the message
|
|
7
|
+
* 2. Optionally auto-retains the inbound message (off by default)
|
|
8
|
+
* 3. Auto-recalls relevant memories (on by default)
|
|
9
|
+
* 4. Builds a HindsightChatContext and passes it to the handler
|
|
10
|
+
*
|
|
11
|
+
* Works with `onNewMention`, `onSubscribedMessage`, and `onNewMessage`.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* chat.onNewMention(
|
|
16
|
+
* withHindsightChat(
|
|
17
|
+
* { client, bankId: (msg) => msg.author.userId },
|
|
18
|
+
* async (thread, message, ctx) => {
|
|
19
|
+
* const result = await streamText({
|
|
20
|
+
* system: ctx.memoriesAsSystemPrompt(),
|
|
21
|
+
* messages: [{ role: 'user', content: message.text }],
|
|
22
|
+
* });
|
|
23
|
+
* await thread.post(result.textStream);
|
|
24
|
+
* await ctx.retain(`User: ${message.text}\nAssistant: ${fullResponse}`);
|
|
25
|
+
* }
|
|
26
|
+
* )
|
|
27
|
+
* );
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export declare function withHindsightChat<TState = unknown>(options: HindsightChatOptions, handler: HindsightChatHandler<TState>): (thread: ChatThread<TState>, message: ChatMessage) => Promise<void>;
|
package/dist/wrapper.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { formatMemoriesAsSystemPrompt } from './format.js';
|
|
2
|
+
/**
|
|
3
|
+
* Wraps a Chat SDK handler to automatically provide Hindsight memory context.
|
|
4
|
+
*
|
|
5
|
+
* Before the handler runs:
|
|
6
|
+
* 1. Resolves the bank ID from the message
|
|
7
|
+
* 2. Optionally auto-retains the inbound message (off by default)
|
|
8
|
+
* 3. Auto-recalls relevant memories (on by default)
|
|
9
|
+
* 4. Builds a HindsightChatContext and passes it to the handler
|
|
10
|
+
*
|
|
11
|
+
* Works with `onNewMention`, `onSubscribedMessage`, and `onNewMessage`.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* chat.onNewMention(
|
|
16
|
+
* withHindsightChat(
|
|
17
|
+
* { client, bankId: (msg) => msg.author.userId },
|
|
18
|
+
* async (thread, message, ctx) => {
|
|
19
|
+
* const result = await streamText({
|
|
20
|
+
* system: ctx.memoriesAsSystemPrompt(),
|
|
21
|
+
* messages: [{ role: 'user', content: message.text }],
|
|
22
|
+
* });
|
|
23
|
+
* await thread.post(result.textStream);
|
|
24
|
+
* await ctx.retain(`User: ${message.text}\nAssistant: ${fullResponse}`);
|
|
25
|
+
* }
|
|
26
|
+
* )
|
|
27
|
+
* );
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function withHindsightChat(options, handler) {
|
|
31
|
+
const { client, bankId: bankIdResolver, recall: recallOpts = {}, retain: retainOpts = {} } = options;
|
|
32
|
+
const recallEnabled = recallOpts.enabled !== false;
|
|
33
|
+
const retainEnabled = retainOpts.enabled === true;
|
|
34
|
+
const retainAsync = retainOpts.async !== false; // default true when retain is enabled
|
|
35
|
+
return async (thread, message) => {
|
|
36
|
+
// 1. Resolve bank ID
|
|
37
|
+
const resolvedBankId = typeof bankIdResolver === 'function' ? bankIdResolver(message) : bankIdResolver;
|
|
38
|
+
// 2. Auto-retain inbound message (fire-and-forget if async)
|
|
39
|
+
if (retainEnabled && message.text && !message.author.isMe) {
|
|
40
|
+
const retainPromise = client
|
|
41
|
+
.retain(resolvedBankId, message.text, {
|
|
42
|
+
tags: retainOpts.tags,
|
|
43
|
+
metadata: retainOpts.metadata,
|
|
44
|
+
async: retainAsync,
|
|
45
|
+
})
|
|
46
|
+
.catch((err) => {
|
|
47
|
+
console.warn('[hindsight-chat] Auto-retain failed:', err);
|
|
48
|
+
});
|
|
49
|
+
// If not async, wait for retain to complete before proceeding
|
|
50
|
+
if (!retainAsync) {
|
|
51
|
+
await retainPromise;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// 3. Auto-recall memories
|
|
55
|
+
let memories = [];
|
|
56
|
+
let entities = null;
|
|
57
|
+
if (recallEnabled && message.text) {
|
|
58
|
+
try {
|
|
59
|
+
const recallResponse = await client.recall(resolvedBankId, message.text, {
|
|
60
|
+
budget: recallOpts.budget ?? 'mid',
|
|
61
|
+
maxTokens: recallOpts.maxTokens,
|
|
62
|
+
types: recallOpts.types,
|
|
63
|
+
includeEntities: recallOpts.includeEntities !== false,
|
|
64
|
+
});
|
|
65
|
+
memories = recallResponse.results ?? [];
|
|
66
|
+
entities = recallResponse.entities ?? null;
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
console.warn('[hindsight-chat] Auto-recall failed:', err);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// 4. Build context
|
|
73
|
+
const ctx = {
|
|
74
|
+
bankId: resolvedBankId,
|
|
75
|
+
memories,
|
|
76
|
+
entities,
|
|
77
|
+
memoriesAsSystemPrompt(promptOptions) {
|
|
78
|
+
return formatMemoriesAsSystemPrompt(memories, entities, promptOptions);
|
|
79
|
+
},
|
|
80
|
+
retain(content, retainCallOpts) {
|
|
81
|
+
return client.retain(resolvedBankId, content, retainCallOpts);
|
|
82
|
+
},
|
|
83
|
+
recall(query, recallCallOpts) {
|
|
84
|
+
return client.recall(resolvedBankId, query, recallCallOpts);
|
|
85
|
+
},
|
|
86
|
+
reflect(query, reflectCallOpts) {
|
|
87
|
+
return client.reflect(resolvedBankId, query, reflectCallOpts);
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
// 5. Call the user's handler
|
|
91
|
+
await handler(thread, message, ctx);
|
|
92
|
+
};
|
|
93
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vectorize-io/hindsight-chat",
|
|
3
|
+
"version": "0.4.14",
|
|
4
|
+
"description": "Hindsight memory integration for Vercel Chat SDK - Give your chat bots persistent, per-user memory",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"chat",
|
|
16
|
+
"chatbot",
|
|
17
|
+
"slack",
|
|
18
|
+
"discord",
|
|
19
|
+
"teams",
|
|
20
|
+
"memory",
|
|
21
|
+
"hindsight",
|
|
22
|
+
"agents",
|
|
23
|
+
"llm",
|
|
24
|
+
"long-term-memory"
|
|
25
|
+
],
|
|
26
|
+
"author": "Vectorize <support@vectorize.io>",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/vectorize-io/hindsight.git",
|
|
31
|
+
"directory": "hindsight-integrations/chat"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"README.md"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc",
|
|
39
|
+
"dev": "tsc --watch",
|
|
40
|
+
"clean": "rm -rf dist",
|
|
41
|
+
"test": "vitest run",
|
|
42
|
+
"test:watch": "vitest",
|
|
43
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"chat": "^4.0.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^22.0.0",
|
|
50
|
+
"@vitest/ui": "^4.0.18",
|
|
51
|
+
"chat": "^4.0.0",
|
|
52
|
+
"typescript": "^5.7.0",
|
|
53
|
+
"vitest": "^4.0.18"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=22"
|
|
57
|
+
}
|
|
58
|
+
}
|