Skip to content

fix(agents): keep Langfuse completion content as assistant text - #2215

Open
Ordinary (OrdinarySF) wants to merge 1 commit into
JetBrains:developfrom
OrdinarySF:fix/langfuse-completion-prefers-text-over-reasoning
Open

Ordinary (OrdinarySF) wants to merge 1 commit into
JetBrains:developfrom
OrdinarySF:fix/langfuse-completion-prefers-text-over-reasoning

Conversation

@OrdinarySF

@OrdinarySF Ordinary (OrdinarySF) commented Aug 16, 2026 •

Copy link
Copy Markdown
Contributor

What

LangfuseSpanAdapter no longer writes MessagePart.Reasoning into gen_ai.completion.{i}.content (or the matching prompt attribute) when a visible assistant reply exists.

  • gen_ai.completion.{i}.content / gen_ai.prompt.{i}.content stay tool calls or MessagePart.Text
  • reasoning is emitted on gen_ai.completion.{i}.reasoning / gen_ai.prompt.{i}.reasoning
  • finish_reason is kept on text completions even when reasoning parts are present

gen_ai.output.messages is unchanged and still contains both part types.

Why

Langfuse Preview maps gen_ai.completion* to observation output. The adapter preferred reasoning over text:

when {
    toolCalls.isNotEmpty() -> /* tool JSON */
    reasoningParts.isNotEmpty() -> /* thinking, as content */
    else -> /* actual text */
}

Reasoning models (Grok, o-series, DeepSeek, …) return thinking as MessagePart.Reasoning and the short final answer as MessagePart.Text. Preview then showed the thinking (often an English paraphrase of a non-English prompt) while gen_ai.usage.output_tokens still counted only the visible completion — for example 3 tokens for RELEVANT next to a long translation.

The agent itself is fine: Message.textContent() already reads only MessagePart.Text. This is an export mapping bug, not a model or Langfuse UI bug.

How it was verified

  • Regression tests in LangfuseSpanAdapterTest:
    • testCompletionAttributesPreferTextOverReasoning
    • testCompletionAttributesReasoningOnlyLeavesContentEmpty
    • testPromptAttributesPreferTextOverReasoning
  • ./gradlew :agents:agents-features:agents-features-opentelemetry:jvmTest passes locally.

Notes

  • Scope is the Langfuse adapter only.
  • WeaveSpanAdapter still prefers reasoning over text in the same when. Happy to follow up if you want the same split there.

closes #2216

LangfuseSpanAdapter preferred MessagePart.Reasoning over Text when
writing gen_ai.completion.{i}.content. Langfuse Preview maps that
attribute to observation output, so reasoning traces (for example an
English paraphrase of a Chinese prompt) replaced the real reply such
as RELEVANT. Token usage still counted only the visible completion.

Write text or tool calls to content, emit reasoning on
gen_ai.completion.{i}.reasoning, and keep finish_reason when thinking
is present. Apply the same split to prompt history attributes.

Rebased onto develop after JetBrains#2248. Empty reasoning parts stay filtered
out. Non-empty reasoning is written to gen_ai.*.reasoning, not .content.
@OrdinarySF
Ordinary (OrdinarySF) force-pushed the fix/langfuse-completion-prefers-text-over-reasoning branch from 8062fb7 to 08cdd64 Compare September 26, 2026 02:26
@OrdinarySF

Copy link
Copy Markdown
Contributor Author

Rebased onto develop after #2248.

#2248 filters out a reasoning part whose content is empty (Gemini signature-only parts). That filter stays, on both prompt and completion attributes.

This PR covers the remaining case: a reasoning part that has text. Langfuse Preview reads .content, so the visible reply stays on gen_ai.completion.{i}.content / gen_ai.prompt.{i}.content. The thinking moves to gen_ai.*.reasoning. finish_reason still comes from the text (or tool-call) branch.

LangfuseSpanAdapterTest keeps the #2248 empty-part test and adds the non-empty cases. Local :agents:agents-features:agents-features-opentelemetry:jvmTest --tests ai.koog.agents.features.opentelemetry.integration.langfuse.LangfuseSpanAdapterTest passed.

Oleksandr Shylenko (@shilenkoalexander) #2248 landed in this same when. This branch is the follow-up for reasoning parts that actually contain text.

This branch has not been deployed

No deployments
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.

Langfuse Preview shows reasoning as completion content

1 participant