Skip to content

Comments

UN-3376 For langchain tools, make sure the UI knows that the communication between those nodes has been completed#337

Merged
Noravee merged 14 commits intomainfrom
langchain_tool_callback
Jul 31, 2025
Merged

UN-3376 For langchain tools, make sure the UI knows that the communication between those nodes has been completed#337
Noravee merged 14 commits intomainfrom
langchain_tool_callback

Conversation

@Noravee
Copy link
Collaborator

@Noravee Noravee commented Jul 29, 2025

Summary

This PR introduces a proof of concept for logging (journaling) output from a LangChain tool using a callback-based approach.

Problem

For coded tools, journaling is handled within the activation class. However, LangChain tools are subclasses of LangChain’s BaseTool, and do not invoke our custom implementation (LangchainOpenaiFunctionTool) that creates an activation class and handles logging. As a result:

  • No journal is created for LangChain tools.

  • There is no clear indication when such a tool finishes execution.

Solution

Leverage JournalingCallbackHandler, which defines on_tool_start() and on_tool_end()—methods that are triggered at the start and end of tool execution.

Previously, callbacks were passed as arguments when instantiating the LLM, which limited their use to LLM-related events (e.g., on_llm_end()). By instead using the invoke config, callbacks can now respond to a broader set of events, including tool-level events like on_tool_start() and on_tool_end().

Changes

  • ToolboxFactory.create_tool_from_toolbox()

    • Added agent_name argument to override or prefix the LangChain tool name. This supports naming for single-tool agents and for agents with toolkits.

    • Added "langchain_tool" tag to identify tools that should trigger journaling.

  • JournalingCallbackHandler

    • __init__:

      • Renamed journal to calling_agent_journal.

      • Added new parameters: base_journal, parent_origin, and origination.

    • on_tool_start:

      • Checks for the "langchain_tool" tag.

      • Creates a langchain_tool_journal and logs the tool’s input.

      • Prevents double journaling for coded or branch tools.

    • on_tool_end:

      • Logs the tool's output to both the calling_agent_journal and the langchain_tool_journal.
  • LangChainRunContext

    • Moved callback assignment from create_resources() to wait_from_run() to ensure proper callback usage for tools as well as LLMs.
  • 'TestToolboxFactory: Added attribute nameandtags` to mock tools.

Tests

Tested by running the following agents and inspecting their corresponding thinking files to verify tool-level logging:

  • bing_search

  • music_nerd_pro

  • music_nerd_pro_multi_agent

Confirmed that:

  • Tool input is logged at the start (on_tool_start)

  • Tool output is logged at the end (on_tool_end)

  • Journals are correctly scoped and do not duplicate entries for coded or branch tools

Future Development

This callback-based approach could be extended to handle journaling for coded tools, branch tools, and other tool types—providing a unified and consistent logging mechanism across tool types.

@Noravee Noravee marked this pull request as draft July 29, 2025 09:17
# Replace langchain tool's name with agent name
instance.name = agent_name
# Add "langchain_tool" tags so journal callback can idenitify it
instance.tags = ["langchain_tool"]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Override langchain tool's name with agent_anme and add langchain_tool to tags as identifier.

toolkit: List[BaseTool] = instance.get_tools()
for tool in toolkit:
# Prefix the name of the agent to each tool
tool.name = f"{agent_name}_{tool.name}"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For agent with multiple tools (toolkit), prefix the tool names with the agent name.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth noting for above argument that you are not really checking for a None agent_name value,
so that kinda bolsters my argument as to its necessity of not being None.

"session_id": run.get_id()
}
},
"callbacks": callbacks
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use callbacks as invoke_config in wait_on_run() to have access to on_tool_start() and on_tool_run()

passed to the tool.
"""

if "langchain_tool" in tags:
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check if the tool is langchain's tool with tags.

# Create a journal entry for this invocation and log the combined inputs
self.langchain_tool_journal = OriginatingJournal(self.base_journal, origin)
message: BaseMessage = AgentMessage(content=f"Received arguments {combined_args}")
await self.langchain_tool_journal.write_message(message)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating journal for langchain tool and write message.


# Also log the tool output to the LangChain tool-specific journal
message: BaseMessage = AgentMessage(content=f"Got result: {output}")
await self.langchain_tool_journal.write_message(message)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Write message in the langchain journal with tool output. This can be used as an indicator that the tool is finished.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Noravee Since the UI already checks for "Got result:" for coded tool end events on the text field, this should work fine.

We also may want to consider adding a way to signify an end event for coded tools or langchain tools on the structure key instead, so that we don't have to do string parsing on the text field.

cc: @d1donlydfink

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can do that in a separate PR @swensel for full consistency

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @d1donlydfink , that would be great.

cc: @Noravee

Copy link
Collaborator

@d1donlydfink d1donlydfink left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please answer the questions sprinkled throughout the PR.

dsargent
dsargent previously approved these changes Jul 29, 2025
Copy link
Contributor

@dsargent dsargent left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't really know enough to be useful here. I will defer to @d1donlydfink's questions.


def create_agent_with_fallbacks(self, prompt_template: ChatPromptTemplate,
callbacks: List[BaseCallbackHandler]) -> Agent:
def create_agent_with_fallbacks(self, prompt_template: ChatPromptTemplate) -> Agent:
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove callbacks from the argument list of create_agent_with_fallbacks. This eliminates the need for
a callbacks parameter in both default_llm_factory and standard_langchain_llm_factory.
However, that cleanup is outside the scope of this PR and will be addressed separately.

@Noravee Noravee requested a review from d1donlydfink July 29, 2025 22:06
@Noravee Noravee marked this pull request as ready for review July 30, 2025 07:36
Copy link
Collaborator

@d1donlydfink d1donlydfink left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Please add nice explanation of need for 2 Journals you added in the PR to the code itself
  • Make agent_name a required argument of create_tool_from_toolbox(()

@Noravee Noravee merged commit 08ad6bc into main Jul 31, 2025
4 checks passed
@Noravee Noravee deleted the langchain_tool_callback branch July 31, 2025 04:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants