@tambo-ai/react 0.69.1 → 0.71.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/README.md +7 -7
- package/dist/hooks/use-tambo-threads.test.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.ts +4 -5
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +4 -5
- package/dist/mcp/index.js.map +1 -1
- package/dist/model/component-metadata.d.ts +88 -241
- package/dist/model/component-metadata.d.ts.map +1 -1
- package/dist/model/component-metadata.js.map +1 -1
- package/dist/model/mcp-server-info.d.ts +3 -3
- package/dist/model/mcp-server-info.js.map +1 -1
- package/dist/providers/hooks/use-tambo-session-token.test.js.map +1 -1
- package/dist/providers/tambo-component-provider.d.ts +2 -2
- package/dist/providers/tambo-component-provider.d.ts.map +1 -1
- package/dist/providers/tambo-component-provider.js.map +1 -1
- package/dist/providers/tambo-interactable-provider.d.ts +1 -1
- package/dist/providers/tambo-registry-provider.d.ts +4 -4
- package/dist/providers/tambo-registry-provider.d.ts.map +1 -1
- package/dist/providers/tambo-registry-provider.js +11 -8
- package/dist/providers/tambo-registry-provider.js.map +1 -1
- package/dist/providers/tambo-registry-provider.test.js +31 -0
- package/dist/providers/tambo-registry-provider.test.js.map +1 -1
- package/dist/providers/tambo-registry-schema-compat.test.js +42 -52
- package/dist/providers/tambo-registry-schema-compat.test.js.map +1 -1
- package/dist/providers/tambo-stubs.d.ts +2 -2
- package/dist/providers/tambo-stubs.d.ts.map +1 -1
- package/dist/providers/tambo-stubs.js.map +1 -1
- package/dist/providers/tambo-thread-provider-initial-messages.test.js.map +1 -1
- package/dist/providers/tambo-thread-provider.d.ts.map +1 -1
- package/dist/providers/tambo-thread-provider.js +107 -141
- package/dist/providers/tambo-thread-provider.js.map +1 -1
- package/dist/providers/tambo-thread-provider.test.js +274 -445
- package/dist/providers/tambo-thread-provider.test.js.map +1 -1
- package/dist/schema/index.d.ts +1 -2
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +1 -5
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/schema.d.ts +7 -24
- package/dist/schema/schema.d.ts.map +1 -1
- package/dist/schema/schema.js +34 -105
- package/dist/schema/schema.js.map +1 -1
- package/dist/schema/schema.test.js +26 -124
- package/dist/schema/schema.test.js.map +1 -1
- package/dist/testing/tools.d.ts +2 -12
- package/dist/testing/tools.d.ts.map +1 -1
- package/dist/testing/tools.js +1 -20
- package/dist/testing/tools.js.map +1 -1
- package/dist/testing/types.d.ts +2 -2
- package/dist/testing/types.d.ts.map +1 -1
- package/dist/testing/types.js.map +1 -1
- package/dist/util/registry-validators.d.ts +2 -2
- package/dist/util/registry-validators.d.ts.map +1 -1
- package/dist/util/registry-validators.js +37 -17
- package/dist/util/registry-validators.js.map +1 -1
- package/dist/util/registry-validators.test.js +64 -25
- package/dist/util/registry-validators.test.js.map +1 -1
- package/dist/util/registry.d.ts +4 -10
- package/dist/util/registry.d.ts.map +1 -1
- package/dist/util/registry.js +6 -22
- package/dist/util/registry.js.map +1 -1
- package/dist/util/registry.test.js +1 -47
- package/dist/util/registry.test.js.map +1 -1
- package/dist/util/tool-caller.d.ts +2 -2
- package/dist/util/tool-caller.d.ts.map +1 -1
- package/dist/util/tool-caller.js +5 -12
- package/dist/util/tool-caller.js.map +1 -1
- package/dist/v1/hooks/use-tambo-v1-messages.d.ts +58 -0
- package/dist/v1/hooks/use-tambo-v1-messages.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-messages.js +54 -0
- package/dist/v1/hooks/use-tambo-v1-messages.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-messages.test.d.ts +2 -0
- package/dist/v1/hooks/use-tambo-v1-messages.test.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-messages.test.js +137 -0
- package/dist/v1/hooks/use-tambo-v1-messages.test.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-send-message.d.ts +96 -0
- package/dist/v1/hooks/use-tambo-v1-send-message.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-send-message.js +227 -0
- package/dist/v1/hooks/use-tambo-v1-send-message.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-send-message.test.d.ts +2 -0
- package/dist/v1/hooks/use-tambo-v1-send-message.test.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-send-message.test.js +827 -0
- package/dist/v1/hooks/use-tambo-v1-send-message.test.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread-list.d.ts +61 -0
- package/dist/v1/hooks/use-tambo-v1-thread-list.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread-list.js +56 -0
- package/dist/v1/hooks/use-tambo-v1-thread-list.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread-list.test.d.ts +2 -0
- package/dist/v1/hooks/use-tambo-v1-thread-list.test.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread-list.test.js +98 -0
- package/dist/v1/hooks/use-tambo-v1-thread-list.test.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread.d.ts +37 -0
- package/dist/v1/hooks/use-tambo-v1-thread.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread.js +49 -0
- package/dist/v1/hooks/use-tambo-v1-thread.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread.test.d.ts +2 -0
- package/dist/v1/hooks/use-tambo-v1-thread.test.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread.test.js +83 -0
- package/dist/v1/hooks/use-tambo-v1-thread.test.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1.d.ts +107 -0
- package/dist/v1/hooks/use-tambo-v1.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1.js +87 -0
- package/dist/v1/hooks/use-tambo-v1.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1.test.d.ts +2 -0
- package/dist/v1/hooks/use-tambo-v1.test.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1.test.js +150 -0
- package/dist/v1/hooks/use-tambo-v1.test.js.map +1 -0
- package/dist/v1/index.d.ts +73 -0
- package/dist/v1/index.d.ts.map +1 -0
- package/dist/v1/index.js +106 -0
- package/dist/v1/index.js.map +1 -0
- package/dist/v1/providers/tambo-v1-provider.d.ts +91 -0
- package/dist/v1/providers/tambo-v1-provider.d.ts.map +1 -0
- package/dist/v1/providers/tambo-v1-provider.js +110 -0
- package/dist/v1/providers/tambo-v1-provider.js.map +1 -0
- package/dist/v1/providers/tambo-v1-provider.test.d.ts +2 -0
- package/dist/v1/providers/tambo-v1-provider.test.d.ts.map +1 -0
- package/dist/v1/providers/tambo-v1-provider.test.js +123 -0
- package/dist/v1/providers/tambo-v1-provider.test.js.map +1 -0
- package/dist/v1/providers/tambo-v1-stream-context.d.ts +136 -0
- package/dist/v1/providers/tambo-v1-stream-context.d.ts.map +1 -0
- package/dist/v1/providers/tambo-v1-stream-context.js +230 -0
- package/dist/v1/providers/tambo-v1-stream-context.js.map +1 -0
- package/dist/v1/providers/tambo-v1-stream-context.test.d.ts +2 -0
- package/dist/v1/providers/tambo-v1-stream-context.test.d.ts.map +1 -0
- package/dist/v1/providers/tambo-v1-stream-context.test.js +85 -0
- package/dist/v1/providers/tambo-v1-stream-context.test.js.map +1 -0
- package/dist/v1/types/component.d.ts +50 -0
- package/dist/v1/types/component.d.ts.map +1 -0
- package/dist/v1/types/component.js +14 -0
- package/dist/v1/types/component.js.map +1 -0
- package/dist/v1/types/event.d.ts +72 -0
- package/dist/v1/types/event.d.ts.map +1 -0
- package/dist/v1/types/event.js +54 -0
- package/dist/v1/types/event.js.map +1 -0
- package/dist/v1/types/event.test.d.ts +2 -0
- package/dist/v1/types/event.test.d.ts.map +1 -0
- package/dist/v1/types/event.test.js +70 -0
- package/dist/v1/types/event.test.js.map +1 -0
- package/dist/v1/types/message.d.ts +35 -0
- package/dist/v1/types/message.d.ts.map +1 -0
- package/dist/v1/types/message.js +10 -0
- package/dist/v1/types/message.js.map +1 -0
- package/dist/v1/types/thread.d.ts +52 -0
- package/dist/v1/types/thread.d.ts.map +1 -0
- package/dist/v1/types/thread.js +9 -0
- package/dist/v1/types/thread.js.map +1 -0
- package/dist/v1/utils/event-accumulator.d.ts +100 -0
- package/dist/v1/utils/event-accumulator.d.ts.map +1 -0
- package/dist/v1/utils/event-accumulator.js +715 -0
- package/dist/v1/utils/event-accumulator.js.map +1 -0
- package/dist/v1/utils/event-accumulator.test.d.ts +2 -0
- package/dist/v1/utils/event-accumulator.test.d.ts.map +1 -0
- package/dist/v1/utils/event-accumulator.test.js +1010 -0
- package/dist/v1/utils/event-accumulator.test.js.map +1 -0
- package/dist/v1/utils/json-patch.d.ts +18 -0
- package/dist/v1/utils/json-patch.d.ts.map +1 -0
- package/dist/v1/utils/json-patch.js +35 -0
- package/dist/v1/utils/json-patch.js.map +1 -0
- package/dist/v1/utils/json-patch.test.d.ts +2 -0
- package/dist/v1/utils/json-patch.test.d.ts.map +1 -0
- package/dist/v1/utils/json-patch.test.js +28 -0
- package/dist/v1/utils/json-patch.test.js.map +1 -0
- package/dist/v1/utils/registry-conversion.d.ts +53 -0
- package/dist/v1/utils/registry-conversion.d.ts.map +1 -0
- package/dist/v1/utils/registry-conversion.js +114 -0
- package/dist/v1/utils/registry-conversion.js.map +1 -0
- package/dist/v1/utils/registry-conversion.test.d.ts +2 -0
- package/dist/v1/utils/registry-conversion.test.d.ts.map +1 -0
- package/dist/v1/utils/registry-conversion.test.js +179 -0
- package/dist/v1/utils/registry-conversion.test.js.map +1 -0
- package/dist/v1/utils/stream-handler.d.ts +45 -0
- package/dist/v1/utils/stream-handler.d.ts.map +1 -0
- package/dist/v1/utils/stream-handler.js +47 -0
- package/dist/v1/utils/stream-handler.js.map +1 -0
- package/dist/v1/utils/stream-handler.test.d.ts +2 -0
- package/dist/v1/utils/stream-handler.test.d.ts.map +1 -0
- package/dist/v1/utils/stream-handler.test.js +74 -0
- package/dist/v1/utils/stream-handler.test.js.map +1 -0
- package/dist/v1/utils/tool-call-tracker.d.ts +41 -0
- package/dist/v1/utils/tool-call-tracker.d.ts.map +1 -0
- package/dist/v1/utils/tool-call-tracker.js +90 -0
- package/dist/v1/utils/tool-call-tracker.js.map +1 -0
- package/dist/v1/utils/tool-executor.d.ts +33 -0
- package/dist/v1/utils/tool-executor.d.ts.map +1 -0
- package/dist/v1/utils/tool-executor.js +103 -0
- package/dist/v1/utils/tool-executor.js.map +1 -0
- package/dist/v1/utils/tool-executor.test.d.ts +2 -0
- package/dist/v1/utils/tool-executor.test.d.ts.map +1 -0
- package/dist/v1/utils/tool-executor.test.js +222 -0
- package/dist/v1/utils/tool-executor.test.js.map +1 -0
- package/esm/hooks/use-tambo-threads.test.js.map +1 -1
- package/esm/index.d.ts +1 -1
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js.map +1 -1
- package/esm/mcp/index.d.ts +4 -5
- package/esm/mcp/index.d.ts.map +1 -1
- package/esm/mcp/index.js +4 -5
- package/esm/mcp/index.js.map +1 -1
- package/esm/model/component-metadata.d.ts +88 -241
- package/esm/model/component-metadata.d.ts.map +1 -1
- package/esm/model/component-metadata.js.map +1 -1
- package/esm/model/mcp-server-info.d.ts +3 -3
- package/esm/model/mcp-server-info.js.map +1 -1
- package/esm/providers/hooks/use-tambo-session-token.test.js.map +1 -1
- package/esm/providers/tambo-component-provider.d.ts +2 -2
- package/esm/providers/tambo-component-provider.d.ts.map +1 -1
- package/esm/providers/tambo-component-provider.js.map +1 -1
- package/esm/providers/tambo-interactable-provider.d.ts +1 -1
- package/esm/providers/tambo-registry-provider.d.ts +4 -4
- package/esm/providers/tambo-registry-provider.d.ts.map +1 -1
- package/esm/providers/tambo-registry-provider.js +11 -8
- package/esm/providers/tambo-registry-provider.js.map +1 -1
- package/esm/providers/tambo-registry-provider.test.js +31 -0
- package/esm/providers/tambo-registry-provider.test.js.map +1 -1
- package/esm/providers/tambo-registry-schema-compat.test.js +42 -52
- package/esm/providers/tambo-registry-schema-compat.test.js.map +1 -1
- package/esm/providers/tambo-stubs.d.ts +2 -2
- package/esm/providers/tambo-stubs.d.ts.map +1 -1
- package/esm/providers/tambo-stubs.js.map +1 -1
- package/esm/providers/tambo-thread-provider-initial-messages.test.js.map +1 -1
- package/esm/providers/tambo-thread-provider.d.ts.map +1 -1
- package/esm/providers/tambo-thread-provider.js +107 -141
- package/esm/providers/tambo-thread-provider.js.map +1 -1
- package/esm/providers/tambo-thread-provider.test.js +241 -445
- package/esm/providers/tambo-thread-provider.test.js.map +1 -1
- package/esm/schema/index.d.ts +1 -2
- package/esm/schema/index.d.ts.map +1 -1
- package/esm/schema/index.js +1 -2
- package/esm/schema/index.js.map +1 -1
- package/esm/schema/schema.d.ts +7 -24
- package/esm/schema/schema.d.ts.map +1 -1
- package/esm/schema/schema.js +34 -103
- package/esm/schema/schema.js.map +1 -1
- package/esm/schema/schema.test.js +27 -125
- package/esm/schema/schema.test.js.map +1 -1
- package/esm/testing/tools.d.ts +2 -12
- package/esm/testing/tools.d.ts.map +1 -1
- package/esm/testing/tools.js +2 -20
- package/esm/testing/tools.js.map +1 -1
- package/esm/testing/types.d.ts +2 -2
- package/esm/testing/types.d.ts.map +1 -1
- package/esm/testing/types.js.map +1 -1
- package/esm/util/registry-validators.d.ts +2 -2
- package/esm/util/registry-validators.d.ts.map +1 -1
- package/esm/util/registry-validators.js +38 -18
- package/esm/util/registry-validators.js.map +1 -1
- package/esm/util/registry-validators.test.js +64 -25
- package/esm/util/registry-validators.test.js.map +1 -1
- package/esm/util/registry.d.ts +4 -10
- package/esm/util/registry.d.ts.map +1 -1
- package/esm/util/registry.js +7 -22
- package/esm/util/registry.js.map +1 -1
- package/esm/util/registry.test.js +3 -49
- package/esm/util/registry.test.js.map +1 -1
- package/esm/util/tool-caller.d.ts +2 -2
- package/esm/util/tool-caller.d.ts.map +1 -1
- package/esm/util/tool-caller.js +5 -12
- package/esm/util/tool-caller.js.map +1 -1
- package/esm/v1/hooks/use-tambo-v1-messages.d.ts +58 -0
- package/esm/v1/hooks/use-tambo-v1-messages.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-messages.js +51 -0
- package/esm/v1/hooks/use-tambo-v1-messages.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-messages.test.d.ts +2 -0
- package/esm/v1/hooks/use-tambo-v1-messages.test.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-messages.test.js +132 -0
- package/esm/v1/hooks/use-tambo-v1-messages.test.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-send-message.d.ts +96 -0
- package/esm/v1/hooks/use-tambo-v1-send-message.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-send-message.js +223 -0
- package/esm/v1/hooks/use-tambo-v1-send-message.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-send-message.test.d.ts +2 -0
- package/esm/v1/hooks/use-tambo-v1-send-message.test.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-send-message.test.js +822 -0
- package/esm/v1/hooks/use-tambo-v1-send-message.test.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread-list.d.ts +61 -0
- package/esm/v1/hooks/use-tambo-v1-thread-list.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread-list.js +53 -0
- package/esm/v1/hooks/use-tambo-v1-thread-list.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread-list.test.d.ts +2 -0
- package/esm/v1/hooks/use-tambo-v1-thread-list.test.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread-list.test.js +93 -0
- package/esm/v1/hooks/use-tambo-v1-thread-list.test.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread.d.ts +37 -0
- package/esm/v1/hooks/use-tambo-v1-thread.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread.js +46 -0
- package/esm/v1/hooks/use-tambo-v1-thread.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread.test.d.ts +2 -0
- package/esm/v1/hooks/use-tambo-v1-thread.test.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread.test.js +78 -0
- package/esm/v1/hooks/use-tambo-v1-thread.test.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1.d.ts +107 -0
- package/esm/v1/hooks/use-tambo-v1.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1.js +84 -0
- package/esm/v1/hooks/use-tambo-v1.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1.test.d.ts +2 -0
- package/esm/v1/hooks/use-tambo-v1.test.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1.test.js +145 -0
- package/esm/v1/hooks/use-tambo-v1.test.js.map +1 -0
- package/esm/v1/index.d.ts +73 -0
- package/esm/v1/index.d.ts.map +1 -0
- package/esm/v1/index.js +83 -0
- package/esm/v1/index.js.map +1 -0
- package/esm/v1/providers/tambo-v1-provider.d.ts +91 -0
- package/esm/v1/providers/tambo-v1-provider.d.ts.map +1 -0
- package/esm/v1/providers/tambo-v1-provider.js +74 -0
- package/esm/v1/providers/tambo-v1-provider.js.map +1 -0
- package/esm/v1/providers/tambo-v1-provider.test.d.ts +2 -0
- package/esm/v1/providers/tambo-v1-provider.test.d.ts.map +1 -0
- package/esm/v1/providers/tambo-v1-provider.test.js +118 -0
- package/esm/v1/providers/tambo-v1-provider.test.js.map +1 -0
- package/esm/v1/providers/tambo-v1-stream-context.d.ts +136 -0
- package/esm/v1/providers/tambo-v1-stream-context.d.ts.map +1 -0
- package/esm/v1/providers/tambo-v1-stream-context.js +191 -0
- package/esm/v1/providers/tambo-v1-stream-context.js.map +1 -0
- package/esm/v1/providers/tambo-v1-stream-context.test.d.ts +2 -0
- package/esm/v1/providers/tambo-v1-stream-context.test.d.ts.map +1 -0
- package/esm/v1/providers/tambo-v1-stream-context.test.js +80 -0
- package/esm/v1/providers/tambo-v1-stream-context.test.js.map +1 -0
- package/esm/v1/types/component.d.ts +50 -0
- package/esm/v1/types/component.d.ts.map +1 -0
- package/esm/v1/types/component.js +13 -0
- package/esm/v1/types/component.js.map +1 -0
- package/esm/v1/types/event.d.ts +72 -0
- package/esm/v1/types/event.d.ts.map +1 -0
- package/esm/v1/types/event.js +50 -0
- package/esm/v1/types/event.js.map +1 -0
- package/esm/v1/types/event.test.d.ts +2 -0
- package/esm/v1/types/event.test.d.ts.map +1 -0
- package/esm/v1/types/event.test.js +68 -0
- package/esm/v1/types/event.test.js.map +1 -0
- package/esm/v1/types/message.d.ts +35 -0
- package/esm/v1/types/message.d.ts.map +1 -0
- package/esm/v1/types/message.js +9 -0
- package/esm/v1/types/message.js.map +1 -0
- package/esm/v1/types/thread.d.ts +52 -0
- package/esm/v1/types/thread.d.ts.map +1 -0
- package/esm/v1/types/thread.js +8 -0
- package/esm/v1/types/thread.js.map +1 -0
- package/esm/v1/utils/event-accumulator.d.ts +100 -0
- package/esm/v1/utils/event-accumulator.d.ts.map +1 -0
- package/esm/v1/utils/event-accumulator.js +708 -0
- package/esm/v1/utils/event-accumulator.js.map +1 -0
- package/esm/v1/utils/event-accumulator.test.d.ts +2 -0
- package/esm/v1/utils/event-accumulator.test.d.ts.map +1 -0
- package/esm/v1/utils/event-accumulator.test.js +1008 -0
- package/esm/v1/utils/event-accumulator.test.js.map +1 -0
- package/esm/v1/utils/json-patch.d.ts +18 -0
- package/esm/v1/utils/json-patch.d.ts.map +1 -0
- package/esm/v1/utils/json-patch.js +32 -0
- package/esm/v1/utils/json-patch.js.map +1 -0
- package/esm/v1/utils/json-patch.test.d.ts +2 -0
- package/esm/v1/utils/json-patch.test.d.ts.map +1 -0
- package/esm/v1/utils/json-patch.test.js +26 -0
- package/esm/v1/utils/json-patch.test.js.map +1 -0
- package/esm/v1/utils/registry-conversion.d.ts +53 -0
- package/esm/v1/utils/registry-conversion.d.ts.map +1 -0
- package/esm/v1/utils/registry-conversion.js +108 -0
- package/esm/v1/utils/registry-conversion.js.map +1 -0
- package/esm/v1/utils/registry-conversion.test.d.ts +2 -0
- package/esm/v1/utils/registry-conversion.test.d.ts.map +1 -0
- package/esm/v1/utils/registry-conversion.test.js +177 -0
- package/esm/v1/utils/registry-conversion.test.js.map +1 -0
- package/esm/v1/utils/stream-handler.d.ts +45 -0
- package/esm/v1/utils/stream-handler.d.ts.map +1 -0
- package/esm/v1/utils/stream-handler.js +44 -0
- package/esm/v1/utils/stream-handler.js.map +1 -0
- package/esm/v1/utils/stream-handler.test.d.ts +2 -0
- package/esm/v1/utils/stream-handler.test.d.ts.map +1 -0
- package/esm/v1/utils/stream-handler.test.js +72 -0
- package/esm/v1/utils/stream-handler.test.js.map +1 -0
- package/esm/v1/utils/tool-call-tracker.d.ts +41 -0
- package/esm/v1/utils/tool-call-tracker.d.ts.map +1 -0
- package/esm/v1/utils/tool-call-tracker.js +86 -0
- package/esm/v1/utils/tool-call-tracker.js.map +1 -0
- package/esm/v1/utils/tool-executor.d.ts +33 -0
- package/esm/v1/utils/tool-executor.d.ts.map +1 -0
- package/esm/v1/utils/tool-executor.js +99 -0
- package/esm/v1/utils/tool-executor.js.map +1 -0
- package/esm/v1/utils/tool-executor.test.d.ts +2 -0
- package/esm/v1/utils/tool-executor.test.d.ts.map +1 -0
- package/esm/v1/utils/tool-executor.test.js +220 -0
- package/esm/v1/utils/tool-executor.test.js.map +1 -0
- package/package.json +20 -9
- package/dist/schema/zod.d.ts +0 -57
- package/dist/schema/zod.d.ts.map +0 -1
- package/dist/schema/zod.js +0 -191
- package/dist/schema/zod.js.map +0 -1
- package/dist/schema/zod.test.d.ts +0 -2
- package/dist/schema/zod.test.d.ts.map +0 -1
- package/dist/schema/zod.test.js +0 -663
- package/dist/schema/zod.test.js.map +0 -1
- package/esm/schema/zod.d.ts +0 -57
- package/esm/schema/zod.d.ts.map +0 -1
- package/esm/schema/zod.js +0 -180
- package/esm/schema/zod.js.map +0 -1
- package/esm/schema/zod.test.d.ts +0 -2
- package/esm/schema/zod.test.d.ts.map +0 -1
- package/esm/schema/zod.test.js +0 -628
- package/esm/schema/zod.test.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { advanceStream } from "@tambo-ai/typescript-sdk";
|
|
1
|
+
import TamboAI, { advanceStream } from "@tambo-ai/typescript-sdk";
|
|
2
2
|
import { act, renderHook } from "@testing-library/react";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { z } from "zod/v4";
|
|
@@ -23,9 +23,14 @@ jest.mock("./tambo-client-provider", () => {
|
|
|
23
23
|
TamboClientContext: React.createContext(undefined),
|
|
24
24
|
};
|
|
25
25
|
});
|
|
26
|
-
jest.mock("@tambo-ai/typescript-sdk", () =>
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
jest.mock("@tambo-ai/typescript-sdk", () => {
|
|
27
|
+
const actual = jest.requireActual("@tambo-ai/typescript-sdk");
|
|
28
|
+
return {
|
|
29
|
+
__esModule: true,
|
|
30
|
+
...actual,
|
|
31
|
+
advanceStream: jest.fn(),
|
|
32
|
+
};
|
|
33
|
+
});
|
|
29
34
|
// Mock the getCustomContext
|
|
30
35
|
jest.mock("../util/registry", () => ({
|
|
31
36
|
...jest.requireActual("../util/registry"),
|
|
@@ -68,26 +73,9 @@ const createMockAdvanceResponse = (overrides = {}) => ({
|
|
|
68
73
|
});
|
|
69
74
|
describe("TamboThreadProvider", () => {
|
|
70
75
|
const mockThread = createMockThread();
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
},
|
|
75
|
-
retrieve: jest.fn(),
|
|
76
|
-
advance: jest.fn(),
|
|
77
|
-
advanceByID: jest.fn(),
|
|
78
|
-
generateName: jest.fn(),
|
|
79
|
-
};
|
|
80
|
-
const mockProjectsApi = {
|
|
81
|
-
getCurrent: jest.fn(),
|
|
82
|
-
};
|
|
83
|
-
const mockBeta = {
|
|
84
|
-
threads: mockThreadsApi,
|
|
85
|
-
projects: mockProjectsApi,
|
|
86
|
-
};
|
|
87
|
-
const mockTamboAI = {
|
|
88
|
-
apiKey: "",
|
|
89
|
-
beta: mockBeta,
|
|
90
|
-
};
|
|
76
|
+
let mockTamboAI;
|
|
77
|
+
let mockThreadsApi;
|
|
78
|
+
let mockProjectsApi;
|
|
91
79
|
let mockQueryClient;
|
|
92
80
|
const mockRegistry = [
|
|
93
81
|
{
|
|
@@ -139,8 +127,19 @@ describe("TamboThreadProvider", () => {
|
|
|
139
127
|
};
|
|
140
128
|
// Default wrapper for most tests
|
|
141
129
|
const Wrapper = createWrapper();
|
|
130
|
+
afterEach(() => {
|
|
131
|
+
jest.restoreAllMocks();
|
|
132
|
+
});
|
|
142
133
|
beforeEach(() => {
|
|
143
134
|
jest.clearAllMocks();
|
|
135
|
+
mockTamboAI = new TamboAI({
|
|
136
|
+
apiKey: "",
|
|
137
|
+
fetch: () => {
|
|
138
|
+
throw new Error("Unexpected network call in test");
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
mockThreadsApi = mockTamboAI.beta.threads;
|
|
142
|
+
mockProjectsApi = mockTamboAI.beta.projects;
|
|
144
143
|
// Setup mock query client
|
|
145
144
|
mockQueryClient = {
|
|
146
145
|
invalidateQueries: jest.fn().mockResolvedValue(undefined),
|
|
@@ -149,21 +148,22 @@ describe("TamboThreadProvider", () => {
|
|
|
149
148
|
jest
|
|
150
149
|
.mocked(useTamboQueryClient)
|
|
151
150
|
.mockReturnValue(mockQueryClient);
|
|
152
|
-
jest.
|
|
151
|
+
jest.spyOn(mockThreadsApi, "retrieve").mockResolvedValue(mockThread);
|
|
153
152
|
jest
|
|
154
|
-
.
|
|
153
|
+
.spyOn(mockThreadsApi.messages, "create")
|
|
155
154
|
.mockResolvedValue(createMockMessage());
|
|
156
155
|
jest
|
|
157
|
-
.
|
|
156
|
+
.spyOn(mockThreadsApi, "advance")
|
|
158
157
|
.mockResolvedValue(createMockAdvanceResponse());
|
|
159
158
|
jest
|
|
160
|
-
.
|
|
159
|
+
.spyOn(mockThreadsApi, "advanceByID")
|
|
161
160
|
.mockResolvedValue(createMockAdvanceResponse());
|
|
162
|
-
jest.
|
|
161
|
+
jest.spyOn(mockThreadsApi, "generateName").mockResolvedValue({
|
|
163
162
|
...mockThread,
|
|
164
163
|
name: "Generated Thread Name",
|
|
165
164
|
});
|
|
166
|
-
jest.
|
|
165
|
+
jest.spyOn(mockThreadsApi, "update").mockResolvedValue({});
|
|
166
|
+
jest.spyOn(mockProjectsApi, "getCurrent").mockResolvedValue({
|
|
167
167
|
id: "test-project-id",
|
|
168
168
|
name: "Test Project",
|
|
169
169
|
isTokenRequired: false,
|
|
@@ -231,7 +231,7 @@ describe("TamboThreadProvider", () => {
|
|
|
231
231
|
});
|
|
232
232
|
});
|
|
233
233
|
it("should send a message and update thread state", async () => {
|
|
234
|
-
const
|
|
234
|
+
const mockStreamResponse = {
|
|
235
235
|
responseMessageDto: {
|
|
236
236
|
id: "response-1",
|
|
237
237
|
content: [{ type: "text", text: "Response" }],
|
|
@@ -244,14 +244,17 @@ describe("TamboThreadProvider", () => {
|
|
|
244
244
|
generationStage: GenerationStage.COMPLETE,
|
|
245
245
|
mcpAccessToken: "test-mcp-access-token",
|
|
246
246
|
};
|
|
247
|
-
|
|
248
|
-
.
|
|
249
|
-
|
|
247
|
+
const mockAsyncIterator = {
|
|
248
|
+
[Symbol.asyncIterator]: async function* () {
|
|
249
|
+
yield mockStreamResponse;
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
jest.mocked(advanceStream).mockResolvedValue(mockAsyncIterator);
|
|
250
253
|
const { result } = renderHook(() => useTamboThread(), { wrapper: Wrapper });
|
|
251
254
|
await act(async () => {
|
|
252
255
|
await result.current.sendThreadMessage("Hello", {
|
|
253
256
|
threadId: "test-thread-1",
|
|
254
|
-
streamResponse:
|
|
257
|
+
streamResponse: true,
|
|
255
258
|
additionalContext: {
|
|
256
259
|
custom: {
|
|
257
260
|
message: "additional instructions",
|
|
@@ -259,7 +262,7 @@ describe("TamboThreadProvider", () => {
|
|
|
259
262
|
},
|
|
260
263
|
});
|
|
261
264
|
});
|
|
262
|
-
expect(
|
|
265
|
+
expect(advanceStream).toHaveBeenCalledWith(expect.anything(), {
|
|
263
266
|
messageToAppend: {
|
|
264
267
|
content: [{ type: "text", text: "Hello" }],
|
|
265
268
|
role: "user",
|
|
@@ -273,7 +276,7 @@ describe("TamboThreadProvider", () => {
|
|
|
273
276
|
contextKey: undefined,
|
|
274
277
|
clientTools: [],
|
|
275
278
|
toolCallCounts: {},
|
|
276
|
-
});
|
|
279
|
+
}, "test-thread-1");
|
|
277
280
|
expect(result.current.generationStage).toBe(GenerationStage.COMPLETE);
|
|
278
281
|
});
|
|
279
282
|
it("should handle streaming responses", async () => {
|
|
@@ -308,7 +311,7 @@ describe("TamboThreadProvider", () => {
|
|
|
308
311
|
expect(result.current.generationStage).toBe(GenerationStage.COMPLETE);
|
|
309
312
|
});
|
|
310
313
|
it("should handle tool calls during message processing.", async () => {
|
|
311
|
-
const
|
|
314
|
+
const mockToolCallChunk = {
|
|
312
315
|
responseMessageDto: {
|
|
313
316
|
id: "tool-call-1",
|
|
314
317
|
content: [{ type: "text", text: "Tool response" }],
|
|
@@ -324,10 +327,7 @@ describe("TamboThreadProvider", () => {
|
|
|
324
327
|
generationStage: GenerationStage.COMPLETE,
|
|
325
328
|
mcpAccessToken: "test-mcp-access-token",
|
|
326
329
|
};
|
|
327
|
-
|
|
328
|
-
.mocked(mockThreadsApi.advanceByID)
|
|
329
|
-
.mockResolvedValueOnce(mockToolCallResponse)
|
|
330
|
-
.mockResolvedValueOnce({
|
|
330
|
+
const mockFinalChunk = {
|
|
331
331
|
responseMessageDto: {
|
|
332
332
|
id: "advance-response2",
|
|
333
333
|
content: [{ type: "text", text: "response 2" }],
|
|
@@ -338,12 +338,26 @@ describe("TamboThreadProvider", () => {
|
|
|
338
338
|
},
|
|
339
339
|
generationStage: GenerationStage.COMPLETE,
|
|
340
340
|
mcpAccessToken: "test-mcp-access-token",
|
|
341
|
-
}
|
|
341
|
+
};
|
|
342
|
+
const mockAsyncIterator = {
|
|
343
|
+
[Symbol.asyncIterator]: async function* () {
|
|
344
|
+
yield mockToolCallChunk;
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
const mockAsyncIterator2 = {
|
|
348
|
+
[Symbol.asyncIterator]: async function* () {
|
|
349
|
+
yield mockFinalChunk;
|
|
350
|
+
},
|
|
351
|
+
};
|
|
352
|
+
jest
|
|
353
|
+
.mocked(advanceStream)
|
|
354
|
+
.mockResolvedValueOnce(mockAsyncIterator)
|
|
355
|
+
.mockResolvedValueOnce(mockAsyncIterator2);
|
|
342
356
|
const { result } = renderHook(() => useTamboThread(), { wrapper: Wrapper });
|
|
343
357
|
await act(async () => {
|
|
344
358
|
await result.current.sendThreadMessage("Use tool", {
|
|
345
359
|
threadId: "test-thread-1",
|
|
346
|
-
streamResponse:
|
|
360
|
+
streamResponse: true,
|
|
347
361
|
});
|
|
348
362
|
});
|
|
349
363
|
expect(result.current.generationStage).toBe(GenerationStage.COMPLETE);
|
|
@@ -356,7 +370,7 @@ describe("TamboThreadProvider", () => {
|
|
|
356
370
|
const mockOnCallUnregisteredTool = jest
|
|
357
371
|
.fn()
|
|
358
372
|
.mockResolvedValue("unregistered-tool-result");
|
|
359
|
-
const
|
|
373
|
+
const mockUnregisteredToolCallChunk = {
|
|
360
374
|
responseMessageDto: {
|
|
361
375
|
id: "unregistered-tool-call-1",
|
|
362
376
|
content: [{ type: "text", text: "Unregistered tool response" }],
|
|
@@ -374,10 +388,7 @@ describe("TamboThreadProvider", () => {
|
|
|
374
388
|
generationStage: GenerationStage.COMPLETE,
|
|
375
389
|
mcpAccessToken: "test-mcp-access-token",
|
|
376
390
|
};
|
|
377
|
-
|
|
378
|
-
.mocked(mockThreadsApi.advanceByID)
|
|
379
|
-
.mockResolvedValueOnce(mockUnregisteredToolCallResponse)
|
|
380
|
-
.mockResolvedValueOnce({
|
|
391
|
+
const mockFinalChunk = {
|
|
381
392
|
responseMessageDto: {
|
|
382
393
|
id: "advance-response2",
|
|
383
394
|
content: [{ type: "text", text: "response 2" }],
|
|
@@ -388,7 +399,21 @@ describe("TamboThreadProvider", () => {
|
|
|
388
399
|
},
|
|
389
400
|
generationStage: GenerationStage.COMPLETE,
|
|
390
401
|
mcpAccessToken: "test-mcp-access-token",
|
|
391
|
-
}
|
|
402
|
+
};
|
|
403
|
+
const mockAsyncIterator = {
|
|
404
|
+
[Symbol.asyncIterator]: async function* () {
|
|
405
|
+
yield mockUnregisteredToolCallChunk;
|
|
406
|
+
},
|
|
407
|
+
};
|
|
408
|
+
const mockAsyncIterator2 = {
|
|
409
|
+
[Symbol.asyncIterator]: async function* () {
|
|
410
|
+
yield mockFinalChunk;
|
|
411
|
+
},
|
|
412
|
+
};
|
|
413
|
+
jest
|
|
414
|
+
.mocked(advanceStream)
|
|
415
|
+
.mockResolvedValueOnce(mockAsyncIterator)
|
|
416
|
+
.mockResolvedValueOnce(mockAsyncIterator2);
|
|
392
417
|
const { result } = renderHook(() => useTamboThread(), {
|
|
393
418
|
wrapper: createWrapper({
|
|
394
419
|
onCallUnregisteredTool: mockOnCallUnregisteredTool,
|
|
@@ -397,14 +422,14 @@ describe("TamboThreadProvider", () => {
|
|
|
397
422
|
await act(async () => {
|
|
398
423
|
await result.current.sendThreadMessage("Use unregistered tool", {
|
|
399
424
|
threadId: "test-thread-1",
|
|
400
|
-
streamResponse:
|
|
425
|
+
streamResponse: true,
|
|
401
426
|
});
|
|
402
427
|
});
|
|
403
428
|
expect(result.current.generationStage).toBe(GenerationStage.COMPLETE);
|
|
404
429
|
expect(mockOnCallUnregisteredTool).toHaveBeenCalledWith("unregistered-tool", [{ parameterName: "input", parameterValue: "test-input" }]);
|
|
405
430
|
});
|
|
406
431
|
it("should handle unregistered tool calls without onCallUnregisteredTool", async () => {
|
|
407
|
-
const
|
|
432
|
+
const mockUnregisteredToolCallChunk = {
|
|
408
433
|
responseMessageDto: {
|
|
409
434
|
id: "unregistered-tool-call-1",
|
|
410
435
|
content: [{ type: "text", text: "Unregistered tool response" }],
|
|
@@ -422,10 +447,7 @@ describe("TamboThreadProvider", () => {
|
|
|
422
447
|
generationStage: GenerationStage.COMPLETE,
|
|
423
448
|
mcpAccessToken: "test-mcp-access-token",
|
|
424
449
|
};
|
|
425
|
-
|
|
426
|
-
.mocked(mockThreadsApi.advanceByID)
|
|
427
|
-
.mockResolvedValueOnce(mockUnregisteredToolCallResponse)
|
|
428
|
-
.mockResolvedValueOnce({
|
|
450
|
+
const mockFinalChunk = {
|
|
429
451
|
responseMessageDto: {
|
|
430
452
|
id: "advance-response2",
|
|
431
453
|
content: [{ type: "text", text: "response 2" }],
|
|
@@ -436,12 +458,26 @@ describe("TamboThreadProvider", () => {
|
|
|
436
458
|
},
|
|
437
459
|
generationStage: GenerationStage.COMPLETE,
|
|
438
460
|
mcpAccessToken: "test-mcp-access-token",
|
|
439
|
-
}
|
|
461
|
+
};
|
|
462
|
+
const mockAsyncIterator = {
|
|
463
|
+
[Symbol.asyncIterator]: async function* () {
|
|
464
|
+
yield mockUnregisteredToolCallChunk;
|
|
465
|
+
},
|
|
466
|
+
};
|
|
467
|
+
const mockAsyncIterator2 = {
|
|
468
|
+
[Symbol.asyncIterator]: async function* () {
|
|
469
|
+
yield mockFinalChunk;
|
|
470
|
+
},
|
|
471
|
+
};
|
|
472
|
+
jest
|
|
473
|
+
.mocked(advanceStream)
|
|
474
|
+
.mockResolvedValueOnce(mockAsyncIterator)
|
|
475
|
+
.mockResolvedValueOnce(mockAsyncIterator2);
|
|
440
476
|
const { result } = renderHook(() => useTamboThread(), { wrapper: Wrapper });
|
|
441
477
|
await act(async () => {
|
|
442
478
|
await result.current.sendThreadMessage("Use unregistered tool", {
|
|
443
479
|
threadId: "test-thread-1",
|
|
444
|
-
streamResponse:
|
|
480
|
+
streamResponse: true,
|
|
445
481
|
});
|
|
446
482
|
});
|
|
447
483
|
expect(result.current.generationStage).toBe(GenerationStage.COMPLETE);
|
|
@@ -502,77 +538,16 @@ describe("TamboThreadProvider", () => {
|
|
|
502
538
|
expect(mockThreadsApi.advance).not.toHaveBeenCalled();
|
|
503
539
|
expect(mockThreadsApi.advanceByID).not.toHaveBeenCalled();
|
|
504
540
|
});
|
|
505
|
-
it("should
|
|
506
|
-
// Use wrapper with streaming=true to show that explicit streamResponse=false overrides provider setting
|
|
541
|
+
it("should throw error when streamResponse=false (non-streaming not supported)", async () => {
|
|
507
542
|
const { result } = renderHook(() => useTamboThread(), {
|
|
508
543
|
wrapper: createWrapper({ streaming: true }),
|
|
509
544
|
});
|
|
510
545
|
await act(async () => {
|
|
511
|
-
await result.current.sendThreadMessage("Hello non-streaming", {
|
|
546
|
+
await expect(result.current.sendThreadMessage("Hello non-streaming", {
|
|
512
547
|
threadId: "test-thread-1",
|
|
513
548
|
streamResponse: false,
|
|
514
|
-
|
|
515
|
-
custom: {
|
|
516
|
-
message: "additional instructions",
|
|
517
|
-
},
|
|
518
|
-
},
|
|
519
|
-
});
|
|
520
|
-
});
|
|
521
|
-
expect(mockThreadsApi.advanceByID).toHaveBeenCalledWith("test-thread-1", {
|
|
522
|
-
messageToAppend: {
|
|
523
|
-
content: [{ type: "text", text: "Hello non-streaming" }],
|
|
524
|
-
role: "user",
|
|
525
|
-
additionalContext: {
|
|
526
|
-
custom: {
|
|
527
|
-
message: "additional instructions",
|
|
528
|
-
},
|
|
529
|
-
},
|
|
530
|
-
},
|
|
531
|
-
availableComponents: serializeRegistry(mockRegistry),
|
|
532
|
-
contextKey: undefined,
|
|
533
|
-
clientTools: [],
|
|
534
|
-
forceToolChoice: undefined,
|
|
535
|
-
toolCallCounts: {},
|
|
536
|
-
});
|
|
537
|
-
// Should not call advance or advanceStream
|
|
538
|
-
expect(mockThreadsApi.advance).not.toHaveBeenCalled();
|
|
539
|
-
expect(advanceStream).not.toHaveBeenCalled();
|
|
540
|
-
});
|
|
541
|
-
it("should call advanceById when streamResponse is undefined and provider streaming=false", async () => {
|
|
542
|
-
// Use wrapper with streaming=false to test that undefined streamResponse respects provider setting
|
|
543
|
-
const { result } = renderHook(() => useTamboThread(), {
|
|
544
|
-
wrapper: createWrapper({ streaming: false }),
|
|
545
|
-
});
|
|
546
|
-
await act(async () => {
|
|
547
|
-
await result.current.sendThreadMessage("Hello default", {
|
|
548
|
-
threadId: "test-thread-1",
|
|
549
|
-
// streamResponse is undefined, should use provider's streaming=false
|
|
550
|
-
additionalContext: {
|
|
551
|
-
custom: {
|
|
552
|
-
message: "additional instructions",
|
|
553
|
-
},
|
|
554
|
-
},
|
|
555
|
-
});
|
|
549
|
+
})).rejects.toThrow();
|
|
556
550
|
});
|
|
557
|
-
expect(mockThreadsApi.advanceByID).toHaveBeenCalledWith("test-thread-1", {
|
|
558
|
-
messageToAppend: {
|
|
559
|
-
content: [{ type: "text", text: "Hello default" }],
|
|
560
|
-
role: "user",
|
|
561
|
-
additionalContext: {
|
|
562
|
-
custom: {
|
|
563
|
-
message: "additional instructions",
|
|
564
|
-
},
|
|
565
|
-
},
|
|
566
|
-
},
|
|
567
|
-
availableComponents: serializeRegistry(mockRegistry),
|
|
568
|
-
contextKey: undefined,
|
|
569
|
-
clientTools: [],
|
|
570
|
-
forceToolChoice: undefined,
|
|
571
|
-
toolCallCounts: {},
|
|
572
|
-
});
|
|
573
|
-
// Should not call advance or advanceStream
|
|
574
|
-
expect(mockThreadsApi.advance).not.toHaveBeenCalled();
|
|
575
|
-
expect(advanceStream).not.toHaveBeenCalled();
|
|
576
551
|
});
|
|
577
552
|
it("should call advanceStream when streamResponse is undefined and provider streaming=true (default)", async () => {
|
|
578
553
|
const mockStreamResponse = {
|
|
@@ -628,44 +603,6 @@ describe("TamboThreadProvider", () => {
|
|
|
628
603
|
expect(mockThreadsApi.advance).not.toHaveBeenCalled();
|
|
629
604
|
expect(mockThreadsApi.advanceByID).not.toHaveBeenCalled();
|
|
630
605
|
});
|
|
631
|
-
it("should call advance when streamResponse=false for placeholder thread", async () => {
|
|
632
|
-
// Use wrapper with streaming=true to show that explicit streamResponse=false overrides provider setting
|
|
633
|
-
const { result } = renderHook(() => useTamboThread(), {
|
|
634
|
-
wrapper: createWrapper({ streaming: true }),
|
|
635
|
-
});
|
|
636
|
-
// Start with placeholder thread (which is the default state)
|
|
637
|
-
expect(result.current.thread.id).toBe("placeholder");
|
|
638
|
-
await act(async () => {
|
|
639
|
-
await result.current.sendThreadMessage("Hello new thread", {
|
|
640
|
-
threadId: "placeholder",
|
|
641
|
-
streamResponse: false,
|
|
642
|
-
additionalContext: {
|
|
643
|
-
custom: {
|
|
644
|
-
message: "additional instructions",
|
|
645
|
-
},
|
|
646
|
-
},
|
|
647
|
-
});
|
|
648
|
-
});
|
|
649
|
-
expect(mockThreadsApi.advance).toHaveBeenCalledWith({
|
|
650
|
-
messageToAppend: {
|
|
651
|
-
content: [{ type: "text", text: "Hello new thread" }],
|
|
652
|
-
role: "user",
|
|
653
|
-
additionalContext: {
|
|
654
|
-
custom: {
|
|
655
|
-
message: "additional instructions",
|
|
656
|
-
},
|
|
657
|
-
},
|
|
658
|
-
},
|
|
659
|
-
availableComponents: serializeRegistry(mockRegistry),
|
|
660
|
-
contextKey: undefined,
|
|
661
|
-
clientTools: [],
|
|
662
|
-
forceToolChoice: undefined,
|
|
663
|
-
toolCallCounts: {},
|
|
664
|
-
});
|
|
665
|
-
// Should not call advanceById or advanceStream
|
|
666
|
-
expect(mockThreadsApi.advanceByID).not.toHaveBeenCalled();
|
|
667
|
-
expect(advanceStream).not.toHaveBeenCalled();
|
|
668
|
-
});
|
|
669
606
|
it("should call advanceStream when streamResponse=true for placeholder thread", async () => {
|
|
670
607
|
const mockStreamResponse = {
|
|
671
608
|
responseMessageDto: {
|
|
@@ -812,10 +749,10 @@ describe("TamboThreadProvider", () => {
|
|
|
812
749
|
});
|
|
813
750
|
});
|
|
814
751
|
describe("error handling", () => {
|
|
815
|
-
it("should set generation stage to ERROR when
|
|
816
|
-
const testError = new Error("API call failed");
|
|
817
|
-
// Mock
|
|
818
|
-
jest.mocked(
|
|
752
|
+
it("should set generation stage to ERROR when streaming sendThreadMessage fails", async () => {
|
|
753
|
+
const testError = new Error("Streaming API call failed");
|
|
754
|
+
// Mock advanceStream to throw an error
|
|
755
|
+
jest.mocked(advanceStream).mockRejectedValue(testError);
|
|
819
756
|
const { result } = renderHook(() => useTamboThread(), {
|
|
820
757
|
wrapper: Wrapper,
|
|
821
758
|
});
|
|
@@ -824,48 +761,91 @@ describe("TamboThreadProvider", () => {
|
|
|
824
761
|
await result.current.switchCurrentThread("test-thread-1");
|
|
825
762
|
await expect(result.current.sendThreadMessage("Hello", {
|
|
826
763
|
threadId: "test-thread-1",
|
|
827
|
-
streamResponse:
|
|
828
|
-
})).rejects.toThrow("API call failed");
|
|
764
|
+
streamResponse: true,
|
|
765
|
+
})).rejects.toThrow("Streaming API call failed");
|
|
829
766
|
});
|
|
830
767
|
// Verify generation stage is set to ERROR
|
|
831
768
|
expect(result.current.generationStage).toBe(GenerationStage.ERROR);
|
|
832
769
|
});
|
|
833
|
-
it("should
|
|
834
|
-
const testError = new Error("
|
|
835
|
-
// Mock advanceStream to throw an error
|
|
770
|
+
it("should rollback optimistic user message when sendThreadMessage fails", async () => {
|
|
771
|
+
const testError = new Error("API call failed");
|
|
836
772
|
jest.mocked(advanceStream).mockRejectedValue(testError);
|
|
837
773
|
const { result } = renderHook(() => useTamboThread(), {
|
|
838
774
|
wrapper: Wrapper,
|
|
839
775
|
});
|
|
840
|
-
// Expect the error to be thrown
|
|
841
776
|
await act(async () => {
|
|
842
777
|
await result.current.switchCurrentThread("test-thread-1");
|
|
778
|
+
});
|
|
779
|
+
const initialMessageCount = result.current.thread.messages.length;
|
|
780
|
+
await act(async () => {
|
|
843
781
|
await expect(result.current.sendThreadMessage("Hello", {
|
|
844
782
|
threadId: "test-thread-1",
|
|
845
783
|
streamResponse: true,
|
|
846
|
-
})).rejects.toThrow("
|
|
784
|
+
})).rejects.toThrow("API call failed");
|
|
847
785
|
});
|
|
848
|
-
// Verify
|
|
849
|
-
expect(result.current.
|
|
786
|
+
// Verify user message was rolled back
|
|
787
|
+
expect(result.current.thread.messages.length).toBe(initialMessageCount);
|
|
850
788
|
});
|
|
851
|
-
it("should
|
|
852
|
-
const testError = new Error("
|
|
853
|
-
|
|
854
|
-
jest.mocked(mockThreadsApi.advance).mockRejectedValue(testError);
|
|
789
|
+
it("should rollback optimistic message when addThreadMessage fails", async () => {
|
|
790
|
+
const testError = new Error("Create message failed");
|
|
791
|
+
jest.mocked(mockThreadsApi.messages.create).mockRejectedValue(testError);
|
|
855
792
|
const { result } = renderHook(() => useTamboThread(), {
|
|
856
793
|
wrapper: Wrapper,
|
|
857
794
|
});
|
|
858
|
-
// Start with placeholder thread (which is the default state)
|
|
859
|
-
expect(result.current.thread.id).toBe("placeholder");
|
|
860
|
-
// Expect the error to be thrown
|
|
861
795
|
await act(async () => {
|
|
862
|
-
await
|
|
863
|
-
threadId: "placeholder",
|
|
864
|
-
streamResponse: false,
|
|
865
|
-
})).rejects.toThrow("Advance API call failed");
|
|
796
|
+
await result.current.switchCurrentThread("test-thread-1");
|
|
866
797
|
});
|
|
867
|
-
|
|
868
|
-
|
|
798
|
+
const initialMessageCount = result.current.thread.messages.length;
|
|
799
|
+
const newMessage = createMockMessage({ threadId: "test-thread-1" });
|
|
800
|
+
await act(async () => {
|
|
801
|
+
await expect(result.current.addThreadMessage(newMessage, true)).rejects.toThrow("Create message failed");
|
|
802
|
+
});
|
|
803
|
+
// Verify message was rolled back
|
|
804
|
+
expect(result.current.thread.messages.length).toBe(initialMessageCount);
|
|
805
|
+
});
|
|
806
|
+
it("should rollback optimistic update when updateThreadMessage fails", async () => {
|
|
807
|
+
const testError = new Error("Update message failed");
|
|
808
|
+
jest.mocked(mockThreadsApi.messages.create).mockRejectedValue(testError);
|
|
809
|
+
const { result } = renderHook(() => useTamboThread(), {
|
|
810
|
+
wrapper: Wrapper,
|
|
811
|
+
});
|
|
812
|
+
await act(async () => {
|
|
813
|
+
await result.current.switchCurrentThread("test-thread-1");
|
|
814
|
+
});
|
|
815
|
+
const existingMessage = createMockMessage({
|
|
816
|
+
id: "existing-msg",
|
|
817
|
+
threadId: "test-thread-1",
|
|
818
|
+
content: [{ type: "text", text: "Old content" }],
|
|
819
|
+
});
|
|
820
|
+
await act(async () => {
|
|
821
|
+
await result.current.addThreadMessage(existingMessage, false);
|
|
822
|
+
});
|
|
823
|
+
const initialMessageCount = result.current.thread.messages.length;
|
|
824
|
+
await act(async () => {
|
|
825
|
+
await expect(result.current.updateThreadMessage("existing-msg", {
|
|
826
|
+
threadId: "test-thread-1",
|
|
827
|
+
content: [{ type: "text", text: "New content" }],
|
|
828
|
+
role: "assistant",
|
|
829
|
+
}, true)).rejects.toThrow("Update message failed");
|
|
830
|
+
});
|
|
831
|
+
// Verify message was rolled back
|
|
832
|
+
expect(result.current.thread.messages.length).toBe(initialMessageCount - 1);
|
|
833
|
+
});
|
|
834
|
+
it("should rollback optimistic name update when updateThreadName fails", async () => {
|
|
835
|
+
const testError = new Error("Update name failed");
|
|
836
|
+
jest.mocked(mockThreadsApi.update).mockRejectedValue(testError);
|
|
837
|
+
const { result } = renderHook(() => useTamboThread(), {
|
|
838
|
+
wrapper: Wrapper,
|
|
839
|
+
});
|
|
840
|
+
await act(async () => {
|
|
841
|
+
await result.current.switchCurrentThread("test-thread-1");
|
|
842
|
+
});
|
|
843
|
+
const initialName = result.current.thread.name;
|
|
844
|
+
await act(async () => {
|
|
845
|
+
await expect(result.current.updateThreadName("New Name", "test-thread-1")).rejects.toThrow("Update name failed");
|
|
846
|
+
});
|
|
847
|
+
// Verify name was rolled back
|
|
848
|
+
expect(result.current.thread.name).toBe(initialName);
|
|
869
849
|
});
|
|
870
850
|
});
|
|
871
851
|
describe("refetch threads list behavior", () => {
|
|
@@ -873,8 +853,8 @@ describe("TamboThreadProvider", () => {
|
|
|
873
853
|
const { result } = renderHook(() => useTamboThread(), {
|
|
874
854
|
wrapper: Wrapper,
|
|
875
855
|
});
|
|
876
|
-
// Mock the
|
|
877
|
-
const
|
|
856
|
+
// Mock the stream response to return a new thread ID
|
|
857
|
+
const mockStreamResponse = {
|
|
878
858
|
responseMessageDto: {
|
|
879
859
|
id: "response-1",
|
|
880
860
|
content: [{ type: "text", text: "Response" }],
|
|
@@ -887,16 +867,19 @@ describe("TamboThreadProvider", () => {
|
|
|
887
867
|
generationStage: GenerationStage.COMPLETE,
|
|
888
868
|
mcpAccessToken: "test-mcp-access-token",
|
|
889
869
|
};
|
|
890
|
-
|
|
891
|
-
.
|
|
892
|
-
|
|
870
|
+
const mockAsyncIterator = {
|
|
871
|
+
[Symbol.asyncIterator]: async function* () {
|
|
872
|
+
yield mockStreamResponse;
|
|
873
|
+
},
|
|
874
|
+
};
|
|
875
|
+
jest.mocked(advanceStream).mockResolvedValue(mockAsyncIterator);
|
|
893
876
|
// Start with placeholder thread
|
|
894
877
|
expect(result.current.thread.id).toBe("placeholder");
|
|
895
878
|
// Send a message which will create a new thread with contextKey
|
|
896
879
|
await act(async () => {
|
|
897
880
|
await result.current.sendThreadMessage("Hello", {
|
|
898
881
|
threadId: "placeholder",
|
|
899
|
-
streamResponse:
|
|
882
|
+
streamResponse: true,
|
|
900
883
|
contextKey: "test-context-key",
|
|
901
884
|
});
|
|
902
885
|
});
|
|
@@ -934,93 +917,6 @@ describe("TamboThreadProvider", () => {
|
|
|
934
917
|
});
|
|
935
918
|
});
|
|
936
919
|
describe("transformToContent", () => {
|
|
937
|
-
it("should use custom transformToContent when provided (non-streaming)", async () => {
|
|
938
|
-
const mockTransformToContent = jest.fn().mockReturnValue([
|
|
939
|
-
{ type: "text", text: "Custom transformed content" },
|
|
940
|
-
{
|
|
941
|
-
type: "image_url",
|
|
942
|
-
image_url: { url: "https://example.com/image.png" },
|
|
943
|
-
},
|
|
944
|
-
]);
|
|
945
|
-
const customToolRegistry = [
|
|
946
|
-
{
|
|
947
|
-
name: "TestComponent",
|
|
948
|
-
component: () => React.createElement("div", null, "Test"),
|
|
949
|
-
description: "Test",
|
|
950
|
-
propsSchema: z.object({ test: z.string() }),
|
|
951
|
-
associatedTools: [
|
|
952
|
-
{
|
|
953
|
-
name: "custom-tool",
|
|
954
|
-
tool: jest.fn().mockResolvedValue({ data: "tool result" }),
|
|
955
|
-
description: "Tool with custom transform",
|
|
956
|
-
inputSchema: z.object({ input: z.string() }),
|
|
957
|
-
outputSchema: z.object({ data: z.string() }),
|
|
958
|
-
transformToContent: mockTransformToContent,
|
|
959
|
-
},
|
|
960
|
-
],
|
|
961
|
-
},
|
|
962
|
-
];
|
|
963
|
-
const mockToolCallResponse = {
|
|
964
|
-
responseMessageDto: {
|
|
965
|
-
id: "tool-call-1",
|
|
966
|
-
content: [{ type: "text", text: "Tool response" }],
|
|
967
|
-
role: "tool",
|
|
968
|
-
threadId: "test-thread-1",
|
|
969
|
-
toolCallRequest: {
|
|
970
|
-
toolName: "custom-tool",
|
|
971
|
-
parameters: [{ parameterName: "input", parameterValue: "test" }],
|
|
972
|
-
},
|
|
973
|
-
componentState: {},
|
|
974
|
-
createdAt: new Date().toISOString(),
|
|
975
|
-
},
|
|
976
|
-
generationStage: GenerationStage.COMPLETE,
|
|
977
|
-
mcpAccessToken: "test-mcp-access-token",
|
|
978
|
-
};
|
|
979
|
-
jest
|
|
980
|
-
.mocked(mockThreadsApi.advanceByID)
|
|
981
|
-
.mockResolvedValueOnce(mockToolCallResponse)
|
|
982
|
-
.mockResolvedValueOnce({
|
|
983
|
-
responseMessageDto: {
|
|
984
|
-
id: "final-response",
|
|
985
|
-
content: [{ type: "text", text: "Final response" }],
|
|
986
|
-
role: "assistant",
|
|
987
|
-
threadId: "test-thread-1",
|
|
988
|
-
componentState: {},
|
|
989
|
-
createdAt: new Date().toISOString(),
|
|
990
|
-
},
|
|
991
|
-
generationStage: GenerationStage.COMPLETE,
|
|
992
|
-
mcpAccessToken: "test-mcp-access-token",
|
|
993
|
-
});
|
|
994
|
-
const { result } = renderHook(() => useTamboThread(), {
|
|
995
|
-
wrapper: createWrapper({ components: customToolRegistry }),
|
|
996
|
-
});
|
|
997
|
-
await act(async () => {
|
|
998
|
-
await result.current.sendThreadMessage("Use custom tool", {
|
|
999
|
-
threadId: "test-thread-1",
|
|
1000
|
-
streamResponse: false,
|
|
1001
|
-
});
|
|
1002
|
-
});
|
|
1003
|
-
// Verify the tool was called with single object arg (new inputSchema interface)
|
|
1004
|
-
expect(customToolRegistry[0]?.associatedTools?.[0]?.tool).toHaveBeenCalledWith({ input: "test" });
|
|
1005
|
-
// Verify transformToContent was called with the tool result
|
|
1006
|
-
expect(mockTransformToContent).toHaveBeenCalledWith({
|
|
1007
|
-
data: "tool result",
|
|
1008
|
-
});
|
|
1009
|
-
// Verify the second advance call included the transformed content
|
|
1010
|
-
expect(mockThreadsApi.advanceByID).toHaveBeenCalledTimes(2);
|
|
1011
|
-
expect(mockThreadsApi.advanceByID).toHaveBeenLastCalledWith("test-thread-1", expect.objectContaining({
|
|
1012
|
-
messageToAppend: expect.objectContaining({
|
|
1013
|
-
content: [
|
|
1014
|
-
{ type: "text", text: "Custom transformed content" },
|
|
1015
|
-
{
|
|
1016
|
-
type: "image_url",
|
|
1017
|
-
image_url: { url: "https://example.com/image.png" },
|
|
1018
|
-
},
|
|
1019
|
-
],
|
|
1020
|
-
role: "tool",
|
|
1021
|
-
}),
|
|
1022
|
-
}));
|
|
1023
|
-
});
|
|
1024
920
|
it("should use custom async transformToContent when provided (streaming)", async () => {
|
|
1025
921
|
const mockTransformToContent = jest
|
|
1026
922
|
.fn()
|
|
@@ -1117,169 +1013,6 @@ describe("TamboThreadProvider", () => {
|
|
|
1117
1013
|
}),
|
|
1118
1014
|
}), "test-thread-1");
|
|
1119
1015
|
});
|
|
1120
|
-
it("should fallback to stringified text when transformToContent is not provided", async () => {
|
|
1121
|
-
const toolWithoutTransform = [
|
|
1122
|
-
{
|
|
1123
|
-
name: "TestComponent",
|
|
1124
|
-
component: () => React.createElement("div", null, "Test"),
|
|
1125
|
-
description: "Test",
|
|
1126
|
-
propsSchema: z.object({ test: z.string() }),
|
|
1127
|
-
associatedTools: [
|
|
1128
|
-
{
|
|
1129
|
-
name: "no-transform-tool",
|
|
1130
|
-
tool: jest
|
|
1131
|
-
.fn()
|
|
1132
|
-
.mockResolvedValue({ complex: "data", nested: { value: 42 } }),
|
|
1133
|
-
description: "Tool without custom transform",
|
|
1134
|
-
inputSchema: z.object({ input: z.string() }),
|
|
1135
|
-
outputSchema: z.object({
|
|
1136
|
-
complex: z.string(),
|
|
1137
|
-
nested: z.object({ value: z.number() }),
|
|
1138
|
-
}),
|
|
1139
|
-
// No transformToContent provided
|
|
1140
|
-
},
|
|
1141
|
-
],
|
|
1142
|
-
},
|
|
1143
|
-
];
|
|
1144
|
-
const mockToolCallResponse = {
|
|
1145
|
-
responseMessageDto: {
|
|
1146
|
-
id: "tool-call-1",
|
|
1147
|
-
content: [{ type: "text", text: "Tool call" }],
|
|
1148
|
-
role: "tool",
|
|
1149
|
-
threadId: "test-thread-1",
|
|
1150
|
-
toolCallRequest: {
|
|
1151
|
-
toolName: "no-transform-tool",
|
|
1152
|
-
parameters: [{ parameterName: "input", parameterValue: "test" }],
|
|
1153
|
-
},
|
|
1154
|
-
componentState: {},
|
|
1155
|
-
createdAt: new Date().toISOString(),
|
|
1156
|
-
},
|
|
1157
|
-
generationStage: GenerationStage.COMPLETE,
|
|
1158
|
-
mcpAccessToken: "test-mcp-access-token",
|
|
1159
|
-
};
|
|
1160
|
-
jest
|
|
1161
|
-
.mocked(mockThreadsApi.advanceByID)
|
|
1162
|
-
.mockResolvedValueOnce(mockToolCallResponse)
|
|
1163
|
-
.mockResolvedValueOnce({
|
|
1164
|
-
responseMessageDto: {
|
|
1165
|
-
id: "final-response",
|
|
1166
|
-
content: [{ type: "text", text: "Final response" }],
|
|
1167
|
-
role: "assistant",
|
|
1168
|
-
threadId: "test-thread-1",
|
|
1169
|
-
componentState: {},
|
|
1170
|
-
createdAt: new Date().toISOString(),
|
|
1171
|
-
},
|
|
1172
|
-
generationStage: GenerationStage.COMPLETE,
|
|
1173
|
-
mcpAccessToken: "test-mcp-access-token",
|
|
1174
|
-
});
|
|
1175
|
-
const { result } = renderHook(() => useTamboThread(), {
|
|
1176
|
-
wrapper: createWrapper({ components: toolWithoutTransform }),
|
|
1177
|
-
});
|
|
1178
|
-
await act(async () => {
|
|
1179
|
-
await result.current.sendThreadMessage("Use tool without transform", {
|
|
1180
|
-
threadId: "test-thread-1",
|
|
1181
|
-
streamResponse: false,
|
|
1182
|
-
});
|
|
1183
|
-
});
|
|
1184
|
-
// Verify the tool was called with single object arg (new inputSchema interface)
|
|
1185
|
-
expect(toolWithoutTransform[0]?.associatedTools?.[0]?.tool).toHaveBeenCalledWith({ input: "test" });
|
|
1186
|
-
// Verify the second advance call used stringified content
|
|
1187
|
-
expect(mockThreadsApi.advanceByID).toHaveBeenLastCalledWith("test-thread-1", expect.objectContaining({
|
|
1188
|
-
messageToAppend: expect.objectContaining({
|
|
1189
|
-
content: [
|
|
1190
|
-
{
|
|
1191
|
-
type: "text",
|
|
1192
|
-
text: '{"complex":"data","nested":{"value":42}}',
|
|
1193
|
-
},
|
|
1194
|
-
],
|
|
1195
|
-
role: "tool",
|
|
1196
|
-
}),
|
|
1197
|
-
}));
|
|
1198
|
-
});
|
|
1199
|
-
it("should always return text for error responses even with transformToContent", async () => {
|
|
1200
|
-
const mockTransformToContent = jest.fn().mockReturnValue([
|
|
1201
|
-
{
|
|
1202
|
-
type: "image_url",
|
|
1203
|
-
image_url: { url: "https://example.com/error.png" },
|
|
1204
|
-
},
|
|
1205
|
-
]);
|
|
1206
|
-
const toolWithTransform = [
|
|
1207
|
-
{
|
|
1208
|
-
name: "TestComponent",
|
|
1209
|
-
component: () => React.createElement("div", null, "Test"),
|
|
1210
|
-
description: "Test",
|
|
1211
|
-
propsSchema: z.object({ test: z.string() }),
|
|
1212
|
-
associatedTools: [
|
|
1213
|
-
{
|
|
1214
|
-
name: "error-tool",
|
|
1215
|
-
tool: jest
|
|
1216
|
-
.fn()
|
|
1217
|
-
.mockRejectedValue(new Error("Tool execution failed")),
|
|
1218
|
-
description: "Tool that errors",
|
|
1219
|
-
inputSchema: z.object({ input: z.string() }),
|
|
1220
|
-
outputSchema: z.string(),
|
|
1221
|
-
transformToContent: mockTransformToContent,
|
|
1222
|
-
},
|
|
1223
|
-
],
|
|
1224
|
-
},
|
|
1225
|
-
];
|
|
1226
|
-
const mockToolCallResponse = {
|
|
1227
|
-
responseMessageDto: {
|
|
1228
|
-
id: "tool-call-1",
|
|
1229
|
-
content: [{ type: "text", text: "Tool call" }],
|
|
1230
|
-
role: "tool",
|
|
1231
|
-
threadId: "test-thread-1",
|
|
1232
|
-
toolCallRequest: {
|
|
1233
|
-
toolName: "error-tool",
|
|
1234
|
-
parameters: [{ parameterName: "input", parameterValue: "test" }],
|
|
1235
|
-
},
|
|
1236
|
-
componentState: {},
|
|
1237
|
-
createdAt: new Date().toISOString(),
|
|
1238
|
-
},
|
|
1239
|
-
generationStage: GenerationStage.COMPLETE,
|
|
1240
|
-
mcpAccessToken: "test-mcp-access-token",
|
|
1241
|
-
};
|
|
1242
|
-
jest
|
|
1243
|
-
.mocked(mockThreadsApi.advanceByID)
|
|
1244
|
-
.mockResolvedValueOnce(mockToolCallResponse)
|
|
1245
|
-
.mockResolvedValueOnce({
|
|
1246
|
-
responseMessageDto: {
|
|
1247
|
-
id: "final-response",
|
|
1248
|
-
content: [{ type: "text", text: "Final response" }],
|
|
1249
|
-
role: "assistant",
|
|
1250
|
-
threadId: "test-thread-1",
|
|
1251
|
-
componentState: {},
|
|
1252
|
-
createdAt: new Date().toISOString(),
|
|
1253
|
-
},
|
|
1254
|
-
generationStage: GenerationStage.COMPLETE,
|
|
1255
|
-
mcpAccessToken: "test-mcp-access-token",
|
|
1256
|
-
});
|
|
1257
|
-
const { result } = renderHook(() => useTamboThread(), {
|
|
1258
|
-
wrapper: createWrapper({ components: toolWithTransform }),
|
|
1259
|
-
});
|
|
1260
|
-
await act(async () => {
|
|
1261
|
-
await result.current.sendThreadMessage("Use error tool", {
|
|
1262
|
-
threadId: "test-thread-1",
|
|
1263
|
-
streamResponse: false,
|
|
1264
|
-
});
|
|
1265
|
-
});
|
|
1266
|
-
// Verify the tool was called with single object arg (new inputSchema interface)
|
|
1267
|
-
expect(toolWithTransform[0]?.associatedTools?.[0]?.tool).toHaveBeenCalledWith({ input: "test" });
|
|
1268
|
-
// Verify transformToContent was NOT called for error responses
|
|
1269
|
-
expect(mockTransformToContent).not.toHaveBeenCalled();
|
|
1270
|
-
// Verify the second advance call used text content with the error message
|
|
1271
|
-
expect(mockThreadsApi.advanceByID).toHaveBeenLastCalledWith("test-thread-1", expect.objectContaining({
|
|
1272
|
-
messageToAppend: expect.objectContaining({
|
|
1273
|
-
content: [
|
|
1274
|
-
expect.objectContaining({
|
|
1275
|
-
type: "text",
|
|
1276
|
-
// Error message should be in text format
|
|
1277
|
-
}),
|
|
1278
|
-
],
|
|
1279
|
-
role: "tool",
|
|
1280
|
-
}),
|
|
1281
|
-
}));
|
|
1282
|
-
});
|
|
1283
1016
|
});
|
|
1284
1017
|
describe("tamboStreamableHint streaming behavior", () => {
|
|
1285
1018
|
it("should call streamable tool during streaming when tamboStreamableHint is true", async () => {
|
|
@@ -1612,6 +1345,25 @@ describe("TamboThreadProvider", () => {
|
|
|
1612
1345
|
});
|
|
1613
1346
|
describe("auto-generate thread name", () => {
|
|
1614
1347
|
it("should auto-generate thread name after reaching threshold", async () => {
|
|
1348
|
+
const mockStreamResponse = {
|
|
1349
|
+
responseMessageDto: {
|
|
1350
|
+
id: "response-1",
|
|
1351
|
+
content: [{ type: "text", text: "Response" }],
|
|
1352
|
+
role: "assistant",
|
|
1353
|
+
threadId: "test-thread-1",
|
|
1354
|
+
component: undefined,
|
|
1355
|
+
componentState: {},
|
|
1356
|
+
createdAt: new Date().toISOString(),
|
|
1357
|
+
},
|
|
1358
|
+
generationStage: GenerationStage.COMPLETE,
|
|
1359
|
+
mcpAccessToken: "test-mcp-access-token",
|
|
1360
|
+
};
|
|
1361
|
+
const mockAsyncIterator = {
|
|
1362
|
+
[Symbol.asyncIterator]: async function* () {
|
|
1363
|
+
yield mockStreamResponse;
|
|
1364
|
+
},
|
|
1365
|
+
};
|
|
1366
|
+
jest.mocked(advanceStream).mockResolvedValue(mockAsyncIterator);
|
|
1615
1367
|
const { result } = renderHook(() => useTamboThread(), {
|
|
1616
1368
|
wrapper: createWrapper({ autoGenerateNameThreshold: 2 }),
|
|
1617
1369
|
});
|
|
@@ -1643,13 +1395,34 @@ describe("TamboThreadProvider", () => {
|
|
|
1643
1395
|
}), false);
|
|
1644
1396
|
});
|
|
1645
1397
|
await act(async () => {
|
|
1646
|
-
await result.current.sendThreadMessage("Test message"
|
|
1398
|
+
await result.current.sendThreadMessage("Test message", {
|
|
1399
|
+
streamResponse: true,
|
|
1400
|
+
});
|
|
1647
1401
|
});
|
|
1648
1402
|
expect(mockThreadsApi.generateName).toHaveBeenCalledWith("test-thread-1");
|
|
1649
1403
|
expect(result.current.thread.name).toBe("Generated Thread Name");
|
|
1650
1404
|
expect(mockQueryClient.setQueryData).toHaveBeenCalledWith(["threads", "test-project-id", undefined], expect.any(Function));
|
|
1651
1405
|
});
|
|
1652
1406
|
it("should NOT auto-generate when autoGenerateThreadName is false", async () => {
|
|
1407
|
+
const mockStreamResponse = {
|
|
1408
|
+
responseMessageDto: {
|
|
1409
|
+
id: "response-1",
|
|
1410
|
+
content: [{ type: "text", text: "Response" }],
|
|
1411
|
+
role: "assistant",
|
|
1412
|
+
threadId: "test-thread-1",
|
|
1413
|
+
component: undefined,
|
|
1414
|
+
componentState: {},
|
|
1415
|
+
createdAt: new Date().toISOString(),
|
|
1416
|
+
},
|
|
1417
|
+
generationStage: GenerationStage.COMPLETE,
|
|
1418
|
+
mcpAccessToken: "test-mcp-access-token",
|
|
1419
|
+
};
|
|
1420
|
+
const mockAsyncIterator = {
|
|
1421
|
+
[Symbol.asyncIterator]: async function* () {
|
|
1422
|
+
yield mockStreamResponse;
|
|
1423
|
+
},
|
|
1424
|
+
};
|
|
1425
|
+
jest.mocked(advanceStream).mockResolvedValue(mockAsyncIterator);
|
|
1653
1426
|
const { result } = renderHook(() => useTamboThread(), {
|
|
1654
1427
|
wrapper: createWrapper({
|
|
1655
1428
|
autoGenerateThreadName: false,
|
|
@@ -1681,12 +1454,33 @@ describe("TamboThreadProvider", () => {
|
|
|
1681
1454
|
}), false);
|
|
1682
1455
|
});
|
|
1683
1456
|
await act(async () => {
|
|
1684
|
-
await result.current.sendThreadMessage("Test message"
|
|
1457
|
+
await result.current.sendThreadMessage("Test message", {
|
|
1458
|
+
streamResponse: true,
|
|
1459
|
+
});
|
|
1685
1460
|
});
|
|
1686
1461
|
// Should NOT generate name because feature is disabled
|
|
1687
1462
|
expect(mockThreadsApi.generateName).not.toHaveBeenCalled();
|
|
1688
1463
|
});
|
|
1689
1464
|
it("should NOT auto-generate when thread already has a name", async () => {
|
|
1465
|
+
const mockStreamResponse = {
|
|
1466
|
+
responseMessageDto: {
|
|
1467
|
+
id: "response-1",
|
|
1468
|
+
content: [{ type: "text", text: "Response" }],
|
|
1469
|
+
role: "assistant",
|
|
1470
|
+
threadId: "test-thread-1",
|
|
1471
|
+
component: undefined,
|
|
1472
|
+
componentState: {},
|
|
1473
|
+
createdAt: new Date().toISOString(),
|
|
1474
|
+
},
|
|
1475
|
+
generationStage: GenerationStage.COMPLETE,
|
|
1476
|
+
mcpAccessToken: "test-mcp-access-token",
|
|
1477
|
+
};
|
|
1478
|
+
const mockAsyncIterator = {
|
|
1479
|
+
[Symbol.asyncIterator]: async function* () {
|
|
1480
|
+
yield mockStreamResponse;
|
|
1481
|
+
},
|
|
1482
|
+
};
|
|
1483
|
+
jest.mocked(advanceStream).mockResolvedValue(mockAsyncIterator);
|
|
1690
1484
|
const { result } = renderHook(() => useTamboThread(), {
|
|
1691
1485
|
wrapper: createWrapper({ autoGenerateNameThreshold: 2 }),
|
|
1692
1486
|
});
|
|
@@ -1720,7 +1514,9 @@ describe("TamboThreadProvider", () => {
|
|
|
1720
1514
|
expect(result.current.thread.messages).toHaveLength(2);
|
|
1721
1515
|
// Send another message to reach threshold (3 messages total)
|
|
1722
1516
|
await act(async () => {
|
|
1723
|
-
await result.current.sendThreadMessage("Test message"
|
|
1517
|
+
await result.current.sendThreadMessage("Test message", {
|
|
1518
|
+
streamResponse: true,
|
|
1519
|
+
});
|
|
1724
1520
|
});
|
|
1725
1521
|
// Should NOT generate name because thread already has one
|
|
1726
1522
|
expect(mockThreadsApi.generateName).not.toHaveBeenCalled();
|