diff --git a/dashscope/finetune/reinforcement/component/observability/genai/_core.py b/dashscope/finetune/reinforcement/component/observability/genai/_core.py index 3c0fe794..08091c6c 100644 --- a/dashscope/finetune/reinforcement/component/observability/genai/_core.py +++ b/dashscope/finetune/reinforcement/component/observability/genai/_core.py @@ -59,12 +59,19 @@ ToolCall, ) + try: # Reasoning part exists in loongsuite-util-genai >= 0.4.0. + from opentelemetry.util.genai import types as _genai_types + + Reasoning = getattr(_genai_types, "Reasoning", None) + except ImportError: # pragma: no cover - older genai util + Reasoning = None # type: ignore[assignment,misc] + GENAI_AVAILABLE = True except ImportError: # pragma: no cover get_extended_telemetry_handler = None # type: ignore[assignment] ExecuteToolInvocation = None # type: ignore[assignment] # type: ignore[assignment] - InputMessage = OutputMessage = Text = ToolCall = None + InputMessage = OutputMessage = Reasoning = Text = ToolCall = None GENAI_AVAILABLE = False _logger = logging.getLogger(__name__) diff --git a/dashscope/finetune/reinforcement/component/observability/genai/messages.py b/dashscope/finetune/reinforcement/component/observability/genai/messages.py index 44abe0f5..ab09826a 100644 --- a/dashscope/finetune/reinforcement/component/observability/genai/messages.py +++ b/dashscope/finetune/reinforcement/component/observability/genai/messages.py @@ -454,6 +454,23 @@ def openai_chat_messages_to_input_messages( return out +def _reasoning_part(msg: Any) -> Any: + """Return a ``Reasoning`` part when the message carries thinking content. + + ``None`` when the GenAI util is unavailable or the field is absent, so + callers keep their existing part ordering logic. + """ + if _core.Reasoning is None or msg is None: + return None + if isinstance(msg, Dict): + reasoning = msg.get("reasoning_content") + else: + reasoning = getattr(msg, "reasoning_content", None) + if not isinstance(reasoning, str) or not reasoning: + return None + return _core.Reasoning(content=reasoning) + + def openai_completion_to_output_messages( # pylint: disable=too-many-branches completion: Any, ) -> List[Any]: @@ -513,6 +530,9 @@ def openai_completion_to_output_messages( # pylint: disable=too-many-branches tool_calls = getattr(msg, "tool_calls", None) if tool_calls: parts.extend(tool_calls_to_parts(tool_calls)) + reasoning_part = _reasoning_part(msg) + if reasoning_part is not None: + parts.insert(0, reasoning_part) if not parts: parts.append(_core.Text(content="")) result.append( @@ -561,6 +581,9 @@ def dashscope_response_to_output_messages(response: Any) -> List[Any]: ) if tool_calls: parts.extend(tool_calls_to_parts(tool_calls)) + reasoning_part = _reasoning_part(msg) + if reasoning_part is not None: + parts.insert(0, reasoning_part) if not parts: parts.append(_core.Text(content="")) result.append( diff --git a/dashscope/finetune/reinforcement/component/observability/genai/tools.py b/dashscope/finetune/reinforcement/component/observability/genai/tools.py index c0d48b85..bdf20b9a 100644 --- a/dashscope/finetune/reinforcement/component/observability/genai/tools.py +++ b/dashscope/finetune/reinforcement/component/observability/genai/tools.py @@ -535,7 +535,7 @@ def _bind_tool_arguments( bound = sig.bind_partial(*args, **kwargs) bound.apply_defaults() param_names = list(sig.parameters.keys()) - raw = Dict(bound.arguments) + raw = dict(bound.arguments) if param_names and param_names[0] in ("self", "cls"): raw = {k: v for k, v in raw.items() if k not in ("self", "cls")} return _json_serializable(raw)