Skip to content

feat(tracing): Langfuse prompt/reply/cost + one-trace-per-turn (repairs #4399 build break)#4434

Merged
senamakel merged 3 commits into
tinyhumansai:mainfrom
M3gA-Mind:feat/from-upstream-main
Jul 3, 2026
Merged

feat(tracing): Langfuse prompt/reply/cost + one-trace-per-turn (repairs #4399 build break)#4434
senamakel merged 3 commits into
tinyhumansai:mainfrom
M3gA-Mind:feat/from-upstream-main

Conversation

@M3gA-Mind

@M3gA-Mind M3gA-Mind commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Exports the turn's prompt + reply to Langfuse as native input/output — opt-in via observability.agent_tracing.capture_content (default off, preserves the PII-free default).
  • Promotes token usage + cost into Langfuse's native usageDetails/costDetails (previously buried in span metadata, so cost always rendered 0).
  • Fixes an observation-id collision — per-turn sequential ids (0000…0001) reset each turn, and Langfuse dedupes observations by id globally, so later turns' observations silently bound to the first trace that claimed each id (new traces looked empty). Ids now carry a per-turn nonce.
  • One trace per turn, grouped by sessionId (thread) + userId (client).
  • push_spans now surfaces 207 per-event rejections instead of swallowing them.

Problem

Agent traces in Langfuse were unusable: prompt/reply were never sent, cost always showed 0 (token/cost rode in span metadata, not Langfuse's native fields), and the observation-id collision above made observations from any turn after the first vanish from their trace.

Solution

  • New AgentProgress::TurnContent carries the prompt/reply onto the turn span; the exporter emits them as Langfuse input/output only when observability.agent_tracing.capture_content is set (env toggle OPENHUMAN_AGENT_TRACING_CAPTURE_CONTENT), preserving the metadata-only default.
  • gen_ai.usage.* attributes are promoted onto a Langfuse generation with usageDetails / costDetails / model.
  • Span ids get a per-turn nonce prefix (global uniqueness); the trace id is unique per request with the thread as sessionId for grouping.
  • push_spans inspects the 207 body and warns on per-event rejections.

Submission Checklist

  • Tests added or updated — new progress_tracing::langfuse exporter test (usage→generation promotion + content on/off gate) plus collector tests (TurnContent, grouping, span-id uniqueness), run_log skip, and the env-toggle test. 29+ tests green.
  • Diff coverage ≥ 80% — targeted tests added for the changed logic; the CI diff-cover job is the authoritative gate and re-runs on this rebase.
  • N/A: Coverage matrix — observability-only change; no user-facing feature row.
  • N/A: Affected feature IDs — none.
  • No new external network dependencies introduced.
  • N/A: Manual smoke checklist — no release-cut surface change.
  • N/A: Linked issue — observability enhancement; no closing issue.

Impact

  • Desktop/CLI Rust core — agent tracing exporter only. No frontend change.
  • Security/privacy: prompt/reply export is opt-in, default-off; token/cost are non-PII and always on. Preserves the "never log secrets or full PII" default.

AI Authored PR Metadata

Validation Run

  • N/A: frontend format:check / typecheck — no frontend changes.
  • Focused tests: cargo test --lib progress_tracing → all green (incl. new exporter test).
  • Rust fmt/check: cargo fmt clean; GGML_NATIVE=OFF cargo check green.

Behavior Changes

  • Intended: agent traces carry native token/cost; prompt/reply when capture_content is enabled; one Langfuse trace per turn grouped by session/user.
  • User-visible effect: none by default (content off).

@M3gA-Mind M3gA-Mind requested a review from a team July 3, 2026 13:08
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 15 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 94db7bfb-6172-4b00-9425-a20b1c0280ed

📥 Commits

Reviewing files that changed from the base of the PR and between 634be88 and 18c1161.

📒 Files selected for processing (12)
  • src/bin/harness_subagent_audit.rs
  • src/openhuman/agent/harness/session/turn/core.rs
  • src/openhuman/agent/progress.rs
  • src/openhuman/agent/progress_tracing.rs
  • src/openhuman/agent/progress_tracing/langfuse.rs
  • src/openhuman/agent/progress_tracing/tests.rs
  • src/openhuman/channels/providers/web/progress_bridge.rs
  • src/openhuman/config/schema/load/env_overlay.rs
  • src/openhuman/config/schema/load_tests.rs
  • src/openhuman/config/schema/observability.rs
  • src/openhuman/threads/turn_state/mirror.rs
  • src/openhuman/workflows/run_log.rs
📝 Walkthrough

Walkthrough

Adds a new AgentProgress::TurnContent event carrying turn input/output, threads it through tracing and Langfuse export behind capture_content, updates downstream consumers to ignore it, switches orchestration checkpointing to SqliteCheckpointer, and wraps two builtin agent graph functions in Some(...).

Changes

TurnContent Capture and Tracing

Layer / File(s) Summary
TurnContent event definition and emission
src/openhuman/agent/progress.rs, src/openhuman/agent/harness/session/turn/core.rs
Defines AgentProgress::TurnContent and emits it after turn accounting before TurnCompleted.
Trace span content and grouping
src/openhuman/agent/progress_tracing.rs
Adds session grouping, span content fields, unique span IDs, root thread attributes, and TurnContent attachment in the span collector.
Langfuse export gating and usage promotion
src/openhuman/agent/progress_tracing/langfuse.rs
Extends Langfuse batching with content gating, trace grouping fields, usage/model promotion, generation-create selection, response-body handling, and exporter tests.
Capture-content config and env overlay
src/openhuman/config/schema/observability.rs, src/openhuman/config/schema/load/env_overlay.rs, src/openhuman/agent/progress_tracing/tests.rs, src/openhuman/config/schema/load_tests.rs
Adds AgentTracingConfig.capture_content, parses the env override, and updates config-focused tests to set and exercise the field explicitly.
Downstream TurnContent handling
src/openhuman/channels/providers/web/progress_bridge.rs, src/openhuman/threads/turn_state/mirror.rs, src/openhuman/workflows/run_log.rs
Updates the web progress bridge to stamp session grouping and ignore TurnContent, makes the turn-state mirror no-op on it, and skips it in run-log formatting.

Orchestration Checkpointer Swap

Layer / File(s) Summary
SqliteCheckpointer wiring
src/openhuman/orchestration/graph/mod.rs, src/openhuman/orchestration/graph/state.rs, src/openhuman/orchestration/ops.rs
Switches orchestration graph code and tests from SqlRunLedgerCheckpointer to SqliteCheckpointer and updates the related doc comment.

Builtin Agent Graph Wiring

Layer / File(s) Summary
Builtin graph_fn wrapping
src/openhuman/agent_registry/agents/loader.rs
Changes the builtin registry entries to store custom graph providers through Some(...).

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant run_turn_via_tinyagents_session
  participant SpanCollector
  participant LangfuseExporter
  participant DownstreamConsumers

  run_turn_via_tinyagents_session->>SpanCollector: AgentProgress::TurnContent { input, output }
  SpanCollector->>SpanCollector: attach input/output to root turn span
  run_turn_via_tinyagents_session->>DownstreamConsumers: AgentProgress::TurnContent
  DownstreamConsumers->>DownstreamConsumers: ignore / skip logging
  SpanCollector->>LangfuseExporter: push_spans(capture_content)
  alt capture_content = true
    LangfuseExporter->>LangfuseExporter: include input/output in batch
  else capture_content = false
    LangfuseExporter->>LangfuseExporter: withhold input/output
  end
Loading

Possibly related PRs

Suggested labels: rust-core, agent, bug

Poem

A rabbit hops through spans of light,
With turn-time words tucked out of sight.
Flip capture on, the fields appear,
Then hop to Langfuse, crisp and clear.
SQLite keeps the checkpoints snug,
While graph and trace both get a tug. 🐰

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main tracing and Langfuse changes, including prompt/reply/cost capture and the build-break fix.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added bug rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. labels Jul 3, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 26901cbba0

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

/// (via `AgentProgress::TurnContent`); the exporter still gates transmission
/// behind `observability.agent_tracing.capture_content`.
#[serde(skip_serializing_if = "Option::is_none")]
pub input: Option<serde_json::Value>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate local trace content on capture_content

When local tracing is enabled but observability.agent_tracing.capture_content remains at its default false, SpanCollector still fills this field from TurnContent and export_spans serializes TraceSpan directly via spans_to_ndjson, so prompts are written to the configured NDJSON file or app log despite the opt-in being off. The Langfuse push path strips content, but the local exporter needs the same gate before serializing input/output.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/openhuman/agent/progress_tracing.rs (1)

590-646: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicate "lazily resolve turn span index" boilerplate.

TurnCostUpdated and TurnContent both repeat the same match self.turn_span_index { Some(idx) => idx, None => { self.ensure_turn_span(...); self.turn_span_index.expect(...) } } pattern. Consider extracting a small helper (e.g. fn turn_span_index(&mut self, now_unix_ms: u64) -> usize) to avoid the two copies drifting.

♻️ Suggested helper
+    fn turn_span_index_mut(&mut self, now_unix_ms: u64) -> usize {
+        match self.turn_span_index {
+            Some(idx) => idx,
+            None => {
+                self.ensure_turn_span(now_unix_ms);
+                self.turn_span_index.expect("turn span just created")
+            }
+        }
+    }

Then both TurnCostUpdated and TurnContent arms call let index = self.turn_span_index_mut(now_unix_ms);.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/openhuman/agent/progress_tracing.rs` around lines 590 - 646, The
`TurnCostUpdated` and `TurnContent` branches in `progress_tracing.rs` duplicate
the same lazy turn-span lookup logic. Extract that `match self.turn_span_index {
... }` pattern into a small helper on the same type, such as a
`turn_span_index(now_unix_ms)` method that ensures the span exists and returns
the index, then update both `AgentProgress::TurnCostUpdated` and
`AgentProgress::TurnContent` to call the helper so the behavior stays in one
place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/openhuman/agent/progress_tracing.rs`:
- Around line 590-646: The `TurnCostUpdated` and `TurnContent` branches in
`progress_tracing.rs` duplicate the same lazy turn-span lookup logic. Extract
that `match self.turn_span_index { ... }` pattern into a small helper on the
same type, such as a `turn_span_index(now_unix_ms)` method that ensures the span
exists and returns the index, then update both `AgentProgress::TurnCostUpdated`
and `AgentProgress::TurnContent` to call the helper so the behavior stays in one
place.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6806e69f-d052-47eb-b0a0-ffcceaa0f10a

📥 Commits

Reviewing files that changed from the base of the PR and between 4e75a63 and 26901cb.

📒 Files selected for processing (14)
  • src/openhuman/agent/harness/session/turn/core.rs
  • src/openhuman/agent/progress.rs
  • src/openhuman/agent/progress_tracing.rs
  • src/openhuman/agent/progress_tracing/langfuse.rs
  • src/openhuman/agent/progress_tracing/tests.rs
  • src/openhuman/agent_registry/agents/loader.rs
  • src/openhuman/channels/providers/web/progress_bridge.rs
  • src/openhuman/config/schema/load/env_overlay.rs
  • src/openhuman/config/schema/observability.rs
  • src/openhuman/orchestration/graph/mod.rs
  • src/openhuman/orchestration/graph/state.rs
  • src/openhuman/orchestration/ops.rs
  • src/openhuman/threads/turn_state/mirror.rs
  • src/openhuman/workflows/run_log.rs

@coderabbitai coderabbitai Bot added the agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. label Jul 3, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 3, 2026
@M3gA-Mind

Copy link
Copy Markdown
Collaborator Author

Split out the build-fix: the fix(orchestration): repair TinyAgents-migration build break commit is now its own PR — #4442 — so it can merge quickly and unblock CI. This PR is rebased to feature-only (the tracing changes + tests) and is effectively stacked on #4442. Until #4442 lands in main, this branch won't compile on its own (the #4399 break lives in main), so CI here stays red until then — merge #4442 first, then this rebases clean and green.

M3gA-Mind added 2 commits July 3, 2026 20:45
Adds opt-in content capture and promotes token/cost onto agent trace
spans, plus two correctness fixes to the Langfuse exporter path.

- Content (opt-in): the turn's prompt and the model's reply ride a new
  AgentProgress::TurnContent event onto the turn span; the exporter emits
  them as Langfuse `input`/`output` only when
  observability.agent_tracing.capture_content is set (default off; env
  toggle OPENHUMAN_AGENT_TRACING_CAPTURE_CONTENT). Preserves the
  metadata-only, PII-free default.
- Native usage/cost: the turn's gen_ai.usage.* attributes are promoted
  into Langfuse's native generation / usageDetails / costDetails (they were
  buried in metadata, so cost always rendered 0).
- Fix (span-id collision): span ids were a per-turn sequence (0000...0001)
  reset each turn; Langfuse dedupes observations by id globally, so later
  turns' observations silently bound to whichever trace first claimed each
  id (turns appeared empty). Ids now carry a per-turn nonce prefix.
- One trace per turn: the trace id is unique per request; the thread id
  rides along as Langfuse `sessionId` (and client as `userId`) so a
  conversation's per-turn traces still group under one session.
- Robustness: push_spans now inspects the 207 response body and warns on
  per-event rejections instead of swallowing them.

Tests: 29 progress_tracing tests pass, including a new exporter test
asserting usage->generation promotion and the content gate (on/off).
…ntent toggle

Adds targeted unit tests for the new tracing behavior to lift diff coverage
on the changed lines:

- collector: TurnContent attaches input/output to the turn span;
  session_group is stamped as thread.id and client as user.id; span ids are
  globally unique across turns (regression guard for the Langfuse dedup
  collision).
- exporter: trace-create promotes user.id/thread.id to Langfuse
  userId/sessionId.
- run_log: TurnContent produces no log line (content stays out of the run log).
- config: OPENHUMAN_AGENT_TRACING_CAPTURE_CONTENT env toggle flips the flag
  on/off.

All six pass (cargo test --lib).
@M3gA-Mind M3gA-Mind force-pushed the feat/from-upstream-main branch from 19f9098 to ab66c7f Compare July 3, 2026 15:15
…dit match

The tracing feature adds the AgentProgress::TurnContent variant; the
harness-subagent-audit bin exhaustively matches AgentProgress and didn't cover
it, so `cargo check --all-targets` (CI's Rust Quality clippy) failed E0004 even
though `cargo check --lib` passed (it doesn't build bins). Fold TurnContent into
the ignored-variants arm — an audit tool has no use for prompt/reply content.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. bug rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure.

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

2 participants