From ee480ec95315e7b017ef2af103a4706a3bfb9eb3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 10 Oct 2026 18:37:08 +0000 Subject: [PATCH] fix(openai): Fast Mode sends service_tier priority for GPT models on the Responses path The Responses builder had the Codex rule (toggle on + gpt-family model -> priority) replaced by the xAI per-instance call alone, so the toggle was a silent no-op for GPT. Restore the rule, filling the key only when the xAI gate has not set it. Chat Completions is unchanged: it still carries the tier only for an eligible (xAI) instance. Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_01PV4ECd3Yzu9k81sw7vHjS3 --- CHANGELOG.md | 1 + .../openai/OpenAIProviderResponses.kt | 12 +++ .../app/provider/CodexFastModeTest.kt | 84 +++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 src/android/app/src/test/java/com/openminis/app/provider/CodexFastModeTest.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index ecb7ff09..44509f37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This changelog tracks **Minis for Android** as an independently maintained Andro - `android-shizuku-cli` is now `android-root-cli`: the same subcommands and JSON, run as root through `su`. Its Agent permission switch carries over. - The virtual screen runs in a root service (libsu) instead of a Shizuku UserService. Run its compatibility check once more to switch it back on. - Accessibility repair, clearing restricted settings and one-tap authorize use Root; Settings > System has a Root page in place of "Root & Shizuku", and the readiness checklist no longer lists Shizuku. +- Fast Mode works for GPT models again on the Responses path (Codex login and Responses relays): with the toggle on, requests carry `service_tier: "priority"`, as OpenMinis 1.14 does. A change that kept only the xAI case had turned the toggle into a no-op for GPT. ## 0.0.2 — 2026-10-10 diff --git a/src/android/app/src/main/java/com/openminis/app/provider/openai/OpenAIProviderResponses.kt b/src/android/app/src/main/java/com/openminis/app/provider/openai/OpenAIProviderResponses.kt index 2b0a098e..92275c4d 100644 --- a/src/android/app/src/main/java/com/openminis/app/provider/openai/OpenAIProviderResponses.kt +++ b/src/android/app/src/main/java/com/openminis/app/provider/openai/OpenAIProviderResponses.kt @@ -209,7 +209,19 @@ internal fun OpenAIProvider.buildResponsesAPIBody( // through, so no isOAuth narrowing. Ineligible upstreams ignore the // field or silently downgrade (receipt visible via the // response.completed service_tier log). + // + // [T-android-xai-priority] The per-instance (xAI) tier is resolved first and the + // gpt-family rule only fills a missing key, so neither clobbers the other. The two + // gates are disjoint today: that toggle is xAI-only and xAI never reaches this builder. + // The gpt-family rule had been replaced by the xAI call alone, which made Fast Mode a + // silent no-op for every GPT model (OpenMinis 1.14 keeps both). resolvedServiceTier()?.let { body.put("service_tier", it) } + if (!body.has("service_tier") && + com.openminis.app.data.FastModePrefs.isEnabled() && + model.id.contains("gpt", ignoreCase = true) + ) { + body.put("service_tier", "priority") + } if (systemPrompt != null) { body.put("instructions", systemPrompt) diff --git a/src/android/app/src/test/java/com/openminis/app/provider/CodexFastModeTest.kt b/src/android/app/src/test/java/com/openminis/app/provider/CodexFastModeTest.kt new file mode 100644 index 00000000..601c2d50 --- /dev/null +++ b/src/android/app/src/test/java/com/openminis/app/provider/CodexFastModeTest.kt @@ -0,0 +1,84 @@ +package com.openminis.app.provider + +import com.openminis.app.data.FastModePrefs +import com.openminis.app.data.model.LLMMessage +import com.openminis.app.data.model.LLMModel +import com.openminis.app.provider.openai.OpenAIProvider +import com.openminis.app.provider.openai.buildRequestBody +import com.openminis.app.provider.openai.buildRequestBody +import com.openminis.app.provider.openai.buildResponsesAPIBody +import org.json.JSONObject +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test + +/** + * [T-codex-fast-mode] Fast Mode on the Responses path for GPT models. + * + * The Responses builder sends `service_tier: "priority"` when the app-level toggle is on and + * the model is a gpt-family model (OpenMinis 1.14 behaviour). It had been replaced by the + * xAI per-instance call alone, so the toggle did nothing for GPT. xAI's own gate is covered + * by [XAIPriorityProcessingTest]. + */ +class CodexFastModeTest { + + @After + fun tearDown() { + FastModePrefs.setCachedEnabledForTest(false) + } + + private val history = listOf(LLMMessage(LLMMessage.Role.USER, "hello")) + + private fun provider(modelId: String, useResponsesAPI: Boolean = true) = OpenAIProvider( + apiKey = "test-key", + model = LLMModel(id = modelId, displayName = modelId, provider = "OpenAI"), + basePath = "https://example.invalid/v1", + useResponsesAPI = useResponsesAPI, + ) + + private fun responsesBody(modelId: String, fastMode: Boolean): JSONObject { + FastModePrefs.setCachedEnabledForTest(fastMode) + return provider(modelId).buildResponsesAPIBody( + messages = history, + systemPrompt = null, + maxTokens = 1024, + stream = false, + ) + } + + @Test + fun `a gpt model on the Responses path carries priority when Fast Mode is on`() { + assertEquals("priority", responsesBody("gpt-6-luna", fastMode = true).getString("service_tier")) + } + + @Test + fun `the tier is absent when Fast Mode is off`() { + assertFalse(responsesBody("gpt-6-luna", fastMode = false).has("service_tier")) + } + + @Test + fun `a model outside the gpt family never carries the tier`() { + assertFalse( + "service_tier leaked into a non-GPT model's Responses body", + responsesBody("deepseek-v4-pro", fastMode = true).has("service_tier"), + ) + } + + @Test + fun `Chat Completions for a gpt model does not carry the tier`() { + FastModePrefs.setCachedEnabledForTest(true) + val body = provider("gpt-6-luna", useResponsesAPI = false).buildRequestBody( + messages = history, + systemPrompt = null, + maxTokens = 1024, + stream = false, + temperature = null, + imageParts = emptyList(), + ) + assertFalse( + "the Chat Completions body only carries the tier for an eligible (xAI) instance", + body.has("service_tier"), + ) + } +}