Populate normalized finish_reason from Google finishReason (+ fix streaming drop)#67
Merged
Conversation
7de2a47 to
fb69c60
Compare
…eaming drop)
Map the candidate-level finishReason onto the normalized
OmniAI::Chat::FinishReason symbols via a mapping table next to the deserializer:
STOP -> :stop
MAX_TOKENS -> :length
SAFETY/RECITATION/LANGUAGE/BLOCKLIST/
PROHIBITED_CONTENT/SPII/IMAGE_SAFETY -> :filter
The whole content-policy family maps to :filter; unrecognized values (OTHER,
MALFORMED_FUNCTION_CALL, ...) -> :other. The raw finishReason stays available
via response.data.
Also fixes a streaming bug: finishReason arrives on the terminal chunk after
content has streamed, but Stream#merge_candidate! only merged content.parts
into an already-seen candidate and dropped top-level keys -- so finishReason
(and thus :length on a MAX_TOKENS cutoff) was silently lost. The merge now
preserves top-level candidate keys and no longer skips content-less terminal
chunks.
Requires omniai ~> 3.7. Releases as 3.10.0.
f6e7ac1 to
6c415ad
Compare
omniai 3.7 widened its http constraint to >= 5, < 7. Make the google gem work under http 6 (while staying compatible with http 5): - transcribe: http 6's .timeout no longer accepts a per-operation hash via keyword-splat, and rejects nil per-operation values. Pass the options hash positionally, and return :null (the http no-timeout sentinel, valid in both 5 and 6) from #http_timeout_options when no timeout is configured. - client_spec: HTTP.persistent returns HTTP::Session on http 6 (HTTP::Client on http 5); assert the request contract instead of the version-specific class. Suite green under both http 6.0.3 and http 5.3.1. Lock resolves omniai 3.7.0.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Maps the Gemini candidate
finishReasononto a normalizedOmniAI::Chat::FinishReasonvalue object onResponse#finish_reason(and per-choice) — and fixes a real, pre-existing streaming bug that silently dropped it.Mapping (table lives next to the deserializer)
finishReasonreasonSTOP:stopMAX_TOKENS:lengthSAFETY,RECITATION,LANGUAGE,BLOCKLIST,PROHIBITED_CONTENT,SPII,IMAGE_SAFETY:filterOTHER,MALFORMED_FUNCTION_CALL, anything unrecognized:otherThe whole content-policy family maps to
:filter(so a consumer branching on:filtercatches every policy termination), while the verbatim token is preserved asfinish_reason.value— soRECITATIONvsSAFETYgranularity is never lost. Values verified against the FinishReason enum.Streaming fix — a REAL pre-existing bug (independent of this feature)
In real Gemini streams,
finishReasonarrives on the terminal chunk, after content has streamed. ButStream#merge_candidate!only mergedcontent.partsinto an already-seen candidate and dropped top-level keys, andprocess_candidate!skipped candidates with nocontententirely. Net effect: on a streamedMAX_TOKENS/SAFETYcutoff,finishReasonwas silently lost — the exact failures a blank-completion detector exists to catch. (Two pre-existing specs even encoded the dropped behavior as "correct.")Verified merge semantics after the fix:
finishReasonarriving on a later chunk is merged onto the existing candidate (not discarded);dig-guarded part iteration — no spurious deltas yielded for content-less chunks;merge_part!tolerates a content-less first chunk —content/partsare initialized on demand, so any chunk ordering assembles correctly.Two pre-existing specs corrected to the preserving behavior, plus a new regression spec for the realistic ordering (content → terminal
finishReason-only chunk).Tests
TDD; non-streaming + streaming + regression + a mapping-table spec (reason + verbatim-value preservation).
284 examples, 0 failures; rubocop clean.Dependency / release ordering
Bumps the
omniaifloor to~> 3.7; releases as omniai-google 3.10.0.