Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions trpc_agent_sdk/dsl/graph/_node_action/_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,17 @@ async def execute(self, state: State) -> dict[str, Any]:
if isinstance(candidate, str) and candidate:
last_response = candidate

if (not self._is_graph_event(event)) and (
not event.partial) and event.content and event.content.parts:
text_parts = [part.text for part in event.content.parts if part.text]
if text_parts:
last_response = text_parts[-1]
if (not self._is_graph_event(event)) and event.is_final_response():
# Only clean final answers may become last_response. Skip
# thought parts (part.thought) so a thinking model's
# reasoning monologue is never surfaced as the answer.
# is_final_response() already excludes partial, function
# call/response, and code-execution events, so plan and
# transfer_to_agent rounds are naturally ignored.
if event.content and event.content.parts:
visible_text = [part.text for part in event.content.parts if part.text and not part.thought]
if visible_text:
last_response = "".join(visible_text)

if not event.visible:
if event.actions and event.actions.transfer_to_agent:
Expand Down Expand Up @@ -246,7 +252,7 @@ async def execute(self, state: State) -> dict[str, Any]:

node_response: Any = last_response
structured_output: Any = None
if isinstance(self.agent, LlmAgent) and self.agent.output_schema is not None:
if isinstance(self.agent, LlmAgent) and self.agent.output_schema is not None and last_response:
node_response = json.loads(last_response)
structured_output = node_response

Expand Down
4 changes: 3 additions & 1 deletion trpc_agent_sdk/dsl/graph/_node_action/_llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,9 @@ async def _run_model_round(
continue

response_parts = list(llm_response.content.parts)
text_parts = [part.text for part in response_parts if part.text]
# Exclude thought parts so a thinking model's reasoning does not
# leak into response_text / STATE_KEY_LAST_RESPONSE.
text_parts = [part.text for part in response_parts if part.text and not part.thought]
response_text = "".join(text_parts)

logger.debug(f"[{self.name}] LLM response received ({len(response_text)} chars)")
Expand Down
Loading