validateToolPairing function

List<ToolPairingViolation> validateToolPairing(
  1. List<Message> messages
)

Validates the pairing invariant on the wire-equivalent sequence of messages. An empty result means the context is safe to send.

Implementation

List<ToolPairingViolation> validateToolPairing(List<Message> messages) {
  final violations = <ToolPairingViolation>[];
  final seenCalls = <String>{};
  final answered = <String>{};
  var pending = const <String>[];
  for (final item in _wireView(messages)) {
    switch (item) {
      case _WireAssistant(:final callIds):
        _flagUnanswered(pending, answered, violations);
        pending = callIds;
        for (final id in callIds) {
          if (!seenCalls.add(id)) {
            violations.add(
              ToolPairingViolation(
                ToolPairingViolationKind.duplicateCallId,
                id,
                'tool_use id appears in more than one assistant message',
              ),
            );
          }
        }
      case _WireUser(:final blocks):
        var sawText = false;
        for (final (:resultId) in blocks) {
          if (resultId == null) {
            sawText = true;
            continue;
          }
          if (answered.contains(resultId)) {
            violations.add(
              ToolPairingViolation(
                ToolPairingViolationKind.duplicateResult,
                resultId,
                'a second tool_result answers the same id',
              ),
            );
          } else if (pending.contains(resultId)) {
            answered.add(resultId);
            if (sawText) {
              violations.add(
                ToolPairingViolation(
                  ToolPairingViolationKind.interleavedResult,
                  resultId,
                  'tool_result appears after other content in its message',
                ),
              );
            }
          } else {
            violations.add(
              ToolPairingViolation(
                ToolPairingViolationKind.orphanedResult,
                resultId,
                'its tool_use is not in the immediately preceding assistant '
                'message',
              ),
            );
          }
        }
        _flagUnanswered(pending, answered, violations);
        pending = const [];
    }
  }
  _flagUnanswered(pending, answered, violations);
  return violations;
}