Skip to content

model-usage: parse numeric-string costs and skip non-finite values#37877

Closed
shuofengzhang wants to merge 1 commit into
openclaw:mainfrom
shuofengzhang:fix/model-usage-parse-cost
Closed

model-usage: parse numeric-string costs and skip non-finite values#37877
shuofengzhang wants to merge 1 commit into
openclaw:mainfrom
shuofengzhang:fix/model-usage-parse-cost

Conversation

@shuofengzhang

Copy link
Copy Markdown
Contributor

What changed

  • Added a parse_cost() helper in skills/model-usage/scripts/model_usage.py.
  • aggregate_costs(), pick_current_model(), and latest_day_cost() now use parse_cost() so they:
    • accept numeric string costs (for example "1.75"),
    • ignore boolean values,
    • ignore non-finite values (NaN, Infinity).
  • Extended skills/model-usage/scripts/test_model_usage.py with coverage for:
    • numeric-string cost aggregation,
    • skipping non-finite/bool costs,
    • parsing numeric-string latest-day cost.

Why

  • Some cost payloads can contain numeric values serialized as strings.
  • The previous logic only accepted int/float, so valid string costs were silently dropped.
  • Filtering non-finite and boolean values makes output more robust and avoids propagating invalid totals.

Testing

  • source .venv/bin/activate && pytest -q skills/model-usage/scripts/test_model_usage.py
  • scripts/clone_and_test.sh openclaw/openclaw

@greptile-apps

greptile-apps Bot commented Mar 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces a parse_cost() helper in model_usage.py to robustly normalize cost values before aggregation. The helper correctly handles three previously unguarded cases: numeric strings (e.g. "1.75"), boolean values (which Python silently treats as int without an explicit guard), and non-finite floats such as NaN or Infinity. All three call sites (aggregate_costs, pick_current_model, latest_day_cost) are updated consistently to use the helper, and the existing logic is unchanged for valid numeric inputs.

Key observations:

  • The bool guard (isinstance(value, bool) before isinstance(value, (int, float))) is essential and correctly placed, since bool is a subclass of int in Python.
  • math.isfinite cleanly covers both NaN and ±Infinity in a single check.
  • Two new tests validate string-cost parsing in aggregate_costs and latest_day_cost, but pick_current_model — which received the same change — has no new test coverage for these edge cases.

Confidence Score: 4/5

  • Safe to merge; the changes are backward-compatible with no logical errors.
  • This PR is well-scoped and correct. The implementation properly handles all edge cases with good defensive checks. The one gap is incomplete test coverage for pick_current_model's use of parse_cost with string/bool/non-finite costs, which prevents a perfect score but does not represent a functional risk.
  • skills/model-usage/scripts/test_model_usage.py — consider adding test coverage for pick_current_model with the new cost type handling, to match coverage of aggregate_costs and latest_day_cost.

Last reviewed commit: 9316b09

Comment on lines +38 to +65
def test_aggregate_costs_accepts_numeric_strings_and_skips_non_finite_values(self):
entries = [
{
"modelBreakdowns": [
{"modelName": "gpt-5", "cost": "1.25"},
{"modelName": "gpt-5", "cost": "nan"},
{"modelName": "gpt-5", "cost": True},
{"modelName": "gpt-5-mini", "cost": 0.75},
]
}
]

totals = aggregate_costs(entries)

self.assertEqual(totals["gpt-5"], 1.25)
self.assertEqual(totals["gpt-5-mini"], 0.75)

def test_latest_day_cost_parses_numeric_string(self):
entries = [
{"date": "2026-03-04", "modelBreakdowns": [{"modelName": "gpt-5", "cost": 0.5}]},
{"date": "2026-03-05", "modelBreakdowns": [{"modelName": "gpt-5", "cost": "1.75"}]},
]

latest_date, latest_cost = latest_day_cost(entries, "gpt-5")

self.assertEqual(latest_date, "2026-03-05")
self.assertEqual(latest_cost, 1.75)

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.

Missing coverage for pick_current_model with new cost types

pick_current_model was updated to use parse_cost, but there is no test exercising the new behaviour with string costs, boolean values, or non-finite values. The two new tests only cover aggregate_costs and latest_day_cost. Consider adding a test such as:

def test_pick_current_model_accepts_numeric_string_cost(self):
    entries = [
        {"date": "2026-03-05", "modelBreakdowns": [
            {"modelName": "gpt-5",      "cost": "2.00"},
            {"modelName": "gpt-5-mini", "cost": True},   # should be ignored
        ]}
    ]
    model, _ = pick_current_model(entries)
    self.assertEqual(model, "gpt-5")

Without this, a regression in pick_current_model's use of parse_cost would go undetected.

Prompt To Fix With AI
This is a comment left during a code review.
Path: skills/model-usage/scripts/test_model_usage.py
Line: 38-65

Comment:
Missing coverage for `pick_current_model` with new cost types

`pick_current_model` was updated to use `parse_cost`, but there is no test exercising the new behaviour with string costs, boolean values, or non-finite values. The two new tests only cover `aggregate_costs` and `latest_day_cost`. Consider adding a test such as:

```python
def test_pick_current_model_accepts_numeric_string_cost(self):
    entries = [
        {"date": "2026-03-05", "modelBreakdowns": [
            {"modelName": "gpt-5",      "cost": "2.00"},
            {"modelName": "gpt-5-mini", "cost": True},   # should be ignored
        ]}
    ]
    model, _ = pick_current_model(entries)
    self.assertEqual(model, "gpt-5")
```

Without this, a regression in `pick_current_model`'s use of `parse_cost` would go undetected.

How can I resolve this? If you propose a fix, please make it concise.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9316b090e0

ℹ️ 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 +115 to +116
if isinstance(value, (int, float)):
parsed = float(value)

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.

P2 Badge Handle overflow when parsing integer costs

parse_cost now does float(value) for any int without catching OverflowError, and this commit calls parse_cost before modelName validation in both aggregate_costs and pick_current_model. With a malformed row such as {"modelName": null, "cost": 10**1000}, the script now raises and exits instead of skipping the bad row, so a single corrupt payload entry can break the whole usage report.

Useful? React with 👍 / 👎.

@thewilloftheshadow thewilloftheshadow added the r: too-many-prs Auto-close: author has more than twenty active PRs. label Mar 6, 2026
@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because the author has more than 10 active PRs in this repo. Please reduce the active PR queue and reopen or resubmit once it is back under the limit. You can close your own PRs to get back under the limit.

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

Labels

r: too-many-prs Auto-close: author has more than twenty active PRs. size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants