Run Telemetry Model ID Extraction#
Overview#
When assistant-ui reports a run to the cloud backend, it includes a model_id field derived during telemetry extraction. Model ID is never read directly from the AI SDK's native response object — it is only available if the application explicitly forwards it through messageMetadata. This is the core gap: if a route omits the messageMetadata callback (or omits the finish-step branch), model_id will be absent from all cloud run reports.
How Model ID Flows In#
The model ID must be injected by the API route via the AI SDK's messageMetadata callback in toUIMessageStreamResponse. The cloud template route shows the canonical pattern:
- On
finish-step,part.response.modelIdis surfaced and returned as{ modelId: part.response.modelId }. - On
finish, token usage is forwarded as{ usage: part.totalUsage }.
This metadata is serialized into the AI SDK UI message's metadata field and flows to the client.
Extraction Logic#
packages/cloud-ai-sdk — extractRunTelemetry.ts#
The extractRunTelemetry function (used by the cloud-ai-sdk package) processes the last assistant UIMessage and reads model_id only from message.metadata.modelId :
const metadata = assistant.metadata as Record<string, unknown> | undefined;
const modelId =
typeof metadata?.modelId === "string" ? metadata.modelId : undefined;
There is no fallback to any native AI SDK response property. If messageMetadata was not configured on the route, modelId is undefined and is omitted from the returned RunTelemetryData .
packages/core — AssistantCloudThreadHistoryAdapter.ts#
The history adapter contains two parallel extraction paths :
| Format | Extractor |
|---|---|
aui/v0 | extractAuiV0 |
ai-sdk/v6 | extractAiSdkV6 / aggregateAiSdkV6RunSteps |
Both paths funnel through extractModelId, which checks two locations in message metadata:
metadata.modelId(top-level)metadata.custom.modelId(nested custom field)
Neither path reads the AI SDK's native response.modelId directly from the stream or response object — they only read what was forwarded into the message metadata by the route.
For aui/v0, the lookup is :
const modelId =
msg.metadata?.modelId ??
(typeof msg.metadata?.custom?.modelId === "string"
? msg.metadata.custom.modelId
: undefined);
Final Report#
The extracted modelId (or undefined) is included in the _sendReport payload as model_id and submitted via this.cloudRef.current.runs.report(...) .
The Gap#
The AI SDK exposes the actual model that served the request as part.response.modelId on the finish-step event. This is the provider-confirmed model ID (e.g., the resolved alias like gpt-4o-2024-08-06). The extraction layer never reads this directly — it only reads whatever the application chose to pass through messageMetadata.
Consequences:
- Routes that omit the
messageMetadatacallback entirely will produce run reports with nomodel_id. - Routes that only handle
finish(usage) but skipfinish-stepwill also omitmodel_id. - If a route forwards
modelIdfor only some steps in a multi-step run, only the last step's metadata is used (sinceaggregateAiSdkV6RunStepsoverwritesmetadataon each assistant message it encounters) .
Required Route Configuration#
To populate model_id in telemetry, every API route must include the finish-step branch in messageMetadata :
messageMetadata: ({ part }) => {
if (part.type === "finish-step") {
return { modelId: part.response.modelId };
}
if (part.type === "finish") {
return { usage: part.totalUsage };
}
return undefined;
},
Without this, the cloud dashboard will show runs with missing model information. See the cloud template route for the full working example.
Key Files#
| File | Role |
|---|---|
packages/cloud-ai-sdk/src/core/extractRunTelemetry.ts | Extracts telemetry from AI SDK UIMessage array (used by cloud-ai-sdk package) |
packages/core/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts | Main cloud history adapter; contains extractModelId, extractAuiV0, extractAiSdkV6 |
templates/cloud/app/api/chat/route.ts | Reference route showing correct messageMetadata configuration |