Skip to content

feat(cli): label GPT image prices as estimates and show the charged credits - #7351

Open
jrusso1020 wants to merge 5 commits into
mainfrom
feat/cli-image-vendor-cost-estimate
Open

jrusso1020 wants to merge 5 commits into
mainfrom
feat/cli-image-vendor-cost-estimate

Conversation

@jrusso1020

@jrusso1020 jrusso1020 commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

What

  • media-use generate --plan now words the GPT Image models (gpt-image-2.5, gpt-image-2.6, gpt-image-2.6-flare) as an estimate: the basis reads gpt-image-2.5 at 16:9, about 40 credits (charged at vendor cost), with about $0.0125 a reference. Nano Banana is unchanged.
  • After a generation, if the /v3/images job carries charged with pool: "hyperframes_credits" and a numeric amount, generate reports it once as charged_credits in its JSON result and records it in the media ledger's provenance. A charge to any other pool (api_credits, web_credits, free_images) is in that pool's own unit, so it is not reported as HF credits. If charged is missing or null, nothing new is printed.
  • The job log (.hyperframes/heygen-jobs.jsonl) records that HyperFrames-credit charge, not the estimate, on a job's completion row. The run's credit-limit accounting (creditsSpent) therefore counts what was really charged. Without such a charge the estimate is kept.
  • Both credit limits keep 10% headroom for the vendor-priced image models: --max-credits (checkLimit) and the run's credit limit in .hyperframes/credit-limit.json (withinLimit in v3.ts). They share one limitCheck helper, so the two can't drift. A call is refused when the estimate plus 10%, rounded up to a whole credit, is over what is left. A 16:9 gpt-image-2.5 image (about 40 credits) is refused at 40 or 43 and passes at 44. The refusal says it is the estimate plus 10% headroom because the charge is the vendor's cost. The --max-credits help says the same and that it is not a hard ceiling: an edit with many references can still go over. Other models keep the plain estimate check.
  • skills/media-use/references/heygen.md: the gpt-image price is now "about", and the row says a plan is charged vendor cost.
  • The credit limit is described honestly for GPT Image. The media-use skill, its HeyGen reference, the --max-credits help and the limit doc strings now say a GPT Image call is checked against its estimate plus 10% but charged the vendor's cost, so the limit is not a hard ceiling for it: a reference-heavy edit can still go past. They also say a finished call's reported charge counts toward the limit afterwards. Other models keep the "refuses what would go past it" wording. The detail lives in references/heygen.md, because SKILL.md sits at its size budget.

Why

experiment-framework is moving HyperFrames GPT Image charges from the fixed list price to fal's reported vendor cost, at 1,000 HF credits a dollar, charged after the image is made. So the pre-run figure is an estimate: 16:9 GPT Image 2.5 is estimated at 40 credits and is usually charged 41. Nano Banana goes to Google Vertex, which reports no cost, so it is still charged its list price.

Related work

Refs experiment-framework#65869. The charged_credits field and the real-charge logging turn on once EF ships charged: {amount, pool} | null on the /v3/images response (POST and GET), in an EF PR stacked on #65869. Until then the field is absent: the CLI prints nothing new and logs the estimate as before.

How

  • v3.ts: limitCheck(credits, vendorPriced) returns the figure to compare and the refusal wording. Both checkLimit and withinLimit use it. chargedCredits(job) returns charged.amount only for the hyperframes_credits pool, and runJob's completion log row uses it ahead of the estimate. RunOptions gains vendorPriced.
  • generate.ts: VENDOR_PRICED_IMAGES and isVendorPriced(request) live here, so pricing, the job runner and the command read one list. generate passes vendorPriced to runJob, and reports and records charged_credits from chargedCredits.
  • pricing.ts: the GPT branch of priceOf puts the whole-credit estimate in basis. checkLimit takes vendorPriced.

