[dotnet-port-api] Add Response.ModelID - #1156
PratikDhanave (PratikDhanave) wants to merge 1 commit into
Conversation
Response and ResponseUpdate now carry ModelID, the identifier of the model that produced the response, matching M.E.AI ChatResponse/ChatResponseUpdate (ProcessUpdate folds ModelId). Response.Update folds it like ResponseID, and ToUpdates round-trips it. The OpenAI (chat + responses), Anthropic, and Gemini providers populate it from the model on the response. ConversationID is intentionally not added: in Go the conversation identity lives on Session.ServiceID, so a separate field would be redundant. Implements microsoft#1140.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Streaming model IDs and metadata-only round-tripping remain incomplete.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 4
Open (4)
What changed in this PR
Adds provider-agnostic ModelID fields to agent responses and response updates, with provider mappings and folding support.
Changes:
- Adds and propagates
ModelID. - Surfaces model IDs across OpenAI, Anthropic, and Gemini providers.
- Adds response and OpenAI chat tests.
| File | Reviewed changes |
|---|---|
provider/openaiprovider/responses.go |
Non-streaming model ID is populated; streaming updates still omit it. |
provider/openaiprovider/chat.go |
Surfaces the model ID for chat responses. |
provider/openaiprovider/chat_test.go |
Tests OpenAI chat model ID exposure. |
provider/geminiprovider/agent.go |
Non-streaming model ID is populated; streaming updates still omit it. |
provider/anthropicprovider/agent.go |
Non-streaming model ID is populated; streaming updates still omit it. |
agent/response.go |
Adds and folds ModelID; metadata-only updates omit it during round-tripping. |
agent/response_test.go |
Tests model ID folding and round-tripping. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| AgentID: resp.AgentID, | ||
| MessageID: msg.ID, | ||
| ResponseID: resp.ID, | ||
| ModelID: resp.ModelID, |
| Role: message.RoleAssistant, | ||
| MessageID: resp.ID, | ||
| ResponseID: resp.ID, | ||
| ModelID: string(resp.Model), |
| yield(&agent.ResponseUpdate{ | ||
| Contents: responseContents, | ||
| Role: message.RoleAssistant, | ||
| ModelID: resp.ModelVersion, |
|
|
||
| currentUpdate := &agent.ResponseUpdate{ | ||
| ResponseID: resp.ID, | ||
| ModelID: resp.Model, |
|
Scope: public API Changed Go contract: Upstream evidence reviewed:
Result: findings reported. Both upstream .NET and Python implementations intentionally keep model identity on the lower-level
|
There was a problem hiding this comment.
Generated by Go API Consistency Review Agent · copilot · auto · 98.2 AIC · ⌖ 5.96 AIC · ⊞ 9.2K
|
|
||
| // ModelID is the identifier of the model that produced this response, when | ||
| // the provider supplies it. It is empty otherwise. | ||
| ModelID string `json:",omitzero"` |
There was a problem hiding this comment.
This PR adds ModelID to agent.Response/agent.ResponseUpdate (the Go equivalent of upstream AgentResponse/AgentResponseUpdate, not ChatResponse/ChatResponseUpdate). Upstream, ModelId/model exists only on the lower-level ChatResponse/ChatResponseUpdate types:
- .NET:
Microsoft.Agents.AI.Abstractions/AgentResponse.csandAgentResponseUpdate.cshave noModelIdproperty (properties areAgentId,ResponseId,ContinuationToken,CreatedAt,FinishReason,Usage,RawRepresentation,AdditionalProperties).ModelIdis folded only inAIAgentChatClient.CloneWithConversationIdonChatResponse(dotnet/src/Microsoft.Agents.AI/ChatClient/AIAgentChatClient.cs:368), and referenced inAgentResponseUpdateTests.ConstructorWithChatResponseUpdateRoundtrips(dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateTests.cs:42) as aChatResponseUpdatefield that is not asserted to roundtrip ontoAgentResponseUpdate. - Python:
AgentResponse.__init__/AgentResponseUpdate.__init__(python/packages/core/agent_framework/_types.py:2848and:3134) take nomodel/model_idparameter, while the siblingChatResponse.__init__/ChatResponseUpdate.__init__(same file,:2449and:2735) do.
So both upstream implementations deliberately keep model identity at the chat-client layer and omit it from the agent-level response contract. Adding ModelID unconditionally to Go's agent.Response/ResponseUpdate (the AgentResponse analog) is a divergence from that design, not merely an idiomatic Go difference — it's a new agent-level field neither upstream implementation exposes.
Suggested resolution: either (a) confirm with maintainers this is an intentional Go-specific enhancement over the agent-level contract (and document that divergence), or (b) if a ModelID surface is wanted, scope it to a chat-level type analogous to ChatResponse/ChatResponseUpdate if/when Go introduces one, rather than agent.Response.

Implements #1140.
M.E.AI
ChatResponse/ChatResponseUpdatecarry aModelIdthatProcessUpdatefolds onto the response. Go had no canonical, provider-agnostic way to read which model produced a response — it was only surfaced ad hoc via providerAdditionalProperties.Change
ModelIDtoagent.Responseandagent.ResponseUpdate.Response.Update(likeResponseID/FinishReason) and propagate it inToUpdates.ConversationIDfrom the issue is intentionally omitted: Go conversation identity lives onSession.ServiceID, so a separate field would be redundant.Tests
TestResponse_Update_ModelID: ModelID folds from a later update and round-trips throughToUpdates.TestChatModelIDSurfaced: the OpenAI chat provider surfaces the response model onResponse.ModelID.