fix(openai-ws): exclude terminal events from TTFT

This commit is contained in:
wucm667 committed 2026-08-07 00:25:37 +08:00
1 parent a19c9f8d8a
commit ab326c96eb
3 files changed
+84 -21

No files matched your search

@@ -953,23 +953,7 @@ func shouldParseUsage(eventType string) bool {
}
func isTokenEvent(eventType string) bool {
if eventType == "" {
return false
}
switch eventType {
case "response.created", "response.in_progress", "response.output_item.added", "response.output_item.done":
return false
}
if strings.Contains(eventType, ".delta") {
return true
}
if strings.HasPrefix(eventType, "response.output_text") {
return true
}
if strings.HasPrefix(eventType, "response.output") {
return true
}
return eventType == "response.completed" || eventType == "response.done"
return strings.HasSuffix(strings.TrimSpace(eventType), ".delta")
}
func minDuration(a, b time.Duration) time.Duration {
@@ -246,7 +246,7 @@ func TestHelperFunctionsCoverage(t *testing.T) {
require.True(t, isTokenEvent("response.output_text.delta"))
require.True(t, isTokenEvent("response.output_audio.delta"))
require.True(t, isTokenEvent("response.completed"))
require.False(t, isTokenEvent("response.completed"))
require.False(t, isTokenEvent(""))
require.False(t, isTokenEvent("response.created"))
@@ -407,8 +407,29 @@ func TestIsTokenEventCoverageBranches(t *testing.T) {
require.False(t, isTokenEvent("response.in_progress"))
require.False(t, isTokenEvent("response.output_item.added"))
require.True(t, isTokenEvent("response.output_audio.delta"))
require.True(t, isTokenEvent("response.output"))
require.True(t, isTokenEvent("response.done"))
require.True(t, isTokenEvent("response.function_call_arguments.delta"))
require.True(t, isTokenEvent("response.reasoning_summary_text.delta"))
require.False(t, isTokenEvent("response.output"))
require.False(t, isTokenEvent("response.output_text.done"))
require.False(t, isTokenEvent("response.output_audio.done"))
require.False(t, isTokenEvent("response.output_text.annotation.added"))
require.False(t, isTokenEvent("response.done"))
}
func TestTerminalAndTokenEventSetsAreDisjoint(t *testing.T) {
t.Parallel()
for _, eventType := range []string{
"response.completed",
"response.done",
"response.failed",
"response.incomplete",
"response.cancelled",
"response.canceled",
} {
require.True(t, isTerminalEvent(eventType), eventType)
require.False(t, isTokenEvent(eventType), eventType)
}
}
func TestShouldParseUsageTerminalEvents(t *testing.T) {
@@ -202,7 +202,7 @@ func TestRelay_BasicRelayAndUsage(t *testing.T) {
require.Equal(t, 7, result.Usage.InputTokens)
require.Equal(t, 3, result.Usage.OutputTokens)
require.Equal(t, 2, result.Usage.CacheReadInputTokens)
require.NotNil(t, result.FirstTokenMs)
require.Nil(t, result.FirstTokenMs)
require.Equal(t, int64(1), result.ClientToUpstreamFrames)
require.Equal(t, int64(1), result.UpstreamToClientFrames)
require.Equal(t, int64(0), result.DroppedDownstreamFrames)
@@ -865,6 +865,64 @@ func (c *errorOnWriteFrameConn) Close() error {
return nil
}
func TestRelay_NoDeltaTerminalSequence_FirstTokenMsNil(t *testing.T) {
t.Parallel()
for _, terminalEvent := range []string{"response.completed", "response.done"} {
terminalEvent := terminalEvent
t.Run(terminalEvent, func(t *testing.T) {
t.Parallel()
clientConn := newPassthroughTestFrameConn(nil, false)
upstreamConn := newPassthroughTestFrameConn([]passthroughTestFrame{
{
msgType: coderws.MessageText,
payload: []byte(`{"type":"response.created","response":{"id":"resp_no_delta"}}`),
},
{
msgType: coderws.MessageText,
payload: []byte(`{"type":"response.in_progress","response":{"id":"resp_no_delta"}}`),
},
{
msgType: coderws.MessageText,
payload: []byte(`{"type":"response.output_text.done","response_id":"resp_no_delta","text":""}`),
},
{
msgType: coderws.MessageText,
payload: []byte(`{"type":"response.content_part.done","response_id":"resp_no_delta"}`),
},
{
msgType: coderws.MessageText,
payload: []byte(`{"type":"response.output_item.done","response_id":"resp_no_delta"}`),
},
{
msgType: coderws.MessageText,
payload: []byte(`{"type":"` + terminalEvent + `","response":{"id":"resp_no_delta","usage":{"input_tokens":2,"output_tokens":0}}}`),
},
}, true)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
var turn RelayTurnResult
result, relayExit := Relay(
ctx,
clientConn,
upstreamConn,
[]byte(`{"type":"response.create","model":"gpt-5.3-codex","input":[]}`),
RelayOptions{OnTurnComplete: func(current RelayTurnResult) { turn = current }},
)
require.Nil(t, relayExit)
require.Equal(t, terminalEvent, turn.TerminalEventType)
require.Nil(t, turn.FirstTokenMs)
require.Equal(t, terminalEvent, result.TerminalEventType)
require.Nil(t, result.FirstTokenMs)
require.Equal(t, int64(6), result.UpstreamToClientFrames)
})
}
}
func TestRelay_OnTurnComplete_RealOpenAIStream_FirstTokenMs(t *testing.T) {
t.Parallel()