Skip to content

Commit e94d39c

Browse files
authored
fix(codex): preserve fast service tier requests
1 parent 76d7722 commit e94d39c

4 files changed

Lines changed: 102 additions & 1 deletion

File tree

internal/translator/codex/claude/codex_claude_request.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,13 +321,29 @@ func ConvertClaudeRequestToCodex(modelName string, inputRawJSON []byte, _ bool)
321321
}
322322
template, _ = sjson.SetBytes(template, "reasoning.effort", reasoningEffort)
323323
template, _ = sjson.SetBytes(template, "reasoning.summary", "auto")
324+
if serviceTier := normalizeCodexServiceTier(rootResult.Get("service_tier")); serviceTier != "" {
325+
template, _ = sjson.SetBytes(template, "service_tier", serviceTier)
326+
}
324327
template, _ = sjson.SetBytes(template, "stream", true)
325328
template, _ = sjson.SetBytes(template, "store", false)
326329
template, _ = sjson.SetBytes(template, "include", []string{"reasoning.encrypted_content"})
327330

328331
return template
329332
}
330333

334+
func normalizeCodexServiceTier(result gjson.Result) string {
335+
if !result.Exists() || result.Type != gjson.String {
336+
return ""
337+
}
338+
339+
switch strings.ToLower(strings.TrimSpace(result.String())) {
340+
case "fast", "priority":
341+
return "priority"
342+
default:
343+
return ""
344+
}
345+
}
346+
331347
// isFernetLikeReasoningSignature checks only the encrypted_content envelope shape
332348
// observed in OpenAI reasoning signatures. It does not authenticate source or payload type.
333349
func isFernetLikeReasoningSignature(signature string) bool {

internal/translator/codex/claude/codex_claude_request_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,40 @@ func TestConvertClaudeRequestToCodex_ParallelToolCalls(t *testing.T) {
136136
}
137137
}
138138

139+
func TestConvertClaudeRequestToCodex_ServiceTier(t *testing.T) {
140+
tests := []struct {
141+
name string
142+
serviceTier string
143+
want string
144+
}{
145+
{name: "priority passes through", serviceTier: "priority", want: "priority"},
146+
{name: "fast normalizes to priority", serviceTier: "fast", want: "priority"},
147+
{name: "invalid tier is omitted", serviceTier: "default", want: ""},
148+
}
149+
150+
for _, tt := range tests {
151+
t.Run(tt.name, func(t *testing.T) {
152+
result := ConvertClaudeRequestToCodex("test-model", []byte(`{
153+
"model": "claude-3-opus",
154+
"service_tier": "`+tt.serviceTier+`",
155+
"messages": [{"role": "user", "content": "hello"}]
156+
}`), false)
157+
resultJSON := gjson.ParseBytes(result)
158+
159+
if tt.want == "" {
160+
if resultJSON.Get("service_tier").Exists() {
161+
t.Fatalf("service_tier should be omitted. Output: %s", string(result))
162+
}
163+
return
164+
}
165+
166+
if got := resultJSON.Get("service_tier").String(); got != tt.want {
167+
t.Fatalf("service_tier = %q, want %q. Output: %s", got, tt.want, string(result))
168+
}
169+
})
170+
}
171+
}
172+
139173
func TestConvertClaudeRequestToCodex_ToolChoiceModeMapping(t *testing.T) {
140174
tests := []struct {
141175
name string

internal/translator/codex/openai/responses/codex_openai-responses_request.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package responses
22

33
import (
44
"fmt"
5+
"strings"
56

67
log "github.com/sirupsen/logrus"
78
"github.com/tidwall/gjson"
@@ -27,7 +28,9 @@ func ConvertOpenAIResponsesRequestToCodex(modelName string, inputRawJSON []byte,
2728
rawJSON, _ = sjson.DeleteBytes(rawJSON, "temperature")
2829
rawJSON, _ = sjson.DeleteBytes(rawJSON, "top_p")
2930
if v := gjson.GetBytes(rawJSON, "service_tier"); v.Exists() {
30-
if v.String() != "priority" {
31+
if serviceTier := normalizeCodexServiceTier(v); serviceTier != "" {
32+
rawJSON, _ = sjson.SetBytes(rawJSON, "service_tier", serviceTier)
33+
} else {
3134
rawJSON, _ = sjson.DeleteBytes(rawJSON, "service_tier")
3235
}
3336
}
@@ -45,6 +48,19 @@ func ConvertOpenAIResponsesRequestToCodex(modelName string, inputRawJSON []byte,
4548
return rawJSON
4649
}
4750

51+
func normalizeCodexServiceTier(result gjson.Result) string {
52+
if !result.Exists() || result.Type != gjson.String {
53+
return ""
54+
}
55+
56+
switch strings.ToLower(strings.TrimSpace(result.String())) {
57+
case "fast", "priority":
58+
return "priority"
59+
default:
60+
return ""
61+
}
62+
}
63+
4864
// applyResponsesCompactionCompatibility handles OpenAI Responses context_management.compaction
4965
// for Codex upstream compatibility.
5066
//

internal/translator/codex/openai/responses/codex_openai-responses_request_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,41 @@ func TestConvertOpenAIResponsesRequestToCodex_OriginalIssue(t *testing.T) {
219219
}
220220
}
221221

222+
func TestConvertOpenAIResponsesRequestToCodex_ServiceTier(t *testing.T) {
223+
tests := []struct {
224+
name string
225+
serviceTier string
226+
want string
227+
}{
228+
{name: "priority passes through", serviceTier: "priority", want: "priority"},
229+
{name: "fast normalizes to priority", serviceTier: "fast", want: "priority"},
230+
{name: "invalid tier is stripped", serviceTier: "default", want: ""},
231+
}
232+
233+
for _, tt := range tests {
234+
t.Run(tt.name, func(t *testing.T) {
235+
inputJSON := []byte(`{
236+
"model": "gpt-5.4",
237+
"service_tier": "` + tt.serviceTier + `",
238+
"input": "hello"
239+
}`)
240+
241+
output := ConvertOpenAIResponsesRequestToCodex("gpt-5.4", inputJSON, false)
242+
243+
if tt.want == "" {
244+
if gjson.GetBytes(output, "service_tier").Exists() {
245+
t.Fatalf("service_tier should be stripped. Output: %s", string(output))
246+
}
247+
return
248+
}
249+
250+
if got := gjson.GetBytes(output, "service_tier").String(); got != tt.want {
251+
t.Fatalf("service_tier = %q, want %q. Output: %s", got, tt.want, string(output))
252+
}
253+
})
254+
}
255+
}
256+
222257
// TestConvertSystemRoleToDeveloper_AssistantRole tests that assistant role is preserved
223258
func TestConvertSystemRoleToDeveloper_AssistantRole(t *testing.T) {
224259
inputJSON := []byte(`{

0 commit comments

Comments
 (0)