From 969e54f23b13f7de78eeeb5cf76b33fd03d22b2f Mon Sep 17 00:00:00 2001 From: yetone Date: Mon, 5 Oct 2026 11:04:17 +0800 Subject: [PATCH] =?UTF-8?q?gateway:=20a=20turn=20on=20one=20of=20Codex's?= =?UTF-8?q?=20own=20models=20logs=20the=20reasoning=20Codex=20asked=20for?= =?UTF-8?q?=20(Karen,=20Discord=20#feedback:=20ChatGPT=20=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E7=99=BB=E5=BD=95=EF=BC=8C=E5=8D=87=E7=BA=A7=E5=88=B0?= =?UTF-8?q?=E6=9C=80=E6=96=B0=E7=89=88=E6=8E=A8=E7=90=86=E5=BC=BA=E5=BA=A6?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E4=B8=8D=E6=98=BE=E7=A4=BA=E4=BA=86)=20?= =?UTF-8?q?=E2=80=94=20Codex=20signed=20in=20with=20ChatGPT=20sends=20its?= =?UTF-8?q?=20own=20models=20(gpt-6-astra,=20gpt-6.1-sol)=20on=20to=20chat?= =?UTF-8?q?gpt.com=20through=20/backend-api/codex,=20and=20that=20relay=20?= =?UTF-8?q?(codexUpstream)=20wrote=20its=20request-log=20row,=20its=20Rout?= =?UTF-8?q?ing=20trace=20and=20the=20trace's=20try=20without=20the=20effor?= =?UTF-8?q?t,=20so=20the=20log's=20=E6=8E=A8=E7=90=86=E5=BC=BA=E5=BA=A6=20?= =?UTF-8?q?column=20was=20=E2=80=94=20on=20every=20one.=20It=20now=20recor?= =?UTF-8?q?ds=20reasoning.effort=20from=20the=20(zstd-decoded)=20body=20as?= =?UTF-8?q?=20it=20goes=20on,=20unchanged,=20in=20all=20three.=20With=20th?= =?UTF-8?q?e=20owner's=20real=20ChatGPT=20account=20in=20a=20sandbox=20mag?= =?UTF-8?q?pie,=20codex=20exec=20at=20xhigh=20through=20openai=5Fbase=5Fur?= =?UTF-8?q?l:=20the=20row=20says=20openai=20=C2=B7=20chatgpt.com,=20gpt-5.?= =?UTF-8?q?5,=20effort=20xhigh.=20TestCodexOwnModelLogsEffort=20(a=20zstd?= =?UTF-8?q?=20body=20at=20xhigh:=20the=20ledger's=20Effort,=20the=20route'?= =?UTF-8?q?s=20and=20its=20try's)=20fails=20on=20the=20old=20code.=20go=20?= =?UTF-8?q?vet,=20builds=20(darwin,=20GOOS=3Dlinux=20nogui,=20GOOS=3Dwindo?= =?UTF-8?q?ws)=20and=20go=20test=20-tags=20nogui=20-count=3D1=20./...=20un?= =?UTF-8?q?der=20a=20temp=20HOME=20pass=20(TestProvidersAnswerWhileListsCo?= =?UTF-8?q?meIn,=20TestUserPath=20and=20TestClaudeSignedOut=20timed=20out?= =?UTF-8?q?=20under=20load=20and=20pass=20on=20their=20own).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/gateway/codex_backend.go | 8 ++++--- internal/gateway/codex_backend_test.go | 30 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/internal/gateway/codex_backend.go b/internal/gateway/codex_backend.go index 82c12515..a8127d58 100644 --- a/internal/gateway/codex_backend.go +++ b/internal/gateway/codex_backend.go @@ -312,6 +312,7 @@ func (s *Server) codexUpstream(w http.ResponseWriter, r *http.Request, rest stri kind := requestCallKind(r.Header, metadata) var titleReply capturedBody // bounded; ordinary streams incur no copy captureTitle := false + effort := "" // the reasoning Codex asked for, which goes on as it is end := func(status int, msg string, tokens, out int) {} if rest == "/responses" { who := "Codex's own sign-in" @@ -319,11 +320,12 @@ func (s *Server) codexUpstream(w http.ResponseWriter, r *http.Request, rest stri who = "Codex's API key" } model := modelOf(body) + effort = requestEffort(provider.Responses, body) seat := Weighed{ID: "codex", Provider: "openai", Name: "OpenAI", Icon: "openai", Who: who, Kind: "account", Agent: "codex", Model: model} link := s.titlePrompts.observe(r, body, metadata, kind, start) captureTitle = link != nil && isTitleKind(kind) - tr = s.trace.begin(Route{TitleLink: link, Time: start, Agent: agentOf(r), Session: sessionOf(r.Header), ParentSession: titleParentSession(r.Header, metadata, kind), Kind: kind, Model: model, Provider: "openai", - Order: []Weighed{seat}, Tries: []Try{{ID: seat.ID, Model: model, Start: start}}}) + tr = s.trace.begin(Route{TitleLink: link, Time: start, Agent: agentOf(r), Session: sessionOf(r.Header), ParentSession: titleParentSession(r.Header, metadata, kind), Kind: kind, Model: model, Effort: effort, Provider: "openai", + Order: []Weighed{seat}, Tries: []Try{{ID: seat.ID, Model: model, Effort: effort, Start: start}}}) end = func(status int, msg string, tokens, out int) { ms := time.Since(start).Milliseconds() ttft, text := first.ms() @@ -492,7 +494,7 @@ func (s *Server) codexUpstream(w http.ResponseWriter, r *http.Request, rest stri rec := usage.Record{RouteID: tr.ID, Time: start, Agent: call.Agent, Provider: call.Provider, Host: provider.HostOf(base), Model: call.Model, Requested: call.Model, Served: served, Input: uu.Input, Output: uu.Output, CacheRead: uu.CacheRead, CacheWrite: uu.CacheWrite, - Reasoning: uu.Reasoning, Millis: call.Millis, TTFT: call.TTFT, FirstText: call.FirstText, Status: call.Status, Session: sessionOf(r.Header), NativeSession: nativeSessionOf(r.Header), Kind: call.Kind, + Reasoning: uu.Reasoning, Effort: effort, Millis: call.Millis, TTFT: call.TTFT, FirstText: call.FirstText, Status: call.Status, Session: sessionOf(r.Header), NativeSession: nativeSessionOf(r.Header), Kind: call.Kind, RequestID: requestID(res.Header), ResponseID: uu.ResponseID, Endpoint: r.URL.Path} failedWith(&rec, call.Status, call.Error, errType) appendUsage(r, rec) diff --git a/internal/gateway/codex_backend_test.go b/internal/gateway/codex_backend_test.go index 3cf208f3..0edc9f1a 100644 --- a/internal/gateway/codex_backend_test.go +++ b/internal/gateway/codex_backend_test.go @@ -752,3 +752,33 @@ func TestCodexOwnModelKindAccounting(t *testing.T) { }) } } + +// A turn on one of Codex's own models logs the reasoning Codex asked for, +// as the others' turns do: the request log's reasoning column was blank +// for every one (Karen, #feedback), its body sent zstd as Codex sends it. +func TestCodexOwnModelLogsEffort(t *testing.T) { + setup(t, provider.Chat, &fake{t: t}) + var got []byte + chatgpt(t, func(w http.ResponseWriter, r *http.Request) { + got, _ = io.ReadAll(r.Body) + w.Header()["Content-Type"] = nil + io.WriteString(w, sse(`data: {"type":"response.completed","response":{"id":"r1","usage":{"input_tokens":9,"output_tokens":2}}}`)) + }) + enc, _ := zstd.NewWriter(nil) + z := enc.EncodeAll([]byte(`{"model":"gpt-5.5","stream":true,"input":"hi","reasoning":{"effort":"xhigh","summary":"auto"}}`), nil) + s := New() + rec := httptest.NewRecorder() + req := httptest.NewRequest("POST", CodexPath+"/responses", bytes.NewReader(z)) + req.Header.Set("Content-Encoding", "zstd") + req.Header.Set("Authorization", "Bearer chatgpt-token") + s.Handler().ServeHTTP(rec, req) + if rec.Code != 200 || !strings.Contains(string(got), `"effort":"xhigh"`) { + t.Fatalf("%d %s, sent %s", rec.Code, rec.Body, got) + } + if u := usage.Load(time.Time{}); len(u) != 1 || u[0].Effort != "xhigh" { + t.Errorf("usage %+v", u) + } + if rs := s.Trace(t.Context(), 0, 0).Routes; len(rs) != 1 || rs[0].Effort != "xhigh" || rs[0].Tries[0].Effort != "xhigh" { + t.Errorf("trace %+v", rs) + } +}