import { AiCloudConfig, FunctionCallHandler, AiErrorHandler, AiSDKError, } from "@vn-sdk/shared"; import { ActionRenderProps, CatchAllActionRenderProps, FrontendAction, } from "../types/frontend-action"; import React from "react"; import { TreeNodeId, Tree } from "../hooks/use-tree"; import { DocumentPointer } from "../types"; import { AiChatSuggestionConfiguration } from "../types/chat-suggestion-configuration"; import { AiAgentStateRender, AiAgentStateRenderProps } from "../types/ai-agent-action"; import { AiAgentState } from "../types/ai-agent-state"; import { AiRuntimeClient, ExtensionsInput, ForwardedParametersInput, } from "@vn-sdk/runtime-client-gql"; import { Agent } from "@vn-sdk/runtime-client-gql"; import { LangGraphInterruptAction, LangGraphInterruptActionSetter, } from "../types/interrupt-action"; import { SuggestionItem } from "../utils/suggestions"; /** * Interface for the configuration of the AI API. */ export interface AiApiConfig { /** * The public API key for VN Cloud. */ publicApiKey?: string; /** * The configuration for VN Cloud. */ cloud?: AiCloudConfig; /** * The endpoint for the chat API. */ chatApiEndpoint: string; /** * The endpoint for the AI transcribe audio service. */ transcribeAudioUrl?: string; /** * The endpoint for the AI text to speech service. */ textToSpeechUrl?: string; /** * additional headers to be sent with the request * @default {} * @example * ``` * { * 'Authorization': 'Bearer your_token_here' * } * ``` */ headers: Record; /** * Custom properties to be sent with the request * @default {} * @example * ``` * { * 'user_id': 'user_id' * } * ``` */ properties?: Record; /** * Indicates whether the user agent should send or receive cookies from the other domain * in the case of cross-origin requests. */ credentials?: RequestCredentials; /** * Optional configuration for connecting to Model Context Protocol (MCP) servers. * This is typically derived from the AiProps and used internally. * @experimental */ mcpServers?: Array<{ endpoint: string; apiKey?: string }>; } export type InChatRenderFunction | CatchAllActionRenderProps> = (props: TProps) => string | JSX.Element; export type AiAgentInChatRenderFunction = ( props: AiAgentStateRenderProps, ) => string | JSX.Element | undefined | null; export interface ChatComponentsCache { actions: Record; aiAgentStateRenders: Record; } export interface AgentSession { agentName: string; threadId?: string; nodeName?: string; } export interface AuthState { status: "authenticated" | "unauthenticated"; authHeaders: Record; userId?: string; metadata?: Record; } export type ActionName = string; export type ContextTree = Tree; export interface AiContextParams { // function-calling actions: Record>; setAction: (id: string, action: FrontendAction) => void; removeAction: (id: string) => void; // coagent actions aiAgentStateRenders: Record>; setAiAgentStateRender: (id: string, stateRender: AiAgentStateRender) => void; removeAiAgentStateRender: (id: string) => void; chatComponentsCache: React.RefObject; getFunctionCallHandler: ( customEntryPoints?: Record>, ) => FunctionCallHandler; // text context addContext: (context: string, parentId?: string, categories?: string[]) => TreeNodeId; removeContext: (id: TreeNodeId) => void; getAllContext: () => Tree; getContextString: (documents: DocumentPointer[], categories: string[]) => string; // document context addDocumentContext: (documentPointer: DocumentPointer, categories?: string[]) => TreeNodeId; removeDocumentContext: (documentId: string) => void; getDocumentsContext: (categories: string[]) => DocumentPointer[]; isLoading: boolean; setIsLoading: React.Dispatch>; chatSuggestionConfiguration: { [key: string]: AiChatSuggestionConfiguration }; addChatSuggestionConfiguration: ( id: string, suggestion: AiChatSuggestionConfiguration, ) => void; removeChatSuggestionConfiguration: (id: string) => void; chatInstructions: string; setChatInstructions: React.Dispatch>; additionalInstructions?: string[]; setAdditionalInstructions: React.Dispatch>; // api endpoints aiApiConfig: AiApiConfig; showDevConsole: boolean; // agents aiAgentStates: Record; setAiAgentStates: React.Dispatch>>; aiAgentStatesRef: React.RefObject>; setAiAgentStatesWithRef: ( value: | Record | ((prev: Record) => Record), ) => void; agentSession: AgentSession | null; setAgentSession: React.Dispatch>; agentLock: string | null; threadId: string; setThreadId: React.Dispatch>; runId: string | null; setRunId: React.Dispatch>; // The chat abort controller can be used to stop generation globally, // i.e. when using `stop()` from `useChat` chatAbortControllerRef: React.MutableRefObject; // runtime runtimeClient: AiRuntimeClient; /** * The forwarded parameters to use for the task. */ forwardedParameters?: Partial>; availableAgents: Agent[]; /** * The auth states for the VN SDK. */ authStates_c?: Record; setAuthStates_c?: React.Dispatch>>; /** * The auth config for the VN SDK. */ authConfig_c?: { SignInComponent: React.ComponentType<{ onSignInComplete: (authState: AuthState) => void; }>; }; extensions: ExtensionsInput; setExtensions: React.Dispatch>; langGraphInterruptAction: LangGraphInterruptAction | null; setLangGraphInterruptAction: LangGraphInterruptActionSetter; removeLangGraphInterruptAction: (threadId: string) => void; /** * Optional trace handler for comprehensive debugging and observability. */ onError: AiErrorHandler; // banner error state bannerError: AiSDKError | null; setBannerError: React.Dispatch>; // Internal error handlers // These are used to handle errors that occur during the execution of the chat. // They are not intended for use by the developer. A component can register itself an error listener to be activated somewhere else as needed internalErrorHandlers: Record; setInternalErrorHandler: (handler: Record) => void; removeInternalErrorHandler: (id: string) => void; } const emptyAiContext: AiContextParams = { actions: {}, setAction: () => {}, removeAction: () => {}, aiAgentStateRenders: {}, setAiAgentStateRender: () => {}, removeAiAgentStateRender: () => {}, chatComponentsCache: { current: { actions: {}, aiAgentStateRenders: {} } }, getContextString: (documents: DocumentPointer[], categories: string[]) => returnAndThrowInDebug(""), addContext: () => "", removeContext: () => {}, getAllContext: () => [], getFunctionCallHandler: () => returnAndThrowInDebug(async () => {}), isLoading: false, setIsLoading: () => returnAndThrowInDebug(false), chatInstructions: "", setChatInstructions: () => returnAndThrowInDebug(""), additionalInstructions: [], setAdditionalInstructions: () => returnAndThrowInDebug([]), getDocumentsContext: (categories: string[]) => returnAndThrowInDebug([]), addDocumentContext: () => returnAndThrowInDebug(""), removeDocumentContext: () => {}, runtimeClient: {} as any, aiApiConfig: new (class implements AiApiConfig { get chatApiEndpoint(): string { throw new Error("Remember to wrap your app in a ` {...} ` !!!"); } get headers(): Record { return {}; } get body(): Record { return {}; } })(), chatSuggestionConfiguration: {}, addChatSuggestionConfiguration: () => {}, removeChatSuggestionConfiguration: () => {}, showDevConsole: false, aiAgentStates: {}, setAiAgentStates: () => {}, aiAgentStatesRef: { current: {} }, setAiAgentStatesWithRef: () => {}, agentSession: null, setAgentSession: () => {}, forwardedParameters: {}, agentLock: null, threadId: "", setThreadId: () => {}, runId: null, setRunId: () => {}, chatAbortControllerRef: { current: null }, availableAgents: [], extensions: {}, setExtensions: () => {}, langGraphInterruptAction: null, setLangGraphInterruptAction: () => {}, removeLangGraphInterruptAction: () => {}, onError: () => {}, bannerError: null, setBannerError: () => {}, internalErrorHandlers: {}, setInternalErrorHandler: () => {}, removeInternalErrorHandler: () => {}, }; export const AiContext = React.createContext(emptyAiContext); export function useAiContext(): AiContextParams { const context = React.useContext(AiContext); if (context === emptyAiContext) { throw new Error("Remember to wrap your app in a ` {...} ` !!!"); } return context; } function returnAndThrowInDebug(_value: T): T { throw new Error("Remember to wrap your app in a ` {...} ` !!!"); }