@@ -416,7 +416,7 @@ def test_human_in_the_loop_middleware_single_tool_accept() -> None:
416
416
state = {"messages" : [HumanMessage (content = "Hello" ), ai_message ]}
417
417
418
418
def mock_accept (requests ):
419
- return [{"type" : "approve" , "args" : None , "tool_call_id" : "1" }]
419
+ return [{"type" : "approve" }]
420
420
421
421
with patch ("langchain.agents.middleware.human_in_the_loop.interrupt" , side_effect = mock_accept ):
422
422
result = middleware .after_model (state )
@@ -449,7 +449,6 @@ def mock_edit(requests):
449
449
"type" : "edit" ,
450
450
"action" : "test_tool" ,
451
451
"args" : {"input" : "edited" },
452
- "tool_call_id" : "1" ,
453
452
}
454
453
]
455
454
@@ -479,7 +478,7 @@ def test_human_in_the_loop_middleware_single_tool_ignore() -> None:
479
478
state = {"messages" : [HumanMessage (content = "Hello" ), ai_message ]}
480
479
481
480
def mock_ignore (requests ):
482
- return [{"type" : "ignore" , "args" : None , "tool_call_id" : "1" }]
481
+ return [{"type" : "ignore" }]
483
482
484
483
with patch ("langchain.agents.middleware.human_in_the_loop.interrupt" , side_effect = mock_ignore ):
485
484
result = middleware .after_model (state )
@@ -511,7 +510,7 @@ def test_human_in_the_loop_middleware_single_tool_response() -> None:
511
510
state = {"messages" : [HumanMessage (content = "Hello" ), ai_message ]}
512
511
513
512
def mock_response (requests ):
514
- return [{"type" : "response" , "tool_message" : "Custom response" , "tool_call_id" : "1" }]
513
+ return [{"type" : "response" , "tool_message" : "Custom response" }]
515
514
516
515
with patch (
517
516
"langchain.agents.middleware.human_in_the_loop.interrupt" , side_effect = mock_response
@@ -552,8 +551,8 @@ def test_human_in_the_loop_middleware_multiple_tools_mixed_responses() -> None:
552
551
553
552
def mock_mixed_responses (requests ):
554
553
return [
555
- {"type" : "approve" , "args" : None , "tool_call_id" : "1" },
556
- {"type" : "ignore" , "args" : None , "tool_call_id" : "2" },
554
+ {"type" : "approve" },
555
+ {"type" : "ignore" },
557
556
]
558
557
559
558
with patch (
@@ -605,13 +604,11 @@ def mock_edit_responses(requests):
605
604
"type" : "edit" ,
606
605
"action" : "get_forecast" ,
607
606
"args" : {"location" : "New York" },
608
- "tool_call_id" : "1" ,
609
607
},
610
608
{
611
609
"type" : "edit" ,
612
610
"action" : "get_temperature" ,
613
611
"args" : {"location" : "New York" },
614
- "tool_call_id" : "2" ,
615
612
},
616
613
]
617
614
@@ -657,12 +654,10 @@ def mock_response_responses(requests):
657
654
{
658
655
"type" : "response" ,
659
656
"tool_message" : "actually, please get the conditions in NYC" ,
660
- "tool_call_id" : "1" ,
661
657
},
662
658
{
663
659
"type" : "response" ,
664
660
"tool_message" : "actually, please get the temperature in NYC" ,
665
- "tool_call_id" : "2" ,
666
661
},
667
662
]
668
663
@@ -704,12 +699,12 @@ def test_human_in_the_loop_middleware_unknown_response_type() -> None:
704
699
state = {"messages" : [HumanMessage (content = "Hello" ), ai_message ]}
705
700
706
701
def mock_unknown (requests ):
707
- return [{"type" : "unknown" , "args" : None , "tool_call_id" : "1" }]
702
+ return [{"type" : "unknown" }]
708
703
709
704
with patch ("langchain.agents.middleware.human_in_the_loop.interrupt" , side_effect = mock_unknown ):
710
705
with pytest .raises (
711
706
ValueError ,
712
- match = "Unexpected human response: {'type': 'unknown', 'args': None, 'tool_call_id': '1' }. Response type 'unknown' is not allowed for tool 'test_tool'. Expected one with `'type'` in \\ ['accept', 'edit', 'response', 'ignore'\\ ] based on the tool's interrupt configuration." ,
707
+ match = "Unexpected human response: {'type': 'unknown'}. Response type 'unknown' is not allowed for tool 'test_tool'. Expected one with `'type'` in \\ ['accept', 'edit', 'response', 'ignore'\\ ] based on the tool's interrupt configuration." ,
713
708
):
714
709
middleware .after_model (state )
715
710
@@ -731,15 +726,15 @@ def test_human_in_the_loop_middleware_disallowed_response_type() -> None:
731
726
state = {"messages" : [HumanMessage (content = "Hello" ), ai_message ]}
732
727
733
728
def mock_disallowed_response (requests ):
734
- return [{"type" : "response" , "args " : "Custom response" , "tool_call_id" : "1 " }]
729
+ return [{"type" : "response" , "tool_message " : "Custom response" }]
735
730
736
731
with patch (
737
732
"langchain.agents.middleware.human_in_the_loop.interrupt" ,
738
733
side_effect = mock_disallowed_response ,
739
734
):
740
735
with pytest .raises (
741
736
ValueError ,
742
- match = "Unexpected human response: {'type': 'response', 'args ': 'Custom response', 'tool_call_id': '1 '}. Response type 'response' is not allowed for tool 'test_tool'. Expected one with `'type'` in \\ ['accept', 'ignore'\\ ] based on the tool's interrupt configuration." ,
737
+ match = "Unexpected human response: {'type': 'response', 'tool_message ': 'Custom response'}. Response type 'response' is not allowed for tool 'test_tool'. Expected one with `'type'` in \\ ['accept', 'ignore'\\ ] based on the tool's interrupt configuration." ,
743
738
):
744
739
middleware .after_model (state )
745
740
@@ -766,7 +761,6 @@ def mock_disallowed_edit(requests):
766
761
"type" : "edit" ,
767
762
"action" : "test_tool" ,
768
763
"args" : {"input" : "edited" },
769
- "tool_call_id" : "1" ,
770
764
}
771
765
]
772
766
@@ -775,7 +769,7 @@ def mock_disallowed_edit(requests):
775
769
):
776
770
with pytest .raises (
777
771
ValueError ,
778
- match = "Unexpected human response: {'type': 'edit', 'action': 'test_tool', 'args': {'input': 'edited'}, 'tool_call_id': '1' }. Response type 'edit' is not allowed for tool 'test_tool'. Expected one with `'type'` in \\ ['accept', 'response', 'ignore'\\ ] based on the tool's interrupt configuration." ,
772
+ match = "Unexpected human response: {'type': 'edit', 'action': 'test_tool', 'args': {'input': 'edited'}}. Response type 'edit' is not allowed for tool 'test_tool'. Expected one with `'type'` in \\ ['accept', 'response', 'ignore'\\ ] based on the tool's interrupt configuration." ,
779
773
):
780
774
middleware .after_model (state )
781
775
@@ -800,7 +794,7 @@ def test_human_in_the_loop_middleware_mixed_auto_approved_and_interrupt() -> Non
800
794
state = {"messages" : [HumanMessage (content = "Hello" ), ai_message ]}
801
795
802
796
def mock_accept (requests ):
803
- return [{"type" : "approve" , "args" : None , "tool_call_id" : "2" }]
797
+ return [{"type" : "approve" }]
804
798
805
799
with patch ("langchain.agents.middleware.human_in_the_loop.interrupt" , side_effect = mock_accept ):
806
800
result = middleware .after_model (state )
@@ -839,8 +833,8 @@ def test_human_in_the_loop_middleware_all_ignored() -> None:
839
833
840
834
def mock_all_ignore (requests ):
841
835
return [
842
- {"type" : "ignore" , "args" : None , "tool_call_id" : "1" },
843
- {"type" : "ignore" , "args" : None , "tool_call_id" : "2" },
836
+ {"type" : "ignore" },
837
+ {"type" : "ignore" },
844
838
]
845
839
846
840
with patch (
@@ -883,7 +877,7 @@ def test_human_in_the_loop_middleware_interrupt_request_structure() -> None:
883
877
884
878
def mock_capture_requests (requests ):
885
879
captured_requests .extend (requests )
886
- return [{"type" : "approve" , "args" : None , "tool_call_id" : "1" }]
880
+ return [{"type" : "approve" }]
887
881
888
882
with patch (
889
883
"langchain.agents.middleware.human_in_the_loop.interrupt" , side_effect = mock_capture_requests
@@ -919,7 +913,7 @@ def test_human_in_the_loop_middleware_boolean_configs() -> None:
919
913
# Test approve
920
914
with patch (
921
915
"langchain.agents.middleware.human_in_the_loop.interrupt" ,
922
- return_value = [{"type" : "approve" , "tool_call_id" : "1" }],
916
+ return_value = [{"type" : "approve" }],
923
917
):
924
918
result = middleware .after_model (state )
925
919
assert result is not None
@@ -933,7 +927,6 @@ def test_human_in_the_loop_middleware_boolean_configs() -> None:
933
927
return_value = [
934
928
{
935
929
"type" : "edit" ,
936
- "tool_call_id" : "1" ,
937
930
"action" : "test_tool" ,
938
931
"args" : {"input" : "edited" },
939
932
}
@@ -948,7 +941,7 @@ def test_human_in_the_loop_middleware_boolean_configs() -> None:
948
941
# Test ignore
949
942
with patch (
950
943
"langchain.agents.middleware.human_in_the_loop.interrupt" ,
951
- return_value = [{"type" : "ignore" , "tool_call_id" : "1" }],
944
+ return_value = [{"type" : "ignore" }],
952
945
):
953
946
result = middleware .after_model (state )
954
947
assert result is not None
@@ -963,7 +956,7 @@ def test_human_in_the_loop_middleware_boolean_configs() -> None:
963
956
# Test response
964
957
with patch (
965
958
"langchain.agents.middleware.human_in_the_loop.interrupt" ,
966
- return_value = [{"type" : "response" , "tool_call_id" : "1" , " tool_message" : "Custom response" }],
959
+ return_value = [{"type" : "response" , "tool_message" : "Custom response" }],
967
960
):
968
961
result = middleware .after_model (state )
969
962
assert result is not None
@@ -981,8 +974,8 @@ def test_human_in_the_loop_middleware_boolean_configs() -> None:
981
974
assert result is None
982
975
983
976
984
- def test_human_in_the_loop_middleware_missing_tool_call_id () -> None :
985
- """Test that missing tool call ID in resume raises an error."""
977
+ def test_human_in_the_loop_middleware_sequence_mismatch () -> None :
978
+ """Test that sequence mismatch in resume raises an error."""
986
979
middleware = HumanInTheLoopMiddleware (tool_configs = {"test_tool" : True })
987
980
988
981
ai_message = AIMessage (
@@ -991,23 +984,25 @@ def test_human_in_the_loop_middleware_missing_tool_call_id() -> None:
991
984
)
992
985
state = {"messages" : [HumanMessage (content = "Hello" ), ai_message ]}
993
986
987
+ # Test with too few responses
994
988
with patch (
995
989
"langchain.agents.middleware.human_in_the_loop.interrupt" ,
996
- return_value = [{ "type" : "approve" } ], # Missing tool_call_id
990
+ return_value = [], # No responses for 1 tool call
997
991
):
998
992
with pytest .raises (
999
993
ValueError ,
1000
- match = r"Unexpected human response: \{'type': 'approve'\}\. Expected one with `'tool_call_id'` in \['1'\] \." ,
994
+ match = r"Number of human responses \(0\) does not match number of hanging tool calls \(1\) \." ,
1001
995
):
1002
996
middleware .after_model (state )
1003
997
998
+ # Test with too many responses
1004
999
with patch (
1005
1000
"langchain.agents.middleware.human_in_the_loop.interrupt" ,
1006
- return_value = [{"type" : "approve" , "tool_call_id " : "nonexistent " }],
1001
+ return_value = [{"type" : "approve" }, { "type " : "approve " }], # 2 responses for 1 tool call
1007
1002
):
1008
1003
with pytest .raises (
1009
1004
ValueError ,
1010
- match = r"Unexpected human response: \{'type': 'approve', 'tool_call_id': 'nonexistent'\}\. Expected one with `'tool_call_id'` in \['1'\] \." ,
1005
+ match = r"Number of human responses \(2\) does not match number of hanging tool calls \(1\) \." ,
1011
1006
):
1012
1007
middleware .after_model (state )
1013
1008
0 commit comments