mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-20 14:05:08 +00:00
fix(ai): remove <thinking> tag wrapping, convert to plain text on cross-model handoff
- Remove <thinking> tag generation from google-shared.ts, transorm-messages.ts, openai-completions.ts - Thinking blocks now convert to plain text when switching models (prevents models mimicking tags) - Skip empty thinking blocks to avoid API errors - Keep thinking blocks only when same provider AND same model fixes #561
This commit is contained in:
parent
8fda948866
commit
16e142ef7d
5 changed files with 17 additions and 12 deletions
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
- Fixed Gemini CLI abort handling: detect native `AbortError` in retry catch block, cancel SSE reader when abort signal fires ([#568](https://github.com/badlogic/pi-mono/pull/568) by [@tmustier](https://github.com/tmustier))
|
- Fixed Gemini CLI abort handling: detect native `AbortError` in retry catch block, cancel SSE reader when abort signal fires ([#568](https://github.com/badlogic/pi-mono/pull/568) by [@tmustier](https://github.com/tmustier))
|
||||||
- Fixed Antigravity provider 429 errors by aligning request payload with CLIProxyAPI v6.6.89: inject Antigravity system instruction with `role: "user"`, set `requestType: "agent"`, and use `antigravity` userAgent. Added bridge prompt to override Antigravity behavior (identity, paths, web dev guidelines) with Pi defaults. ([#571](https://github.com/badlogic/pi-mono/pull/571) by [@ben-vargas](https://github.com/ben-vargas))
|
- Fixed Antigravity provider 429 errors by aligning request payload with CLIProxyAPI v6.6.89: inject Antigravity system instruction with `role: "user"`, set `requestType: "agent"`, and use `antigravity` userAgent. Added bridge prompt to override Antigravity behavior (identity, paths, web dev guidelines) with Pi defaults. ([#571](https://github.com/badlogic/pi-mono/pull/571) by [@ben-vargas](https://github.com/ben-vargas))
|
||||||
|
- Fixed thinking block handling for cross-model conversations: thinking blocks are now converted to plain text (no `<thinking>` tags) when switching models. Previously, `<thinking>` tags caused models to mimic the pattern and output literal tags. Also fixed empty thinking blocks causing API errors. ([#561](https://github.com/badlogic/pi-mono/issues/561))
|
||||||
|
|
||||||
## [0.38.0] - 2026-01-08
|
## [0.38.0] - 2026-01-08
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,8 @@ export function convertMessages<T extends GoogleApiType>(model: Model<T>, contex
|
||||||
}
|
}
|
||||||
} else if (msg.role === "assistant") {
|
} else if (msg.role === "assistant") {
|
||||||
const parts: Part[] = [];
|
const parts: Part[] = [];
|
||||||
|
// Check if message is from same provider and model - only then keep thinking blocks
|
||||||
|
const isSameProviderAndModel = msg.provider === model.provider && msg.model === model.id;
|
||||||
|
|
||||||
for (const block of msg.content) {
|
for (const block of msg.content) {
|
||||||
if (block.type === "text") {
|
if (block.type === "text") {
|
||||||
|
|
@ -84,17 +86,19 @@ export function convertMessages<T extends GoogleApiType>(model: Model<T>, contex
|
||||||
if (!block.text || block.text.trim() === "") continue;
|
if (!block.text || block.text.trim() === "") continue;
|
||||||
parts.push({ text: sanitizeSurrogates(block.text) });
|
parts.push({ text: sanitizeSurrogates(block.text) });
|
||||||
} else if (block.type === "thinking") {
|
} else if (block.type === "thinking") {
|
||||||
// Thinking blocks require signatures for Claude via Antigravity.
|
// Skip empty thinking blocks
|
||||||
// If signature is missing (e.g. from GPT-OSS), convert to regular text with delimiters.
|
if (!block.thinking || block.thinking.trim() === "") continue;
|
||||||
if (block.thinkingSignature) {
|
// Only keep as thinking block if same provider AND same model
|
||||||
|
// Otherwise convert to plain text (no tags to avoid model mimicking them)
|
||||||
|
if (isSameProviderAndModel) {
|
||||||
parts.push({
|
parts.push({
|
||||||
thought: true,
|
thought: true,
|
||||||
text: sanitizeSurrogates(block.thinking),
|
text: sanitizeSurrogates(block.thinking),
|
||||||
thoughtSignature: block.thinkingSignature,
|
...(block.thinkingSignature && { thoughtSignature: block.thinkingSignature }),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
parts.push({
|
parts.push({
|
||||||
text: `<thinking>\n${sanitizeSurrogates(block.thinking)}\n</thinking>`,
|
text: sanitizeSurrogates(block.thinking),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (block.type === "toolCall") {
|
} else if (block.type === "toolCall") {
|
||||||
|
|
|
||||||
|
|
@ -490,10 +490,8 @@ function convertMessages(
|
||||||
const nonEmptyThinkingBlocks = thinkingBlocks.filter((b) => b.thinking && b.thinking.trim().length > 0);
|
const nonEmptyThinkingBlocks = thinkingBlocks.filter((b) => b.thinking && b.thinking.trim().length > 0);
|
||||||
if (nonEmptyThinkingBlocks.length > 0) {
|
if (nonEmptyThinkingBlocks.length > 0) {
|
||||||
if (compat.requiresThinkingAsText) {
|
if (compat.requiresThinkingAsText) {
|
||||||
// Convert thinking blocks to text with <thinking> delimiters
|
// Convert thinking blocks to plain text (no tags to avoid model mimicking them)
|
||||||
const thinkingText = nonEmptyThinkingBlocks
|
const thinkingText = nonEmptyThinkingBlocks.map((b) => b.thinking).join("\n\n");
|
||||||
.map((b) => `<thinking>\n${b.thinking}\n</thinking>`)
|
|
||||||
.join("\n");
|
|
||||||
const textContent = assistantMsg.content as Array<{ type: "text"; text: string }> | null;
|
const textContent = assistantMsg.content as Array<{ type: "text"; text: string }> | null;
|
||||||
if (textContent) {
|
if (textContent) {
|
||||||
textContent.unshift({ type: "text", text: thinkingText });
|
textContent.unshift({ type: "text", text: thinkingText });
|
||||||
|
|
|
||||||
|
|
@ -45,12 +45,13 @@ export function transformMessages<TApi extends Api>(messages: Message[], model:
|
||||||
assistantMsg.api !== model.api;
|
assistantMsg.api !== model.api;
|
||||||
|
|
||||||
// Transform message from different provider/model
|
// Transform message from different provider/model
|
||||||
const transformedContent = assistantMsg.content.map((block) => {
|
const transformedContent = assistantMsg.content.flatMap((block) => {
|
||||||
if (block.type === "thinking") {
|
if (block.type === "thinking") {
|
||||||
// Convert thinking block to text block with <thinking> tags
|
// Skip empty thinking blocks, convert others to plain text
|
||||||
|
if (!block.thinking || block.thinking.trim() === "") return [];
|
||||||
return {
|
return {
|
||||||
type: "text" as const,
|
type: "text" as const,
|
||||||
text: `<thinking>\n${block.thinking}\n</thinking>`,
|
text: block.thinking,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Normalize tool call IDs for github-copilot cross-API switches
|
// Normalize tool call IDs for github-copilot cross-API switches
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
- ESC key now works during "Working..." state after auto-retry ([#568](https://github.com/badlogic/pi-mono/pull/568) by [@tmustier](https://github.com/tmustier))
|
- ESC key now works during "Working..." state after auto-retry ([#568](https://github.com/badlogic/pi-mono/pull/568) by [@tmustier](https://github.com/tmustier))
|
||||||
- Abort messages now show correct retry attempt count (e.g., "Aborted after 2 retry attempts") ([#568](https://github.com/badlogic/pi-mono/pull/568) by [@tmustier](https://github.com/tmustier))
|
- Abort messages now show correct retry attempt count (e.g., "Aborted after 2 retry attempts") ([#568](https://github.com/badlogic/pi-mono/pull/568) by [@tmustier](https://github.com/tmustier))
|
||||||
- Fixed Antigravity provider returning 429 errors despite available quota ([#571](https://github.com/badlogic/pi-mono/pull/571) by [@ben-vargas](https://github.com/ben-vargas))
|
- Fixed Antigravity provider returning 429 errors despite available quota ([#571](https://github.com/badlogic/pi-mono/pull/571) by [@ben-vargas](https://github.com/ben-vargas))
|
||||||
|
- Fixed malformed thinking text in Gemini/Antigravity responses where thinking content appeared as regular text or vice versa. Cross-model conversations now properly convert thinking blocks to plain text. ([#561](https://github.com/badlogic/pi-mono/issues/561))
|
||||||
|
|
||||||
## [0.38.0] - 2026-01-08
|
## [0.38.0] - 2026-01-08
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue