- 
          
- 
                Notifications
    You must be signed in to change notification settings 
- Fork 870
feat(react-hooks): add TriggerChatTransport for AI SDK useChat #2644
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat(react-hooks): add TriggerChatTransport for AI SDK useChat #2644
Conversation
…ok with a custom transport for trigger.tdev tasks
| 🦋 Changeset detectedLatest commit: c4f800f The changes in this PR will be included in the next version bump. This PR includes changesets to release 23 packages
 Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR | 
| Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the  You can disable this status message by setting the  ✨ Finishing touches🧪 Generate unit tests (beta)
 Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment  | 
| "check-exports": "attw --pack ." | ||
| }, | ||
| "dependencies": { | ||
| "@ai-sdk/react": "^2.0.14", | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ideally we don't have to include this dependency unless devs are using the useTriggerChat hook
| "@ai-sdk/react": "^2.0.14", | ||
| "@electric-sql/client": "1.0.14", | ||
| "@trigger.dev/core": "workspace:^4.0.5", | ||
| "ai": "^5.0.82", | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as above
| * @remarks | ||
| * **CRITICAL:** Your Trigger.dev task MUST call `metadata.stream()` with the AI SDK stream. | ||
| * The stream key used in `metadata.stream()` must match the `streamKey` option (default: "chat"). | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if there may be a better place to put this piece of documentation
the tricky bit here is there are 2 external files required to enable this hook
- the trigger.dev task definition itself, which must call a stream()method fromaiand forward the stream tometadata.stream()
- the server action which invoked the trigger.dev task server-side
| * @example Trigger.dev task that streams AI responses: | ||
| * ```ts | ||
| * import { metadata, task } from "@trigger.dev/sdk/v3"; | ||
| * import { streamText } from "ai"; | ||
| * import { openai } from "@ai-sdk/openai"; | ||
| * | ||
| * export const chatTask = task({ | ||
| * id: "chat", | ||
| * run: async ({ messages }) => { | ||
| * const result = streamText({ | ||
| * model: openai("gpt-4"), | ||
| * messages, | ||
| * }); | ||
| * | ||
| * // CRITICAL: Stream to client using metadata.stream() | ||
| * await metadata.stream("chat", result.toUIMessageStream()); | ||
| * | ||
| * return { text: await result.text }; | ||
| * }, | ||
| * }); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
similar to the above, it's a bit awkward adding this example here, because the task parameter actually just needs the string which is the id of the task ("chat" in the above example)
still unsure if this is the best way to document this
| function parseMetadata( | ||
| metadata: Record<string, unknown> | string | undefined | ||
| ): ParsedMetadata | undefined { | ||
| if (!metadata) return undefined; | ||
|  | ||
| if (typeof metadata === "string") { | ||
| try { | ||
| return JSON.parse(metadata) as ParsedMetadata; | ||
| } catch { | ||
| return undefined; | ||
| } | ||
| } | ||
|  | ||
| if (typeof metadata === "object") { | ||
| return metadata as ParsedMetadata; | ||
| } | ||
|  | ||
| return undefined; | ||
| } | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
normally I would use zod for this, but I think this matches patterns in other hooks
| * @remarks | ||
| * **CRITICAL SETUP REQUIREMENTS:** | ||
| * | ||
| * 1. Your Trigger.dev task MUST call `metadata.stream()` to stream responses: | ||
| * ```ts | ||
| * await metadata.stream("chat", result.toUIMessageStream()); | ||
| * ``` | ||
| * | ||
| * 2. You must provide a server action that calls `tasks.trigger()`: | ||
| * ```ts | ||
| * "use server"; | ||
| * export async function triggerChat(task: string, payload: unknown) { | ||
| * const handle = await tasks.trigger(task, payload); | ||
| * return { success: true, runId: handle.id, publicAccessToken: handle.publicAccessToken }; | ||
| * } | ||
| * ``` | ||
| * | ||
| * @example Complete setup with three files: | ||
| * | ||
| * **1. Trigger.dev task (src/trigger/chat.ts):** | ||
| * ```ts | ||
| * import { metadata, task } from "@trigger.dev/sdk/v3"; | ||
| * import { streamText } from "ai"; | ||
| * import { openai } from "@ai-sdk/openai"; | ||
| * | ||
| * export const chatTask = task({ | ||
| * id: "chat", | ||
| * run: async ({ messages }) => { | ||
| * const result = streamText({ model: openai("gpt-4"), messages }); | ||
| * // CRITICAL: Stream to client | ||
| * await metadata.stream("chat", result.toUIMessageStream()); | ||
| * return { text: await result.text }; | ||
| * }, | ||
| * }); | ||
| * ``` | ||
| * | ||
| * **2. Server action (src/actions.ts):** | ||
| * ```ts | ||
| * "use server"; | ||
| * import { tasks } from "@trigger.dev/sdk/v3"; | ||
| * | ||
| * export async function triggerChat(task: string, payload: unknown) { | ||
| * const handle = await tasks.trigger(task, payload); | ||
| * return { success: true, runId: handle.id, publicAccessToken: handle.publicAccessToken }; | ||
| * } | ||
| * ``` | ||
| * | ||
| * **3. Client component (src/components/Chat.tsx):** | ||
| * ```ts | ||
| * "use client"; | ||
| * import { useTriggerChat } from "@trigger.dev/react-hooks"; | ||
| * import { triggerChat } from "../actions"; | ||
| * | ||
| * export function Chat() { | ||
| * const { messages, input, handleInputChange, handleSubmit } = useTriggerChat({ | ||
| * transportOptions: { triggerTask: triggerChat } | ||
| * }); | ||
| * | ||
| * return ( | ||
| * <form onSubmit={handleSubmit}> | ||
| * {messages.map(m => <div key={m.id}>{m.role}: {m.content}</div>)} | ||
| * <input value={input} onChange={handleInputChange} /> | ||
| * <button type="submit">Send</button> | ||
| * </form> | ||
| * ); | ||
| * } | ||
| * ``` | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the above comments apply here as well
Closes #2634
✅ Checklist
Summary
Adds a custom transport implementation for AI SDK's
useChathook that integrates with Trigger.dev background tasks. This enables long-running AI conversations by triggering tasks, subscribing to realtime run updates via Electric SQL, and streaming AI responses via Server-Sent Events (SSE).Changes
useTriggerChat- A drop-in replacement for AI SDK'suseChatthat works with Trigger.dev tasksTriggerChatTransport- Custom transport implementation following AI SDK's transport patternai(^5.0.82),@ai-sdk/react(^2.0.14), andeventsource-parser(^3.0.0)Technical Details
Architecture
EventSourceParserStreamfor streaming AI responsesuseRealtimehookDeveloper Requirements
Important: Developers must create their own server action to trigger tasks. Since
useTriggerChatis a client-side hook, it cannot directly calltasks.trigger()(which requires server-side execution). ThetriggerTaskoption expects a server action that:tasks.trigger()on the server{ success: true, runId, publicAccessToken }Error Handling
Testing
Manually tested with a local project using
pnpm patchto verify:Usage Example
1. Define your Trigger.dev task (e.g.
src/trigger/chat.ts):2. Create a server action (e.g.
src/actions.ts):3. Use the hook in your component (e.g.
src/components/Chat.tsx):Changelog
Added
useTriggerChathook to@trigger.dev/react-hooksthat provides AI SDKuseChatintegration with Trigger.dev background tasks. Enables long-running AI conversations with realtime streaming via custom transport implementation.New exports:
useTriggerChat- Hook for AI chat integrationTriggerChatTransport- Custom transport classTriggerChatTransportOptions- Transport configuration typeTriggerChatTaskPayload- Task payload typeNew dependencies:
ai@^5.0.82@ai-sdk/react@^2.0.14eventsource-parser@^3.0.0Resources
Screenshots
N/A - This is a developer-facing hook with no UI
💯