chore: initial commit
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user