Skip to content

fix: preserve prompt_cache_key in Responses API, escape \n in tagContent (#517, #515) - #518

Merged
diegosouzapw merged 1 commit into
mainfrom
fix/issue-517-515-prompt-cache-key-tagcontent
Mar 21, 2026
Merged

diegosouzapw merged 1 commit into
mainfrom
fix/issue-517-515-prompt-cache-key-tagcontent

Conversation

@diegosouzapw

Copy link
Copy Markdown
Owner

Fixes

Closes #517, #515

Changes

fix(translator): preserve prompt_cache_key (#517)

Codex uses prompt_cache_key as a cache-affinity signal — the same key routes the request to the same cached slot. OmniRoute was deleting it during Responses API → Chat Completions translation, which prevented caching from working.

Files changed:

  • open-sse/translator/request/openai-responses.ts — removed delete result.prompt_cache_key
  • open-sse/translator/helpers/responsesApiHelper.ts — removed delete result.prompt_cache_key

fix(combo): escape \n in tagContent JSON injection (#515)

The tagContent variable used template literal newlines (U+000A character). When injected into a JSON string via regex replacement, these raw newlines produce invalid JSON (unescaped control characters). Replaced with literal \n escape sequences.

File changed:

  • open-sse/services/combo.tstagContent now uses \\n instead of actual newlines

Tests

  • 821/821 pass ✅

…n in tagContent (#517, #515)

fix(translator): preserve prompt_cache_key when translating Responses API requests
  (#517) — prompt_cache_key is an account-affinity signal used by Codex for
  prompt cache routing. Deleting it from the translated request prevented full
  cache effectiveness. Removed delete from openai-responses.ts and
  responsesApiHelper.ts cleanup blocks.

fix(combo): escape \n in tagContent so injected JSON string is valid (#515)
  — omniModel tag content used template literal newlines (U+000A) which produce
  unescaped newline chars inside a JSON string value. Replaced with literal \n
  escape sequences for valid JSON injection in streaming SSE content chunks.
Copilot AI review requested due to automatic review settings March 21, 2026 20:11
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request implements two crucial fixes to enhance system stability and functionality. It addresses an issue where the prompt_cache_key was inadvertently removed during API translation, which prevented effective caching. Concurrently, it resolves a JSON parsing error by correctly escaping newline characters in dynamically generated content, ensuring valid JSON output. These changes collectively improve the reliability of API interactions and data integrity.

Highlights

  • Preserved prompt_cache_key: The prompt_cache_key is now intentionally preserved during Responses API to Chat Completions translation. This ensures that caching mechanisms, particularly for providers like Codex, can correctly utilize this key as a cache-affinity signal, preventing caching from breaking.
  • Escaped newlines in tagContent: Newline characters (\n) within the tagContent variable are now properly escaped. Previously, raw newlines were causing invalid JSON injection when the content was used in regex replacements, which has now been fixed by using \\n.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces two fixes. The first preserves the prompt_cache_key in the Responses API to Chat Completions translation, which is a necessary fix for cache affinity with providers like Codex. This change is correct and well-documented. The second fix escapes newline characters in tagContent to prevent invalid JSON during stream injection. While this solves the issue for the streaming case, I've found a critical issue where this change breaks another part of the code that also uses tagContent. Please see my detailed comment.

// that marker is silently dropped.
if (!res.body) return res;
const tagContent = `\n<omniModel>${modelStr}</omniModel>\n`;
const tagContent = `\\n<omniModel>${modelStr}</omniModel>\\n`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This change fixes the JSON injection for streaming responses by escaping the newline characters. However, this breaks another usage of tagContent in the flush part of the TransformStream (line 514), where it's used in a JSON.stringify call.

With this change, tagContent is a string containing a literal backslash and 'n' (e.g., "\\n..."). When JSON.stringify({ content: tagContent }) is called, the backslash is also escaped, resulting in "\\\\n..." in the JSON output. The client will then parse this as a literal \n string, not a newline character.

To address this, tagContent should contain raw newlines when passed to JSON.stringify. A comprehensive fix would involve defining the raw content and escaping it only for the regex injection. Since that requires changing code outside this diff, a more localized fix would be to un-escape tagContent at line 514 before it's stringified. For example: delta: { content: tagContent.replace(/\\n/g, '\n') }.

Please adjust the implementation to ensure both use cases are handled correctly.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes two translation/streaming edge cases: keep prompt_cache_key during OpenAI Responses → Chat Completions translation to preserve Codex prompt-cache affinity, and avoid invalid JSON in combo streaming tag injection by eliminating raw newline characters in the injected tagContent.

Changes:

  • Preserve prompt_cache_key in Responses→Chat translation cleanup steps (fixes Codex cache-affinity regression, #517).
  • Change combo streaming <omniModel> tag injection to use escaped newline sequences instead of raw newlines to keep injected JSON valid (#515).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
open-sse/translator/request/openai-responses.ts Stops deleting prompt_cache_key during Responses→Chat translation cleanup.
open-sse/translator/helpers/responsesApiHelper.ts Stops deleting prompt_cache_key during Responses→Chat conversion helper cleanup.
open-sse/services/combo.ts Updates streamed <omniModel> tag injection to avoid raw newlines in JSON-string regex replacement.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// that marker is silently dropped.
if (!res.body) return res;
const tagContent = `\n<omniModel>${modelStr}</omniModel>\n`;
const tagContent = `\\n<omniModel>${modelStr}</omniModel>\\n`;

Copilot AI Mar 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tagContent is now pre-escaped with \\n, but later you embed it into JSON in two different ways: (1) via raw string replacement into an existing JSON line, and (2) via JSON.stringify(...) in flush(). With the new value, path (2) will double-escape backslashes so clients receive a literal "\n" sequence, while path (1) will be parsed as an actual newline escape, making behavior inconsistent. Consider keeping tagContent as real newlines and using proper JSON escaping for the replacement (e.g., derive an escaped form from JSON.stringify(tagContent)), or otherwise ensure both paths emit the same decoded content.

Copilot uses AI. Check for mistakes.
@kilo-code-bot

kilo-code-bot Bot commented Mar 21, 2026

Copy link
Copy Markdown

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Overview

The PR correctly implements fixes for issues #517 and #515:

  1. prompt_cache_key preservation - The prompt_cache_key field is now preserved during Responses API to Chat Completions translation, enabling Codex and other providers to use it as a cache-affinity signal. The changes in both openai-responses.ts and responsesApiHelper.ts are correct and well-documented with comments referencing issue Codex prompt caching may be broken by stripping prompt_cache_key and lacking prompt-cache affinity (codex-lb comparison) #517.

  2. tagContent JSON escaping - The fix for issue [BUG] \n characters are being interpreted as newlines instead of literal text in tagContent #515 correctly addresses the invalid JSON issue by escaping newline characters in the tagContent template literal. The change from actual newline characters (\n) to literal escape sequences (\\n) ensures valid JSON output in SSE streams.

Note: There is a minor inconsistency between the two code paths that use tagContent (regex replacement path vs JSON.stringify path) - they produce different escape levels (\\n vs \\\\n). However, both produce valid JSON now, which was the primary goal of the fix.

Files Reviewed (3 files)
  • open-sse/services/combo.ts - tagContent escape fix
  • open-sse/translator/helpers/responsesApiHelper.ts - prompt_cache_key preservation
  • open-sse/translator/request/openai-responses.ts - prompt_cache_key preservation

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8b556de03b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// that marker is silently dropped.
if (!res.body) return res;
const tagContent = `\n<omniModel>${modelStr}</omniModel>\n`;
const tagContent = `\\n<omniModel>${modelStr}</omniModel>\\n`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid double-escaping the fallback omniModel chunk

When context_cache_protection is enabled and a streamed response never emits a delta.content chunk before flush runs (tool-call-only turns are a common case), this escaped tagContent is fed through JSON.stringify in the fallback chunk builder, so clients receive literal \\n text instead of real newlines. On the next request extractPinnedModel() still finds the tag, but stripModelTags() in open-sse/services/comboAgentMiddleware.ts removes only the tag and leaves "\\n\\n" behind, which then gets forwarded upstream as prompt text. The old raw-newline form trimmed away cleanly, so this change regresses context-cache protection for no-text/tool-only streams.

Useful? React with 👍 / 👎.

@diegosouzapw
diegosouzapw merged commit d68c884 into main Mar 21, 2026
17 checks passed
@diegosouzapw
diegosouzapw deleted the fix/issue-517-515-prompt-cache-key-tagcontent branch March 23, 2026 21:27
prakersh pushed a commit to prakersh/OmniRoute that referenced this pull request Mar 26, 2026
…15-prompt-cache-key-tagcontent

fix: preserve prompt_cache_key in Responses API, escape \n in tagContent (diegosouzapw#517, diegosouzapw#515)
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
…15-prompt-cache-key-tagcontent

fix: preserve prompt_cache_key in Responses API, escape \n in tagContent (diegosouzapw#517, diegosouzapw#515)
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.

Codex prompt caching may be broken by stripping prompt_cache_key and lacking prompt-cache affinity (codex-lb comparison)

2 participants