Summary
cost_usd and all packaged pricing strategies compute and store USD as Python
float. Per-unit-times-count, per-1000-character division, and the agent batch sum
accumulate binary float representation error, so user-facing and analytics values
land in Parquet rows, manifest steps, and AgentResult.total_cost_usd as values
like 0.30000000000000004.
Affected files
libs/core/genblaze_core/providers/pricing.py:61-207 (all strategies multiply/divide raw float: per_unit, per_input_chars, per_output_second, tiered, by_param, by_model_and_param, bucketed_by_duration)
libs/core/genblaze_core/providers/model_registry.py:750 (compute_cost returns spec.pricing(ctx) with no boundary rounding)
libs/core/genblaze_core/providers/base.py:1051 (step.cost_usd = cost), also :1652, :1822
libs/core/genblaze_core/agents/loop.py:300 (total_cost = sum(step.cost_usd or 0.0 ...), stored at loop.py:84)
libs/core/genblaze_core/sinks/parquet.py:133 (writes cost_usd raw)
cost_usd: float declared at libs/core/genblaze_core/models/step.py:61 and libs/core/genblaze_core/models/chat.py:227
Evidence
Runtime demo with the actual functions:
per_unit(0.10) summed over 10 assets = 0.9999999999999999 (== 1.0? False)
by_param size a=0.1 b=0.2 -> a+b = 0.30000000000000004 (== 0.3? False)
per_input_chars(0.002, per=1000) x1000 = 0.0020000000000000304 (== 0.002? False)
These values are assigned straight to step.cost_usd and persisted/summed
unrounded. Scope note (verified, to avoid over-claiming): the canonical hash is NOT
affected, because canonical/_normalize.py:34 rounds every float to 10 decimals
before hashing, so determinism holds. The defect is the unrounded values exposed to
consumers (Parquet sink, AgentResult.total_cost_usd, AgentCompletedEvent / tracer
cost attributes). There is no display-side rounding anywhere in the CLI or runtime.
Why it matters
USD amounts shown to users and written to analytics tables carry float noise like
0.30000000000000004, which looks wrong in cost reports and complicates downstream
aggregation.
Suggested fix
Round at the single boundary where cost is finalized, for example in compute_cost
(model_registry.py:750) return round(result, 6) (micro-dollar precision), and
round the agents/loop.py:300 sum the same way. This is the cheapest, lowest-risk
option. Switching strategies and cost_usd to Decimal would be exact but is a
larger change and likely overkill for a field documented as "Estimated cost".
Acceptance criteria
- Persisted and aggregated
cost_usd values are free of float-representation noise.
- Existing pricing-strategy behavior is otherwise unchanged; canonical hashing is
unaffected.
Summary
cost_usdand all packaged pricing strategies compute and store USD as Pythonfloat. Per-unit-times-count, per-1000-character division, and the agent batch sumaccumulate binary float representation error, so user-facing and analytics values
land in Parquet rows, manifest steps, and
AgentResult.total_cost_usdas valueslike
0.30000000000000004.Affected files
libs/core/genblaze_core/providers/pricing.py:61-207(all strategies multiply/divide rawfloat:per_unit,per_input_chars,per_output_second,tiered,by_param,by_model_and_param,bucketed_by_duration)libs/core/genblaze_core/providers/model_registry.py:750(compute_costreturnsspec.pricing(ctx)with no boundary rounding)libs/core/genblaze_core/providers/base.py:1051(step.cost_usd = cost), also:1652,:1822libs/core/genblaze_core/agents/loop.py:300(total_cost = sum(step.cost_usd or 0.0 ...), stored atloop.py:84)libs/core/genblaze_core/sinks/parquet.py:133(writescost_usdraw)cost_usd: floatdeclared atlibs/core/genblaze_core/models/step.py:61andlibs/core/genblaze_core/models/chat.py:227Evidence
Runtime demo with the actual functions:
These values are assigned straight to
step.cost_usdand persisted/summedunrounded. Scope note (verified, to avoid over-claiming): the canonical hash is NOT
affected, because
canonical/_normalize.py:34rounds every float to 10 decimalsbefore hashing, so determinism holds. The defect is the unrounded values exposed to
consumers (Parquet sink,
AgentResult.total_cost_usd,AgentCompletedEvent/ tracercost attributes). There is no display-side rounding anywhere in the CLI or runtime.
Why it matters
USD amounts shown to users and written to analytics tables carry float noise like
0.30000000000000004, which looks wrong in cost reports and complicates downstreamaggregation.
Suggested fix
Round at the single boundary where cost is finalized, for example in
compute_cost(
model_registry.py:750)return round(result, 6)(micro-dollar precision), andround the
agents/loop.py:300sum the same way. This is the cheapest, lowest-riskoption. Switching strategies and
cost_usdtoDecimalwould be exact but is alarger change and likely overkill for a field documented as "Estimated cost".
Acceptance criteria
cost_usdvalues are free of float-representation noise.unaffected.