Skip to content

fix(adapters): preserve routed model identity - #895

Closed
0xWinner98 wants to merge 2 commits into
lidge-jun:devfrom
0xWinner98:agent/routed-model-identity
Closed

fix(adapters): preserve routed model identity#895
0xWinner98 wants to merge 2 commits into
lidge-jun:devfrom
0xWinner98:agent/routed-model-identity

Conversation

@0xWinner98

@0xWinner98 0xWinner98 commented Aug 2, 2026

Copy link
Copy Markdown

Summary

  • add one central helper that replaces inherited Codex/GPT identity text with the concrete routed model ID when that ID is safe to interpolate;
  • use the same helper for generated catalog instructions and request-time openai-chat, Anthropic, Google/Antigravity, and Kiro system messages;
  • derive identity from the adapter-normalized wire model ID, including bracket-suffix stripping and Antigravity effort aliases;
  • fall back to configured model for blank, overlong, or unsafe model IDs;
  • keep the proxy identity out of forwarded prompts without guessing the model's creator from incomplete name prefixes.

Why

Issue #622 and PR #638 fixed current Codex CLI wording that escaped identity neutralization. That prevents a routed non-OpenAI model from inheriting the false Codex/GPT identity, but the neutral replacement still cannot name the model the user actually selected.

The catalog and adapters already know the concrete upstream model ID. Reusing the final wire value produces an accurate answer to model-identity questions while retaining a strict character allowlist and a safe fallback.

Behavior and safety

  • The helper replaces only the narrow Codex GPT-5 identity sentence already recognized by CODEX_GPT5_IDENTITY_RE.
  • Model IDs are limited to 128 characters and ASCII alphanumerics plus ._/@:+-[]~.
  • Unsafe text is never interpolated and falls back to configured model.
  • Valid identities are told not to claim a different model or creator; no vendor is inferred from a model-name prefix.
  • Adapter-specific normalization happens before identity interpolation, so the prompt and wire model stay aligned.
  • No OpenCodex proxy identity is added to the prompt.

Related

Test plan

  • bun test tests/identity-neutralize.test.ts tests/codex-catalog-golden.test.ts tests/openai-chat-model-suffix.test.ts tests/google-antigravity-wire.test.ts tests/kiro-adapter.test.ts tests/anthropic-tail-guard.test.ts — 123 passed, 0 failed
  • bun run typecheck
  • bun run privacy:scan
  • bun run prepush reached 6,966 passed / 8 skipped; the only 7 failures reproduce unchanged on clean dev because this host resolves reserved/example test hostnames into the benchmark address range

Summary by CodeRabbit

  • Bug Fixes
    • Improved routed-model identification across supported providers.
    • System prompts now accurately reflect the model handling the request while avoiding unsupported GPT-5 or OpenAI claims.
    • Added safer handling for model names, model IDs, suffixes, and invalid values.
    • Improved identity handling for OpenAI, Google, Anthropic, and Kiro requests.
  • Tests
    • Expanded coverage for model identity replacement, fallback behavior, escaping, and provider-specific request formatting.

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d9fab803-51d2-4568-a336-348fdd5eea90

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The change adds identifyRoutedModel, validates routed model names, and applies routed identity text to OpenAI Chat, Google, Anthropic, Kiro, and Codex catalog flows. Tests cover model IDs, suffix handling, fallback behavior, and adapter output.

Changes

Routed model identity

Layer / File(s) Summary
Routed identity transformation
src/adapters/identity.ts
The identity module validates model names, safely interpolates them, and adds identifyRoutedModel.
Adapter routed-model integration
src/adapters/openai-chat.ts, src/adapters/google.ts, src/adapters/anthropic.ts, src/adapters/kiro.ts
Adapters identify the effective routed model in system prompts. Google Antigravity resolves the effort-specific wire model before formatting messages.
Codex catalog integration
src/codex/catalog/sync.ts, tests/codex-catalog-golden.test.ts
Catalog synchronization uses identifyRoutedModel for routed instructions. The fixture updates the Codex identity wording.
Identity behavior coverage
tests/identity-neutralize.test.ts
Tests cover model-name validation, fallback behavior, replacement escaping, wire-model suffix handling, alias preservation, and routed identities across adapters.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ClientAdapter
  participant MessageFormatter
  participant identifyRoutedModel
  participant UpstreamRequest
  ClientAdapter->>MessageFormatter: messages and effective wire model ID
  MessageFormatter->>identifyRoutedModel: assembled system text and model ID
  identifyRoutedModel-->>MessageFormatter: routed identity text
  MessageFormatter->>UpstreamRequest: formatted request with transformed system text
