diff --git a/packages/ai/src/api/pi-messages.ts b/packages/ai/src/api/pi-messages.ts index 605194937..83ddc06c7 100644 --- a/packages/ai/src/api/pi-messages.ts +++ b/packages/ai/src/api/pi-messages.ts @@ -74,6 +74,7 @@ export type PiMessagesEvent = reason: Extract; usage: PiMessagesUsage; responseId?: string; + responseModel?: string; providerThinkingLevel?: string; rewrite?: PiMessagesRewriteImpact; } @@ -83,6 +84,7 @@ export type PiMessagesEvent = usage: PiMessagesUsage; errorMessage?: string; responseId?: string; + responseModel?: string; providerThinkingLevel?: string; rewrite?: PiMessagesRewriteImpact; }; @@ -199,6 +201,9 @@ function createEventConverter(model: Model<"pi-messages">) { usage: event.usage, responseId: event.responseId, }); + if (event.responseModel !== undefined) { + partial.responseModel = event.responseModel; + } if (event.providerThinkingLevel !== undefined) { partial.providerThinkingLevel = event.providerThinkingLevel; } @@ -211,6 +216,9 @@ function createEventConverter(model: Model<"pi-messages">) { errorMessage: event.errorMessage, responseId: event.responseId, }); + if (event.responseModel !== undefined) { + partial.responseModel = event.responseModel; + } if (event.providerThinkingLevel !== undefined) { partial.providerThinkingLevel = event.providerThinkingLevel; } diff --git a/packages/ai/src/types.ts b/packages/ai/src/types.ts index d1fcee314..32b5a5f31 100644 --- a/packages/ai/src/types.ts +++ b/packages/ai/src/types.ts @@ -588,7 +588,7 @@ export interface AssistantMessage { api: Api; provider: ProviderId; model: string; - responseModel?: string; // Concrete model reported by the provider when different from the requested `model` + responseModel?: string; // Concrete model reported by the provider, when reported. May equal the requested `model` responseId?: string; // Provider-specific response/message identifier when the upstream API exposes one /** Exact provider-native effort level used for this response. Absent for legacy or unmanaged responses. */ providerThinkingLevel?: string; diff --git a/packages/ai/test/pi-messages.test.ts b/packages/ai/test/pi-messages.test.ts index 211c24cb0..54b3d4d9e 100644 --- a/packages/ai/test/pi-messages.test.ts +++ b/packages/ai/test/pi-messages.test.ts @@ -116,6 +116,7 @@ describe("pi-messages", () => { reason: "toolUse", usage, responseId: "resp_1", + responseModel: "claude-opus-5-5", providerThinkingLevel: "high", }, ], @@ -145,6 +146,7 @@ describe("pi-messages", () => { expect(message.responseId).toBe("resp_1"); expect(message.providerThinkingLevel).toBe("high"); expect(message.model).toBe("auto"); + expect(message.responseModel).toBe("claude-opus-5-5"); expect(message.provider).toBe("radius"); expect(message.content).toEqual([ { type: "text", text: "Hello", textSignature: undefined }, @@ -190,6 +192,7 @@ describe("pi-messages", () => { expect(eventModels).toEqual(wireEvents.map(() => model)); expect(message.stopReason).toBe("stop"); expect(message.responseId).toBe("resp_1"); + expect(message).not.toHaveProperty("responseModel"); expect(message.content).toEqual([{ type: "text", text: "Hello", textSignature: undefined }]); }); @@ -234,7 +237,16 @@ describe("pi-messages", () => { it("propagates server-sent error events", async () => { const { baseUrl } = await startServer({ - events: [{ type: "start" }, { type: "error", reason: "error", usage, errorMessage: "Upstream failed" }], + events: [ + { type: "start" }, + { + type: "error", + reason: "error", + usage, + errorMessage: "Upstream failed", + responseModel: "claude-opus-5-5", + }, + ], }); const model = createModel(baseUrl); @@ -243,6 +255,7 @@ describe("pi-messages", () => { expect(message.stopReason).toBe("error"); expect(message.errorMessage).toBe("Upstream failed"); expect(message.usage).toEqual(usage); + expect(message.responseModel).toBe("claude-opus-5-5"); }); it("errors when no API key is provided", async () => { diff --git a/packages/coding-agent/docs/message-types.md b/packages/coding-agent/docs/message-types.md index dfdafd505..6c5d95c63 100644 --- a/packages/coding-agent/docs/message-types.md +++ b/packages/coding-agent/docs/message-types.md @@ -140,7 +140,7 @@ interface AssistantMessage { } ``` -`responseModel` records a concrete provider response model when it differs from the requested model. `responseId`, `providerThinkingLevel`, `thinkingLevel`, `diagnostics`, and `rawStopReason` preserve provider or runtime details. +`responseModel` records the concrete model reported by the provider, when the provider reports one. It may equal the requested model. `responseId`, `providerThinkingLevel`, `thinkingLevel`, `diagnostics`, and `rawStopReason` preserve provider or runtime details. `"pending"` is used for a partial assistant message while it streams. The completed message in `message_end` has a terminal stop reason, and Pi does not persist `"pending"` assistant messages in session JSONL.