Mirrowel commited on
Commit
aeb8eaf
Β·
1 Parent(s): 1ce8eba

fix(provider): πŸ› add automatic ID repair for mismatched tool call responses

Browse files

Implements a recovery mechanism to handle cases where proxies or clients mutate tool call IDs (e.g., transforming "toolu_" prefix to "call_" prefix), which previously caused response grouping failures.

- Enhanced pending group handling to attempt orphan response matching when expected IDs are missing
- Automatically repairs response IDs to match their corresponding tool calls
- Maintains response order by using first available orphan for each unmatched call
- Added warning logs for ID mismatch repairs and partial group satisfaction
- Integrated tool response grouping fix into the main message transformation pipeline

This prevents tool call conversation corruption when intermediary services modify request/response identifiers.

src/rotator_library/providers/antigravity_provider.py CHANGED
@@ -1307,16 +1307,38 @@ class AntigravityProvider(AntigravityAuthBase, ProviderInterface):
1307
  new_contents.append(content)
1308
 
1309
  # Handle remaining groups (shouldn't happen in well-formed conversations)
 
1310
  for group in pending_groups:
1311
  group_ids = group["ids"]
1312
- available_ids = [gid for gid in group_ids if gid in collected_responses]
1313
- if available_ids:
1314
- group_responses = [collected_responses.pop(gid) for gid in available_ids]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1315
  new_contents.append({"parts": group_responses, "role": "user"})
1316
- lib_logger.warning(
1317
- f"[Grouping] Partial group satisfaction: expected {len(group_ids)}, "
1318
- f"got {len(available_ids)} responses"
1319
- )
 
 
1320
 
1321
  # Warn about unmatched responses
1322
  if collected_responses:
@@ -2305,6 +2327,7 @@ class AntigravityProvider(AntigravityAuthBase, ProviderInterface):
2305
  internal_model = self._alias_to_internal(model)
2306
 
2307
  system_instruction, contents = self._transform_messages(messages, internal_model)
 
2308
 
2309
  gemini_payload = {"contents": contents}
2310
  if system_instruction:
 
1307
  new_contents.append(content)
1308
 
1309
  # Handle remaining groups (shouldn't happen in well-formed conversations)
1310
+ # Attempt recovery by matching orphans to unsatisfied calls
1311
  for group in pending_groups:
1312
  group_ids = group["ids"]
1313
+ group_responses = []
1314
+
1315
+ for expected_id in group_ids:
1316
+ if expected_id in collected_responses:
1317
+ group_responses.append(collected_responses.pop(expected_id))
1318
+ elif collected_responses:
1319
+ # Recovery: Match with an orphan response
1320
+ # This handles cases where client/proxy mutates IDs (e.g. toolu_ -> call_)
1321
+ # Get the first available orphan ID to maintain order
1322
+ orphan_id = next(iter(collected_responses))
1323
+ orphan_resp = collected_responses.pop(orphan_id)
1324
+
1325
+ # Fix the ID in the response to match the call
1326
+ orphan_resp["functionResponse"]["id"] = expected_id
1327
+
1328
+ lib_logger.warning(
1329
+ f"[Grouping] Auto-repaired ID mismatch: mapped response '{orphan_id}' "
1330
+ f"to call '{expected_id}'"
1331
+ )
1332
+ group_responses.append(orphan_resp)
1333
+
1334
+ if group_responses:
1335
  new_contents.append({"parts": group_responses, "role": "user"})
1336
+
1337
+ if len(group_responses) != len(group_ids):
1338
+ lib_logger.warning(
1339
+ f"[Grouping] Partial group satisfaction after repair: "
1340
+ f"expected {len(group_ids)}, got {len(group_responses)} responses"
1341
+ )
1342
 
1343
  # Warn about unmatched responses
1344
  if collected_responses:
 
2327
  internal_model = self._alias_to_internal(model)
2328
 
2329
  system_instruction, contents = self._transform_messages(messages, internal_model)
2330
+ contents = self._fix_tool_response_grouping(contents)
2331
 
2332
  gemini_payload = {"contents": contents}
2333
  if system_instruction: