Skip to content

Support GPT-5.6 max and ultra reasoning levels#209

Open
DVerdeV wants to merge 2 commits into
friuns2:mainfrom
DVerdeV:fix/gpt-5-6-reasoning-levels
Open

Support GPT-5.6 max and ultra reasoning levels#209
DVerdeV wants to merge 2 commits into
friuns2:mainfrom
DVerdeV:fix/gpt-5-6-reasoning-levels

Conversation

@DVerdeV

@DVerdeV DVerdeV commented Jul 10, 2026

Copy link
Copy Markdown

Summary

  • Add support for the GPT-5.6 max and ultra reasoning levels.
  • Preserve each Codex model's supportedReasoningEfforts and defaultReasoningEffort from the existing model/list response.
  • Filter the Thinking selector to the selected model's advertised capabilities.
  • Fall back to the target model's default effort when switching from an incompatible selection.
  • Preserve max and ultra through config normalization and turn/collaboration-mode payloads.
  • Add automated regression coverage and model-scoped manual test documentation.

Why

Codex Mobile previously recognized reasoning levels only through xhigh, so GPT-5.6's newer values were discarded. Simply adding them globally was also incorrect because support varies by model: GPT-5.6 Sol and Terra support max/ultra, Luna supports max, and GPT-5.5 supports neither.

The selector now follows the capability metadata already returned by Codex instead of maintaining one global menu for every model.

Testing

  • env -u CODEXUI_MEMORIES pnpm run test:unit
    • 152 tests passed.
  • pnpm run build
    • Frontend typecheck and production build passed.
    • CLI build passed.
  • node dist-cli/index.js --help
    • CLI smoke test passed.
  • Playwright verification at 375×812 and 768×1024 in light and dark themes:
    • GPT-5.6 Sol: Low, Medium, High, Extra high, Max, Ultra.
    • GPT-5.6 Luna: the same list without Ultra.
    • GPT-5.5: only Low, Medium, High, Extra high.
    • Switching from GPT-5.6 Ultra to GPT-5.5 selects GPT-5.5's Medium default.

Performance

No additional requests or asynchronous work were introduced. The implementation retains reasoning metadata from the existing model/list response and performs bounded lookups over the small model/effort catalogs.

Summary by CodeRabbit

  • New Features

    • Added Max and Ultra thinking levels for GPT-5.6 (only where supported per model).
    • Thinking-level selections now adapt to the selected model/provider’s supported options and persist across reloads.
    • Send the selected reasoning level to Codex with correct effort mapping.
  • Improvements

    • Expanded available-model handling to preserve per-model reasoning effort metadata (supported/default efforts).
  • Tests

    • Added and updated coverage for model-specific reasoning levels, configuration normalization, and available-model reasoning metadata.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change centralizes reasoning-effort values, adds max and ultra, propagates model-specific reasoning metadata through Codex and desktop state, filters the Thinking selector per model, and adds automated and manual validation.

Changes

GPT-5.6 reasoning effort support

