diff --git a/lib/callbacks.ex b/lib/callbacks.ex index 191310c5..0716b1bc 100644 --- a/lib/callbacks.ex +++ b/lib/callbacks.ex @@ -42,7 +42,7 @@ defmodule LangChain.Callbacks do rescue err -> msg = - "Callback handler for #{inspect(callback_name)} raised an exception: #{inspect(err)}" + "Callback handler for #{inspect(callback_name)} raised an exception: #{LangChainError.format_exception(err, __STACKTRACE__, :short)}" Logger.error(msg) raise LangChainError, msg diff --git a/lib/chains/llm_chain.ex b/lib/chains/llm_chain.ex index 56e805a1..9df0307d 100644 --- a/lib/chains/llm_chain.ex +++ b/lib/chains/llm_chain.ex @@ -978,7 +978,9 @@ defmodule LangChain.Chains.LLMChain do end rescue err -> - Logger.error("Function #{function.name} failed in execution. Exception: #{inspect(err)}") + Logger.error( + "Function #{function.name} failed in execution. Exception: #{LangChainError.format_exception(err, __STACKTRACE__)}" + ) ToolResult.new!(%{ tool_call_id: call.call_id, diff --git a/lib/function.ex b/lib/function.ex index 8a2070b9..68b9609a 100644 --- a/lib/function.ex +++ b/lib/function.ex @@ -281,8 +281,11 @@ defmodule LangChain.Function do end rescue err -> - Logger.error("Function #{function.name} failed in execution. Exception: #{inspect(err)}") - {:error, "ERROR: #{inspect(err)}"} + Logger.error( + "Function! #{function.name} failed in execution. Exception: #{LangChainError.format_exception(err, __STACKTRACE__)}" + ) + + {:error, "ERROR: #{LangChainError.format_exception(err, __STACKTRACE__, :short)}"} end end diff --git a/lib/langchain_error.ex b/lib/langchain_error.ex index c9b5e197..3ac4212b 100644 --- a/lib/langchain_error.ex +++ b/lib/langchain_error.ex @@ -47,4 +47,19 @@ defmodule LangChain.LangChainError do original: Keyword.get(opts, :original) } end + + @doc """ + Formats the exception as a string using a stacktrace to provide context. + """ + @spec format_exception(exception :: struct(), trace :: Exception.stacktrace(), format :: atom()) :: + String.t() + def format_exception(exception, trace, format \\ :full_stacktrace) do + case format do + :short -> + "(#{inspect(exception.__struct__)}) #{Exception.message(exception)} at #{Enum.at(trace, 0) |> Exception.format_stacktrace_entry()}" + + _ -> + Exception.format(:error, exception, trace) + end + end end diff --git a/test/callbacks_test.exs b/test/callbacks_test.exs index 2363b168..b4c74de9 100644 --- a/test/callbacks_test.exs +++ b/test/callbacks_test.exs @@ -34,7 +34,7 @@ defmodule LangChain.CallbacksTest do handlers = %{custom: fn _value -> raise ArgumentError, "BOOM!" end} assert_raise LangChainError, - "Callback handler for :custom raised an exception: %ArgumentError{message: \"BOOM!\"}", + "Callback handler for :custom raised an exception: (ArgumentError) BOOM! at test/callbacks_test.exs:#{__ENV__.line - 3}: anonymous fn/1 in LangChain.CallbacksTest.\"test fire/3 handles when a handler errors\"/1", fn -> Callbacks.fire([handlers], :custom, ["123"]) end diff --git a/test/chains/llm_chain_test.exs b/test/chains/llm_chain_test.exs index b4f711b4..39405bfb 100644 --- a/test/chains/llm_chain_test.exs +++ b/test/chains/llm_chain_test.exs @@ -1756,7 +1756,10 @@ defmodule LangChain.Chains.LLMChainTest do assert updated_chain.last_message.role == :tool [%ToolResult{} = result] = updated_chain.last_message.tool_results - assert result.content == "ERROR: %RuntimeError{message: \"Stuff went boom!\"}" + + assert result.content == + "ERROR: (RuntimeError) Stuff went boom! at test/chains/llm_chain_test.exs:#{__ENV__.line - 19}: anonymous fn/2 in LangChain.Chains.LLMChainTest.\"test execute_tool_calls/2 catches exceptions from executed function and returns Tool result with error message\"/1" + assert result.is_error == true end diff --git a/test/function_test.exs b/test/function_test.exs index 25242330..acabeb3c 100644 --- a/test/function_test.exs +++ b/test/function_test.exs @@ -136,7 +136,10 @@ defmodule LangChain.FunctionTest do # rescues an exception and returns as string text result = Function.execute(function, %{}, %{result: :exception}) - assert result == {:error, "ERROR: %RuntimeError{message: \"fake exception\"}"} + + assert result == + {:error, + "ERROR: (RuntimeError) fake exception at test/function_test.exs:13: LangChain.FunctionTest.returns_context/2"} # returns an error when anything else is returned result = Function.execute(function, %{}, %{result: 123})