Merge pull request #5307 from lenzhang/fix/ws-prewarm-continuation

fix(ws): allow Codex prewarm continuation
This commit is contained in:
Wesley Liddick
2026-08-07 16:15:11 +08:00
committed by GitHub
2 changed files with 51 additions and 1 deletions
@@ -551,13 +551,22 @@ func TestNormalizeOpenAIWSPayloadWithoutInputAndPreviousResponseID(t *testing.T)
t.Parallel()
normalized, err := normalizeOpenAIWSPayloadWithoutInputAndPreviousResponseID(
[]byte(`{"model":"gpt-5.1","input":[1],"previous_response_id":"resp_x","metadata":{"b":2,"a":1}}`),
[]byte(`{"model":"gpt-5.1","input":[1],"previous_response_id":"resp_x","client_metadata":{"request_start_ms":"1"},"stream_options":{"include_usage":true},"generate":false,"metadata":{"b":2,"a":1}}`),
)
require.NoError(t, err)
require.False(t, gjson.GetBytes(normalized, "input").Exists())
require.False(t, gjson.GetBytes(normalized, "previous_response_id").Exists())
require.False(t, gjson.GetBytes(normalized, "client_metadata").Exists())
require.False(t, gjson.GetBytes(normalized, "stream_options").Exists())
require.False(t, gjson.GetBytes(normalized, "generate").Exists())
require.Equal(t, float64(1), gjson.GetBytes(normalized, "metadata.a").Float())
normalized, err = normalizeOpenAIWSPayloadWithoutInputAndPreviousResponseID(
[]byte(`{"model":"gpt-5.1","generate":true}`),
)
require.NoError(t, err)
require.True(t, gjson.GetBytes(normalized, "generate").Bool())
_, err = normalizeOpenAIWSPayloadWithoutInputAndPreviousResponseID(nil)
require.Error(t, err)
@@ -662,6 +671,36 @@ func TestShouldKeepIngressPreviousResponseID(t *testing.T) {
require.Equal(t, "strict_incremental_ok", reason)
})
t.Run("codex_prewarm_to_business_keep", func(t *testing.T) {
prewarmPayload := []byte(`{
"type":"response.create",
"model":"gpt-5.1",
"store":false,
"generate":false,
"client_metadata":{"x-codex-ws-stream-request-start-ms":"100"},
"stream_options":{"include_usage":true},
"input":[{"type":"input_text","text":"hello"}]
}`)
businessPayload := []byte(`{
"type":"response.create",
"model":"gpt-5.1",
"store":false,
"client_metadata":{"x-codex-ws-stream-request-start-ms":"200"},
"previous_response_id":"resp_prewarm",
"input":[{"type":"input_text","text":"hello"}]
}`)
keep, reason, err := shouldKeepIngressPreviousResponseID(
prewarmPayload,
businessPayload,
"resp_prewarm",
false,
)
require.NoError(t, err)
require.True(t, keep)
require.Equal(t, "strict_incremental_ok", reason)
})
t.Run("missing_previous_response_id", func(t *testing.T) {
payload := []byte(`{"type":"response.create","model":"gpt-5.1","input":[]}`)
keep, reason, err := shouldKeepIngressPreviousResponseID(previousPayload, payload, "resp_turn_1", false)
@@ -449,6 +449,17 @@ func normalizeOpenAIWSPayloadWithoutInputAndPreviousResponseID(payload []byte) (
}
delete(decoded, "input")
delete(decoded, "previous_response_id")
// Codex changes transport-only metadata for every response.create. These fields
// do not alter the context referenced by previous_response_id and are excluded
// from Codex's own websocket reuse comparison.
delete(decoded, "client_metadata")
delete(decoded, "stream_options")
// Official Codex prewarms a connection with generate=false, then omits the
// field on the business request that continues from the prewarm response.
// Only normalize false so a meaningful generate=true change remains visible.
if generate, ok := decoded["generate"].(bool); ok && !generate {
delete(decoded, "generate")
}
return json.Marshal(decoded)
}