Loading

Possibly related PRs

  • lidge-jun/opencodex#302: Both changes modify Kiro payload handling and system-prompt model identity processing.
  • lidge-jun/opencodex#638: This change expands the earlier identity-neutralization work into routed-model identification.

Suggested reviewers: lidge-jun, wibias, ingwannu

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes preserving the routed model identity, which is the primary change across the adapters and catalog synchronization.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the bug Something isn't working label Aug 2, 2026
@0xWinner98

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/identity-neutralize.test.ts`:
- Around line 68-72: Add focused regression tests alongside the existing
identifyRoutedCatalogModel tests for an empty model ID and an ID created with
"x".repeat(129). Assert both outputs use the fallback text "configured model",
preserving the existing unsafe-text regression coverage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bebd700c-f5cc-402b-90f8-80f699960b78

📥 Commits

Reviewing files that changed from the base of the PR and between f9b9440 and f939304.

📒 Files selected for processing (5)
  • src/adapters/identity.ts
  • src/adapters/openai-chat.ts
  • src/codex/catalog/sync.ts
  • tests/codex-catalog-golden.test.ts
  • tests/identity-neutralize.test.ts

Comment thread tests/identity-neutralize.test.ts
@0xWinner98

0xWinner98 commented Aug 2, 2026

Copy link
Copy Markdown
Author

Updated in 38686e5 after CodeRabbit and two independent review rounds. Identity now follows the adapter-normalized wire model across OpenAI Chat, Anthropic, Google/Antigravity, Kiro, and catalog sync; unsafe IDs fall back, valid OpenRouter aliases are preserved, and creator provenance is no longer guessed from incomplete prefixes. Validation: focused 123/123 (independent review: 200/200); typecheck, privacy scan, and diff check pass. The full prepush run reached 6,966 pass / 8 skip; its 7 failures are the same host-DNS fixture failures reproduced on clean dev. @coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

@0xWinner98 I will review the updates in commit 38686e51, including wire-model identity handling and validation fallbacks across the affected adapters and catalog sync.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== PR metadata and commits =='
gh pr view 895 --repo lidge-jun/opencodex --json headRefOid,baseRefOid,commits,title --jq '{title, headRefOid, baseRefOid, commits: [.commits[].oid]}'

echo '== Changed files =='
git diff --name-status origin/"$(gh pr view 895 --repo lidge-jun/opencodex --json baseRefName --jq -r .baseRefName)"...HEAD

echo '== Diff since requested commit parent =='
git show --format=fuller --stat 38686e51
git diff --find-renames 38686e51^ 38686e51 -- src/adapters src/codex tests

echo '== Source outlines =='
fd -t f . src/adapters src/codex tests | sort | sed -n '1,240p'
ast-grep outline src/adapters/identity.ts --items all 2>/dev/null || true
ast-grep outline src/adapters/openai-chat.ts --items all 2>/dev/null || true

echo '== Routed identity helper call sites and wire-model normalization candidates =='
rg -n -C 4 --glob '*.ts' \
  'identifyRoutedCatalogModel\s*\(|neutralizeIdentity\s*\(|wireModel|wire_model|modelName|model_name|requestedModel|requested_model' \
  src/adapters src/codex tests

Length of output: 1921


🏁 Script executed:

#!/bin/bash
set -euo pipefail

base='f9b9440c551e3d7f3e2041098caa2ee4de57698e'
head='38686e51db22fad2e036544e67b675cc24d82aa4'

echo '== Complete PR file list =='
git diff --name-status "${base}...${head}"

echo '== Current commit stat and patch =='
git show --format=fuller --stat "${head}"
git diff --find-renames "${head}^" "${head}" -- src/adapters src/codex tests

echo '== Final source outlines =='
for file in src/adapters/identity.ts src/adapters/openai-chat.ts; do
  if [ -f "$file" ]; then
    echo "---- $file ----"
    ast-grep outline "$file" --items all || true
  fi
done

echo '== Identity-helper call sites with context =='
rg -n -C 8 --glob '*.ts' \
  'identifyRoutedCatalogModel\s*\(|neutralizeIdentity\s*\(' \
  src/adapters src/codex tests || true

echo '== Model normalization and forwarded system-message construction =='
rg -n -C 6 --glob '*.ts' \
  'wire[Mm]odel|model[Aa]lias|resolve[A-Za-z]*Model|normalized[A-Za-z]*Model|system.*(?:message|prompt)|(?:message|prompt).*system' \
  src/adapters src/codex tests || true

echo '== Identity source and identity test source =='
for file in src/adapters/identity.ts tests/identity-neutralize.test.ts; do
  if [ -f "$file" ]; then
    echo "---- $file ----"
    cat -n "$file"
  fi
done

Length of output: 224493


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== OpenAI Chat request construction =='
sed -n '80,175p' src/adapters/openai-chat.ts
sed -n '555,760p' src/adapters/openai-chat.ts

echo '== Anthropic request construction =='
sed -n '430,555p' src/adapters/anthropic.ts
sed -n '630,760p' src/adapters/anthropic.ts

echo '== Google request construction and Antigravity resolver =='
sed -n '115,150p' src/adapters/google.ts
sed -n '290,405p' src/adapters/google.ts
fd -t f 'antigravity-models.ts' src/providers --exec sh -c 'echo "---- $1 ----"; cat -n "$1"' sh {}

echo '== Kiro model normalization and payload construction =='
rg -n -C 8 --glob 'kiro.ts' \
  'function normalizeKiroModelId|normalizeKiroModelId\s*\(|buildKiroPayload|modelId\s*:|model\s*:' \
  src/adapters/kiro.ts
sed -n '400,535p' src/adapters/kiro.ts
sed -n '700,860p' src/adapters/kiro.ts

echo '== Catalog model identity source =='
sed -n '145,215p' src/codex/catalog/sync.ts
rg -n -C 5 --glob '*.ts' 'deriveEntry\s*\(' tests src | head -n 220

echo '== All final model assignments in changed adapters =='
rg -n -C 3 --glob '*.ts' \
  '(?:model|modelId)\s*:\s*(?:parsed\.modelId|modelId|wireModelId|routedModelId|stripBracketedModelSuffix)' \
  src/adapters/openai-chat.ts src/adapters/anthropic.ts src/adapters/google.ts src/adapters/kiro.ts

Length of output: 77009


🏁 Script executed:

#!/bin/bash
set -euo pipefail

base='f9b9440c551e3d7f3e2041098caa2ee4de57698e'
head='38686e51db22fad2e036544e67b675cc24d82aa4'

echo '== Kiro model mapping and final payload field =='
rg -n -C 10 --glob 'kiro.ts' \
  'function mapModelId|const modelId = mapModelId|modelId\s*:|model\s*:' \
  src/adapters/kiro.ts
sed -n '535,615p' src/adapters/kiro.ts

echo '== Complete PR patch, excluding generated or unrelated files =='
git diff --find-renames --unified=40 "${base}...${head}" -- \
  src/adapters/identity.ts \
  src/adapters/openai-chat.ts \
  src/adapters/anthropic.ts \
  src/adapters/google.ts \
  src/adapters/kiro.ts \
  src/codex/catalog/sync.ts \
  tests/identity-neutralize.test.ts \
  tests/codex-catalog-golden.test.ts

echo '== Tests that exercise Antigravity model resolution =='
rg -n -C 5 --glob '*.test.ts' \
  'resolveAntigravityEffortWireModel|cloud-code-assist|gemini-3\.6-flash-(?:low|medium|high)|gemini-pro-agent' \
  tests

Length of output: 105019


@0xWinner98 Review complete. I found no blocking issues.

I verified that the identity text uses the same model identifier as the outbound request in the changed adapters:

  • src/adapters/openai-chat.ts: the bracket-stripped value is used for both identifyRoutedModel and body.model.
  • src/adapters/anthropic.ts: parsed.modelId is used for both the system block and body.model.
  • src/adapters/google.ts: the resolved Antigravity wire ID is used for both the system instruction and the Cloud Code Assist envelope model.
  • src/adapters/kiro.ts: mapModelId(parsed.modelId) is used for both the identity text and the nested Kiro modelId.
  • src/codex/catalog/sync.ts: routed catalog instructions use the shared helper with CatalogModel.id.

src/adapters/identity.ts also safely rejects blank, overlong, and unsafe IDs. It uses a replacement callback, so replacement-string tokens cannot reintroduce matched prompt text.

The focused tests cover unsafe interpolation, OpenRouter aliases, bracket normalization, and the changed adapter paths. I did not run tests in the inspection sandbox.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/identity-neutralize.test.ts`:
- Around line 173-180: Add a focused Cloud Code Assist regression test alongside
the existing identity-neutralization test, configuring the provider with
googleMode "cloud-code-assist", baseUrl, apiKey, and project, and using an
effort-mapped model. Exercise the adapter’s request envelope and assert that
envelope.request.systemInstruction identifies the same model ID as
envelope.model, ensuring resolveAntigravityEffortWireModel is covered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a214308e-4bb6-4a09-a756-64d1cdef3b5d

