@tambo-ai/react 0.70.0 → 0.72.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/v1/hooks/use-tambo-v1-component-state.d.ts +44 -0
- package/dist/v1/hooks/use-tambo-v1-component-state.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-component-state.js +134 -0
- package/dist/v1/hooks/use-tambo-v1-component-state.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-component-state.test.d.ts +2 -0
- package/dist/v1/hooks/use-tambo-v1-component-state.test.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-component-state.test.js +292 -0
- package/dist/v1/hooks/use-tambo-v1-component-state.test.js.map +1 -0
- 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-input.d.ts +62 -0
- package/dist/v1/hooks/use-tambo-v1-thread-input.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread-input.js +76 -0
- package/dist/v1/hooks/use-tambo-v1-thread-input.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread-input.test.d.ts +2 -0
- package/dist/v1/hooks/use-tambo-v1-thread-input.test.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread-input.test.js +168 -0
- package/dist/v1/hooks/use-tambo-v1-thread-input.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 +65 -16
- package/dist/v1/index.d.ts.map +1 -1
- package/dist/v1/index.js +119 -26
- package/dist/v1/index.js.map +1 -1
- package/dist/v1/providers/tambo-v1-provider.d.ts +133 -0
- package/dist/v1/providers/tambo-v1-provider.d.ts.map +1 -0
- package/dist/v1/providers/tambo-v1-provider.js +131 -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 +181 -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 +5 -2
- package/dist/v1/types/component.d.ts.map +1 -1
- package/dist/v1/types/component.js +5 -2
- package/dist/v1/types/component.js.map +1 -1
- package/dist/v1/types/event.d.ts +21 -12
- package/dist/v1/types/event.d.ts.map +1 -1
- package/dist/v1/types/event.js +46 -1
- package/dist/v1/types/event.js.map +1 -1
- 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 +30 -9
- package/dist/v1/types/message.d.ts.map +1 -1
- package/dist/v1/types/message.js +1 -1
- package/dist/v1/types/message.js.map +1 -1
- package/dist/v1/types/thread.d.ts +1 -3
- package/dist/v1/types/thread.d.ts.map +1 -1
- package/dist/v1/types/thread.js +1 -1
- package/dist/v1/types/thread.js.map +1 -1
- package/dist/v1/utils/component-renderer.d.ts +89 -0
- package/dist/v1/utils/component-renderer.d.ts.map +1 -0
- package/dist/v1/utils/component-renderer.js +216 -0
- package/dist/v1/utils/component-renderer.js.map +1 -0
- package/dist/v1/utils/component-renderer.test.d.ts +2 -0
- package/dist/v1/utils/component-renderer.test.d.ts.map +1 -0
- package/dist/v1/utils/component-renderer.test.js +380 -0
- package/dist/v1/utils/component-renderer.test.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 +735 -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 +1205 -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/v1/hooks/use-tambo-v1-component-state.d.ts +44 -0
- package/esm/v1/hooks/use-tambo-v1-component-state.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-component-state.js +131 -0
- package/esm/v1/hooks/use-tambo-v1-component-state.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-component-state.test.d.ts +2 -0
- package/esm/v1/hooks/use-tambo-v1-component-state.test.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-component-state.test.js +290 -0
- package/esm/v1/hooks/use-tambo-v1-component-state.test.js.map +1 -0
- 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-input.d.ts +62 -0
- package/esm/v1/hooks/use-tambo-v1-thread-input.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread-input.js +73 -0
- package/esm/v1/hooks/use-tambo-v1-thread-input.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread-input.test.d.ts +2 -0
- package/esm/v1/hooks/use-tambo-v1-thread-input.test.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread-input.test.js +166 -0
- package/esm/v1/hooks/use-tambo-v1-thread-input.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 +65 -16
- package/esm/v1/index.d.ts.map +1 -1
- package/esm/v1/index.js +83 -27
- package/esm/v1/index.js.map +1 -1
- package/esm/v1/providers/tambo-v1-provider.d.ts +133 -0
- package/esm/v1/providers/tambo-v1-provider.d.ts.map +1 -0
- package/esm/v1/providers/tambo-v1-provider.js +94 -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 +176 -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 +5 -2
- package/esm/v1/types/component.d.ts.map +1 -1
- package/esm/v1/types/component.js +5 -2
- package/esm/v1/types/component.js.map +1 -1
- package/esm/v1/types/event.d.ts +21 -12
- package/esm/v1/types/event.d.ts.map +1 -1
- package/esm/v1/types/event.js +44 -2
- package/esm/v1/types/event.js.map +1 -1
- 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 +30 -9
- package/esm/v1/types/message.d.ts.map +1 -1
- package/esm/v1/types/message.js +1 -1
- package/esm/v1/types/message.js.map +1 -1
- package/esm/v1/types/thread.d.ts +1 -3
- package/esm/v1/types/thread.d.ts.map +1 -1
- package/esm/v1/types/thread.js +1 -1
- package/esm/v1/types/thread.js.map +1 -1
- package/esm/v1/utils/component-renderer.d.ts +89 -0
- package/esm/v1/utils/component-renderer.d.ts.map +1 -0
- package/esm/v1/utils/component-renderer.js +175 -0
- package/esm/v1/utils/component-renderer.js.map +1 -0
- package/esm/v1/utils/component-renderer.test.d.ts +2 -0
- package/esm/v1/utils/component-renderer.test.d.ts.map +1 -0
- package/esm/v1/utils/component-renderer.test.js +375 -0
- package/esm/v1/utils/component-renderer.test.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 +728 -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 +1203 -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 +7 -6
- package/dist/v1/types/tool.d.ts +0 -52
- package/dist/v1/types/tool.d.ts.map +0 -1
- package/dist/v1/types/tool.js +0 -11
- package/dist/v1/types/tool.js.map +0 -1
- package/esm/v1/types/tool.d.ts +0 -52
- package/esm/v1/types/tool.d.ts.map +0 -1
- package/esm/v1/types/tool.js +0 -10
- package/esm/v1/types/tool.js.map +0 -1
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
// React import needed for JSX transform (jsxImportSource is not set to react-jsx)
|
|
2
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { render, screen } from "@testing-library/react";
|
|
5
|
+
import { isComponentContent, renderComponentContent, renderMessageContent, renderMessageComponents, useV1ComponentContent, useV1ComponentContentOptional, } from "./component-renderer";
|
|
6
|
+
// Test component that displays its props
|
|
7
|
+
function TestComponent({ title, count }) {
|
|
8
|
+
return (React.createElement("div", { "data-testid": "test-component" },
|
|
9
|
+
React.createElement("span", { "data-testid": "title" }, title),
|
|
10
|
+
React.createElement("span", { "data-testid": "count" }, count)));
|
|
11
|
+
}
|
|
12
|
+
// Test loading component
|
|
13
|
+
function TestLoadingComponent() {
|
|
14
|
+
return React.createElement("div", { "data-testid": "loading" }, "Loading...");
|
|
15
|
+
}
|
|
16
|
+
// Test component that uses the content context
|
|
17
|
+
function ContextAwareComponent() {
|
|
18
|
+
const context = useV1ComponentContent();
|
|
19
|
+
return (React.createElement("div", { "data-testid": "context-aware" },
|
|
20
|
+
React.createElement("span", { "data-testid": "componentId" }, context.componentId),
|
|
21
|
+
React.createElement("span", { "data-testid": "threadId" }, context.threadId),
|
|
22
|
+
React.createElement("span", { "data-testid": "messageId" }, context.messageId)));
|
|
23
|
+
}
|
|
24
|
+
const mockRegistry = {
|
|
25
|
+
TestComponent: {
|
|
26
|
+
component: TestComponent,
|
|
27
|
+
name: "TestComponent",
|
|
28
|
+
description: "A test component",
|
|
29
|
+
props: {},
|
|
30
|
+
contextTools: [],
|
|
31
|
+
},
|
|
32
|
+
TestWithLoading: {
|
|
33
|
+
component: TestComponent,
|
|
34
|
+
loadingComponent: TestLoadingComponent,
|
|
35
|
+
name: "TestWithLoading",
|
|
36
|
+
description: "A test component with loading state",
|
|
37
|
+
props: {},
|
|
38
|
+
contextTools: [],
|
|
39
|
+
},
|
|
40
|
+
ContextAware: {
|
|
41
|
+
component: ContextAwareComponent,
|
|
42
|
+
name: "ContextAware",
|
|
43
|
+
description: "A context-aware component",
|
|
44
|
+
props: {},
|
|
45
|
+
contextTools: [],
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
describe("isComponentContent", () => {
|
|
49
|
+
it("returns true for component content", () => {
|
|
50
|
+
const content = {
|
|
51
|
+
type: "component",
|
|
52
|
+
id: "comp_1",
|
|
53
|
+
name: "Test",
|
|
54
|
+
props: {},
|
|
55
|
+
streamingState: "done",
|
|
56
|
+
};
|
|
57
|
+
expect(isComponentContent(content)).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
it("returns false for text content", () => {
|
|
60
|
+
const content = { type: "text", text: "hello" };
|
|
61
|
+
expect(isComponentContent(content)).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
describe("renderComponentContent", () => {
|
|
65
|
+
it("renders a component from registry", () => {
|
|
66
|
+
const content = {
|
|
67
|
+
type: "component",
|
|
68
|
+
id: "comp_1",
|
|
69
|
+
name: "TestComponent",
|
|
70
|
+
props: { title: "Hello", count: 42 },
|
|
71
|
+
streamingState: "done",
|
|
72
|
+
};
|
|
73
|
+
const result = renderComponentContent(content, {
|
|
74
|
+
threadId: "thread_1",
|
|
75
|
+
messageId: "msg_1",
|
|
76
|
+
componentList: mockRegistry,
|
|
77
|
+
});
|
|
78
|
+
expect(result.renderedComponent).not.toBeNull();
|
|
79
|
+
// Render and check output
|
|
80
|
+
render(React.createElement(React.Fragment, null, result.renderedComponent));
|
|
81
|
+
expect(screen.getByTestId("title")).toHaveTextContent("Hello");
|
|
82
|
+
expect(screen.getByTestId("count")).toHaveTextContent("42");
|
|
83
|
+
});
|
|
84
|
+
it("returns null renderedComponent for unknown component", () => {
|
|
85
|
+
const content = {
|
|
86
|
+
type: "component",
|
|
87
|
+
id: "comp_1",
|
|
88
|
+
name: "UnknownComponent",
|
|
89
|
+
props: {},
|
|
90
|
+
streamingState: "done",
|
|
91
|
+
};
|
|
92
|
+
const consoleWarn = jest.spyOn(console, "warn").mockImplementation();
|
|
93
|
+
const result = renderComponentContent(content, {
|
|
94
|
+
threadId: "thread_1",
|
|
95
|
+
messageId: "msg_1",
|
|
96
|
+
componentList: mockRegistry,
|
|
97
|
+
});
|
|
98
|
+
expect(result.renderedComponent).toBeNull();
|
|
99
|
+
expect(consoleWarn).toHaveBeenCalledWith('Component "UnknownComponent" not found in registry');
|
|
100
|
+
consoleWarn.mockRestore();
|
|
101
|
+
});
|
|
102
|
+
it("shows loading component when streaming", () => {
|
|
103
|
+
const content = {
|
|
104
|
+
type: "component",
|
|
105
|
+
id: "comp_1",
|
|
106
|
+
name: "TestWithLoading",
|
|
107
|
+
props: { title: "Loading Test", count: 0 },
|
|
108
|
+
streamingState: "streaming",
|
|
109
|
+
};
|
|
110
|
+
const result = renderComponentContent(content, {
|
|
111
|
+
threadId: "thread_1",
|
|
112
|
+
messageId: "msg_1",
|
|
113
|
+
componentList: mockRegistry,
|
|
114
|
+
});
|
|
115
|
+
render(React.createElement(React.Fragment, null, result.renderedComponent));
|
|
116
|
+
expect(screen.getByTestId("loading")).toHaveTextContent("Loading...");
|
|
117
|
+
});
|
|
118
|
+
it("shows main component when done streaming", () => {
|
|
119
|
+
const content = {
|
|
120
|
+
type: "component",
|
|
121
|
+
id: "comp_1",
|
|
122
|
+
name: "TestWithLoading",
|
|
123
|
+
props: { title: "Done Test", count: 99 },
|
|
124
|
+
streamingState: "done",
|
|
125
|
+
};
|
|
126
|
+
const result = renderComponentContent(content, {
|
|
127
|
+
threadId: "thread_1",
|
|
128
|
+
messageId: "msg_1",
|
|
129
|
+
componentList: mockRegistry,
|
|
130
|
+
});
|
|
131
|
+
render(React.createElement(React.Fragment, null, result.renderedComponent));
|
|
132
|
+
expect(screen.getByTestId("title")).toHaveTextContent("Done Test");
|
|
133
|
+
expect(screen.getByTestId("count")).toHaveTextContent("99");
|
|
134
|
+
});
|
|
135
|
+
it("provides component context to rendered components", () => {
|
|
136
|
+
const content = {
|
|
137
|
+
type: "component",
|
|
138
|
+
id: "comp_123",
|
|
139
|
+
name: "ContextAware",
|
|
140
|
+
props: {},
|
|
141
|
+
streamingState: "done",
|
|
142
|
+
};
|
|
143
|
+
const result = renderComponentContent(content, {
|
|
144
|
+
threadId: "thread_456",
|
|
145
|
+
messageId: "msg_789",
|
|
146
|
+
componentList: mockRegistry,
|
|
147
|
+
});
|
|
148
|
+
render(React.createElement(React.Fragment, null, result.renderedComponent));
|
|
149
|
+
expect(screen.getByTestId("componentId")).toHaveTextContent("comp_123");
|
|
150
|
+
expect(screen.getByTestId("threadId")).toHaveTextContent("thread_456");
|
|
151
|
+
expect(screen.getByTestId("messageId")).toHaveTextContent("msg_789");
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
describe("renderMessageContent", () => {
|
|
155
|
+
it("renders component content blocks and passes through others", () => {
|
|
156
|
+
const content = [
|
|
157
|
+
{ type: "text", text: "Hello" },
|
|
158
|
+
{
|
|
159
|
+
type: "component",
|
|
160
|
+
id: "comp_1",
|
|
161
|
+
name: "TestComponent",
|
|
162
|
+
props: { title: "Test", count: 1 },
|
|
163
|
+
streamingState: "done",
|
|
164
|
+
},
|
|
165
|
+
{ type: "text", text: "World" },
|
|
166
|
+
];
|
|
167
|
+
const result = renderMessageContent(content, {
|
|
168
|
+
threadId: "thread_1",
|
|
169
|
+
messageId: "msg_1",
|
|
170
|
+
componentList: mockRegistry,
|
|
171
|
+
});
|
|
172
|
+
expect(result).toHaveLength(3);
|
|
173
|
+
expect(result[0]).toEqual({ type: "text", text: "Hello" });
|
|
174
|
+
expect(result[1].renderedComponent).not.toBeNull();
|
|
175
|
+
expect(result[2]).toEqual({ type: "text", text: "World" });
|
|
176
|
+
});
|
|
177
|
+
it("handles multiple component blocks in a message", () => {
|
|
178
|
+
const content = [
|
|
179
|
+
{
|
|
180
|
+
type: "component",
|
|
181
|
+
id: "comp_1",
|
|
182
|
+
name: "TestComponent",
|
|
183
|
+
props: { title: "First", count: 1 },
|
|
184
|
+
streamingState: "done",
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
type: "component",
|
|
188
|
+
id: "comp_2",
|
|
189
|
+
name: "TestComponent",
|
|
190
|
+
props: { title: "Second", count: 2 },
|
|
191
|
+
streamingState: "done",
|
|
192
|
+
},
|
|
193
|
+
];
|
|
194
|
+
const result = renderMessageContent(content, {
|
|
195
|
+
threadId: "thread_1",
|
|
196
|
+
messageId: "msg_1",
|
|
197
|
+
componentList: mockRegistry,
|
|
198
|
+
});
|
|
199
|
+
expect(result).toHaveLength(2);
|
|
200
|
+
expect(result[0].renderedComponent).not.toBeNull();
|
|
201
|
+
expect(result[1].renderedComponent).not.toBeNull();
|
|
202
|
+
});
|
|
203
|
+
it("handles tool_use content blocks unchanged", () => {
|
|
204
|
+
const content = [
|
|
205
|
+
{
|
|
206
|
+
type: "tool_use",
|
|
207
|
+
id: "tool_1",
|
|
208
|
+
name: "search",
|
|
209
|
+
input: { query: "test" },
|
|
210
|
+
},
|
|
211
|
+
];
|
|
212
|
+
const result = renderMessageContent(content, {
|
|
213
|
+
threadId: "thread_1",
|
|
214
|
+
messageId: "msg_1",
|
|
215
|
+
componentList: mockRegistry,
|
|
216
|
+
});
|
|
217
|
+
expect(result).toHaveLength(1);
|
|
218
|
+
expect(result[0]).toEqual(content[0]);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
describe("renderComponentContent edge cases", () => {
|
|
222
|
+
it("shows main component when streamingState is 'started' (no loading component)", () => {
|
|
223
|
+
const content = {
|
|
224
|
+
type: "component",
|
|
225
|
+
id: "comp_1",
|
|
226
|
+
name: "TestComponent",
|
|
227
|
+
props: { title: "Started", count: 0 },
|
|
228
|
+
streamingState: "started",
|
|
229
|
+
};
|
|
230
|
+
const result = renderComponentContent(content, {
|
|
231
|
+
threadId: "thread_1",
|
|
232
|
+
messageId: "msg_1",
|
|
233
|
+
componentList: mockRegistry,
|
|
234
|
+
});
|
|
235
|
+
// TestComponent has no loading component, so it shows main even when streaming
|
|
236
|
+
render(React.createElement(React.Fragment, null, result.renderedComponent));
|
|
237
|
+
expect(screen.getByTestId("title")).toHaveTextContent("Started");
|
|
238
|
+
});
|
|
239
|
+
it("shows loading component when streamingState is 'started' and loading exists", () => {
|
|
240
|
+
const content = {
|
|
241
|
+
type: "component",
|
|
242
|
+
id: "comp_1",
|
|
243
|
+
name: "TestWithLoading",
|
|
244
|
+
props: { title: "Started", count: 0 },
|
|
245
|
+
streamingState: "started",
|
|
246
|
+
};
|
|
247
|
+
const result = renderComponentContent(content, {
|
|
248
|
+
threadId: "thread_1",
|
|
249
|
+
messageId: "msg_1",
|
|
250
|
+
componentList: mockRegistry,
|
|
251
|
+
});
|
|
252
|
+
render(React.createElement(React.Fragment, null, result.renderedComponent));
|
|
253
|
+
expect(screen.getByTestId("loading")).toHaveTextContent("Loading...");
|
|
254
|
+
});
|
|
255
|
+
it("passes state as initialState prop", () => {
|
|
256
|
+
// Component that reads initialState
|
|
257
|
+
function StatefulComponent({ initialState, }) {
|
|
258
|
+
return (React.createElement("div", { "data-testid": "initial-count" }, initialState?.count ?? "none"));
|
|
259
|
+
}
|
|
260
|
+
const registryWithStateful = {
|
|
261
|
+
StatefulComponent: {
|
|
262
|
+
component: StatefulComponent,
|
|
263
|
+
name: "StatefulComponent",
|
|
264
|
+
description: "A stateful component",
|
|
265
|
+
props: {},
|
|
266
|
+
contextTools: [],
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
const content = {
|
|
270
|
+
type: "component",
|
|
271
|
+
id: "comp_1",
|
|
272
|
+
name: "StatefulComponent",
|
|
273
|
+
props: {},
|
|
274
|
+
state: { count: 42 },
|
|
275
|
+
streamingState: "done",
|
|
276
|
+
};
|
|
277
|
+
const result = renderComponentContent(content, {
|
|
278
|
+
threadId: "thread_1",
|
|
279
|
+
messageId: "msg_1",
|
|
280
|
+
componentList: registryWithStateful,
|
|
281
|
+
});
|
|
282
|
+
render(React.createElement(React.Fragment, null, result.renderedComponent));
|
|
283
|
+
expect(screen.getByTestId("initial-count")).toHaveTextContent("42");
|
|
284
|
+
});
|
|
285
|
+
it("preserves original content properties in returned object", () => {
|
|
286
|
+
const content = {
|
|
287
|
+
type: "component",
|
|
288
|
+
id: "comp_custom",
|
|
289
|
+
name: "TestComponent",
|
|
290
|
+
props: { title: "Test", count: 5 },
|
|
291
|
+
state: { value: "preserved" },
|
|
292
|
+
streamingState: "done",
|
|
293
|
+
};
|
|
294
|
+
const result = renderComponentContent(content, {
|
|
295
|
+
threadId: "thread_1",
|
|
296
|
+
messageId: "msg_1",
|
|
297
|
+
componentList: mockRegistry,
|
|
298
|
+
});
|
|
299
|
+
expect(result.id).toBe("comp_custom");
|
|
300
|
+
expect(result.name).toBe("TestComponent");
|
|
301
|
+
expect(result.props).toEqual({ title: "Test", count: 5 });
|
|
302
|
+
expect(result.state).toEqual({ value: "preserved" });
|
|
303
|
+
expect(result.streamingState).toBe("done");
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
describe("useV1ComponentContentOptional", () => {
|
|
307
|
+
it("returns null when used outside rendered component", () => {
|
|
308
|
+
function TestConsumer() {
|
|
309
|
+
const context = useV1ComponentContentOptional();
|
|
310
|
+
return (React.createElement("div", { "data-testid": "context" }, context ? "has context" : "no context"));
|
|
311
|
+
}
|
|
312
|
+
render(React.createElement(TestConsumer, null));
|
|
313
|
+
expect(screen.getByTestId("context")).toHaveTextContent("no context");
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
describe("renderMessageComponents", () => {
|
|
317
|
+
it("renders all components in a message", () => {
|
|
318
|
+
const message = {
|
|
319
|
+
id: "msg_1",
|
|
320
|
+
role: "assistant",
|
|
321
|
+
content: [
|
|
322
|
+
{ type: "text", text: "Here is the weather:" },
|
|
323
|
+
{
|
|
324
|
+
type: "component",
|
|
325
|
+
id: "comp_1",
|
|
326
|
+
name: "TestComponent",
|
|
327
|
+
props: { title: "Weather", count: 72 },
|
|
328
|
+
streamingState: "done",
|
|
329
|
+
},
|
|
330
|
+
],
|
|
331
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
332
|
+
};
|
|
333
|
+
const result = renderMessageComponents(message, {
|
|
334
|
+
threadId: "thread_1",
|
|
335
|
+
componentList: mockRegistry,
|
|
336
|
+
});
|
|
337
|
+
expect(result.id).toBe("msg_1");
|
|
338
|
+
expect(result.content).toHaveLength(2);
|
|
339
|
+
expect(result.content[0]).toEqual({
|
|
340
|
+
type: "text",
|
|
341
|
+
text: "Here is the weather:",
|
|
342
|
+
});
|
|
343
|
+
expect(result.content[1].renderedComponent).not.toBeNull();
|
|
344
|
+
});
|
|
345
|
+
it("preserves message metadata", () => {
|
|
346
|
+
const message = {
|
|
347
|
+
id: "msg_123",
|
|
348
|
+
role: "assistant",
|
|
349
|
+
content: [],
|
|
350
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
351
|
+
metadata: { custom: "value" },
|
|
352
|
+
};
|
|
353
|
+
const result = renderMessageComponents(message, {
|
|
354
|
+
threadId: "thread_1",
|
|
355
|
+
componentList: mockRegistry,
|
|
356
|
+
});
|
|
357
|
+
expect(result.id).toBe("msg_123");
|
|
358
|
+
expect(result.role).toBe("assistant");
|
|
359
|
+
expect(result.createdAt).toBe("2024-01-01T00:00:00.000Z");
|
|
360
|
+
expect(result.metadata).toEqual({ custom: "value" });
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
describe("useV1ComponentContent error handling", () => {
|
|
364
|
+
it("throws when used outside rendered component", () => {
|
|
365
|
+
function TestConsumer() {
|
|
366
|
+
useV1ComponentContent();
|
|
367
|
+
return React.createElement("div", null, "Should not render");
|
|
368
|
+
}
|
|
369
|
+
// Suppress React error boundary logs
|
|
370
|
+
const consoleSpy = jest.spyOn(console, "error").mockImplementation();
|
|
371
|
+
expect(() => render(React.createElement(TestConsumer, null))).toThrow("useV1ComponentContent must be used within a rendered component");
|
|
372
|
+
consoleSpy.mockRestore();
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
//# sourceMappingURL=component-renderer.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-renderer.test.js","sourceRoot":"","sources":["../../../src/v1/utils/component-renderer.test.tsx"],"names":[],"mappings":"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","sourcesContent":["// React import needed for JSX transform (jsxImportSource is not set to react-jsx)\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nimport React from \"react\";\nimport { render, screen } from \"@testing-library/react\";\nimport type { ComponentRegistry } from \"../../model/component-metadata\";\nimport type { V1ComponentContent } from \"../types/message\";\nimport {\n isComponentContent,\n renderComponentContent,\n renderMessageContent,\n renderMessageComponents,\n useV1ComponentContent,\n useV1ComponentContentOptional,\n} from \"./component-renderer\";\n\n// Test component that displays its props\nfunction TestComponent({ title, count }: { title: string; count: number }) {\n return (\n <div data-testid=\"test-component\">\n <span data-testid=\"title\">{title}</span>\n <span data-testid=\"count\">{count}</span>\n </div>\n );\n}\n\n// Test loading component\nfunction TestLoadingComponent() {\n return <div data-testid=\"loading\">Loading...</div>;\n}\n\n// Test component that uses the content context\nfunction ContextAwareComponent() {\n const context = useV1ComponentContent();\n return (\n <div data-testid=\"context-aware\">\n <span data-testid=\"componentId\">{context.componentId}</span>\n <span data-testid=\"threadId\">{context.threadId}</span>\n <span data-testid=\"messageId\">{context.messageId}</span>\n </div>\n );\n}\n\nconst mockRegistry: ComponentRegistry = {\n TestComponent: {\n component: TestComponent,\n name: \"TestComponent\",\n description: \"A test component\",\n props: {},\n contextTools: [],\n },\n TestWithLoading: {\n component: TestComponent,\n loadingComponent: TestLoadingComponent,\n name: \"TestWithLoading\",\n description: \"A test component with loading state\",\n props: {},\n contextTools: [],\n },\n ContextAware: {\n component: ContextAwareComponent,\n name: \"ContextAware\",\n description: \"A context-aware component\",\n props: {},\n contextTools: [],\n },\n};\n\ndescribe(\"isComponentContent\", () => {\n it(\"returns true for component content\", () => {\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_1\",\n name: \"Test\",\n props: {},\n streamingState: \"done\",\n };\n expect(isComponentContent(content)).toBe(true);\n });\n\n it(\"returns false for text content\", () => {\n const content = { type: \"text\", text: \"hello\" };\n expect(isComponentContent(content as any)).toBe(false);\n });\n});\n\ndescribe(\"renderComponentContent\", () => {\n it(\"renders a component from registry\", () => {\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_1\",\n name: \"TestComponent\",\n props: { title: \"Hello\", count: 42 },\n streamingState: \"done\",\n };\n\n const result = renderComponentContent(content, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n expect(result.renderedComponent).not.toBeNull();\n\n // Render and check output\n render(<>{result.renderedComponent}</>);\n expect(screen.getByTestId(\"title\")).toHaveTextContent(\"Hello\");\n expect(screen.getByTestId(\"count\")).toHaveTextContent(\"42\");\n });\n\n it(\"returns null renderedComponent for unknown component\", () => {\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_1\",\n name: \"UnknownComponent\",\n props: {},\n streamingState: \"done\",\n };\n\n const consoleWarn = jest.spyOn(console, \"warn\").mockImplementation();\n\n const result = renderComponentContent(content, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n expect(result.renderedComponent).toBeNull();\n expect(consoleWarn).toHaveBeenCalledWith(\n 'Component \"UnknownComponent\" not found in registry',\n );\n\n consoleWarn.mockRestore();\n });\n\n it(\"shows loading component when streaming\", () => {\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_1\",\n name: \"TestWithLoading\",\n props: { title: \"Loading Test\", count: 0 },\n streamingState: \"streaming\",\n };\n\n const result = renderComponentContent(content, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n render(<>{result.renderedComponent}</>);\n expect(screen.getByTestId(\"loading\")).toHaveTextContent(\"Loading...\");\n });\n\n it(\"shows main component when done streaming\", () => {\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_1\",\n name: \"TestWithLoading\",\n props: { title: \"Done Test\", count: 99 },\n streamingState: \"done\",\n };\n\n const result = renderComponentContent(content, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n render(<>{result.renderedComponent}</>);\n expect(screen.getByTestId(\"title\")).toHaveTextContent(\"Done Test\");\n expect(screen.getByTestId(\"count\")).toHaveTextContent(\"99\");\n });\n\n it(\"provides component context to rendered components\", () => {\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_123\",\n name: \"ContextAware\",\n props: {},\n streamingState: \"done\",\n };\n\n const result = renderComponentContent(content, {\n threadId: \"thread_456\",\n messageId: \"msg_789\",\n componentList: mockRegistry,\n });\n\n render(<>{result.renderedComponent}</>);\n expect(screen.getByTestId(\"componentId\")).toHaveTextContent(\"comp_123\");\n expect(screen.getByTestId(\"threadId\")).toHaveTextContent(\"thread_456\");\n expect(screen.getByTestId(\"messageId\")).toHaveTextContent(\"msg_789\");\n });\n});\n\ndescribe(\"renderMessageContent\", () => {\n it(\"renders component content blocks and passes through others\", () => {\n const content = [\n { type: \"text\", text: \"Hello\" },\n {\n type: \"component\",\n id: \"comp_1\",\n name: \"TestComponent\",\n props: { title: \"Test\", count: 1 },\n streamingState: \"done\",\n } as V1ComponentContent,\n { type: \"text\", text: \"World\" },\n ];\n\n const result = renderMessageContent(content as any, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n expect(result).toHaveLength(3);\n expect(result[0]).toEqual({ type: \"text\", text: \"Hello\" });\n expect((result[1] as any).renderedComponent).not.toBeNull();\n expect(result[2]).toEqual({ type: \"text\", text: \"World\" });\n });\n\n it(\"handles multiple component blocks in a message\", () => {\n const content = [\n {\n type: \"component\",\n id: \"comp_1\",\n name: \"TestComponent\",\n props: { title: \"First\", count: 1 },\n streamingState: \"done\",\n } as V1ComponentContent,\n {\n type: \"component\",\n id: \"comp_2\",\n name: \"TestComponent\",\n props: { title: \"Second\", count: 2 },\n streamingState: \"done\",\n } as V1ComponentContent,\n ];\n\n const result = renderMessageContent(content as any, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n expect(result).toHaveLength(2);\n expect((result[0] as V1ComponentContent).renderedComponent).not.toBeNull();\n expect((result[1] as V1ComponentContent).renderedComponent).not.toBeNull();\n });\n\n it(\"handles tool_use content blocks unchanged\", () => {\n const content = [\n {\n type: \"tool_use\",\n id: \"tool_1\",\n name: \"search\",\n input: { query: \"test\" },\n },\n ];\n\n const result = renderMessageContent(content as any, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n expect(result).toHaveLength(1);\n expect(result[0]).toEqual(content[0]);\n });\n});\n\ndescribe(\"renderComponentContent edge cases\", () => {\n it(\"shows main component when streamingState is 'started' (no loading component)\", () => {\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_1\",\n name: \"TestComponent\",\n props: { title: \"Started\", count: 0 },\n streamingState: \"started\",\n };\n\n const result = renderComponentContent(content, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n // TestComponent has no loading component, so it shows main even when streaming\n render(<>{result.renderedComponent}</>);\n expect(screen.getByTestId(\"title\")).toHaveTextContent(\"Started\");\n });\n\n it(\"shows loading component when streamingState is 'started' and loading exists\", () => {\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_1\",\n name: \"TestWithLoading\",\n props: { title: \"Started\", count: 0 },\n streamingState: \"started\",\n };\n\n const result = renderComponentContent(content, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n render(<>{result.renderedComponent}</>);\n expect(screen.getByTestId(\"loading\")).toHaveTextContent(\"Loading...\");\n });\n\n it(\"passes state as initialState prop\", () => {\n // Component that reads initialState\n function StatefulComponent({\n initialState,\n }: {\n initialState?: { count: number };\n }) {\n return (\n <div data-testid=\"initial-count\">{initialState?.count ?? \"none\"}</div>\n );\n }\n\n const registryWithStateful: ComponentRegistry = {\n StatefulComponent: {\n component: StatefulComponent,\n name: \"StatefulComponent\",\n description: \"A stateful component\",\n props: {},\n contextTools: [],\n },\n };\n\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_1\",\n name: \"StatefulComponent\",\n props: {},\n state: { count: 42 },\n streamingState: \"done\",\n };\n\n const result = renderComponentContent(content, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: registryWithStateful,\n });\n\n render(<>{result.renderedComponent}</>);\n expect(screen.getByTestId(\"initial-count\")).toHaveTextContent(\"42\");\n });\n\n it(\"preserves original content properties in returned object\", () => {\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_custom\",\n name: \"TestComponent\",\n props: { title: \"Test\", count: 5 },\n state: { value: \"preserved\" },\n streamingState: \"done\",\n };\n\n const result = renderComponentContent(content, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n expect(result.id).toBe(\"comp_custom\");\n expect(result.name).toBe(\"TestComponent\");\n expect(result.props).toEqual({ title: \"Test\", count: 5 });\n expect(result.state).toEqual({ value: \"preserved\" });\n expect(result.streamingState).toBe(\"done\");\n });\n});\n\ndescribe(\"useV1ComponentContentOptional\", () => {\n it(\"returns null when used outside rendered component\", () => {\n function TestConsumer() {\n const context = useV1ComponentContentOptional();\n return (\n <div data-testid=\"context\">\n {context ? \"has context\" : \"no context\"}\n </div>\n );\n }\n\n render(<TestConsumer />);\n expect(screen.getByTestId(\"context\")).toHaveTextContent(\"no context\");\n });\n});\n\ndescribe(\"renderMessageComponents\", () => {\n it(\"renders all components in a message\", () => {\n const message = {\n id: \"msg_1\",\n role: \"assistant\" as const,\n content: [\n { type: \"text\" as const, text: \"Here is the weather:\" },\n {\n type: \"component\",\n id: \"comp_1\",\n name: \"TestComponent\",\n props: { title: \"Weather\", count: 72 },\n streamingState: \"done\",\n } as V1ComponentContent,\n ],\n createdAt: \"2024-01-01T00:00:00.000Z\",\n };\n\n const result = renderMessageComponents(message, {\n threadId: \"thread_1\",\n componentList: mockRegistry,\n });\n\n expect(result.id).toBe(\"msg_1\");\n expect(result.content).toHaveLength(2);\n expect(result.content[0]).toEqual({\n type: \"text\",\n text: \"Here is the weather:\",\n });\n expect(\n (result.content[1] as V1ComponentContent).renderedComponent,\n ).not.toBeNull();\n });\n\n it(\"preserves message metadata\", () => {\n const message = {\n id: \"msg_123\",\n role: \"assistant\" as const,\n content: [],\n createdAt: \"2024-01-01T00:00:00.000Z\",\n metadata: { custom: \"value\" },\n };\n\n const result = renderMessageComponents(message, {\n threadId: \"thread_1\",\n componentList: mockRegistry,\n });\n\n expect(result.id).toBe(\"msg_123\");\n expect(result.role).toBe(\"assistant\");\n expect(result.createdAt).toBe(\"2024-01-01T00:00:00.000Z\");\n expect(result.metadata).toEqual({ custom: \"value\" });\n });\n});\n\ndescribe(\"useV1ComponentContent error handling\", () => {\n it(\"throws when used outside rendered component\", () => {\n function TestConsumer() {\n useV1ComponentContent();\n return <div>Should not render</div>;\n }\n\n // Suppress React error boundary logs\n const consoleSpy = jest.spyOn(console, \"error\").mockImplementation();\n\n expect(() => render(<TestConsumer />)).toThrow(\n \"useV1ComponentContent must be used within a rendered component\",\n );\n\n consoleSpy.mockRestore();\n });\n});\n"]}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Accumulation Logic for v1 Streaming API
|
|
3
|
+
*
|
|
4
|
+
* Implements a reducer that transforms AG-UI event streams into React state.
|
|
5
|
+
* Used with useReducer to accumulate events into thread state.
|
|
6
|
+
*/
|
|
7
|
+
import type { AGUIEvent } from "@ag-ui/core";
|
|
8
|
+
import type { StreamingState, TamboV1Thread } from "../types/thread";
|
|
9
|
+
/**
|
|
10
|
+
* Error thrown when an unreachable case is reached in a switch statement.
|
|
11
|
+
* This indicates a programming error where not all cases were handled.
|
|
12
|
+
*/
|
|
13
|
+
export declare class UnreachableCaseError extends Error {
|
|
14
|
+
constructor(value: never);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Per-thread state managed by the stream reducer.
|
|
18
|
+
* Tracks thread data, streaming status, and accumulating data.
|
|
19
|
+
*/
|
|
20
|
+
export interface ThreadState {
|
|
21
|
+
thread: TamboV1Thread;
|
|
22
|
+
streaming: StreamingState;
|
|
23
|
+
/**
|
|
24
|
+
* Accumulating tool call arguments as JSON strings (for streaming).
|
|
25
|
+
* Maps tool call ID to accumulated JSON string.
|
|
26
|
+
*/
|
|
27
|
+
accumulatingToolArgs: Map<string, string>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* State managed by the stream reducer.
|
|
31
|
+
* Maintains a map of all threads being tracked.
|
|
32
|
+
*/
|
|
33
|
+
export interface StreamState {
|
|
34
|
+
/**
|
|
35
|
+
* Map of thread ID to thread state
|
|
36
|
+
*/
|
|
37
|
+
threadMap: Record<string, ThreadState>;
|
|
38
|
+
/**
|
|
39
|
+
* Current active thread ID (for UI context)
|
|
40
|
+
*/
|
|
41
|
+
currentThreadId: string | null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Event action - dispatches an AG-UI event to update thread state.
|
|
45
|
+
*/
|
|
46
|
+
export interface EventAction {
|
|
47
|
+
type: "EVENT";
|
|
48
|
+
event: AGUIEvent;
|
|
49
|
+
threadId: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Initialize thread action - creates a new thread in the threadMap.
|
|
53
|
+
*/
|
|
54
|
+
export interface InitThreadAction {
|
|
55
|
+
type: "INIT_THREAD";
|
|
56
|
+
threadId: string;
|
|
57
|
+
initialThread?: Partial<TamboV1Thread>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Set current thread action - changes the active thread.
|
|
61
|
+
*/
|
|
62
|
+
export interface SetCurrentThreadAction {
|
|
63
|
+
type: "SET_CURRENT_THREAD";
|
|
64
|
+
threadId: string | null;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Start new thread action - atomically creates and switches to a new thread.
|
|
68
|
+
* This prevents race conditions when multiple calls happen concurrently.
|
|
69
|
+
*/
|
|
70
|
+
export interface StartNewThreadAction {
|
|
71
|
+
type: "START_NEW_THREAD";
|
|
72
|
+
threadId: string;
|
|
73
|
+
initialThread?: Partial<TamboV1Thread>;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Action type for the stream reducer.
|
|
77
|
+
*/
|
|
78
|
+
export type StreamAction = EventAction | InitThreadAction | SetCurrentThreadAction | StartNewThreadAction;
|
|
79
|
+
/**
|
|
80
|
+
* Create initial thread state for a new thread.
|
|
81
|
+
* @param threadId - Unique thread identifier
|
|
82
|
+
* @returns Initial thread state
|
|
83
|
+
*/
|
|
84
|
+
export declare function createInitialThreadState(threadId: string): ThreadState;
|
|
85
|
+
/**
|
|
86
|
+
* Create initial stream state with empty threadMap.
|
|
87
|
+
* @returns Initial stream state
|
|
88
|
+
*/
|
|
89
|
+
export declare function createInitialState(): StreamState;
|
|
90
|
+
/**
|
|
91
|
+
* Stream reducer that accumulates events into thread state.
|
|
92
|
+
*
|
|
93
|
+
* This reducer handles all AG-UI events and Tambo custom events,
|
|
94
|
+
* transforming them into immutable state updates per thread.
|
|
95
|
+
* @param state - Current stream state
|
|
96
|
+
* @param action - Action to process
|
|
97
|
+
* @returns Updated stream state
|
|
98
|
+
*/
|
|
99
|
+
export declare function streamReducer(state: StreamState, action: StreamAction): StreamState;
|
|
100
|
+
//# sourceMappingURL=event-accumulator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-accumulator.d.ts","sourceRoot":"","sources":["../../../src/v1/utils/event-accumulator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,SAAS,EAYV,MAAM,aAAa,CAAC;AAWrB,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGrE;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,KAAK,EAAE,KAAK;CAIzB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,cAAc,CAAC;IAC1B;;;OAGG;IACH,oBAAoB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3C;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAEvC;;OAEG;IACH,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,aAAa,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;CACxC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,oBAAoB,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,kBAAkB,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;CACxC;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GACpB,WAAW,GACX,gBAAgB,GAChB,sBAAsB,GACtB,oBAAoB,CAAC;AASzB;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAatE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,WAAW,CAKhD;AAuGD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,YAAY,GACnB,WAAW,CAwLb"}
|