@tambo-ai/react 0.58.0 → 0.58.1
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/mcp/tambo-mcp-provider.js +1 -1
- package/dist/mcp/tambo-mcp-provider.js.map +1 -1
- package/dist/model/component-metadata.d.ts +1 -1
- package/dist/model/component-metadata.d.ts.map +1 -1
- package/dist/model/component-metadata.js.map +1 -1
- package/dist/providers/__tests__/tambo-thread-provider.test.js +374 -0
- package/dist/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
- package/dist/providers/tambo-thread-provider.d.ts.map +1 -1
- package/dist/providers/tambo-thread-provider.js +6 -8
- package/dist/providers/tambo-thread-provider.js.map +1 -1
- package/dist/testing/tools.d.ts +1 -1
- package/esm/mcp/tambo-mcp-provider.js +1 -1
- package/esm/mcp/tambo-mcp-provider.js.map +1 -1
- package/esm/model/component-metadata.d.ts +1 -1
- package/esm/model/component-metadata.d.ts.map +1 -1
- package/esm/model/component-metadata.js.map +1 -1
- package/esm/providers/__tests__/tambo-thread-provider.test.js +374 -0
- package/esm/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
- package/esm/providers/tambo-thread-provider.d.ts.map +1 -1
- package/esm/providers/tambo-thread-provider.js +6 -8
- package/esm/providers/tambo-thread-provider.js.map +1 -1
- package/esm/testing/tools.d.ts +1 -1
- package/package.json +1 -1
|
@@ -37,8 +37,8 @@ exports.useTamboMcpServers = exports.TamboMcpProvider = void 0;
|
|
|
37
37
|
exports.extractErrorMessage = extractErrorMessage;
|
|
38
38
|
const fast_equals_1 = require("fast-equals");
|
|
39
39
|
const react_1 = __importStar(require("react"));
|
|
40
|
-
const content_parts_1 = require("../util/content-parts");
|
|
41
40
|
const tambo_registry_provider_1 = require("../providers/tambo-registry-provider");
|
|
41
|
+
const content_parts_1 = require("../util/content-parts");
|
|
42
42
|
const mcp_client_1 = require("./mcp-client");
|
|
43
43
|
/**
|
|
44
44
|
* Extracts error message from MCP tool result content.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tambo-mcp-provider.js","sourceRoot":"","sources":["../../src/mcp/tambo-mcp-provider.tsx"],"names":[],"mappings":"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","sourcesContent":["import { deepEqual } from \"fast-equals\";\nimport React, {\n createContext,\n FC,\n useContext,\n useEffect,\n useState,\n} from \"react\";\nimport { TamboTool } from \"../model/component-metadata\";\nimport { isContentPartArray, toText } from \"../util/content-parts\";\nimport { useTamboRegistry } from \"../providers/tambo-registry-provider\";\nimport { MCPClient, MCPTransport } from \"./mcp-client\";\n\n/**\n * Extracts error message from MCP tool result content.\n * Handles both array and string content formats.\n * Always returns a string, even for invalid/null inputs.\n * @returns The extracted error message as a string\n */\nexport function extractErrorMessage(content: unknown): string {\n if (content === undefined || content === null) {\n return \"Unknown error occurred\";\n }\n\n if (Array.isArray(content)) {\n const textItems = content\n .filter(\n (item) => item && item.type === \"text\" && typeof item.text === \"string\",\n )\n .map((item) => item.text);\n\n return textItems.length > 0\n ? textItems.join(\" \")\n : \"Error occurred but no details provided\";\n }\n\n if (typeof content === \"object\") {\n return JSON.stringify(content);\n }\n\n return `${content}`;\n}\n\nexport interface McpServerInfo {\n name?: string;\n url: string;\n description?: string;\n transport?: MCPTransport;\n customHeaders?: Record<string, string>;\n}\n\nexport interface ConnectedMcpServer extends McpServerInfo {\n client: MCPClient;\n}\n\nexport interface FailedMcpServer extends McpServerInfo {\n client?: never;\n connectionError: Error;\n}\n\nexport type McpServer = ConnectedMcpServer | FailedMcpServer;\n\nconst McpProviderContext = createContext<McpServer[]>([]);\n/**\n * This provider is used to register tools from MCP servers.\n * @returns the wrapped children\n */\nexport const TamboMcpProvider: FC<{\n mcpServers: (McpServerInfo | string)[];\n children: React.ReactNode;\n}> = ({ mcpServers, children }) => {\n const { registerTool } = useTamboRegistry();\n const [connectedMcpServers, setConnectedMcpServers] = useState<McpServer[]>(\n [],\n );\n\n useEffect(() => {\n if (!mcpServers) {\n return;\n }\n async function registerMcpServers(mcpServerInfos: McpServerInfo[]) {\n // Maps tool names to the MCP client that registered them\n const mcpServerMap = new Map<string, McpServer>();\n setConnectedMcpServers((prev) =>\n // remove any servers that are not in the new list\n prev.filter((s) =>\n mcpServerInfos.some((mcpServerInfo) =>\n equalsMcpServer(s, mcpServerInfo),\n ),\n ),\n );\n\n // initialize the MCP clients, converting McpServerInfo -> McpServer\n const mcpServers = await Promise.allSettled(\n mcpServerInfos.map(async (mcpServerInfo): Promise<McpServer> => {\n try {\n const client = await MCPClient.create(\n mcpServerInfo.url,\n mcpServerInfo.transport,\n mcpServerInfo.customHeaders,\n undefined, // no oauth support yet\n undefined, // starting with no session id at first.\n );\n const connectedMcpServer = {\n ...mcpServerInfo,\n client: client,\n };\n // note because the promises may resolve in any order, the resulting\n // array may not be in the same order as the input array\n setConnectedMcpServers((prev) => [\n // replace the server if it already exists\n ...prev.filter((s) => !equalsMcpServer(s, mcpServerInfo)),\n connectedMcpServer,\n ]);\n return connectedMcpServer;\n } catch (error) {\n const failedMcpServer = {\n ...mcpServerInfo,\n connectionError: error as Error,\n };\n // note because the promises may resolve in any order, the resulting\n // array may not be in the same order as the input array\n setConnectedMcpServers((prev) => [\n // replace the server if it already exists\n ...prev.filter((s) => !equalsMcpServer(s, mcpServerInfo)),\n failedMcpServer,\n ]);\n return failedMcpServer;\n }\n }),\n );\n\n // note do not rely on the state\n const connectedMcpServers = mcpServers\n .filter((result) => result.status === \"fulfilled\")\n .map((result) => result.value);\n\n // Now create a map of tool name to MCP client\n const serverToolLists = connectedMcpServers.map(async (mcpServer) => {\n const tools = (await mcpServer.client?.listTools()) ?? [];\n tools.forEach((tool) => {\n mcpServerMap.set(tool.name, mcpServer);\n });\n return tools;\n });\n const toolResults = await Promise.allSettled(serverToolLists);\n\n // Just log the failed tools, we can't do anything about them\n const failedTools = toolResults.filter(\n (result) => result.status === \"rejected\",\n );\n if (failedTools.length > 0) {\n console.error(\n \"Failed to register tools from MCP servers:\",\n failedTools.map((result) => result.reason),\n );\n }\n\n // Register the successful tools\n const allTools = toolResults\n .filter((result) => result.status === \"fulfilled\")\n .map((result) => result.value)\n .flat();\n allTools.forEach((tool) => {\n registerTool({\n description: tool.description ?? \"\",\n name: tool.name,\n tool: async (args: Record<string, unknown>) => {\n const mcpServer = mcpServerMap.get(tool.name);\n if (!mcpServer) {\n // should never happen\n throw new Error(`MCP server for tool ${tool.name} not found`);\n }\n if (!mcpServer.client) {\n // this can't actually happen because the tool can't be registered if the server is not connected\n throw new Error(\n `MCP server for tool ${tool.name} is not connected`,\n );\n }\n const result = await mcpServer.client.callTool(tool.name, args);\n if (result.isError) {\n const errorMessage = extractErrorMessage(result.content);\n throw new Error(errorMessage);\n }\n return result.content;\n },\n toolSchema: tool.inputSchema as TamboTool[\"toolSchema\"],\n transformToContent: (content: unknown) => {\n // MCP tools can return content in various formats; pass through arrays of content parts\n // unchanged, otherwise stringify into a text content part.\n if (isContentPartArray(content)) {\n return content;\n }\n return [{ type: \"text\", text: toText(content) }];\n },\n });\n });\n }\n\n // normalize the server infos\n const mcpServerInfos = mcpServers.map((mcpServer) =>\n typeof mcpServer === \"string\"\n ? { url: mcpServer, transport: MCPTransport.SSE }\n : mcpServer,\n );\n\n registerMcpServers(mcpServerInfos);\n }, [mcpServers, registerTool]);\n\n return (\n <McpProviderContext.Provider value={connectedMcpServers}>\n {children}\n </McpProviderContext.Provider>\n );\n};\n\n/**\n * Hook to access the actual MCP servers, as they are connected (or fail to\n * connect).\n *\n * You can call methods on the MCP client that is included in the MCP server\n * object.\n *\n * If the server fails to connect, the `client` property will be `undefined` and\n * the `connectionError` property will be set.\n *\n * For example, to forcibly disconnect and reconnect all MCP servers:\n *\n * ```tsx\n * const mcpServers = useTamboMcpServers();\n * mcpServers.forEach((mcpServer) => {\n * mcpServer.client?.reconnect();\n * });\n * ```\n *\n * Note that the MCP servers are not guaranteed to be in the same order as the\n * input array, because they are added as they are connected.\n * @returns The MCP servers\n */\nexport const useTamboMcpServers = () => {\n return useContext(McpProviderContext);\n};\nfunction equalsMcpServer(s: McpServer, mcpServerInfo: McpServerInfo): boolean {\n return (\n s.url === mcpServerInfo.url &&\n s.transport === mcpServerInfo.transport &&\n deepEqual(s.customHeaders, mcpServerInfo.customHeaders)\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tambo-mcp-provider.js","sourceRoot":"","sources":["../../src/mcp/tambo-mcp-provider.tsx"],"names":[],"mappings":"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","sourcesContent":["import { deepEqual } from \"fast-equals\";\nimport React, {\n createContext,\n FC,\n useContext,\n useEffect,\n useState,\n} from \"react\";\nimport { TamboTool } from \"../model/component-metadata\";\nimport { useTamboRegistry } from \"../providers/tambo-registry-provider\";\nimport { isContentPartArray, toText } from \"../util/content-parts\";\nimport { MCPClient, MCPTransport } from \"./mcp-client\";\n\n/**\n * Extracts error message from MCP tool result content.\n * Handles both array and string content formats.\n * Always returns a string, even for invalid/null inputs.\n * @returns The extracted error message as a string\n */\nexport function extractErrorMessage(content: unknown): string {\n if (content === undefined || content === null) {\n return \"Unknown error occurred\";\n }\n\n if (Array.isArray(content)) {\n const textItems = content\n .filter(\n (item) => item && item.type === \"text\" && typeof item.text === \"string\",\n )\n .map((item) => item.text);\n\n return textItems.length > 0\n ? textItems.join(\" \")\n : \"Error occurred but no details provided\";\n }\n\n if (typeof content === \"object\") {\n return JSON.stringify(content);\n }\n\n return `${content}`;\n}\n\nexport interface McpServerInfo {\n name?: string;\n url: string;\n description?: string;\n transport?: MCPTransport;\n customHeaders?: Record<string, string>;\n}\n\nexport interface ConnectedMcpServer extends McpServerInfo {\n client: MCPClient;\n}\n\nexport interface FailedMcpServer extends McpServerInfo {\n client?: never;\n connectionError: Error;\n}\n\nexport type McpServer = ConnectedMcpServer | FailedMcpServer;\n\nconst McpProviderContext = createContext<McpServer[]>([]);\n/**\n * This provider is used to register tools from MCP servers.\n * @returns the wrapped children\n */\nexport const TamboMcpProvider: FC<{\n mcpServers: (McpServerInfo | string)[];\n children: React.ReactNode;\n}> = ({ mcpServers, children }) => {\n const { registerTool } = useTamboRegistry();\n const [connectedMcpServers, setConnectedMcpServers] = useState<McpServer[]>(\n [],\n );\n\n useEffect(() => {\n if (!mcpServers) {\n return;\n }\n async function registerMcpServers(mcpServerInfos: McpServerInfo[]) {\n // Maps tool names to the MCP client that registered them\n const mcpServerMap = new Map<string, McpServer>();\n setConnectedMcpServers((prev) =>\n // remove any servers that are not in the new list\n prev.filter((s) =>\n mcpServerInfos.some((mcpServerInfo) =>\n equalsMcpServer(s, mcpServerInfo),\n ),\n ),\n );\n\n // initialize the MCP clients, converting McpServerInfo -> McpServer\n const mcpServers = await Promise.allSettled(\n mcpServerInfos.map(async (mcpServerInfo): Promise<McpServer> => {\n try {\n const client = await MCPClient.create(\n mcpServerInfo.url,\n mcpServerInfo.transport,\n mcpServerInfo.customHeaders,\n undefined, // no oauth support yet\n undefined, // starting with no session id at first.\n );\n const connectedMcpServer = {\n ...mcpServerInfo,\n client: client,\n };\n // note because the promises may resolve in any order, the resulting\n // array may not be in the same order as the input array\n setConnectedMcpServers((prev) => [\n // replace the server if it already exists\n ...prev.filter((s) => !equalsMcpServer(s, mcpServerInfo)),\n connectedMcpServer,\n ]);\n return connectedMcpServer;\n } catch (error) {\n const failedMcpServer = {\n ...mcpServerInfo,\n connectionError: error as Error,\n };\n // note because the promises may resolve in any order, the resulting\n // array may not be in the same order as the input array\n setConnectedMcpServers((prev) => [\n // replace the server if it already exists\n ...prev.filter((s) => !equalsMcpServer(s, mcpServerInfo)),\n failedMcpServer,\n ]);\n return failedMcpServer;\n }\n }),\n );\n\n // note do not rely on the state\n const connectedMcpServers = mcpServers\n .filter((result) => result.status === \"fulfilled\")\n .map((result) => result.value);\n\n // Now create a map of tool name to MCP client\n const serverToolLists = connectedMcpServers.map(async (mcpServer) => {\n const tools = (await mcpServer.client?.listTools()) ?? [];\n tools.forEach((tool) => {\n mcpServerMap.set(tool.name, mcpServer);\n });\n return tools;\n });\n const toolResults = await Promise.allSettled(serverToolLists);\n\n // Just log the failed tools, we can't do anything about them\n const failedTools = toolResults.filter(\n (result) => result.status === \"rejected\",\n );\n if (failedTools.length > 0) {\n console.error(\n \"Failed to register tools from MCP servers:\",\n failedTools.map((result) => result.reason),\n );\n }\n\n // Register the successful tools\n const allTools = toolResults\n .filter((result) => result.status === \"fulfilled\")\n .map((result) => result.value)\n .flat();\n allTools.forEach((tool) => {\n registerTool({\n description: tool.description ?? \"\",\n name: tool.name,\n tool: async (args: Record<string, unknown>) => {\n const mcpServer = mcpServerMap.get(tool.name);\n if (!mcpServer) {\n // should never happen\n throw new Error(`MCP server for tool ${tool.name} not found`);\n }\n if (!mcpServer.client) {\n // this can't actually happen because the tool can't be registered if the server is not connected\n throw new Error(\n `MCP server for tool ${tool.name} is not connected`,\n );\n }\n const result = await mcpServer.client.callTool(tool.name, args);\n if (result.isError) {\n const errorMessage = extractErrorMessage(result.content);\n throw new Error(errorMessage);\n }\n return result.content;\n },\n toolSchema: tool.inputSchema as TamboTool[\"toolSchema\"],\n transformToContent: (content: unknown) => {\n // MCP tools can return content in various formats; pass through arrays of content parts\n // unchanged, otherwise stringify into a text content part.\n if (isContentPartArray(content)) {\n return content;\n }\n return [{ type: \"text\", text: toText(content) }];\n },\n });\n });\n }\n\n // normalize the server infos\n const mcpServerInfos = mcpServers.map((mcpServer) =>\n typeof mcpServer === \"string\"\n ? { url: mcpServer, transport: MCPTransport.SSE }\n : mcpServer,\n );\n\n registerMcpServers(mcpServerInfos);\n }, [mcpServers, registerTool]);\n\n return (\n <McpProviderContext.Provider value={connectedMcpServers}>\n {children}\n </McpProviderContext.Provider>\n );\n};\n\n/**\n * Hook to access the actual MCP servers, as they are connected (or fail to\n * connect).\n *\n * You can call methods on the MCP client that is included in the MCP server\n * object.\n *\n * If the server fails to connect, the `client` property will be `undefined` and\n * the `connectionError` property will be set.\n *\n * For example, to forcibly disconnect and reconnect all MCP servers:\n *\n * ```tsx\n * const mcpServers = useTamboMcpServers();\n * mcpServers.forEach((mcpServer) => {\n * mcpServer.client?.reconnect();\n * });\n * ```\n *\n * Note that the MCP servers are not guaranteed to be in the same order as the\n * input array, because they are added as they are connected.\n * @returns The MCP servers\n */\nexport const useTamboMcpServers = () => {\n return useContext(McpProviderContext);\n};\nfunction equalsMcpServer(s: McpServer, mcpServerInfo: McpServerInfo): boolean {\n return (\n s.url === mcpServerInfo.url &&\n s.transport === mcpServerInfo.transport &&\n deepEqual(s.customHeaders, mcpServerInfo.customHeaders)\n );\n}\n"]}
|
|
@@ -44,7 +44,7 @@ export interface TamboTool<Args extends z.ZodTuple<any, any> = z.ZodTuple<any, a
|
|
|
44
44
|
* @param result - The result returned by the tool function
|
|
45
45
|
* @returns An array of content parts to be sent back to the AI
|
|
46
46
|
*/
|
|
47
|
-
transformToContent?: (result: z.infer<Returns>) => TamboAI.Beta.Threads.ChatCompletionContentPart[];
|
|
47
|
+
transformToContent?: (result: z.infer<Returns>) => Promise<TamboAI.Beta.Threads.ChatCompletionContentPart[]> | TamboAI.Beta.Threads.ChatCompletionContentPart[];
|
|
48
48
|
}
|
|
49
49
|
export type TamboToolAssociations = Record<string, string[]>;
|
|
50
50
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-metadata.d.ts","sourceRoot":"","sources":["../../src/model/component-metadata.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,0BAA0B,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,CAAC,MAAM,KAAK,CAAC;AACpB,OAAO,KAAK,eAAe,MAAM,oBAAoB,CAAC;AACtD,+FAA+F;AAC/F,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,cAAc,GAAG;IACnD,MAAM,CAAC,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;CAC7C,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,4BACf,SAAQ,OAAO,CAAC,4BAA4B;IAC5C,UAAU,EAAE,aAAa,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,mBAAmB,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IACtD,UAAU,EAAE,4BAA4B,CAAC;CAC1C;AAED,MAAM,WAAW,mBAAoB,SAAQ,OAAO,CAAC,kBAAkB;IACrE,SAAS,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC;IAC9B,gBAAgB,CAAC,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC;CACvC;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AAEpE,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,GAAG;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,WAAW,SAAS,CACxB,IAAI,SAAS,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,EACxD,OAAO,SAAS,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU;IAE3C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnD,UAAU,EAAE,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,cAAc,CAAC;IAC1D;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,CACnB,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"component-metadata.d.ts","sourceRoot":"","sources":["../../src/model/component-metadata.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,0BAA0B,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,CAAC,MAAM,KAAK,CAAC;AACpB,OAAO,KAAK,eAAe,MAAM,oBAAoB,CAAC;AACtD,+FAA+F;AAC/F,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,cAAc,GAAG;IACnD,MAAM,CAAC,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;CAC7C,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,4BACf,SAAQ,OAAO,CAAC,4BAA4B;IAC5C,UAAU,EAAE,aAAa,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,mBAAmB,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IACtD,UAAU,EAAE,4BAA4B,CAAC;CAC1C;AAED,MAAM,WAAW,mBAAoB,SAAQ,OAAO,CAAC,kBAAkB;IACrE,SAAS,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC;IAC9B,gBAAgB,CAAC,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC;CACvC;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AAEpE,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,GAAG;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,WAAW,SAAS,CACxB,IAAI,SAAS,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,EACxD,OAAO,SAAS,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU;IAE3C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnD,UAAU,EAAE,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,cAAc,CAAC;IAC1D;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,CACnB,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAEtB,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,yBAAyB,EAAE,CAAC,GACzD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,yBAAyB,EAAE,CAAC;CACtD;AAED,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;AAC7D;;GAEG;AAEH,MAAM,WAAW,cAAc;IAC7B,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;;;;;;;;;;;;;OAkBG;IACH,SAAS,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC;IAE9B;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,CAAC,UAAU,GAAG,WAAW,CAAC;IACzC;;;;OAIG;IACH,eAAe,CAAC,EAAE,GAAG,CAAC;IACtB,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC;IACtC,uDAAuD;IACvD,eAAe,CAAC,EAAE,SAAS,EAAE,CAAC;CAC/B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-metadata.js","sourceRoot":"","sources":["../../src/model/component-metadata.ts"],"names":[],"mappings":"","sourcesContent":["import TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { JSONSchema7 } from \"json-schema\";\nimport { ComponentType } from \"react\";\nimport z from \"zod\";\nimport type zodToJsonSchema from \"zod-to-json-schema\";\n/** Extension of the ToolParameters interface from Tambo AI to include JSONSchema definition */\nexport type ParameterSpec = TamboAI.ToolParameters & {\n schema?: ReturnType<typeof zodToJsonSchema>;\n};\n\n/**\n * Extends the base ContextTool interface from Tambo AI to include schema information\n * for parameter validation using zod-to-json-schema.\n */\nexport interface ComponentContextToolMetadata\n extends TamboAI.ComponentContextToolMetadata {\n parameters: ParameterSpec[];\n}\n\nexport interface ComponentContextTool {\n getComponentContext: (...args: any[]) => Promise<any>;\n definition: ComponentContextToolMetadata;\n}\n\nexport interface RegisteredComponent extends TamboAI.AvailableComponent {\n component: ComponentType<any>;\n loadingComponent?: ComponentType<any>;\n}\n\nexport type ComponentRegistry = Record<string, RegisteredComponent>;\n\nexport type TamboToolRegistry = Record<string, TamboTool>;\n\n/**\n * A JSON Schema that is compatible with the MCP.\n * This is a simplified JSON Schema that is compatible with the MCPClient and the toolSchema.\n *\n * Do not export this type from the SDK.\n */\nexport type JSONSchemaLite = ReturnType<typeof zodToJsonSchema> & {\n description?: string;\n};\n\nexport interface TamboTool<\n Args extends z.ZodTuple<any, any> = z.ZodTuple<any, any>,\n Returns extends z.ZodTypeAny = z.ZodTypeAny,\n> {\n name: string;\n description: string;\n tool: (...args: z.infer<Args>) => z.infer<Returns>;\n toolSchema: z.ZodFunction<Args, Returns> | JSONSchemaLite;\n /**\n * Optional function to transform the tool's return value into an array of content parts.\n * If not provided, the return value will be converted to a string and wrapped in a text content part.\n * @param result - The result returned by the tool function\n * @returns An array of content parts to be sent back to the AI\n */\n transformToContent?: (\n result: z.infer<Returns>,\n )
|
|
1
|
+
{"version":3,"file":"component-metadata.js","sourceRoot":"","sources":["../../src/model/component-metadata.ts"],"names":[],"mappings":"","sourcesContent":["import TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { JSONSchema7 } from \"json-schema\";\nimport { ComponentType } from \"react\";\nimport z from \"zod\";\nimport type zodToJsonSchema from \"zod-to-json-schema\";\n/** Extension of the ToolParameters interface from Tambo AI to include JSONSchema definition */\nexport type ParameterSpec = TamboAI.ToolParameters & {\n schema?: ReturnType<typeof zodToJsonSchema>;\n};\n\n/**\n * Extends the base ContextTool interface from Tambo AI to include schema information\n * for parameter validation using zod-to-json-schema.\n */\nexport interface ComponentContextToolMetadata\n extends TamboAI.ComponentContextToolMetadata {\n parameters: ParameterSpec[];\n}\n\nexport interface ComponentContextTool {\n getComponentContext: (...args: any[]) => Promise<any>;\n definition: ComponentContextToolMetadata;\n}\n\nexport interface RegisteredComponent extends TamboAI.AvailableComponent {\n component: ComponentType<any>;\n loadingComponent?: ComponentType<any>;\n}\n\nexport type ComponentRegistry = Record<string, RegisteredComponent>;\n\nexport type TamboToolRegistry = Record<string, TamboTool>;\n\n/**\n * A JSON Schema that is compatible with the MCP.\n * This is a simplified JSON Schema that is compatible with the MCPClient and the toolSchema.\n *\n * Do not export this type from the SDK.\n */\nexport type JSONSchemaLite = ReturnType<typeof zodToJsonSchema> & {\n description?: string;\n};\n\nexport interface TamboTool<\n Args extends z.ZodTuple<any, any> = z.ZodTuple<any, any>,\n Returns extends z.ZodTypeAny = z.ZodTypeAny,\n> {\n name: string;\n description: string;\n tool: (...args: z.infer<Args>) => z.infer<Returns>;\n toolSchema: z.ZodFunction<Args, Returns> | JSONSchemaLite;\n /**\n * Optional function to transform the tool's return value into an array of content parts.\n * If not provided, the return value will be converted to a string and wrapped in a text content part.\n * @param result - The result returned by the tool function\n * @returns An array of content parts to be sent back to the AI\n */\n transformToContent?: (\n result: z.infer<Returns>,\n ) =>\n | Promise<TamboAI.Beta.Threads.ChatCompletionContentPart[]>\n | TamboAI.Beta.Threads.ChatCompletionContentPart[];\n}\n\nexport type TamboToolAssociations = Record<string, string[]>;\n/**\n * A component that can be registered with the TamboRegistryProvider.\n */\n\nexport interface TamboComponent {\n /** The name of the component */\n name: string;\n /** The description of the component */\n description: string;\n /**\n * The React component to render.\n *\n * Make sure to pass the Component itself, not an instance of the component. For example,\n * if you have a component like this:\n *\n * ```tsx\n * const MyComponent = () => {\n * return <div>My Component</div>;\n * };\n * ```\n *\n * You should pass the `Component`:\n *\n * ```tsx\n * const components = [MyComponent];\n * <TamboRegistryProvider components={components} />\n * ```\n */\n component: ComponentType<any>;\n\n /**\n * A zod schema for the component props. (Recommended)\n * Either this or propsDefinition must be provided, but not both.\n */\n propsSchema?: z.ZodTypeAny | JSONSchema7;\n /**\n * The props definition of the component as a JSON object.\n * Either this or propsSchema must be provided, but not both.\n * @deprecated Use propsSchema instead.\n */\n propsDefinition?: any;\n /** The loading component to render while the component is loading */\n loadingComponent?: ComponentType<any>;\n /** The tools that are associated with the component */\n associatedTools?: TamboTool[];\n}\n"]}
|
|
@@ -849,5 +849,379 @@ describe("TamboThreadProvider", () => {
|
|
|
849
849
|
expect(result.current.thread.id).toBe("existing-thread-123");
|
|
850
850
|
});
|
|
851
851
|
});
|
|
852
|
+
describe("transformToContent", () => {
|
|
853
|
+
it("should use custom transformToContent when provided (non-streaming)", async () => {
|
|
854
|
+
const mockTransformToContent = jest.fn().mockReturnValue([
|
|
855
|
+
{ type: "text", text: "Custom transformed content" },
|
|
856
|
+
{
|
|
857
|
+
type: "image_url",
|
|
858
|
+
image_url: { url: "https://example.com/image.png" },
|
|
859
|
+
},
|
|
860
|
+
]);
|
|
861
|
+
const customToolRegistry = [
|
|
862
|
+
{
|
|
863
|
+
name: "TestComponent",
|
|
864
|
+
component: () => react_2.default.createElement("div", null, "Test"),
|
|
865
|
+
description: "Test",
|
|
866
|
+
propsSchema: zod_1.z.object({ test: zod_1.z.string() }),
|
|
867
|
+
associatedTools: [
|
|
868
|
+
{
|
|
869
|
+
name: "custom-tool",
|
|
870
|
+
tool: jest.fn().mockResolvedValue({ data: "tool result" }),
|
|
871
|
+
description: "Tool with custom transform",
|
|
872
|
+
toolSchema: zod_1.z
|
|
873
|
+
.function()
|
|
874
|
+
.args(zod_1.z.string())
|
|
875
|
+
.returns(zod_1.z.object({ data: zod_1.z.string() })),
|
|
876
|
+
transformToContent: mockTransformToContent,
|
|
877
|
+
},
|
|
878
|
+
],
|
|
879
|
+
},
|
|
880
|
+
];
|
|
881
|
+
const wrapperWithCustomTool = ({ children, }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { components: customToolRegistry },
|
|
882
|
+
react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, { contextHelpers: {
|
|
883
|
+
currentTimeContextHelper: () => null,
|
|
884
|
+
currentPageContextHelper: () => null,
|
|
885
|
+
} },
|
|
886
|
+
react_2.default.createElement(tambo_thread_provider_1.TamboThreadProvider, { streaming: false }, children))));
|
|
887
|
+
const mockToolCallResponse = {
|
|
888
|
+
responseMessageDto: {
|
|
889
|
+
id: "tool-call-1",
|
|
890
|
+
content: [{ type: "text", text: "Tool response" }],
|
|
891
|
+
role: "tool",
|
|
892
|
+
threadId: "test-thread-1",
|
|
893
|
+
toolCallRequest: {
|
|
894
|
+
toolName: "custom-tool",
|
|
895
|
+
parameters: [{ parameterName: "input", parameterValue: "test" }],
|
|
896
|
+
},
|
|
897
|
+
componentState: {},
|
|
898
|
+
createdAt: new Date().toISOString(),
|
|
899
|
+
},
|
|
900
|
+
generationStage: generate_component_response_1.GenerationStage.COMPLETE,
|
|
901
|
+
mcpAccessToken: "test-mcp-access-token",
|
|
902
|
+
};
|
|
903
|
+
jest
|
|
904
|
+
.mocked(mockThreadsApi.advanceByID)
|
|
905
|
+
.mockResolvedValueOnce(mockToolCallResponse)
|
|
906
|
+
.mockResolvedValueOnce({
|
|
907
|
+
responseMessageDto: {
|
|
908
|
+
id: "final-response",
|
|
909
|
+
content: [{ type: "text", text: "Final response" }],
|
|
910
|
+
role: "assistant",
|
|
911
|
+
threadId: "test-thread-1",
|
|
912
|
+
componentState: {},
|
|
913
|
+
createdAt: new Date().toISOString(),
|
|
914
|
+
},
|
|
915
|
+
generationStage: generate_component_response_1.GenerationStage.COMPLETE,
|
|
916
|
+
mcpAccessToken: "test-mcp-access-token",
|
|
917
|
+
});
|
|
918
|
+
const { result } = (0, react_1.renderHook)(() => (0, tambo_thread_provider_1.useTamboThread)(), {
|
|
919
|
+
wrapper: wrapperWithCustomTool,
|
|
920
|
+
});
|
|
921
|
+
await (0, react_1.act)(async () => {
|
|
922
|
+
await result.current.sendThreadMessage("Use custom tool", {
|
|
923
|
+
threadId: "test-thread-1",
|
|
924
|
+
streamResponse: false,
|
|
925
|
+
});
|
|
926
|
+
});
|
|
927
|
+
// Verify the tool was called
|
|
928
|
+
expect(customToolRegistry[0]?.associatedTools?.[0]?.tool).toHaveBeenCalledWith("test");
|
|
929
|
+
// Verify transformToContent was called with the tool result
|
|
930
|
+
expect(mockTransformToContent).toHaveBeenCalledWith({
|
|
931
|
+
data: "tool result",
|
|
932
|
+
});
|
|
933
|
+
// Verify the second advance call included the transformed content
|
|
934
|
+
expect(mockThreadsApi.advanceByID).toHaveBeenCalledTimes(2);
|
|
935
|
+
expect(mockThreadsApi.advanceByID).toHaveBeenLastCalledWith("test-thread-1", expect.objectContaining({
|
|
936
|
+
messageToAppend: expect.objectContaining({
|
|
937
|
+
content: [
|
|
938
|
+
{ type: "text", text: "Custom transformed content" },
|
|
939
|
+
{
|
|
940
|
+
type: "image_url",
|
|
941
|
+
image_url: { url: "https://example.com/image.png" },
|
|
942
|
+
},
|
|
943
|
+
],
|
|
944
|
+
role: "tool",
|
|
945
|
+
}),
|
|
946
|
+
}));
|
|
947
|
+
});
|
|
948
|
+
it("should use custom async transformToContent when provided (streaming)", async () => {
|
|
949
|
+
const mockTransformToContent = jest
|
|
950
|
+
.fn()
|
|
951
|
+
.mockResolvedValue([
|
|
952
|
+
{ type: "text", text: "Async transformed content" },
|
|
953
|
+
]);
|
|
954
|
+
const customToolRegistry = [
|
|
955
|
+
{
|
|
956
|
+
name: "TestComponent",
|
|
957
|
+
component: () => react_2.default.createElement("div", null, "Test"),
|
|
958
|
+
description: "Test",
|
|
959
|
+
propsSchema: zod_1.z.object({ test: zod_1.z.string() }),
|
|
960
|
+
associatedTools: [
|
|
961
|
+
{
|
|
962
|
+
name: "async-tool",
|
|
963
|
+
tool: jest.fn().mockResolvedValue({ data: "async tool result" }),
|
|
964
|
+
description: "Tool with async transform",
|
|
965
|
+
toolSchema: zod_1.z
|
|
966
|
+
.function()
|
|
967
|
+
.args(zod_1.z.string())
|
|
968
|
+
.returns(zod_1.z.object({ data: zod_1.z.string() })),
|
|
969
|
+
transformToContent: mockTransformToContent,
|
|
970
|
+
},
|
|
971
|
+
],
|
|
972
|
+
},
|
|
973
|
+
];
|
|
974
|
+
const wrapperWithAsyncTool = ({ children, }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { components: customToolRegistry },
|
|
975
|
+
react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, { contextHelpers: {
|
|
976
|
+
currentTimeContextHelper: () => null,
|
|
977
|
+
currentPageContextHelper: () => null,
|
|
978
|
+
} },
|
|
979
|
+
react_2.default.createElement(tambo_thread_provider_1.TamboThreadProvider, { streaming: true }, children))));
|
|
980
|
+
const mockToolCallChunk = {
|
|
981
|
+
responseMessageDto: {
|
|
982
|
+
id: "tool-call-chunk",
|
|
983
|
+
content: [{ type: "text", text: "Tool call" }],
|
|
984
|
+
role: "tool",
|
|
985
|
+
threadId: "test-thread-1",
|
|
986
|
+
toolCallRequest: {
|
|
987
|
+
toolName: "async-tool",
|
|
988
|
+
parameters: [
|
|
989
|
+
{ parameterName: "input", parameterValue: "async-test" },
|
|
990
|
+
],
|
|
991
|
+
},
|
|
992
|
+
componentState: {},
|
|
993
|
+
createdAt: new Date().toISOString(),
|
|
994
|
+
},
|
|
995
|
+
generationStage: generate_component_response_1.GenerationStage.COMPLETE,
|
|
996
|
+
mcpAccessToken: "test-mcp-access-token",
|
|
997
|
+
};
|
|
998
|
+
const mockFinalChunk = {
|
|
999
|
+
responseMessageDto: {
|
|
1000
|
+
id: "final-chunk",
|
|
1001
|
+
content: [{ type: "text", text: "Final streaming response" }],
|
|
1002
|
+
role: "assistant",
|
|
1003
|
+
threadId: "test-thread-1",
|
|
1004
|
+
componentState: {},
|
|
1005
|
+
createdAt: new Date().toISOString(),
|
|
1006
|
+
},
|
|
1007
|
+
generationStage: generate_component_response_1.GenerationStage.COMPLETE,
|
|
1008
|
+
mcpAccessToken: "test-mcp-access-token",
|
|
1009
|
+
};
|
|
1010
|
+
const mockAsyncIterator = {
|
|
1011
|
+
[Symbol.asyncIterator]: async function* () {
|
|
1012
|
+
yield mockToolCallChunk;
|
|
1013
|
+
yield mockFinalChunk;
|
|
1014
|
+
},
|
|
1015
|
+
};
|
|
1016
|
+
jest
|
|
1017
|
+
.mocked(typescript_sdk_1.advanceStream)
|
|
1018
|
+
.mockResolvedValueOnce(mockAsyncIterator)
|
|
1019
|
+
.mockResolvedValueOnce({
|
|
1020
|
+
[Symbol.asyncIterator]: async function* () {
|
|
1021
|
+
yield mockFinalChunk;
|
|
1022
|
+
},
|
|
1023
|
+
});
|
|
1024
|
+
const { result } = (0, react_1.renderHook)(() => (0, tambo_thread_provider_1.useTamboThread)(), {
|
|
1025
|
+
wrapper: wrapperWithAsyncTool,
|
|
1026
|
+
});
|
|
1027
|
+
await (0, react_1.act)(async () => {
|
|
1028
|
+
await result.current.sendThreadMessage("Use async tool", {
|
|
1029
|
+
threadId: "test-thread-1",
|
|
1030
|
+
streamResponse: true,
|
|
1031
|
+
});
|
|
1032
|
+
});
|
|
1033
|
+
// Verify the tool was called
|
|
1034
|
+
expect(customToolRegistry[0]?.associatedTools?.[0]?.tool).toHaveBeenCalledWith("async-test");
|
|
1035
|
+
// Verify transformToContent was called
|
|
1036
|
+
expect(mockTransformToContent).toHaveBeenCalledWith({
|
|
1037
|
+
data: "async tool result",
|
|
1038
|
+
});
|
|
1039
|
+
// Verify advanceStream was called twice (initial request and tool response)
|
|
1040
|
+
expect(typescript_sdk_1.advanceStream).toHaveBeenCalledTimes(2);
|
|
1041
|
+
// Verify the second advanceStream call included the transformed content
|
|
1042
|
+
expect(typescript_sdk_1.advanceStream).toHaveBeenLastCalledWith(mockTamboAI, expect.objectContaining({
|
|
1043
|
+
messageToAppend: expect.objectContaining({
|
|
1044
|
+
content: [{ type: "text", text: "Async transformed content" }],
|
|
1045
|
+
role: "tool",
|
|
1046
|
+
}),
|
|
1047
|
+
}), "test-thread-1");
|
|
1048
|
+
});
|
|
1049
|
+
it("should fallback to stringified text when transformToContent is not provided", async () => {
|
|
1050
|
+
const toolWithoutTransform = [
|
|
1051
|
+
{
|
|
1052
|
+
name: "TestComponent",
|
|
1053
|
+
component: () => react_2.default.createElement("div", null, "Test"),
|
|
1054
|
+
description: "Test",
|
|
1055
|
+
propsSchema: zod_1.z.object({ test: zod_1.z.string() }),
|
|
1056
|
+
associatedTools: [
|
|
1057
|
+
{
|
|
1058
|
+
name: "no-transform-tool",
|
|
1059
|
+
tool: jest
|
|
1060
|
+
.fn()
|
|
1061
|
+
.mockResolvedValue({ complex: "data", nested: { value: 42 } }),
|
|
1062
|
+
description: "Tool without custom transform",
|
|
1063
|
+
toolSchema: zod_1.z
|
|
1064
|
+
.function()
|
|
1065
|
+
.args(zod_1.z.string())
|
|
1066
|
+
.returns(zod_1.z.object({
|
|
1067
|
+
complex: zod_1.z.string(),
|
|
1068
|
+
nested: zod_1.z.object({ value: zod_1.z.number() }),
|
|
1069
|
+
})),
|
|
1070
|
+
// No transformToContent provided
|
|
1071
|
+
},
|
|
1072
|
+
],
|
|
1073
|
+
},
|
|
1074
|
+
];
|
|
1075
|
+
const wrapperWithoutTransform = ({ children, }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { components: toolWithoutTransform },
|
|
1076
|
+
react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, { contextHelpers: {
|
|
1077
|
+
currentTimeContextHelper: () => null,
|
|
1078
|
+
currentPageContextHelper: () => null,
|
|
1079
|
+
} },
|
|
1080
|
+
react_2.default.createElement(tambo_thread_provider_1.TamboThreadProvider, { streaming: false }, children))));
|
|
1081
|
+
const mockToolCallResponse = {
|
|
1082
|
+
responseMessageDto: {
|
|
1083
|
+
id: "tool-call-1",
|
|
1084
|
+
content: [{ type: "text", text: "Tool call" }],
|
|
1085
|
+
role: "tool",
|
|
1086
|
+
threadId: "test-thread-1",
|
|
1087
|
+
toolCallRequest: {
|
|
1088
|
+
toolName: "no-transform-tool",
|
|
1089
|
+
parameters: [{ parameterName: "input", parameterValue: "test" }],
|
|
1090
|
+
},
|
|
1091
|
+
componentState: {},
|
|
1092
|
+
createdAt: new Date().toISOString(),
|
|
1093
|
+
},
|
|
1094
|
+
generationStage: generate_component_response_1.GenerationStage.COMPLETE,
|
|
1095
|
+
mcpAccessToken: "test-mcp-access-token",
|
|
1096
|
+
};
|
|
1097
|
+
jest
|
|
1098
|
+
.mocked(mockThreadsApi.advanceByID)
|
|
1099
|
+
.mockResolvedValueOnce(mockToolCallResponse)
|
|
1100
|
+
.mockResolvedValueOnce({
|
|
1101
|
+
responseMessageDto: {
|
|
1102
|
+
id: "final-response",
|
|
1103
|
+
content: [{ type: "text", text: "Final response" }],
|
|
1104
|
+
role: "assistant",
|
|
1105
|
+
threadId: "test-thread-1",
|
|
1106
|
+
componentState: {},
|
|
1107
|
+
createdAt: new Date().toISOString(),
|
|
1108
|
+
},
|
|
1109
|
+
generationStage: generate_component_response_1.GenerationStage.COMPLETE,
|
|
1110
|
+
mcpAccessToken: "test-mcp-access-token",
|
|
1111
|
+
});
|
|
1112
|
+
const { result } = (0, react_1.renderHook)(() => (0, tambo_thread_provider_1.useTamboThread)(), {
|
|
1113
|
+
wrapper: wrapperWithoutTransform,
|
|
1114
|
+
});
|
|
1115
|
+
await (0, react_1.act)(async () => {
|
|
1116
|
+
await result.current.sendThreadMessage("Use tool without transform", {
|
|
1117
|
+
threadId: "test-thread-1",
|
|
1118
|
+
streamResponse: false,
|
|
1119
|
+
});
|
|
1120
|
+
});
|
|
1121
|
+
// Verify the tool was called
|
|
1122
|
+
expect(toolWithoutTransform[0]?.associatedTools?.[0]?.tool).toHaveBeenCalledWith("test");
|
|
1123
|
+
// Verify the second advance call used stringified content
|
|
1124
|
+
expect(mockThreadsApi.advanceByID).toHaveBeenLastCalledWith("test-thread-1", expect.objectContaining({
|
|
1125
|
+
messageToAppend: expect.objectContaining({
|
|
1126
|
+
content: [
|
|
1127
|
+
{
|
|
1128
|
+
type: "text",
|
|
1129
|
+
text: '{"complex":"data","nested":{"value":42}}',
|
|
1130
|
+
},
|
|
1131
|
+
],
|
|
1132
|
+
role: "tool",
|
|
1133
|
+
}),
|
|
1134
|
+
}));
|
|
1135
|
+
});
|
|
1136
|
+
it("should always return text for error responses even with transformToContent", async () => {
|
|
1137
|
+
const mockTransformToContent = jest.fn().mockReturnValue([
|
|
1138
|
+
{
|
|
1139
|
+
type: "image_url",
|
|
1140
|
+
image_url: { url: "https://example.com/error.png" },
|
|
1141
|
+
},
|
|
1142
|
+
]);
|
|
1143
|
+
const toolWithTransform = [
|
|
1144
|
+
{
|
|
1145
|
+
name: "TestComponent",
|
|
1146
|
+
component: () => react_2.default.createElement("div", null, "Test"),
|
|
1147
|
+
description: "Test",
|
|
1148
|
+
propsSchema: zod_1.z.object({ test: zod_1.z.string() }),
|
|
1149
|
+
associatedTools: [
|
|
1150
|
+
{
|
|
1151
|
+
name: "error-tool",
|
|
1152
|
+
tool: jest
|
|
1153
|
+
.fn()
|
|
1154
|
+
.mockRejectedValue(new Error("Tool execution failed")),
|
|
1155
|
+
description: "Tool that errors",
|
|
1156
|
+
toolSchema: zod_1.z.function().args(zod_1.z.string()).returns(zod_1.z.string()),
|
|
1157
|
+
transformToContent: mockTransformToContent,
|
|
1158
|
+
},
|
|
1159
|
+
],
|
|
1160
|
+
},
|
|
1161
|
+
];
|
|
1162
|
+
const wrapperWithErrorTool = ({ children, }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { components: toolWithTransform },
|
|
1163
|
+
react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, { contextHelpers: {
|
|
1164
|
+
currentTimeContextHelper: () => null,
|
|
1165
|
+
currentPageContextHelper: () => null,
|
|
1166
|
+
} },
|
|
1167
|
+
react_2.default.createElement(tambo_thread_provider_1.TamboThreadProvider, { streaming: false }, children))));
|
|
1168
|
+
const mockToolCallResponse = {
|
|
1169
|
+
responseMessageDto: {
|
|
1170
|
+
id: "tool-call-1",
|
|
1171
|
+
content: [{ type: "text", text: "Tool call" }],
|
|
1172
|
+
role: "tool",
|
|
1173
|
+
threadId: "test-thread-1",
|
|
1174
|
+
toolCallRequest: {
|
|
1175
|
+
toolName: "error-tool",
|
|
1176
|
+
parameters: [{ parameterName: "input", parameterValue: "test" }],
|
|
1177
|
+
},
|
|
1178
|
+
componentState: {},
|
|
1179
|
+
createdAt: new Date().toISOString(),
|
|
1180
|
+
},
|
|
1181
|
+
generationStage: generate_component_response_1.GenerationStage.COMPLETE,
|
|
1182
|
+
mcpAccessToken: "test-mcp-access-token",
|
|
1183
|
+
};
|
|
1184
|
+
jest
|
|
1185
|
+
.mocked(mockThreadsApi.advanceByID)
|
|
1186
|
+
.mockResolvedValueOnce(mockToolCallResponse)
|
|
1187
|
+
.mockResolvedValueOnce({
|
|
1188
|
+
responseMessageDto: {
|
|
1189
|
+
id: "final-response",
|
|
1190
|
+
content: [{ type: "text", text: "Final response" }],
|
|
1191
|
+
role: "assistant",
|
|
1192
|
+
threadId: "test-thread-1",
|
|
1193
|
+
componentState: {},
|
|
1194
|
+
createdAt: new Date().toISOString(),
|
|
1195
|
+
},
|
|
1196
|
+
generationStage: generate_component_response_1.GenerationStage.COMPLETE,
|
|
1197
|
+
mcpAccessToken: "test-mcp-access-token",
|
|
1198
|
+
});
|
|
1199
|
+
const { result } = (0, react_1.renderHook)(() => (0, tambo_thread_provider_1.useTamboThread)(), {
|
|
1200
|
+
wrapper: wrapperWithErrorTool,
|
|
1201
|
+
});
|
|
1202
|
+
await (0, react_1.act)(async () => {
|
|
1203
|
+
await result.current.sendThreadMessage("Use error tool", {
|
|
1204
|
+
threadId: "test-thread-1",
|
|
1205
|
+
streamResponse: false,
|
|
1206
|
+
});
|
|
1207
|
+
});
|
|
1208
|
+
// Verify the tool was called
|
|
1209
|
+
expect(toolWithTransform[0]?.associatedTools?.[0]?.tool).toHaveBeenCalledWith("test");
|
|
1210
|
+
// Verify transformToContent was NOT called for error responses
|
|
1211
|
+
expect(mockTransformToContent).not.toHaveBeenCalled();
|
|
1212
|
+
// Verify the second advance call used text content with the error message
|
|
1213
|
+
expect(mockThreadsApi.advanceByID).toHaveBeenLastCalledWith("test-thread-1", expect.objectContaining({
|
|
1214
|
+
messageToAppend: expect.objectContaining({
|
|
1215
|
+
content: [
|
|
1216
|
+
expect.objectContaining({
|
|
1217
|
+
type: "text",
|
|
1218
|
+
// Error message should be in text format
|
|
1219
|
+
}),
|
|
1220
|
+
],
|
|
1221
|
+
role: "tool",
|
|
1222
|
+
}),
|
|
1223
|
+
}));
|
|
1224
|
+
});
|
|
1225
|
+
});
|
|
852
1226
|
});
|
|
853
1227
|
//# sourceMappingURL=tambo-thread-provider.test.js.map
|