@tambo-ai/react 0.58.0 → 0.59.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.
- package/dist/hooks/use-tambo-stream-status.js +1 -1
- package/dist/hooks/use-tambo-stream-status.js.map +1 -1
- package/dist/mcp/__tests__/mcp-client.test.js +0 -266
- package/dist/mcp/__tests__/mcp-client.test.js.map +1 -1
- package/dist/mcp/__tests__/tambo-mcp-provider.test.js +218 -12
- package/dist/mcp/__tests__/tambo-mcp-provider.test.js.map +1 -1
- package/dist/mcp/__tests__/use-mcp-servers.test.js +34 -9
- package/dist/mcp/__tests__/use-mcp-servers.test.js.map +1 -1
- package/dist/mcp/index.d.ts +3 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +4 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/mcp-client.d.ts +12 -79
- package/dist/mcp/mcp-client.d.ts.map +1 -1
- package/dist/mcp/mcp-client.js +22 -159
- package/dist/mcp/mcp-client.js.map +1 -1
- package/dist/mcp/mcp-hooks.d.ts +93 -0
- package/dist/mcp/mcp-hooks.d.ts.map +1 -0
- package/dist/mcp/mcp-hooks.js +56 -0
- package/dist/mcp/mcp-hooks.js.map +1 -0
- package/dist/mcp/tambo-mcp-provider.d.ts +30 -2
- package/dist/mcp/tambo-mcp-provider.d.ts.map +1 -1
- package/dist/mcp/tambo-mcp-provider.js +127 -17
- 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-initial-messages.test.js +3 -1
- package/dist/providers/__tests__/tambo-thread-provider-initial-messages.test.js.map +1 -1
- package/dist/providers/__tests__/tambo-thread-provider.test.js +395 -8
- package/dist/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
- package/dist/providers/tambo-mcp-token-provider.d.ts +34 -0
- package/dist/providers/tambo-mcp-token-provider.d.ts.map +1 -0
- package/dist/providers/tambo-mcp-token-provider.js +69 -0
- package/dist/providers/tambo-mcp-token-provider.js.map +1 -0
- package/dist/providers/tambo-provider.d.ts.map +1 -1
- package/dist/providers/tambo-provider.js +7 -5
- package/dist/providers/tambo-provider.js.map +1 -1
- package/dist/providers/tambo-thread-provider.d.ts.map +1 -1
- package/dist/providers/tambo-thread-provider.js +20 -8
- package/dist/providers/tambo-thread-provider.js.map +1 -1
- package/dist/testing/tools.d.ts +1 -1
- package/esm/hooks/use-tambo-stream-status.js +1 -1
- package/esm/hooks/use-tambo-stream-status.js.map +1 -1
- package/esm/mcp/__tests__/mcp-client.test.js +0 -266
- package/esm/mcp/__tests__/mcp-client.test.js.map +1 -1
- package/esm/mcp/__tests__/tambo-mcp-provider.test.js +218 -12
- package/esm/mcp/__tests__/tambo-mcp-provider.test.js.map +1 -1
- package/esm/mcp/__tests__/use-mcp-servers.test.js +34 -9
- package/esm/mcp/__tests__/use-mcp-servers.test.js.map +1 -1
- package/esm/mcp/index.d.ts +3 -1
- package/esm/mcp/index.d.ts.map +1 -1
- package/esm/mcp/index.js +1 -0
- package/esm/mcp/index.js.map +1 -1
- package/esm/mcp/mcp-client.d.ts +12 -79
- package/esm/mcp/mcp-client.d.ts.map +1 -1
- package/esm/mcp/mcp-client.js +22 -159
- package/esm/mcp/mcp-client.js.map +1 -1
- package/esm/mcp/mcp-hooks.d.ts +93 -0
- package/esm/mcp/mcp-hooks.d.ts.map +1 -0
- package/esm/mcp/mcp-hooks.js +52 -0
- package/esm/mcp/mcp-hooks.js.map +1 -0
- package/esm/mcp/tambo-mcp-provider.d.ts +30 -2
- package/esm/mcp/tambo-mcp-provider.d.ts.map +1 -1
- package/esm/mcp/tambo-mcp-provider.js +128 -18
- 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-initial-messages.test.js +3 -1
- package/esm/providers/__tests__/tambo-thread-provider-initial-messages.test.js.map +1 -1
- package/esm/providers/__tests__/tambo-thread-provider.test.js +395 -8
- package/esm/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
- package/esm/providers/tambo-mcp-token-provider.d.ts +34 -0
- package/esm/providers/tambo-mcp-token-provider.d.ts.map +1 -0
- package/esm/providers/tambo-mcp-token-provider.js +31 -0
- package/esm/providers/tambo-mcp-token-provider.js.map +1 -0
- package/esm/providers/tambo-provider.d.ts.map +1 -1
- package/esm/providers/tambo-provider.js +7 -5
- package/esm/providers/tambo-provider.js.map +1 -1
- package/esm/providers/tambo-thread-provider.d.ts.map +1 -1
- package/esm/providers/tambo-thread-provider.js +20 -8
- package/esm/providers/tambo-thread-provider.js.map +1 -1
- package/esm/testing/tools.d.ts +1 -1
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ import { GenerationStage, } from "../../model/generate-component-response";
|
|
|
6
6
|
import { serializeRegistry } from "../../testing/tools";
|
|
7
7
|
import { useTamboClient, useTamboQueryClient } from "../tambo-client-provider";
|
|
8
8
|
import { TamboContextHelpersProvider } from "../tambo-context-helpers-provider";
|
|
9
|
+
import { TamboMcpTokenProvider } from "../tambo-mcp-token-provider";
|
|
9
10
|
import { TamboRegistryProvider } from "../tambo-registry-provider";
|
|
10
11
|
import { TamboThreadProvider, useTamboThread } from "../tambo-thread-provider";
|
|
11
12
|
// Mock crypto.randomUUID
|
|
@@ -111,7 +112,8 @@ describe("TamboThreadProvider", () => {
|
|
|
111
112
|
currentTimeContextHelper: () => null,
|
|
112
113
|
currentPageContextHelper: () => null,
|
|
113
114
|
} },
|
|
114
|
-
React.createElement(
|
|
115
|
+
React.createElement(TamboMcpTokenProvider, null,
|
|
116
|
+
React.createElement(TamboThreadProvider, { streaming: false }, children)))));
|
|
115
117
|
beforeEach(() => {
|
|
116
118
|
jest.clearAllMocks();
|
|
117
119
|
// Setup mock query client
|
|
@@ -327,7 +329,8 @@ describe("TamboThreadProvider", () => {
|
|
|
327
329
|
currentTimeContextHelper: () => null,
|
|
328
330
|
currentPageContextHelper: () => null,
|
|
329
331
|
} },
|
|
330
|
-
React.createElement(
|
|
332
|
+
React.createElement(TamboMcpTokenProvider, null,
|
|
333
|
+
React.createElement(TamboThreadProvider, { streaming: false }, children)))));
|
|
331
334
|
const mockUnregisteredToolCallResponse = {
|
|
332
335
|
responseMessageDto: {
|
|
333
336
|
id: "unregistered-tool-call-1",
|
|
@@ -425,7 +428,8 @@ describe("TamboThreadProvider", () => {
|
|
|
425
428
|
currentTimeContextHelper: () => null,
|
|
426
429
|
currentPageContextHelper: () => null,
|
|
427
430
|
} },
|
|
428
|
-
React.createElement(
|
|
431
|
+
React.createElement(TamboMcpTokenProvider, null,
|
|
432
|
+
React.createElement(TamboThreadProvider, { streaming: true }, children)))));
|
|
429
433
|
const mockStreamResponse = {
|
|
430
434
|
responseMessageDto: {
|
|
431
435
|
id: "stream-response",
|
|
@@ -486,7 +490,8 @@ describe("TamboThreadProvider", () => {
|
|
|
486
490
|
currentTimeContextHelper: () => null,
|
|
487
491
|
currentPageContextHelper: () => null,
|
|
488
492
|
} },
|
|
489
|
-
React.createElement(
|
|
493
|
+
React.createElement(TamboMcpTokenProvider, null,
|
|
494
|
+
React.createElement(TamboThreadProvider, { streaming: true }, children)))));
|
|
490
495
|
const { result } = renderHook(() => useTamboThread(), {
|
|
491
496
|
wrapper: wrapperWithStreaming,
|
|
492
497
|
});
|
|
@@ -528,7 +533,8 @@ describe("TamboThreadProvider", () => {
|
|
|
528
533
|
currentTimeContextHelper: () => null,
|
|
529
534
|
currentPageContextHelper: () => null,
|
|
530
535
|
} },
|
|
531
|
-
React.createElement(
|
|
536
|
+
React.createElement(TamboMcpTokenProvider, null,
|
|
537
|
+
React.createElement(TamboThreadProvider, { streaming: false }, children)))));
|
|
532
538
|
const { result } = renderHook(() => useTamboThread(), {
|
|
533
539
|
wrapper: wrapperWithoutStreaming,
|
|
534
540
|
});
|
|
@@ -570,7 +576,8 @@ describe("TamboThreadProvider", () => {
|
|
|
570
576
|
currentTimeContextHelper: () => null,
|
|
571
577
|
currentPageContextHelper: () => null,
|
|
572
578
|
} },
|
|
573
|
-
React.createElement(
|
|
579
|
+
React.createElement(TamboMcpTokenProvider, null,
|
|
580
|
+
React.createElement(TamboThreadProvider, null, children)))));
|
|
574
581
|
const mockStreamResponse = {
|
|
575
582
|
responseMessageDto: {
|
|
576
583
|
id: "stream-response",
|
|
@@ -631,7 +638,8 @@ describe("TamboThreadProvider", () => {
|
|
|
631
638
|
currentTimeContextHelper: () => null,
|
|
632
639
|
currentPageContextHelper: () => null,
|
|
633
640
|
} },
|
|
634
|
-
React.createElement(
|
|
641
|
+
React.createElement(TamboMcpTokenProvider, null,
|
|
642
|
+
React.createElement(TamboThreadProvider, { streaming: true }, children)))));
|
|
635
643
|
const { result } = renderHook(() => useTamboThread(), {
|
|
636
644
|
wrapper: wrapperWithStreaming,
|
|
637
645
|
});
|
|
@@ -675,7 +683,8 @@ describe("TamboThreadProvider", () => {
|
|
|
675
683
|
currentTimeContextHelper: () => null,
|
|
676
684
|
currentPageContextHelper: () => null,
|
|
677
685
|
} },
|
|
678
|
-
React.createElement(
|
|
686
|
+
React.createElement(TamboMcpTokenProvider, null,
|
|
687
|
+
React.createElement(TamboThreadProvider, { streaming: false }, children)))));
|
|
679
688
|
const mockStreamResponse = {
|
|
680
689
|
responseMessageDto: {
|
|
681
690
|
id: "stream-response",
|
|
@@ -844,5 +853,383 @@ describe("TamboThreadProvider", () => {
|
|
|
844
853
|
expect(result.current.thread.id).toBe("existing-thread-123");
|
|
845
854
|
});
|
|
846
855
|
});
|
|
856
|
+
describe("transformToContent", () => {
|
|
857
|
+
it("should use custom transformToContent when provided (non-streaming)", async () => {
|
|
858
|
+
const mockTransformToContent = jest.fn().mockReturnValue([
|
|
859
|
+
{ type: "text", text: "Custom transformed content" },
|
|
860
|
+
{
|
|
861
|
+
type: "image_url",
|
|
862
|
+
image_url: { url: "https://example.com/image.png" },
|
|
863
|
+
},
|
|
864
|
+
]);
|
|
865
|
+
const customToolRegistry = [
|
|
866
|
+
{
|
|
867
|
+
name: "TestComponent",
|
|
868
|
+
component: () => React.createElement("div", null, "Test"),
|
|
869
|
+
description: "Test",
|
|
870
|
+
propsSchema: z.object({ test: z.string() }),
|
|
871
|
+
associatedTools: [
|
|
872
|
+
{
|
|
873
|
+
name: "custom-tool",
|
|
874
|
+
tool: jest.fn().mockResolvedValue({ data: "tool result" }),
|
|
875
|
+
description: "Tool with custom transform",
|
|
876
|
+
toolSchema: z
|
|
877
|
+
.function()
|
|
878
|
+
.args(z.string())
|
|
879
|
+
.returns(z.object({ data: z.string() })),
|
|
880
|
+
transformToContent: mockTransformToContent,
|
|
881
|
+
},
|
|
882
|
+
],
|
|
883
|
+
},
|
|
884
|
+
];
|
|
885
|
+
const wrapperWithCustomTool = ({ children, }) => (React.createElement(TamboRegistryProvider, { components: customToolRegistry },
|
|
886
|
+
React.createElement(TamboContextHelpersProvider, { contextHelpers: {
|
|
887
|
+
currentTimeContextHelper: () => null,
|
|
888
|
+
currentPageContextHelper: () => null,
|
|
889
|
+
} },
|
|
890
|
+
React.createElement(TamboMcpTokenProvider, null,
|
|
891
|
+
React.createElement(TamboThreadProvider, { streaming: false }, children)))));
|
|
892
|
+
const mockToolCallResponse = {
|
|
893
|
+
responseMessageDto: {
|
|
894
|
+
id: "tool-call-1",
|
|
895
|
+
content: [{ type: "text", text: "Tool response" }],
|
|
896
|
+
role: "tool",
|
|
897
|
+
threadId: "test-thread-1",
|
|
898
|
+
toolCallRequest: {
|
|
899
|
+
toolName: "custom-tool",
|
|
900
|
+
parameters: [{ parameterName: "input", parameterValue: "test" }],
|
|
901
|
+
},
|
|
902
|
+
componentState: {},
|
|
903
|
+
createdAt: new Date().toISOString(),
|
|
904
|
+
},
|
|
905
|
+
generationStage: GenerationStage.COMPLETE,
|
|
906
|
+
mcpAccessToken: "test-mcp-access-token",
|
|
907
|
+
};
|
|
908
|
+
jest
|
|
909
|
+
.mocked(mockThreadsApi.advanceByID)
|
|
910
|
+
.mockResolvedValueOnce(mockToolCallResponse)
|
|
911
|
+
.mockResolvedValueOnce({
|
|
912
|
+
responseMessageDto: {
|
|
913
|
+
id: "final-response",
|
|
914
|
+
content: [{ type: "text", text: "Final response" }],
|
|
915
|
+
role: "assistant",
|
|
916
|
+
threadId: "test-thread-1",
|
|
917
|
+
componentState: {},
|
|
918
|
+
createdAt: new Date().toISOString(),
|
|
919
|
+
},
|
|
920
|
+
generationStage: GenerationStage.COMPLETE,
|
|
921
|
+
mcpAccessToken: "test-mcp-access-token",
|
|
922
|
+
});
|
|
923
|
+
const { result } = renderHook(() => useTamboThread(), {
|
|
924
|
+
wrapper: wrapperWithCustomTool,
|
|
925
|
+
});
|
|
926
|
+
await act(async () => {
|
|
927
|
+
await result.current.sendThreadMessage("Use custom tool", {
|
|
928
|
+
threadId: "test-thread-1",
|
|
929
|
+
streamResponse: false,
|
|
930
|
+
});
|
|
931
|
+
});
|
|
932
|
+
// Verify the tool was called
|
|
933
|
+
expect(customToolRegistry[0]?.associatedTools?.[0]?.tool).toHaveBeenCalledWith("test");
|
|
934
|
+
// Verify transformToContent was called with the tool result
|
|
935
|
+
expect(mockTransformToContent).toHaveBeenCalledWith({
|
|
936
|
+
data: "tool result",
|
|
937
|
+
});
|
|
938
|
+
// Verify the second advance call included the transformed content
|
|
939
|
+
expect(mockThreadsApi.advanceByID).toHaveBeenCalledTimes(2);
|
|
940
|
+
expect(mockThreadsApi.advanceByID).toHaveBeenLastCalledWith("test-thread-1", expect.objectContaining({
|
|
941
|
+
messageToAppend: expect.objectContaining({
|
|
942
|
+
content: [
|
|
943
|
+
{ type: "text", text: "Custom transformed content" },
|
|
944
|
+
{
|
|
945
|
+
type: "image_url",
|
|
946
|
+
image_url: { url: "https://example.com/image.png" },
|
|
947
|
+
},
|
|
948
|
+
],
|
|
949
|
+
role: "tool",
|
|
950
|
+
}),
|
|
951
|
+
}));
|
|
952
|
+
});
|
|
953
|
+
it("should use custom async transformToContent when provided (streaming)", async () => {
|
|
954
|
+
const mockTransformToContent = jest
|
|
955
|
+
.fn()
|
|
956
|
+
.mockResolvedValue([
|
|
957
|
+
{ type: "text", text: "Async transformed content" },
|
|
958
|
+
]);
|
|
959
|
+
const customToolRegistry = [
|
|
960
|
+
{
|
|
961
|
+
name: "TestComponent",
|
|
962
|
+
component: () => React.createElement("div", null, "Test"),
|
|
963
|
+
description: "Test",
|
|
964
|
+
propsSchema: z.object({ test: z.string() }),
|
|
965
|
+
associatedTools: [
|
|
966
|
+
{
|
|
967
|
+
name: "async-tool",
|
|
968
|
+
tool: jest.fn().mockResolvedValue({ data: "async tool result" }),
|
|
969
|
+
description: "Tool with async transform",
|
|
970
|
+
toolSchema: z
|
|
971
|
+
.function()
|
|
972
|
+
.args(z.string())
|
|
973
|
+
.returns(z.object({ data: z.string() })),
|
|
974
|
+
transformToContent: mockTransformToContent,
|
|
975
|
+
},
|
|
976
|
+
],
|
|
977
|
+
},
|
|
978
|
+
];
|
|
979
|
+
const wrapperWithAsyncTool = ({ children, }) => (React.createElement(TamboRegistryProvider, { components: customToolRegistry },
|
|
980
|
+
React.createElement(TamboContextHelpersProvider, { contextHelpers: {
|
|
981
|
+
currentTimeContextHelper: () => null,
|
|
982
|
+
currentPageContextHelper: () => null,
|
|
983
|
+
} },
|
|
984
|
+
React.createElement(TamboMcpTokenProvider, null,
|
|
985
|
+
React.createElement(TamboThreadProvider, { streaming: true }, children)))));
|
|
986
|
+
const mockToolCallChunk = {
|
|
987
|
+
responseMessageDto: {
|
|
988
|
+
id: "tool-call-chunk",
|
|
989
|
+
content: [{ type: "text", text: "Tool call" }],
|
|
990
|
+
role: "tool",
|
|
991
|
+
threadId: "test-thread-1",
|
|
992
|
+
toolCallRequest: {
|
|
993
|
+
toolName: "async-tool",
|
|
994
|
+
parameters: [
|
|
995
|
+
{ parameterName: "input", parameterValue: "async-test" },
|
|
996
|
+
],
|
|
997
|
+
},
|
|
998
|
+
componentState: {},
|
|
999
|
+
createdAt: new Date().toISOString(),
|
|
1000
|
+
},
|
|
1001
|
+
generationStage: GenerationStage.COMPLETE,
|
|
1002
|
+
mcpAccessToken: "test-mcp-access-token",
|
|
1003
|
+
};
|
|
1004
|
+
const mockFinalChunk = {
|
|
1005
|
+
responseMessageDto: {
|
|
1006
|
+
id: "final-chunk",
|
|
1007
|
+
content: [{ type: "text", text: "Final streaming response" }],
|
|
1008
|
+
role: "assistant",
|
|
1009
|
+
threadId: "test-thread-1",
|
|
1010
|
+
componentState: {},
|
|
1011
|
+
createdAt: new Date().toISOString(),
|
|
1012
|
+
},
|
|
1013
|
+
generationStage: GenerationStage.COMPLETE,
|
|
1014
|
+
mcpAccessToken: "test-mcp-access-token",
|
|
1015
|
+
};
|
|
1016
|
+
const mockAsyncIterator = {
|
|
1017
|
+
[Symbol.asyncIterator]: async function* () {
|
|
1018
|
+
yield mockToolCallChunk;
|
|
1019
|
+
yield mockFinalChunk;
|
|
1020
|
+
},
|
|
1021
|
+
};
|
|
1022
|
+
jest
|
|
1023
|
+
.mocked(advanceStream)
|
|
1024
|
+
.mockResolvedValueOnce(mockAsyncIterator)
|
|
1025
|
+
.mockResolvedValueOnce({
|
|
1026
|
+
[Symbol.asyncIterator]: async function* () {
|
|
1027
|
+
yield mockFinalChunk;
|
|
1028
|
+
},
|
|
1029
|
+
});
|
|
1030
|
+
const { result } = renderHook(() => useTamboThread(), {
|
|
1031
|
+
wrapper: wrapperWithAsyncTool,
|
|
1032
|
+
});
|
|
1033
|
+
await act(async () => {
|
|
1034
|
+
await result.current.sendThreadMessage("Use async tool", {
|
|
1035
|
+
threadId: "test-thread-1",
|
|
1036
|
+
streamResponse: true,
|
|
1037
|
+
});
|
|
1038
|
+
});
|
|
1039
|
+
// Verify the tool was called
|
|
1040
|
+
expect(customToolRegistry[0]?.associatedTools?.[0]?.tool).toHaveBeenCalledWith("async-test");
|
|
1041
|
+
// Verify transformToContent was called
|
|
1042
|
+
expect(mockTransformToContent).toHaveBeenCalledWith({
|
|
1043
|
+
data: "async tool result",
|
|
1044
|
+
});
|
|
1045
|
+
// Verify advanceStream was called twice (initial request and tool response)
|
|
1046
|
+
expect(advanceStream).toHaveBeenCalledTimes(2);
|
|
1047
|
+
// Verify the second advanceStream call included the transformed content
|
|
1048
|
+
expect(advanceStream).toHaveBeenLastCalledWith(mockTamboAI, expect.objectContaining({
|
|
1049
|
+
messageToAppend: expect.objectContaining({
|
|
1050
|
+
content: [{ type: "text", text: "Async transformed content" }],
|
|
1051
|
+
role: "tool",
|
|
1052
|
+
}),
|
|
1053
|
+
}), "test-thread-1");
|
|
1054
|
+
});
|
|
1055
|
+
it("should fallback to stringified text when transformToContent is not provided", async () => {
|
|
1056
|
+
const toolWithoutTransform = [
|
|
1057
|
+
{
|
|
1058
|
+
name: "TestComponent",
|
|
1059
|
+
component: () => React.createElement("div", null, "Test"),
|
|
1060
|
+
description: "Test",
|
|
1061
|
+
propsSchema: z.object({ test: z.string() }),
|
|
1062
|
+
associatedTools: [
|
|
1063
|
+
{
|
|
1064
|
+
name: "no-transform-tool",
|
|
1065
|
+
tool: jest
|
|
1066
|
+
.fn()
|
|
1067
|
+
.mockResolvedValue({ complex: "data", nested: { value: 42 } }),
|
|
1068
|
+
description: "Tool without custom transform",
|
|
1069
|
+
toolSchema: z
|
|
1070
|
+
.function()
|
|
1071
|
+
.args(z.string())
|
|
1072
|
+
.returns(z.object({
|
|
1073
|
+
complex: z.string(),
|
|
1074
|
+
nested: z.object({ value: z.number() }),
|
|
1075
|
+
})),
|
|
1076
|
+
// No transformToContent provided
|
|
1077
|
+
},
|
|
1078
|
+
],
|
|
1079
|
+
},
|
|
1080
|
+
];
|
|
1081
|
+
const wrapperWithoutTransform = ({ children, }) => (React.createElement(TamboRegistryProvider, { components: toolWithoutTransform },
|
|
1082
|
+
React.createElement(TamboContextHelpersProvider, { contextHelpers: {
|
|
1083
|
+
currentTimeContextHelper: () => null,
|
|
1084
|
+
currentPageContextHelper: () => null,
|
|
1085
|
+
} },
|
|
1086
|
+
React.createElement(TamboMcpTokenProvider, null,
|
|
1087
|
+
React.createElement(TamboThreadProvider, { streaming: false }, children)))));
|
|
1088
|
+
const mockToolCallResponse = {
|
|
1089
|
+
responseMessageDto: {
|
|
1090
|
+
id: "tool-call-1",
|
|
1091
|
+
content: [{ type: "text", text: "Tool call" }],
|
|
1092
|
+
role: "tool",
|
|
1093
|
+
threadId: "test-thread-1",
|
|
1094
|
+
toolCallRequest: {
|
|
1095
|
+
toolName: "no-transform-tool",
|
|
1096
|
+
parameters: [{ parameterName: "input", parameterValue: "test" }],
|
|
1097
|
+
},
|
|
1098
|
+
componentState: {},
|
|
1099
|
+
createdAt: new Date().toISOString(),
|
|
1100
|
+
},
|
|
1101
|
+
generationStage: GenerationStage.COMPLETE,
|
|
1102
|
+
mcpAccessToken: "test-mcp-access-token",
|
|
1103
|
+
};
|
|
1104
|
+
jest
|
|
1105
|
+
.mocked(mockThreadsApi.advanceByID)
|
|
1106
|
+
.mockResolvedValueOnce(mockToolCallResponse)
|
|
1107
|
+
.mockResolvedValueOnce({
|
|
1108
|
+
responseMessageDto: {
|
|
1109
|
+
id: "final-response",
|
|
1110
|
+
content: [{ type: "text", text: "Final response" }],
|
|
1111
|
+
role: "assistant",
|
|
1112
|
+
threadId: "test-thread-1",
|
|
1113
|
+
componentState: {},
|
|
1114
|
+
createdAt: new Date().toISOString(),
|
|
1115
|
+
},
|
|
1116
|
+
generationStage: GenerationStage.COMPLETE,
|
|
1117
|
+
mcpAccessToken: "test-mcp-access-token",
|
|
1118
|
+
});
|
|
1119
|
+
const { result } = renderHook(() => useTamboThread(), {
|
|
1120
|
+
wrapper: wrapperWithoutTransform,
|
|
1121
|
+
});
|
|
1122
|
+
await act(async () => {
|
|
1123
|
+
await result.current.sendThreadMessage("Use tool without transform", {
|
|
1124
|
+
threadId: "test-thread-1",
|
|
1125
|
+
streamResponse: false,
|
|
1126
|
+
});
|
|
1127
|
+
});
|
|
1128
|
+
// Verify the tool was called
|
|
1129
|
+
expect(toolWithoutTransform[0]?.associatedTools?.[0]?.tool).toHaveBeenCalledWith("test");
|
|
1130
|
+
// Verify the second advance call used stringified content
|
|
1131
|
+
expect(mockThreadsApi.advanceByID).toHaveBeenLastCalledWith("test-thread-1", expect.objectContaining({
|
|
1132
|
+
messageToAppend: expect.objectContaining({
|
|
1133
|
+
content: [
|
|
1134
|
+
{
|
|
1135
|
+
type: "text",
|
|
1136
|
+
text: '{"complex":"data","nested":{"value":42}}',
|
|
1137
|
+
},
|
|
1138
|
+
],
|
|
1139
|
+
role: "tool",
|
|
1140
|
+
}),
|
|
1141
|
+
}));
|
|
1142
|
+
});
|
|
1143
|
+
it("should always return text for error responses even with transformToContent", async () => {
|
|
1144
|
+
const mockTransformToContent = jest.fn().mockReturnValue([
|
|
1145
|
+
{
|
|
1146
|
+
type: "image_url",
|
|
1147
|
+
image_url: { url: "https://example.com/error.png" },
|
|
1148
|
+
},
|
|
1149
|
+
]);
|
|
1150
|
+
const toolWithTransform = [
|
|
1151
|
+
{
|
|
1152
|
+
name: "TestComponent",
|
|
1153
|
+
component: () => React.createElement("div", null, "Test"),
|
|
1154
|
+
description: "Test",
|
|
1155
|
+
propsSchema: z.object({ test: z.string() }),
|
|
1156
|
+
associatedTools: [
|
|
1157
|
+
{
|
|
1158
|
+
name: "error-tool",
|
|
1159
|
+
tool: jest
|
|
1160
|
+
.fn()
|
|
1161
|
+
.mockRejectedValue(new Error("Tool execution failed")),
|
|
1162
|
+
description: "Tool that errors",
|
|
1163
|
+
toolSchema: z.function().args(z.string()).returns(z.string()),
|
|
1164
|
+
transformToContent: mockTransformToContent,
|
|
1165
|
+
},
|
|
1166
|
+
],
|
|
1167
|
+
},
|
|
1168
|
+
];
|
|
1169
|
+
const wrapperWithErrorTool = ({ children, }) => (React.createElement(TamboRegistryProvider, { components: toolWithTransform },
|
|
1170
|
+
React.createElement(TamboContextHelpersProvider, { contextHelpers: {
|
|
1171
|
+
currentTimeContextHelper: () => null,
|
|
1172
|
+
currentPageContextHelper: () => null,
|
|
1173
|
+
} },
|
|
1174
|
+
React.createElement(TamboMcpTokenProvider, null,
|
|
1175
|
+
React.createElement(TamboThreadProvider, { streaming: false }, children)))));
|
|
1176
|
+
const mockToolCallResponse = {
|
|
1177
|
+
responseMessageDto: {
|
|
1178
|
+
id: "tool-call-1",
|
|
1179
|
+
content: [{ type: "text", text: "Tool call" }],
|
|
1180
|
+
role: "tool",
|
|
1181
|
+
threadId: "test-thread-1",
|
|
1182
|
+
toolCallRequest: {
|
|
1183
|
+
toolName: "error-tool",
|
|
1184
|
+
parameters: [{ parameterName: "input", parameterValue: "test" }],
|
|
1185
|
+
},
|
|
1186
|
+
componentState: {},
|
|
1187
|
+
createdAt: new Date().toISOString(),
|
|
1188
|
+
},
|
|
1189
|
+
generationStage: GenerationStage.COMPLETE,
|
|
1190
|
+
mcpAccessToken: "test-mcp-access-token",
|
|
1191
|
+
};
|
|
1192
|
+
jest
|
|
1193
|
+
.mocked(mockThreadsApi.advanceByID)
|
|
1194
|
+
.mockResolvedValueOnce(mockToolCallResponse)
|
|
1195
|
+
.mockResolvedValueOnce({
|
|
1196
|
+
responseMessageDto: {
|
|
1197
|
+
id: "final-response",
|
|
1198
|
+
content: [{ type: "text", text: "Final response" }],
|
|
1199
|
+
role: "assistant",
|
|
1200
|
+
threadId: "test-thread-1",
|
|
1201
|
+
componentState: {},
|
|
1202
|
+
createdAt: new Date().toISOString(),
|
|
1203
|
+
},
|
|
1204
|
+
generationStage: GenerationStage.COMPLETE,
|
|
1205
|
+
mcpAccessToken: "test-mcp-access-token",
|
|
1206
|
+
});
|
|
1207
|
+
const { result } = renderHook(() => useTamboThread(), {
|
|
1208
|
+
wrapper: wrapperWithErrorTool,
|
|
1209
|
+
});
|
|
1210
|
+
await act(async () => {
|
|
1211
|
+
await result.current.sendThreadMessage("Use error tool", {
|
|
1212
|
+
threadId: "test-thread-1",
|
|
1213
|
+
streamResponse: false,
|
|
1214
|
+
});
|
|
1215
|
+
});
|
|
1216
|
+
// Verify the tool was called
|
|
1217
|
+
expect(toolWithTransform[0]?.associatedTools?.[0]?.tool).toHaveBeenCalledWith("test");
|
|
1218
|
+
// Verify transformToContent was NOT called for error responses
|
|
1219
|
+
expect(mockTransformToContent).not.toHaveBeenCalled();
|
|
1220
|
+
// Verify the second advance call used text content with the error message
|
|
1221
|
+
expect(mockThreadsApi.advanceByID).toHaveBeenLastCalledWith("test-thread-1", expect.objectContaining({
|
|
1222
|
+
messageToAppend: expect.objectContaining({
|
|
1223
|
+
content: [
|
|
1224
|
+
expect.objectContaining({
|
|
1225
|
+
type: "text",
|
|
1226
|
+
// Error message should be in text format
|
|
1227
|
+
}),
|
|
1228
|
+
],
|
|
1229
|
+
role: "tool",
|
|
1230
|
+
}),
|
|
1231
|
+
}));
|
|
1232
|
+
});
|
|
1233
|
+
});
|
|
847
1234
|
});
|
|
848
1235
|
//# sourceMappingURL=tambo-thread-provider.test.js.map
|