Skip to content

feat(observability): full-content Langfuse tracing — generations, tools, subagents, provider-qualified models, real costs#4506

Merged
senamakel merged 7 commits into
tinyhumansai:mainfrom
senamakel:feat/langfuse-full-content-tracing
Jul 4, 2026
Merged

feat(observability): full-content Langfuse tracing — generations, tools, subagents, provider-qualified models, real costs#4506
senamakel merged 7 commits into
tinyhumansai:mainfrom
senamakel:feat/langfuse-full-content-tracing

Conversation

@senamakel

@senamakel senamakel commented Jul 4, 2026

Copy link
Copy Markdown
Member

Summary

  • Langfuse agent traces now carry full content: every llm.* generation records the request messages (system prompt included) and the completion; tool spans record arguments, result text, and real elapsed time; subagent spans record the delegated prompt, final output, and the model that served them.
  • Subagent (incl. Context Scout) model calls now emit per-call generation telemetry nested under the subagent span — previously suppressed entirely.
  • Langfuse model labels are provider-qualified: managed.chat-v1, managed.burst-v1, openai.gpt-4o, … via a new Provider::telemetry_provider_id().
  • Per-call cost is no longer $0 for managed tiers: the bridge estimates via the tier-aware agent::cost table (charged>estimate precedence from the provider-usage carry is preserved and the generation telemetry reuses the exact resolved figures).
  • Headless/autonomous traces attribute to the real user (on-disk session profile email/id) instead of the transport client id when the auth_get_me cache is cold.

Problem

Live staging traces (project Staging on fuse.tinyhumans.ai) were missing all content: generations had usage but no input/output (system prompt never logged), the Context Scout flow showed no model and no I/O, tool spans reported output_chars: 0 / elapsed_ms: 0 with no payloads, per-call cost read $0 for managed tiers, and headless runs attributed traces to socket ids. Root cause was concentrated in the tinyagents→openhuman event bridge: payload capture was never enabled, child model-call events were suppressed, tool completions were hardcoded to zeros, and cost was estimated through the vendor-only catalog that does not know managed tier handles.

