eleven / ocho Public

Clone
git clone https://amadan.net/eleven/ocho

Plain git — no account needed to clone.

Download

Download this file

1package bot
2
3import (
4 "encoding/json"
5 "strings"
6 "testing"
7)
8
9// TestAnthBlockMarshalArglessToolUse pins the echo wire shape: a tool_use the
10// model called with NO arguments must round-trip with an explicit empty
11// input (the API 400s without it — "tool_use.input: Field required"), while
12// other block shapes never grow an input field.
13func TestAnthBlockMarshalArglessToolUse(t *testing.T) {
14 argless, err := json.Marshal(anthBlock{Type: "tool_use", ID: "tu_1", Name: "issues.list"})
15 if err != nil {
16 t.Fatal(err)
17 }
18 if !strings.Contains(string(argless), `"input":{}`) {
19 t.Fatalf("argless tool_use must carry an explicit empty input: %s", argless)
20 }
21
22 withArgs, _ := json.Marshal(anthBlock{Type: "tool_use", ID: "tu_2", Name: "fetch",
23 Input: map[string]any{"url": "https://a.com/"}})
24 if !strings.Contains(string(withArgs), `"url":"https://a.com/"`) {
25 t.Fatalf("tool args lost: %s", withArgs)
26 }
27
28 for _, b := range []anthBlock{
29 {Type: "text", Text: "hi"},
30 {Type: "thinking", Thinking: "hmm", Signature: "sig"},
31 {Type: "tool_result", ToolUseID: "tu_1", Content: "ok"},
32 } {
33 out, _ := json.Marshal(b)
34 if strings.Contains(string(out), `"input"`) {
35 t.Fatalf("%s block grew an input field: %s", b.Type, out)
36 }
37 }
38}
39
40// TestAnthBlockMarshalEmptyThinking pins the other half of the echo trap: a
41// ZERO-LENGTH thinking block (adaptive thinking emits these on continuation
42// turns — seen live from the read_app_source loop) must round-trip with an
43// explicit empty thinking field, or the echo 400s ("thinking.thinking: Field
44// required") and the whole reply goes silent. Non-thinking blocks never grow
45// the field.
46func TestAnthBlockMarshalEmptyThinking(t *testing.T) {
47 empty, err := json.Marshal(anthBlock{Type: "thinking", Signature: "sig"})
48 if err != nil {
49 t.Fatal(err)
50 }
51 if !strings.Contains(string(empty), `"thinking":""`) {
52 t.Fatalf("empty thinking block must carry an explicit thinking field: %s", empty)
53 }
54 if !strings.Contains(string(empty), `"signature":"sig"`) {
55 t.Fatalf("signature lost from empty thinking block: %s", empty)
56 }
57
58 full, _ := json.Marshal(anthBlock{Type: "thinking", Thinking: "hmm", Signature: "sig"})
59 if !strings.Contains(string(full), `"thinking":"hmm"`) {
60 t.Fatalf("thinking text lost: %s", full)
61 }
62
63 for _, b := range []anthBlock{
64 {Type: "text", Text: "hi"},
65 {Type: "tool_result", ToolUseID: "tu_1", Content: "ok"},
66 } {
67 out, _ := json.Marshal(b)
68 if strings.Contains(string(out), `"thinking"`) {
69 t.Fatalf("%s block grew a thinking field: %s", b.Type, out)
70 }
71 }
72}
73