fleet-memory/hindsight-integrations/chat/src/format.ts
Ben fed987f931
feat: add Chat SDK integration for persistent chat bot memory (#442)
Adds @vectorize-io/hindsight-chat, a wrapper for the Vercel Chat SDK
that gives any chat bot (Slack, Discord, Teams, etc.) long-term memory
via Hindsight. Includes withHindsightChat() handler wrapper with
auto-recall, auto-retain, and memoriesAsSystemPrompt() formatting.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 17:07:47 +01:00

68 lines
1.9 KiB
TypeScript

import type { RecallResult, EntityState, MemoryPromptOptions } from './types.js';
const DEFAULT_PREAMBLE =
'You have access to the following memories about this user from previous interactions:';
/**
* Formats recalled memories and entity observations into a system prompt string.
*
* Returns an empty string when there are no memories or entities to include,
* so callers can safely concatenate or conditionally append.
*/
export function formatMemoriesAsSystemPrompt(
memories: RecallResult[],
entities: Record<string, EntityState> | null | undefined,
options?: MemoryPromptOptions
): string {
const {
preamble = DEFAULT_PREAMBLE,
maxMemories,
includeTypes,
includeEntities = true,
} = options ?? {};
let filtered = memories;
if (includeTypes && includeTypes.length > 0) {
filtered = filtered.filter(
(m) => m.type != null && includeTypes.includes(m.type as never)
);
}
if (maxMemories != null && maxMemories > 0) {
filtered = filtered.slice(0, maxMemories);
}
const hasMemories = filtered.length > 0;
const entityEntries = entities ? Object.values(entities) : [];
const hasEntities = includeEntities && entityEntries.length > 0;
if (!hasMemories && !hasEntities) {
return '';
}
const parts: string[] = [preamble, ''];
if (hasMemories) {
parts.push('<memories>');
for (const memory of filtered) {
const typeSuffix = memory.type ? ` [${memory.type}]` : '';
parts.push(`- ${memory.text}${typeSuffix}`);
}
parts.push('</memories>');
}
if (hasEntities) {
if (hasMemories) parts.push('');
parts.push('<entity_observations>');
for (const entity of entityEntries) {
parts.push(`## ${entity.canonical_name}`);
for (const obs of entity.observations) {
parts.push(`- ${obs.text}`);
}
}
parts.push('</entity_observations>');
}
return parts.join('\n');
}