Layer / File(s) Summary
Reasoning-effort contract
src/types/codex.ts, src/api/codexGateway.ts, src/server/codexAppServerBridge.ts, src/composables/useDesktopState.ts
Defines shared reasoning-effort values and runtime validation for Codex normalization and desktop state options.
Available model metadata
src/api/codexGateway.ts, src/api/codexGateway.test.ts
Normalizes available models with supported and default reasoning efforts, merges provider models, and tests metadata preservation.
Model-aware desktop selection
src/composables/useDesktopState.ts, src/composables/useDesktopState.test.ts
Tracks per-model reasoning metadata, refreshes full model records, and clamps selections to supported or default efforts.
Composer wiring and validation
src/components/content/ThreadComposer.vue, src/App.vue, src/api/codexGateway.test.ts, tests/providers-models/*
Filters Thinking options by model, adds Max and Ultra, wires metadata into both composers, and documents automated and manual coverage.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ThreadComposer
  participant useDesktopState
  participant getAvailableModels
  participant CodexAppServer
  ThreadComposer->>useDesktopState: select model and reasoning effort
  useDesktopState->>getAvailableModels: refresh model metadata
  getAvailableModels->>CodexAppServer: request model/list
  CodexAppServer-->>getAvailableModels: return supported/default efforts
  getAvailableModels-->>useDesktopState: return normalized models
  useDesktopState-->>ThreadComposer: expose supported options
  ThreadComposer->>CodexAppServer: start turn with validated reasoning_effort
Loading
🚥 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 accurately summarizes the main change: adding GPT-5.6 max and ultra reasoning support.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Support GPT-5.6 'max' and 'ultra' reasoning levels

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 10-20 Minutes

Grey Divider

AI Description

• Extend ReasoningEffort to include GPT-5.6's new max and ultra levels, replacing hardcoded
 allow-lists with a shared REASONING_EFFORTS list and isReasoningEffort guard.
• Preserve max/ultra when reading Codex config and pass them through turn/collaboration-mode
 payloads on both client and server.
• Add Max and Ultra to the Thinking selector UI and expand its dropdown height so all eight
 options fit without scrolling on mobile.
• Add unit tests for payload passthrough and config normalization, plus a manual test doc for UI
 verification.
Diagram

graph TD
  T["types/codex.ts REASONING_EFFORTS"] --> G["codexGateway.ts normalize"] --> P["turn/start payload"]
  T --> B["codexAppServerBridge.ts normalize"] --> P
  T --> U["useDesktopState.ts options"] --> C["ThreadComposer.vue selector"]
  P --> E["Codex App Server"]
Loading
High-Level Assessment

Centralizing the reasoning-effort list into a single REASONING_EFFORTS constant with a shared type guard is the correct approach: it eliminates duplicated allow-lists across client and server and makes future additions a one-line change. No meaningfully different approach was needed for this well-scoped enum extension.

Files changed (8) +101 / -14

Enhancement (5) +29 / -13
codex.tsExtend ReasoningEffort with max/ultra and add type guard +16/-1

Extend ReasoningEffort with max/ultra and add type guard

• Replaces the ReasoningEffort union with a REASONING_EFFORTS const array (adding 'max' and 'ultra') and derives the type from it. Adds an isReasoningEffort type guard for shared validation logic.

src/types/codex.ts

codexGateway.tsUse shared isReasoningEffort guard for normalization +3/-5

Use shared isReasoningEffort guard for normalization

• Replaces the hardcoded allow-list in normalizeReasoningEffort with the shared isReasoningEffort guard, automatically supporting the new max/ultra values.

src/api/codexGateway.ts

codexAppServerBridge.tsUse shared isReasoningEffort guard on server side +2/-5

Use shared isReasoningEffort guard on server side

• Imports isReasoningEffort from types/codex.js and uses it in normalizeReasoningEffort, replacing the hardcoded allow-list so max/ultra values are preserved server-side.

src/server/codexAppServerBridge.ts

useDesktopState.tsDerive reasoning effort options from shared constant +2/-1

Derive reasoning effort options from shared constant

• Replaces the hardcoded REASONING_EFFORT_OPTIONS array with the shared REASONING_EFFORTS constant so the composable stays in sync with supported values.

src/composables/useDesktopState.ts

ThreadComposer.vueAdd Max/Ultra options and resize Thinking dropdown +6/-1

Add Max/Ultra options and resize Thinking dropdown

• Adds 'Max' and 'Ultra' entries to the Thinking selector's reasoningOptions, tags the dropdown with a new class, and increases the dropdown options max-height so all eight options are visible on mobile without scrolling.

src/components/content/ThreadComposer.vue

Tests (1) +43 / -1
codexGateway.test.tsAdd tests for GPT-5.6 max/ultra reasoning passthrough +43/-1

Add tests for GPT-5.6 max/ultra reasoning passthrough

• Adds a test verifying 'ultra' reasoning effort is passed through startThreadTurn payloads, and parametrized tests confirming getCurrentModelConfig preserves 'max' and 'ultra' reasoning levels read from Codex config.

src/api/codexGateway.test.ts

Documentation (2) +29 / -0
gpt-5-6-max-and-ultra-thinking-levels.mdAdd manual test doc for GPT-5.6 Max/Ultra thinking levels +28/-0

Add manual test doc for GPT-5.6 Max/Ultra thinking levels

• New manual test procedure covering selector visibility, selection, config persistence, and theme readability for the max/ultra reasoning levels.

tests/providers-models/gpt-5-6-max-and-ultra-thinking-levels.md

index.mdLink new manual test doc in providers-models index +1/-0

Link new manual test doc in providers-models index

• Adds a row linking to the new GPT-5.6 Max and Ultra thinking levels manual test document.

tests/providers-models/index.md

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jul 10, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Fallback picks none 🐞 Bug ≡ Correctness ⭐ New
Description
pickReasoningEffortForModel() falls back to supportedEfforts[0] when there is no preferred or
model-default effort; for models without reasoning metadata this becomes 'none' because
REASONING_EFFORTS is ordered with 'none' first. refreshModelPreferences() then assigns that
value to selectedReasoningEffort, overriding the prior 'medium' default and causing turn/start
to send effort: 'none' explicitly.
Code

src/composables/useDesktopState.ts[R1665-1676]

+  function pickReasoningEffortForModel(
+    modelId: string,
+    preferredEffort: ReasoningEffort | '' = selectedReasoningEffort.value,
+  ): ReasoningEffort | '' {
+    const normalizedModelId = modelId.trim()
+    const supportedEfforts = readSupportedReasoningEffortsForModel(normalizedModelId)
+    if (preferredEffort && supportedEfforts.includes(preferredEffort)) return preferredEffort
+
+    const defaultEffort = availableModelDefaultReasoningEfforts.value[normalizedModelId]
+    if (defaultEffort && supportedEfforts.includes(defaultEffort)) return defaultEffort
+    return supportedEfforts[0] ?? ''
+  }
Evidence
The global reasoning list is ordered with 'none' first; when no per-model metadata exists,
readSupportedReasoningEffortsForModel() returns that global list, and
pickReasoningEffortForModel() returns index 0. refreshModelPreferences() assigns the result into
selectedReasoningEffort, and startThreadTurn() serializes any non-empty effort into the
turn/start RPC params.

src/types/codex.ts[5-14]
src/composables/useDesktopState.ts[1441-1443]
src/composables/useDesktopState.ts[1661-1676]
src/composables/useDesktopState.ts[2023-2094]
src/api/codexGateway.ts[709-711]
src/api/codexGateway.ts[1890-1900]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
When a model lacks `supportedReasoningEfforts`/`defaultReasoningEffort` metadata, `pickReasoningEffortForModel()` falls back to the first global effort (`'none'`). `refreshModelPreferences()` then writes that into `selectedReasoningEffort`, which makes subsequent `turn/start` calls send an explicit `effort: 'none'`.

## Issue Context
- For models without metadata, `readSupportedReasoningEffortsForModel()` uses `REASONING_EFFORT_OPTIONS` (the global list).
- That list is now backed by `REASONING_EFFORTS`, whose first entry is `'none'`.
- `refreshModelPreferences()` always normalizes `selectedReasoningEffort` via `pickReasoningEffortForModel(...)`, so the fallback value becomes the new selected effort.

## Fix Focus Areas
- src/composables/useDesktopState.ts[1661-1676]
- src/composables/useDesktopState.ts[2023-2095]

### Suggested fix direction
Adjust `pickReasoningEffortForModel()` so that when `preferredEffort` is empty/unspecified and there is no model-default effort available, it returns `''` (meaning “use server/model default”) instead of `supportedEfforts[0]`. Keep the existing `supportedEfforts[0]` fallback for the case where the user had a non-empty `preferredEffort` that is incompatible and the model has no default.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Schema effort union stale 🐞 Bug ⚙ Maintainability
Description
The PR adds 'max'/'ultra' to the runtime ReasoningEffort union, but the in-repo app-server
schema/types still define ReasoningEffort only through 'xhigh', leaving two conflicting sources of
truth. Any code that relies on the schema-generated types (re-exported via appServerDtos) will
remain unable to represent these new values without casts/workarounds.
Code

src/types/codex.ts[R5-20]

+export const REASONING_EFFORTS = [
+  'none',
+  'minimal',
+  'low',
+  'medium',
+  'high',
+  'xhigh',
+  'max',
+  'ultra',
+] as const
+
+export type ReasoningEffort = (typeof REASONING_EFFORTS)[number]
+
+export function isReasoningEffort(value: unknown): value is ReasoningEffort {
+  return typeof value === 'string' && REASONING_EFFORTS.some((effort) => effort === value)
+}
Evidence
The PR introduces new allowed values in src/types/codex.ts, but the schema-generated
ReasoningEffort type remains limited to xhigh and is still re-exported for protocol DTOs,
creating an inconsistent contract within the repo.

src/types/codex.ts[5-20]
documentation/app-server-schemas/typescript/ReasoningEffort.ts[1-8]
src/api/appServerDtos.ts[1-18]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The repository now has conflicting definitions of `ReasoningEffort`: runtime/UI types allow `max`/`ultra`, but the schema-generated protocol types still only allow up to `xhigh`. This mismatch will cause friction and incorrect typing anywhere schema types are used.
### Issue Context
`src/api/appServerDtos.ts` re-exports the schema-generated `ReasoningEffort`, so even though the current PR updated runtime normalization to accept new values, the protocol type artifacts remain stale.
### Fix Focus Areas
- documentation/app-server-schemas/typescript/ReasoningEffort.ts[1-8]
- documentation/app-server-schemas/json/v2/ModelListResponse.json[85-96]
- documentation/app-server-schemas/json/v2/TurnStartParams.json[1-200]
- src/api/appServerDtos.ts[1-18]
### Notes
- Regenerate these files from the upstream schema generator if applicable, or update them in-place if this repo is the source of truth.
- Ensure all schema references to `ReasoningEffort` (v1/v2, events, requests) include `max` and `ultra` where appropriate.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Effort not model-gated ✓ Resolved 🐞 Bug ≡ Correctness
Description
ThreadComposer now always offers 'max'/'ultra', and startThreadTurn forwards the selected effort
directly into the turn/start params without validating against the selected model’s advertised
supportedReasoningEfforts. This can produce turn/start payloads that the model catalog does not
advertise as supported for the current model.
Code

src/components/content/ThreadComposer.vue[R595-596]

+  { value: 'max', label: 'Max' },
+  { value: 'ultra', label: 'Ultra' },
Evidence
The selector hard-codes max/ultra options, while the request layer forwards any non-empty effort
verbatim; meanwhile, the protocol schema explicitly models per-model supported reasoning efforts,
and the current model fetch path discards that metadata.

src/components/content/ThreadComposer.vue[588-597]
src/api/codexGateway.ts[1883-1905]
documentation/app-server-schemas/typescript/v2/Model.ts[4-8]
src/api/codexGateway.ts[2037-2051]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The UI exposes `max`/`ultra` reasoning efforts unconditionally, but the protocol advertises per-model `supportedReasoningEfforts`. The client currently does not ingest/use that per-model capability data, and will send whatever effort the user selects.
### Issue Context
- `ThreadComposer.vue` hard-codes the full effort list.
- `codexGateway.startThreadTurn` sends `params.effort = effort` when non-empty.
- `model/list` returns full model objects including `supportedReasoningEfforts`, but the client only extracts IDs.
### Fix Focus Areas
- src/components/content/ThreadComposer.vue[588-597]
- src/api/codexGateway.ts[1877-1905]
- src/api/codexGateway.ts[2037-2051]
- documentation/app-server-schemas/typescript/v2/Model.ts[4-8]
### Implementation direction
- Extend the client model loading path to retain `supportedReasoningEfforts` for the selected model (not just IDs).
- Filter the Thinking dropdown options to those supported by the selected model.
- If a previously-selected effort becomes unsupported when switching models, automatically fall back to the model’s `defaultReasoningEffort` (or '') and persist that change.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Previous review results

Review updated until commit 52e208e

Results up to commit N/A


🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Remediation recommended
1. Schema effort union stale 🐞 Bug ⚙ Maintainability
Description
The PR adds 'max'/'ultra' to the runtime ReasoningEffort union, but the in-repo app-server
schema/types still define ReasoningEffort only through 'xhigh', leaving two conflicting sources of
truth. Any code that relies on the schema-generated types (re-exported via appServerDtos) will
remain unable to represent these new values without casts/workarounds.
Code

src/types/codex.ts[R5-20]

+export const REASONING_EFFORTS = [
+  'none',
+  'minimal',
+  'low',
+  'medium',
+  'high',
+  'xhigh',
+  'max',
+  'ultra',
+] as const
+
+export type ReasoningEffort = (typeof REASONING_EFFORTS)[number]
+
+export function isReasoningEffort(value: unknown): value is ReasoningEffort {
+  return typeof value === 'string' && REASONING_EFFORTS.some((effort) => effort === value)
+}
Evidence
The PR introduces new allowed values in src/types/codex.ts, but the schema-generated
ReasoningEffort type remains limited to xhigh and is still re-exported for protocol DTOs,
creating an inconsistent contract within the repo.

src/types/codex.ts[5-20]
documentation/app-server-schemas/typescript/ReasoningEffort.ts[1-8]
src/api/appServerDtos.ts[1-18]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The repository now has conflicting definitions of `ReasoningEffort`: runtime/UI types allow `max`/`ultra`, but the schema-generated protocol types still only allow up to `xhigh`. This mismatch will cause friction and incorrect typing anywhere schema types are used.
### Issue Context
`src/api/appServerDtos.ts` re-exports the schema-generated `ReasoningEffort`, so even though the current PR updated runtime normalization to accept new values, the protocol type artifacts remain stale.
### Fix Focus Areas
- documentation/app-server-schemas/typescript/ReasoningEffort.ts[1-8]
- documentation/app-server-schemas/json/v2/ModelListResponse.json[85-96]
- documentation/app-server-schemas/json/v2/TurnStartParams.json[1-200]
- src/api/appServerDtos.ts[1-18]
### Notes
- Regenerate these files from the upstream schema generator if applicable, or update them in-place if this repo is the source of truth.
- Ensure all schema references to `ReasoningEffort` (v1/v2, events, requests) include `max` and `ultra` where appropriate.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Effort not model-gated ✓ Resolved 🐞 Bug ≡ Correctness
Description
ThreadComposer now always offers 'max'/'ultra', and startThreadTurn forwards the selected effort
directly into the turn/start params without validating against the selected model’s advertised
supportedReasoningEfforts. This can produce turn/start payloads that the model catalog does not
advertise as supported for the current model.
Code

src/components/content/ThreadComposer.vue[R595-596]

+  { value: 'max', label: 'Max' },
+  { value: 'ultra', label: 'Ultra' },
Evidence
The selector hard-codes max/ultra options, while the request layer forwards any non-empty effort
verbatim; meanwhile, the protocol schema explicitly models per-model supported reasoning efforts,
and the current model fetch path discards that metadata.

src/components/content/ThreadComposer.vue[588-597]
src/api/codexGateway.ts[1883-1905]
documentation/app-server-schemas/typescript/v2/Model.ts[4-8]
src/api/codexGateway.ts[2037-2051]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The UI exposes `max`/`ultra` reasoning efforts unconditionally, but the protocol advertises per-model `supportedReasoningEfforts`. The client currently does not ingest/use that per-model capability data, and will send whatever effort the user selects.
### Issue Context
- `ThreadComposer.vue` hard-codes the full effort list.
- `codexGateway.startThreadTurn` sends `params.effort = effort` when non-empty.
- `model/list` returns full model objects including `supportedReasoningEfforts`, but the client only extracts IDs.
### Fix Focus Areas
- src/components/content/ThreadComposer.vue[588-597]
- src/api/codexGateway.ts[1877-1905]
- src/api/codexGateway.ts[2037-2051]
- documentation/app-server-schemas/typescript/v2/Model.ts[4-8]
### Implementation direction
- Extend the client model loading path to retain `supportedReasoningEfforts` for the selected model (not just IDs).
- Filter the Thinking dropdown options to those supported by the selected model.
- If a previously-selected effort becomes unsupported when switching models, automatically fall back to the model’s `defaultReasoningEffort` (or '') and persist that change.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Qodo Logo

Comment thread src/types/codex.ts
Comment on lines +5 to +20
export const REASONING_EFFORTS = [
'none',
'minimal',
'low',
'medium',
'high',
'xhigh',
'max',
'ultra',
] as const

export type ReasoningEffort = (typeof REASONING_EFFORTS)[number]

export function isReasoningEffort(value: unknown): value is ReasoningEffort {
return typeof value === 'string' && REASONING_EFFORTS.some((effort) => effort === value)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. Schema effort union stale 🐞 Bug ⚙ Maintainability

The PR adds 'max'/'ultra' to the runtime ReasoningEffort union, but the in-repo app-server
schema/types still define ReasoningEffort only through 'xhigh', leaving two conflicting sources of
truth. Any code that relies on the schema-generated types (re-exported via appServerDtos) will
remain unable to represent these new values without casts/workarounds.
Agent Prompt
### Issue description
The repository now has conflicting definitions of `ReasoningEffort`: runtime/UI types allow `max`/`ultra`, but the schema-generated protocol types still only allow up to `xhigh`. This mismatch will cause friction and incorrect typing anywhere schema types are used.

### Issue Context
`src/api/appServerDtos.ts` re-exports the schema-generated `ReasoningEffort`, so even though the current PR updated runtime normalization to accept new values, the protocol type artifacts remain stale.

### Fix Focus Areas
- documentation/app-server-schemas/typescript/ReasoningEffort.ts[1-8]
- documentation/app-server-schemas/json/v2/ModelListResponse.json[85-96]
- documentation/app-server-schemas/json/v2/TurnStartParams.json[1-200]
- src/api/appServerDtos.ts[1-18]

### Notes
- Regenerate these files from the upstream schema generator if applicable, or update them in-place if this repo is the source of truth.
- Ensure all schema references to `ReasoningEffort` (v1/v2, events, requests) include `max` and `ultra` where appropriate.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/components/content/ThreadComposer.vue
@DVerdeV

DVerdeV commented Jul 10, 2026

Copy link
Copy Markdown
Author

/review

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/composables/useDesktopState.test.ts (1)

901-939: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Exercise max as well as ultra.

This test only selects and asserts ultra; max is checked only as metadata. Add a max selection/assertion, or table-drive both new values, so a regression in the max filtering or selection path cannot pass unnoticed.

🤖 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 `@src/composables/useDesktopState.test.ts` around lines 901 - 939, Extend the
test covering model-specific reasoning levels to exercise both newly supported
values, max and ultra, rather than only ultra. Update the selection and
assertion flow around setSelectedReasoningEffort and ensure max is explicitly
selected and validated, or table-drive equivalent cases so filtering and
incompatible-selection behavior are covered for both values.
🤖 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 `@src/composables/useDesktopState.ts`:
- Around line 1661-1696: setAvailableModelMetadata currently replaces cached
reasoning-effort maps with data from the latest model list, allowing placeholder
records to erase known metadata. Update setAvailableModelMetadata to merge new
entries into the existing availableModelReasoningEfforts and
availableModelDefaultReasoningEfforts maps, preserving prior entries and only
updating supported, non-null metadata; continue handling explicit defaults
consistently.

In `@tests/providers-models/gpt-5-6-max-and-ultra-thinking-levels.md`:
- Around line 11-20: Update the responsive verification steps in the GPT-5.6
thinking-level test to specify the exact mobile or tablet device and viewport
dimensions, then explicitly verify all eight options remain visible and readable
in both light and dark themes.

---

Nitpick comments:
In `@src/composables/useDesktopState.test.ts`:
- Around line 901-939: Extend the test covering model-specific reasoning levels
to exercise both newly supported values, max and ultra, rather than only ultra.
Update the selection and assertion flow around setSelectedReasoningEffort and
ensure max is explicitly selected and validated, or table-drive equivalent cases
so filtering and incompatible-selection behavior are covered for both values.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 85665583-9911-4933-a9d1-29ee2a798f0d

📥 Commits

Reviewing files that changed from the base of the PR and between fac2291 and 52e208e.

📒 Files selected for processing (10)
  • src/App.vue
  • src/api/codexGateway.test.ts
  • src/api/codexGateway.ts
  • src/components/content/ThreadComposer.vue
  • src/composables/useDesktopState.test.ts
  • src/composables/useDesktopState.ts
  • src/server/codexAppServerBridge.ts
  • src/types/codex.ts
  • tests/providers-models/gpt-5-6-max-and-ultra-thinking-levels.md
  • tests/providers-models/index.md

Comment on lines +1661 to +1696
function readSupportedReasoningEffortsForModel(modelId: string): readonly ReasoningEffort[] {
return availableModelReasoningEfforts.value[modelId.trim()] ?? REASONING_EFFORT_OPTIONS
}

function pickReasoningEffortForModel(
modelId: string,
preferredEffort: ReasoningEffort | '' = selectedReasoningEffort.value,
): ReasoningEffort | '' {
const normalizedModelId = modelId.trim()
const supportedEfforts = readSupportedReasoningEffortsForModel(normalizedModelId)
if (preferredEffort && supportedEfforts.includes(preferredEffort)) return preferredEffort

const defaultEffort = availableModelDefaultReasoningEfforts.value[normalizedModelId]
if (defaultEffort && supportedEfforts.includes(defaultEffort)) return defaultEffort
return supportedEfforts[0] ?? ''
}

function ensureReasoningEffortSupportedForModel(modelId: string): void {
selectedReasoningEffort.value = pickReasoningEffortForModel(modelId)
}

function setAvailableModelMetadata(models: AvailableModel[]): void {
const reasoningEfforts: Record<string, ReasoningEffort[]> = {}
const defaultReasoningEfforts: Record<string, ReasoningEffort> = {}
for (const model of models) {
if (model.supportedReasoningEfforts !== null) {
reasoningEfforts[model.id] = [...model.supportedReasoningEfforts]
}
if (model.defaultReasoningEffort) {
defaultReasoningEfforts[model.id] = model.defaultReasoningEffort
}
}
availableModelReasoningEfforts.value = reasoningEfforts
availableModelDefaultReasoningEfforts.value = defaultReasoningEfforts
}

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Full cache replace on setAvailableModelMetadata can transiently drop known reasoning-effort metadata.

setAvailableModelMetadata (called from refreshModelPreferences, Line 2039) always builds reasoningEfforts/defaultReasoningEfforts from scratch using only the models returned by the current getAvailableModels call, then unconditionally overwrites the reactive maps. When the active thread is provider-backed, getAvailableModels returns only placeholder records (supportedReasoningEfforts: null), so this wipes cached metadata for other models (e.g. Codex-native gpt-5.6-sol). Since readSupportedReasoningEffortsForModel falls back to the full 8-option list when an entry is missing, a model that only supports a subset of efforts would momentarily appear to support all of them again, until the next refresh for that model's context repopulates the cache.

Impact is transient/self-correcting (next selectThread call refreshes it), but it's a genuine cache-invalidation gap.

♻️ Suggested fix: merge instead of replace
 function setAvailableModelMetadata(models: AvailableModel[]): void {
-    const reasoningEfforts: Record<string, ReasoningEffort[]> = {}
-    const defaultReasoningEfforts: Record<string, ReasoningEffort> = {}
+    const reasoningEfforts: Record<string, ReasoningEffort[]> = { ...availableModelReasoningEfforts.value }
+    const defaultReasoningEfforts: Record<string, ReasoningEffort> = { ...availableModelDefaultReasoningEfforts.value }
     for (const model of models) {
       if (model.supportedReasoningEfforts !== null) {
         reasoningEfforts[model.id] = [...model.supportedReasoningEfforts]
       }
       if (model.defaultReasoningEffort) {
         defaultReasoningEfforts[model.id] = model.defaultReasoningEffort
       }
     }
     availableModelReasoningEfforts.value = reasoningEfforts
     availableModelDefaultReasoningEfforts.value = defaultReasoningEfforts
   }

As per coding guidelines, "For startup, thread loading, realtime rendering, routing, API, filesystem, git, or module-loading changes, explicitly check duplicate requests, blocking work, unbounded fanout, large payloads, and cache invalidation risk."

Also applies to: 2033-2039

🤖 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 `@src/composables/useDesktopState.ts` around lines 1661 - 1696,
setAvailableModelMetadata currently replaces cached reasoning-effort maps with
data from the latest model list, allowing placeholder records to erase known
metadata. Update setAvailableModelMetadata to merge new entries into the
existing availableModelReasoningEfforts and
availableModelDefaultReasoningEfforts maps, preserving prior entries and only
updating supported, non-null metadata; continue handling explicit defaults
consistently.

Source: Coding guidelines

Comment on lines +11 to +20
#### Steps
1. Start a new chat and select `GPT-5.6-Sol` or `GPT-5.6-Terra`.
2. Open the Thinking selector in light theme and confirm `Max` and `Ultra` are present.
3. Select `Max`, send a prompt, and confirm the turn starts without an invalid reasoning-effort error.
4. Select `Ultra`, send a second prompt, and confirm the turn starts without an invalid reasoning-effort error.
5. Select `GPT-5.6-Luna` and confirm `Max` is present but `Ultra` is absent.
6. Select `GPT-5.5` and confirm both `Max` and `Ultra` are absent.
7. Switch from a GPT-5.6 model with `Ultra` selected to GPT-5.5 and confirm Thinking changes to GPT-5.5's default effort.
8. Switch to dark theme and repeat the selector visibility checks.
9. Reload the page while `Ultra` is configured for GPT-5.6 Sol or Terra and confirm the selector still displays `Ultra`.

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.

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

Specify the mobile/tablet viewport for the responsive check.

These steps never set a mobile or tablet viewport, so the “all eight options remain visible on mobile” behavior can be skipped while still following the procedure. Add the exact device/viewport used and explicitly verify text readability in both themes.

🧰 Tools
🪛 LanguageTool

[style] ~16-~16: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...t an invalid reasoning-effort error. 5. Select GPT-5.6-Luna and confirm Max is pre...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~17-~17: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...xis present butUltrais absent. 6. SelectGPT-5.5and confirm bothMaxandU...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

🤖 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/providers-models/gpt-5-6-max-and-ultra-thinking-levels.md` around lines
11 - 20, Update the responsive verification steps in the GPT-5.6 thinking-level
test to specify the exact mobile or tablet device and viewport dimensions, then
explicitly verify all eight options remain visible and readable in both light
and dark themes.

Source: Coding guidelines

Comment on lines +1665 to +1676
function pickReasoningEffortForModel(
modelId: string,
preferredEffort: ReasoningEffort | '' = selectedReasoningEffort.value,
): ReasoningEffort | '' {
const normalizedModelId = modelId.trim()
const supportedEfforts = readSupportedReasoningEffortsForModel(normalizedModelId)
if (preferredEffort && supportedEfforts.includes(preferredEffort)) return preferredEffort

const defaultEffort = availableModelDefaultReasoningEfforts.value[normalizedModelId]
if (defaultEffort && supportedEfforts.includes(defaultEffort)) return defaultEffort
return supportedEfforts[0] ?? ''
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. Fallback picks none 🐞 Bug ≡ Correctness

pickReasoningEffortForModel() falls back to supportedEfforts[0] when there is no preferred or
model-default effort; for models without reasoning metadata this becomes 'none' because
REASONING_EFFORTS is ordered with 'none' first. refreshModelPreferences() then assigns that
value to selectedReasoningEffort, overriding the prior 'medium' default and causing turn/start
to send effort: 'none' explicitly.
Agent Prompt
## Issue description
When a model lacks `supportedReasoningEfforts`/`defaultReasoningEffort` metadata, `pickReasoningEffortForModel()` falls back to the first global effort (`'none'`). `refreshModelPreferences()` then writes that into `selectedReasoningEffort`, which makes subsequent `turn/start` calls send an explicit `effort: 'none'`.

## Issue Context
- For models without metadata, `readSupportedReasoningEffortsForModel()` uses `REASONING_EFFORT_OPTIONS` (the global list).
- That list is now backed by `REASONING_EFFORTS`, whose first entry is `'none'`.
- `refreshModelPreferences()` always normalizes `selectedReasoningEffort` via `pickReasoningEffortForModel(...)`, so the fallback value becomes the new selected effort.

## Fix Focus Areas
- src/composables/useDesktopState.ts[1661-1676]
- src/composables/useDesktopState.ts[2023-2095]

### Suggested fix direction
Adjust `pickReasoningEffortForModel()` so that when `preferredEffort` is empty/unspecified and there is no model-default effort available, it returns `''` (meaning “use server/model default”) instead of `supportedEfforts[0]`. Keep the existing `supportedEfforts[0]` fallback for the case where the user had a non-empty `preferredEffort` that is incompatible and the model has no default.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 52e208e

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.

1 participant