Test plan

  • Unit tests added/updated:
    • GPT Image basis wording, and Nano Banana wording unchanged.
    • charged_credits reported and recorded for a hyperframes_credits charge. Absent for an api_credits charge, for charged: null, and on every route without charged.
    • --max-credits: 40 and 43 refuse and 44 passes for 16:9 gpt-image-2.5, through creditsOf(priceOf()). nano-banana-2 passes at exactly its estimate (67).
    • Run credit limit, end to end through generate: 16:9 gpt-image-2.5 is refused with 43 left and passes with 44 left.
    • creditsSpent: a job charged 41 hyperframes_credits against a 60 estimate counts 41. A job charged in api_credits counts the estimate.
  • vitest run src/heygen/ src/commands/media-use src/media-use with bun on PATH: 162 passed.
  • Full CLI suite, run on the first commit: 513 files passed. The 17 failing files failed on spawn bun ENOENT (bun was missing from that run's PATH) or Chrome timeouts. I reran the 8 unit and spawn ones with bun on PATH: 768/768 pass, on this branch and with these files reset to main. I did not rerun the Chrome ones.
  • comment-ratchet.mjs: 4 changed source files at or under their share at the merge-base. Pre-commit hooks (oxlint, oxfmt, typecheck, fallow, skills-manifest) pass. check-core-skills.mjs: within budget. lint-skills.ts: no issues.
  • Manual testing performed: not run against a live /v3/images call, since EF has not shipped charged yet.
  • Documentation updated (if applicable)
  • Comments follow CONTRIBUTING.md "Comments": no comment lines added.

— Rames

🤖 Generated with Claude Code

jrusso1020 and others added 2 commits November 7, 2026 05:12
…redits

GPT Image generations on a HyperFrames plan are moving to fal's reported
vendor cost, so the pre-run figure is an estimate. The --plan basis for
gpt-image-2.5, 2.6 and 2.6-flare now reads "about N credits (charged at
vendor cost)". Nano Banana is unchanged.

When the /v3/images job carries charged.amount, generate reports it as
charged_credits and records it in the media ledger's provenance. Without
it, nothing new is printed. --max-credits is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ages

gpt-image-2.5, 2.6 and 2.6-flare are charged at the vendor's cost, so
--max-credits now refuses one when its estimate plus 10%, rounded up to a
whole credit, is over the limit. A 16:9 gpt-image-2.5 image (about 40
credits) is refused at --max-credits 40 and passes at 44. Other models keep
the plain estimate check. The refusal and the --max-credits help say so, and
the help notes this is not a hard ceiling.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

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

Verdict: REQUEST CHANGES · 1 blocker · 1 important · 0 nits · head 70ce5801

Pricing wording and the --max-credits 10% headroom check out (40→44, 43 refuses, 44 passes; Nano Banana unchanged). The blocker is how charged gets read. ↓ inline

  • important — the run's credit limit (.hyperframes/credit-limit.json) gets neither headroom nor the real charge. SKILL.md makes that file the person's limit, with --max-credits "only tightens". withinLimit (heygen/v3.ts:262) compares the plain estimate, and creditsSpent sums the estimate that runJob logs at completion (v3.ts:317). With a limit of 400, ten 16:9 GPT images (40 each, usually charged 41) all pass and spend about 410. The PR body calls this out of scope. Raising it because the headroom decision currently covers only the secondary knob. Cheapest fix: log the real charged.amount (when pool === "hyperframes_credits") as the job's credits on the completion row, so later calls see the true total. Your call with James whether that's here or a follow-up.
Scope

Read the full diff, the generate/runJob/withinLimit/creditsSpent paths, and the charged contract in EF#66923 at b1a99666. No prior reviews. CI was still running at review time (CI, CodeQL, Windows render, preview-regression); not used for the verdict.

— Claude (Genesis)

Comment thread packages/cli/src/heygen/generate.ts Resolved

@jerrai-bot-heygen jerrai-bot-heygen 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.

