@tambo-ai/react 0.48.0 → 0.49.0

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.
Files changed (41) hide show
  1. package/dist/providers/__tests__/tambo-registry-provider.test.d.ts +2 -0
  2. package/dist/providers/__tests__/tambo-registry-provider.test.d.ts.map +1 -0
  3. package/dist/providers/__tests__/tambo-registry-provider.test.js +224 -0
  4. package/dist/providers/__tests__/tambo-registry-provider.test.js.map +1 -0
  5. package/dist/providers/__tests__/tambo-thread-provider.test.js +99 -0
  6. package/dist/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
  7. package/dist/providers/tambo-provider.d.ts.map +1 -1
  8. package/dist/providers/tambo-provider.js +2 -2
  9. package/dist/providers/tambo-provider.js.map +1 -1
  10. package/dist/providers/tambo-registry-provider.d.ts +12 -0
  11. package/dist/providers/tambo-registry-provider.d.ts.map +1 -1
  12. package/dist/providers/tambo-registry-provider.js +3 -1
  13. package/dist/providers/tambo-registry-provider.js.map +1 -1
  14. package/dist/providers/tambo-thread-provider.d.ts.map +1 -1
  15. package/dist/providers/tambo-thread-provider.js +5 -3
  16. package/dist/providers/tambo-thread-provider.js.map +1 -1
  17. package/dist/util/tool-caller.d.ts +1 -1
  18. package/dist/util/tool-caller.d.ts.map +1 -1
  19. package/dist/util/tool-caller.js +11 -2
  20. package/dist/util/tool-caller.js.map +1 -1
  21. package/esm/providers/__tests__/tambo-registry-provider.test.d.ts +2 -0
  22. package/esm/providers/__tests__/tambo-registry-provider.test.d.ts.map +1 -0
  23. package/esm/providers/__tests__/tambo-registry-provider.test.js +219 -0
  24. package/esm/providers/__tests__/tambo-registry-provider.test.js.map +1 -0
  25. package/esm/providers/__tests__/tambo-thread-provider.test.js +99 -0
  26. package/esm/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
  27. package/esm/providers/tambo-provider.d.ts.map +1 -1
  28. package/esm/providers/tambo-provider.js +2 -2
  29. package/esm/providers/tambo-provider.js.map +1 -1
  30. package/esm/providers/tambo-registry-provider.d.ts +12 -0
  31. package/esm/providers/tambo-registry-provider.d.ts.map +1 -1
  32. package/esm/providers/tambo-registry-provider.js +3 -1
  33. package/esm/providers/tambo-registry-provider.js.map +1 -1
  34. package/esm/providers/tambo-thread-provider.d.ts.map +1 -1
  35. package/esm/providers/tambo-thread-provider.js +5 -3
  36. package/esm/providers/tambo-thread-provider.js.map +1 -1
  37. package/esm/util/tool-caller.d.ts +1 -1
  38. package/esm/util/tool-caller.d.ts.map +1 -1
  39. package/esm/util/tool-caller.js +11 -2
  40. package/esm/util/tool-caller.js.map +1 -1
  41. package/package.json +2 -2
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=tambo-registry-provider.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tambo-registry-provider.test.d.ts","sourceRoot":"","sources":["../../../src/providers/__tests__/tambo-registry-provider.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const react_1 = require("@testing-library/react");
7
+ const react_2 = __importDefault(require("react"));
8
+ const zod_1 = require("zod");
9
+ const tambo_registry_provider_1 = require("../tambo-registry-provider");
10
+ // Shared tool registry for all tests
11
+ const createMockTools = () => [
12
+ {
13
+ name: "test-tool-1",
14
+ description: "First test tool",
15
+ tool: jest.fn().mockResolvedValue("test-tool-1-result"),
16
+ toolSchema: zod_1.z
17
+ .function()
18
+ .args(zod_1.z.string().describe("input parameter"))
19
+ .returns(zod_1.z.string()),
20
+ },
21
+ {
22
+ name: "test-tool-2",
23
+ description: "Second test tool",
24
+ tool: jest.fn().mockResolvedValue("test-tool-2-result"),
25
+ toolSchema: zod_1.z
26
+ .function()
27
+ .args(zod_1.z.number().describe("number parameter"))
28
+ .returns(zod_1.z.string()),
29
+ },
30
+ ];
31
+ const createMockComponents = (tools) => [
32
+ {
33
+ name: "TestComponent",
34
+ component: () => react_2.default.createElement("div", null, "TestComponent"),
35
+ description: "Test component",
36
+ propsSchema: zod_1.z.object({
37
+ test: zod_1.z.string(),
38
+ }),
39
+ associatedTools: tools,
40
+ },
41
+ ];
42
+ describe("TamboRegistryProvider", () => {
43
+ const mockTools = createMockTools();
44
+ const mockComponents = createMockComponents(mockTools);
45
+ beforeEach(() => {
46
+ jest.clearAllMocks();
47
+ });
48
+ describe("Component and tool registration", () => {
49
+ it("should register components and tools correctly", () => {
50
+ const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { components: mockComponents }, children));
51
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
52
+ expect(result.current.componentList).toHaveProperty("TestComponent");
53
+ expect(result.current.toolRegistry).toHaveProperty("test-tool-1");
54
+ expect(result.current.toolRegistry).toHaveProperty("test-tool-2");
55
+ expect(result.current.componentToolAssociations).toHaveProperty("TestComponent");
56
+ expect(result.current.componentToolAssociations.TestComponent).toEqual([
57
+ "test-tool-1",
58
+ "test-tool-2",
59
+ ]);
60
+ });
61
+ it("should provide onCallUnregisteredTool in context", () => {
62
+ const mockonCallUnregisteredTool = jest
63
+ .fn()
64
+ .mockResolvedValue("test-result");
65
+ const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { components: mockComponents, onCallUnregisteredTool: mockonCallUnregisteredTool }, children));
66
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
67
+ // The onCallUnregisteredTool should be available in the context
68
+ expect(result.current.onCallUnregisteredTool).toBeDefined();
69
+ expect(typeof result.current.onCallUnregisteredTool).toBe("function");
70
+ });
71
+ it("should handle tool registration and association", () => {
72
+ const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null, children));
73
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
74
+ // Register a new tool
75
+ (0, react_1.act)(() => {
76
+ result.current.registerTool(mockTools[0]);
77
+ });
78
+ expect(result.current.toolRegistry).toHaveProperty("test-tool-1");
79
+ // Register a new component
80
+ (0, react_1.act)(() => {
81
+ result.current.registerComponent(mockComponents[0]);
82
+ });
83
+ expect(result.current.componentList).toHaveProperty("TestComponent");
84
+ expect(result.current.componentToolAssociations).toHaveProperty("TestComponent");
85
+ });
86
+ it("should handle multiple tool registration", () => {
87
+ const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null, children));
88
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
89
+ // Register multiple tools
90
+ (0, react_1.act)(() => {
91
+ result.current.registerTools(mockTools);
92
+ });
93
+ expect(result.current.toolRegistry).toHaveProperty("test-tool-1");
94
+ expect(result.current.toolRegistry).toHaveProperty("test-tool-2");
95
+ expect(Object.keys(result.current.toolRegistry)).toHaveLength(2);
96
+ });
97
+ it("should handle tool association with components", () => {
98
+ const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { components: mockComponents }, children));
99
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
100
+ // Add a new tool association
101
+ (0, react_1.act)(() => {
102
+ const newTool = {
103
+ name: "new-tool",
104
+ description: "New tool",
105
+ tool: jest.fn().mockResolvedValue("new-tool-result"),
106
+ toolSchema: zod_1.z
107
+ .function()
108
+ .args(zod_1.z.string().describe("input"))
109
+ .returns(zod_1.z.string()),
110
+ };
111
+ result.current.addToolAssociation("TestComponent", newTool);
112
+ });
113
+ expect(result.current.componentToolAssociations.TestComponent).toContain("new-tool");
114
+ });
115
+ it("should throw error when adding tool association to non-existent component", () => {
116
+ const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null, children));
117
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
118
+ const newTool = {
119
+ name: "new-tool",
120
+ description: "New tool",
121
+ tool: jest.fn().mockResolvedValue("new-tool-result"),
122
+ toolSchema: zod_1.z
123
+ .function()
124
+ .args(zod_1.z.string().describe("input"))
125
+ .returns(zod_1.z.string()),
126
+ };
127
+ expect(() => {
128
+ (0, react_1.act)(() => {
129
+ result.current.addToolAssociation("NonExistentComponent", newTool);
130
+ });
131
+ }).toThrow("Component NonExistentComponent not found in registry");
132
+ });
133
+ it("should validate tool schemas and throw error for invalid schemas", () => {
134
+ const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null, children));
135
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
136
+ const invalidTool = {
137
+ name: "invalid-tool",
138
+ description: "Invalid tool",
139
+ tool: jest.fn().mockResolvedValue("result"),
140
+ toolSchema: zod_1.z.record(zod_1.z.string()), // This should cause validation to fail
141
+ };
142
+ // This should throw during registration due to invalid schema
143
+ expect(() => {
144
+ (0, react_1.act)(() => {
145
+ result.current.registerTool(invalidTool);
146
+ });
147
+ }).toThrow('z.record() is not supported in toolSchema of tool "invalid-tool"');
148
+ });
149
+ it("should validate component schemas and throw error for invalid schemas", () => {
150
+ const invalidComponent = {
151
+ name: "InvalidComponent",
152
+ component: () => react_2.default.createElement("div", null, "Invalid"),
153
+ description: "Invalid component",
154
+ propsSchema: zod_1.z.record(zod_1.z.string()), // This should cause validation to fail
155
+ };
156
+ const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null, children));
157
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
158
+ // This should throw during registration due to invalid schema
159
+ expect(() => {
160
+ (0, react_1.act)(() => {
161
+ result.current.registerComponent(invalidComponent);
162
+ });
163
+ }).toThrow('z.record() is not supported in propsSchema of component "InvalidComponent"');
164
+ });
165
+ it("should throw error when component has both propsSchema and propsDefinition", () => {
166
+ const invalidComponent = {
167
+ name: "InvalidComponent",
168
+ component: () => react_2.default.createElement("div", null, "Invalid"),
169
+ description: "Invalid component",
170
+ propsSchema: zod_1.z.object({ test: zod_1.z.string() }),
171
+ propsDefinition: { test: "string" }, // Both defined - should throw
172
+ };
173
+ const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null, children));
174
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
175
+ expect(() => {
176
+ (0, react_1.act)(() => {
177
+ result.current.registerComponent(invalidComponent);
178
+ });
179
+ }).toThrow("Component InvalidComponent cannot have both propsSchema and propsDefinition defined");
180
+ });
181
+ it("should throw error when component has neither propsSchema nor propsDefinition", () => {
182
+ const invalidComponent = {
183
+ name: "InvalidComponent",
184
+ component: () => react_2.default.createElement("div", null, "Invalid"),
185
+ description: "Invalid component",
186
+ // Neither propsSchema nor propsDefinition defined - should throw
187
+ };
188
+ const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null, children));
189
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
190
+ expect(() => {
191
+ (0, react_1.act)(() => {
192
+ result.current.registerComponent(invalidComponent);
193
+ });
194
+ }).toThrow("Component InvalidComponent must have either propsSchema (recommended) or propsDefinition defined");
195
+ });
196
+ });
197
+ describe("Tool call handling", () => {
198
+ it("should provide onCallUnregisteredTool callback for handling unknown tools", async () => {
199
+ const mockonCallUnregisteredTool = jest
200
+ .fn()
201
+ .mockResolvedValue("unknown-tool-result");
202
+ const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { components: mockComponents, onCallUnregisteredTool: mockonCallUnregisteredTool }, children));
203
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
204
+ // Verify the callback is available
205
+ expect(result.current.onCallUnregisteredTool).toBeDefined();
206
+ expect(typeof result.current.onCallUnregisteredTool).toBe("function");
207
+ // Simulate calling the unknown tool handler
208
+ const toolName = "unknown-tool";
209
+ const args = [{ parameterName: "input", parameterValue: "test-input" }];
210
+ await (0, react_1.act)(async () => {
211
+ if (result.current.onCallUnregisteredTool) {
212
+ await result.current.onCallUnregisteredTool(toolName, args);
213
+ }
214
+ });
215
+ expect(mockonCallUnregisteredTool).toHaveBeenCalledWith(toolName, args);
216
+ });
217
+ it("should handle onCallUnregisteredTool being undefined", () => {
218
+ const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { components: mockComponents }, children));
219
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
220
+ expect(result.current.onCallUnregisteredTool).toBeUndefined();
221
+ });
222
+ });
223
+ });
224
+ //# sourceMappingURL=tambo-registry-provider.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tambo-registry-provider.test.js","sourceRoot":"","sources":["../../../src/providers/__tests__/tambo-registry-provider.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import { act, renderHook } from \"@testing-library/react\";\nimport React from \"react\";\nimport { z } from \"zod\";\nimport { TamboComponent, TamboTool } from \"../../model/component-metadata\";\nimport {\n TamboRegistryProvider,\n useTamboRegistry,\n} from \"../tambo-registry-provider\";\n\n// Shared tool registry for all tests\nconst createMockTools = (): TamboTool[] => [\n {\n name: \"test-tool-1\",\n description: \"First test tool\",\n tool: jest.fn().mockResolvedValue(\"test-tool-1-result\"),\n toolSchema: z\n .function()\n .args(z.string().describe(\"input parameter\"))\n .returns(z.string()),\n },\n {\n name: \"test-tool-2\",\n description: \"Second test tool\",\n tool: jest.fn().mockResolvedValue(\"test-tool-2-result\"),\n toolSchema: z\n .function()\n .args(z.number().describe(\"number parameter\"))\n .returns(z.string()),\n },\n];\n\nconst createMockComponents = (tools: TamboTool[]): TamboComponent[] => [\n {\n name: \"TestComponent\",\n component: () => <div>TestComponent</div>,\n description: \"Test component\",\n propsSchema: z.object({\n test: z.string(),\n }),\n associatedTools: tools,\n },\n];\n\ndescribe(\"TamboRegistryProvider\", () => {\n const mockTools = createMockTools();\n const mockComponents = createMockComponents(mockTools);\n\n beforeEach(() => {\n jest.clearAllMocks();\n });\n\n describe(\"Component and tool registration\", () => {\n it(\"should register components and tools correctly\", () => {\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboRegistryProvider components={mockComponents}>\n {children}\n </TamboRegistryProvider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n expect(result.current.componentList).toHaveProperty(\"TestComponent\");\n expect(result.current.toolRegistry).toHaveProperty(\"test-tool-1\");\n expect(result.current.toolRegistry).toHaveProperty(\"test-tool-2\");\n expect(result.current.componentToolAssociations).toHaveProperty(\n \"TestComponent\",\n );\n expect(result.current.componentToolAssociations.TestComponent).toEqual([\n \"test-tool-1\",\n \"test-tool-2\",\n ]);\n });\n\n it(\"should provide onCallUnregisteredTool in context\", () => {\n const mockonCallUnregisteredTool = jest\n .fn()\n .mockResolvedValue(\"test-result\");\n\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboRegistryProvider\n components={mockComponents}\n onCallUnregisteredTool={mockonCallUnregisteredTool}\n >\n {children}\n </TamboRegistryProvider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n // The onCallUnregisteredTool should be available in the context\n expect(result.current.onCallUnregisteredTool).toBeDefined();\n expect(typeof result.current.onCallUnregisteredTool).toBe(\"function\");\n });\n\n it(\"should handle tool registration and association\", () => {\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboRegistryProvider>{children}</TamboRegistryProvider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n // Register a new tool\n act(() => {\n result.current.registerTool(mockTools[0]);\n });\n\n expect(result.current.toolRegistry).toHaveProperty(\"test-tool-1\");\n\n // Register a new component\n act(() => {\n result.current.registerComponent(mockComponents[0]);\n });\n\n expect(result.current.componentList).toHaveProperty(\"TestComponent\");\n expect(result.current.componentToolAssociations).toHaveProperty(\n \"TestComponent\",\n );\n });\n\n it(\"should handle multiple tool registration\", () => {\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboRegistryProvider>{children}</TamboRegistryProvider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n // Register multiple tools\n act(() => {\n result.current.registerTools(mockTools);\n });\n\n expect(result.current.toolRegistry).toHaveProperty(\"test-tool-1\");\n expect(result.current.toolRegistry).toHaveProperty(\"test-tool-2\");\n expect(Object.keys(result.current.toolRegistry)).toHaveLength(2);\n });\n\n it(\"should handle tool association with components\", () => {\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboRegistryProvider components={mockComponents}>\n {children}\n </TamboRegistryProvider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n // Add a new tool association\n act(() => {\n const newTool: TamboTool = {\n name: \"new-tool\",\n description: \"New tool\",\n tool: jest.fn().mockResolvedValue(\"new-tool-result\"),\n toolSchema: z\n .function()\n .args(z.string().describe(\"input\"))\n .returns(z.string()),\n };\n result.current.addToolAssociation(\"TestComponent\", newTool);\n });\n\n expect(result.current.componentToolAssociations.TestComponent).toContain(\n \"new-tool\",\n );\n });\n\n it(\"should throw error when adding tool association to non-existent component\", () => {\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboRegistryProvider>{children}</TamboRegistryProvider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n const newTool: TamboTool = {\n name: \"new-tool\",\n description: \"New tool\",\n tool: jest.fn().mockResolvedValue(\"new-tool-result\"),\n toolSchema: z\n .function()\n .args(z.string().describe(\"input\"))\n .returns(z.string()),\n };\n\n expect(() => {\n act(() => {\n result.current.addToolAssociation(\"NonExistentComponent\", newTool);\n });\n }).toThrow(\"Component NonExistentComponent not found in registry\");\n });\n\n it(\"should validate tool schemas and throw error for invalid schemas\", () => {\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboRegistryProvider>{children}</TamboRegistryProvider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n const invalidTool: TamboTool = {\n name: \"invalid-tool\",\n description: \"Invalid tool\",\n tool: jest.fn().mockResolvedValue(\"result\"),\n toolSchema: z.record(z.string()), // This should cause validation to fail\n };\n\n // This should throw during registration due to invalid schema\n expect(() => {\n act(() => {\n result.current.registerTool(invalidTool);\n });\n }).toThrow(\n 'z.record() is not supported in toolSchema of tool \"invalid-tool\"',\n );\n });\n\n it(\"should validate component schemas and throw error for invalid schemas\", () => {\n const invalidComponent: TamboComponent = {\n name: \"InvalidComponent\",\n component: () => <div>Invalid</div>,\n description: \"Invalid component\",\n propsSchema: z.record(z.string()), // This should cause validation to fail\n };\n\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboRegistryProvider>{children}</TamboRegistryProvider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n // This should throw during registration due to invalid schema\n expect(() => {\n act(() => {\n result.current.registerComponent(invalidComponent);\n });\n }).toThrow(\n 'z.record() is not supported in propsSchema of component \"InvalidComponent\"',\n );\n });\n\n it(\"should throw error when component has both propsSchema and propsDefinition\", () => {\n const invalidComponent: TamboComponent = {\n name: \"InvalidComponent\",\n component: () => <div>Invalid</div>,\n description: \"Invalid component\",\n propsSchema: z.object({ test: z.string() }),\n propsDefinition: { test: \"string\" }, // Both defined - should throw\n };\n\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboRegistryProvider>{children}</TamboRegistryProvider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n expect(() => {\n act(() => {\n result.current.registerComponent(invalidComponent);\n });\n }).toThrow(\n \"Component InvalidComponent cannot have both propsSchema and propsDefinition defined\",\n );\n });\n\n it(\"should throw error when component has neither propsSchema nor propsDefinition\", () => {\n const invalidComponent: TamboComponent = {\n name: \"InvalidComponent\",\n component: () => <div>Invalid</div>,\n description: \"Invalid component\",\n // Neither propsSchema nor propsDefinition defined - should throw\n };\n\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboRegistryProvider>{children}</TamboRegistryProvider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n expect(() => {\n act(() => {\n result.current.registerComponent(invalidComponent);\n });\n }).toThrow(\n \"Component InvalidComponent must have either propsSchema (recommended) or propsDefinition defined\",\n );\n });\n });\n\n describe(\"Tool call handling\", () => {\n it(\"should provide onCallUnregisteredTool callback for handling unknown tools\", async () => {\n const mockonCallUnregisteredTool = jest\n .fn()\n .mockResolvedValue(\"unknown-tool-result\");\n\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboRegistryProvider\n components={mockComponents}\n onCallUnregisteredTool={mockonCallUnregisteredTool}\n >\n {children}\n </TamboRegistryProvider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n // Verify the callback is available\n expect(result.current.onCallUnregisteredTool).toBeDefined();\n expect(typeof result.current.onCallUnregisteredTool).toBe(\"function\");\n\n // Simulate calling the unknown tool handler\n const toolName = \"unknown-tool\";\n const args = [{ parameterName: \"input\", parameterValue: \"test-input\" }];\n\n await act(async () => {\n if (result.current.onCallUnregisteredTool) {\n await result.current.onCallUnregisteredTool(toolName, args);\n }\n });\n\n expect(mockonCallUnregisteredTool).toHaveBeenCalledWith(toolName, args);\n });\n\n it(\"should handle onCallUnregisteredTool being undefined\", () => {\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboRegistryProvider components={mockComponents}>\n {children}\n </TamboRegistryProvider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n expect(result.current.onCallUnregisteredTool).toBeUndefined();\n });\n });\n});\n"]}
@@ -304,6 +304,105 @@ describe("TamboThreadProvider", () => {
304
304
  expect(result.current.generationStage).toBe(generate_component_response_1.GenerationStage.COMPLETE);
305
305
  expect(mockRegistry[0]?.associatedTools?.[0]?.tool).toHaveBeenCalledWith("test");
306
306
  });
