@tambo-ai/react 1.2.4 → 1.2.6
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/dist/context-helpers/current-interactables-context-helper.d.ts +1 -1
- package/dist/context-helpers/current-interactables-context-helper.d.ts.map +1 -1
- package/dist/context-helpers/current-interactables-context-helper.js +2 -2
- package/dist/context-helpers/current-interactables-context-helper.js.map +1 -1
- package/dist/context-helpers/current-interactables-context-helper.test.js +3 -3
- package/dist/context-helpers/current-interactables-context-helper.test.js.map +1 -1
- package/dist/mcp/tambo-mcp-provider.js.map +1 -1
- package/dist/model/tambo-interactable.d.ts +5 -2
- package/dist/model/tambo-interactable.d.ts.map +1 -1
- package/dist/model/tambo-interactable.js.map +1 -1
- package/dist/providers/tambo-interactables-additional-context.test.js.map +1 -1
- package/dist/v1/hooks/use-tambo-v1-send-message.d.ts.map +1 -1
- package/dist/v1/hooks/use-tambo-v1-send-message.js +5 -0
- package/dist/v1/hooks/use-tambo-v1-send-message.js.map +1 -1
- package/dist/v1/hooks/use-tambo-v1-stream-status.js.map +1 -1
- package/dist/v1/hooks/use-tambo-v1-suggestions.d.ts.map +1 -1
- package/dist/v1/hooks/use-tambo-v1-suggestions.js +14 -1
- package/dist/v1/hooks/use-tambo-v1-suggestions.js.map +1 -1
- package/dist/v1/hooks/use-tambo-v1-suggestions.test.js +79 -0
- package/dist/v1/hooks/use-tambo-v1-suggestions.test.js.map +1 -1
- package/dist/v1/hooks/use-tambo-v1.js.map +1 -1
- package/esm/context-helpers/current-interactables-context-helper.d.ts +1 -1
- package/esm/context-helpers/current-interactables-context-helper.d.ts.map +1 -1
- package/esm/context-helpers/current-interactables-context-helper.js +2 -2
- package/esm/context-helpers/current-interactables-context-helper.js.map +1 -1
- package/esm/context-helpers/current-interactables-context-helper.test.js +3 -3
- package/esm/context-helpers/current-interactables-context-helper.test.js.map +1 -1
- package/esm/mcp/tambo-mcp-provider.js.map +1 -1
- package/esm/model/tambo-interactable.d.ts +5 -2
- package/esm/model/tambo-interactable.d.ts.map +1 -1
- package/esm/model/tambo-interactable.js.map +1 -1
- package/esm/providers/tambo-interactables-additional-context.test.js.map +1 -1
- package/esm/v1/hooks/use-tambo-v1-send-message.d.ts.map +1 -1
- package/esm/v1/hooks/use-tambo-v1-send-message.js +5 -0
- package/esm/v1/hooks/use-tambo-v1-send-message.js.map +1 -1
- package/esm/v1/hooks/use-tambo-v1-stream-status.js.map +1 -1
- package/esm/v1/hooks/use-tambo-v1-suggestions.d.ts.map +1 -1
- package/esm/v1/hooks/use-tambo-v1-suggestions.js +14 -1
- package/esm/v1/hooks/use-tambo-v1-suggestions.js.map +1 -1
- package/esm/v1/hooks/use-tambo-v1-suggestions.test.js +79 -0
- package/esm/v1/hooks/use-tambo-v1-suggestions.test.js.map +1 -1
- package/esm/v1/hooks/use-tambo-v1.js.map +1 -1
- package/package.json +6 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-tambo-v1-send-message.js","sourceRoot":"","sources":["../../../src/v1/hooks/use-tambo-v1-send-message.ts"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\n/**\n * Send Message Hook\n *\n * React Query mutation hook for sending messages and handling streaming responses.\n */\n\nimport React, { useContext } from \"react\";\nimport { EventType, type RunErrorEvent } from \"@ag-ui/core\";\nimport {\n asTamboCustomEvent,\n type RunAwaitingInputEvent,\n} from \"@tambo-ai/client\";\nimport type TamboAI from \"@tambo-ai/typescript-sdk\";\nimport type { Stream } from \"@tambo-ai/typescript-sdk/core/streaming\";\nimport {\n useTamboClient,\n useTamboQueryClient,\n} from \"../../providers/tambo-client-provider\";\nimport { useTamboMutation } from \"../../hooks/react-query-hooks\";\nimport {\n TamboRegistryContext,\n type TamboRegistryContext as TamboRegistry,\n} from \"../../providers/tambo-registry-provider\";\nimport {\n useStreamDispatch,\n useStreamState,\n} from \"../providers/tambo-v1-stream-context\";\nimport { useTamboConfig } from \"../providers/tambo-v1-provider\";\nimport { useTamboAuthState } from \"./use-tambo-v1-auth-state\";\nimport { useTamboContextHelpers } from \"../../providers/tambo-context-helpers-provider\";\nimport type { InitialInputMessage, InputMessage } from \"../types/message\";\nimport type { ToolChoice } from \"@tambo-ai/client\";\nimport { isPlaceholderThreadId, type StreamAction } from \"@tambo-ai/client\";\nimport { toAvailableComponents, toAvailableTools } from \"@tambo-ai/client\";\nimport { handleEventStream } from \"@tambo-ai/client\";\nimport {\n executeAllPendingTools,\n createThrottledStreamableExecutor,\n} from \"@tambo-ai/client\";\nimport type { ToolResultContent } from \"@tambo-ai/typescript-sdk/resources/threads/threads\";\nimport type { RunCreateParams } from \"@tambo-ai/typescript-sdk/resources/threads/runs\";\nimport { ToolCallTracker } from \"@tambo-ai/client\";\n\n/**\n * Dispatches synthetic AG-UI events to show a user message in the thread.\n * @param dispatch - Stream state dispatcher\n * @param targetThreadId - Thread to add the message to\n * @param messageId - Stable ID for the user message\n * @param messageText - Text content of the message\n */\nfunction dispatchUserMessage(\n dispatch: React.Dispatch<StreamAction>,\n targetThreadId: string,\n messageId: string,\n messageText: string,\n): void {\n dispatch({\n type: \"EVENT\",\n threadId: targetThreadId,\n event: {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n role: \"user\" as const,\n },\n });\n\n dispatch({\n type: \"EVENT\",\n threadId: targetThreadId,\n event: {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n delta: messageText,\n },\n });\n\n dispatch({\n type: \"EVENT\",\n threadId: targetThreadId,\n event: {\n type: EventType.TEXT_MESSAGE_END,\n messageId,\n },\n });\n}\n\n/**\n * Dispatches synthetic events for tool results as optimistic local state.\n * The server doesn't echo these back for client-side tools.\n * @param dispatch - Stream state dispatcher\n * @param targetThreadId - Thread to add results to\n * @param toolResults - Tool execution results to dispatch\n */\nfunction dispatchToolResults(\n dispatch: React.Dispatch<StreamAction>,\n targetThreadId: string,\n toolResults: ToolResultContent[],\n): void {\n if (toolResults.length === 0) return;\n\n const toolResultMessageId = `msg_tool_result_${Date.now()}`;\n\n dispatch({\n type: \"EVENT\",\n threadId: targetThreadId,\n event: {\n type: EventType.TEXT_MESSAGE_START,\n messageId: toolResultMessageId,\n role: \"user\" as const,\n },\n });\n\n for (const result of toolResults) {\n dispatch({\n type: \"EVENT\",\n threadId: targetThreadId,\n event: {\n type: EventType.TOOL_CALL_RESULT,\n toolCallId: result.toolUseId,\n messageId: toolResultMessageId,\n content:\n result.content\n .filter((c) => c.type === \"text\")\n .map((c) => (c as { type: \"text\"; text: string }).text)\n .join(\"\") || JSON.stringify(result.content),\n },\n });\n }\n\n dispatch({\n type: \"EVENT\",\n threadId: targetThreadId,\n event: {\n type: EventType.TEXT_MESSAGE_END,\n messageId: toolResultMessageId,\n },\n });\n}\n\n/**\n * Checks whether a thread name should be auto-generated based on config\n * and the current thread state.\n * @param threadId - The thread ID (undefined or placeholder means no)\n * @param threadAlreadyHasName - Whether the thread already has a name\n * @param preMutationMessageCount - Message count before the current mutation\n * @param autoGenerateNameThreshold - Minimum message count to trigger generation\n * @returns Whether to generate a thread name\n */\nfunction shouldGenerateThreadName(\n threadId: string | undefined,\n threadAlreadyHasName: boolean,\n preMutationMessageCount: number,\n autoGenerateNameThreshold: number,\n): threadId is string {\n return (\n !threadAlreadyHasName &&\n !!threadId &&\n !isPlaceholderThreadId(threadId) &&\n // +2 accounts for the user message and assistant response just added\n preMutationMessageCount + 2 >= autoGenerateNameThreshold\n );\n}\n\n/**\n * Generates a thread name via the beta API, dispatches the name update,\n * and invalidates the thread list cache. Errors are logged, never thrown.\n * @param client - The Tambo API client\n * @param dispatch - Stream state dispatcher\n * @param queryClient - React Query client for cache invalidation\n * @param threadId - The thread to generate a name for\n */\nasync function generateThreadName(\n client: TamboAI,\n dispatch: React.Dispatch<StreamAction>,\n queryClient: ReturnType<typeof useTamboQueryClient>,\n threadId: string,\n): Promise<void> {\n try {\n const threadWithName = await client.beta.threads.generateName(threadId);\n if (threadWithName.name) {\n dispatch({\n type: \"UPDATE_THREAD_NAME\",\n threadId,\n name: threadWithName.name,\n });\n await queryClient.invalidateQueries({\n queryKey: [\"v1-threads\", \"list\"],\n });\n }\n } catch (error) {\n console.error(\n \"[useTamboSendMessage] Failed to auto-generate thread name:\",\n error,\n );\n }\n}\n\n/**\n * Options for sending a message\n */\nexport interface SendMessageOptions {\n /**\n * The message to send\n */\n message: InputMessage;\n\n /**\n * User message text for optimistic display.\n * If provided, synthetic AG-UI events will be dispatched to show\n * the user message in the thread immediately after getting the threadId.\n */\n userMessageText?: string;\n\n /**\n * Enable debug logging for the stream\n */\n debug?: boolean;\n\n /**\n * How the model should use tools. Defaults to \"auto\".\n * - \"auto\": Model decides whether to use tools\n * - \"required\": Model must use at least one tool\n * - \"none\": Model cannot use tools\n * - { name: \"toolName\" }: Model must use the specified tool\n */\n toolChoice?: ToolChoice;\n}\n\n/**\n * Parameters for creating a run stream\n */\nexport interface CreateRunStreamParams {\n client: TamboAI;\n threadId: string | undefined;\n message: InputMessage;\n registry: TamboRegistry;\n userKey: string | undefined;\n /**\n * Previous run ID for continuing a thread with existing messages.\n * Required when threadId is provided and the thread has previous runs.\n */\n previousRunId: string | undefined;\n /**\n * Additional context gathered from context helpers (including interactables).\n * Merged into the message's additionalContext before sending.\n */\n additionalContext?: Record<string, unknown>;\n /**\n * How the model should use tools.\n */\n toolChoice?: ToolChoice;\n /**\n * Initial messages to seed the thread with when creating a new thread.\n * Only used when threadId is undefined (new thread creation).\n */\n initialMessages?: InitialInputMessage[];\n}\n\n/**\n * Stream types from the SDK\n */\ntype RunStream = Stream<TamboAI.Threads.Runs.RunRunResponse>;\ntype CreateStream = Stream<TamboAI.Threads.Runs.RunCreateResponse>;\n\n/**\n * Result from creating a run stream\n */\nexport interface CreateRunStreamResult {\n stream: RunStream | CreateStream;\n initialThreadId: string | undefined;\n}\n\n/**\n * Parameters for executing tools and continuing the run\n */\ninterface ExecuteToolsParams {\n event: RunAwaitingInputEvent;\n toolTracker: ToolCallTracker;\n registry: TamboRegistry;\n client: TamboAI;\n threadId: string;\n runId: string;\n userKey: string | undefined;\n additionalContext?: Record<string, unknown>;\n toolChoice?: ToolChoice;\n}\n\n/**\n * Result from executing tools and continuing the run\n */\ninterface ExecuteToolsResult {\n stream: RunStream;\n toolResults: ToolResultContent[];\n}\n\n/**\n * Executes pending tools and returns a continuation stream.\n *\n * This function does NOT process the continuation stream - it just executes\n * the tools and returns the new stream for the caller to process. This enables\n * the flat loop pattern that correctly handles multi-round tool execution.\n * @param params - The parameters for tool execution\n * @returns The continuation stream and tool results for optimistic local state updates\n */\nasync function executeToolsAndContinue(\n params: ExecuteToolsParams,\n): Promise<ExecuteToolsResult> {\n const {\n event,\n toolTracker,\n registry,\n client,\n threadId,\n runId,\n userKey,\n additionalContext,\n toolChoice,\n } = params;\n\n const pendingToolCallIds = event.value.pendingToolCalls.map(\n (tc) => tc.toolCallId,\n );\n const toolCallsToExecute = toolTracker.getToolCallsById(pendingToolCallIds);\n\n // Execute tools\n const toolResults = await executeAllPendingTools(\n toolCallsToExecute,\n registry.toolRegistry,\n );\n\n // Clear executed tool calls before continuing\n toolTracker.clearToolCalls(pendingToolCallIds);\n\n // Return the continuation stream and tool results\n const stream = await client.threads.runs.run(threadId, {\n message: {\n role: \"user\",\n content: toolResults,\n additionalContext,\n },\n previousRunId: runId,\n availableComponents: toAvailableComponents(registry.componentList),\n tools: toAvailableTools(registry.toolRegistry),\n userKey,\n toolChoice,\n });\n\n return { stream, toolResults };\n}\n\n/**\n * Creates a run stream by calling the appropriate API method.\n *\n * If threadId is provided, runs on existing thread via client.threads.runs.run().\n * If no threadId, creates new thread via client.threads.runs.create().\n * @param params - The parameters for creating the run stream\n * @returns The stream and initial thread ID (undefined if creating new thread)\n */\nexport async function createRunStream(\n params: CreateRunStreamParams,\n): Promise<CreateRunStreamResult> {\n const {\n client,\n threadId,\n message,\n registry,\n userKey,\n previousRunId,\n additionalContext,\n toolChoice,\n initialMessages,\n } = params;\n\n // Merge helper context with any caller-provided additionalContext on the message\n const mergedContext =\n additionalContext || message.additionalContext\n ? {\n ...((message.additionalContext as Record<string, unknown>) ?? {}),\n ...additionalContext,\n }\n : undefined;\n const messageWithContext: InputMessage = mergedContext\n ? { ...message, additionalContext: mergedContext }\n : message;\n\n // Convert registry components/tools to API format\n const availableComponents = toAvailableComponents(registry.componentList);\n const availableTools = toAvailableTools(registry.toolRegistry);\n\n if (threadId) {\n // Run on existing thread\n const stream = await client.threads.runs.run(threadId, {\n message: messageWithContext,\n availableComponents,\n tools: availableTools,\n userKey,\n previousRunId,\n toolChoice,\n });\n return { stream, initialThreadId: threadId };\n } else {\n // Create new thread - include initialMessages if provided\n const threadConfig: RunCreateParams.Thread = {};\n if (userKey) {\n threadConfig.userKey = userKey;\n }\n if (initialMessages?.length) {\n threadConfig.initialMessages = initialMessages;\n }\n\n const stream = await client.threads.runs.create({\n message: messageWithContext,\n availableComponents,\n tools: availableTools,\n thread: Object.keys(threadConfig).length > 0 ? threadConfig : undefined,\n toolChoice,\n });\n // threadId will be extracted from first event (RUN_STARTED)\n return { stream, initialThreadId: undefined };\n }\n}\n\n/**\n * Hook to send a message and handle streaming responses.\n *\n * This hook handles two scenarios:\n * - If threadId provided: runs on existing thread via client.threads.runs.run()\n * - If no threadId: creates new thread via client.threads.runs.create()\n *\n * The hook:\n * - Sends a user message to the API\n * - Streams AG-UI events in real-time\n * - Dispatches events to the stream reducer\n * - Extracts threadId from events when creating new thread\n * - Handles tool execution (Phase 6)\n * - Invalidates thread queries on completion\n * @param threadId - Optional thread ID to send message to. If not provided, creates new thread\n * @returns React Query mutation object with threadId in mutation result\n * @example\n * ```tsx\n * function ChatInput({ threadId }: { threadId?: string }) {\n * const sendMessage = useTamboSendMessage(threadId);\n *\n * const handleSubmit = async (text: string) => {\n * const result = await sendMessage.mutateAsync({\n * message: {\n * role: \"user\",\n * content: [{ type: \"text\", text }],\n * },\n * });\n *\n * // If threadId wasn't provided, a new thread was created\n * if (!threadId) {\n * console.log(\"Created thread:\", result.threadId);\n * }\n * };\n *\n * return (\n * <div>\n * <input onSubmit={handleSubmit} />\n * {sendMessage.isPending && <Spinner />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useTamboSendMessage(threadId?: string) {\n const client = useTamboClient();\n const dispatch = useStreamDispatch();\n const streamState = useStreamState();\n const {\n userKey,\n autoGenerateThreadName = true,\n autoGenerateNameThreshold = 3,\n initialMessages,\n } = useTamboConfig();\n const registry = useContext(TamboRegistryContext);\n const queryClient = useTamboQueryClient();\n const { getAdditionalContext } = useTamboContextHelpers();\n const authState = useTamboAuthState();\n\n if (!registry) {\n throw new Error(\n \"useTamboSendMessage must be used within TamboRegistryProvider\",\n );\n }\n\n // Placeholder ID isn't a valid API thread ID - treat as new thread creation\n const isNewThread = isPlaceholderThreadId(threadId);\n const apiThreadId = isNewThread ? undefined : threadId;\n\n // Get previousRunId from the thread's streaming state (active run) or\n // lastCompletedRunId (persisted after run finishes / loaded from API).\n // The latter is essential after page reload when streaming state is gone.\n const threadState = apiThreadId\n ? streamState.threadMap[apiThreadId]\n : undefined;\n const previousRunId =\n threadState?.streaming.runId ?? threadState?.lastCompletedRunId;\n\n return useTamboMutation({\n mutationFn: async (options: SendMessageOptions) => {\n if (authState.status !== \"identified\") {\n const messages: Record<string, string> = {\n unauthenticated:\n \"Cannot send message: no userKey or userToken provided. \" +\n \"Configure authentication in TamboProvider.\",\n exchanging:\n \"Cannot send message: token exchange is still in progress. \" +\n \"Wait for authentication to complete.\",\n error:\n \"Cannot send message: token exchange failed. \" +\n \"Check your userToken configuration.\",\n invalid:\n \"Cannot send message: both userKey and userToken were provided. \" +\n \"You must provide one or the other, not both.\",\n };\n throw new Error(messages[authState.status]);\n }\n\n const { message, userMessageText, debug = false, toolChoice } = options;\n\n // Capture pre-mutation state for auto thread name generation\n const existingThread = streamState.threadMap[apiThreadId ?? \"\"];\n const preMutationMessageCount =\n existingThread?.thread.messages.length ?? 0;\n const threadAlreadyHasName = !!existingThread?.thread.name;\n\n const toolTracker = new ToolCallTracker(registry.toolRegistry);\n const throttledStreamable = createThrottledStreamableExecutor(\n toolTracker,\n registry.toolRegistry,\n );\n\n // Generate a stable message ID for the user message\n const userMessageId = userMessageText\n ? `user_msg_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`\n : undefined;\n\n // Add user message immediately for instant feedback\n // Use threadId (which could be temp_xxx for new threads) for display\n if (threadId && userMessageText && userMessageId) {\n dispatchUserMessage(dispatch, threadId, userMessageId, userMessageText);\n }\n\n // Gather additional context from all registered context helpers.\n // TODO: This snapshot is captured once and reused for the entire multi-round\n // tool loop. If interactables change during streaming (e.g. user interactions),\n // continuations will send stale context. Re-gather before each continuation\n // if freshness matters.\n const helperContexts = await getAdditionalContext();\n const additionalContext: Record<string, unknown> = {};\n for (const helperContext of helperContexts) {\n additionalContext[helperContext.name] = helperContext.context;\n }\n\n // Create the run stream\n // Include initialMessages only on first send (new thread creation)\n const { stream, initialThreadId } = await createRunStream({\n client,\n threadId: apiThreadId,\n message,\n registry,\n userKey,\n previousRunId,\n additionalContext,\n toolChoice,\n initialMessages: isNewThread ? initialMessages : undefined,\n });\n\n let actualThreadId = initialThreadId;\n let runId: string | undefined;\n let currentStream: CreateRunStreamResult[\"stream\"] = stream;\n\n try {\n // Outer loop handles stream replacement for multi-round tool execution.\n // When we hit awaiting_input, we execute tools, get a new stream, and continue.\n // This flat loop pattern correctly handles tool→AI→tool→AI chains.\n while (true) {\n let pendingAwaitingInput: RunAwaitingInputEvent | undefined;\n\n // Process current stream until completion or awaiting_input\n for await (const event of handleEventStream(currentStream, {\n debug,\n })) {\n // Extract threadId and runId from RUN_STARTED event\n if (event.type === EventType.RUN_STARTED) {\n runId = event.runId;\n actualThreadId ??= event.threadId;\n\n // For threads with no ID at all: add user message now that we have the real threadId\n // Note: temp thread migration (temp_xxx -> real ID) is handled automatically\n // by the reducer when it sees RUN_STARTED with a different threadId\n if (!threadId && userMessageText && userMessageId) {\n dispatchUserMessage(\n dispatch,\n actualThreadId,\n userMessageId,\n userMessageText,\n );\n }\n } else if (!actualThreadId) {\n throw new Error(\n `Expected first event to be RUN_STARTED with threadId, got: ${event.type}`,\n );\n }\n\n toolTracker.handleEvent(event);\n\n // Parse partial JSON once for TOOL_CALL_ARGS — reused by both dispatch and streamable execution\n const parsedToolArgs =\n event.type === EventType.TOOL_CALL_ARGS\n ? toolTracker.parsePartialArgs(event.toolCallId)\n : undefined;\n\n dispatch({\n type: \"EVENT\",\n event,\n threadId: actualThreadId,\n parsedToolArgs,\n toolSchemas: toolTracker.toolSchemas,\n });\n\n // Schedule debounced streamable tool execution with the same pre-parsed args\n if (parsedToolArgs && event.type === EventType.TOOL_CALL_ARGS) {\n throttledStreamable.schedule(event.toolCallId, parsedToolArgs);\n }\n\n // Handle custom events\n if (event.type === EventType.CUSTOM) {\n const customEvent = asTamboCustomEvent(event);\n\n if (customEvent?.name === \"tambo.run.awaiting_input\") {\n pendingAwaitingInput = customEvent;\n break; // Exit stream loop to handle tool execution\n }\n }\n }\n\n // Flush any pending debounced streamable calls before continuing\n throttledStreamable.flush();\n\n // If stream finished without awaiting_input, we're done\n if (!pendingAwaitingInput) {\n break;\n }\n\n // Execute tools and get continuation stream\n // These checks should never fail since awaiting_input comes after RUN_STARTED\n if (!runId || !actualThreadId) {\n throw new Error(\n \"Cannot continue run after awaiting_input: missing runId or threadId\",\n );\n }\n\n const { stream: continuationStream, toolResults } =\n await executeToolsAndContinue({\n event: pendingAwaitingInput,\n toolTracker,\n registry,\n client,\n threadId: actualThreadId,\n runId,\n userKey,\n additionalContext,\n toolChoice,\n });\n\n dispatchToolResults(dispatch, actualThreadId, toolResults);\n\n currentStream = continuationStream;\n }\n\n return {\n threadId: actualThreadId,\n preMutationMessageCount,\n threadAlreadyHasName,\n };\n } catch (error) {\n // Dispatch a synthetic RUN_ERROR event to clean up thread state\n // This ensures the thread doesn't stay stuck in \"streaming\" status\n if (actualThreadId) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown streaming error\";\n const errorEvent: RunErrorEvent = {\n type: EventType.RUN_ERROR,\n message: errorMessage,\n };\n dispatch({\n type: \"EVENT\",\n event: errorEvent,\n threadId: actualThreadId,\n });\n }\n throw error;\n }\n },\n onSuccess: async (result) => {\n await queryClient.invalidateQueries({\n queryKey: [\"v1-threads\", result.threadId],\n });\n\n if (\n autoGenerateThreadName &&\n shouldGenerateThreadName(\n result.threadId,\n result.threadAlreadyHasName,\n result.preMutationMessageCount,\n autoGenerateNameThreshold,\n )\n ) {\n await generateThreadName(\n client,\n dispatch,\n queryClient,\n result.threadId,\n );\n }\n },\n onError: (error) => {\n console.error(\"[useTamboSendMessage] Mutation failed:\", error);\n },\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"use-tambo-v1-send-message.js","sourceRoot":"","sources":["../../../src/v1/hooks/use-tambo-v1-send-message.ts"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\n/**\n * Send Message Hook\n *\n * React Query mutation hook for sending messages and handling streaming responses.\n */\n\nimport React, { useContext } from \"react\";\nimport { EventType, type RunErrorEvent } from \"@ag-ui/core\";\nimport {\n asTamboCustomEvent,\n type RunAwaitingInputEvent,\n} from \"@tambo-ai/client\";\nimport type TamboAI from \"@tambo-ai/typescript-sdk\";\nimport type { Stream } from \"@tambo-ai/typescript-sdk/core/streaming\";\nimport {\n useTamboClient,\n useTamboQueryClient,\n} from \"../../providers/tambo-client-provider\";\nimport { useTamboMutation } from \"../../hooks/react-query-hooks\";\nimport {\n TamboRegistryContext,\n type TamboRegistryContext as TamboRegistry,\n} from \"../../providers/tambo-registry-provider\";\nimport {\n useStreamDispatch,\n useStreamState,\n} from \"../providers/tambo-v1-stream-context\";\nimport { useTamboConfig } from \"../providers/tambo-v1-provider\";\nimport { useTamboAuthState } from \"./use-tambo-v1-auth-state\";\nimport { useTamboContextHelpers } from \"../../providers/tambo-context-helpers-provider\";\nimport { useTamboInteractable } from \"../../providers/tambo-interactable-provider\";\nimport type { InitialInputMessage, InputMessage } from \"../types/message\";\nimport type { ToolChoice } from \"@tambo-ai/client\";\nimport { isPlaceholderThreadId, type StreamAction } from \"@tambo-ai/client\";\nimport { toAvailableComponents, toAvailableTools } from \"@tambo-ai/client\";\nimport { handleEventStream } from \"@tambo-ai/client\";\nimport {\n executeAllPendingTools,\n createThrottledStreamableExecutor,\n} from \"@tambo-ai/client\";\nimport type { ToolResultContent } from \"@tambo-ai/typescript-sdk/resources/threads/threads\";\nimport type { RunCreateParams } from \"@tambo-ai/typescript-sdk/resources/threads/runs\";\nimport { ToolCallTracker } from \"@tambo-ai/client\";\n\n/**\n * Dispatches synthetic AG-UI events to show a user message in the thread.\n * @param dispatch - Stream state dispatcher\n * @param targetThreadId - Thread to add the message to\n * @param messageId - Stable ID for the user message\n * @param messageText - Text content of the message\n */\nfunction dispatchUserMessage(\n dispatch: React.Dispatch<StreamAction>,\n targetThreadId: string,\n messageId: string,\n messageText: string,\n): void {\n dispatch({\n type: \"EVENT\",\n threadId: targetThreadId,\n event: {\n type: EventType.TEXT_MESSAGE_START,\n messageId,\n role: \"user\" as const,\n },\n });\n\n dispatch({\n type: \"EVENT\",\n threadId: targetThreadId,\n event: {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId,\n delta: messageText,\n },\n });\n\n dispatch({\n type: \"EVENT\",\n threadId: targetThreadId,\n event: {\n type: EventType.TEXT_MESSAGE_END,\n messageId,\n },\n });\n}\n\n/**\n * Dispatches synthetic events for tool results as optimistic local state.\n * The server doesn't echo these back for client-side tools.\n * @param dispatch - Stream state dispatcher\n * @param targetThreadId - Thread to add results to\n * @param toolResults - Tool execution results to dispatch\n */\nfunction dispatchToolResults(\n dispatch: React.Dispatch<StreamAction>,\n targetThreadId: string,\n toolResults: ToolResultContent[],\n): void {\n if (toolResults.length === 0) return;\n\n const toolResultMessageId = `msg_tool_result_${Date.now()}`;\n\n dispatch({\n type: \"EVENT\",\n threadId: targetThreadId,\n event: {\n type: EventType.TEXT_MESSAGE_START,\n messageId: toolResultMessageId,\n role: \"user\" as const,\n },\n });\n\n for (const result of toolResults) {\n dispatch({\n type: \"EVENT\",\n threadId: targetThreadId,\n event: {\n type: EventType.TOOL_CALL_RESULT,\n toolCallId: result.toolUseId,\n messageId: toolResultMessageId,\n content:\n result.content\n .filter((c) => c.type === \"text\")\n .map((c) => (c as { type: \"text\"; text: string }).text)\n .join(\"\") || JSON.stringify(result.content),\n },\n });\n }\n\n dispatch({\n type: \"EVENT\",\n threadId: targetThreadId,\n event: {\n type: EventType.TEXT_MESSAGE_END,\n messageId: toolResultMessageId,\n },\n });\n}\n\n/**\n * Checks whether a thread name should be auto-generated based on config\n * and the current thread state.\n * @param threadId - The thread ID (undefined or placeholder means no)\n * @param threadAlreadyHasName - Whether the thread already has a name\n * @param preMutationMessageCount - Message count before the current mutation\n * @param autoGenerateNameThreshold - Minimum message count to trigger generation\n * @returns Whether to generate a thread name\n */\nfunction shouldGenerateThreadName(\n threadId: string | undefined,\n threadAlreadyHasName: boolean,\n preMutationMessageCount: number,\n autoGenerateNameThreshold: number,\n): threadId is string {\n return (\n !threadAlreadyHasName &&\n !!threadId &&\n !isPlaceholderThreadId(threadId) &&\n // +2 accounts for the user message and assistant response just added\n preMutationMessageCount + 2 >= autoGenerateNameThreshold\n );\n}\n\n/**\n * Generates a thread name via the beta API, dispatches the name update,\n * and invalidates the thread list cache. Errors are logged, never thrown.\n * @param client - The Tambo API client\n * @param dispatch - Stream state dispatcher\n * @param queryClient - React Query client for cache invalidation\n * @param threadId - The thread to generate a name for\n */\nasync function generateThreadName(\n client: TamboAI,\n dispatch: React.Dispatch<StreamAction>,\n queryClient: ReturnType<typeof useTamboQueryClient>,\n threadId: string,\n): Promise<void> {\n try {\n const threadWithName = await client.beta.threads.generateName(threadId);\n if (threadWithName.name) {\n dispatch({\n type: \"UPDATE_THREAD_NAME\",\n threadId,\n name: threadWithName.name,\n });\n await queryClient.invalidateQueries({\n queryKey: [\"v1-threads\", \"list\"],\n });\n }\n } catch (error) {\n console.error(\n \"[useTamboSendMessage] Failed to auto-generate thread name:\",\n error,\n );\n }\n}\n\n/**\n * Options for sending a message\n */\nexport interface SendMessageOptions {\n /**\n * The message to send\n */\n message: InputMessage;\n\n /**\n * User message text for optimistic display.\n * If provided, synthetic AG-UI events will be dispatched to show\n * the user message in the thread immediately after getting the threadId.\n */\n userMessageText?: string;\n\n /**\n * Enable debug logging for the stream\n */\n debug?: boolean;\n\n /**\n * How the model should use tools. Defaults to \"auto\".\n * - \"auto\": Model decides whether to use tools\n * - \"required\": Model must use at least one tool\n * - \"none\": Model cannot use tools\n * - { name: \"toolName\" }: Model must use the specified tool\n */\n toolChoice?: ToolChoice;\n}\n\n/**\n * Parameters for creating a run stream\n */\nexport interface CreateRunStreamParams {\n client: TamboAI;\n threadId: string | undefined;\n message: InputMessage;\n registry: TamboRegistry;\n userKey: string | undefined;\n /**\n * Previous run ID for continuing a thread with existing messages.\n * Required when threadId is provided and the thread has previous runs.\n */\n previousRunId: string | undefined;\n /**\n * Additional context gathered from context helpers (including interactables).\n * Merged into the message's additionalContext before sending.\n */\n additionalContext?: Record<string, unknown>;\n /**\n * How the model should use tools.\n */\n toolChoice?: ToolChoice;\n /**\n * Initial messages to seed the thread with when creating a new thread.\n * Only used when threadId is undefined (new thread creation).\n */\n initialMessages?: InitialInputMessage[];\n}\n\n/**\n * Stream types from the SDK\n */\ntype RunStream = Stream<TamboAI.Threads.Runs.RunRunResponse>;\ntype CreateStream = Stream<TamboAI.Threads.Runs.RunCreateResponse>;\n\n/**\n * Result from creating a run stream\n */\nexport interface CreateRunStreamResult {\n stream: RunStream | CreateStream;\n initialThreadId: string | undefined;\n}\n\n/**\n * Parameters for executing tools and continuing the run\n */\ninterface ExecuteToolsParams {\n event: RunAwaitingInputEvent;\n toolTracker: ToolCallTracker;\n registry: TamboRegistry;\n client: TamboAI;\n threadId: string;\n runId: string;\n userKey: string | undefined;\n additionalContext?: Record<string, unknown>;\n toolChoice?: ToolChoice;\n}\n\n/**\n * Result from executing tools and continuing the run\n */\ninterface ExecuteToolsResult {\n stream: RunStream;\n toolResults: ToolResultContent[];\n}\n\n/**\n * Executes pending tools and returns a continuation stream.\n *\n * This function does NOT process the continuation stream - it just executes\n * the tools and returns the new stream for the caller to process. This enables\n * the flat loop pattern that correctly handles multi-round tool execution.\n * @param params - The parameters for tool execution\n * @returns The continuation stream and tool results for optimistic local state updates\n */\nasync function executeToolsAndContinue(\n params: ExecuteToolsParams,\n): Promise<ExecuteToolsResult> {\n const {\n event,\n toolTracker,\n registry,\n client,\n threadId,\n runId,\n userKey,\n additionalContext,\n toolChoice,\n } = params;\n\n const pendingToolCallIds = event.value.pendingToolCalls.map(\n (tc) => tc.toolCallId,\n );\n const toolCallsToExecute = toolTracker.getToolCallsById(pendingToolCallIds);\n\n // Execute tools\n const toolResults = await executeAllPendingTools(\n toolCallsToExecute,\n registry.toolRegistry,\n );\n\n // Clear executed tool calls before continuing\n toolTracker.clearToolCalls(pendingToolCallIds);\n\n // Return the continuation stream and tool results\n const stream = await client.threads.runs.run(threadId, {\n message: {\n role: \"user\",\n content: toolResults,\n additionalContext,\n },\n previousRunId: runId,\n availableComponents: toAvailableComponents(registry.componentList),\n tools: toAvailableTools(registry.toolRegistry),\n userKey,\n toolChoice,\n });\n\n return { stream, toolResults };\n}\n\n/**\n * Creates a run stream by calling the appropriate API method.\n *\n * If threadId is provided, runs on existing thread via client.threads.runs.run().\n * If no threadId, creates new thread via client.threads.runs.create().\n * @param params - The parameters for creating the run stream\n * @returns The stream and initial thread ID (undefined if creating new thread)\n */\nexport async function createRunStream(\n params: CreateRunStreamParams,\n): Promise<CreateRunStreamResult> {\n const {\n client,\n threadId,\n message,\n registry,\n userKey,\n previousRunId,\n additionalContext,\n toolChoice,\n initialMessages,\n } = params;\n\n // Merge helper context with any caller-provided additionalContext on the message\n const mergedContext =\n additionalContext || message.additionalContext\n ? {\n ...((message.additionalContext as Record<string, unknown>) ?? {}),\n ...additionalContext,\n }\n : undefined;\n const messageWithContext: InputMessage = mergedContext\n ? { ...message, additionalContext: mergedContext }\n : message;\n\n // Convert registry components/tools to API format\n const availableComponents = toAvailableComponents(registry.componentList);\n const availableTools = toAvailableTools(registry.toolRegistry);\n\n if (threadId) {\n // Run on existing thread\n const stream = await client.threads.runs.run(threadId, {\n message: messageWithContext,\n availableComponents,\n tools: availableTools,\n userKey,\n previousRunId,\n toolChoice,\n });\n return { stream, initialThreadId: threadId };\n } else {\n // Create new thread - include initialMessages if provided\n const threadConfig: RunCreateParams.Thread = {};\n if (userKey) {\n threadConfig.userKey = userKey;\n }\n if (initialMessages?.length) {\n threadConfig.initialMessages = initialMessages;\n }\n\n const stream = await client.threads.runs.create({\n message: messageWithContext,\n availableComponents,\n tools: availableTools,\n thread: Object.keys(threadConfig).length > 0 ? threadConfig : undefined,\n toolChoice,\n });\n // threadId will be extracted from first event (RUN_STARTED)\n return { stream, initialThreadId: undefined };\n }\n}\n\n/**\n * Hook to send a message and handle streaming responses.\n *\n * This hook handles two scenarios:\n * - If threadId provided: runs on existing thread via client.threads.runs.run()\n * - If no threadId: creates new thread via client.threads.runs.create()\n *\n * The hook:\n * - Sends a user message to the API\n * - Streams AG-UI events in real-time\n * - Dispatches events to the stream reducer\n * - Extracts threadId from events when creating new thread\n * - Handles tool execution (Phase 6)\n * - Invalidates thread queries on completion\n * @param threadId - Optional thread ID to send message to. If not provided, creates new thread\n * @returns React Query mutation object with threadId in mutation result\n * @example\n * ```tsx\n * function ChatInput({ threadId }: { threadId?: string }) {\n * const sendMessage = useTamboSendMessage(threadId);\n *\n * const handleSubmit = async (text: string) => {\n * const result = await sendMessage.mutateAsync({\n * message: {\n * role: \"user\",\n * content: [{ type: \"text\", text }],\n * },\n * });\n *\n * // If threadId wasn't provided, a new thread was created\n * if (!threadId) {\n * console.log(\"Created thread:\", result.threadId);\n * }\n * };\n *\n * return (\n * <div>\n * <input onSubmit={handleSubmit} />\n * {sendMessage.isPending && <Spinner />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useTamboSendMessage(threadId?: string) {\n const client = useTamboClient();\n const dispatch = useStreamDispatch();\n const streamState = useStreamState();\n const {\n userKey,\n autoGenerateThreadName = true,\n autoGenerateNameThreshold = 3,\n initialMessages,\n } = useTamboConfig();\n const registry = useContext(TamboRegistryContext);\n const queryClient = useTamboQueryClient();\n const { getAdditionalContext } = useTamboContextHelpers();\n const { clearInteractableSelections } = useTamboInteractable();\n const authState = useTamboAuthState();\n\n if (!registry) {\n throw new Error(\n \"useTamboSendMessage must be used within TamboRegistryProvider\",\n );\n }\n\n // Placeholder ID isn't a valid API thread ID - treat as new thread creation\n const isNewThread = isPlaceholderThreadId(threadId);\n const apiThreadId = isNewThread ? undefined : threadId;\n\n // Get previousRunId from the thread's streaming state (active run) or\n // lastCompletedRunId (persisted after run finishes / loaded from API).\n // The latter is essential after page reload when streaming state is gone.\n const threadState = apiThreadId\n ? streamState.threadMap[apiThreadId]\n : undefined;\n const previousRunId =\n threadState?.streaming.runId ?? threadState?.lastCompletedRunId;\n\n return useTamboMutation({\n mutationFn: async (options: SendMessageOptions) => {\n if (authState.status !== \"identified\") {\n const messages: Record<string, string> = {\n unauthenticated:\n \"Cannot send message: no userKey or userToken provided. \" +\n \"Configure authentication in TamboProvider.\",\n exchanging:\n \"Cannot send message: token exchange is still in progress. \" +\n \"Wait for authentication to complete.\",\n error:\n \"Cannot send message: token exchange failed. \" +\n \"Check your userToken configuration.\",\n invalid:\n \"Cannot send message: both userKey and userToken were provided. \" +\n \"You must provide one or the other, not both.\",\n };\n throw new Error(messages[authState.status]);\n }\n\n const { message, userMessageText, debug = false, toolChoice } = options;\n\n // Capture pre-mutation state for auto thread name generation\n const existingThread = streamState.threadMap[apiThreadId ?? \"\"];\n const preMutationMessageCount =\n existingThread?.thread.messages.length ?? 0;\n const threadAlreadyHasName = !!existingThread?.thread.name;\n\n const toolTracker = new ToolCallTracker(registry.toolRegistry);\n const throttledStreamable = createThrottledStreamableExecutor(\n toolTracker,\n registry.toolRegistry,\n );\n\n // Generate a stable message ID for the user message\n const userMessageId = userMessageText\n ? `user_msg_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`\n : undefined;\n\n // Add user message immediately for instant feedback\n // Use threadId (which could be temp_xxx for new threads) for display\n if (threadId && userMessageText && userMessageId) {\n dispatchUserMessage(dispatch, threadId, userMessageId, userMessageText);\n }\n\n // Gather additional context from all registered context helpers.\n // TODO: This snapshot is captured once and reused for the entire multi-round\n // tool loop. If interactables change during streaming (e.g. user interactions),\n // continuations will send stale context. Re-gather before each continuation\n // if freshness matters.\n const helperContexts = await getAdditionalContext();\n const additionalContext: Record<string, unknown> = {};\n for (const helperContext of helperContexts) {\n additionalContext[helperContext.name] = helperContext.context;\n }\n\n // Clear interactable selections after capturing context so they only\n // apply to this single message send.\n clearInteractableSelections();\n\n // Create the run stream\n // Include initialMessages only on first send (new thread creation)\n const { stream, initialThreadId } = await createRunStream({\n client,\n threadId: apiThreadId,\n message,\n registry,\n userKey,\n previousRunId,\n additionalContext,\n toolChoice,\n initialMessages: isNewThread ? initialMessages : undefined,\n });\n\n let actualThreadId = initialThreadId;\n let runId: string | undefined;\n let currentStream: CreateRunStreamResult[\"stream\"] = stream;\n\n try {\n // Outer loop handles stream replacement for multi-round tool execution.\n // When we hit awaiting_input, we execute tools, get a new stream, and continue.\n // This flat loop pattern correctly handles tool→AI→tool→AI chains.\n while (true) {\n let pendingAwaitingInput: RunAwaitingInputEvent | undefined;\n\n // Process current stream until completion or awaiting_input\n for await (const event of handleEventStream(currentStream, {\n debug,\n })) {\n // Extract threadId and runId from RUN_STARTED event\n if (event.type === EventType.RUN_STARTED) {\n runId = event.runId;\n actualThreadId ??= event.threadId;\n\n // For threads with no ID at all: add user message now that we have the real threadId\n // Note: temp thread migration (temp_xxx -> real ID) is handled automatically\n // by the reducer when it sees RUN_STARTED with a different threadId\n if (!threadId && userMessageText && userMessageId) {\n dispatchUserMessage(\n dispatch,\n actualThreadId,\n userMessageId,\n userMessageText,\n );\n }\n } else if (!actualThreadId) {\n throw new Error(\n `Expected first event to be RUN_STARTED with threadId, got: ${event.type}`,\n );\n }\n\n toolTracker.handleEvent(event);\n\n // Parse partial JSON once for TOOL_CALL_ARGS — reused by both dispatch and streamable execution\n const parsedToolArgs =\n event.type === EventType.TOOL_CALL_ARGS\n ? toolTracker.parsePartialArgs(event.toolCallId)\n : undefined;\n\n dispatch({\n type: \"EVENT\",\n event,\n threadId: actualThreadId,\n parsedToolArgs,\n toolSchemas: toolTracker.toolSchemas,\n });\n\n // Schedule debounced streamable tool execution with the same pre-parsed args\n if (parsedToolArgs && event.type === EventType.TOOL_CALL_ARGS) {\n throttledStreamable.schedule(event.toolCallId, parsedToolArgs);\n }\n\n // Handle custom events\n if (event.type === EventType.CUSTOM) {\n const customEvent = asTamboCustomEvent(event);\n\n if (customEvent?.name === \"tambo.run.awaiting_input\") {\n pendingAwaitingInput = customEvent;\n break; // Exit stream loop to handle tool execution\n }\n }\n }\n\n // Flush any pending debounced streamable calls before continuing\n throttledStreamable.flush();\n\n // If stream finished without awaiting_input, we're done\n if (!pendingAwaitingInput) {\n break;\n }\n\n // Execute tools and get continuation stream\n // These checks should never fail since awaiting_input comes after RUN_STARTED\n if (!runId || !actualThreadId) {\n throw new Error(\n \"Cannot continue run after awaiting_input: missing runId or threadId\",\n );\n }\n\n const { stream: continuationStream, toolResults } =\n await executeToolsAndContinue({\n event: pendingAwaitingInput,\n toolTracker,\n registry,\n client,\n threadId: actualThreadId,\n runId,\n userKey,\n additionalContext,\n toolChoice,\n });\n\n dispatchToolResults(dispatch, actualThreadId, toolResults);\n\n currentStream = continuationStream;\n }\n\n return {\n threadId: actualThreadId,\n preMutationMessageCount,\n threadAlreadyHasName,\n };\n } catch (error) {\n // Dispatch a synthetic RUN_ERROR event to clean up thread state\n // This ensures the thread doesn't stay stuck in \"streaming\" status\n if (actualThreadId) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown streaming error\";\n const errorEvent: RunErrorEvent = {\n type: EventType.RUN_ERROR,\n message: errorMessage,\n };\n dispatch({\n type: \"EVENT\",\n event: errorEvent,\n threadId: actualThreadId,\n });\n }\n throw error;\n }\n },\n onSuccess: async (result) => {\n await queryClient.invalidateQueries({\n queryKey: [\"v1-threads\", result.threadId],\n });\n\n if (\n autoGenerateThreadName &&\n shouldGenerateThreadName(\n result.threadId,\n result.threadAlreadyHasName,\n result.preMutationMessageCount,\n autoGenerateNameThreshold,\n )\n ) {\n await generateThreadName(\n client,\n dispatch,\n queryClient,\n result.threadId,\n );\n }\n },\n onError: (error) => {\n console.error(\"[useTamboSendMessage] Mutation failed:\", error);\n },\n });\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-tambo-v1-stream-status.js","sourceRoot":"","sources":["../../../src/v1/hooks/use-tambo-v1-stream-status.ts"],"names":[],"mappings":";AAAA,YAAY,CAAC;;AAgOb,oDA6EC;AA3SD;;;;;;;GAOG;AAEH,iCAA6D;AAC7D,oEAAkE;AAClE,kFAAsE;AACtE,6CAAwD;AAsExD;;;;;;;GAOG;AACH,SAAS,uBAAuB,CAC9B,KAAwB,EACxB,uBAA4E;IAE5E,8CAA8C;IAC9C,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,IAAA,gBAAQ,EAAc,IAAI,GAAG,EAAE,CAAC,CAAC;IAEzE,gDAAgD;IAChD,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,CAAC,KAAK;YAAE,OAAO;QAEnB,eAAe,CAAC,CAAC,IAAI,EAAE,EAAE;YACvB,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;YAEjC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACjD,MAAM,UAAU,GACd,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;gBACxD,IAAI,UAAU,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;oBACvC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;oBACpB,OAAO,GAAG,IAAI,CAAC;gBACjB,CAAC;YACH,CAAC;YAED,OAAO,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;QACrC,CAAC,CAAC,CAAC;IACL,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAEZ,kEAAkE;IAClE,OAAO,IAAA,eAAO,EAAC,GAAG,EAAE;QAClB,IAAI,CAAC,KAAK;YAAE,OAAO,EAAqC,CAAC;QAEzD,MAAM,eAAe,GAAG,uBAAuB,KAAK,MAAM,CAAC;QAC3D,MAAM,oBAAoB,GAAG,uBAAuB,KAAK,WAAW,CAAC;QAErE,MAAM,MAAM,GAAG,EAAqC,CAAC;QACrD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACrC,MAAM,UAAU,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACzC,MAAM,UAAU,GAAG,UAAU,IAAI,eAAe,CAAC;YAEjD,MAAM,CAAC,GAAkB,CAAC,GAAG;gBAC3B,SAAS,EAAE,CAAC,UAAU,IAAI,CAAC,UAAU;gBACrC,WAAW,EAAE,UAAU,IAAI,CAAC,UAAU,IAAI,oBAAoB;gBAC9D,SAAS,EAAE,UAAU;gBACrB,KAAK,EAAE,SAAS;aACjB,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,uBAAuB,CAAC,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,wBAAwB,CAC/B,uBAA4E,EAC5E,UAA+C,EAC/C,YAAqB,EACrB,WAAmB;IAEnB,MAAM,YAAY,GAAiB,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CACjE,CAAC,CAAC,EAAmB,EAAE,CAAC,CAAC,KAAK,SAAS,CACxC,CAAC;IACF,MAAM,aAAa,GAAG,CAAC,CAAC,WAAW,CAAC;IAEpC,+FAA+F;IAC/F,MAAM,kBAAkB,GACtB,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAEpE,oFAAoF;IACpF,MAAM,oBAAoB,GAAG,uBAAuB,KAAK,WAAW,CAAC;IACrE,MAAM,gBAAgB,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IAEjE,+CAA+C;IAC/C,MAAM,UAAU,GAAG,WAAW,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;IAE3E,OAAO;QACL,oGAAoG;QACpG,SAAS,EACP,CAAC,YAAY;YACb,CAAC,CAAC,aAAa;gBACb,CAAC,oBAAoB;gBACrB,CAAC,kBAAkB;gBACnB,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAE3C,sFAAsF;QACtF,WAAW,EAAE,CAAC,aAAa,IAAI,CAAC,oBAAoB,IAAI,gBAAgB,CAAC;QAEzE,mDAAmD;QACnD,SAAS,EAAE,kBAAkB,IAAI,CAAC,aAAa;QAE/C,8CAA8C;QAC9C,OAAO,EAAE,aAAa,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;QAE3D,WAAW,EAAE,UAAU;KACxB,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,SAAgB,oBAAoB;IAMlC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,IAAA,wCAAmB,GAAE,CAAC;IACxD,MAAM,WAAW,GAAG,IAAA,wCAAc,GAAE,CAAC;IAErC;;;;;OAKG;IACH,MAAM,qBAAqB,GAAG,IAAA,cAAM,EAAC,WAAW,CAAC,CAAC;IAClD,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,WAAW,KAAK,qBAAqB,CAAC,OAAO,EAAE,CAAC;YAClD,OAAO,CAAC,KAAK,CACX,mDAAmD,qBAAqB,CAAC,OAAO,SAAS,WAAW,KAAK;gBACvG,0EAA0E;gBAC1E,mEAAmE;gBACnE,mEAAmE,CACtE,CAAC;YACF,qBAAqB,CAAC,OAAO,GAAG,WAAW,CAAC;QAC9C,CAAC;IACH,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IAElB,mCAAmC;IACnC,MAAM,WAAW,GAAG,WAAW,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAEpD,iDAAiD;IACjD,MAAM,kBAAkB,GAAG,WAAW,EAAE,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC;IAEjE,uCAAuC;IACvC,MAAM,gBAAgB,GAAG,IAAA,6BAAoB,EAC3C,WAAW,EACX,QAAQ,EACR,WAAW,CACZ,CAAC;IAEF,sCAAsC;IACtC,MAAM,cAAc,GACjB,gBAAgB,EAAE,KAA2B,IAAK,EAAY,CAAC;IAElE,wCAAwC;IACxC,MAAM,uBAAuB,GAAG,gBAAgB,EAAE,cAAc,CAAC;IAEjE,sCAAsC;IACtC,MAAM,UAAU,GAAG,uBAAuB,CACxC,cAAc,EACd,uBAAuB,CACxB,CAAC;IAEF,mFAAmF;IACnF,MAAM,YAAY,GAAG,IAAA,eAAO,EAAC,GAAG,EAAE;QAChC,MAAM,YAAY,GAAG,CAAC,CAAC,gBAAgB,CAAC;QACxC,MAAM,WAAW,GAAG,kBAAkB;YACpC,CAAC,CAAC,IAAI,KAAK,CAAC,kBAAkB,CAAC;YAC/B,CAAC,CAAC,SAAS,CAAC;QACd,OAAO,wBAAwB,CAC7B,uBAAuB,EACvB,UAAU,EACV,YAAY,EACZ,WAAW,CACZ,CAAC;IACJ,CAAC,EAAE;QACD,uBAAuB;QACvB,UAAU;QACV,gBAAgB;QAChB,kBAAkB;KACnB,CAAC,CAAC;IAEH,OAAO;QACL,YAAY;QACZ,UAAU;KACX,CAAC;AACJ,CAAC","sourcesContent":["\"use client\";\n\n/**\n * useTamboStreamStatus - Stream Status Hook\n *\n * Provides granular streaming status for components being rendered,\n * allowing UI to respond to prop-level streaming states.\n *\n * Must be used within a component rendered via the component renderer.\n */\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useComponentContent } from \"../utils/component-renderer\";\nimport { useStreamState } from \"../providers/tambo-v1-stream-context\";\nimport { findComponentContent } from \"@tambo-ai/client\";\nimport type { TamboComponentContent } from \"../types/message\";\n\n/**\n * Global stream status flags for a specific component in a message.\n * Represents the aggregate state across all props for this component only.\n * Once a component completes, its status remains stable regardless of other generations.\n */\nexport interface StreamStatus {\n /**\n * Indicates no tokens have been received for any prop and generation is not active.\n * Useful for showing initial loading states before any data arrives.\n */\n isPending: boolean;\n\n /**\n * Indicates active streaming - at least one prop is still streaming.\n * Use this to show loading animations or skeleton states during data transmission.\n */\n isStreaming: boolean;\n\n /**\n * Indicates successful completion - component streaming is done AND every prop finished without error.\n * Safe to render the final component when this is true.\n */\n isSuccess: boolean;\n\n /**\n * Indicates a fatal error occurred in any prop or the stream itself.\n * Check streamError for details about what went wrong.\n */\n isError: boolean;\n\n /**\n * The first fatal error encountered during streaming (if any).\n * Will be undefined if no errors occurred.\n */\n streamError?: Error;\n}\n\n/**\n * Streaming status flags for individual component props.\n * Tracks the state of each prop as it streams from the LLM.\n */\nexport interface PropStatus {\n /**\n * Indicates no tokens have been received for this specific prop yet.\n * The prop value is still undefined, null, or empty string.\n */\n isPending: boolean;\n\n /**\n * Indicates at least one token has been received but streaming is not complete.\n * The prop has partial content that may still be updating.\n */\n isStreaming: boolean;\n\n /**\n * Indicates this prop has finished streaming successfully.\n * The prop value is complete and stable.\n */\n isSuccess: boolean;\n\n /**\n * The error that occurred during streaming (if any).\n * Will be undefined if no error occurred for this prop.\n */\n error?: Error;\n}\n\n/**\n * Track streaming status for individual props by monitoring their values.\n * Monitors when props receive their first token and when they complete streaming.\n * @template Props - The type of the component props being tracked\n * @param props - The current component props object\n * @param componentStreamingState - The current streaming state of the component\n * @returns A record mapping each prop key to its PropStatus\n */\nfunction usePropsStreamingStatus<Props extends object>(\n props: Props | undefined,\n componentStreamingState: TamboComponentContent[\"streamingState\"] | undefined,\n): Partial<Record<keyof Props, PropStatus>> {\n /** Track which props have received content */\n const [startedProps, setStartedProps] = useState<Set<string>>(new Set());\n\n /** Update started props when content arrives */\n useEffect(() => {\n if (!props) return;\n\n setStartedProps((prev) => {\n let changed = false;\n const newStarted = new Set(prev);\n\n for (const [key, value] of Object.entries(props)) {\n const hasContent =\n value !== undefined && value !== null && value !== \"\";\n if (hasContent && !newStarted.has(key)) {\n newStarted.add(key);\n changed = true;\n }\n }\n\n return changed ? newStarted : prev;\n });\n }, [props]);\n\n /** Derive prop statuses from started props and streaming state */\n return useMemo(() => {\n if (!props) return {} as Record<keyof Props, PropStatus>;\n\n const isStreamingDone = componentStreamingState === \"done\";\n const isComponentStreaming = componentStreamingState === \"streaming\";\n\n const result = {} as Record<keyof Props, PropStatus>;\n for (const key of Object.keys(props)) {\n const hasStarted = startedProps.has(key);\n const isComplete = hasStarted && isStreamingDone;\n\n result[key as keyof Props] = {\n isPending: !hasStarted && !isComplete,\n isStreaming: hasStarted && !isComplete && isComponentStreaming,\n isSuccess: isComplete,\n error: undefined,\n };\n }\n return result;\n }, [props, startedProps, componentStreamingState]);\n}\n\n/**\n * Derives global StreamStatus from component streaming state and individual prop statuses.\n * Aggregates individual prop states into a unified stream status.\n * @template Props - The type of the component props\n * @param componentStreamingState - The current streaming state of the component\n * @param propStatus - Status record for each individual prop\n * @param hasComponent - Whether a component exists in the current message\n * @param streamError - Any error from the streaming process itself\n * @returns The aggregated StreamStatus for the entire component\n */\nfunction deriveGlobalStreamStatus(\n componentStreamingState: TamboComponentContent[\"streamingState\"] | undefined,\n propStatus: Partial<Record<string, PropStatus>>,\n hasComponent: boolean,\n streamError?: Error,\n): StreamStatus {\n const propStatuses: PropStatus[] = Object.values(propStatus).filter(\n (p): p is PropStatus => p !== undefined,\n );\n const isStreamError = !!streamError;\n\n // If all props are already successful, the component is complete regardless of streaming state\n const allPropsSuccessful =\n propStatuses.length > 0 && propStatuses.every((p) => p.isSuccess);\n\n // Component is streaming if streamingState is \"streaming\" (even before props start)\n const isComponentStreaming = componentStreamingState === \"streaming\";\n const anyPropStreaming = propStatuses.some((p) => p.isStreaming);\n\n /** Find first error from stream or any prop */\n const firstError = streamError ?? propStatuses.find((p) => p.error)?.error;\n\n return {\n /** isPending: no component yet OR (not streaming, not error, not success, and all props pending) */\n isPending:\n !hasComponent ||\n (!isStreamError &&\n !isComponentStreaming &&\n !allPropsSuccessful &&\n propStatuses.every((p) => p.isPending)),\n\n /** isStreaming: component is streaming OR any prop is streaming (but not if error) */\n isStreaming: !isStreamError && (isComponentStreaming || anyPropStreaming),\n\n /** isSuccess: all props successful and no error */\n isSuccess: allPropsSuccessful && !isStreamError,\n\n /** isError: stream error OR any prop error */\n isError: isStreamError || propStatuses.some((p) => p.error),\n\n streamError: firstError,\n };\n}\n\n/**\n * Track streaming status for Tambo component props.\n *\n * **Important**: Props update repeatedly during streaming and may be partial.\n * Use `propStatus.<field>?.isSuccess` before treating a prop as complete.\n *\n * Pair with `useTamboComponentState` to disable inputs while streaming.\n * @see {@link https://docs.tambo.co/concepts/generative-interfaces/component-state}\n * @template Props - Component props type\n * @returns `streamStatus` (overall) and `propStatus` (per-prop) flags\n * @throws {Error} When used outside a rendered component\n * @example\n * ```tsx\n * // Wait for entire stream\n * const { streamStatus } = useTamboStreamStatus();\n * if (!streamStatus.isSuccess) return <Spinner />;\n * return <Card {...props} />;\n * ```\n * @example\n * ```tsx\n * // Highlight in-flight props\n * const { propStatus } = useTamboStreamStatus<Props>();\n * <h2 className={propStatus.title?.isStreaming ? \"animate-pulse\" : \"\"}>\n * {title}\n * </h2>\n * ```\n */\nexport function useTamboStreamStatus<\n Props extends object = Record<string, unknown>,\n>(): {\n streamStatus: StreamStatus;\n propStatus: Partial<Record<keyof Props, PropStatus>>;\n} {\n const { componentId, threadId } = useComponentContent();\n const streamState = useStreamState();\n\n /**\n * Error if componentId changes - this indicates the provider hierarchy is broken.\n * The componentId should remain stable for the lifetime of the component.\n * If this fires, the ComponentRenderer is likely being used incorrectly,\n * or the component tree is being remounted in unexpected ways.\n */\n const initialComponentIdRef = useRef(componentId);\n useEffect(() => {\n if (componentId !== initialComponentIdRef.current) {\n console.error(\n `useTamboStreamStatus: componentId changed from \"${initialComponentIdRef.current}\" to \"${componentId}\". ` +\n \"This indicates a bug in the component tree or incorrect provider usage. \" +\n \"The componentId must remain stable for the component's lifetime. \" +\n \"Check that ComponentRenderer is not being remounted unexpectedly.\",\n );\n initialComponentIdRef.current = componentId;\n }\n }, [componentId]);\n\n /** Get the current thread state */\n const threadState = streamState.threadMap[threadId];\n\n /** Get error message from stream state if any */\n const streamErrorMessage = threadState?.streaming.error?.message;\n\n /** Find the component content block */\n const componentContent = findComponentContent(\n streamState,\n threadId,\n componentId,\n );\n\n /** Get the current component props */\n const componentProps =\n (componentContent?.props as Props | undefined) ?? ({} as Props);\n\n /** Get the component streaming state */\n const componentStreamingState = componentContent?.streamingState;\n\n /** Track per-prop streaming status */\n const propStatus = usePropsStreamingStatus(\n componentProps,\n componentStreamingState,\n );\n\n /** Derive global stream status from prop statuses and component streaming state */\n const streamStatus = useMemo(() => {\n const hasComponent = !!componentContent;\n const streamError = streamErrorMessage\n ? new Error(streamErrorMessage)\n : undefined;\n return deriveGlobalStreamStatus(\n componentStreamingState,\n propStatus,\n hasComponent,\n streamError,\n );\n }, [\n componentStreamingState,\n propStatus,\n componentContent,\n streamErrorMessage,\n ]);\n\n return {\n streamStatus,\n propStatus,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"use-tambo-v1-stream-status.js","sourceRoot":"","sources":["../../../src/v1/hooks/use-tambo-v1-stream-status.ts"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\n/**\n * useTamboStreamStatus - Stream Status Hook\n *\n * Provides granular streaming status for components being rendered,\n * allowing UI to respond to prop-level streaming states.\n *\n * Must be used within a component rendered via the component renderer.\n */\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useComponentContent } from \"../utils/component-renderer\";\nimport { useStreamState } from \"../providers/tambo-v1-stream-context\";\nimport { findComponentContent } from \"@tambo-ai/client\";\nimport type { TamboComponentContent } from \"../types/message\";\n\n/**\n * Global stream status flags for a specific component in a message.\n * Represents the aggregate state across all props for this component only.\n * Once a component completes, its status remains stable regardless of other generations.\n */\nexport interface StreamStatus {\n /**\n * Indicates no tokens have been received for any prop and generation is not active.\n * Useful for showing initial loading states before any data arrives.\n */\n isPending: boolean;\n\n /**\n * Indicates active streaming - at least one prop is still streaming.\n * Use this to show loading animations or skeleton states during data transmission.\n */\n isStreaming: boolean;\n\n /**\n * Indicates successful completion - component streaming is done AND every prop finished without error.\n * Safe to render the final component when this is true.\n */\n isSuccess: boolean;\n\n /**\n * Indicates a fatal error occurred in any prop or the stream itself.\n * Check streamError for details about what went wrong.\n */\n isError: boolean;\n\n /**\n * The first fatal error encountered during streaming (if any).\n * Will be undefined if no errors occurred.\n */\n streamError?: Error;\n}\n\n/**\n * Streaming status flags for individual component props.\n * Tracks the state of each prop as it streams from the LLM.\n */\nexport interface PropStatus {\n /**\n * Indicates no tokens have been received for this specific prop yet.\n * The prop value is still undefined, null, or empty string.\n */\n isPending: boolean;\n\n /**\n * Indicates at least one token has been received but streaming is not complete.\n * The prop has partial content that may still be updating.\n */\n isStreaming: boolean;\n\n /**\n * Indicates this prop has finished streaming successfully.\n * The prop value is complete and stable.\n */\n isSuccess: boolean;\n\n /**\n * The error that occurred during streaming (if any).\n * Will be undefined if no error occurred for this prop.\n */\n error?: Error;\n}\n\n/**\n * Track streaming status for individual props by monitoring their values.\n * Monitors when props receive their first token and when they complete streaming.\n * @template Props - The type of the component props being tracked\n * @param props - The current component props object\n * @param componentStreamingState - The current streaming state of the component\n * @returns A record mapping each prop key to its PropStatus\n */\nfunction usePropsStreamingStatus<Props extends object>(\n props: Props | undefined,\n componentStreamingState: TamboComponentContent[\"streamingState\"] | undefined,\n): Partial<Record<keyof Props, PropStatus>> {\n /** Track which props have received content */\n const [startedProps, setStartedProps] = useState(new Set<string>());\n\n /** Update started props when content arrives */\n useEffect(() => {\n if (!props) return;\n\n setStartedProps((prev) => {\n let changed = false;\n const newStarted = new Set(prev);\n\n for (const [key, value] of Object.entries(props)) {\n const hasContent =\n value !== undefined && value !== null && value !== \"\";\n if (hasContent && !newStarted.has(key)) {\n newStarted.add(key);\n changed = true;\n }\n }\n\n return changed ? newStarted : prev;\n });\n }, [props]);\n\n /** Derive prop statuses from started props and streaming state */\n return useMemo(() => {\n if (!props) return {} as Record<keyof Props, PropStatus>;\n\n const isStreamingDone = componentStreamingState === \"done\";\n const isComponentStreaming = componentStreamingState === \"streaming\";\n\n const result = {} as Record<keyof Props, PropStatus>;\n for (const key of Object.keys(props)) {\n const hasStarted = startedProps.has(key);\n const isComplete = hasStarted && isStreamingDone;\n\n result[key as keyof Props] = {\n isPending: !hasStarted && !isComplete,\n isStreaming: hasStarted && !isComplete && isComponentStreaming,\n isSuccess: isComplete,\n error: undefined,\n };\n }\n return result;\n }, [props, startedProps, componentStreamingState]);\n}\n\n/**\n * Derives global StreamStatus from component streaming state and individual prop statuses.\n * Aggregates individual prop states into a unified stream status.\n * @template Props - The type of the component props\n * @param componentStreamingState - The current streaming state of the component\n * @param propStatus - Status record for each individual prop\n * @param hasComponent - Whether a component exists in the current message\n * @param streamError - Any error from the streaming process itself\n * @returns The aggregated StreamStatus for the entire component\n */\nfunction deriveGlobalStreamStatus(\n componentStreamingState: TamboComponentContent[\"streamingState\"] | undefined,\n propStatus: Partial<Record<string, PropStatus>>,\n hasComponent: boolean,\n streamError?: Error,\n): StreamStatus {\n const propStatuses: PropStatus[] = Object.values(propStatus).filter(\n (p): p is PropStatus => p !== undefined,\n );\n const isStreamError = !!streamError;\n\n // If all props are already successful, the component is complete regardless of streaming state\n const allPropsSuccessful =\n propStatuses.length > 0 && propStatuses.every((p) => p.isSuccess);\n\n // Component is streaming if streamingState is \"streaming\" (even before props start)\n const isComponentStreaming = componentStreamingState === \"streaming\";\n const anyPropStreaming = propStatuses.some((p) => p.isStreaming);\n\n /** Find first error from stream or any prop */\n const firstError = streamError ?? propStatuses.find((p) => p.error)?.error;\n\n return {\n /** isPending: no component yet OR (not streaming, not error, not success, and all props pending) */\n isPending:\n !hasComponent ||\n (!isStreamError &&\n !isComponentStreaming &&\n !allPropsSuccessful &&\n propStatuses.every((p) => p.isPending)),\n\n /** isStreaming: component is streaming OR any prop is streaming (but not if error) */\n isStreaming: !isStreamError && (isComponentStreaming || anyPropStreaming),\n\n /** isSuccess: all props successful and no error */\n isSuccess: allPropsSuccessful && !isStreamError,\n\n /** isError: stream error OR any prop error */\n isError: isStreamError || propStatuses.some((p) => p.error),\n\n streamError: firstError,\n };\n}\n\n/**\n * Track streaming status for Tambo component props.\n *\n * **Important**: Props update repeatedly during streaming and may be partial.\n * Use `propStatus.<field>?.isSuccess` before treating a prop as complete.\n *\n * Pair with `useTamboComponentState` to disable inputs while streaming.\n * @see {@link https://docs.tambo.co/concepts/generative-interfaces/component-state}\n * @template Props - Component props type\n * @returns `streamStatus` (overall) and `propStatus` (per-prop) flags\n * @throws {Error} When used outside a rendered component\n * @example\n * ```tsx\n * // Wait for entire stream\n * const { streamStatus } = useTamboStreamStatus();\n * if (!streamStatus.isSuccess) return <Spinner />;\n * return <Card {...props} />;\n * ```\n * @example\n * ```tsx\n * // Highlight in-flight props\n * const { propStatus } = useTamboStreamStatus<Props>();\n * <h2 className={propStatus.title?.isStreaming ? \"animate-pulse\" : \"\"}>\n * {title}\n * </h2>\n * ```\n */\nexport function useTamboStreamStatus<\n Props extends object = Record<string, unknown>,\n>(): {\n streamStatus: StreamStatus;\n propStatus: Partial<Record<keyof Props, PropStatus>>;\n} {\n const { componentId, threadId } = useComponentContent();\n const streamState = useStreamState();\n\n /**\n * Error if componentId changes - this indicates the provider hierarchy is broken.\n * The componentId should remain stable for the lifetime of the component.\n * If this fires, the ComponentRenderer is likely being used incorrectly,\n * or the component tree is being remounted in unexpected ways.\n */\n const initialComponentIdRef = useRef(componentId);\n useEffect(() => {\n if (componentId !== initialComponentIdRef.current) {\n console.error(\n `useTamboStreamStatus: componentId changed from \"${initialComponentIdRef.current}\" to \"${componentId}\". ` +\n \"This indicates a bug in the component tree or incorrect provider usage. \" +\n \"The componentId must remain stable for the component's lifetime. \" +\n \"Check that ComponentRenderer is not being remounted unexpectedly.\",\n );\n initialComponentIdRef.current = componentId;\n }\n }, [componentId]);\n\n /** Get the current thread state */\n const threadState = streamState.threadMap[threadId];\n\n /** Get error message from stream state if any */\n const streamErrorMessage = threadState?.streaming.error?.message;\n\n /** Find the component content block */\n const componentContent = findComponentContent(\n streamState,\n threadId,\n componentId,\n );\n\n /** Get the current component props */\n const componentProps =\n (componentContent?.props as Props | undefined) ?? ({} as Props);\n\n /** Get the component streaming state */\n const componentStreamingState = componentContent?.streamingState;\n\n /** Track per-prop streaming status */\n const propStatus = usePropsStreamingStatus(\n componentProps,\n componentStreamingState,\n );\n\n /** Derive global stream status from prop statuses and component streaming state */\n const streamStatus = useMemo(() => {\n const hasComponent = !!componentContent;\n const streamError = streamErrorMessage\n ? new Error(streamErrorMessage)\n : undefined;\n return deriveGlobalStreamStatus(\n componentStreamingState,\n propStatus,\n hasComponent,\n streamError,\n );\n }, [\n componentStreamingState,\n propStatus,\n componentContent,\n streamErrorMessage,\n ]);\n\n return {\n streamStatus,\n propStatus,\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-tambo-v1-suggestions.d.ts","sourceRoot":"","sources":["../../../src/v1/hooks/use-tambo-v1-suggestions.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,OAAO,MAAM,0BAA0B,CAAC;AACpD,OAAO,KAAK,EACV,wBAAwB,EACxB,sBAAsB,EACvB,MAAM,wDAAwD,CAAC;AAYhE;;GAEG;AACH,KAAK,wBAAwB,GACzB,sBAAsB,GACtB,wBAAwB,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,kEAAkE;IAClE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,YAAY,CAAC,EAAE,IAAI,CACjB,eAAe,CAAC,wBAAwB,CAAC,EACzC,UAAU,GAAG,SAAS,GAAG,SAAS,CACnC,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,+BAA+B;IAC/B,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;IAC5C,sFAAsF;IACtF,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IAKxC;;;OAGG;IACH,IAAI,EAAE,wBAAwB,GAAG,SAAS,CAAC;IAE3C,mFAAmF;IACnF,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;IAM/C,6DAA6D;IAC7D,SAAS,EAAE,OAAO,CAAC;IAEnB,wDAAwD;IACxD,SAAS,EAAE,OAAO,CAAC;IAEnB,qDAAqD;IACrD,OAAO,EAAE,OAAO,CAAC;IAEjB,6CAA6C;IAC7C,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAEpB,8EAA8E;IAC9E,UAAU,EAAE,OAAO,CAAC;IAMpB;;;OAGG;IACH,QAAQ,EAAE,MAAM,OAAO,CAAC,wBAAwB,GAAG,SAAS,CAAC,CAAC;IAE9D,iEAAiE;IACjE,YAAY,EAAE,OAAO,CAAC;IAEtB,gDAAgD;IAChD,aAAa,EAAE,KAAK,GAAG,IAAI,CAAC;IAM5B;;;OAGG;IACH,MAAM,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5D,oDAAoD;IACpD,WAAW,EAAE,OAAO,CAAC;IAErB,gDAAgD;IAChD,WAAW,EAAE,KAAK,GAAG,IAAI,CAAC;IAM1B,8CAA8C;IAC9C,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,0BAA+B,GACvC,yBAAyB,
|
|
1
|
+
{"version":3,"file":"use-tambo-v1-suggestions.d.ts","sourceRoot":"","sources":["../../../src/v1/hooks/use-tambo-v1-suggestions.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,OAAO,MAAM,0BAA0B,CAAC;AACpD,OAAO,KAAK,EACV,wBAAwB,EACxB,sBAAsB,EACvB,MAAM,wDAAwD,CAAC;AAYhE;;GAEG;AACH,KAAK,wBAAwB,GACzB,sBAAsB,GACtB,wBAAwB,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,kEAAkE;IAClE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,YAAY,CAAC,EAAE,IAAI,CACjB,eAAe,CAAC,wBAAwB,CAAC,EACzC,UAAU,GAAG,SAAS,GAAG,SAAS,CACnC,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,+BAA+B;IAC/B,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;IAC5C,sFAAsF;IACtF,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IAKxC;;;OAGG;IACH,IAAI,EAAE,wBAAwB,GAAG,SAAS,CAAC;IAE3C,mFAAmF;IACnF,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;IAM/C,6DAA6D;IAC7D,SAAS,EAAE,OAAO,CAAC;IAEnB,wDAAwD;IACxD,SAAS,EAAE,OAAO,CAAC;IAEnB,qDAAqD;IACrD,OAAO,EAAE,OAAO,CAAC;IAEjB,6CAA6C;IAC7C,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAEpB,8EAA8E;IAC9E,UAAU,EAAE,OAAO,CAAC;IAMpB;;;OAGG;IACH,QAAQ,EAAE,MAAM,OAAO,CAAC,wBAAwB,GAAG,SAAS,CAAC,CAAC;IAE9D,iEAAiE;IACjE,YAAY,EAAE,OAAO,CAAC;IAEtB,gDAAgD;IAChD,aAAa,EAAE,KAAK,GAAG,IAAI,CAAC;IAM5B;;;OAGG;IACH,MAAM,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5D,oDAAoD;IACpD,WAAW,EAAE,OAAO,CAAC;IAErB,gDAAgD;IAChD,WAAW,EAAE,KAAK,GAAG,IAAI,CAAC;IAM1B,8CAA8C;IAC9C,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,0BAA+B,GACvC,yBAAyB,CAyN3B"}
|
|
@@ -97,7 +97,20 @@ function useTamboSuggestions(options = {}) {
|
|
|
97
97
|
enabled: Boolean(shouldFetchSuggestions),
|
|
98
98
|
refetchOnWindowFocus: false,
|
|
99
99
|
refetchOnReconnect: false,
|
|
100
|
-
|
|
100
|
+
// Retry on 404s to handle the race condition where the assistant message
|
|
101
|
+
// hasn't been persisted to the DB yet when suggestions are requested
|
|
102
|
+
// immediately after a run completes.
|
|
103
|
+
retry: (failureCount, error) => {
|
|
104
|
+
if (failureCount >= 3)
|
|
105
|
+
return false;
|
|
106
|
+
// Use structural check instead of instanceof to handle duplicate SDK copies
|
|
107
|
+
// where the APIError constructor identity may differ between packages.
|
|
108
|
+
const status = typeof error === "object" && error !== null && "status" in error
|
|
109
|
+
? error.status
|
|
110
|
+
: undefined;
|
|
111
|
+
return status === 404;
|
|
112
|
+
},
|
|
113
|
+
retryDelay: (attemptIndex) => Math.min(500 * 2 ** attemptIndex, 3000),
|
|
101
114
|
});
|
|
102
115
|
// Mutation to manually generate suggestions
|
|
103
116
|
const generateMutation = (0, react_query_hooks_1.useTamboMutation)({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-tambo-v1-suggestions.js","sourceRoot":"","sources":["../../../src/v1/hooks/use-tambo-v1-suggestions.ts"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\n/**\n * useTamboSuggestions - Suggestions Hook\n *\n * Manages AI-powered suggestions for thread messages.\n * Uses the API endpoints for listing and creating suggestions.\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { UseQueryOptions } from \"@tanstack/react-query\";\nimport type TamboAI from \"@tambo-ai/typescript-sdk\";\nimport type {\n SuggestionCreateResponse,\n SuggestionListResponse,\n} from \"@tambo-ai/typescript-sdk/resources/threads/suggestions\";\nimport {\n useTamboClient,\n useTamboQueryClient,\n} from \"../../providers/tambo-client-provider\";\nimport { useTamboQuery, useTamboMutation } from \"../../hooks/react-query-hooks\";\nimport { useTamboRegistry } from \"../../providers/tambo-registry-provider\";\nimport { useTamboConfig } from \"../providers/tambo-v1-provider\";\nimport { useTambo } from \"./use-tambo-v1\";\nimport { useTamboThreadInput } from \"./use-tambo-v1-thread-input\";\nimport { toAvailableComponents } from \"@tambo-ai/client\";\n\n/**\n * Response type for suggestions queries (union of list and create responses)\n */\ntype SuggestionsQueryResponse =\n | SuggestionListResponse\n | SuggestionCreateResponse;\n\n/**\n * Configuration options for the useTamboSuggestions hook\n */\nexport interface UseTamboSuggestionsOptions {\n /** Maximum number of suggestions to generate (1-10, default 3) */\n maxSuggestions?: number;\n /**\n * Whether to automatically generate suggestions when the latest message is from the assistant.\n * Default: true\n */\n autoGenerate?: boolean;\n /**\n * Additional React Query options for the suggestions query.\n * Allows customizing caching, refetching behavior, etc.\n */\n queryOptions?: Omit<\n UseQueryOptions<SuggestionsQueryResponse>,\n \"queryKey\" | \"queryFn\" | \"enabled\"\n >;\n}\n\n/**\n * Options for accepting a suggestion\n */\nexport interface AcceptSuggestionOptions {\n /** The suggestion to accept */\n suggestion: TamboAI.Beta.Threads.Suggestion;\n /** Whether to automatically submit the suggestion after accepting (default: false) */\n shouldSubmit?: boolean;\n}\n\n/**\n * Return type for useTamboSuggestions hook\n */\nexport interface UseTamboSuggestionsReturn {\n // ---------------------------------------------------------------------------\n // Data (mirrors react-query patterns)\n // ---------------------------------------------------------------------------\n\n /**\n * The raw response data from the suggestions query.\n * Use this for direct access to the API response shape.\n */\n data: SuggestionsQueryResponse | undefined;\n\n /** List of available suggestions for the current message (convenience accessor) */\n suggestions: TamboAI.Beta.Threads.Suggestion[];\n\n // ---------------------------------------------------------------------------\n // Query state (matches react-query UseQueryResult)\n // ---------------------------------------------------------------------------\n\n /** Whether the suggestions query is loading (first fetch) */\n isLoading: boolean;\n\n /** Whether suggestions have been successfully loaded */\n isSuccess: boolean;\n\n /** Whether there was an error loading suggestions */\n isError: boolean;\n\n /** Error from loading suggestions, if any */\n error: Error | null;\n\n /** Whether the query is currently fetching (includes background refetches) */\n isFetching: boolean;\n\n // ---------------------------------------------------------------------------\n // Generate mutation (for manual suggestion generation)\n // ---------------------------------------------------------------------------\n\n /**\n * Manually generate new suggestions for the current message.\n * Use this when autoGenerate is false or to refresh suggestions.\n */\n generate: () => Promise<SuggestionCreateResponse | undefined>;\n\n /** Whether suggestions are being generated (mutation pending) */\n isGenerating: boolean;\n\n /** Error from generating suggestions, if any */\n generateError: Error | null;\n\n // ---------------------------------------------------------------------------\n // Accept mutation (for applying a suggestion)\n // ---------------------------------------------------------------------------\n\n /**\n * Accept and apply a suggestion.\n * Sets the suggestion text as input value, optionally submitting it.\n */\n accept: (options: AcceptSuggestionOptions) => Promise<void>;\n\n /** Whether accepting a suggestion is in progress */\n isAccepting: boolean;\n\n /** Error from accepting a suggestion, if any */\n acceptError: Error | null;\n\n // ---------------------------------------------------------------------------\n // UI state\n // ---------------------------------------------------------------------------\n\n /** ID of the currently selected suggestion */\n selectedSuggestionId: string | null;\n}\n\n/**\n * Hook for managing AI-powered suggestions in a thread.\n *\n * Provides functionality to:\n * - Automatically generate suggestions when an assistant message arrives\n * - Manually generate suggestions on demand\n * - Accept suggestions by setting them as input or auto-submitting\n * @param options - Configuration options\n * @returns Suggestions state and control functions\n * @example\n * ```tsx\n * function SuggestionsPanel() {\n * const {\n * suggestions,\n * accept,\n * isLoading,\n * selectedSuggestionId,\n * } = useTamboSuggestions();\n *\n * if (isLoading) return <Spinner />;\n *\n * return (\n * <div>\n * {suggestions.map((suggestion) => (\n * <button\n * key={suggestion.id}\n * onClick={() => accept({ suggestion })}\n * className={selectedSuggestionId === suggestion.id ? 'selected' : ''}\n * >\n * {suggestion.title}\n * </button>\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function useTamboSuggestions(\n options: UseTamboSuggestionsOptions = {},\n): UseTamboSuggestionsReturn {\n const { maxSuggestions = 3, autoGenerate = true, queryOptions } = options;\n\n const client = useTamboClient();\n const { userKey } = useTamboConfig();\n const { componentList } = useTamboRegistry();\n const queryClient = useTamboQueryClient();\n\n const { messages, isIdle, currentThreadId } = useTambo();\n const { setValue: setInputValue, submit } = useTamboThreadInput();\n\n const [selectedSuggestionId, setSelectedSuggestionId] = useState<\n string | null\n >(null);\n\n // Get the latest message info\n const latestMessage = messages[messages.length - 1];\n const isLatestFromAssistant = latestMessage?.role === \"assistant\";\n const latestMessageId = latestMessage?.id;\n\n // Reset selected suggestion when the message changes\n useEffect(() => {\n setSelectedSuggestionId(null);\n }, [latestMessageId]);\n\n // Determine if we should fetch/generate suggestions\n const shouldFetchSuggestions =\n currentThreadId &&\n latestMessageId &&\n isLatestFromAssistant &&\n isIdle &&\n autoGenerate;\n\n const suggestionsQueryKey = [\n \"v1-suggestions\",\n currentThreadId ?? null,\n latestMessageId ?? null,\n ] as const;\n\n // Query to list existing suggestions\n const suggestionsQuery = useTamboQuery({\n queryKey: suggestionsQueryKey,\n queryFn: async (): Promise<SuggestionsQueryResponse> => {\n if (!shouldFetchSuggestions || !latestMessageId || !currentThreadId) {\n return { suggestions: [], hasMore: false };\n }\n\n return await client.threads.suggestions.list(latestMessageId, {\n threadId: currentThreadId,\n userKey,\n });\n },\n ...queryOptions,\n enabled: Boolean(shouldFetchSuggestions),\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n retry: false,\n });\n\n // Mutation to manually generate suggestions\n const generateMutation = useTamboMutation({\n mutationFn: async () => {\n if (!currentThreadId || !latestMessageId || !isLatestFromAssistant) {\n return undefined;\n }\n\n const availableComponents = toAvailableComponents(componentList);\n\n return await client.threads.suggestions.create(latestMessageId, {\n threadId: currentThreadId,\n maxSuggestions,\n availableComponents,\n userKey,\n });\n },\n onSuccess: (data) => {\n if (data && currentThreadId && latestMessageId) {\n // Update the query cache with new suggestions\n queryClient.setQueryData(suggestionsQueryKey, data);\n }\n },\n });\n\n const lastAutoGenerateMessageIdRef = useRef<string | null>(null);\n\n useEffect(() => {\n lastAutoGenerateMessageIdRef.current = null;\n }, [latestMessageId]);\n\n const listSuggestionsCount = suggestionsQuery.data?.suggestions.length ?? 0;\n const generateMutate = generateMutation.mutate;\n const isGenerating = generateMutation.isPending;\n const isListError = suggestionsQuery.isError;\n const isListSuccess = suggestionsQuery.isSuccess;\n\n useEffect(() => {\n if (!shouldFetchSuggestions || !latestMessageId) {\n return;\n }\n\n if (listSuggestionsCount > 0) {\n return;\n }\n\n const shouldAutoGenerate =\n (isListSuccess && listSuggestionsCount === 0) || isListError;\n\n if (!shouldAutoGenerate) {\n return;\n }\n\n if (isGenerating) {\n return;\n }\n\n if (lastAutoGenerateMessageIdRef.current === latestMessageId) {\n return;\n }\n\n lastAutoGenerateMessageIdRef.current = latestMessageId;\n generateMutate();\n }, [\n generateMutate,\n isGenerating,\n isListError,\n isListSuccess,\n latestMessageId,\n listSuggestionsCount,\n shouldFetchSuggestions,\n ]);\n\n // Mutation to accept a suggestion\n const acceptMutation = useTamboMutation({\n mutationFn: async ({\n suggestion,\n shouldSubmit = false,\n }: AcceptSuggestionOptions) => {\n const text = suggestion.detailedSuggestion?.trim();\n if (!text) {\n throw new Error(\"Suggestion has no content\");\n }\n\n if (shouldSubmit) {\n // Set value and submit\n setInputValue(text);\n await submit();\n } else {\n // Just set the input value\n setInputValue(text);\n }\n\n setSelectedSuggestionId(suggestion.id);\n },\n });\n\n // Generate callback\n const generateMutateAsync = generateMutation.mutateAsync;\n const generate = useCallback(async () => {\n return await generateMutateAsync();\n }, [generateMutateAsync]);\n\n // Accept callback\n const accept = useCallback(\n async (acceptOptions: AcceptSuggestionOptions) => {\n await acceptMutation.mutateAsync(acceptOptions);\n },\n [acceptMutation],\n );\n\n // Get suggestions from query or mutation result\n const queryData = suggestionsQuery.data;\n const mutationData = generateMutation.data;\n\n // Use mutation data if available (more recent), otherwise query data\n const currentData = mutationData ?? queryData;\n const suggestions = isLatestFromAssistant\n ? (currentData?.suggestions ?? [])\n : [];\n\n return {\n // Data\n data: currentData,\n suggestions,\n\n // Query state (matches react-query patterns)\n isLoading: suggestionsQuery.isLoading,\n isSuccess: suggestionsQuery.isSuccess,\n isError: suggestionsQuery.isError,\n error: suggestionsQuery.error,\n isFetching: suggestionsQuery.isFetching,\n\n // Generate mutation\n generate,\n isGenerating: generateMutation.isPending,\n generateError: generateMutation.error,\n\n // Accept mutation\n accept,\n isAccepting: acceptMutation.isPending,\n acceptError: acceptMutation.error,\n\n // UI state\n selectedSuggestionId,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"use-tambo-v1-suggestions.js","sourceRoot":"","sources":["../../../src/v1/hooks/use-tambo-v1-suggestions.ts"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\n/**\n * useTamboSuggestions - Suggestions Hook\n *\n * Manages AI-powered suggestions for thread messages.\n * Uses the API endpoints for listing and creating suggestions.\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { UseQueryOptions } from \"@tanstack/react-query\";\nimport type TamboAI from \"@tambo-ai/typescript-sdk\";\nimport type {\n SuggestionCreateResponse,\n SuggestionListResponse,\n} from \"@tambo-ai/typescript-sdk/resources/threads/suggestions\";\nimport {\n useTamboClient,\n useTamboQueryClient,\n} from \"../../providers/tambo-client-provider\";\nimport { useTamboQuery, useTamboMutation } from \"../../hooks/react-query-hooks\";\nimport { useTamboRegistry } from \"../../providers/tambo-registry-provider\";\nimport { useTamboConfig } from \"../providers/tambo-v1-provider\";\nimport { useTambo } from \"./use-tambo-v1\";\nimport { useTamboThreadInput } from \"./use-tambo-v1-thread-input\";\nimport { toAvailableComponents } from \"@tambo-ai/client\";\n\n/**\n * Response type for suggestions queries (union of list and create responses)\n */\ntype SuggestionsQueryResponse =\n | SuggestionListResponse\n | SuggestionCreateResponse;\n\n/**\n * Configuration options for the useTamboSuggestions hook\n */\nexport interface UseTamboSuggestionsOptions {\n /** Maximum number of suggestions to generate (1-10, default 3) */\n maxSuggestions?: number;\n /**\n * Whether to automatically generate suggestions when the latest message is from the assistant.\n * Default: true\n */\n autoGenerate?: boolean;\n /**\n * Additional React Query options for the suggestions query.\n * Allows customizing caching, refetching behavior, etc.\n */\n queryOptions?: Omit<\n UseQueryOptions<SuggestionsQueryResponse>,\n \"queryKey\" | \"queryFn\" | \"enabled\"\n >;\n}\n\n/**\n * Options for accepting a suggestion\n */\nexport interface AcceptSuggestionOptions {\n /** The suggestion to accept */\n suggestion: TamboAI.Beta.Threads.Suggestion;\n /** Whether to automatically submit the suggestion after accepting (default: false) */\n shouldSubmit?: boolean;\n}\n\n/**\n * Return type for useTamboSuggestions hook\n */\nexport interface UseTamboSuggestionsReturn {\n // ---------------------------------------------------------------------------\n // Data (mirrors react-query patterns)\n // ---------------------------------------------------------------------------\n\n /**\n * The raw response data from the suggestions query.\n * Use this for direct access to the API response shape.\n */\n data: SuggestionsQueryResponse | undefined;\n\n /** List of available suggestions for the current message (convenience accessor) */\n suggestions: TamboAI.Beta.Threads.Suggestion[];\n\n // ---------------------------------------------------------------------------\n // Query state (matches react-query UseQueryResult)\n // ---------------------------------------------------------------------------\n\n /** Whether the suggestions query is loading (first fetch) */\n isLoading: boolean;\n\n /** Whether suggestions have been successfully loaded */\n isSuccess: boolean;\n\n /** Whether there was an error loading suggestions */\n isError: boolean;\n\n /** Error from loading suggestions, if any */\n error: Error | null;\n\n /** Whether the query is currently fetching (includes background refetches) */\n isFetching: boolean;\n\n // ---------------------------------------------------------------------------\n // Generate mutation (for manual suggestion generation)\n // ---------------------------------------------------------------------------\n\n /**\n * Manually generate new suggestions for the current message.\n * Use this when autoGenerate is false or to refresh suggestions.\n */\n generate: () => Promise<SuggestionCreateResponse | undefined>;\n\n /** Whether suggestions are being generated (mutation pending) */\n isGenerating: boolean;\n\n /** Error from generating suggestions, if any */\n generateError: Error | null;\n\n // ---------------------------------------------------------------------------\n // Accept mutation (for applying a suggestion)\n // ---------------------------------------------------------------------------\n\n /**\n * Accept and apply a suggestion.\n * Sets the suggestion text as input value, optionally submitting it.\n */\n accept: (options: AcceptSuggestionOptions) => Promise<void>;\n\n /** Whether accepting a suggestion is in progress */\n isAccepting: boolean;\n\n /** Error from accepting a suggestion, if any */\n acceptError: Error | null;\n\n // ---------------------------------------------------------------------------\n // UI state\n // ---------------------------------------------------------------------------\n\n /** ID of the currently selected suggestion */\n selectedSuggestionId: string | null;\n}\n\n/**\n * Hook for managing AI-powered suggestions in a thread.\n *\n * Provides functionality to:\n * - Automatically generate suggestions when an assistant message arrives\n * - Manually generate suggestions on demand\n * - Accept suggestions by setting them as input or auto-submitting\n * @param options - Configuration options\n * @returns Suggestions state and control functions\n * @example\n * ```tsx\n * function SuggestionsPanel() {\n * const {\n * suggestions,\n * accept,\n * isLoading,\n * selectedSuggestionId,\n * } = useTamboSuggestions();\n *\n * if (isLoading) return <Spinner />;\n *\n * return (\n * <div>\n * {suggestions.map((suggestion) => (\n * <button\n * key={suggestion.id}\n * onClick={() => accept({ suggestion })}\n * className={selectedSuggestionId === suggestion.id ? 'selected' : ''}\n * >\n * {suggestion.title}\n * </button>\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function useTamboSuggestions(\n options: UseTamboSuggestionsOptions = {},\n): UseTamboSuggestionsReturn {\n const { maxSuggestions = 3, autoGenerate = true, queryOptions } = options;\n\n const client = useTamboClient();\n const { userKey } = useTamboConfig();\n const { componentList } = useTamboRegistry();\n const queryClient = useTamboQueryClient();\n\n const { messages, isIdle, currentThreadId } = useTambo();\n const { setValue: setInputValue, submit } = useTamboThreadInput();\n\n const [selectedSuggestionId, setSelectedSuggestionId] = useState<\n string | null\n >(null);\n\n // Get the latest message info\n const latestMessage = messages[messages.length - 1];\n const isLatestFromAssistant = latestMessage?.role === \"assistant\";\n const latestMessageId = latestMessage?.id;\n\n // Reset selected suggestion when the message changes\n useEffect(() => {\n setSelectedSuggestionId(null);\n }, [latestMessageId]);\n\n // Determine if we should fetch/generate suggestions\n const shouldFetchSuggestions =\n currentThreadId &&\n latestMessageId &&\n isLatestFromAssistant &&\n isIdle &&\n autoGenerate;\n\n const suggestionsQueryKey = [\n \"v1-suggestions\",\n currentThreadId ?? null,\n latestMessageId ?? null,\n ] as const;\n\n // Query to list existing suggestions\n const suggestionsQuery = useTamboQuery({\n queryKey: suggestionsQueryKey,\n queryFn: async (): Promise<SuggestionsQueryResponse> => {\n if (!shouldFetchSuggestions || !latestMessageId || !currentThreadId) {\n return { suggestions: [], hasMore: false };\n }\n\n return await client.threads.suggestions.list(latestMessageId, {\n threadId: currentThreadId,\n userKey,\n });\n },\n ...queryOptions,\n enabled: Boolean(shouldFetchSuggestions),\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n // Retry on 404s to handle the race condition where the assistant message\n // hasn't been persisted to the DB yet when suggestions are requested\n // immediately after a run completes.\n retry: (failureCount, error) => {\n if (failureCount >= 3) return false;\n // Use structural check instead of instanceof to handle duplicate SDK copies\n // where the APIError constructor identity may differ between packages.\n const status =\n typeof error === \"object\" && error !== null && \"status\" in error\n ? (error as { status: unknown }).status\n : undefined;\n return status === 404;\n },\n retryDelay: (attemptIndex) => Math.min(500 * 2 ** attemptIndex, 3000),\n });\n\n // Mutation to manually generate suggestions\n const generateMutation = useTamboMutation({\n mutationFn: async () => {\n if (!currentThreadId || !latestMessageId || !isLatestFromAssistant) {\n return undefined;\n }\n\n const availableComponents = toAvailableComponents(componentList);\n\n return await client.threads.suggestions.create(latestMessageId, {\n threadId: currentThreadId,\n maxSuggestions,\n availableComponents,\n userKey,\n });\n },\n onSuccess: (data) => {\n if (data && currentThreadId && latestMessageId) {\n // Update the query cache with new suggestions\n queryClient.setQueryData(suggestionsQueryKey, data);\n }\n },\n });\n\n const lastAutoGenerateMessageIdRef = useRef<string | null>(null);\n\n useEffect(() => {\n lastAutoGenerateMessageIdRef.current = null;\n }, [latestMessageId]);\n\n const listSuggestionsCount = suggestionsQuery.data?.suggestions.length ?? 0;\n const generateMutate = generateMutation.mutate;\n const isGenerating = generateMutation.isPending;\n const isListError = suggestionsQuery.isError;\n const isListSuccess = suggestionsQuery.isSuccess;\n\n useEffect(() => {\n if (!shouldFetchSuggestions || !latestMessageId) {\n return;\n }\n\n if (listSuggestionsCount > 0) {\n return;\n }\n\n const shouldAutoGenerate =\n (isListSuccess && listSuggestionsCount === 0) || isListError;\n\n if (!shouldAutoGenerate) {\n return;\n }\n\n if (isGenerating) {\n return;\n }\n\n if (lastAutoGenerateMessageIdRef.current === latestMessageId) {\n return;\n }\n\n lastAutoGenerateMessageIdRef.current = latestMessageId;\n generateMutate();\n }, [\n generateMutate,\n isGenerating,\n isListError,\n isListSuccess,\n latestMessageId,\n listSuggestionsCount,\n shouldFetchSuggestions,\n ]);\n\n // Mutation to accept a suggestion\n const acceptMutation = useTamboMutation({\n mutationFn: async ({\n suggestion,\n shouldSubmit = false,\n }: AcceptSuggestionOptions) => {\n const text = suggestion.detailedSuggestion?.trim();\n if (!text) {\n throw new Error(\"Suggestion has no content\");\n }\n\n if (shouldSubmit) {\n // Set value and submit\n setInputValue(text);\n await submit();\n } else {\n // Just set the input value\n setInputValue(text);\n }\n\n setSelectedSuggestionId(suggestion.id);\n },\n });\n\n // Generate callback\n const generateMutateAsync = generateMutation.mutateAsync;\n const generate = useCallback(async () => {\n return await generateMutateAsync();\n }, [generateMutateAsync]);\n\n // Accept callback\n const accept = useCallback(\n async (acceptOptions: AcceptSuggestionOptions) => {\n await acceptMutation.mutateAsync(acceptOptions);\n },\n [acceptMutation],\n );\n\n // Get suggestions from query or mutation result\n const queryData = suggestionsQuery.data;\n const mutationData = generateMutation.data;\n\n // Use mutation data if available (more recent), otherwise query data\n const currentData = mutationData ?? queryData;\n const suggestions = isLatestFromAssistant\n ? (currentData?.suggestions ?? [])\n : [];\n\n return {\n // Data\n data: currentData,\n suggestions,\n\n // Query state (matches react-query patterns)\n isLoading: suggestionsQuery.isLoading,\n isSuccess: suggestionsQuery.isSuccess,\n isError: suggestionsQuery.isError,\n error: suggestionsQuery.error,\n isFetching: suggestionsQuery.isFetching,\n\n // Generate mutation\n generate,\n isGenerating: generateMutation.isPending,\n generateError: generateMutation.error,\n\n // Accept mutation\n accept,\n isAccepting: acceptMutation.isPending,\n acceptError: acceptMutation.error,\n\n // UI state\n selectedSuggestionId,\n };\n}\n"]}
|
|
@@ -535,6 +535,85 @@ describe("useTamboSuggestions", () => {
|
|
|
535
535
|
expect(result.current.isGenerating).toBe(false);
|
|
536
536
|
});
|
|
537
537
|
});
|
|
538
|
+
describe("Retry on 404", () => {
|
|
539
|
+
it("retries list query on 404 and succeeds on subsequent attempt", async () => {
|
|
540
|
+
// Use a plain object with status to verify structural check works
|
|
541
|
+
// (simulates duplicate SDK copy where instanceof would fail)
|
|
542
|
+
const notFoundError = Object.assign(new Error("Message not found"), {
|
|
543
|
+
status: 404,
|
|
544
|
+
});
|
|
545
|
+
// First call: 404, second call: success with empty suggestions
|
|
546
|
+
mockListSuggestions
|
|
547
|
+
.mockRejectedValueOnce(notFoundError)
|
|
548
|
+
.mockResolvedValueOnce({ suggestions: [], hasMore: false });
|
|
549
|
+
jest.mocked(use_tambo_v1_1.useTambo).mockReturnValue({
|
|
550
|
+
messages: [
|
|
551
|
+
{
|
|
552
|
+
id: "msg_1",
|
|
553
|
+
role: "assistant",
|
|
554
|
+
content: [],
|
|
555
|
+
createdAt: "2024-01-01T00:00:00Z",
|
|
556
|
+
},
|
|
557
|
+
],
|
|
558
|
+
thread: undefined,
|
|
559
|
+
isIdle: true,
|
|
560
|
+
isStreaming: false,
|
|
561
|
+
currentThreadId: "thread_123",
|
|
562
|
+
startNewThread: jest.fn(),
|
|
563
|
+
switchThread: jest.fn(),
|
|
564
|
+
initThread: jest.fn(),
|
|
565
|
+
streamingState: { status: "idle" },
|
|
566
|
+
});
|
|
567
|
+
const { result } = (0, react_1.renderHook)(() => (0, use_tambo_v1_suggestions_1.useTamboSuggestions)(), {
|
|
568
|
+
wrapper: createWrapper(),
|
|
569
|
+
});
|
|
570
|
+
// Should eventually succeed after retry and trigger auto-generate
|
|
571
|
+
await (0, react_1.waitFor)(() => {
|
|
572
|
+
expect(mockListSuggestions).toHaveBeenCalledTimes(2);
|
|
573
|
+
});
|
|
574
|
+
// After successful list (empty), auto-generate fires
|
|
575
|
+
await (0, react_1.waitFor)(() => {
|
|
576
|
+
expect(mockCreateSuggestions).toHaveBeenCalled();
|
|
577
|
+
});
|
|
578
|
+
await (0, react_1.waitFor)(() => {
|
|
579
|
+
expect(result.current.suggestions).toHaveLength(2);
|
|
580
|
+
});
|
|
581
|
+
});
|
|
582
|
+
it("does not retry on non-404 errors", async () => {
|
|
583
|
+
const serverError = Object.assign(new Error("Internal server error"), {
|
|
584
|
+
status: 500,
|
|
585
|
+
});
|
|
586
|
+
mockListSuggestions.mockRejectedValue(serverError);
|
|
587
|
+
jest.mocked(use_tambo_v1_1.useTambo).mockReturnValue({
|
|
588
|
+
messages: [
|
|
589
|
+
{
|
|
590
|
+
id: "msg_1",
|
|
591
|
+
role: "assistant",
|
|
592
|
+
content: [],
|
|
593
|
+
createdAt: "2024-01-01T00:00:00Z",
|
|
594
|
+
},
|
|
595
|
+
],
|
|
596
|
+
thread: undefined,
|
|
597
|
+
isIdle: true,
|
|
598
|
+
isStreaming: false,
|
|
599
|
+
currentThreadId: "thread_123",
|
|
600
|
+
startNewThread: jest.fn(),
|
|
601
|
+
switchThread: jest.fn(),
|
|
602
|
+
initThread: jest.fn(),
|
|
603
|
+
streamingState: { status: "idle" },
|
|
604
|
+
});
|
|
605
|
+
(0, react_1.renderHook)(() => (0, use_tambo_v1_suggestions_1.useTamboSuggestions)(), {
|
|
606
|
+
wrapper: createWrapper(),
|
|
607
|
+
});
|
|
608
|
+
// Wait for the query to settle - 500 errors should not be retried
|
|
609
|
+
await (0, react_1.waitFor)(() => {
|
|
610
|
+
expect(mockListSuggestions).toHaveBeenCalledTimes(1);
|
|
611
|
+
});
|
|
612
|
+
// Give it a moment to ensure no additional retries happen
|
|
613
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
614
|
+
expect(mockListSuggestions).toHaveBeenCalledTimes(1);
|
|
615
|
+
});
|
|
616
|
+
});
|
|
538
617
|
describe("Manual Generation", () => {
|
|
539
618
|
it("allows manual generation via generate function", async () => {
|
|
540
619
|
jest.mocked(use_tambo_v1_1.useTambo).mockReturnValue({
|