LLM observability, built in

Every LLM call your tasks make is recorded with its model, cost, tokens, and latency. See the whole workload on a dashboard, open any single call in the run trace, and query all of it yourself.

Eric Allam

Eric Allam

CTO, Trigger.dev

James Ritchie

James Ritchie

Co-founder, Trigger.dev

You could always see what a task did. Now you can see what it spent. One flag, nothing to install, and every LLM call shows up timed and priced, from a single span up to your whole AI bill.

Turn it on

Set experimental_telemetry on any Vercel AI SDK call. Nothing to install.


import { task } from "@trigger.dev/sdk";
import { generateText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
export const summarize = task({
id: "summarize",
run: async (payload: { text: string }) => {
const { text } = await generateText({
model: anthropic("claude-sonnet-4-5"),
prompt: payload.text,
experimental_telemetry: { isEnabled: true },
});
return text;
},
});

It works the same on generateText, streamText, and generateObject. Every call becomes its own span.

The AI SDK is just one way to emit those spans. They follow the OpenTelemetry GenAI semantic conventions, and that's the whole contract: any span with the standard gen_ai attributes gets cost data attached and the same trace and dashboard observability, whatever produced it.

See the whole workload

The dashboard at the top rolls up every recorded call across your project: cost over time, cost by model, total tokens, latency. The shape of your AI spend, without instrumenting anything.

It runs on TRQL, our SQL-style query language for your Trigger.dev data. It reads LLM usage alongside your runs and metrics tables, so cost per task, tokens by model, and latency trends are one query. Or ask in plain English on the Query page and it writes the query for you:

Show me the 10 most expensive tasks over the last 7 days

Open any single call

The dashboard tells you what you spent. The trace tells you why. Open a model span and the AI inspector breaks the call down:

  • Overview: model, provider, prompt/completion/total token counts, cost, and an input/output preview
  • Messages: the full thread sent to the model, system prompt and tool results included
  • Tools: the tool definitions you passed and every tool call the model made, with arguments
One AI generation span, every tab: the model, tokens and cost; the full message thread; the tools you passed; and the linked prompt version.

So when a call runs slow or a bill jumps, you open the span and see which model ran, what went in, and what came back. No guessing which of five generateText calls in a task was the expensive one.

If you manage prompts with Prompts, resolve one and spread toAISDKTelemetry() into the call. The span then carries the prompt too, and you can tag it with your own attributes:


const resolved = await summarizePrompt.resolve({ tone: "brief" });
const result = await generateText({
model: anthropic("claude-sonnet-4-5"),
prompt: resolved.text,
...resolved.toAISDKTelemetry({
"task.type": "summarization",
"customer.tier": "enterprise",
}),
});

A fourth Prompt tab appears on the span with the linked template, its version, and the input variables, so a bad output traces straight back to the prompt that caused it.

Chat agents get it too

chat.agent reports its model calls through the same telemetry. chat.toStreamTextOptions() folds it in with the stored prompt, model, and config, so once you set a system prompt with chat.prompt.set(), every turn shows up in the trace with no extra wiring.

Get started

Add experimental_telemetry: { isEnabled: true } to a task, trigger a run, and the whole workload starts filling in. Custom metadata and prompt linking are in the AI observability docs.

Ready to start building?

Build and deploy your first task in 3 minutes.

Get started now