307
+ it("should handle unregistered tool calls with onCallUnregisteredTool", async () => {
308
+ const mockOnCallUnregisteredTool = jest
309
+ .fn()
310
+ .mockResolvedValue("unregistered-tool-result");
311
+ const wrapperWithUnregisteredTool = ({ children, }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { components: mockRegistry, onCallUnregisteredTool: mockOnCallUnregisteredTool },
312
+ react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, { contextHelpers: {
313
+ currentTimeContextHelper: () => null,
314
+ currentPageContextHelper: () => null,
315
+ } },
316
+ react_2.default.createElement(tambo_thread_provider_1.TamboThreadProvider, { streaming: false }, children))));
317
+ const mockUnregisteredToolCallResponse = {
318
+ responseMessageDto: {
319
+ id: "unregistered-tool-call-1",
320
+ content: [{ type: "text", text: "Unregistered tool response" }],
321
+ role: "tool",
322
+ threadId: "test-thread-1",
323
+ toolCallRequest: {
324
+ toolName: "unregistered-tool",
325
+ parameters: [
326
+ { parameterName: "input", parameterValue: "test-input" },
327
+ ],
328
+ },
329
+ componentState: {},
330
+ createdAt: new Date().toISOString(),
331
+ },
332
+ generationStage: generate_component_response_1.GenerationStage.COMPLETE,
333
+ mcpAccessToken: "test-mcp-access-token",
334
+ };
335
+ jest
336
+ .mocked(mockThreadsApi.advanceById)
337
+ .mockResolvedValueOnce(mockUnregisteredToolCallResponse)
338
+ .mockResolvedValueOnce({
339
+ responseMessageDto: {
340
+ id: "advance-response2",
341
+ content: [{ type: "text", text: "response 2" }],
342
+ role: "user",
343
+ threadId: "test-thread-1",
344
+ componentState: {},
345
+ createdAt: new Date().toISOString(),
346
+ },
347
+ generationStage: generate_component_response_1.GenerationStage.COMPLETE,
348
+ mcpAccessToken: "test-mcp-access-token",
349
+ });
350
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_thread_provider_1.useTamboThread)(), {
351
+ wrapper: wrapperWithUnregisteredTool,
352
+ });
353
+ await (0, react_1.act)(async () => {
354
+ await result.current.sendThreadMessage("Use unregistered tool", {
355
+ threadId: "test-thread-1",
356
+ streamResponse: false,
357
+ });
358
+ });
359
+ expect(result.current.generationStage).toBe(generate_component_response_1.GenerationStage.COMPLETE);
360
+ expect(mockOnCallUnregisteredTool).toHaveBeenCalledWith("unregistered-tool", [{ parameterName: "input", parameterValue: "test-input" }]);
361
+ });
362
+ it("should handle unregistered tool calls without onCallUnregisteredTool", async () => {
363
+ const mockUnregisteredToolCallResponse = {
364
+ responseMessageDto: {
365
+ id: "unregistered-tool-call-1",
366
+ content: [{ type: "text", text: "Unregistered tool response" }],
367
+ role: "tool",
368
+ threadId: "test-thread-1",
369
+ toolCallRequest: {
370
+ toolName: "unregistered-tool",
371
+ parameters: [
372
+ { parameterName: "input", parameterValue: "test-input" },
373
+ ],
374
+ },
375
+ componentState: {},
376
+ createdAt: new Date().toISOString(),
377
+ },
378
+ generationStage: generate_component_response_1.GenerationStage.COMPLETE,
379
+ mcpAccessToken: "test-mcp-access-token",
380
+ };
381
+ jest
382
+ .mocked(mockThreadsApi.advanceById)
383
+ .mockResolvedValueOnce(mockUnregisteredToolCallResponse)
384
+ .mockResolvedValueOnce({
385
+ responseMessageDto: {
386
+ id: "advance-response2",
387
+ content: [{ type: "text", text: "response 2" }],
388
+ role: "user",
389
+ threadId: "test-thread-1",
390
+ componentState: {},
391
+ createdAt: new Date().toISOString(),
392
+ },
393
+ generationStage: generate_component_response_1.GenerationStage.COMPLETE,
394
+ mcpAccessToken: "test-mcp-access-token",
395
+ });
396
+ const { result } = (0, react_1.renderHook)(() => (0, tambo_thread_provider_1.useTamboThread)(), { wrapper });
397
+ await (0, react_1.act)(async () => {
398
+ await result.current.sendThreadMessage("Use unregistered tool", {
399
+ threadId: "test-thread-1",
400
+ streamResponse: false,
401
+ });
402
+ });
403
+ expect(result.current.generationStage).toBe(generate_component_response_1.GenerationStage.COMPLETE);
404
+ // Should not throw an error, but the tool call should fail gracefully
405
+ });
307
406
  describe("streaming behavior", () => {
308
407
  it("should call advanceStream when streamResponse=true", async () => {
309
408
  // Use wrapper with streaming=true to show that explicit streamResponse=true works