TestChatToolArgsStreamLive: each tool-call arguments fragment is forwarded as an EventToolArgs as it arrives, so the UI can tick its live token estimate while a file streams into write_file, not just once at stream end. Fragments concatenate to the full arguments and all precede the resolved EventTo
(t *testing.T)
| 160 | // while a file streams into write_file, not just once at stream end. Fragments |
| 161 | // concatenate to the full arguments and all precede the resolved EventToolCall. |
| 162 | func TestChatToolArgsStreamLive(t *testing.T) { |
| 163 | srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { |
| 164 | sseOK(w, []string{ |
| 165 | `{"choices":[{"delta":{"tool_calls":[{"index":0,"id":"c1","function":{"name":"write_file"}}]}}]}`, |
| 166 | `{"choices":[{"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"path\":\"a"}}]}}]}`, |
| 167 | `{"choices":[{"delta":{"tool_calls":[{"index":0,"function":{"arguments":".txt\",\"content\":\"hi"}}]}}]}`, |
| 168 | `{"choices":[{"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]}}]}`, |
| 169 | `{"choices":[{"delta":{},"finish_reason":"tool_calls"}]}`, |
| 170 | }) |
| 171 | })) |
| 172 | defer srv.Close() |
| 173 | c := New(srv.URL, "m", "") |
| 174 | var args strings.Builder |
| 175 | sawCall := false |
| 176 | argsAllBeforeCall := true |
| 177 | for _, e := range collect(c.Chat(context.Background(), nil, nil)) { |
| 178 | switch e.Kind { |
| 179 | case EventToolArgs: |
| 180 | args.WriteString(e.Content) |
| 181 | if sawCall { |
| 182 | argsAllBeforeCall = false |
| 183 | } |
| 184 | case EventToolCall: |
| 185 | sawCall = true |
| 186 | } |
| 187 | } |
| 188 | if got := args.String(); got != `{"path":"a.txt","content":"hi"}` { |
| 189 | t.Fatalf("EventToolArgs fragments should concatenate to the full args, got %q", got) |
| 190 | } |
| 191 | if !sawCall { |
| 192 | t.Fatalf("expected a resolved EventToolCall after the fragments") |
| 193 | } |
| 194 | if !argsAllBeforeCall { |
| 195 | t.Fatalf("every EventToolArgs must precede the resolved EventToolCall") |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | // TestChatToolCallMultipleByIndex: two tool calls interleaved across chunks. |
| 200 | // Each fragment must route to its slot by `index`, not by slice position. |