Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 1.4k
feat(sdk): onEvent observability callback on the chat transport#4187
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
f6de9a554fa79027fcb0d259fb0155ec22878deb6c43d4a9b2434ee0a917250File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| --- | ||
| "@trigger.dev/sdk": patch | ||
| --- | ||
| Add an `onEvent` callback to `TriggerChatTransport` / `useTriggerChatTransport` that emits typed lifecycle events for sends, stream connects, first chunk, and turn completion. Send-success metrics, time-to-first-token, and "sent but never answered" watchdogs become a few lines of client code. | ||
| ```ts | ||
| onEvent: (event) => { | ||
| if (event.type === "message-sent") metrics.timing("chat.send_ms", event.durationMs); | ||
| if (event.type === "first-chunk") metrics.timing("chat.ttft_ms", event.sinceSendMs ?? 0); | ||
| }, | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -574,3 +574,49 @@ baseURL: ({ endpoint }) => | ||
| ``` | ||
| For per-request control beyond URL routing (header injection, custom retries, tracing), pass a `fetch` override. See [Trusted edge signals](/ai-chat/patterns/trusted-edge-signals) for a full proxy walkthrough. | ||
| ## Monitoring message delivery | ||
| `sendMessage` from `useChat` gives no feedback about whether the message actually reached the backend. The transport's `onEvent` callback closes that gap with typed lifecycle events (see the [event catalog](/ai-chat/reference#transport-events)) so you can record real metrics: | ||
| ```ts | ||
| const transport = useTriggerChatTransport({ | ||
| task: "my-chat", | ||
| accessToken: ({ chatId }) => mintChatAccessToken(chatId), | ||
| onEvent: (event) => { | ||
| switch (event.type) { | ||
| case "message-sent": | ||
| metrics.increment("chat.message_sent"); | ||
| metrics.timing("chat.send_duration_ms", event.durationMs); | ||
| break; | ||
| case "message-send-failed": | ||
| metrics.increment("chat.message_send_failed", { status: event.status }); | ||
| break; | ||
| } | ||
| }, | ||
| }); | ||
| ``` | ||
| A `message-sent` event means the message is durably written to the session's input stream (the stream the agent consumes from), so it's a true "sent successfully" signal. Because send and response events share the same callback, "sent but never answered" becomes a small client-side watchdog: | ||
| ```ts | ||
| // Module scope (or a ref) so re-renders don't recreate it. Keyed by chatId | ||
| // on purpose: a new send on the same chat supersedes the in-flight turn. | ||
| const pending = new Map<string, ReturnType<typeof setTimeout>>(); | ||
| onEvent: (event) => { | ||
| if (event.type === "message-sent" && event.source === "submit-message") { | ||
| pending.set(event.chatId, setTimeout(() => { | ||
| // Log the chatId; don't tag the metric with it (unbounded cardinality). | ||
| metrics.increment("chat.sent_but_unanswered"); | ||
| console.warn("sent but unanswered", event.chatId); | ||
| }, 30_000)); | ||
| } | ||
| if (event.type === "first-chunk" || event.type === "turn-completed" || event.type === "stream-error") { | ||
| clearTimeout(pending.get(event.chatId)); | ||
| pending.delete(event.chatId); | ||
| } | ||
| }; | ||
| ``` | ||
| Time to first token is `first-chunk`'s `sinceSendMs` (the transport tracks the last turn-producing send per chat, so no bookkeeping is needed), and `turn-completed`'s `sinceSendMs` is the full turn latency. Exceptions thrown inside `onEvent` are swallowed, so a failing metrics pipeline can never break the chat. | ||
coderabbitai[bot] marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.