Skip to content

Feat:豆包视频端点支持、视频模型计费支持、异步任务页面优化 - #713

Open
NyaDoo wants to merge 2 commits into
fawney19:mainfrom
NyaDoo:feat/doubao-video-endpoint
Open

Feat:豆包视频端点支持、视频模型计费支持、异步任务页面优化#713
NyaDoo wants to merge 2 commits into
fawney19:mainfrom
NyaDoo:feat/doubao-video-endpoint

Conversation

@NyaDoo

@NyaDoo NyaDoo commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

1、增加对豆包视频端点的原生支持。
2、修复视频模型计费BUG,优化视频模型计费维度。
3、异步任务页面优化。

LewisPen and others added 2 commits August 4, 2026 15:24
Expose Volcengine Ark's content generation task surface at
/v3/contents/generations/tasks as a third video contract alongside
openai:video and gemini:video. The three share the video_tasks table,
the polling worker and the billing pipeline, but keep separate client
contracts, route classification and plan kinds.

Request modeling keeps a minimal whitelist (model, content text, ratio,
resolution, duration, callback_url) and passes everything else through
verbatim, so Ark parameters such as generate_audio and multi-modal
content entries keep working without a gateway change.

Notable decisions:

- callback_url is rejected with 400. The gateway owns task state, so an
  upstream callback would bypass it and leak the upstream task id.
  Silently stripping it would leave clients waiting forever.
- The task list is served from gateway state rather than proxied, which
  would return every task owned by the shared provider key across all
  tenants.
- Downloads are proxied instead of handing out content.video_url, whose
  signed URL expires within a day.
- The upstream base URL is appended to verbatim; no API-root segment is
  inferred, so a misconfigured base stays visible in the resulting URL
  instead of being silently repaired.
- usage.completion_tokens is recorded as output_tokens, since Ark bills
  video generation by tokens unlike the per-second video surfaces.

Also reports upstream failures with their status code instead of a
generic "no execution path" miss. That applies to every AI surface, not
just Doubao, and only fires when all candidates actually reached an
upstream and were refused there.

A one-way openai:video to doubao:video request conversion is included so
Sora clients can target Doubao providers. The reverse is deliberately
not implemented: Ark requests carry reference video/audio, multiple
images and generate_audio, none of which Sora can express, so it would
fail on most real requests. Cross-format candidate selection is not
wired up yet, so the conversion is not reachable end to end.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Video tasks recorded their tokens but always settled at zero cost: the
admin UI wrote billing.video.price_per_second_by_resolution, yet no Rust
code ever read it. Nothing in the pipeline could turn a finished render
into a charge.

Video pricing lives in the model config rather than in tiered_pricing,
because it is keyed by rendered resolution instead of token counts. Two
billing modes are supported, and both vary by resolution and by whether
the request supplied a reference video:

- per_second charges a resolution price times the rendered duration.
- per_token charges resolution-keyed token rates. It does not fall back
  to the model-wide token tier catalog, whose only key is context size.

The modes are mutually exclusive at rule level, not by UI convention, so
editing the config through the API cannot produce double charging:
per_second zeroes the token rates and drops the tier tables, per_token
overrides the tier-resolved rates with dimension-sourced ones.

Each mode also takes a default price for resolutions the table omits,
mirroring image output pricing. Resolution and the reference-video flag
reach settlement as usage-event dimensions, the same path image_size
already used, so no data-layer change was needed.

Notable decisions:

- Sections resolve whole: the with_video_input row, then that section's
  default, then the base row, then the base default. A with_video_input
  block carrying only a default therefore covers every resolution rather
  than silently leaking back to base prices.
- An unlisted resolution with no configured default resolves to nothing
  instead of inventing a price, so a partial config underbills visibly
  rather than overbilling silently.
- Resolution keys are normalized to match the admin UI: providers report
  either a tier (720p) or pixels (1280x720), and a pixel pair is ordered
  smallest-first so portrait and landscape share one entry.
- The token short-circuit in calculate_with_resolution now lets video
  through. An empty tier catalog is the expected shape for a video model,
  and Doubao always reports tokens, so the guard would otherwise kill the
  charge before the video price was ever read.

Display had to learn the mode too. Switching modes deliberately keeps the
other mode's tables so operators can compare, which meant a model moved
to per_token kept showing its stale per-second price. The read logic is
now shared in utils/form.ts and gates on mode, and the model lists and
detail drawer render per-token video prices, which are absent from
default_tiered_pricing and previously left the price column empty.

The admin dialogs merge the base and with_video_input tables into one
grid, with the override as an optional column and the default as a pinned
row, and switch modes from a Repeat2 button matching the tiered editor.

Doubao video create/remix reports now defer settlement: an async job
answers 200 before its tokens are known, and settling then froze the
record at zero. Keeping it pending lets the terminal poll write tokens
first.

Existing configurations keep working: an absent mode means per_second.
Already-settled tasks are not backfilled.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant