feat: GENERATE_CONVERSATIONAL_OUTPUT node for reliable structured-output extraction#965
Open
maxduu wants to merge 2 commits into
Open
feat: GENERATE_CONVERSATIONAL_OUTPUT node for reliable structured-output extraction#965maxduu wants to merge 2 commits into
GENERATE_CONVERSATIONAL_OUTPUT node for reliable structured-output extraction#965maxduu wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds an optional (flagged) intermediate LangGraph node for conversational agents to reliably extract structured custom output fields via a forced set_conversational_output tool call, keeping the main AGENT LLM focused on the conversational reply while ensuring schema compliance at termination.
Changes:
- Introduces
GENERATE_CONVERSATIONAL_OUTPUTnode + wiring/routing to run between AGENT and TERMINATE when conversational + flag enabled + custom output fields exist. - Adds utilities/factories (
config_without_streaming,has_custom_conversational_output_fields,build_conversational_output_args_schema,create_set_conversational_output_tool) and updates termination to merge extracted custom fields. - Adds/updates tests covering the new node, routing behavior, termination behavior, and helper utilities.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/agent/tools/test_utils.py | Adds unit tests for the new shared config_without_streaming helper. |
| tests/agent/tools/internal_tools/test_analyze_files_tool.py | Removes the old private _config_without_streaming tests after refactor to shared helper. |
| tests/agent/react/test_utils.py | Adds tests for has_custom_conversational_output_fields and build_conversational_output_args_schema. |
| tests/agent/react/test_terminate_node.py | Adds termination tests for extracting/validating conversational custom output fields. |
| tests/agent/react/test_router_conversational.py | Adds routing tests for the new conversational-output node branch. |
| tests/agent/react/test_flow_control_tools.py | Adds tests for the new create_set_conversational_output_tool factory. |
| tests/agent/react/test_create_agent.py | Adds topology tests ensuring the node is conditionally inserted by config/schema. |
| tests/agent/react/test_conversational_output_node.py | Adds tests for the new node (tool binding, TAG_NOSTREAM config, streaming disabled, instruction handling). |
| src/uipath_langchain/agent/tools/utils.py | Adds shared config_without_streaming helper used by multiple internal LLM calls. |
| src/uipath_langchain/agent/tools/internal_tools/analyze_files_tool.py | Refactors to use config_without_streaming and keeps internal LLM call non-streamed. |
| src/uipath_langchain/agent/react/utils.py | Adds helpers to detect custom conversational output fields and generate tool args schema. |
| src/uipath_langchain/agent/react/types.py | Adds new graph node enum value, new config flag, and extends flow-control tool list. |
| src/uipath_langchain/agent/react/tools/tools.py | Adds create_set_conversational_output_tool factory. |
| src/uipath_langchain/agent/react/terminate_node.py | Extracts/merges set_conversational_output args into conversational termination output and improves validation errors. |
| src/uipath_langchain/agent/react/router_conversational.py | Adds optional routing to GENERATE_CONVERSATIONAL_OUTPUT when enabled. |
| src/uipath_langchain/agent/react/conversational_output_node.py | Implements the new focused LLM extraction node (non-streamed, TAG_NOSTREAM, forced tool call). |
| src/uipath_langchain/agent/react/constants.py | Adds constant for the response-messages field name. |
| src/uipath_langchain/agent/react/agent.py | Conditionally inserts the new node and passes routing flag based on config + schema analysis. |
Comment on lines
+98
to
+100
| custom_output_fields: dict[str, Any] = ( | ||
| dict(set_output_call["args"]) if set_output_call is not None else {} | ||
| ) |
Comment on lines
+83
to
+86
| detail=( | ||
| "The language model returned an unexpected response type." | ||
| "If you are using a BYOM configuration, verify your model deployment.", | ||
| ), |
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Summary
Adds a dedicated graph node that runs after AGENT for conversational agents
with a custom outputSchema, producing the structured output via a focused,
forced tool call to `set_conversational_output`. Decouples conversational
quality from schema compliance — the main AGENT LLM stays focused on the
reply, and a second LLM call (tagged `TAG_NOSTREAM`, non-streamed) reliably
fills in the routing/handoff fields.
Motivation
Previously, conversational agents with custom output schemas would fail:
Changes
Graph structure (agent/react/)
`set_conversational_output` bound (`tool_choice="any"`, `disable_streaming=True`,
`TAG_NOSTREAM` tag). Reuses `state.messages` for full agent context and
appends the framework instruction as a HumanMessage that never persists to state.
when `config.is_conversational and config.conversational_outputs_enabled and has_custom_conversational_output_fields(output_schema)`.
(or straight to TERMINATE if the flag is off).
Config
top-level feature flag. Defaults to False; existing callers unchanged.
Terminate node
from `state.messages[-1]`. If the tool call is absent, custom fields stay
empty and Pydantic surfaces a clear per-field error at schema validation.
into the response payload.
Utilities
`build_conversational_output_args_schema` helpers in `utils.py` (strips
`uipath__agent_response_messages` from the LLM-fillable args schema).
refactored out of `analyze_files_tool.py` since it's now used in two places.
Tests: full topology + router + terminate + utils + node coverage added.
Related PRs
Part of a coordinated four-repo change. Each PR is independently reviewable, but they land together:
conversationalService.enableOutputsFPS flag onUiPathRuntimeContextuipath-runtime-python#142 — surfaces theconversationalService.enableOutputsFPS flag onUiPathRuntimeContext.set_conversational_outputtool + generate-output prompt for conversational agents uipath-python#1785 — adds theset_conversational_outputflow-control tool + generate-output prompt primitives.GENERATE_CONVERSATIONAL_OUTPUTnode for reliable structured-output extraction #965 — implements theGENERATE_CONVERSATIONAL_OUTPUTgraph node, router path, and terminate extraction (the main change).build_agent_graph.Test plan