Skip to content

fix(agents): normalize Gemini finish_reason values for the OpenAI SDK#372

Open
manana2520 wants to merge 1 commit into
AIDotNet:mainfrom
keboola:feat/gemini-finish-reason-normalizer
Open

fix(agents): normalize Gemini finish_reason values for the OpenAI SDK#372
manana2520 wants to merge 1 commit into
AIDotNet:mainfrom
keboola:feat/gemini-finish-reason-normalizer

Conversation

@manana2520

Copy link
Copy Markdown
Contributor

Part of #371 (tracking issue for upstreaming generic features from a self-hosted fork).

Problem

When an OpenAI-compatible provider is backed by Gemini, the streamed SSE responses carry non-OpenAI finish_reason values (STOP, MAX_TOKENS, SAFETY, RECITATION, …). The OpenAI SDK cannot deserialize these and the agent run fails — observed during wiki generation / chat with a Gemini-backed provider.

Fix

Add FinishReasonNormalizingHandler as the outermost handler in the agent HttpClient chain:

FinishReasonNormalizingHandler -> LoggingHttpHandler -> HttpClientHandler

It rewrites the streamed finish_reason to the OpenAI SDK's expected set after LoggingHttpHandler's retry logic has produced the final response, and before deserialization. Values that are already valid (non-Gemini providers) pass through unchanged, so there is no behavioural change for OpenAI/Anthropic/etc.

Tests

Adds tests/OpenDeepWiki.Tests/Agents/FinishReasonNormalizingHandlerTests.cs covering the value mapping and the SSE line transform (32 cases). dotnet test green; relies on the existing InternalsVisibleTo("OpenDeepWiki.Tests").

Scope

Three files: the new handler, the AgentFactory handler-chain wiring, and the test. No schema/config/API changes.

Gemini's OpenAI-compatible SSE responses carry non-OpenAI finish_reason values
(STOP, MAX_TOKENS, SAFETY, RECITATION, ...) that the OpenAI SDK cannot deserialize,
surfacing as run failures during wiki generation/chat with a Gemini-backed provider.

Add FinishReasonNormalizingHandler as the outermost handler in the agent HttpClient
chain (FinishReasonNormalizingHandler -> LoggingHttpHandler -> HttpClientHandler) so the
streamed response is rewritten to the SDK's expected finish_reason set before
deserialization. Non-Gemini providers are unaffected (values already valid pass through).

Adds unit tests for the value mapping and the SSE line transform.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant