Repository navigation
feat(codex): choose reasoning effort and fast mode per turn - #394
Closed
yuga-hashimoto wants to merge 5 commits into
Closed
yuga-hashimoto wants to merge 5 commits into
yuga-hashimoto wants to merge 5 commits into
Conversation
Codex's model/list reports each model's supported reasoning efforts and service tiers, but the app only read id and name, and turn/start carried neither setting, so the thinking chip never appeared for Codex and fast mode could not be turned on. - CodexModels fills variants from supportedReasoningEfforts (server order) and records the fast tier, its description, and each model's default effort and service tier - every turn sends explicit effort and serviceTier: the app-server applies them to subsequent turns too, so an omitted value would silently keep the previous turn's (resolveCodexTurnSettings) - a global fast-mode choice (null = follow each model's default) is persisted and exposed to the chat as fastModeAvailable/Enabled - composer gets a Fast chip next to the thinking chip; labels for the none/minimal/ultra efforts in all locales - verified against codex 0.163.0 generated app-server types and a live model/list response
Conflicts with #391 (workspace attachments) in ChatViewModel and ChatHomeScreen, resolved by keeping both sides: - ChatViewModel constructor: keep workspaceAttachmentNoteTemplate and the Codex fast-mode parameters. - Prompt request: send the Codex service tier together with modelAttachments. #391 split pendingAttachments into modelAttachments and workspaceAttachments, and #389 still passed pendingAttachments to the request; pendingAttachments stays for the optimistic message and previews. - ChatHomeScreen imports: keep Role and the semantics imports. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xw2ucui6t6dQdyGRrnCbHb
The pre-PR review found that docs/CODEX.md explains why every turn sends effort and serviceTier, but not that a model_reasoning_effort or service_tier in ~/.codex/config.toml therefore stops applying to turns sent from the app, nor that scheduled tasks, voice and quick input (which pass no variant and no tier) always use the model defaults. Also record that serviceTier is a plain string in the 0.142.5 and 0.155.1 turn/start schema, so the explicit "default" is not rejected as an unknown enum value there. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xw2ucui6t6dQdyGRrnCbHb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Supersedes #389 by @doublepi123. #389 no longer merged cleanly after #391 landed on
main, and its branch lives in a fork this session cannot push to, so its head is merged here onto the currentmain. The author's three commits are kept as they are; this branch adds one merge commit that resolves the conflicts, and one docs commit.What was resolved (
ChatViewModel.kt,ChatHomeScreen.kt; both sides kept):ChatViewModelconstructor:workspaceAttachmentNoteTemplate(fix: keep runtime error text on device and copy unsupported attachments into the workspace #391) and the Codex fast-mode parameters (feat(codex): choose reasoning effort and fast mode per turn #389).modelAttachments(fix: keep runtime error text on device and copy unsupported attachments into the workspace #391 splitpendingAttachmentsinto model and workspace attachments) together withserviceTier = serviceTierForPrompt()(feat(codex): choose reasoning effort and fast mode per turn #389).pendingAttachmentsstays for the optimistic message and previews.ChatHomeScreenimports:Roleand the semantics imports.What was added:
docs/CODEX.mdnow says plainly that the per-turn settings replace~/.codex/config.toml(see "Decision for the maintainer"), and records thatserviceTieris a plainstring | nullin the 0.142.5 and 0.155.1turn/startschema (generated withcodex app-server generate-json-schema), so the explicit"default"cannot fail as an unknown enum value there.The author's own comparison holds after the merge: the changed lines of #389 alone (
64e90a99..58947621) and of this branch againstmainare the same set, so nothing of #389 was dropped or altered and nothing ofmainwas lost.Original description of #389
Codex's
model/listreports each model's supported reasoning efforts and service tiers, but the app only readidandname, andturn/startcarried neither setting. So the thinking chip never appeared for Codex, and fast mode could not be turned on.CodexModelsturnssupportedReasoningEffortsinto model variants, in the server's order, so the existing thinking chip lists them.none,minimalandultraget labels in all locales. An effort the app does not know yet shows its raw id, capitalized.serviceTierslists the "Fast" tier (priority), or that uses the deprecatedadditionalSpeedTiers: ["fast"], gets a bolt Fast chip beside the thinking chip. The tier's own server description is the chip's screen-reader text. The user's choice is global and persisted. Unset means each model follows its owndefaultServiceTier.effortandserviceTierexplicitly (resolveCodexTurnSettings). The app-server applies both to subsequent turns as well, so an omitted value would silently keep the previous turn's.model_reasoning_effortorservice_tierset in~/.codex/config.toml, because the app now always says what the chips show.PromptRequest.serviceTieris Codex-only. The OpenCode V1 and V2 clients build their request bodies field by field, so it never reaches an OpenCode server.docs/CODEX.mddocuments both controls.Decision for the maintainer
Every turn sends
effortandserviceTier, so amodel_reasoning_effortorservice_tierin~/.codex/config.tomlstops applying to turns sent from AndCode, even if the chips were never touched. The app-server keeps both for later turns, so not sending them would carry a previous model'sultraor a fast tier over to the next one; the author chose to be explicit. The review below judged this non-blocking, but it is a behaviour change for existing Codex users, so please accept it knowingly. The scope is wider than chat: scheduled tasks, voice and quick input also go throughCodexTarget, pass no variant and no tier, and therefore always use the model defaults and do not follow the global Fast choice. Alternatives, if you would rather not accept it, are in the follow-ups below.Testing
./gradlew :app:testGithubDebugUnitTestrestricted toChatViewModelTest(77),*Codex*(the Codex test classes, including the newCodexModelsTest,CodexTurnStartParamsTest,CodexServiceTierTest),LegalDisclosureComplianceTest(19),RuntimeCatalogRepositoryTest(16) andOpenCodeAgentSelectionTest(7): 209 tests, 0 failures../gradlew detekt spotlessCheck :app:compileGithubDebugAndroidTestKotlin: passed.values/strings.xmlexists in all seven locale files, no duplicate names in any file.:app:lintGithubDebug, the release build and any on-device test; CI covers them. Nothing here was run against a live, signed-in Codex.Review note
The
repo-reviewersubagent type is not registered in this environment. The review below was produced by a general read-only agent that was told to read.opencode/agents/repo-reviewer.mdand follow it, with the output format it prescribes. It coveredd63f345b, the first version of the merge commit; the merge was since reworded (its message saidmainrenamed the attachment list, which the review rightly flagged as inaccurate) anddocs/CODEX.mdgained the paragraph described above. The code is unchanged since the review.Pre-PR review
判定: APPROVE
ブロッカー
なし
提案(非ブロッキング)
resolveCodexTurnSettingsはserviceTierを常に非 null で返す。何も選んでいなければ"default"になり、全ターンのturn/startに必ず載る。CodexModels.kt:100-101はserviceTiersを返さない旧サーバー(additionalSpeedTiers: ["fast"]だけを返す版)を明示的に扱っている。docs/CODEX.md:214 も「フィールドが無い版ではチップが出ないだけ」と書いている。"default"を送る。その版のTurnStartParams.serviceTierがfast/flexの列挙型だった場合、"default"はデシリアライズエラーになる。すると全ターンが SessionError で失敗する。CodexInstallerはインストール時点の latest を入れるだけで、自動更新はしない。docs によれば、端末で動いている実績があるのは 0.155.1。一方、ライブ検証は 0.163.0 だけ。codex app-server generate-json-schemaを実行し、TurnStartParams.serviceTierの型を見る。"default"を送るのは、そのサーバーがserviceTiers/defaultServiceTierを返したときだけに絞る。拒否されると確認できたら、これはブロッカーとして扱うべき。model_reasoning_effort/service_tierを毎ターン上書きする件): 非ブロッキングと判断しますCodexTargetを通る全経路が対象で、ScheduleExecutionService.kt:299/AndCodeVoiceSession.kt:383/QuickInputActivity.kt:148も含む。これらは variant も serviceTier も渡さないので、常に「モデル既定値 +"default"」になり、グローバルの Fast 設定も反映されない。selectedVariantは保存されず、モデル切替や再起動で null に戻る(ChatViewModel.kt:931)。config.toml で high を使っていた人は毎回選び直しになる。service_tier = "flex"は常に"default"に上書きされ、これはコストが上がる方向。model/listの既定値という意味になる。model/listの値ではなく app-server の実効設定にする(実効設定を読む API がその版にあれば)。fallbackModelId(カタログ先頭)の efforts を thinking チップに出す。CodexTargetはisDefaultのモデルの既定値で解決する。Codex 本体は config.toml のmodelか組み込み既定を使う。isDefaultを揃えることを推奨。toggleableは true/false しか返さないので、ChatViewModel.setFastMode(null)には UI から到達できない。defaultServiceTier=priorityのモデルで一度でも触ると、以後ずっとグローバルに固定される。docs の「unset follows the model」と運用がずれる。メニューか設定画面に「既定に戻す」を足すとよい。sendMessageのモデル解決に単体テストがない。対象はisDefaultへのフォールバック、成功したときだけmodelsRefreshed=trueにする処理、未知モデルのときの 1 回だけの再取得。CodexTurnStartParamsTestが押さえている。上記はその外側なので、解決部分を純関数に切り出してテストすることを推奨。pendingAttachmentsをmodelAttachments/workspaceAttachmentsに分け、PromptRequest へmodelAttachmentsを渡すよう変えただけ。pendingAttachmentsは楽観表示用の ChatMessage とプレビューで今も使われている。解決の中身は正しい。チェック済み項目
git log、git diff --stat、git show --remerge-diff HEADで確認。4 コミット・24 ファイル。merge-base は origin/main 47c8383、feat(codex): choose reasoning effort and fast mode per turn #389 の元の base は 64e90a9。pendingAttachmentsの扱い:git show origin/main:で見ると、main でも名前は変わっておらず、fix: keep runtime error text on device and copy unsupported attachments into the workspace #391 が分割を足しただけ(origin/main の ChatViewModel.kt:1327-1409)。sendMessage(ChatViewModel.kt:1405-1556)では、PromptRequest にmodelAttachmentsとserviceTierが渡る。楽観表示はpendingAttachments、offline/queue 系はmodelAttachments。PromptRequest に旧名のまま渡している箇所は残っていない。sendQueuedからsendMessageに戻るので、serviceTier も付く。serviceTierForPrompt()とvariantは、どちらも coroutine 内で評価する既存パターンどおり。ワークスペース参照は本文テキストに入るだけで、Codex の入力組み立てとは干渉しない。OpenCodeAgent.hidden: Boolean?と、feat(codex): choose reasoning effort and fast mode per turn #389 の OpenCodeModel/PromptRequest へのフィールド追加は別クラスで、干渉しない。PromptRequest は OpenCodeApiClient / V2ApiClient がフィールドごとに組み立てているため、serviceTier は OpenCode に送られない。onCodexFastModeChanged/initialCodexFastMode/workspaceAttachmentNoteTemplate/videoNotSupportedMessage/awaitForegroundが揃っている。ModeChip(onRefresh, loading)と、AndCodeApp からのagentsLoading/onRefreshAgentsは残っている。onToggleFastModeも配線済み。(Boolean?)->Unitの参照を(Boolean)->Unitに渡しても型は合う。python3 -Iで解析した。重複 0、欠落 0。新規 6 キーは全ロケールにあり、%1$sも一致。fix(chat): retry agents that fail to load instead of a dead mode chip #390 のchat_agents_*との衝突や重複もない。containsで「未設定(null)」と「false」を区別できている。main 側の CrashReporter の追加とも干渉しない。catalog()は常にプロバイダ 1 つを返すので、.single()は安全。@Volatileの 2 フィールドが別々に更新される点と、modelsRefreshedの確認と更新の間の競合は、起きてもmodel/listを余分に読むか直近のカタログが混ざる程度で無害。commands()非対応のため通らない。Follow-ups (not done here, from the review)
"default": the review's top item, whether an old server rejects"default", was checked for the schema only:serviceTieris a plain string in 0.142.5 and 0.155.1. Whether those servers treat"default"as standard speed at runtime was not run (needs a signed-in account).true/false, sosetFastMode(null)is unreachable from the UI.CodexTargetresolves against theisDefaultmodel.CodexTarget.sendMessage's model resolution (fallback toisDefault, one refresh for an unknown model,modelsRefreshedonly after a successful read).serviceTierForTurn(turn-scoped, does not persist), leavingeffortexplicit.Generated by Claude Code