hermes - 💡(How to fix) Fix [Bug]: Langfuse traces collapse into single trace when task_id == session_id (ACP mode) [1 pull requests]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Root Cause

The trace ID is deterministically generated from a seed that never changes in ACP mode.

Fix Action

Fixed

Code Example

# acp_adapter/server.py:1456-1459
result = agent.run_conversation(
    user_message=user_content,
    conversation_history=state.history,
    task_id=session_id,    # ← never changes across turns
)

---

# plugins/observability/langfuse/__init__.py:222-224
def _trace_key(task_id, session_id):
    if task_id:
        return task_id    # → session_id (unchanging)

---

# plugins/observability/langfuse/__init__.py:544
trace_id = client.create_trace_id(
    seed=f"{session_id or 'sessionless'}::{task_id or task_key}"
)
# When task_id == session_id, seed is always "{session_id}::{session_id}"
# → deterministic, always the same trace_id

---

# plugins/observability/langfuse/__init__.py:711-725
state = _TRACE_STATE.get(task_key)   # hits cache after first turn
if state is None:
    state = _start_root_trace(...)   # only called once
    _TRACE_STATE[task_key] = state

---

# _start_root_trace, line 544
seed = f"{session_id or 'sessionless'}::{task_id or task_key}"
if task_id and task_id == session_id:
    # Platform didn't provide a unique task_id — add monotonic timestamp
    seed = f"{seed}::{time.monotonic_ns()}"
trace_id = client.create_trace_id(seed=seed)

---

# Track turns per session_key in a module-level dict
_SESSION_TURN = {}
session_key = session_id or task_key
turn = _SESSION_TURN.get(session_key, 0) + 1
_SESSION_TURN[session_key] = turn
seed = f"{session_id or 'sessionless'}::{task_id or task_key}::turn:{turn}"
RAW_BUFFERClick to expand / collapse

Problem

When using Hermes via ACP (e.g., IntelliJ IDEA integration), all conversation turns within a session produce only 1 trace in Langfuse, regardless of how many rounds you chat. Each turn should produce a separate trace, like the CLI/TUI modes do.

Root Cause

The trace ID is deterministically generated from a seed that never changes in ACP mode.

Call chain (source-level)

1. ACP Server passes task_id=session_id — always the same value:

# acp_adapter/server.py:1456-1459
result = agent.run_conversation(
    user_message=user_content,
    conversation_history=state.history,
    task_id=session_id,    # ← never changes across turns
)

2. _trace_key() returns the same key every time:

# plugins/observability/langfuse/__init__.py:222-224
def _trace_key(task_id, session_id):
    if task_id:
        return task_id    # → session_id (unchanging)

3. _start_root_trace() generates identical trace_id:

# plugins/observability/langfuse/__init__.py:544
trace_id = client.create_trace_id(
    seed=f"{session_id or 'sessionless'}::{task_id or task_key}"
)
# When task_id == session_id, seed is always "{session_id}::{session_id}"
# → deterministic, always the same trace_id

4. _TRACE_STATE cache ensures reuse:

# plugins/observability/langfuse/__init__.py:711-725
state = _TRACE_STATE.get(task_key)   # hits cache after first turn
if state is None:
    state = _start_root_trace(...)   # only called once
    _TRACE_STATE[task_key] = state

Even after _finish_trace() pops the cache and the next turn calls _start_root_trace() again, the seed is identical → same trace_id → Langfuse merges all observations into one trace.

Why CLI/TUI works fine

In CLI/TUI, each run_conversation() receives a unique task_id (UUID), so the seed changes every turn → distinct traces.

Steps to Reproduce

  1. Configure Langfuse observability plugin
  2. Connect to Hermes via ACP (e.g. IDEA with hermes acp)
  3. Send 3+ messages in one session
  4. Check Langfuse traces → only 1 trace appears for the entire session

Expected Behavior

Each turn/prompt should produce a separate Langfuse trace, matching the behavior of CLI and TUI modes.

Proposed Fix (in the Langfuse plugin, not ACP server)

The fix should be in plugins/observability/langfuse/__init__.py so it benefits all platforms, not just ACP. Two approaches:

Option A — Add uniqueness suffix to seed when task_id == session_id:

# _start_root_trace, line 544
seed = f"{session_id or 'sessionless'}::{task_id or task_key}"
if task_id and task_id == session_id:
    # Platform didn't provide a unique task_id — add monotonic timestamp
    seed = f"{seed}::{time.monotonic_ns()}"
trace_id = client.create_trace_id(seed=seed)

Option B — Maintain per-session turn counter in _TRACE_STATE:

# Track turns per session_key in a module-level dict
_SESSION_TURN = {}
session_key = session_id or task_key
turn = _SESSION_TURN.get(session_key, 0) + 1
_SESSION_TURN[session_key] = turn
seed = f"{session_id or 'sessionless'}::{task_id or task_key}::turn:{turn}"

Option A is simpler and doesn't require state management. Option B produces deterministic, reproducible trace IDs (useful for debugging).

Environment

  • Hermes Agent (latest)
  • ACP connection via IntelliJ IDEA
  • Langfuse observability plugin enabled
  • Same behavior expected on all ACP clients (VS Code, Zed, etc.)

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix [Bug]: Langfuse traces collapse into single trace when task_id == session_id (ACP mode) [1 pull requests]