ChatGPT OAuth may normalize explicit image size controls to auto while returning smaller raster bytes. Decode final image headers so Images API metadata and tier billing reflect the actual output for both streaming and non-streaming OAuth responses.
Enrich openai.upstream_failover_switching structured logs with proxy
identity fields (proxy_id, proxy_name, proxy_host, proxy_port) so
operators can see which outbound proxy failed when switching accounts.
Matches the existing gateway.forward_failed / websocket_proxy_failed
logging pattern.
Add proxy_id/proxy_name/proxy_host/proxy_port fields to openai.websocket_proxy_failed so operators can identify which proxy failed without extra lookups. Matches the existing gateway.forward_failed logging pattern.
Add a toggleable group ID column in /admin/groups column settings.
It shows the group id (e.g. #1), is hidden by default for new and
existing admins via column-settings version migration, and remains
sortable against the existing backend sort_by=id support.
WSv2 egress relays upstream events verbatim without the HTTP-path
namespace restore, so flattening requests that take the WSv2 branch
would surface flattened tool names the client cannot match. Resolve
the WS transport decision before flattening and skip flattening only
when the request will actually go WSv2 (passthrough accounts return
via HTTP before the WSv2 branch and still flatten).
Also check all type assertions in responses_namespace_test.go to
satisfy golangci-lint errcheck.
Resolve conflict in openai_gateway_passthrough.go streaming path: keep
main's normalizeCompletedImageGenerationStatus normalization ahead of
this branch's namespace restore block, mirroring the established order
in openai_gateway_response_handling.go.
The snapshot taken before dispatching the OAuth response handler still used
raw c.Writer.Size(), while handleOpenAIImagesOAuthResponseError compares it
against the keepalive-adjusted size. From the second failover round on, the
previous round's heartbeat bytes made the raw snapshot positive while the
adjusted size stayed -1, so a retryable upstream error was misjudged as
'response already written': the failover was swallowed and the client got a
committed 200 with a whitespace-only body.
Snapshot now uses OpenAIImagesJSONKeepaliveAdjustedWrittenSize, matching the
comparison side. Regression test simulates a prior-round heartbeat before
ForwardImages and asserts the retryable error still converts to
UpstreamFailoverError (verified red without the fix, green with it).