Normalize legacy function_call finish reason on the Chat path - #1103
PratikDhanave (PratikDhanave) wants to merge 1 commit into
Conversation
Some OpenAI-compatible endpoints still emit the deprecated "function_call" finish reason. The Chat path forwarded the raw string verbatim, so callers saw "function_call" where the Python and .NET clients report "tool_calls". Normalize "function_call" to "tool_calls" on both the streaming and non-streaming paths, matching the Python client; other reasons pass through unchanged.
There was a problem hiding this comment.
🟡 Changes recommended
Add regression coverage for the streaming Chat path.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Normalizes legacy Chat finish reasons from function_call to tool_calls for compatibility across response paths.
Changes:
- Added shared finish-reason normalization.
- Applied it to streaming and non-streaming Chat responses.
- Added non-streaming regression coverage.
File summaries
| File | Summary |
|---|---|
provider/openaiprovider/chat.go |
Normalizes legacy finish reasons on both Chat paths. |
provider/openaiprovider/chat_test.go |
Tests non-streaming normalization; streaming coverage is still requested. |
Review note (nit, 3 votes): Add streaming regression coverage for function_call normalization.
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| var finishReason string | ||
| if len(chunk.Choices) > 0 { | ||
| finishReason = chunk.Choices[0].FinishReason | ||
| finishReason = normalizeChatFinishReason(chunk.Choices[0].FinishReason) |
|
Scope: user-visible behavior (internal implementation, no exported API change)
|
Some OpenAI-compatible endpoints still emit the deprecated
"function_call"finish reason. The Chat path assignedchoice.FinishReason/chunk.Choices[0].FinishReasonverbatim, so a caller hitting such an endpoint sawFinishReason == "function_call"where the Python and .NET clients report"tool_calls".The Python client normalizes this on both parse paths:
finish_reason = "tool_calls" if choice.finish_reason == "function_call" else choice.finish_reason.Change
normalizeChatFinishReasonand apply it at both the non-streaming and streaming finish-reason assignment sites. Only"function_call"is remapped; all other reasons pass through unchanged.This is distinct from the already-implemented Responses-path
tool_callssynthesis — this remaps a legacy finish-reason string on the Chat path.Test
TestChatLegacyFunctionCallFinishReasonNormalized: a canned completion with"finish_reason":"function_call"now yieldsFinishReason == "tool_calls". Fails before the change ("function_call"), passes after.