@@ -217,6 +217,96 @@ func TestNormalizeAmpToolNames_GlobPreserved(t *testing.T) {
217217 }
218218}
219219
220+ func TestNormalizeAmpToolNames_RequestToolCasing_NonStreaming (t * testing.T ) {
221+ input := []byte (`{"content":[{"type":"tool_use","id":"toolu_01","name":"glob","input":{"pattern":"*.go"}}]}` )
222+ result := normalizeAmpToolNamesForRequest (input , map [string ]string {"glob" : "Glob" })
223+
224+ if ! contains (result , []byte (`"name":"Glob"` )) {
225+ t .Errorf ("expected glob->Glob when request advertised Glob, got %s" , string (result ))
226+ }
227+ }
228+
229+ func TestNormalizeAmpToolNames_RequestToolCasing_Streaming (t * testing.T ) {
230+ input := []byte (`{"type":"content_block_start","index":1,"content_block":{"type":"tool_use","name":"glob","id":"toolu_01","input":{}}}` )
231+ result := normalizeAmpToolNamesForRequest (input , map [string ]string {"glob" : "Glob" })
232+
233+ if ! contains (result , []byte (`"name":"Glob"` )) {
234+ t .Errorf ("expected glob->Glob in streaming when request advertised Glob, got %s" , string (result ))
235+ }
236+ }
237+
238+ func TestResponseRewriter_RequestToolCasingFromBody (t * testing.T ) {
239+ requestBody := []byte (`{"tools":[{"name":"Glob","input_schema":{"type":"object"}}]}` )
240+ rw := & ResponseRewriter {requestToolNames : collectRequestToolNames (requestBody )}
241+ input := []byte (`{"content":[{"type":"tool_use","id":"toolu_01","name":"glob","input":{"pattern":"*.go"}}]}` )
242+
243+ result := rw .rewriteModelInResponse (input )
244+
245+ if ! contains (result , []byte (`"name":"Glob"` )) {
246+ t .Errorf ("expected request body casing to restore glob->Glob, got %s" , string (result ))
247+ }
248+ }
249+
250+ func TestResponseRewriter_LowercaseNativeRequestPreserved (t * testing.T ) {
251+ requestBody := []byte (`{"tools":[{"name":"glob","input_schema":{"type":"object"}}]}` )
252+ rw := & ResponseRewriter {requestToolNames : collectRequestToolNames (requestBody )}
253+ input := []byte (`{"content":[{"type":"tool_use","id":"toolu_01","name":"glob","input":{"pattern":"*.go"}}]}` )
254+
255+ result := rw .rewriteModelInResponse (input )
256+
257+ if string (result ) == string (input ) {
258+ return
259+ }
260+ if ! contains (result , []byte (`"name":"glob"` )) {
261+ t .Errorf ("expected lowercase-native request to preserve glob, got %s" , string (result ))
262+ }
263+ }
264+
265+ func TestCollectRequestToolNames_CollisionIgnored (t * testing.T ) {
266+ tests := []struct {
267+ requestBody []byte
268+ input []byte
269+ forbidden []byte
270+ }{
271+ {
272+ requestBody : []byte (`{"tools":[{"name":"Glob","input_schema":{"type":"object"}},{"name":"glob","input_schema":{"type":"object"}}]}` ),
273+ input : []byte (`{"content":[{"type":"tool_use","id":"toolu_01","name":"glob","input":{"pattern":"*.go"}}]}` ),
274+ forbidden : []byte (`"name":"Glob"` ),
275+ },
276+ {
277+ requestBody : []byte (`{"tools":[{"name":"glob","input_schema":{"type":"object"}},{"name":"Glob","input_schema":{"type":"object"}}]}` ),
278+ input : []byte (`{"content":[{"type":"tool_use","id":"toolu_01","name":"glob","input":{"pattern":"*.go"}}]}` ),
279+ forbidden : []byte (`"name":"Glob"` ),
280+ },
281+ {
282+ requestBody : []byte (`{"tools":[{"name":"Bash","input_schema":{"type":"object"}},{"name":"bash","input_schema":{"type":"object"}}]}` ),
283+ input : []byte (`{"content":[{"type":"tool_use","id":"toolu_01","name":"bash","input":{"cmd":"ls"}}]}` ),
284+ forbidden : []byte (`"name":"Bash"` ),
285+ },
286+ }
287+
288+ for _ , tt := range tests {
289+ rw := & ResponseRewriter {requestToolNames : collectRequestToolNames (tt .requestBody )}
290+ result := rw .rewriteModelInResponse (tt .input )
291+
292+ if contains (result , tt .forbidden ) {
293+ t .Errorf ("expected conflicting tool casing not to force %s, got %s" , string (tt .forbidden ), string (result ))
294+ }
295+ }
296+ }
297+
298+ func TestResponseRewriter_RequestToolCasingFromBody_Streaming (t * testing.T ) {
299+ requestBody := []byte (`{"tools":[{"name":"Glob","input_schema":{"type":"object"}}]}` )
300+ rw := & ResponseRewriter {requestToolNames : collectRequestToolNames (requestBody )}
301+ input := []byte ("event: content_block_start\n data: {\" type\" :\" content_block_start\" ,\" index\" :1,\" content_block\" :{\" type\" :\" tool_use\" ,\" name\" :\" glob\" ,\" id\" :\" toolu_01\" ,\" input\" :{}}}\n \n " )
302+
303+ result := rw .rewriteStreamChunk (input )
304+
305+ if ! contains (result , []byte (`"name":"Glob"` )) {
306+ t .Errorf ("expected streaming response to restore glob->Glob from request body, got %s" , string (result ))
307+ }
308+ }
309+
220310func TestNormalizeAmpToolNames_UnknownToolUntouched (t * testing.T ) {
221311 input := []byte (`{"content":[{"type":"tool_use","id":"toolu_01","name":"edit_file","input":{"path":"/tmp/x"}}]}` )
222312 result := normalizeAmpToolNames (input )
0 commit comments