Solution

  • Enable PayloadCapture::all() in run_policy_for — the crate stamps request/response and tool I/O onto ModelCompleted/ToolCompleted. Off-device transmission stays gated by observability.agent_tracing.capture_content (default off per Trace exporter ignores capture_content — prompts/replies written to NDJSON/app log; Langfuse cached-token double-count #4454); the durable journal already redacts via RedactingSink.
  • OpenhumanEventBridge: project ModelCompleted into ModelCallCompleted for parent and child scopes with captured content, provider id, subagent task attribution, and the same resolved cost/cache/reasoning figures record_usage fed the wallet. Tool completions carry output text, backfilled arguments, and measured duration (middleware-recorded, with a bridge-side stamp fallback).
  • SpanCollector: generations record truncated content (25k-char cap); child generations nest under the subagent iteration and roll model/usage/cost onto the subagent span; subagent spans record prompt/output; tool spans record I/O.
  • Spawn tools emit the full prompt/output on SubagentSpawned/SubagentCompleted.
  • Trace user attribution falls back to the stored app-session profile before the client id.

Verified live against staging Langfuse: a scout-bearing turn audits as fully wired (system prompt visible, managed.burst-v1 scout generations with content, tool I/O + elapsed, non-zero costDetails, user attribution enamakel@tinyhumans.ai).

Submission Checklist

Impact

  • Desktop/core runtime: agent turns now carry model/tool payloads on in-process events and the on-device journal (secret-redacted). Nothing new leaves the device unless observability.agent_tracing.capture_content is enabled.
  • AgentProgress events grew content fields; UI consumers are unaffected (patterns updated).
  • Langfuse traces gain provider-qualified model labels and non-zero managed-tier costs.

Related


AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: feat/langfuse-full-content-tracing
  • Commit SHA: dad57a1

Validation Run

  • pnpm --filter openhuman-app format:check (via pre-push hook)
  • pnpm typecheck
  • Focused tests: cargo test --lib progress_tracing (64 ok), tinyagents::observability (6 ok), turn_state (24 ok), progress_bridge (9 ok), agent_meetings (187 ok)
  • Rust fmt/check (if changed): cargo fmt, cargo check --all-targets clean
  • Tauri fmt/check (if changed): N/A — no app/src-tauri changes

Validation Blocked

  • command: cargo test --lib (full suite)
  • error: three pre-existing nested-subagent stack-overflow aborts (reproduce identically on main on this machine)
  • impact: unrelated to this diff; all touched-module suites pass

Behavior Changes

  • Intended behavior change: agent trace exports carry full model/tool/subagent content (gated by capture_content), provider-qualified model labels, real per-call costs, and real-user attribution on headless runs.
  • User-visible effect: Langfuse traces become fully inspectable end-to-end; UI unchanged.

https://claude.ai/code/session_01Y66Xr118tDtRnLeFT1owFj

Summary by CodeRabbit

  • New Features

    • Progress updates now include more context for subagent spawns and completions, such as the prompt and final output.
    • Telemetry now shows clearer provider names alongside model names for more consistent tracking.
    • When content capture is enabled, tool and model activity can now include request/response details in traces.
  • Bug Fixes

    • Progress and tracing events now handle newer event shapes more reliably.
    • Session attribution now falls back to stored profile info when live identity data isn’t available.

senamakel added 4 commits July 4, 2026 06:53
…ugh agent tracing

- Enable tinyagents PayloadCapture in run_policy_for so ModelCompleted /
  ToolCompleted events carry request messages (incl. system prompt),
  completions, tool arguments and results. Off-device transmission stays
  gated by observability.agent_tracing.capture_content; the durable
  journal already redacts secrets via RedactingSink.
- Bridge: project ModelCompleted into ModelCallCompleted for parent AND
  subagent scopes (new subagent_task_id attribution), carrying captured
  input/output, provider_id, and a real per-call cost. Tool completions
  now carry real output text, backfilled arguments, and measured
  elapsed_ms (was hardcoded 0 / empty).
- Fix $0 cost: estimate via tier-aware agent::cost (knows chat-v1/
  burst-v1/...) instead of the vendor-only cost::catalog lookup.
- Provider identity: new Provider::telemetry_provider_id() (managed /
  openai / ollama / claude-code / custom); exporters render the Langfuse
  model as {provider_id}.{model} (e.g. managed.chat-v1).
- Collector: generations record content (capture-gated, 25k-char cap);
  subagent model calls nest under the subagent iteration and roll
  model/usage/cost onto the subagent span (Context Scout model now
  visible); subagent spans record delegated prompt + final output; tool
  spans record output + backfilled arguments.
- Spawn tools emit full prompt/output on SubagentSpawned/Completed.
- Regression tests for all of the above (collector + bridge).

Claude-Session: https://claude.ai/code/session_01Y66Xr118tDtRnLeFT1owFj
…cache is cold

Headless/autonomous cores never warm the auth_get_me cache, so Langfuse
traces attributed to the transport client id. Fall back to the stored
app-session profile (email, then backend user id) before the client-id
last resort. With tests.

Claude-Session: https://claude.ai/code/session_01Y66Xr118tDtRnLeFT1owFj
…ntent-tracing

# Conflicts:
#	src/bin/harness_subagent_audit.rs
#	src/openhuman/agent/progress_tracing.rs
#	src/openhuman/channels/providers/web/progress_bridge.rs
#	src/openhuman/tinyagents/mod.rs
#	src/openhuman/tinyagents/observability.rs
@senamakel senamakel requested a review from a team July 4, 2026 17:56
@coderabbitai

coderabbitai Bot commented Jul 4, 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: 34 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: 2c11e575-2fab-4f84-8847-1c41b2e18eaf

📥 Commits

Reviewing files that changed from the base of the PR and between d6c2ea8 and ff12f36.

📒 Files selected for processing (3)
  • src/openhuman/agent/progress_tracing.rs
  • src/openhuman/agent/progress_tracing/tests.rs
  • src/openhuman/tinyagents/journal.rs
📝 Walkthrough

Walkthrough

This PR enriches AgentProgress event variants with content-bearing fields (tool output/arguments, subagent prompt/output, model call input/output), adds a telemetry_provider_id() method to the Provider trait implemented across all providers, and updates progress_tracing.rs/tinyagents/observability.rs to gate content capture behind capture_content and stamp spans with provider-labeled model identifiers. Producers across orchestration tools now populate the new fields, consumers use forward-compatible .. patterns, and tests are updated accordingly.

Changes

Progress event content enrichment and provider telemetry

Layer / File(s) Summary
AgentProgress schema and Provider trait additions
src/openhuman/agent/progress.rs, src/openhuman/inference/provider/traits.rs, .../claude_code/mod.rs, .../compatible_provider_impl.rs, .../openhuman_backend.rs, .../reliable.rs, .../router.rs, src/openhuman/routing/provider.rs
New output/prompt/arguments/input fields added to AgentProgress variants; Provider::telemetry_provider_id() added with default "custom" and provider-specific overrides.
Orchestration and tool progress producers
.../agent_orchestration/ops.rs, .../spawn_parallel_graph.rs, .../tools/*.rs, .../session/tool_progress.rs
Subagent spawn/completion sites now populate prompt and output; tool completion reporter sets output/arguments.
progress_tracing capture and provider labeling
src/openhuman/agent/progress_tracing.rs
record_model_call reworked to accept provider_id/subagent_task_id/input/output, gate span content on capture_content, and label gen_ai.request.model as {provider_id}.{model}.
progress_tracing tests
src/openhuman/agent/progress_tracing/tests.rs
Fixtures updated with new fields; new tests validate content-gated capture and provider-labeled model names.
tinyagents observability bridge
src/openhuman/tinyagents/mod.rs, src/openhuman/tinyagents/observability.rs
OpenhumanEventBridge gains provider_id, resolved_calls, tool_started_at; ModelCompleted now emits ModelCallCompleted with resolved cost/token figures; full payload capture enabled.
Forward-compatible consumers and persistence tests
src/bin/harness_subagent_audit.rs, .../web/progress_bridge.rs, .../turn_state/mirror_tests.rs, tests/memory_*_e2e.rs
Match patterns use .. wildcards; session-profile attribution fallback added; tests updated for new struct fields.
Vendor submodule bump
vendor/tinyagents
Submodule pointer advanced to a new commit.

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

Possibly related issues

Possibly related PRs

Suggested reviewers: M3gA-Mind, sanil-23

Poem

A rabbit hops through spans and traces,
Stuffing prompts and outputs into their places,
Provider tags now label each call,
Capture gates guard content, standing tall.
Hop, hop — the telemetry's complete! 🐰✨

🚥 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 observability/tracing changes, including content capture, provider-qualified model IDs, subagent telemetry, and cost reporting.

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

@senamakel senamakel added feature Net-new user-facing capability or product behavior. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. labels Jul 4, 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: dad57a1dc1

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// Snapshot the provider's telemetry id before `provider` moves into the
// harness assembly — the event bridge stamps it on every per-call
// generation event (`{provider_id}.{model}` in Langfuse).
let provider_id = provider.telemetry_provider_id();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Stamp model calls with the actual routed provider

When the active provider is a router or fallback wrapper, the provider that serves a call is selected later per request (for example RouterProvider::resolve(model) can dispatch chat-v1/hint:* to a non-default provider, and ReliableProvider can fail over). Caching provider.telemetry_provider_id() once here means every ModelCallCompleted emitted by the bridge is labeled with the default/primary provider even when the call actually went elsewhere, so provider-qualified Langfuse model labels and per-provider trace attribution are wrong for routed/failover runs.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Correct — the label reflects the configured primary provider, not per-call routing. The crate's harness events carry no per-call provider identity today, so the bridge cannot observe which inner provider actually served a given call (RouterProvider::resolve / ReliableProvider failover happen below the event stream). Wrappers delegate to their primary so the common single-provider case is accurate; per-call provider attribution needs the provider identity threaded onto tinyagents' ModelStarted/ModelCompleted events — noting it as a follow-up rather than expanding this PR's surface.

…ntent-tracing

# Conflicts:
#	src/openhuman/tinyagents/observability.rs

@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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@src/openhuman/agent/progress_tracing.rs`:
- Around line 678-690: The TurnContent path in SpanCollector is ignoring the
TraceContext.capture_content setting because SpanCollector::new leaves
capture_content false and the collector never receives the flag. Update the
SpanCollector construction and/or its TurnContent handling in
progress_tracing.rs so the collector uses self.ctx.capture_content (or passes it
through at init) before populating span.input and span.output with
capture_model_content.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 23e861de-8dc7-46d9-9547-8f703182d742

📥 Commits

Reviewing files that changed from the base of the PR and between d1b5eeb and d6c2ea8.

📒 Files selected for processing (27)
  • src/bin/harness_subagent_audit.rs
  • src/openhuman/agent/harness/session/tool_progress.rs
  • src/openhuman/agent/progress.rs
  • src/openhuman/agent/progress_tracing.rs
  • src/openhuman/agent/progress_tracing/tests.rs
  • src/openhuman/agent_orchestration/ops.rs
  • src/openhuman/agent_orchestration/spawn_parallel_graph.rs
  • src/openhuman/agent_orchestration/tools/agent_prepare_context.rs
  • src/openhuman/agent_orchestration/tools/continue_subagent.rs
  • src/openhuman/agent_orchestration/tools/dispatch.rs
  • src/openhuman/agent_orchestration/tools/spawn_async_subagent.rs
  • src/openhuman/agent_orchestration/tools/spawn_subagent.rs
  • src/openhuman/channels/providers/web/progress_bridge.rs
  • src/openhuman/inference/provider/claude_code/mod.rs
  • src/openhuman/inference/provider/compatible_provider_impl.rs
  • src/openhuman/inference/provider/openhuman_backend.rs
  • src/openhuman/inference/provider/reliable.rs
  • src/openhuman/inference/provider/router.rs
  • src/openhuman/inference/provider/traits.rs
  • src/openhuman/routing/provider.rs
  • src/openhuman/threads/turn_state/mirror_tests.rs
  • src/openhuman/tinyagents/mod.rs
  • src/openhuman/tinyagents/observability.rs
  • tests/memory_core_threads_raw_coverage_e2e.rs
  • tests/memory_raw_coverage_e2e.rs
  • tests/memory_threads_raw_coverage_e2e.rs
  • vendor/tinyagents

Comment thread src/openhuman/agent/progress_tracing.rs
senamakel added 2 commits July 4, 2026 11:42
…ions

tinyagents v1.5 made journal persistence asynchronous (background
AppendWorker); read-after-write now requires flush(), so the replay
test raced the drain thread and read zero events.

Claude-Session: https://claude.ai/code/session_01Y66Xr118tDtRnLeFT1owFj
Review fix (PR tinyhumansai#4506, CodeRabbit): the tinyhumansai#4454 merge left two parallel
capture gates — a collector-level flag read by the TurnContent arm and
the TraceContext flag read by every other content path. The web bridge
only sets the TraceContext flag, so the turn's prompt/reply was always
dropped. with_content_capture now delegates to the single ctx gate;
regression test covers both construction styles.

Claude-Session: https://claude.ai/code/session_01Y66Xr118tDtRnLeFT1owFj

@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: e65cf04784

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +431 to +432
fn telemetry_provider_id(&self) -> String {
"custom".to_string()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Override telemetry id for Claude Agent SDK

When the configured provider is claude_agent_sdk / claude_agent_sdk:<model>, the factory builds ClaudeAgentSdkProvider, but that provider does not override this new method, so all of its Langfuse generations are labeled custom.<model> and gen_ai.provider=custom rather than the actual SDK provider. Since this change makes provider id part of the exported model/provenance, please add a concrete override for the Claude Agent SDK provider (similar to ClaudeCodeProvider) instead of letting this real provider fall through to the custom default.

Useful? React with 👍 / 👎.

@senamakel senamakel merged commit d0f0b6e into tinyhumansai:main Jul 4, 2026
6 checks passed

@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: ff12f36954

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +714 to +717
span.attributes.insert(
"gen_ai.usage.cost_usd".to_string(),
json_f64(prior_cost + cost_usd),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid duplicating subagent model costs in Langfuse

For any subagent that makes an LLM call, this copies the same gen_ai.usage.*/cost data onto the enclosing subagent span after already creating an llm.* generation for that call. spans_to_langfuse_batch promotes any span with these attributes via apply_usage_fields, so the batch will contain both the child generation and the subagent span as generation-create observations with the same cost, double-reporting subagent usage/cost in Langfuse traces.

Useful? React with 👍 / 👎.

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/. feature Net-new user-facing capability or product behavior. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant