From 0c06ce1cc1e2c452f66802476e47db392777cfc0 Mon Sep 17 00:00:00 2001 From: minchangwei Date: Thu, 23 Jul 2026 14:54:52 +0800 Subject: [PATCH] =?UTF-8?q?Feature:=20=E6=94=AF=E6=8C=81trace=E4=B8=8A?= =?UTF-8?q?=E6=8A=A5=E6=80=9D=E8=80=83=E6=96=87=E6=9C=AC=E6=94=BE=E5=9D=97=E9=87=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 方便区分思考文本和正常文本,同时保持兼容性 --- trpc_agent_sdk/telemetry/_trace.py | 41 +++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/trpc_agent_sdk/telemetry/_trace.py b/trpc_agent_sdk/telemetry/_trace.py index 22ff2ad2..9c725906 100644 --- a/trpc_agent_sdk/telemetry/_trace.py +++ b/trpc_agent_sdk/telemetry/_trace.py @@ -62,6 +62,30 @@ def get_trpc_agent_span_name() -> str: return _trpc_agent_span_name +def _join_parts_with_thought_tag(parts) -> str: + """Join part texts, wrapping thought parts in tags. + + Parts with ``thought=True`` (model reasoning/thinking) are wrapped in + ``...`` tags so downstream consumers (e.g. + zhiyan-llm exporter) can distinguish and optionally strip them. + + Args: + parts: A sequence of Part objects with ``text`` and optional + ``thought`` attributes. + + Returns: + The joined text string with thought parts tagged. + """ + result = [] + for part in parts: + text = part.text or "" + if getattr(part, "thought", False): + result.append(f"{text}") + else: + result.append(text) + return "\n".join(result) + + def _safe_json_serialize(obj) -> str: """Convert any Python object to a JSON-serializable type or string. @@ -116,11 +140,11 @@ def trace_runner( span.set_attribute(f"{_trpc_agent_span_name}.runner.session_id", session_id) input_str = "" if new_message and new_message.parts: - input_str = "\n".join([part.text or "" for part in new_message.parts]) + input_str = _join_parts_with_thought_tag(new_message.parts) span.set_attribute(f"{_trpc_agent_span_name}.runner.input", input_str) output_str = "" if last_event and last_event.content and last_event.content.parts: - output_str = "\n".join([part.text or "" for part in last_event.content.parts]) + output_str = _join_parts_with_thought_tag(last_event.content.parts) span.set_attribute(f"{_trpc_agent_span_name}.runner.output", output_str) # Set state attributes for begin and end @@ -181,7 +205,7 @@ def trace_cancellation( # Input (prefixed with [CANCELLED] marker) input_str = "" if new_message and new_message.parts: - input_str += "\n".join([part.text or "" for part in new_message.parts]) + input_str += _join_parts_with_thought_tag(new_message.parts) span.set_attribute(f"{_trpc_agent_span_name}.runner.input", input_str) # Output (prefixed with [CANCELLED] marker, includes partial text or last event) @@ -189,7 +213,7 @@ def trace_cancellation( if partial_text: output_str += partial_text elif last_event and last_event.content and last_event.content.parts: - output_str += "\n".join([part.text or "" for part in last_event.content.parts]) + output_str += _join_parts_with_thought_tag(last_event.content.parts) span.set_attribute(f"{_trpc_agent_span_name}.runner.output", output_str) # Cancellation-specific attributes @@ -245,11 +269,14 @@ def trace_agent( for content in override_messages: if content and content.parts: for part in content.parts: - if part.text and not part.thought: - text_parts.append(part.text) + if part.text: + if getattr(part, "thought", False): + text_parts.append(f"{part.text}") + else: + text_parts.append(part.text) input_str = "\n".join(text_parts) elif invocation_context.user_content and invocation_context.user_content.parts: - input_str = "\n".join([part.text or "" for part in invocation_context.user_content.parts]) + input_str = _join_parts_with_thought_tag(invocation_context.user_content.parts) span.set_attribute(f"{_trpc_agent_span_name}.agent.input", input_str) span.set_attribute(f"{_trpc_agent_span_name}.agent.output", agent_action)