Skip to content

Cost estimate goes negative for routed/variable-price model slugs (OpenRouter -1 pricing sentinel) #137098

Description

@abodis

Summary

Cost estimates go sharply negative for routed / variable-price model slugs served through OpenRouter, because OpenRouter publishes those slugs with a negative pricing sentinel and Hermes prices the sentinel at face value.

Observed on the OpenRouter Jev router:

model.default: typesafe/jev-router
model.provider: openrouter

One ordinary turn (2 API calls, 78,487 input tokens, 64 output tokens) was recorded as:

estimated_cost_usd: -78551.0
cost_status:        estimated
cost_source:        provider_models_api

The same negative figure lands in session_model_usage.estimated_cost_usd, so /insights, /usage, any cost-based reporting, and any automation that gates on spend are all corrupted by it.

Root cause

agent/usage_pricing.py::_pricing_entry_from_metadata reads the OpenRouter catalog pricing and multiplies the per-token number by one million:

value = _to_decimal(raw)
return None if value is None else value * _ONE_MILLION

OpenRouter does not publish a fixed price for router slugs. GET /api/v1/models returns:

typesafe/jev-router         pricing: {"prompt": "-1", "completion": "-1"}
openrouter/auto             pricing: {"prompt": "-1", "completion": "-1"}
openrouter/pareto-code      pricing: {"prompt": "-1", "completion": "-1"}

-1 means "the price is set by whichever model actually serves the request". Multiplied by 1e6 the entry becomes -1,000,000 per million tokens, hence the negative estimate. Any -1-priced catalog entry hits this, not just jev-router.

Suggested fix

Treat a negative catalog price as "unpriced" and let the cost fall back to unknown:

     prompt = per_million("prompt")
     completion = per_million("completion")
     request = _to_decimal(pricing.get("request"))
+    # Routed / variable-price slugs (OpenRouter's ``typesafe/jev-router``, ``openrouter/auto``,
+    # ``openrouter/pareto-code``) are listed with a NEGATIVE sentinel instead of a number: the
+    # price is set by whichever model actually served the request. Pricing them from the sentinel
+    # produces a large negative estimate, which then poisons /insights, /usage and any
+    # cost-based reporting. Treat such a route as unpriced so cost falls back to unknown.
+    if any(value is not None and value < 0 for value in (prompt, completion, request)):
+        return None
     if prompt is None and completion is None and request is None:
         return None

Verified locally: with the guard, the same session flips from estimated_cost_usd: -78551.0 / cost_status: estimated to estimated_cost_usd: 0.0 / cost_status: unknown. The pricing test selection (pytest -k pricing) passes: 137 passed, 1 skipped.

Related improvement

For these routes OpenRouter returns the real per-call cost in the response body (usage.cost), so an actual-cost source exists without a catalog lookup. Reading it when the catalog price is missing or negative would give accurate numbers instead of unknown for router slugs.

Environment

  • Hermes Agent v0.21.6+131.g38880bd (git install, install dir ~/.hermes/hermes-agent)
  • Provider: OpenRouter (provider: openrouter, api_mode: chat_completions)
  • Router metadata confirmed the request was served by an upstream model (deepseek/deepseek-v4.1-flash and openai/gpt-6-luna in two runs) while the session row carried the negative estimate

Activity

  1. added
    type/bugSomething isn't working
    P2Medium — degraded but workaround exists
    comp/agentCore agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint
    area/billingAccount usage, credit usage, billing (cross-cutting)
    area/usage-costToken accounting, usage reporting, billing, cost tracking
    on Oct 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium — degraded but workaround existsarea/billingAccount usage, credit usage, billing (cross-cutting)area/usage-costToken accounting, usage reporting, billing, cost trackingcomp/agentCore agent runtime: loop, agent_init, prompt builder, context-compression, responses endpointprovider/openrouterOpenRouter aggregatortype/bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions