Skip to content

fix: set_conversational_output leaking CAS event, phantom CAS events for OpenAI envelope chunk#979

Open
maxduu wants to merge 6 commits into
mainfrom
fix-cas-events
Open

fix: set_conversational_output leaking CAS event, phantom CAS events for OpenAI envelope chunk#979
maxduu wants to merge 6 commits into
mainfrom
fix-cas-events

Conversation

@maxduu

@maxduu maxduu commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Two independent fixes that stop the runtime from emitting phantom messageStart / toolCall events to CAS for internal machinery that shouldn't be user-visible.

1. OpenAI Responses API envelope chunk

Issue: OpenAI's Responses API (e.g. gpt-5.4) fronts a stream with an empty envelope chunk (id=resp_..., from the SSE response.created event), then streams the actual content under a different id (lc_run--...). The mapper treated the envelope as a new message and emitted a phantom messageStart + contentPartStart pair to CAS.

Fix: src/uipath_langchain/runtime/messages.py — shape-guard that skips streaming message-start/content-part-start events for chunks with no content, no tool calls, and chunk_position != "last".

Before:
image

After:
(With temporary prints). Note the "Skipping phantom message start..." print.

image

2. set_conversational_output tool call leaking to CAS

cc @andrewwan-uipath

Why: GENERATE_CONVERSATIONAL_OUTPUT runs an internal LLM call with tool_choice="any" bound only to the set_conversational_output tool. TAG_NOSTREAM on the invoke config correctly suppressed the LLM callback path, but LangGraph's messages-stream mode also streams message-outputs from the return of {"messages": [response]}. So the chat-mapper emitted toolCallStart + executingToolCall events to CAS for the synthetic tool call.

Fix: Extract the tool call args inside the node and route them via inner_state.conversational_output instead of messages. The AIMessage never enters the messages channel, so there's nothing for LangGraph's sweep to pick up. This refactors and simplifies a lot of the terminate_node logic since it simply just obtains the output from the inner_state of the graph rather than parsing the last message.

Before:
Note the temporary log Mapping AIMessage to events: resp_...
image

image

After:
image

Test plan

  • Added test_returns_extracted_args_via_inner_state and test_raises_when_llm_omits_set_conversational_output_call in test_conversational_output_node.py
  • Updated test_conversational_extracts_custom_output_fields to pass args via inner_state
  • Added envelope-chunk coverage in test_chat_message_mapper.py
  • All 2388 tests pass, lint clean

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings July 10, 2026 02:53

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Prevents the runtime from emitting phantom CAS events for (1) OpenAI Responses API “envelope-only” chunks and (2) the internal set_conversational_output tool call used for conversational structured output extraction.

Changes:

  • Add a shape-guard to skip envelope-only AIMessageChunks (no content / no tool calls / not chunk_position="last") to avoid phantom messageStart events.
  • Route set_conversational_output tool-call args via inner_state.conversational_output (instead of the messages channel) so LangGraph’s messages-stream sweep can’t leak synthetic tool calls to CAS.
  • Update/extend tests to cover envelope-chunk skipping and the new conversational output plumbing + error path.

Reviewed changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
uv.lock Bumps package version to 0.14.4.
pyproject.toml Bumps project version to 0.14.4.
src/uipath_langchain/runtime/messages.py Skips envelope-only chunks to prevent phantom CAS message start/content-part start events.
tests/runtime/test_chat_message_mapper.py Updates existing mapper tests and adds coverage for envelope-only chunk skipping + empty-last-chunk behavior.
src/uipath_langchain/agent/react/types.py Adds InnerAgentGraphState.conversational_output to carry extracted structured output args.
src/uipath_langchain/agent/react/conversational_output_node.py Extracts set_conversational_output args from the internal LLM response and stores them in inner_state; raises LLM_INVALID_RESPONSE if the tool call is missing.
src/uipath_langchain/agent/react/terminate_node.py Reads structured custom output fields from inner_state.conversational_output and removes graph-shape-dependent logic.
src/uipath_langchain/agent/react/agent.py Simplifies terminate node creation (no longer passes with_conversational_output_node).
tests/agent/react/test_conversational_output_node.py Updates expectations to assert inner_state updates (no messages) and adds test for missing tool call -> structured error.
tests/agent/react/test_terminate_node.py Updates termination behavior tests to supply conversational output via inner_state.
tests/agent/react/test_create_agent.py Updates mocks/assertions for the new create_terminate_node signature usage.

async def test_returns_response_with_tool_call(self):
async def test_returns_extracted_args_via_inner_state(self):
"""The extracted set_conversational_output args land on
inner_state.conversational_output_args, not on `messages` — that

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed outdated comment.

@maxduu maxduu changed the title fix: prevent phantom CAS events for OpenAI envelope chunk and set_conversational_output fix: set_conversational_output leaking CAS event, phantom CAS events for OpenAI envelope chunk Jul 10, 2026
@sonarqubecloud

Copy link
Copy Markdown

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.

3 participants