93 lines
3.3 KiB
Go
93 lines
3.3 KiB
Go
package protocol
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestChatOutputReasoning(t *testing.T) {
|
|
for _, tool := range []bool{false, true} {
|
|
message := object{"role": "assistant", "content": "OK", "reasoning_content": "PRIVATE"}
|
|
delta := object{"content": "OK", "reasoning_content": "PRIVATE"}
|
|
finish := "stop"
|
|
if tool {
|
|
call := object{"index": 0, "id": "call_1", "type": "function", "function": object{"name": "weather", "arguments": "{}"}}
|
|
message["content"] = nil
|
|
message["tool_calls"] = []any{call}
|
|
delta = object{"tool_calls": []any{call}, "reasoning_content": nil}
|
|
finish = "tool_calls"
|
|
}
|
|
usage := object{"prompt_tokens": 9, "completion_tokens": 7}
|
|
response := object{"id": "c", "object": "chat.completion", "model": "m", "choices": []any{object{"index": 0, "message": message, "finish_reason": finish}}, "usage": usage}
|
|
body, _ := json.Marshal(response)
|
|
chunk, _ := json.Marshal(object{"id": "c", "choices": []any{object{"index": 0, "delta": delta, "finish_reason": finish}}, "usage": usage})
|
|
reasoning, _ := json.Marshal(object{"id": "c", "choices": []any{object{"index": 0, "delta": object{"role": "assistant", "reasoning_content": "PRIVATE"}, "finish_reason": nil}}})
|
|
sep := string([]byte{10, 10})
|
|
src := "data: " + string(reasoning) + sep + "data: " + string(chunk) + sep + "data: [DONE]" + sep
|
|
for _, target := range []Kind{Chat, Responses, Anthropic} {
|
|
out, err := ConvertResponse(Chat, target, body)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var dst bytes.Buffer
|
|
u, err := Stream(context.Background(), &dst, strings.NewReader(src), Chat, target, "m")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !u.Known || u.Input != 9 || u.Output != 7 {
|
|
t.Fatalf("usage: %+v", u)
|
|
}
|
|
if target == Chat {
|
|
if !bytes.Equal(out, body) || dst.String() != src {
|
|
t.Fatal("native bytes changed")
|
|
}
|
|
continue
|
|
}
|
|
for _, text := range []string{string(out), dst.String()} {
|
|
if strings.Contains(text, "PRIVATE") || strings.Contains(text, "reasoning_content") || strings.Contains(text, "thinking") {
|
|
t.Fatal("reasoning leaked")
|
|
}
|
|
want := "OK"
|
|
if tool {
|
|
want = "weather"
|
|
}
|
|
if !strings.Contains(text, want) {
|
|
t.Fatal("lost output")
|
|
}
|
|
}
|
|
terminal := "response.completed"
|
|
if target == Anthropic {
|
|
terminal = "message_stop"
|
|
}
|
|
if !strings.Contains(dst.String(), terminal) {
|
|
t.Fatal("no terminal")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestChatOutputMetadataValidation(t *testing.T) {
|
|
for _, val := range []any{nil, "", "private"} {
|
|
if _, err := chatOutput(object{"reasoning_content": val}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
for _, val := range []any{true, object{}, []any{}} {
|
|
if _, err := chatOutput(object{"reasoning_content": val}); err == nil {
|
|
t.Fatal("accepted malformed reasoning")
|
|
}
|
|
}
|
|
for _, index := range []any{json.Number("1"), json.Number("-1"), json.Number("0.5"), "0", nil} {
|
|
if _, err := chatResponseMessage(object{"tool_calls": []any{object{"index": index}}}); err == nil {
|
|
t.Fatal("accepted malformed index")
|
|
}
|
|
}
|
|
body := []byte(`{"id":"c","choices":[{"message":{"role":"assistant","content":"OK","reasoning_content":"hidden","unknown_field":true},"finish_reason":"stop"}]}`)
|
|
if _, err := ConvertResponse(Chat, Responses, body); err == nil {
|
|
t.Fatal("accepted unknown field")
|
|
}
|
|
}
|