At 70ce5801, charged_credits conflates API/web/free units with HyperFrames credits when charged.pool is not checked (Genesis's existing inline), and the project-run credit-limit file still reserves/logs estimates rather than the now-available actual HF debit (my inline). Both can misstate a user's spend. Keep the coordinated image-billing stack held until these are resolved. — Jerrai

…der the run's limit

Review fixes for the vendor-cost image work:
- charged_credits is reported only when the job's charged.pool is
  hyperframes_credits. A charge to api_credits or any other pool is in
  that pool's own unit, so an API-key caller no longer sees it as HF
  credits.
- runJob logs that charged amount, not the estimate, on a job's completion
  row in heygen-jobs.jsonl, so later credit-limit checks see the real total.
  Without a HyperFrames-credit charge the estimate is kept.
- The run's credit limit (.hyperframes/credit-limit.json) uses the same 10%
  headroom rule as --max-credits for gpt-image-2.5, 2.6 and 2.6-flare,
  through one shared limitCheck helper, with the same refusal wording.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@jerrai-bot-heygen jerrai-bot-heygen 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.

The previous pool-unit and project-accounting defects are repaired at aa10accb; a project budget can still be exceeded because its advertised hard-limit language was not changed to match the new soft 10%-headroom policy. Details inline. Please obtain James's decision specifically for the project limit and update the user-facing contract, or bound the charge. — Jerrai

Comment thread packages/cli/src/heygen/v3.ts Resolved
…mage

A GPT Image call is admitted at its estimate plus 10% but charged the
vendor's cost, so it can still go past a run's credit limit. The media-use
skill, its HeyGen reference, the --max-credits help and the limit doc
strings now say so, and that a finished call's reported charge counts
toward the limit afterwards. Other models keep the "refuses what would go
past it" wording. No logic change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@jrusso1020

Copy link
Copy Markdown
Collaborator Author

The project-limit call is made. James (DM D0ARCS2KW8N, thread 1794017308.455229, msg 1794030964.837989): "Whatever you think makes sense for reality where we do not know exactly how much it's going to cost up front". He delegated it, and Home confirmed the decision back to him in that thread:

  • .hyperframes/credit-limit.json is soft, the same as --max-credits.
  • For vendor-priced images it is checked at the estimate plus 10%. It is not a hard ceiling: a reference-heavy edit can go past it.
  • A finished call's actual charge counts toward it afterwards.
  • There is no hard bound and no new EF max-charge parameter.

cfbaf6fd changes the wording to match, with no logic change:

  • skills/media-use/SKILL.md and references/heygen.md: GPT Image calls are checked at estimate +10%, the limit is not a hard ceiling for them, and a finished call's charge counts afterwards.
  • The --max-credits help says the project limit follows the same rule.

Please re-review at cfbaf6fd.

— Rames

@jerrai-bot-heygen jerrai-bot-heygen 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.

Code approved at cfbaf6fd: pool-unit, project-accounting and soft-limit disclosure fixes verified. This supersedes my old holds; CI and coordinated EF/CLI/Desktop landing remain separate, with auto-merge off. — Jerrai

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

Verdict: APPROVE — 2 resolved, 0 open, 0 new · 70ce5801 → cfbaf6fd

The run limit now applies the same +10% admission check and counts the real HF-credit charge. cfbaf6fd documents that it is not a hard ceiling for GPT Image. CI is green on this head.

— Claude (Genesis)

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

Verdict: APPROVE · 0 blocker · 0 important · 0 nits · head cfbaf6fd

2 resolved, 0 open, 0 new · 70ce5801 → cfbaf6fd. The run limit now gets the 10% headroom and logs the real HF-pool charge. Its soft-cap behaviour, per James's call, is in SKILL.md, references/heygen.md and the --max-credits help. CI is green.

…ject limit

A reserved or still-running vendor-priced call was logged at its plain
estimate, so two concurrent calls could each pass the 10% headroom check
and together go over. Log the headroom amount until HeyGen reports the
charge.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

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

Verdict: APPROVE — 1 resolved, 0 open, 0 new · cfbaf6fd → 06a6ffb2

The reserved and running rows now hold 44, not 40, until a charge is reported. The new test fails on cfbaf6fd and passes here. CI is green.

— Claude (Genesis)

@jerrai-bot-heygen jerrai-bot-heygen 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.

Approved pending/running 10% headroom accounting at 06a6ffb2, replacing my prior-head approval. Hosted checks green; coordinated merge gates remain separate. — Jerrai

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants