diff --git a/libs/core/langchain_core/output_parsers/json.py b/libs/core/langchain_core/output_parsers/json.py index caed447068f..53c84925055 100644 --- a/libs/core/langchain_core/output_parsers/json.py +++ b/libs/core/langchain_core/output_parsers/json.py @@ -10,6 +10,7 @@ import jsonpatch # type: ignore[import] from langchain_core.exceptions import OutputParserException from langchain_core.output_parsers.format_instructions import JSON_FORMAT_INSTRUCTIONS from langchain_core.output_parsers.transform import BaseCumulativeTransformOutputParser +from langchain_core.outputs import Generation from langchain_core.pydantic_v1 import BaseModel @@ -106,11 +107,7 @@ def parse_partial_json(s: str, *, strict: bool = False) -> Any: new_s += closing_char # Attempt to parse the modified string as JSON. - try: - return json.loads(new_s, strict=strict) - except json.JSONDecodeError: - # If we still can't parse the string as JSON, return None to indicate failure. - return None + return json.loads(new_s, strict=strict) def parse_json_markdown( @@ -187,12 +184,22 @@ class JsonOutputParser(BaseCumulativeTransformOutputParser[Any]): def _diff(self, prev: Optional[Any], next: Any) -> Any: return jsonpatch.make_patch(prev, next).patch - def parse(self, text: str) -> Any: + def parse_result(self, result: List[Generation], *, partial: bool = False) -> Any: + text = result[0].text text = text.strip() - try: - return parse_json_markdown(text.strip()) - except JSONDecodeError as e: - raise OutputParserException(f"Invalid json output: {text}") from e + if partial: + try: + return parse_json_markdown(text) + except JSONDecodeError: + return None + else: + try: + return parse_json_markdown(text) + except JSONDecodeError as e: + raise OutputParserException(f"Invalid json output: {text}") from e + + def parse(self, text: str) -> Any: + return self.parse_result([Generation(text=text)]) def get_format_instructions(self) -> str: if self.pydantic_object is None: diff --git a/libs/core/tests/unit_tests/output_parsers/test_json.py b/libs/core/tests/unit_tests/output_parsers/test_json.py index 89362057445..2a2c30244e3 100644 --- a/libs/core/tests/unit_tests/output_parsers/test_json.py +++ b/libs/core/tests/unit_tests/output_parsers/test_json.py @@ -487,3 +487,9 @@ async def test_partial_text_json_output_parser_diff_async() -> None: chain = input_iter | SimpleJsonOutputParser(diff=True) assert [p async for p in chain.astream(None)] == EXPECTED_STREAMED_JSON_DIFF + + +def test_raises_error() -> None: + parser = SimpleJsonOutputParser() + with pytest.raises(Exception): + parser.invoke("hi") diff --git a/libs/langchain/langchain/output_parsers/openai_functions.py b/libs/langchain/langchain/output_parsers/openai_functions.py index 1b94bd0b355..5f52d4e6b6f 100644 --- a/libs/langchain/langchain/output_parsers/openai_functions.py +++ b/libs/langchain/langchain/output_parsers/openai_functions.py @@ -78,17 +78,20 @@ class JsonOutputFunctionsParser(BaseCumulativeTransformOutputParser[Any]): raise OutputParserException(f"Could not parse function call: {exc}") try: if partial: - if self.args_only: - return parse_partial_json( - function_call["arguments"], strict=self.strict - ) - else: - return { - **function_call, - "arguments": parse_partial_json( + try: + if self.args_only: + return parse_partial_json( function_call["arguments"], strict=self.strict - ), - } + ) + else: + return { + **function_call, + "arguments": parse_partial_json( + function_call["arguments"], strict=self.strict + ), + } + except json.JSONDecodeError: + return None else: if self.args_only: try: