diff --git a/.changeset/tough-tips-arrive.md b/.changeset/tough-tips-arrive.md new file mode 100644 index 000000000..4013334d6 --- /dev/null +++ b/.changeset/tough-tips-arrive.md @@ -0,0 +1,5 @@ +--- +"@assistant-ui/react": patch +--- + +feat: ThreadListRuntime API types diff --git a/packages/react/src/api/AssistantRuntime.ts b/packages/react/src/api/AssistantRuntime.ts index 77f73d05e..63aee983a 100644 --- a/packages/react/src/api/AssistantRuntime.ts +++ b/packages/react/src/api/AssistantRuntime.ts @@ -13,12 +13,12 @@ export type AssistantRuntime = { /** * The currently selected main thread. */ - thread: ThreadRuntime; + readonly thread: ThreadRuntime; /** * The thread manager, to rename, archive and delete threads. */ - threadList: ThreadListRuntime; + readonly threadList: ThreadListRuntime; /** * Switch to a new thread. diff --git a/packages/react/src/api/AttachmentRuntime.ts b/packages/react/src/api/AttachmentRuntime.ts index 0ceea190e..11389db37 100644 --- a/packages/react/src/api/AttachmentRuntime.ts +++ b/packages/react/src/api/AttachmentRuntime.ts @@ -10,15 +10,15 @@ import { import { AttachmentRuntimePath } from "./RuntimePathTypes"; type MessageAttachmentState = CompleteAttachment & { - source: "message"; + readonly source: "message"; }; type ThreadComposerAttachmentState = PendingAttachment & { - source: "thread-composer"; + readonly source: "thread-composer"; }; type EditComposerAttachmentState = Attachment & { - source: "edit-composer"; + readonly source: "edit-composer"; }; export type AttachmentState = @@ -37,7 +37,7 @@ type AttachmentRuntimeSource = AttachmentState["source"]; export type AttachmentRuntime< TSource extends AttachmentRuntimeSource = AttachmentRuntimeSource, > = { - path: AttachmentRuntimePath & { attachmentSource: TSource }; + readonly path: AttachmentRuntimePath & { attachmentSource: TSource }; readonly source: TSource; getState(): AttachmentState & { source: TSource }; remove(): Promise; diff --git a/packages/react/src/api/ComposerRuntime.ts b/packages/react/src/api/ComposerRuntime.ts index a04223a75..732c90627 100644 --- a/packages/react/src/api/ComposerRuntime.ts +++ b/packages/react/src/api/ComposerRuntime.ts @@ -33,23 +33,23 @@ export type ComposerRuntimeCoreBinding = SubscribableWithState< >; type BaseComposerState = { - text: string; - role: MessageRole; - attachments: readonly Attachment[]; + readonly text: string; + readonly role: MessageRole; + readonly attachments: readonly Attachment[]; - canCancel: boolean; - isEditing: boolean; - isEmpty: boolean; + readonly canCancel: boolean; + readonly isEditing: boolean; + readonly isEmpty: boolean; }; export type ThreadComposerState = BaseComposerState & { - type: "thread"; + readonly type: "thread"; - attachments: readonly PendingAttachment[]; + readonly attachments: readonly PendingAttachment[]; }; export type EditComposerState = BaseComposerState & { - type: "edit"; + readonly type: "edit"; }; export type ComposerState = ThreadComposerState | EditComposerState; @@ -90,7 +90,7 @@ const getEditComposerState = ( }; export type ComposerRuntime = { - path: ComposerRuntimePath; + readonly path: ComposerRuntimePath; readonly type: "edit" | "thread"; getState(): ComposerState; diff --git a/packages/react/src/api/ContentPartRuntime.ts b/packages/react/src/api/ContentPartRuntime.ts index a5fe4909f..ebd329910 100644 --- a/packages/react/src/api/ContentPartRuntime.ts +++ b/packages/react/src/api/ContentPartRuntime.ts @@ -14,7 +14,7 @@ export type ContentPartState = ( | ThreadUserContentPart | ThreadAssistantContentPart ) & { - status: ContentPartStatus | ToolCallContentPartStatus; + readonly status: ContentPartStatus | ToolCallContentPartStatus; }; type ContentPartSnapshotBinding = SubscribableWithState< @@ -23,7 +23,7 @@ type ContentPartSnapshotBinding = SubscribableWithState< >; export type ContentPartRuntime = { - path: ContentPartRuntimePath; + readonly path: ContentPartRuntimePath; getState(): ContentPartState; addToolResult(result: any): void; diff --git a/packages/react/src/api/MessageRuntime.ts b/packages/react/src/api/MessageRuntime.ts index 6015efb6a..05b292c7d 100644 --- a/packages/react/src/api/MessageRuntime.ts +++ b/packages/react/src/api/MessageRuntime.ts @@ -34,9 +34,9 @@ import { SKIP_UPDATE } from "./subscribable/SKIP_UPDATE"; import { ShallowMemoizeSubject } from "./subscribable/ShallowMemoizeSubject"; import { SubscribableWithState } from "./subscribable/Subscribable"; -const COMPLETE_STATUS: ContentPartStatus = { +const COMPLETE_STATUS: ContentPartStatus = Object.freeze({ type: "complete", -}; +}); export const toContentPartStatus = ( message: ThreadMessage, @@ -92,17 +92,17 @@ const getContentPartState = ( }; export type MessageState = ThreadMessage & { - parentId: string | null; - isLast: boolean; + readonly parentId: string | null; + readonly isLast: boolean; - branchNumber: number; - branchCount: number; + readonly branchNumber: number; + readonly branchCount: number; /** * @deprecated This API is still under active development and might change without notice. */ - speech: SpeechState | undefined; - submittedFeedback: SubmittedFeedback | undefined; + readonly speech: SpeechState | undefined; + readonly submittedFeedback: SubmittedFeedback | undefined; }; export type MessageStateBinding = SubscribableWithState< @@ -169,7 +169,7 @@ export class MessageRuntimeImpl implements MessageRuntime { ); } - public composer; + public readonly composer; public getState() { return this._core.getState(); diff --git a/packages/react/src/api/RuntimePathTypes.ts b/packages/react/src/api/RuntimePathTypes.ts index f7d52910c..9e9bf04ab 100644 --- a/packages/react/src/api/RuntimePathTypes.ts +++ b/packages/react/src/api/RuntimePathTypes.ts @@ -1,60 +1,60 @@ export type ThreadListRuntimePath = { - ref: string; + readonly ref: string; }; export type ThreadListItemRuntimePath = { - ref: string; - threadSelector: - | { type: "main" } - | { type: "index"; index: number } - | { type: "archiveIndex"; index: number } - | { type: "threadId"; threadId: string }; + readonly ref: string; + readonly threadSelector: + | { readonly type: "main" } + | { readonly type: "index"; readonly index: number } + | { readonly type: "archiveIndex"; readonly index: number } + | { readonly type: "threadId"; readonly threadId: string }; }; export type ThreadRuntimePath = { - ref: string; - threadSelector: { type: "main" }; + readonly ref: string; + readonly threadSelector: { type: "main" }; }; export type MessageRuntimePath = ThreadRuntimePath & { - messageSelector: - | { type: "messageId"; messageId: string } - | { type: "index"; index: number }; + readonly messageSelector: + | { readonly type: "messageId"; readonly messageId: string } + | { readonly type: "index"; readonly index: number }; }; export type ContentPartRuntimePath = MessageRuntimePath & { - contentPartSelector: - | { type: "index"; index: number } - | { type: "toolCallId"; toolCallId: string }; + readonly contentPartSelector: + | { readonly type: "index"; readonly index: number } + | { readonly type: "toolCallId"; readonly toolCallId: string }; }; export type AttachmentRuntimePath = ( | (MessageRuntimePath & { - attachmentSource: "message" | "edit-composer"; + readonly attachmentSource: "message" | "edit-composer"; }) | (ThreadRuntimePath & { - attachmentSource: "thread-composer"; + readonly attachmentSource: "thread-composer"; }) ) & { - attachmentSelector: + readonly attachmentSelector: | { - type: "index"; - index: number; + readonly type: "index"; + readonly index: number; } | { - type: "index"; - index: number; + readonly type: "index"; + readonly index: number; } | { - type: "index"; - index: number; + readonly type: "index"; + readonly index: number; }; }; export type ComposerRuntimePath = | (ThreadRuntimePath & { - composerSource: "thread"; + readonly composerSource: "thread"; }) | (MessageRuntimePath & { - composerSource: "edit"; + readonly composerSource: "edit"; }); diff --git a/packages/react/src/api/ThreadListItemRuntime.ts b/packages/react/src/api/ThreadListItemRuntime.ts index 0efc4b005..f04616808 100644 --- a/packages/react/src/api/ThreadListItemRuntime.ts +++ b/packages/react/src/api/ThreadListItemRuntime.ts @@ -5,11 +5,11 @@ import { SubscribableWithState } from "./subscribable/Subscribable"; import { ThreadListRuntimeCoreBinding } from "./ThreadListRuntime"; export type ThreadListItemState = ThreadMetadata & { - isMain: boolean; + readonly isMain: boolean; }; -export type ThreadListItemRuntime = Readonly<{ - path: ThreadListItemRuntimePath; +export type ThreadListItemRuntime = { + readonly path: ThreadListItemRuntimePath; getState(): ThreadListItemState; switchTo(): Promise; @@ -19,7 +19,7 @@ export type ThreadListItemRuntime = Readonly<{ delete(): Promise; subscribe(callback: () => void): Unsubscribe; -}>; +}; export type ThreadListItemStateBinding = SubscribableWithState< ThreadListItemState, diff --git a/packages/react/src/api/ThreadListRuntime.ts b/packages/react/src/api/ThreadListRuntime.ts index 6faa5428a..8a3d978a7 100644 --- a/packages/react/src/api/ThreadListRuntime.ts +++ b/packages/react/src/api/ThreadListRuntime.ts @@ -10,15 +10,15 @@ import { import { SKIP_UPDATE } from "./subscribable/SKIP_UPDATE"; import { ShallowMemoizeSubject } from "./subscribable/ShallowMemoizeSubject"; -export type ThreadListState = Readonly<{ - mainThreadId: string; - newThread: string | undefined; - threads: readonly string[]; - archivedThreads: readonly string[]; -}>; - -export type ThreadListRuntime = Readonly<{ - path: ThreadListRuntimePath; +export type ThreadListState = { + readonly mainThreadId: string; + readonly newThread: string | undefined; + readonly threads: readonly string[]; + readonly archivedThreads: readonly string[]; +}; + +export type ThreadListRuntime = { + readonly path: ThreadListRuntimePath; getState(): ThreadListState; subscribe(callback: () => void): Unsubscribe; @@ -26,7 +26,7 @@ export type ThreadListRuntime = Readonly<{ getThreadListItemById(threadId: string): ThreadListItemRuntime; getThreadListItemByIndex(idx: number): ThreadListItemRuntime; getThreadListArchivedItemByIndex(idx: number): ThreadListItemRuntime; -}>; +}; const getThreadListState = ( threadList: ThreadListRuntimeCore, diff --git a/packages/react/src/api/ThreadRuntime.ts b/packages/react/src/api/ThreadRuntime.ts index 6b475267b..7db8c86f0 100644 --- a/packages/react/src/api/ThreadRuntime.ts +++ b/packages/react/src/api/ThreadRuntime.ts @@ -74,53 +74,53 @@ export type ThreadRuntimeCoreBinding = SubscribableWithState< outerSubscribe(callback: () => void): Unsubscribe; }; -export type ThreadState = Readonly<{ +export type ThreadState = { /** * The thread ID. - * @deprecated This field is deprecated and will be removed in 0.7.0. Use `metadata.threadId` instead. + * @deprecated This field is deprecated and will be removed in 0.8.0. Use `metadata.threadId` instead. */ - threadId: string; + readonly threadId: string; /** * The thread metadata. */ - metadata: ThreadMetadata; + readonly metadata: ThreadMetadata; /** * Whether the thread is disabled. Disabled threads cannot receive new messages. */ - isDisabled: boolean; + readonly isDisabled: boolean; /** * Whether the thread is running. A thread is considered running when there is an active stream connection to the backend. */ - isRunning: boolean; + readonly isRunning: boolean; /** * The capabilities of the thread, such as whether the thread supports editing, branch switching, etc. */ - capabilities: RuntimeCapabilities; + readonly capabilities: RuntimeCapabilities; /** * The messages in the currently selected branch of the thread. */ - messages: readonly ThreadMessage[]; + readonly messages: readonly ThreadMessage[]; /** * Follow up message suggestions to show the user. */ - suggestions: readonly ThreadSuggestion[]; + readonly suggestions: readonly ThreadSuggestion[]; /** * Custom extra information provided by the runtime. */ - extras: unknown; + readonly extras: unknown; /** * @deprecated This API is still under active development and might change without notice. */ - speech: SpeechState | undefined; -}>; + readonly speech: SpeechState | undefined; +}; export const getThreadState = (runtime: ThreadRuntimeCore): ThreadState => { const lastMessage = runtime.messages.at(-1); @@ -140,13 +140,44 @@ export const getThreadState = (runtime: ThreadRuntimeCore): ThreadState => { }); }; -export type ThreadRuntime = Readonly<{ +export type ThreadRuntime = { + /** + * The selector for the thread runtime. + */ readonly path: ThreadRuntimePath; + /** + * The thread composer runtime. + */ readonly composer: ThreadComposerRuntime; + + /** + * Gets a snapshot of the thread state. + */ getState(): ThreadState; + /** + * Append a new message to the thread. + * + * @example ```ts + * // append a new user message with the text "Hello, world!" + * threadRuntime.append("Hello, world!"); + * ``` + * + * @example ```ts + * // append a new assistant message with the text "Hello, world!" + * threadRuntime.append({ + * role: "assistant", + * content: [{ type: "text", text: "Hello, world!" }], + * }); + * ``` + */ append(message: CreateAppendMessage): void; + + /** + * + * @param parentId + */ startRun(parentId: string | null): void; subscribe(callback: () => void): Unsubscribe; cancelRun(): void; @@ -162,7 +193,7 @@ export type ThreadRuntime = Readonly<{ stopSpeaking: () => void; unstable_on(event: ThreadRuntimeEventType, callback: () => void): Unsubscribe; -}>; +}; export class ThreadRuntimeImpl implements ThreadRuntime { public get path() { diff --git a/packages/react/src/context/stores/AssistantToolUIs.ts b/packages/react/src/context/stores/AssistantToolUIs.ts index 085bf299b..b4d1d86ce 100644 --- a/packages/react/src/context/stores/AssistantToolUIs.ts +++ b/packages/react/src/context/stores/AssistantToolUIs.ts @@ -4,13 +4,20 @@ import { create } from "zustand"; import type { ToolCallContentPartComponent } from "../../types/ContentPartComponentTypes"; import { Unsubscribe } from "../../types"; -export type AssistantToolUIsState = Readonly<{ +export type AssistantToolUIsState = { + /** + * Get the tool UI configured for a given tool name. + */ getToolUI: (toolName: string) => ToolCallContentPartComponent | null; + + /** + * Registers a tool UI for a given tool name. Returns an unsubscribe function to remove the tool UI. + */ setToolUI: ( toolName: string, render: ToolCallContentPartComponent, ) => Unsubscribe; -}>; +}; export const makeAssistantToolUIsStore = () => create((set) => { diff --git a/packages/react/src/context/stores/MessageUtils.ts b/packages/react/src/context/stores/MessageUtils.ts index db8a1733b..417c67f8b 100644 --- a/packages/react/src/context/stores/MessageUtils.ts +++ b/packages/react/src/context/stores/MessageUtils.ts @@ -1,12 +1,12 @@ import { create } from "zustand"; -export type MessageUtilsState = Readonly<{ - isCopied: boolean; - setIsCopied: (value: boolean) => void; +export type MessageUtilsState = { + readonly isCopied: boolean; + readonly setIsCopied: (value: boolean) => void; - isHovering: boolean; - setIsHovering: (value: boolean) => void; -}>; + readonly isHovering: boolean; + readonly setIsHovering: (value: boolean) => void; +}; export const makeMessageUtilsStore = () => create((set) => { diff --git a/packages/react/src/context/stores/ThreadViewport.tsx b/packages/react/src/context/stores/ThreadViewport.tsx index 60a81b4df..f60b0827b 100644 --- a/packages/react/src/context/stores/ThreadViewport.tsx +++ b/packages/react/src/context/stores/ThreadViewport.tsx @@ -1,12 +1,11 @@ -"use client"; import { create } from "zustand"; import type { Unsubscribe } from "../../types/Unsubscribe"; -export type ThreadViewportState = Readonly<{ - isAtBottom: boolean; - scrollToBottom: () => void; - onScrollToBottom: (callback: () => void) => Unsubscribe; -}>; +export type ThreadViewportState = { + readonly isAtBottom: boolean; + readonly scrollToBottom: () => void; + readonly onScrollToBottom: (callback: () => void) => Unsubscribe; +}; export const makeThreadViewportStore = () => { const scrollToBottomListeners = new Set<() => void>(); diff --git a/packages/react/src/runtimes/core/ThreadRuntimeCore.tsx b/packages/react/src/runtimes/core/ThreadRuntimeCore.tsx index eed90f1fc..feac6d858 100644 --- a/packages/react/src/runtimes/core/ThreadRuntimeCore.tsx +++ b/packages/react/src/runtimes/core/ThreadRuntimeCore.tsx @@ -7,16 +7,16 @@ import { ThreadComposerRuntimeCore, } from "./ComposerRuntimeCore"; -export type RuntimeCapabilities = Readonly<{ - switchToBranch: boolean; - edit: boolean; - reload: boolean; - cancel: boolean; - unstable_copy: boolean; - speech: boolean; - attachments: boolean; - feedback: boolean; -}>; +export type RuntimeCapabilities = { + readonly switchToBranch: boolean; + readonly edit: boolean; + readonly reload: boolean; + readonly cancel: boolean; + readonly unstable_copy: boolean; + readonly speech: boolean; + readonly attachments: boolean; + readonly feedback: boolean; +}; export type AddToolResultOptions = { messageId: string; @@ -34,19 +34,19 @@ export type ThreadSuggestion = { prompt: string; }; -export type SpeechState = Readonly<{ - messageId: string; - status: SpeechSynthesisAdapter.Status; -}>; +export type SpeechState = { + readonly messageId: string; + readonly status: SpeechSynthesisAdapter.Status; +}; -export type SubmittedFeedback = Readonly<{ - type: "negative" | "positive"; -}>; +export type SubmittedFeedback = { + readonly type: "negative" | "positive"; +}; export type ThreadMetadata = Readonly<{ - threadId: string; - state: "archived" | "regular" | "new" | "deleted"; - title?: string | undefined; + readonly threadId: string; + readonly state: "archived" | "regular" | "new" | "deleted"; + readonly title?: string | undefined; }>; export type ThreadRuntimeEventType = @@ -55,15 +55,14 @@ export type ThreadRuntimeEventType = | "run-start" | "model-config-update"; -export type ThreadMetadataRuntimeCore = ThreadMetadata & - Readonly<{ - create(title?: string): Promise; - rename(newTitle: string): Promise; - archive(): Promise; - unarchive(): Promise; - delete(): Promise; - subscribe(callback: () => void): Unsubscribe; - }>; +export type ThreadMetadataRuntimeCore = ThreadMetadata & { + create(title?: string): Promise; + rename(newTitle: string): Promise; + archive(): Promise; + unarchive(): Promise; + delete(): Promise; + subscribe(callback: () => void): Unsubscribe; +}; export type ThreadRuntimeCore = Readonly<{ metadata: ThreadMetadataRuntimeCore;