mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-17 05:00:16 +00:00
fix(ai): classify Google thoughtSignature as thinking
Google streaming may emit thoughtSignature without thought=true (including empty-text signature-only parts). Treat non-empty thoughtSignature as thinking to avoid leaking reasoning into normal text and retain signature across streaming deltas. Add unit test coverage.
This commit is contained in:
parent
e80a924292
commit
e42e9e6305
5 changed files with 108 additions and 9 deletions
|
|
@ -9,6 +9,38 @@ import { transformMessages } from "./transorm-messages.js";
|
|||
|
||||
type GoogleApiType = "google-generative-ai" | "google-gemini-cli" | "google-vertex";
|
||||
|
||||
/**
|
||||
* Determines whether a streamed Gemini `Part` should be treated as "thinking".
|
||||
*
|
||||
* Protocol note (Gemini / Vertex AI thought signatures):
|
||||
* - `thoughtSignature` may appear without `thought: true` (including in empty-text parts at the end of streaming).
|
||||
* - When persisting/replaying model outputs, signature-bearing parts must be preserved as-is;
|
||||
* do not merge/move signatures across parts.
|
||||
* - Our streaming representation uses content blocks, so we classify any non-empty `thoughtSignature`
|
||||
* as thinking to avoid leaking thought content into normal assistant text.
|
||||
*
|
||||
* Some Google backends send thought content with `thoughtSignature` but omit `thought: true`
|
||||
* on subsequent deltas. We treat any non-empty `thoughtSignature` as thinking to avoid
|
||||
* leaking thought text into the normal assistant text stream.
|
||||
*/
|
||||
export function isThinkingPart(part: Pick<Part, "thought" | "thoughtSignature">): boolean {
|
||||
return part.thought === true || (typeof part.thoughtSignature === "string" && part.thoughtSignature.length > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retain thought signatures during streaming.
|
||||
*
|
||||
* Some backends only send `thoughtSignature` on the first delta for a given part/block; later deltas may omit it.
|
||||
* This helper preserves the last non-empty signature for the current block.
|
||||
*
|
||||
* Note: this does NOT merge or move signatures across distinct response parts. It only prevents
|
||||
* a signature from being overwritten with `undefined` within the same streamed block.
|
||||
*/
|
||||
export function retainThoughtSignature(existing: string | undefined, incoming: string | undefined): string | undefined {
|
||||
if (typeof incoming === "string" && incoming.length > 0) return incoming;
|
||||
return existing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert internal messages to Gemini Content[] format.
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue