UNPKG

assistant-cloud

Version:

Cloud integration for assistant-ui

115 lines (112 loc) • 3.71 kB
import type { ReadonlyJSONObject } from "assistant-stream/utils"; import type { CloudMessageRunPhase } from "./CloudMessagePersistence"; /** * Format adapter shape — structurally identical to the MessageFormatAdapter * in @assistant-ui/react, but defined here to avoid cross-package type moves. * TypeScript's structural typing ensures these are interchangeable. */ export type MessageFormatAdapter<TMessage, TStorageFormat> = { format: string; encode(item: { parentId: string | null; message: TMessage }): TStorageFormat; decode(stored: { id: string; parent_id: string | null; format: string; content: TStorageFormat; }): { parentId: string | null; message: TMessage }; getId(message: TMessage): string; }; /** * Wraps a CloudMessagePersistence with a MessageFormatAdapter's encode and * decode. The persistence parameter is typed structurally, so a caller does * not need to import the class. * * `append` and `update` take an optional run phase that is passed through to * the persistence: `running` opens (on append) or renews (on update) the * message's run lease, and an end reason makes the write the lease's final * write and ends it, the same as for `aui/v0` messages. */ export const createFormattedPersistence = <TMessage, TStorageFormat>( persistence: { append: ( threadId: string, messageId: string, parentId: string | null, format: string, content: ReadonlyJSONObject, run?: CloudMessageRunPhase, ) => Promise<void>; load: (threadId: string, format?: string) => Promise<any[]>; isPersisted: (messageId: string) => boolean; update?: ( threadId: string, messageId: string, format: string, content: ReadonlyJSONObject, run?: CloudMessageRunPhase, ) => Promise<void>; delete?: (threadId: string, messageIds: string[]) => Promise<void>; }, adapter: MessageFormatAdapter<TMessage, TStorageFormat>, ) => ({ append: async ( threadId: string, item: { parentId: string | null; message: TMessage }, run?: CloudMessageRunPhase, ): Promise<void> => { const messageId = adapter.getId(item.message); const encoded = adapter.encode(item); return persistence.append( threadId, messageId, item.parentId, adapter.format, encoded as ReadonlyJSONObject, ...(run !== undefined ? [run] : []), ); }, update: persistence.update ? async ( threadId: string, item: { parentId: string | null; message: TMessage }, messageId: string, run?: CloudMessageRunPhase, ): Promise<void> => { const encoded = adapter.encode(item); return persistence.update!( threadId, messageId, adapter.format, encoded as ReadonlyJSONObject, ...(run !== undefined ? [run] : []), ); } : undefined, delete: persistence.delete ? async ( threadId: string, items: { parentId: string | null; message: TMessage }[], ): Promise<void> => persistence.delete!( threadId, items.map((item) => adapter.getId(item.message)), ) : undefined, load: async (threadId: string) => { const messages = await persistence.load(threadId, adapter.format); return { messages: messages .filter((m) => m.format === adapter.format) .map((m) => adapter.decode({ id: m.id, parent_id: m.parent_id, format: m.format, content: m.content as TStorageFormat, }), ) .reverse(), }; }, isPersisted: (messageId: string) => persistence.isPersisted(messageId), });