📥 Commits

Reviewing files that changed from the base of the PR and between f939304 and 38686e5.

📒 Files selected for processing (7)
  • src/adapters/anthropic.ts
  • src/adapters/google.ts
  • src/adapters/identity.ts
  • src/adapters/kiro.ts
  • src/adapters/openai-chat.ts
  • src/codex/catalog/sync.ts
  • tests/identity-neutralize.test.ts

Comment on lines 173 to 180
test("google/antigravity: systemInstruction is neutralized, no proxy mention", async () => {
const provider = { adapter: "google", baseUrl: "https://generativelanguage.googleapis.com", apiKey: "key" };
const { body } = await createGoogleAdapter(provider).buildRequest(parsed("gemini-3-pro", "google"));
const sysText = JSON.parse(body).systemInstruction.parts.map((p: { text: string }) => p.text).join("");
expect(sysText).toContain(NEUTRAL_IDENTITY_LINE);
expect(sysText).toContain("identify as gemini-3-pro");
expect(sysText).not.toMatch(/opencodex proxy/i);
expect(sysText).not.toContain(SYS);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Exercise the Cloud Code Assist identity path.

Lines 173-175 omit googleMode: "cloud-code-assist". The test uses the AI Studio branch and never executes the effort-specific resolveAntigravityEffortWireModel path at src/adapters/google.ts lines 298-304.

Add a Cloud Code Assist provider with baseUrl, apiKey, and project. Use an effort-mapped model. Assert that envelope.request.systemInstruction identifies the same model ID as envelope.model.

As per path instructions, tests/** changes must include a focused regression test for the changed adapter behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/identity-neutralize.test.ts` around lines 173 - 180, Add a focused
Cloud Code Assist regression test alongside the existing identity-neutralization
test, configuring the provider with googleMode "cloud-code-assist", baseUrl,
apiKey, and project, and using an effort-mapped model. Exercise the adapter’s
request envelope and assert that envelope.request.systemInstruction identifies
the same model ID as envelope.model, ensuring resolveAntigravityEffortWireModel
is covered.

Source: Path instructions

@lidge-jun

Copy link
Copy Markdown
Owner

Landed in #892 as cbb198d + 9469422 (authorship preserved). The strict ASCII allowlist + callback-based replacement (no $-substitution) passed adversarial review, including the bracket-suffix and Antigravity effort-alias normalization paths. Closing as superseded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants