Skip to content

fix(langfuse): omit explicit zero cost_details for subscription-included providers (#43129) - #43130

Closed
liuhao1024 wants to merge 1 commit into
NousResearch:mainfrom
liuhao1024:fix/langfuse-subscription-zero-cost
Closed

fix(langfuse): omit explicit zero cost_details for subscription-included providers (#43129)#43130
liuhao1024 wants to merge 1 commit into
NousResearch:mainfrom
liuhao1024:fix/langfuse-subscription-zero-cost

Conversation

@liuhao1024

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes Langfuse showing $0 cost for subscription-included providers (e.g. openai-codex). Both the response-object path and the post_api_request dict path were sending explicit zero-valued cost_details to Langfuse, which treats them as authoritative and never recalculates from model pricing.

Related Issue

Fixes #43129

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • plugins/observability/langfuse/__init__.py: In _usage_and_cost(), gate cost_details population on cost.status != "included" so subscription-included routes produce empty cost_details instead of explicit zeros. In the post_api_request dict path, check resolve_billing_route().billing_mode != "subscription_included" before computing per-type costs from the pricing entry.
  • tests/plugins/test_langfuse_plugin.py: Added TestSubscriptionIncludedCostOmission with 4 regression tests covering both code paths (subscription-included → empty cost_details, normal provider → populated cost_details).

How to Test

  1. Run pytest tests/plugins/test_langfuse_plugin.py -xvs — all 43 tests pass
  2. The new tests verify:
    • _usage_and_cost() returns empty cost_details for openai-codex (subscription_included)
    • _usage_and_cost() returns populated cost_details for openai (normal provider)
    • on_post_llm_call dict path returns empty cost_details for openai-codex
    • on_post_llm_call dict path returns populated cost_details for openai
  3. Manual verification: configure Langfuse + openai-codex provider, run a turn, check that Langfuse shows estimated cost (not $0)

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Code Intelligence

  • Analyzed: _usage_and_cost, on_post_llm_call, get_pricing_entry, estimate_usage_cost, resolve_billing_route
  • Blast radius: LOW — changes are confined to the Langfuse plugin's cost-export logic; no behavioral change for non-Langfuse consumers
  • Related patterns: estimate_usage_cost returns CostResult(amount_usd=Decimal("0"), status="included") for subscription routes; get_pricing_entry returns PricingEntry with all _ZERO costs. Both paths now check the billing status before populating cost_details.

…ded providers

For subscription-included providers (e.g. openai-codex), get_pricing_entry()
returns a PricingEntry with all-zero costs.  Both Langfuse code paths
(_usage_and_cost response-object path and post_api_request dict path) checked
`entry.input_cost_per_million is not None` which is True for Decimal("0"),
leading to explicit 0.0 values in cost_details.

Langfuse treats provided cost_details as authoritative and will not
recalculate estimated cost from model pricing when zeros are present.
Omitting cost_details lets Langfuse fall back to its own model-based cost
estimation.

Fixes NousResearch#43129
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/plugins Plugin system and bundled plugins provider/openai OpenAI / Codex Responses API labels Jun 9, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused regression fix. The current-main code still emits zero-valued Langfuse costs in both paths: the response-object path at plugins/observability/langfuse/__init__.py:576-596 and the sanitized usage-dict path at plugins/observability/langfuse/__init__.py:1003-1016. agent/usage_pricing.py:654-655, :801-809, and :919-926 confirm that openai-codex is an included route with zero-valued pricing.

The PR’s two guards cover those two active paths and its tests preserve non-subscription behavior. It adds no core surface, configuration, or cache-sensitive behavior. Its June base is older, but the target logic remains structurally present on current main, making this a high-value salvage candidate.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users labels Jul 14, 2026
@teknium1 teknium1 added the area/usage-cost Token accounting, usage reporting, billing, cost tracking label Jul 19, 2026
erosika added a commit to erosika/hermes-agent that referenced this pull request Aug 10, 2026
Subscription-included routes (e.g. openai-codex) priced every request
at explicit $0: get_pricing_entry returns zero rates for these routes,
and Langfuse treats provided cost_details as authoritative — the zeros
blocked its own model-based cost estimation, so every generation showed
$0 forever (NousResearch#43129). _canonical_usage_and_cost now resolves the billing
route first and sends no cost keys at all for included routes, letting
Langfuse fall back to its own pricing. Usage details still export.

Adopted from NousResearch#43130 — thanks @liuhao1024.

Co-authored-by: liuhao1024 <sunsky.lau@gmail.com>
@erosika

erosika commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

adopted into #83437 with co-author credit — thanks for tracing the authoritative-zero behavior. the check now lives in the shared _canonical_usage_and_cost helper adopted from #64797, so both response paths skip cost keys for subscription-included routes. closes #43129 on merge.

kshitijk4poor added a commit that referenced this pull request Aug 13, 2026
… fan-out

Salvaged from PR #83437 by @erosika, with adopted fixes from @bgodlin (#81054),
@aldoeliacim (#82332), @nftpoetrist (#42326), @rodboev (#39653), @FnExpress
(#64292, supersedes #32175 by @db-aeon), @Per0-1 (#61166), @NaMinhyeok (#64797),
and @liuhao1024 (#43130).

Widens the bundled Langfuse plugin from 6 to 11 hooks and fixes two
attribution bugs. Also adopts shutdown/atexit lifecycle fixes and composes
8 prior community PRs with interaction-fix follow-ups.

Model attribution: on_pre_llm_request and on_post_llm_call now prefer the
wire value (request body model, response model) over the agent attribute,
which goes stale after /model switch or provider fallback.

Cost total: both cost paths now send a summed total alongside the per-type
breakdown, since Langfuse does not derive calculatedTotalCost from
cost_details keys. Subscription-included routes send no cost keys at all.

New coverage: api_request_error closes failed generations with ERROR level;
on_session_finalize/on_session_end close dangling traces for tool-only and
interrupted turns; subagent_start/subagent_stop trace delegated children as
spans; MoA advisor fan-out emits one generation per advisor priced at the
advisor's own model.

Capture modes: HERMES_LANGFUSE_CAPTURE=metadata|sanitized|full (default
sanitized). Sanitized mode redacts secret patterns before truncation.

Adopted lifecycle fixes: shutdown client at session finalize when
reason=shutdown (not on session rotation); atexit finalizer ends open root
spans for short-lived processes; root context manager exited to prevent
interpreter-teardown TypeError; TOCTOU on _get_langfuse() fixed with lock;
reasoning_content surfaced in traces; system prompt included in generation
input for Anthropic/Codex/Bedrock; SDK v3 update_trace replaces set_trace_io.

Closes #29482, #43129, #72661.
Supersedes #81054, #82332, #42326, #39653, #64292, #32175, #61166, #64797, #43130.
Partially addresses #67544 (capture modes + secret redaction; user_id remains open).
@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Merged via #85439 — your fix was adopted and composed into the wider Langfuse tracing PR by @erosika. Your contribution is credited in the commit body. Thanks @liuhao1024!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/usage-cost Token accounting, usage reporting, billing, cost tracking comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have provider/openai OpenAI / Codex Responses API sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Langfuse shows zero cost for Codex-backed Hermes traces

5 participants