diff --git a/libs/core/langchain_core/output_parsers/openai_tools.py b/libs/core/langchain_core/output_parsers/openai_tools.py index 0b756c41491..c42e0946653 100644 --- a/libs/core/langchain_core/output_parsers/openai_tools.py +++ b/libs/core/langchain_core/output_parsers/openai_tools.py @@ -355,8 +355,18 @@ class PydanticToolsParser(JsonOutputToolsParser): f"{res['args']}" ) raise ValueError(msg) + try: - pydantic_objects.append(name_dict[res["type"]](**res["args"])) + tool = name_dict[res["type"]] + except KeyError as e: + available = ", ".join(name_dict.keys()) or "" + msg = ( + f"Unknown tool type: {res['type']!r}. Available tools: {available}" + ) + raise OutputParserException(msg) from e + + try: + pydantic_objects.append(tool(**res["args"])) except (ValidationError, ValueError): if partial: continue diff --git a/libs/core/tests/unit_tests/output_parsers/test_openai_tools.py b/libs/core/tests/unit_tests/output_parsers/test_openai_tools.py index f2e3a475b4a..849f2a2a2c6 100644 --- a/libs/core/tests/unit_tests/output_parsers/test_openai_tools.py +++ b/libs/core/tests/unit_tests/output_parsers/test_openai_tools.py @@ -6,6 +6,7 @@ import pydantic import pytest from pydantic import BaseModel, Field, ValidationError +from langchain_core.exceptions import OutputParserException from langchain_core.messages import ( AIMessage, AIMessageChunk, @@ -1423,3 +1424,31 @@ def test_parse_tool_call_partial_mode_with_none_arguments() -> None: # In partial mode, None arguments returns None (incomplete tool call) assert result is None + + +@pytest.mark.parametrize("partial", [False, True]) +def test_pydantic_tools_parser_unknown_tool_raises_output_parser_exception( + partial: bool, # noqa: FBT001 +) -> None: + class KnownTool(BaseModel): + value: int + + parser = PydanticToolsParser(tools=[KnownTool]) + message = AIMessage( + content="", + tool_calls=[ + { + "id": "call_unknown", + "name": "UnknownTool", + "args": {"value": 1}, + } + ], + ) + generation = ChatGeneration(message=message) + + with pytest.raises(OutputParserException) as excinfo: + parser.parse_result([generation], partial=partial) + + msg = str(excinfo.value) + assert "Unknown tool type" in msg + assert "UnknownTool" in msg