Merge pull request #2869 from Pluviobyte/fix/ws-first-token-terminal-event
fix(ws): exclude terminal events from first-token detection
This commit is contained in:
commit
e6a3f1e12b
@ -3915,7 +3915,10 @@ func isOpenAIWSTokenEvent(eventType string) bool {
|
||||
if strings.HasPrefix(eventType, "response.output") {
|
||||
return true
|
||||
}
|
||||
return eventType == "response.completed" || eventType == "response.done"
|
||||
// 终止事件(response.completed/done/failed/...)由 isOpenAIWSTerminalEvent 单独处理。
|
||||
// 不能把它们当作 token event,否则当上游没有可识别的 delta 时,
|
||||
// firstTokenMs 会被填到终止时刻,等于把"总耗时"误报为"首 token 延迟"。
|
||||
return false
|
||||
}
|
||||
|
||||
func replaceOpenAIWSMessageModel(message []byte, fromModel, toModel string) []byte {
|
||||
|
||||
75
backend/internal/service/openai_ws_forwarder_test.go
Normal file
75
backend/internal/service/openai_ws_forwarder_test.go
Normal file
@ -0,0 +1,75 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestIsOpenAIWSTokenEvent_TerminalEventsExcluded 覆盖 isOpenAIWSTokenEvent 的回归用例。
|
||||
// 重点验证终止事件(response.completed / response.done)不再被当作 token event,
|
||||
// 否则当上游没有可识别的 delta 时,firstTokenMs 会被填到终止时刻,
|
||||
// 等于把"总耗时"误报为"首 token 延迟"(issue #2651)。
|
||||
func TestIsOpenAIWSTokenEvent_TerminalEventsExcluded(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
eventType string
|
||||
want bool
|
||||
}{
|
||||
{name: "empty", eventType: "", want: false},
|
||||
{name: "whitespace_trimmed_empty", eventType: " ", want: false},
|
||||
|
||||
{name: "response.created", eventType: "response.created", want: false},
|
||||
{name: "response.in_progress", eventType: "response.in_progress", want: false},
|
||||
{name: "response.output_item.added", eventType: "response.output_item.added", want: false},
|
||||
{name: "response.output_item.done", eventType: "response.output_item.done", want: false},
|
||||
|
||||
{name: "terminal_response.completed", eventType: "response.completed", want: false},
|
||||
{name: "terminal_response.done", eventType: "response.done", want: false},
|
||||
{name: "terminal_response.completed_padded", eventType: " response.completed ", want: false},
|
||||
{name: "terminal_response.done_padded", eventType: " response.done ", want: false},
|
||||
|
||||
{name: "delta_text", eventType: "response.output_text.delta", want: true},
|
||||
{name: "delta_audio_transcript", eventType: "response.audio_transcript.delta", want: true},
|
||||
{name: "delta_function_call_arguments", eventType: "response.function_call_arguments.delta", want: true},
|
||||
|
||||
{name: "output_text_done", eventType: "response.output_text.done", want: true},
|
||||
{name: "output_text_annotation_added", eventType: "response.output_text.annotation.added", want: true},
|
||||
|
||||
{name: "output_audio_done", eventType: "response.output_audio.done", want: true},
|
||||
|
||||
{name: "reasoning_summary_delta", eventType: "response.reasoning_summary_text.delta", want: true},
|
||||
|
||||
{name: "unrelated_event_error", eventType: "error", want: false},
|
||||
{name: "unknown_event_without_match", eventType: "response.reasoning_summary_part.added", want: false},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := isOpenAIWSTokenEvent(tc.eventType)
|
||||
require.Equal(t, tc.want, got, "isOpenAIWSTokenEvent(%q)", tc.eventType)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestIsOpenAIWSTokenEvent_DisjointWithTerminal 守护「token 事件集合与终止事件集合互斥」的不变量。
|
||||
// firstTokenMs 的计算依赖于 isTokenEvent && !isTerminalEvent;
|
||||
// 若两者再次出现交集,则 issue #2651 描述的 latency 误报会重现。
|
||||
func TestIsOpenAIWSTokenEvent_DisjointWithTerminal(t *testing.T) {
|
||||
terminalEvents := []string{
|
||||
"response.completed",
|
||||
"response.done",
|
||||
"response.failed",
|
||||
"response.incomplete",
|
||||
"response.cancelled",
|
||||
"response.canceled",
|
||||
}
|
||||
for _, ev := range terminalEvents {
|
||||
ev := ev
|
||||
t.Run(ev, func(t *testing.T) {
|
||||
require.True(t, isOpenAIWSTerminalEvent(ev), "expected terminal event %q to be classified as terminal", ev)
|
||||
require.False(t, isOpenAIWSTokenEvent(ev), "terminal event %q must NOT be classified as token event (issue #2651)", ev)
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user