@tambo-ai/react 0.71.0 → 0.73.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.test.js +22 -9
- package/dist/v1/hooks/use-tambo-v1-messages.test.js.map +1 -1
- package/dist/v1/hooks/use-tambo-v1-send-message.d.ts +1 -0
- package/dist/v1/hooks/use-tambo-v1-send-message.d.ts.map +1 -1
- package/dist/v1/hooks/use-tambo-v1-send-message.js +9 -2
- package/dist/v1/hooks/use-tambo-v1-send-message.js.map +1 -1
- package/dist/v1/hooks/use-tambo-v1-send-message.test.js +22 -9
- package/dist/v1/hooks/use-tambo-v1-send-message.test.js.map +1 -1
- package/dist/v1/hooks/use-tambo-v1-suggestions.d.ts +91 -0
- package/dist/v1/hooks/use-tambo-v1-suggestions.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-suggestions.js +152 -0
- package/dist/v1/hooks/use-tambo-v1-suggestions.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-suggestions.test.d.ts +2 -0
- package/dist/v1/hooks/use-tambo-v1-suggestions.test.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-suggestions.test.js +511 -0
- package/dist/v1/hooks/use-tambo-v1-suggestions.test.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread-input.d.ts +11 -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 +16 -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 +297 -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 +6 -4
- package/dist/v1/hooks/use-tambo-v1-thread-list.d.ts.map +1 -1
- package/dist/v1/hooks/use-tambo-v1-thread-list.js +2 -2
- package/dist/v1/hooks/use-tambo-v1-thread-list.js.map +1 -1
- package/dist/v1/hooks/use-tambo-v1-thread-list.test.js +2 -2
- package/dist/v1/hooks/use-tambo-v1-thread-list.test.js.map +1 -1
- package/dist/v1/hooks/use-tambo-v1.test.js +16 -7
- package/dist/v1/hooks/use-tambo-v1.test.js.map +1 -1
- package/dist/v1/index.d.ts +39 -19
- package/dist/v1/index.d.ts.map +1 -1
- package/dist/v1/index.js +60 -34
- package/dist/v1/index.js.map +1 -1
- package/dist/v1/providers/tambo-v1-provider.d.ts +61 -1
- package/dist/v1/providers/tambo-v1-provider.d.ts.map +1 -1
- package/dist/v1/providers/tambo-v1-provider.js +35 -3
- package/dist/v1/providers/tambo-v1-provider.js.map +1 -1
- package/dist/v1/providers/tambo-v1-provider.test.js +78 -3
- package/dist/v1/providers/tambo-v1-provider.test.js.map +1 -1
- package/dist/v1/providers/tambo-v1-stream-context.d.ts +19 -10
- package/dist/v1/providers/tambo-v1-stream-context.d.ts.map +1 -1
- package/dist/v1/providers/tambo-v1-stream-context.js +43 -53
- package/dist/v1/providers/tambo-v1-stream-context.js.map +1 -1
- package/dist/v1/providers/tambo-v1-stream-context.test.js +94 -19
- package/dist/v1/providers/tambo-v1-stream-context.test.js.map +1 -1
- package/dist/v1/providers/tambo-v1-stub-provider.d.ts +74 -0
- package/dist/v1/providers/tambo-v1-stub-provider.d.ts.map +1 -0
- package/dist/v1/providers/tambo-v1-stub-provider.js +212 -0
- package/dist/v1/providers/tambo-v1-stub-provider.js.map +1 -0
- package/dist/v1/providers/tambo-v1-stub-provider.test.d.ts +2 -0
- package/dist/v1/providers/tambo-v1-stub-provider.test.d.ts.map +1 -0
- package/dist/v1/providers/tambo-v1-stub-provider.test.js +162 -0
- package/dist/v1/providers/tambo-v1-stub-provider.test.js.map +1 -0
- package/dist/v1/providers/tambo-v1-thread-input-provider.d.ts +105 -0
- package/dist/v1/providers/tambo-v1-thread-input-provider.d.ts.map +1 -0
- package/dist/v1/providers/tambo-v1-thread-input-provider.js +191 -0
- package/dist/v1/providers/tambo-v1-thread-input-provider.js.map +1 -0
- package/dist/v1/types/message.d.ts +27 -2
- package/dist/v1/types/message.d.ts.map +1 -1
- package/dist/v1/types/message.js.map +1 -1
- package/dist/v1/utils/component-renderer.d.ts +37 -0
- package/dist/v1/utils/component-renderer.d.ts.map +1 -0
- package/dist/v1/utils/component-renderer.js +70 -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 +45 -0
- package/dist/v1/utils/component-renderer.test.js.map +1 -0
- package/dist/v1/utils/event-accumulator.js +28 -8
- package/dist/v1/utils/event-accumulator.js.map +1 -1
- package/dist/v1/utils/event-accumulator.test.js +201 -6
- package/dist/v1/utils/event-accumulator.test.js.map +1 -1
- 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.test.js +22 -9
- package/esm/v1/hooks/use-tambo-v1-messages.test.js.map +1 -1
- package/esm/v1/hooks/use-tambo-v1-send-message.d.ts +1 -0
- package/esm/v1/hooks/use-tambo-v1-send-message.d.ts.map +1 -1
- package/esm/v1/hooks/use-tambo-v1-send-message.js +9 -2
- package/esm/v1/hooks/use-tambo-v1-send-message.js.map +1 -1
- package/esm/v1/hooks/use-tambo-v1-send-message.test.js +22 -9
- package/esm/v1/hooks/use-tambo-v1-send-message.test.js.map +1 -1
- package/esm/v1/hooks/use-tambo-v1-suggestions.d.ts +91 -0
- package/esm/v1/hooks/use-tambo-v1-suggestions.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-suggestions.js +149 -0
- package/esm/v1/hooks/use-tambo-v1-suggestions.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-suggestions.test.d.ts +2 -0
- package/esm/v1/hooks/use-tambo-v1-suggestions.test.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-suggestions.test.js +506 -0
- package/esm/v1/hooks/use-tambo-v1-suggestions.test.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread-input.d.ts +11 -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 +12 -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 +292 -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 +6 -4
- package/esm/v1/hooks/use-tambo-v1-thread-list.d.ts.map +1 -1
- package/esm/v1/hooks/use-tambo-v1-thread-list.js +2 -2
- package/esm/v1/hooks/use-tambo-v1-thread-list.js.map +1 -1
- package/esm/v1/hooks/use-tambo-v1-thread-list.test.js +2 -2
- package/esm/v1/hooks/use-tambo-v1-thread-list.test.js.map +1 -1
- package/esm/v1/hooks/use-tambo-v1.test.js +16 -7
- package/esm/v1/hooks/use-tambo-v1.test.js.map +1 -1
- package/esm/v1/index.d.ts +39 -19
- package/esm/v1/index.d.ts.map +1 -1
- package/esm/v1/index.js +43 -19
- package/esm/v1/index.js.map +1 -1
- package/esm/v1/providers/tambo-v1-provider.d.ts +61 -1
- package/esm/v1/providers/tambo-v1-provider.d.ts.map +1 -1
- package/esm/v1/providers/tambo-v1-provider.js +34 -4
- package/esm/v1/providers/tambo-v1-provider.js.map +1 -1
- package/esm/v1/providers/tambo-v1-provider.test.js +79 -4
- package/esm/v1/providers/tambo-v1-provider.test.js.map +1 -1
- package/esm/v1/providers/tambo-v1-stream-context.d.ts +19 -10
- package/esm/v1/providers/tambo-v1-stream-context.d.ts.map +1 -1
- package/esm/v1/providers/tambo-v1-stream-context.js +44 -54
- package/esm/v1/providers/tambo-v1-stream-context.js.map +1 -1
- package/esm/v1/providers/tambo-v1-stream-context.test.js +95 -20
- package/esm/v1/providers/tambo-v1-stream-context.test.js.map +1 -1
- package/esm/v1/providers/tambo-v1-stub-provider.d.ts +74 -0
- package/esm/v1/providers/tambo-v1-stub-provider.d.ts.map +1 -0
- package/esm/v1/providers/tambo-v1-stub-provider.js +176 -0
- package/esm/v1/providers/tambo-v1-stub-provider.js.map +1 -0
- package/esm/v1/providers/tambo-v1-stub-provider.test.d.ts +2 -0
- package/esm/v1/providers/tambo-v1-stub-provider.test.d.ts.map +1 -0
- package/esm/v1/providers/tambo-v1-stub-provider.test.js +157 -0
- package/esm/v1/providers/tambo-v1-stub-provider.test.js.map +1 -0
- package/esm/v1/providers/tambo-v1-thread-input-provider.d.ts +105 -0
- package/esm/v1/providers/tambo-v1-thread-input-provider.d.ts.map +1 -0
- package/esm/v1/providers/tambo-v1-thread-input-provider.js +153 -0
- package/esm/v1/providers/tambo-v1-thread-input-provider.js.map +1 -0
- package/esm/v1/types/message.d.ts +27 -2
- package/esm/v1/types/message.d.ts.map +1 -1
- package/esm/v1/types/message.js.map +1 -1
- package/esm/v1/utils/component-renderer.d.ts +37 -0
- package/esm/v1/utils/component-renderer.d.ts.map +1 -0
- package/esm/v1/utils/component-renderer.js +33 -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 +40 -0
- package/esm/v1/utils/component-renderer.test.js.map +1 -0
- package/esm/v1/utils/event-accumulator.js +28 -8
- package/esm/v1/utils/event-accumulator.js.map +1 -1
- package/esm/v1/utils/event-accumulator.test.js +201 -6
- package/esm/v1/utils/event-accumulator.test.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TamboV1ThreadInputProvider - Shared Thread Input Context for v1 API
|
|
3
|
+
*
|
|
4
|
+
* Provides shared input state across all components, enabling features like
|
|
5
|
+
* suggestions to update the input field directly.
|
|
6
|
+
*
|
|
7
|
+
* This mirrors the beta SDK's TamboThreadInputProvider pattern.
|
|
8
|
+
*/
|
|
9
|
+
import React, { type PropsWithChildren } from "react";
|
|
10
|
+
import { type StagedImage } from "../../hooks/use-message-images";
|
|
11
|
+
import { type UseTamboMutationResult } from "../../hooks/react-query-hooks";
|
|
12
|
+
export declare const INPUT_ERROR_MESSAGES: {
|
|
13
|
+
readonly EMPTY: "Message cannot be empty";
|
|
14
|
+
readonly VALIDATION: "Invalid message format";
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Options for submitting a message
|
|
18
|
+
*/
|
|
19
|
+
export interface SubmitOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Enable debug logging for the stream
|
|
22
|
+
*/
|
|
23
|
+
debug?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Context props for thread input state
|
|
27
|
+
*/
|
|
28
|
+
export interface TamboV1ThreadInputContextProps extends Omit<UseTamboMutationResult<{
|
|
29
|
+
threadId: string | undefined;
|
|
30
|
+
}, Error, SubmitOptions | undefined>, "mutate" | "mutateAsync"> {
|
|
31
|
+
/** Current value of the input field */
|
|
32
|
+
value: string;
|
|
33
|
+
/**
|
|
34
|
+
* Function to update the input value
|
|
35
|
+
* @param value - New value for the input field
|
|
36
|
+
*/
|
|
37
|
+
setValue: React.Dispatch<React.SetStateAction<string>>;
|
|
38
|
+
/**
|
|
39
|
+
* Function to submit the current input value
|
|
40
|
+
* @param options - Submission options
|
|
41
|
+
* @returns Promise with the threadId
|
|
42
|
+
*/
|
|
43
|
+
submit: (options?: SubmitOptions) => Promise<{
|
|
44
|
+
threadId: string | undefined;
|
|
45
|
+
}>;
|
|
46
|
+
/** Currently staged images */
|
|
47
|
+
images: StagedImage[];
|
|
48
|
+
/** Add a single image */
|
|
49
|
+
addImage: (file: File) => Promise<void>;
|
|
50
|
+
/** Add multiple images */
|
|
51
|
+
addImages: (files: File[]) => Promise<void>;
|
|
52
|
+
/** Remove an image by id */
|
|
53
|
+
removeImage: (id: string) => void;
|
|
54
|
+
/** Clear all staged images */
|
|
55
|
+
clearImages: () => void;
|
|
56
|
+
/** Current thread ID being used for input */
|
|
57
|
+
threadId: string | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* Set the thread ID for input submission.
|
|
60
|
+
* If not set, a new thread will be created on submit.
|
|
61
|
+
*/
|
|
62
|
+
setThreadId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Context for thread input state.
|
|
66
|
+
* @internal
|
|
67
|
+
*/
|
|
68
|
+
export declare const TamboV1ThreadInputContext: React.Context<TamboV1ThreadInputContextProps | undefined>;
|
|
69
|
+
/**
|
|
70
|
+
* Provider that manages shared thread input state across all components.
|
|
71
|
+
*
|
|
72
|
+
* This ensures that useTamboV1ThreadInput, useTamboV1Suggestions, and other components
|
|
73
|
+
* all share the same input state.
|
|
74
|
+
* @param props - Provider props
|
|
75
|
+
* @param props.children - Child components
|
|
76
|
+
* @returns Thread input context provider
|
|
77
|
+
*/
|
|
78
|
+
export declare function TamboV1ThreadInputProvider({ children }: PropsWithChildren): React.JSX.Element;
|
|
79
|
+
/**
|
|
80
|
+
* Hook to access the shared thread input state.
|
|
81
|
+
*
|
|
82
|
+
* All components using this hook share the same input state, enabling
|
|
83
|
+
* features like suggestions to update the input field directly.
|
|
84
|
+
* @returns The shared thread input context
|
|
85
|
+
* @throws {Error} If used outside TamboV1ThreadInputProvider
|
|
86
|
+
* @example
|
|
87
|
+
* ```tsx
|
|
88
|
+
* function ChatInput() {
|
|
89
|
+
* const { value, setValue, submit, isPending } = useTamboV1ThreadInput();
|
|
90
|
+
*
|
|
91
|
+
* return (
|
|
92
|
+
* <form onSubmit={(e) => { e.preventDefault(); submit(); }}>
|
|
93
|
+
* <input
|
|
94
|
+
* value={value}
|
|
95
|
+
* onChange={(e) => setValue(e.target.value)}
|
|
96
|
+
* disabled={isPending}
|
|
97
|
+
* />
|
|
98
|
+
* <button type="submit" disabled={isPending}>Send</button>
|
|
99
|
+
* </form>
|
|
100
|
+
* );
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export declare function useTamboV1ThreadInput(): TamboV1ThreadInputContextProps;
|
|
105
|
+
//# sourceMappingURL=tambo-v1-thread-input-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tambo-v1-thread-input-provider.d.ts","sourceRoot":"","sources":["../../../src/v1/providers/tambo-v1-thread-input-provider.tsx"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,EAKZ,KAAK,iBAAiB,EACvB,MAAM,OAAO,CAAC;AACf,OAAO,EAEL,KAAK,WAAW,EACjB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,+BAA+B,CAAC;AASvC,eAAO,MAAM,oBAAoB;;;CAGvB,CAAC;AAkCX;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,8BAA+B,SAAQ,IAAI,CAC1D,sBAAsB,CACpB;IAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,EAChC,KAAK,EACL,aAAa,GAAG,SAAS,CAC1B,EACD,QAAQ,GAAG,aAAa,CACzB;IACC,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IAEvD;;;;OAIG;IACH,MAAM,EAAE,CACN,OAAO,CAAC,EAAE,aAAa,KACpB,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC,CAAC;IAE/C,8BAA8B;IAC9B,MAAM,EAAE,WAAW,EAAE,CAAC;IAEtB,yBAAyB;IACzB,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAExC,0BAA0B;IAC1B,SAAS,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5C,4BAA4B;IAC5B,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAElC,8BAA8B;IAC9B,WAAW,EAAE,MAAM,IAAI,CAAC;IAExB,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAE7B;;;OAGG;IACH,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;CACvE;AAED;;;GAGG;AACH,eAAO,MAAM,yBAAyB,2DAE1B,CAAC;AAEb;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,EAAE,QAAQ,EAAE,EAAE,iBAAiB,qBAoFzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,qBAAqB,IAAI,8BAA8B,CAQtE"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* TamboV1ThreadInputProvider - Shared Thread Input Context for v1 API
|
|
4
|
+
*
|
|
5
|
+
* Provides shared input state across all components, enabling features like
|
|
6
|
+
* suggestions to update the input field directly.
|
|
7
|
+
*
|
|
8
|
+
* This mirrors the beta SDK's TamboThreadInputProvider pattern.
|
|
9
|
+
*/
|
|
10
|
+
import React, { createContext, useCallback, useContext, useState, } from "react";
|
|
11
|
+
import { useMessageImages, } from "../../hooks/use-message-images";
|
|
12
|
+
import { useTamboMutation, } from "../../hooks/react-query-hooks";
|
|
13
|
+
import { useTamboV1SendMessage } from "../hooks/use-tambo-v1-send-message";
|
|
14
|
+
import { useStreamState } from "./tambo-v1-stream-context";
|
|
15
|
+
// Error messages for various input-related error scenarios.
|
|
16
|
+
// TODO: Reintroduce explicit `NETWORK` and `SERVER` keys once `submit()` maps
|
|
17
|
+
// failures into a small, stable set of user-facing error codes (at minimum:
|
|
18
|
+
// connectivity failures vs non-2xx responses).
|
|
19
|
+
export const INPUT_ERROR_MESSAGES = {
|
|
20
|
+
EMPTY: "Message cannot be empty",
|
|
21
|
+
VALIDATION: "Invalid message format",
|
|
22
|
+
};
|
|
23
|
+
function stagedImageToResourceContent(image) {
|
|
24
|
+
if (!image.dataUrl.startsWith("data:")) {
|
|
25
|
+
throw new Error(INPUT_ERROR_MESSAGES.VALIDATION);
|
|
26
|
+
}
|
|
27
|
+
const commaIndex = image.dataUrl.indexOf(",");
|
|
28
|
+
if (commaIndex === -1) {
|
|
29
|
+
throw new Error(INPUT_ERROR_MESSAGES.VALIDATION);
|
|
30
|
+
}
|
|
31
|
+
const header = image.dataUrl.slice("data:".length, commaIndex);
|
|
32
|
+
const [mimeType, ...params] = header.split(";");
|
|
33
|
+
const isBase64 = params.includes("base64");
|
|
34
|
+
if (mimeType !== image.type || !isBase64) {
|
|
35
|
+
throw new Error(INPUT_ERROR_MESSAGES.VALIDATION);
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
type: "resource",
|
|
39
|
+
resource: {
|
|
40
|
+
name: image.name,
|
|
41
|
+
mimeType: image.type,
|
|
42
|
+
// Shared.Resource.blob expects base64-encoded data; this is the base64
|
|
43
|
+
// payload from the `data:` URL.
|
|
44
|
+
blob: image.dataUrl.slice(commaIndex + 1),
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Context for thread input state.
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
52
|
+
export const TamboV1ThreadInputContext = createContext(undefined);
|
|
53
|
+
/**
|
|
54
|
+
* Provider that manages shared thread input state across all components.
|
|
55
|
+
*
|
|
56
|
+
* This ensures that useTamboV1ThreadInput, useTamboV1Suggestions, and other components
|
|
57
|
+
* all share the same input state.
|
|
58
|
+
* @param props - Provider props
|
|
59
|
+
* @param props.children - Child components
|
|
60
|
+
* @returns Thread input context provider
|
|
61
|
+
*/
|
|
62
|
+
export function TamboV1ThreadInputProvider({ children }) {
|
|
63
|
+
const [inputValue, setInputValue] = useState("");
|
|
64
|
+
const [threadId, setThreadId] = useState(undefined);
|
|
65
|
+
const imageState = useMessageImages();
|
|
66
|
+
const streamState = useStreamState();
|
|
67
|
+
// Use the current thread from stream state if no explicit threadId is set
|
|
68
|
+
const inheritedThreadId = streamState.currentThreadId ?? undefined;
|
|
69
|
+
const effectiveThreadId = threadId ?? inheritedThreadId;
|
|
70
|
+
const shouldAdoptThreadId = threadId === undefined && inheritedThreadId === undefined;
|
|
71
|
+
const sendMessage = useTamboV1SendMessage(effectiveThreadId);
|
|
72
|
+
const submitFn = useCallback(async (options) => {
|
|
73
|
+
const trimmedValue = inputValue.trim();
|
|
74
|
+
// Check if we have content to send
|
|
75
|
+
if (!trimmedValue && imageState.images.length === 0) {
|
|
76
|
+
throw new Error(INPUT_ERROR_MESSAGES.EMPTY);
|
|
77
|
+
}
|
|
78
|
+
const content = [];
|
|
79
|
+
if (trimmedValue) {
|
|
80
|
+
content.push({ type: "text", text: trimmedValue });
|
|
81
|
+
}
|
|
82
|
+
for (const image of imageState.images) {
|
|
83
|
+
content.push(stagedImageToResourceContent(image));
|
|
84
|
+
}
|
|
85
|
+
const result = await sendMessage.mutateAsync({
|
|
86
|
+
message: {
|
|
87
|
+
role: "user",
|
|
88
|
+
content,
|
|
89
|
+
},
|
|
90
|
+
debug: options?.debug,
|
|
91
|
+
});
|
|
92
|
+
// Clear input and images after successful submission
|
|
93
|
+
setInputValue("");
|
|
94
|
+
imageState.clearImages();
|
|
95
|
+
// Update threadId if a new thread was created
|
|
96
|
+
if (result.threadId && shouldAdoptThreadId) {
|
|
97
|
+
setThreadId(result.threadId);
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
},
|
|
101
|
+
// `stagedImageToResourceContent` is a pure module-level helper (not a hook value).
|
|
102
|
+
[inputValue, imageState, sendMessage, shouldAdoptThreadId]);
|
|
103
|
+
const { mutateAsync: submitAsync, mutate: _unusedSubmit, ...mutationState } = useTamboMutation({
|
|
104
|
+
mutationFn: submitFn,
|
|
105
|
+
});
|
|
106
|
+
const contextValue = {
|
|
107
|
+
...mutationState,
|
|
108
|
+
value: inputValue,
|
|
109
|
+
setValue: setInputValue,
|
|
110
|
+
submit: submitAsync,
|
|
111
|
+
images: imageState.images,
|
|
112
|
+
addImage: imageState.addImage,
|
|
113
|
+
addImages: imageState.addImages,
|
|
114
|
+
removeImage: imageState.removeImage,
|
|
115
|
+
clearImages: imageState.clearImages,
|
|
116
|
+
threadId: effectiveThreadId,
|
|
117
|
+
setThreadId,
|
|
118
|
+
};
|
|
119
|
+
return (React.createElement(TamboV1ThreadInputContext.Provider, { value: contextValue }, children));
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Hook to access the shared thread input state.
|
|
123
|
+
*
|
|
124
|
+
* All components using this hook share the same input state, enabling
|
|
125
|
+
* features like suggestions to update the input field directly.
|
|
126
|
+
* @returns The shared thread input context
|
|
127
|
+
* @throws {Error} If used outside TamboV1ThreadInputProvider
|
|
128
|
+
* @example
|
|
129
|
+
* ```tsx
|
|
130
|
+
* function ChatInput() {
|
|
131
|
+
* const { value, setValue, submit, isPending } = useTamboV1ThreadInput();
|
|
132
|
+
*
|
|
133
|
+
* return (
|
|
134
|
+
* <form onSubmit={(e) => { e.preventDefault(); submit(); }}>
|
|
135
|
+
* <input
|
|
136
|
+
* value={value}
|
|
137
|
+
* onChange={(e) => setValue(e.target.value)}
|
|
138
|
+
* disabled={isPending}
|
|
139
|
+
* />
|
|
140
|
+
* <button type="submit" disabled={isPending}>Send</button>
|
|
141
|
+
* </form>
|
|
142
|
+
* );
|
|
143
|
+
* }
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
export function useTamboV1ThreadInput() {
|
|
147
|
+
const context = useContext(TamboV1ThreadInputContext);
|
|
148
|
+
if (!context) {
|
|
149
|
+
throw new Error("useTamboV1ThreadInput must be used within TamboV1ThreadInputProvider");
|
|
150
|
+
}
|
|
151
|
+
return context;
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=tambo-v1-thread-input-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tambo-v1-thread-input-provider.js","sourceRoot":"","sources":["../../../src/v1/providers/tambo-v1-thread-input-provider.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\n/**\n * TamboV1ThreadInputProvider - Shared Thread Input Context for v1 API\n *\n * Provides shared input state across all components, enabling features like\n * suggestions to update the input field directly.\n *\n * This mirrors the beta SDK's TamboThreadInputProvider pattern.\n */\n\nimport React, {\n createContext,\n useCallback,\n useContext,\n useState,\n type PropsWithChildren,\n} from \"react\";\nimport {\n useMessageImages,\n type StagedImage,\n} from \"../../hooks/use-message-images\";\nimport {\n useTamboMutation,\n type UseTamboMutationResult,\n} from \"../../hooks/react-query-hooks\";\nimport { useTamboV1SendMessage } from \"../hooks/use-tambo-v1-send-message\";\nimport type { InputMessage } from \"../types/message\";\nimport { useStreamState } from \"./tambo-v1-stream-context\";\n\n// Error messages for various input-related error scenarios.\n// TODO: Reintroduce explicit `NETWORK` and `SERVER` keys once `submit()` maps\n// failures into a small, stable set of user-facing error codes (at minimum:\n// connectivity failures vs non-2xx responses).\nexport const INPUT_ERROR_MESSAGES = {\n EMPTY: \"Message cannot be empty\",\n VALIDATION: \"Invalid message format\",\n} as const;\n\nfunction stagedImageToResourceContent(\n image: StagedImage,\n): InputMessage[\"content\"][number] {\n if (!image.dataUrl.startsWith(\"data:\")) {\n throw new Error(INPUT_ERROR_MESSAGES.VALIDATION);\n }\n\n const commaIndex = image.dataUrl.indexOf(\",\");\n if (commaIndex === -1) {\n throw new Error(INPUT_ERROR_MESSAGES.VALIDATION);\n }\n\n const header = image.dataUrl.slice(\"data:\".length, commaIndex);\n const [mimeType, ...params] = header.split(\";\");\n const isBase64 = params.includes(\"base64\");\n\n if (mimeType !== image.type || !isBase64) {\n throw new Error(INPUT_ERROR_MESSAGES.VALIDATION);\n }\n\n return {\n type: \"resource\",\n resource: {\n name: image.name,\n mimeType: image.type,\n // Shared.Resource.blob expects base64-encoded data; this is the base64\n // payload from the `data:` URL.\n blob: image.dataUrl.slice(commaIndex + 1),\n },\n };\n}\n\n/**\n * Options for submitting a message\n */\nexport interface SubmitOptions {\n /**\n * Enable debug logging for the stream\n */\n debug?: boolean;\n}\n\n/**\n * Context props for thread input state\n */\nexport interface TamboV1ThreadInputContextProps extends Omit<\n UseTamboMutationResult<\n { threadId: string | undefined },\n Error,\n SubmitOptions | undefined\n >,\n \"mutate\" | \"mutateAsync\"\n> {\n /** Current value of the input field */\n value: string;\n\n /**\n * Function to update the input value\n * @param value - New value for the input field\n */\n setValue: React.Dispatch<React.SetStateAction<string>>;\n\n /**\n * Function to submit the current input value\n * @param options - Submission options\n * @returns Promise with the threadId\n */\n submit: (\n options?: SubmitOptions,\n ) => Promise<{ threadId: string | undefined }>;\n\n /** Currently staged images */\n images: StagedImage[];\n\n /** Add a single image */\n addImage: (file: File) => Promise<void>;\n\n /** Add multiple images */\n addImages: (files: File[]) => Promise<void>;\n\n /** Remove an image by id */\n removeImage: (id: string) => void;\n\n /** Clear all staged images */\n clearImages: () => void;\n\n /** Current thread ID being used for input */\n threadId: string | undefined;\n\n /**\n * Set the thread ID for input submission.\n * If not set, a new thread will be created on submit.\n */\n setThreadId: React.Dispatch<React.SetStateAction<string | undefined>>;\n}\n\n/**\n * Context for thread input state.\n * @internal\n */\nexport const TamboV1ThreadInputContext = createContext<\n TamboV1ThreadInputContextProps | undefined\n>(undefined);\n\n/**\n * Provider that manages shared thread input state across all components.\n *\n * This ensures that useTamboV1ThreadInput, useTamboV1Suggestions, and other components\n * all share the same input state.\n * @param props - Provider props\n * @param props.children - Child components\n * @returns Thread input context provider\n */\nexport function TamboV1ThreadInputProvider({ children }: PropsWithChildren) {\n const [inputValue, setInputValue] = useState(\"\");\n const [threadId, setThreadId] = useState<string | undefined>(undefined);\n const imageState = useMessageImages();\n const streamState = useStreamState();\n\n // Use the current thread from stream state if no explicit threadId is set\n const inheritedThreadId = streamState.currentThreadId ?? undefined;\n const effectiveThreadId = threadId ?? inheritedThreadId;\n const shouldAdoptThreadId =\n threadId === undefined && inheritedThreadId === undefined;\n const sendMessage = useTamboV1SendMessage(effectiveThreadId);\n\n const submitFn = useCallback(\n async (\n options?: SubmitOptions,\n ): Promise<{ threadId: string | undefined }> => {\n const trimmedValue = inputValue.trim();\n\n // Check if we have content to send\n if (!trimmedValue && imageState.images.length === 0) {\n throw new Error(INPUT_ERROR_MESSAGES.EMPTY);\n }\n\n const content: InputMessage[\"content\"] = [];\n\n if (trimmedValue) {\n content.push({ type: \"text\", text: trimmedValue });\n }\n\n for (const image of imageState.images) {\n content.push(stagedImageToResourceContent(image));\n }\n\n const result = await sendMessage.mutateAsync({\n message: {\n role: \"user\",\n content,\n },\n debug: options?.debug,\n });\n\n // Clear input and images after successful submission\n setInputValue(\"\");\n imageState.clearImages();\n\n // Update threadId if a new thread was created\n if (result.threadId && shouldAdoptThreadId) {\n setThreadId(result.threadId);\n }\n\n return result;\n },\n // `stagedImageToResourceContent` is a pure module-level helper (not a hook value).\n [inputValue, imageState, sendMessage, shouldAdoptThreadId],\n );\n\n const {\n mutateAsync: submitAsync,\n mutate: _unusedSubmit,\n ...mutationState\n } = useTamboMutation({\n mutationFn: submitFn,\n });\n\n const contextValue: TamboV1ThreadInputContextProps = {\n ...mutationState,\n value: inputValue,\n setValue: setInputValue,\n submit: submitAsync,\n images: imageState.images,\n addImage: imageState.addImage,\n addImages: imageState.addImages,\n removeImage: imageState.removeImage,\n clearImages: imageState.clearImages,\n threadId: effectiveThreadId,\n setThreadId,\n };\n\n return (\n <TamboV1ThreadInputContext.Provider value={contextValue}>\n {children}\n </TamboV1ThreadInputContext.Provider>\n );\n}\n\n/**\n * Hook to access the shared thread input state.\n *\n * All components using this hook share the same input state, enabling\n * features like suggestions to update the input field directly.\n * @returns The shared thread input context\n * @throws {Error} If used outside TamboV1ThreadInputProvider\n * @example\n * ```tsx\n * function ChatInput() {\n * const { value, setValue, submit, isPending } = useTamboV1ThreadInput();\n *\n * return (\n * <form onSubmit={(e) => { e.preventDefault(); submit(); }}>\n * <input\n * value={value}\n * onChange={(e) => setValue(e.target.value)}\n * disabled={isPending}\n * />\n * <button type=\"submit\" disabled={isPending}>Send</button>\n * </form>\n * );\n * }\n * ```\n */\nexport function useTamboV1ThreadInput(): TamboV1ThreadInputContextProps {\n const context = useContext(TamboV1ThreadInputContext);\n if (!context) {\n throw new Error(\n \"useTamboV1ThreadInput must be used within TamboV1ThreadInputProvider\",\n );\n }\n return context;\n}\n"]}
|
|
@@ -5,18 +5,43 @@
|
|
|
5
5
|
* Messages use Anthropic-style content blocks pattern where a message
|
|
6
6
|
* contains an array of content blocks (text, tool calls, tool results, components).
|
|
7
7
|
*/
|
|
8
|
+
import type { ReactElement } from "react";
|
|
8
9
|
export type { TextContent, ToolUseContent, ToolResultContent, ComponentContent, ResourceContent, } from "@tambo-ai/typescript-sdk/resources/threads/threads";
|
|
9
10
|
export type { InputMessage } from "@tambo-ai/typescript-sdk/resources/threads/runs";
|
|
10
11
|
export type { MessageListResponse, MessageGetResponse, } from "@tambo-ai/typescript-sdk/resources/threads/messages";
|
|
11
12
|
import type { TextContent, ToolUseContent, ToolResultContent, ComponentContent, ResourceContent } from "@tambo-ai/typescript-sdk/resources/threads/threads";
|
|
13
|
+
/**
|
|
14
|
+
* Streaming state for component content blocks.
|
|
15
|
+
* Tracks the lifecycle of component prop/state streaming.
|
|
16
|
+
*/
|
|
17
|
+
export type ComponentStreamingState = "started" | "streaming" | "done";
|
|
18
|
+
/**
|
|
19
|
+
* Extended ComponentContent with streaming state and rendered element.
|
|
20
|
+
* Used by the v1 SDK to track component rendering lifecycle.
|
|
21
|
+
*/
|
|
22
|
+
export interface V1ComponentContent extends ComponentContent {
|
|
23
|
+
/**
|
|
24
|
+
* Current streaming state of this component's props.
|
|
25
|
+
* - 'started': Component block created, awaiting props
|
|
26
|
+
* - 'streaming': Props are being streamed
|
|
27
|
+
* - 'done': Props streaming complete
|
|
28
|
+
*/
|
|
29
|
+
streamingState: ComponentStreamingState;
|
|
30
|
+
/**
|
|
31
|
+
* The rendered React element for this component.
|
|
32
|
+
* undefined if not yet rendered, null if the component couldn't be found in the registry.
|
|
33
|
+
*/
|
|
34
|
+
renderedComponent?: ReactElement | null;
|
|
35
|
+
}
|
|
12
36
|
/**
|
|
13
37
|
* Message role (from SDK)
|
|
14
38
|
*/
|
|
15
39
|
export type MessageRole = "user" | "assistant";
|
|
16
40
|
/**
|
|
17
|
-
* Union type of all content block types
|
|
41
|
+
* Union type of all content block types.
|
|
42
|
+
* Uses V1ComponentContent which includes streaming state and rendered component.
|
|
18
43
|
*/
|
|
19
|
-
export type Content = TextContent | ToolUseContent | ToolResultContent |
|
|
44
|
+
export type Content = TextContent | ToolUseContent | ToolResultContent | V1ComponentContent | ResourceContent;
|
|
20
45
|
/**
|
|
21
46
|
* Message in a thread (simplified from SDK's MessageGetResponse)
|
|
22
47
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../../src/v1/types/message.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../../src/v1/types/message.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAG1C,YAAY,EACV,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,GAChB,MAAM,oDAAoD,CAAC;AAG5D,YAAY,EAAE,YAAY,EAAE,MAAM,iDAAiD,CAAC;AAEpF,YAAY,EACV,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,qDAAqD,CAAC;AAG7D,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EAChB,MAAM,oDAAoD,CAAC;AAE5D;;;GAGG;AACH,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,WAAW,GAAG,MAAM,CAAC;AAEvE;;;GAGG;AACH,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IAC1D;;;;;OAKG;IACH,cAAc,EAAE,uBAAuB,CAAC;IAExC;;;OAGG;IACH,iBAAiB,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,CAAC;AAE/C;;;GAGG;AACH,MAAM,MAAM,OAAO,GACf,WAAW,GACX,cAAc,GACd,iBAAiB,GACjB,kBAAkB,GAClB,eAAe,CAAC;AAEpB;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,gCAAgC;IAChC,EAAE,EAAE,MAAM,CAAC;IAEX,uCAAuC;IACvC,IAAI,EAAE,WAAW,CAAC;IAElB,oCAAoC;IACpC,OAAO,EAAE,OAAO,EAAE,CAAC;IAEnB,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAC;IAElB,uBAAuB;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.js","sourceRoot":"","sources":["../../../src/v1/types/message.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG","sourcesContent":["/**\n * Message and Content Types for v1 API\n *\n * Re-exports message and content types from `@tambo-ai/typescript-sdk`.\n * Messages use Anthropic-style content blocks pattern where a message\n * contains an array of content blocks (text, tool calls, tool results, components).\n */\n\n// Re-export content block types from TypeScript SDK\nexport type {\n TextContent,\n ToolUseContent,\n ToolResultContent,\n ComponentContent,\n ResourceContent,\n} from \"@tambo-ai/typescript-sdk/resources/threads/threads\";\n\n// Re-export message types from TypeScript SDK\nexport type { InputMessage } from \"@tambo-ai/typescript-sdk/resources/threads/runs\";\n\nexport type {\n MessageListResponse,\n MessageGetResponse,\n} from \"@tambo-ai/typescript-sdk/resources/threads/messages\";\n\n// Import for Content union type\nimport type {\n TextContent,\n ToolUseContent,\n ToolResultContent,\n ComponentContent,\n ResourceContent,\n} from \"@tambo-ai/typescript-sdk/resources/threads/threads\";\n\n/**\n * Message role (from SDK)\n */\nexport type MessageRole = \"user\" | \"assistant\";\n\n/**\n * Union type of all content block types
|
|
1
|
+
{"version":3,"file":"message.js","sourceRoot":"","sources":["../../../src/v1/types/message.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG","sourcesContent":["/**\n * Message and Content Types for v1 API\n *\n * Re-exports message and content types from `@tambo-ai/typescript-sdk`.\n * Messages use Anthropic-style content blocks pattern where a message\n * contains an array of content blocks (text, tool calls, tool results, components).\n */\n\nimport type { ReactElement } from \"react\";\n\n// Re-export content block types from TypeScript SDK\nexport type {\n TextContent,\n ToolUseContent,\n ToolResultContent,\n ComponentContent,\n ResourceContent,\n} from \"@tambo-ai/typescript-sdk/resources/threads/threads\";\n\n// Re-export message types from TypeScript SDK\nexport type { InputMessage } from \"@tambo-ai/typescript-sdk/resources/threads/runs\";\n\nexport type {\n MessageListResponse,\n MessageGetResponse,\n} from \"@tambo-ai/typescript-sdk/resources/threads/messages\";\n\n// Import for Content union type\nimport type {\n TextContent,\n ToolUseContent,\n ToolResultContent,\n ComponentContent,\n ResourceContent,\n} from \"@tambo-ai/typescript-sdk/resources/threads/threads\";\n\n/**\n * Streaming state for component content blocks.\n * Tracks the lifecycle of component prop/state streaming.\n */\nexport type ComponentStreamingState = \"started\" | \"streaming\" | \"done\";\n\n/**\n * Extended ComponentContent with streaming state and rendered element.\n * Used by the v1 SDK to track component rendering lifecycle.\n */\nexport interface V1ComponentContent extends ComponentContent {\n /**\n * Current streaming state of this component's props.\n * - 'started': Component block created, awaiting props\n * - 'streaming': Props are being streamed\n * - 'done': Props streaming complete\n */\n streamingState: ComponentStreamingState;\n\n /**\n * The rendered React element for this component.\n * undefined if not yet rendered, null if the component couldn't be found in the registry.\n */\n renderedComponent?: ReactElement | null;\n}\n\n/**\n * Message role (from SDK)\n */\nexport type MessageRole = \"user\" | \"assistant\";\n\n/**\n * Union type of all content block types.\n * Uses V1ComponentContent which includes streaming state and rendered component.\n */\nexport type Content =\n | TextContent\n | ToolUseContent\n | ToolResultContent\n | V1ComponentContent\n | ResourceContent;\n\n/**\n * Message in a thread (simplified from SDK's MessageGetResponse)\n */\nexport interface TamboV1Message {\n /** Unique message identifier */\n id: string;\n\n /** Message role (user or assistant) */\n role: MessageRole;\n\n /** Content blocks in the message */\n content: Content[];\n\n /** When the message was created */\n createdAt: string;\n\n /** Message metadata */\n metadata?: Record<string, unknown>;\n}\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component Renderer Utility for v1 API
|
|
3
|
+
*
|
|
4
|
+
* Provides the component content context for rendered components.
|
|
5
|
+
* Components can use useV1ComponentContent() to access their context.
|
|
6
|
+
*/
|
|
7
|
+
import React from "react";
|
|
8
|
+
/**
|
|
9
|
+
* Context for component content blocks.
|
|
10
|
+
* Provides access to the component ID and thread ID for component state hooks.
|
|
11
|
+
*/
|
|
12
|
+
export interface V1ComponentContentContext {
|
|
13
|
+
/** Component instance ID */
|
|
14
|
+
componentId: string;
|
|
15
|
+
/** Thread ID the component belongs to */
|
|
16
|
+
threadId: string;
|
|
17
|
+
/** Message ID the component belongs to */
|
|
18
|
+
messageId: string;
|
|
19
|
+
/** Component name */
|
|
20
|
+
componentName: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Provider for component content context.
|
|
24
|
+
* Wraps rendered components to provide access to component metadata.
|
|
25
|
+
* @returns Provider component with memoized context value
|
|
26
|
+
*/
|
|
27
|
+
export declare function V1ComponentContentProvider({ children, componentId, threadId, messageId, componentName, }: V1ComponentContentContext & {
|
|
28
|
+
children: React.ReactNode;
|
|
29
|
+
}): React.JSX.Element;
|
|
30
|
+
/**
|
|
31
|
+
* Hook to access the current component content context.
|
|
32
|
+
* Must be used within a rendered component.
|
|
33
|
+
* @returns Component content context
|
|
34
|
+
* @throws {Error} If used outside a rendered component
|
|
35
|
+
*/
|
|
36
|
+
export declare function useV1ComponentContent(): V1ComponentContentContext;
|
|
37
|
+
//# sourceMappingURL=component-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-renderer.d.ts","sourceRoot":"","sources":["../../../src/v1/utils/component-renderer.tsx"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,OAAO,KAA6C,MAAM,OAAO,CAAC;AAElE;;;GAGG;AACH,MAAM,WAAW,yBAAyB;IACxC,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AAMD;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,EACzC,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,SAAS,EACT,aAAa,GACd,EAAE,yBAAyB,GAAG;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,qBAY3D;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,IAAI,yBAAyB,CAQjE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* Component Renderer Utility for v1 API
|
|
4
|
+
*
|
|
5
|
+
* Provides the component content context for rendered components.
|
|
6
|
+
* Components can use useV1ComponentContent() to access their context.
|
|
7
|
+
*/
|
|
8
|
+
import React, { createContext, useContext, useMemo } from "react";
|
|
9
|
+
const ComponentContentContext = createContext(null);
|
|
10
|
+
/**
|
|
11
|
+
* Provider for component content context.
|
|
12
|
+
* Wraps rendered components to provide access to component metadata.
|
|
13
|
+
* @returns Provider component with memoized context value
|
|
14
|
+
*/
|
|
15
|
+
export function V1ComponentContentProvider({ children, componentId, threadId, messageId, componentName, }) {
|
|
16
|
+
// Memoize context value to prevent unnecessary re-renders of consumers
|
|
17
|
+
const value = useMemo(() => ({ componentId, threadId, messageId, componentName }), [componentId, threadId, messageId, componentName]);
|
|
18
|
+
return (React.createElement(ComponentContentContext.Provider, { value: value }, children));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Hook to access the current component content context.
|
|
22
|
+
* Must be used within a rendered component.
|
|
23
|
+
* @returns Component content context
|
|
24
|
+
* @throws {Error} If used outside a rendered component
|
|
25
|
+
*/
|
|
26
|
+
export function useV1ComponentContent() {
|
|
27
|
+
const context = useContext(ComponentContentContext);
|
|
28
|
+
if (!context) {
|
|
29
|
+
throw new Error("useV1ComponentContent must be used within a rendered component");
|
|
30
|
+
}
|
|
31
|
+
return context;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=component-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-renderer.js","sourceRoot":"","sources":["../../../src/v1/utils/component-renderer.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAiBlE,MAAM,uBAAuB,GAAG,aAAa,CAC3C,IAAI,CACL,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,EACzC,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,SAAS,EACT,aAAa,GAC6C;IAC1D,uEAAuE;IACvE,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,EAC3D,CAAC,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,CAAC,CAClD,CAAC;IAEF,OAAO,CACL,oBAAC,uBAAuB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,IAC3C,QAAQ,CACwB,CACpC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB;IACnC,MAAM,OAAO,GAAG,UAAU,CAAC,uBAAuB,CAAC,CAAC;IACpD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,gEAAgE,CACjE,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["\"use client\";\n\n/**\n * Component Renderer Utility for v1 API\n *\n * Provides the component content context for rendered components.\n * Components can use useV1ComponentContent() to access their context.\n */\n\nimport React, { createContext, useContext, useMemo } from \"react\";\n\n/**\n * Context for component content blocks.\n * Provides access to the component ID and thread ID for component state hooks.\n */\nexport interface V1ComponentContentContext {\n /** Component instance ID */\n componentId: string;\n /** Thread ID the component belongs to */\n threadId: string;\n /** Message ID the component belongs to */\n messageId: string;\n /** Component name */\n componentName: string;\n}\n\nconst ComponentContentContext = createContext<V1ComponentContentContext | null>(\n null,\n);\n\n/**\n * Provider for component content context.\n * Wraps rendered components to provide access to component metadata.\n * @returns Provider component with memoized context value\n */\nexport function V1ComponentContentProvider({\n children,\n componentId,\n threadId,\n messageId,\n componentName,\n}: V1ComponentContentContext & { children: React.ReactNode }) {\n // Memoize context value to prevent unnecessary re-renders of consumers\n const value = useMemo(\n () => ({ componentId, threadId, messageId, componentName }),\n [componentId, threadId, messageId, componentName],\n );\n\n return (\n <ComponentContentContext.Provider value={value}>\n {children}\n </ComponentContentContext.Provider>\n );\n}\n\n/**\n * Hook to access the current component content context.\n * Must be used within a rendered component.\n * @returns Component content context\n * @throws {Error} If used outside a rendered component\n */\nexport function useV1ComponentContent(): V1ComponentContentContext {\n const context = useContext(ComponentContentContext);\n if (!context) {\n throw new Error(\n \"useV1ComponentContent must be used within a rendered component\",\n );\n }\n return context;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-renderer.test.d.ts","sourceRoot":"","sources":["../../../src/v1/utils/component-renderer.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import { useV1ComponentContent, V1ComponentContentProvider, } from "./component-renderer";
|
|
4
|
+
// Test component that uses the content context
|
|
5
|
+
const ContextAwareComponent = () => {
|
|
6
|
+
const context = useV1ComponentContent();
|
|
7
|
+
return (React.createElement("div", { "data-testid": "context-aware" },
|
|
8
|
+
React.createElement("span", { "data-testid": "componentId" }, context.componentId),
|
|
9
|
+
React.createElement("span", { "data-testid": "threadId" }, context.threadId),
|
|
10
|
+
React.createElement("span", { "data-testid": "messageId" }, context.messageId),
|
|
11
|
+
React.createElement("span", { "data-testid": "componentName" }, context.componentName)));
|
|
12
|
+
};
|
|
13
|
+
describe("V1ComponentContentProvider", () => {
|
|
14
|
+
it("provides context to child components", () => {
|
|
15
|
+
render(React.createElement(V1ComponentContentProvider, { componentId: "comp_123", threadId: "thread_456", messageId: "msg_789", componentName: "TestComponent" },
|
|
16
|
+
React.createElement(ContextAwareComponent, null)));
|
|
17
|
+
expect(screen.getByTestId("componentId")).toHaveTextContent("comp_123");
|
|
18
|
+
expect(screen.getByTestId("threadId")).toHaveTextContent("thread_456");
|
|
19
|
+
expect(screen.getByTestId("messageId")).toHaveTextContent("msg_789");
|
|
20
|
+
expect(screen.getByTestId("componentName")).toHaveTextContent("TestComponent");
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe("useV1ComponentContent", () => {
|
|
24
|
+
it("throws when used outside provider", () => {
|
|
25
|
+
function TestConsumer() {
|
|
26
|
+
useV1ComponentContent();
|
|
27
|
+
return React.createElement("div", null, "Should not render");
|
|
28
|
+
}
|
|
29
|
+
// Suppress React error boundary logs
|
|
30
|
+
const consoleSpy = jest.spyOn(console, "error").mockImplementation();
|
|
31
|
+
expect(() => render(React.createElement(TestConsumer, null))).toThrow("useV1ComponentContent must be used within a rendered component");
|
|
32
|
+
consoleSpy.mockRestore();
|
|
33
|
+
});
|
|
34
|
+
it("returns context when used within provider", () => {
|
|
35
|
+
render(React.createElement(V1ComponentContentProvider, { componentId: "comp_test", threadId: "thread_test", messageId: "msg_test", componentName: "TestComp" },
|
|
36
|
+
React.createElement(ContextAwareComponent, null)));
|
|
37
|
+
expect(screen.getByTestId("componentId")).toHaveTextContent("comp_test");
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
//# 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":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EACL,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,sBAAsB,CAAC;AAE9B,+CAA+C;AAC/C,MAAM,qBAAqB,GAAa,GAAG,EAAE;IAC3C,MAAM,OAAO,GAAG,qBAAqB,EAAE,CAAC;IACxC,OAAO,CACL,4CAAiB,eAAe;QAC9B,6CAAkB,aAAa,IAAE,OAAO,CAAC,WAAW,CAAQ;QAC5D,6CAAkB,UAAU,IAAE,OAAO,CAAC,QAAQ,CAAQ;QACtD,6CAAkB,WAAW,IAAE,OAAO,CAAC,SAAS,CAAQ;QACxD,6CAAkB,eAAe,IAAE,OAAO,CAAC,aAAa,CAAQ,CAC5D,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,CACJ,oBAAC,0BAA0B,IACzB,WAAW,EAAC,UAAU,EACtB,QAAQ,EAAC,YAAY,EACrB,SAAS,EAAC,SAAS,EACnB,aAAa,EAAC,eAAe;YAE7B,oBAAC,qBAAqB,OAAG,CACE,CAC9B,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;QACxE,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC;QACvE,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;QACrE,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,iBAAiB,CAC3D,eAAe,CAChB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,SAAS,YAAY;YACnB,qBAAqB,EAAE,CAAC;YACxB,OAAO,qDAA4B,CAAC;QACtC,CAAC;QAED,qCAAqC;QACrC,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,kBAAkB,EAAE,CAAC;QAErE,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,oBAAC,YAAY,OAAG,CAAC,CAAC,CAAC,OAAO,CAC5C,gEAAgE,CACjE,CAAC;QAEF,UAAU,CAAC,WAAW,EAAE,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,CACJ,oBAAC,0BAA0B,IACzB,WAAW,EAAC,WAAW,EACvB,QAAQ,EAAC,aAAa,EACtB,SAAS,EAAC,UAAU,EACpB,aAAa,EAAC,UAAU;YAExB,oBAAC,qBAAqB,OAAG,CACE,CAC9B,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import React from \"react\";\nimport { render, screen } from \"@testing-library/react\";\nimport {\n useV1ComponentContent,\n V1ComponentContentProvider,\n} from \"./component-renderer\";\n\n// Test component that uses the content context\nconst ContextAwareComponent: React.FC = () => {\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 <span data-testid=\"componentName\">{context.componentName}</span>\n </div>\n );\n};\n\ndescribe(\"V1ComponentContentProvider\", () => {\n it(\"provides context to child components\", () => {\n render(\n <V1ComponentContentProvider\n componentId=\"comp_123\"\n threadId=\"thread_456\"\n messageId=\"msg_789\"\n componentName=\"TestComponent\"\n >\n <ContextAwareComponent />\n </V1ComponentContentProvider>,\n );\n\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 expect(screen.getByTestId(\"componentName\")).toHaveTextContent(\n \"TestComponent\",\n );\n });\n});\n\ndescribe(\"useV1ComponentContent\", () => {\n it(\"throws when used outside provider\", () => {\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 it(\"returns context when used within provider\", () => {\n render(\n <V1ComponentContentProvider\n componentId=\"comp_test\"\n threadId=\"thread_test\"\n messageId=\"msg_test\"\n componentName=\"TestComp\"\n >\n <ContextAwareComponent />\n </V1ComponentContentProvider>,\n );\n\n expect(screen.getByTestId(\"componentId\")).toHaveTextContent(\"comp_test\");\n });\n});\n"]}
|
|
@@ -610,7 +610,7 @@ function handleCustomEvent(threadState, event) {
|
|
|
610
610
|
}
|
|
611
611
|
/**
|
|
612
612
|
* Handle tambo.component.start event.
|
|
613
|
-
* Adds a component content block to the message.
|
|
613
|
+
* Adds a component content block to the message with 'started' streaming state.
|
|
614
614
|
* @param threadState - Current thread state
|
|
615
615
|
* @param event - Component start event
|
|
616
616
|
* @returns Updated thread state
|
|
@@ -624,12 +624,13 @@ function handleComponentStart(threadState, event) {
|
|
|
624
624
|
throw new Error(`Message ${messageId} not found for tambo.component.start event`);
|
|
625
625
|
}
|
|
626
626
|
const message = messages[messageIndex];
|
|
627
|
-
// Add component content block
|
|
627
|
+
// Add component content block with 'started' streaming state
|
|
628
628
|
const newContent = {
|
|
629
629
|
type: "component",
|
|
630
630
|
id: event.value.componentId,
|
|
631
631
|
name: event.value.componentName,
|
|
632
632
|
props: {},
|
|
633
|
+
streamingState: "started",
|
|
633
634
|
};
|
|
634
635
|
const updatedMessage = {
|
|
635
636
|
...message,
|
|
@@ -639,7 +640,7 @@ function handleComponentStart(threadState, event) {
|
|
|
639
640
|
}
|
|
640
641
|
/**
|
|
641
642
|
* Handle component delta events (both props_delta and state_delta).
|
|
642
|
-
* Applies JSON Patch to the specified field.
|
|
643
|
+
* Applies JSON Patch to the specified field and sets streamingState to 'streaming'.
|
|
643
644
|
* @param threadState - Current thread state
|
|
644
645
|
* @param event - Component delta event (props or state)
|
|
645
646
|
* @param field - Which field to update ('props' or 'state')
|
|
@@ -663,9 +664,11 @@ function handleComponentDelta(threadState, event, field) {
|
|
|
663
664
|
: (componentContent.state ?? {});
|
|
664
665
|
// Apply JSON Patch
|
|
665
666
|
const updatedValue = applyJsonPatch(currentValue, operations);
|
|
667
|
+
// Update field and set streaming state to 'streaming'
|
|
666
668
|
const updatedContent = {
|
|
667
669
|
...componentContent,
|
|
668
670
|
[field]: updatedValue,
|
|
671
|
+
streamingState: "streaming",
|
|
669
672
|
};
|
|
670
673
|
const updatedMessage = {
|
|
671
674
|
...message,
|
|
@@ -675,14 +678,31 @@ function handleComponentDelta(threadState, event, field) {
|
|
|
675
678
|
}
|
|
676
679
|
/**
|
|
677
680
|
* Handle tambo.component.end event.
|
|
678
|
-
*
|
|
681
|
+
* Sets component streaming state to 'done'.
|
|
679
682
|
* @param threadState - Current thread state
|
|
680
|
-
* @param
|
|
683
|
+
* @param event - Component end event
|
|
681
684
|
* @returns Updated thread state
|
|
682
685
|
*/
|
|
683
|
-
function handleComponentEnd(threadState,
|
|
684
|
-
|
|
685
|
-
|
|
686
|
+
function handleComponentEnd(threadState, event) {
|
|
687
|
+
const componentId = event.value.componentId;
|
|
688
|
+
const messages = threadState.thread.messages;
|
|
689
|
+
// Find the component content block
|
|
690
|
+
const { messageIndex, contentIndex } = findContentById(messages, "component", componentId, "tambo.component.end event");
|
|
691
|
+
const message = messages[messageIndex];
|
|
692
|
+
const componentContent = message.content[contentIndex];
|
|
693
|
+
if (componentContent.type !== "component") {
|
|
694
|
+
throw new Error(`Content at index ${contentIndex} is not a component block for tambo.component.end event`);
|
|
695
|
+
}
|
|
696
|
+
// Set streaming state to 'done'
|
|
697
|
+
const updatedContent = {
|
|
698
|
+
...componentContent,
|
|
699
|
+
streamingState: "done",
|
|
700
|
+
};
|
|
701
|
+
const updatedMessage = {
|
|
702
|
+
...message,
|
|
703
|
+
content: updateContentAtIndex(message.content, contentIndex, updatedContent),
|
|
704
|
+
};
|
|
705
|
+
return updateThreadMessages(threadState, updateMessageAtIndex(messages, messageIndex, updatedMessage));
|
|
686
706
|
}
|
|
687
707
|
/**
|
|
688
708
|
* Handle tambo.run.awaiting_input event.
|