From f2e8d7210057b2fcd2769b2273955fc2d0ec0279 Mon Sep 17 00:00:00 2001 From: Danny Kopping Date: Wed, 8 Jul 2026 15:36:42 +0200 Subject: [PATCH] chore: apply openai-go bugfix to fix openrouter response parsing (#27092) Applies https://github.com/coder/openai-go/pull/3 Closes https://github.com/coder/coder/issues/26469 `kylecarbs/openai-go` was renamed to `coder/openai-go` I've created a [branch](https://github.com/coder/openai-go/tree/coder/pinned) to track the changes we've made. We're far behind `main` now, so we should make an effort to update this as some point. I've manually tested using OpenRouter + GLM 5.2 as the bug report states and it works fine. image image
``` : OPENROUTER PROCESSING : OPENROUTER PROCESSING : OPENROUTER PROCESSING : OPENROUTER PROCESSING : OPENROUTER PROCESSING : OPENROUTER PROCESSING : OPENROUTER PROCESSING : OPENROUTER PROCESSING : OPENROUTER PROCESSING : OPENROUTER PROCESSING : OPENROUTER PROCESSING : OPENROUTER PROCESSING : OPENROUTER PROCESSING : OPENROUTER PROCESSING : OPENROUTER PROCESSING : OPENROUTER PROCESSING data: {"id":"gen-1783515596-pm90JeoPBXFHqVq3dDlL","object":"chat.completion.chunk","created":1783515596,"model":"z-ai/glm-5.2-20260616","provider":"Novita","choices":[{"index":0,"delta":{"content":"Yep","role":"assistant"},"finish_reason":null,"native_finish_reason":null}]} data: {"id":"gen-1783515596-pm90JeoPBXFHqVq3dDlL","object":"chat.completion.chunk","created":1783515596,"model":"z-ai/glm-5.2-20260616","provider":"Novita","choices":[{"index":0,"delta":{"content":", I","role":"assistant"},"finish_reason":null,"native_finish_reason":null}]} data: {"id":"gen-1783515596-pm90JeoPBXFHqVq3dDlL","object":"chat.completion.chunk","created":1783515596,"model":"z-ai/glm-5.2-20260616","provider":"Novita","choices":[{"index":0,"delta":{"content":"'m here","role":"assistant"},"finish_reason":null,"native_finish_reason":null}]} data: {"id":"gen-1783515596-pm90JeoPBXFHqVq3dDlL","object":"chat.completion.chunk","created":1783515596,"model":"z-ai/glm-5.2-20260616","provider":"Novita","choices":[{"index":0,"delta":{"content":". What","role":"assistant"},"finish_reason":null,"native_finish_reason":null}]} data: {"id":"gen-1783515596-pm90JeoPBXFHqVq3dDlL","object":"chat.completion.chunk","created":1783515596,"model":"z-ai/glm-5.2-20260616","provider":"Novita","choices":[{"index":0,"delta":{"content":" do you","role":"assistant"},"finish_reason":null,"native_finish_reason":null}]} : OPENROUTER PROCESSING data: {"id":"gen-1783515596-pm90JeoPBXFHqVq3dDlL","object":"chat.completion.chunk","created":1783515596,"model":"z-ai/glm-5.2-20260616","provider":"Novita","choices":[{"index":0,"delta":{"content":" need?","role":"assistant"},"finish_reason":null,"native_finish_reason":null}]} data: {"id":"gen-1783515596-pm90JeoPBXFHqVq3dDlL","object":"chat.completion.chunk","created":1783515596,"model":"z-ai/glm-5.2-20260616","provider":"Novita","choices":[{"index":0,"delta":{"content":"","role":"assistant"},"finish_reason":"stop","native_finish_reason":"stop"}]} data: {"id":"gen-1783515596-pm90JeoPBXFHqVq3dDlL","object":"chat.completion.chunk","created":1783515596,"model":"z-ai/glm-5.2-20260616","provider":"Novita","service_tier":null,"choices":[{"index":0,"delta":{"content":"","role":"assistant"},"finish_reason":"stop","native_finish_reason":"stop"}],"usage":{"prompt_tokens":4855,"completion_tokens":13,"total_tokens":4868,"cost":0.00479794,"is_byok":false,"prompt_tokens_details":{"cached_tokens":0,"cache_write_tokens":0,"audio_tokens":0,"video_tokens":0},"cost_details":{"upstream_inference_cost":0.00479794,"upstream_inference_prompt_cost":0.0047579,"upstream_inference_completions_cost":0.00004004},"completion_tokens_details":{"reasoning_tokens":0,"image_tokens":0,"audio_tokens":0}}} data: [DONE] ```
Signed-off-by: Danny Kopping --- .../integrationtest/responses_internal_test.go | 9 +++++++++ .../internal/integrationtest/trace_internal_test.go | 10 +++++++--- go.mod | 10 +++++++--- go.sum | 4 ++-- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/aibridge/internal/integrationtest/responses_internal_test.go b/aibridge/internal/integrationtest/responses_internal_test.go index 8a39985f67..73fccad639 100644 --- a/aibridge/internal/integrationtest/responses_internal_test.go +++ b/aibridge/internal/integrationtest/responses_internal_test.go @@ -376,6 +376,15 @@ func TestResponsesOutputMatchesUpstream(t *testing.T) { expectModel: "gpt-6.7", expectPromptRecorded: "hello_wrong_format", expectedClient: aibridge.ClientUnknown, + expectTokenUsage: &recorder.TokenUsageRecord{ + MsgID: "resp_123", + Input: 11, + Output: 18, + ExtraTokenTypes: map[string]int64{ + "output_reasoning": 0, + "total_tokens": 29, + }, + }, }, } diff --git a/aibridge/internal/integrationtest/trace_internal_test.go b/aibridge/internal/integrationtest/trace_internal_test.go index baf7a5a3ae..23fcbdeae8 100644 --- a/aibridge/internal/integrationtest/trace_internal_test.go +++ b/aibridge/internal/integrationtest/trace_internal_test.go @@ -632,18 +632,22 @@ func TestTraceOpenAIErr(t *testing.T) { }, }, { - name: "trace_openai_responses_streaming_error", + name: "trace_openai_responses_streaming_wrong_format", streaming: true, fixture: fixtures.OaiResponsesStreamingWrongResponseFormat, path: pathOpenAIResponses, expectCode: http.StatusOK, + // The malformed event lacks a valid data field, so the SSE parser + // skips it and continues to the valid response.completed event. + // The stream is processed successfully and token usage is recorded. expect: []expectTrace{ - {"Intercept", 1, codes.Error}, + {"Intercept", 1, codes.Unset}, {"Intercept.CreateInterceptor", 1, codes.Unset}, {"Intercept.RecordInterception", 1, codes.Unset}, - {"Intercept.ProcessRequest", 1, codes.Error}, + {"Intercept.ProcessRequest", 1, codes.Unset}, {"Intercept.RecordInterceptionEnded", 1, codes.Unset}, {"Intercept.RecordPromptUsage", 1, codes.Unset}, + {"Intercept.RecordTokenUsage", 1, codes.Unset}, {"Intercept.ProcessRequest.Upstream", 1, codes.Unset}, }, }, diff --git a/go.mod b/go.mod index b819ea625b..cd0a452735 100644 --- a/go.mod +++ b/go.mod @@ -116,9 +116,13 @@ replace github.com/charmbracelet/anthropic-sdk-go => github.com/coder/anthropic- // https://github.com/anthropics/anthropic-sdk-go/pull/262 replace github.com/anthropics/anthropic-sdk-go v1.19.0 => github.com/dannykopping/anthropic-sdk-go v0.0.0-20251230111224-88a4315810bd -// SasSwart perf fork of openai-go with fix for WithJSONSet + deferred serialization. -// https://github.com/kylecarbs/openai-go/pull/2 -replace github.com/openai/openai-go/v3 => github.com/kylecarbs/openai-go/v3 v3.0.0-20260319113850-9477dcaedcae +// Fork of openai-go with fix for WithJSONSet + deferred serialization: https://github.com/kylecarbs/openai-go/pull/2 +// Also includes an SSE stream fix for OpenRouter: https://github.com/coder/openai-go/pull/3 +// +// `coder/pinned` is the branch we track. +// +// To update, run: `go mod edit -replace github.com/openai/openai-go/v3=github.com/coder/openai-go/v3@coder/pinned; go mod tidy` +replace github.com/openai/openai-go/v3 => github.com/coder/openai-go/v3 v3.0.0-20260708125056-3633b71968f7 require ( cdr.dev/slog/v3 v3.1.0 diff --git a/go.sum b/go.sum index 1dc6301ab4..097b0511cc 100644 --- a/go.sum +++ b/go.sum @@ -336,6 +336,8 @@ github.com/coder/guts v1.7.0 h1:TaZ/PR9wgN8dlbcckaWV1MxkkuEFZRwSRwBBEm8dYXs= github.com/coder/guts v1.7.0/go.mod h1:30SShdvpmsauNlsNjECRB5AppScjYk08rf2ZVpH3MFg= github.com/coder/gvisor v0.0.0-20260313164934-7a658db7b714 h1:j7tyq3rv0ZXkbyy/BE5K3lYiIqEsV8sDTiTjpLLxjiw= github.com/coder/gvisor v0.0.0-20260313164934-7a658db7b714/go.mod h1:sxc3Uvk/vHcd3tj7/DHVBoR5wvWT/MmRq2pj7HRJnwU= +github.com/coder/openai-go/v3 v3.0.0-20260708125056-3633b71968f7 h1:YEThquYyw8RkFRojQdrGIYUQ4juXvsUNFfx1ZN9II+U= +github.com/coder/openai-go/v3 v3.0.0-20260708125056-3633b71968f7/go.mod h1:cdufnVK14cWcT9qA1rRtrXx4FTRsgbDPW7Ia7SS5cZo= github.com/coder/pq v1.10.5-0.20250807075151-6ad9b0a25151 h1:YAxwg3lraGNRwoQ18H7R7n+wsCqNve7Brdvj0F1rDnU= github.com/coder/pq v1.10.5-0.20250807075151-6ad9b0a25151/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 h1:3A0ES21Ke+FxEM8CXx9n47SZOKOpgSE1bbJzlE4qPVs= @@ -820,8 +822,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylecarbs/chroma/v2 v2.0.0-20240401211003-9e036e0631f3 h1:Z9/bo5PSeMutpdiKYNt/TTSfGM1Ll0naj3QzYX9VxTc= github.com/kylecarbs/chroma/v2 v2.0.0-20240401211003-9e036e0631f3/go.mod h1:BUGjjsD+ndS6eX37YgTchSEG+Jg9Jv1GiZs9sqPqztk= -github.com/kylecarbs/openai-go/v3 v3.0.0-20260319113850-9477dcaedcae h1:xlFZNX4nnxpj9Cf6mTwD3pirXGNtBJ/6COsf9iZmsL0= -github.com/kylecarbs/openai-go/v3 v3.0.0-20260319113850-9477dcaedcae/go.mod h1:cdufnVK14cWcT9qA1rRtrXx4FTRsgbDPW7Ia7SS5cZo= github.com/kylecarbs/spinner v1.18.2-0.20220329160715-20702b5af89e h1:OP0ZMFeZkUnOzTFRfpuK3m7Kp4fNvC6qN+exwj7aI4M= github.com/kylecarbs/spinner v1.18.2-0.20220329160715-20702b5af89e/go.mod h1:mQak9GHqbspjC/5iUx3qMlIho8xBS/ppAL/hX5SmPJU= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=