fix(openai_compat): cover DeepSeek replay requirements
This commit is contained in:
parent
1e59e6056e
commit
0fc364c1cd
1 changed files with 106 additions and 0 deletions
|
|
@ -526,6 +526,112 @@ func TestProviderChat_HistoryCanonicalizationMatrix(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProviderChat_DeepSeekDocsReplayRequirements(t *testing.T) {
|
||||||
|
// DeepSeek's thinking-mode and multi-round chat docs distinguish two cases:
|
||||||
|
// - for a plain assistant turn between two user messages without tool calls,
|
||||||
|
// reasoning_content does not need to be replayed and the API ignores it if sent;
|
||||||
|
// - for a turn that participates in a tool-interaction round, assistant
|
||||||
|
// reasoning_content must be replayed on subsequent requests.
|
||||||
|
//
|
||||||
|
// Keep this behavior explicit here so future changes do not "fix" the
|
||||||
|
// non-tool stripping based on issue reports that are broader than the
|
||||||
|
// vendor documentation.
|
||||||
|
var requestBody map[string]any
|
||||||
|
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp := map[string]any{
|
||||||
|
"choices": []map[string]any{
|
||||||
|
{
|
||||||
|
"message": map[string]any{"content": "ok"},
|
||||||
|
"finish_reason": "stop",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(resp)
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
p := NewProvider("key", server.URL, "")
|
||||||
|
p.SetProviderName("deepseek")
|
||||||
|
|
||||||
|
messages := []Message{
|
||||||
|
{Role: "user", Content: "Who wrote The Hobbit?"},
|
||||||
|
{Role: "assistant", Content: "J.R.R. Tolkien.", ReasoningContent: "I know this from general knowledge."},
|
||||||
|
{Role: "user", Content: "What's the weather tomorrow?"},
|
||||||
|
{
|
||||||
|
Role: "assistant",
|
||||||
|
Content: "Let me check the date first.",
|
||||||
|
ReasoningContent: "I need tomorrow's date before checking the weather.",
|
||||||
|
ToolCalls: []ToolCall{{
|
||||||
|
ID: "call_date",
|
||||||
|
Type: "function",
|
||||||
|
Function: &FunctionCall{
|
||||||
|
Name: "get_date",
|
||||||
|
Arguments: "{}",
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
{Role: "tool", ToolCallID: "call_date", Content: "2026-04-29"},
|
||||||
|
{
|
||||||
|
Role: "assistant",
|
||||||
|
Content: "Tomorrow is 2026-04-30.",
|
||||||
|
ReasoningContent: "Now I can continue with the weather request.",
|
||||||
|
},
|
||||||
|
{Role: "user", Content: "What about Guangzhou?"},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := p.Chat(t.Context(), messages, nil, "deepseek-v4-flash", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Chat() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
reqMessages, ok := requestBody["messages"].([]any)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("messages is not []any: %T", requestBody["messages"])
|
||||||
|
}
|
||||||
|
if len(reqMessages) != len(messages) {
|
||||||
|
t.Fatalf("len(messages) = %d, want %d", len(reqMessages), len(messages))
|
||||||
|
}
|
||||||
|
|
||||||
|
plainAssistant, ok := reqMessages[1].(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("plain assistant message is not map[string]any: %T", reqMessages[1])
|
||||||
|
}
|
||||||
|
if _, exists := plainAssistant["reasoning_content"]; exists {
|
||||||
|
t.Fatalf(
|
||||||
|
"plain DeepSeek turn should omit reasoning_content on replay, got %v",
|
||||||
|
plainAssistant["reasoning_content"],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
toolAssistant, ok := reqMessages[3].(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("tool assistant message is not map[string]any: %T", reqMessages[3])
|
||||||
|
}
|
||||||
|
if toolAssistant["reasoning_content"] != "I need tomorrow's date before checking the weather." {
|
||||||
|
t.Fatalf(
|
||||||
|
"tool assistant reasoning_content = %v, want preserved",
|
||||||
|
toolAssistant["reasoning_content"],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
finalAssistant, ok := reqMessages[5].(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("final assistant message is not map[string]any: %T", reqMessages[5])
|
||||||
|
}
|
||||||
|
if finalAssistant["reasoning_content"] != "Now I can continue with the weather request." {
|
||||||
|
t.Fatalf(
|
||||||
|
"final assistant reasoning_content = %v, want preserved",
|
||||||
|
finalAssistant["reasoning_content"],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestProviderChat_HTTPError(t *testing.T) {
|
func TestProviderChat_HTTPError(t *testing.T) {
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Error(w, "bad request", http.StatusBadRequest)
|
http.Error(w, "bad request", http.StatusBadRequest)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue