112 Commits
Author SHA1 Message Date
CaIon ea4f021012 refactor(relay): move replay metadata onto request bodies 2026-08-06 17:33:10 +08:00
LucasandClaude Fable 5 d6b5ce99de fix(relay): set Request.GetBody so the HTTP/2 transport can transparently retry after an upstream stream reset (#6249)
* fix(relay): set Request.GetBody so the HTTP/2 transport can transparently retry after an upstream stream reset

The outbound request body is a type-erased io.Reader over BodyStorage, so
net/http cannot derive Request.GetBody (it only does so for *bytes.Reader,
*bytes.Buffer and *strings.Reader). With GetBody nil, the HTTP/2 transport
cannot transparently retry a request once the body has been written and the
upstream resets the stream with a retryable error (REFUSED_STREAM, or a
connection-level GOAWAY); the relay request then fails with:

    http2: Transport: cannot retry err [...] after Request.Body was written;
    define Request.GetBody to avoid this error

This affects every relay path that goes through DoApiRequest (chat, claude,
gemini, responses, embedding, image, rerank).

BodyStorage (memory and disk) already implements io.Seeker, so replay support
only needed wiring:

- NewOutboundJSONBody additionally returns a getBody that rewinds the storage
  and hands out a fresh non-closing reader. The transport only calls GetBody
  after the previous attempt's body has been abandoned, so the rewind cannot
  race an in-flight read.
- RelayInfo carries it in the new UpstreamRequestGetBody field, set alongside
  UpstreamRequestBodySize by the handlers that build storage-backed bodies.
- applyUpstreamGetBody (symmetric with applyUpstreamContentLength) wires it
  into DoApiRequest/DoFormRequest/DoTaskApiRequest, only when req.GetBody is
  still nil.

Also remove the hand-rolled GetBody override in DoTaskApiRequest: it returned
the same already-consumed reader, so any transport-level replay would have
silently sent an empty body, and it clobbered the correct snapshot-based
GetBody that net/http derives from the *bytes.Reader bodies the task adaptors
pass in. For non-replayable bodies GetBody now stays nil, so a retry fails
loudly instead of corrupting the request.

Covered by unit tests plus an end-to-end raw-frame HTTP/2 test that resets
the first stream with REFUSED_STREAM after the body is written and asserts
the transport transparently retries with the complete body.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(relay): hand out independent readers from GetBody (address review)

Per the http.Request.GetBody contract ("returns a new copy of Body"),
each call must yield a reader with its own cursor. The previous
implementation rewound and reused the shared BodyStorage, so two
consecutive GetBody readers would interfere with each other, and a
replay could disturb the primary body's offset under extreme transport
timing (e.g. attempt N's body write not yet fully abandoned when the
transport builds attempt N+1).

Instead of snapshotting the payload (an extra copy), add
BodyStorage.NewReader, which returns an independent zero-copy reader:

- memory mode: a fresh bytes.Reader over the same immutable backing
  array;
- disk mode: a separate file descriptor over the cache file, so the
  transport closing a replayed body only closes that descriptor.

NewOutboundJSONBody's getBody now simply hands out storage.NewReader,
and once the handler releases the storage, GetBody fails with
ErrStorageClosed instead of replaying stale data.

Tests: interleaved reads across two replay readers and the primary
body each observe exactly their own byte stream, for both the memory
and the disk-backed storage; the existing GetBody and HTTP/2 retry
suites still pass (h2 e2e tests flake-free with -count=20).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(relay): bind replayable metadata on pass-through requests

* fix(relay): reset upstream body metadata between channels

* test(relay): cover replay across retries and channel attempts

* fix(relay): stop following upstream redirects

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 15:56:38 +08:00
Calcium-Ion 86ac0f7745 refactor: extract protocol conversion layer into standalone relaykit module (#6369)
* test(relayconvert): add golden snapshot matrix and relaykit boundary guard

Phase 0 of the relaykit extraction plan: pin byte-level output of every
registered (from,to) request/response/stream conversion route, and
forbid kit-bound packages from growing host-only imports.

* wip(relayconvert): drop gin.Context from converter signatures; add convmeta draft

Phase 1 in progress: relayconvert now takes context.Context; host media
resolver adapts gin.Context back at the service boundary.

* refactor(relayconvert): decouple converters from RelayInfo, gin, and settings

Phase 1 of the relaykit extraction plan:
- converters now depend on convmeta.Meta (implemented by RelayInfo) instead
  of *relaycommon.RelayInfo; ClaudeConvertInfo and the format guesser move
  to convmeta with aliases left behind
- host settings reach converters via a convmeta.Options snapshot built in
  RelayInfo.ConvOptions; no more model_setting/reasoning global reads inside
  the conversion layer
- effort-suffix helpers move to service/relayconvert/reasoning (old package
  forwards); chat-to-responses upgrade policy moves to service (host routing
  logic, not conversion)
- golden conversion matrix unchanged

* test(relayconvert): tighten boundary — kit packages now free of gin/setting imports

* refactor(dto): drop gin and logger dependencies

Phase 2 (part 1): dto.Request.IsStream now takes *http.Request instead of
*gin.Context (Gemini's impl reads query/path off the std request); dto's
three logger calls become common.SysError. Boundary test allowlist is now
empty — kit-bound packages import no gin/setting/logger/model.

* refactor(kit): extract dependency-free kitutil; dto/types/relayconvert stop importing common

Phase 2 of the relaykit extraction plan:
- new service/relayconvert/kitutil holds the pure helpers the kit needs
  (JSON wrappers, pointer/string/uuid/timestamp utils, MaskSensitiveInfo,
  pluggable LogInfo/LogError hooks, Debug flag)
- dto, types, and all relayconvert packages now use kitutil; their only
  remaining internal deps are dto/types/constant
- common keeps every original symbol (MaskSensitiveInfo delegates to
  kitutil) so host code is untouched; main.go routes kit logging into
  common.SysLog/SysError and mirrors DebugEnabled
- golden conversion matrix unchanged

* refactor(kit): move EndpointType/FinishReason to types; OpenRouter dialect via Options

Kit packages (dto/types/relayconvert/reasonmap) no longer import constant:
- EndpointType and finish-reason values live in types; constant re-exports
- the OpenRouter special-case in claude->openai request conversion reads
  Options.OpenRouterDialect, set by the host from the channel type;
  InitChannelMeta invalidates the cached snapshot on channel switch

* refactor: extract relaykit submodule (dto/types/relayconvert/reasonmap)

Phase 3 of the relaykit extraction plan:
- new go module github.com/QuantumNous/new-api/relaykit containing dto
  (minus task family), types, relayconvert (with convmeta/kitutil/reasoning),
  and reasonmap; host consumes it via require + replace, go.work for dev
- task-family dto (task/suno/midjourney/video) stays in the host dto
  package; dual-consumer host files alias it as taskdto
- relaykit builds and tests standalone (GOWORK=off): no host imports,
  no gin, no DB, no settings
- golden conversion matrix unchanged

* build(docker): copy relaykit/go.mod before go mod download

The local-replace submodule's go.mod must exist inside the build context
for the main module graph to resolve.

* fix: address relaykit extraction regressions

* fix: address relaykit review regressions

* docs: document Meta nil receiver contract

* fix(relaykit): fail OpenAI→Claude conversion without max_tokens; reject negative default_max_tokens

The Claude Messages API requires max_tokens (omitting it is a 400
"Field required"), but with a nil Options.Claude.DefaultMaxTokens hook
the converters silently emitted a request the upstream is guaranteed to
reject. Both OpenAI Chat and Responses → Claude conversions now return
sharedclaude.ErrMissingMaxTokens when no path (client value, default
hook, thinking-adapter floor) supplied one. Unreachable in the host,
which always configures the hook.

Host side, claude.default_max_tokens now rejects negative values at the
option API before persisting — they would wrap into huge unsigned values
during conversion. Zero stays allowed: the current API treats
max_tokens: 0 as cache pre-warming.

* fix: make Gemini safety settings read path race-free
2026-07-27 15:56:21 +08:00
CaIon c9943d37ad fix(billing): extend quantity validation and saturating conversions to remaining paths
Bound max-tokens fields across all relay format validators, saturate
tiered-expression rounding and audio/tool/task token conversions, and
route legacy remix ratios through the guarded setter.
2026-07-07 01:08:52 +08:00
CaIon d0bd8aac74 fix(billing): validate quantity parameters and harden quota calculations
Bound user-supplied count/duration parameters at request validation,
route ratio multipliers through guarded setters, and use saturating
int conversions in all quota math paths.
2026-07-07 00:21:06 +08:00
feitianbubu 043720f9be fix: 任务差额结算后 quota 和阿里视频时长优化 (#5923)
* fix: apply default ali video duration when value is non-positive

* fix: persist task quota after async settlement
2026-07-06 11:49:24 +08:00
Scott 52858ad1e6 feat: support Wan2.7 i2v media mapping (#4984)
* feat: support Wan2.7 i2v media mapping

* fix: normalize wan2.7 i2v image inputs
2026-07-01 21:21:41 +08:00
feitianbubu e514db20f7 feat: support doubao seedance 2.0 safety_identifier/priority and 4k billing (#5824)
* feat: support safety_identifier and priority for doubao video

* feat: add 4k resolution billing tier for doubao video
2026-06-30 22:16:14 +08:00
feitianbubu c8491b41bc feat: bill doubao seedance-2.0 by output resolution and video input (#5300) 2026-06-30 21:09:09 +08:00
yyhhyyyyyy 987b7ecd22 fix(vertex): honor custom base_url as gateway prefix 2026-04-30 15:08:10 +08:00
Seefs 274307b0a9 fix(ali): accept string usage values in task polling 2026-04-09 12:48:17 +08:00
CaIon 8fc0eb78e2 feat(billing): enhance task billing process with video input detection and updated pricing logic
- Added `EstimateBilling` function to check for video input in request metadata and return corresponding discount ratios.
- Updated `ModelPriceHelperPerCall` to incorporate new pricing logic based on model ratios and video input.
- Enhanced task billing logs to include model ratio information and adjusted calculations for actual quota based on additional multipliers.
- Introduced `renderTaskBillingProcess` to improve rendering of task billing information in the UI.
2026-04-06 15:54:55 +08:00
feitianbubu dafc7618c3 feat: add seedance fail reason 2026-04-02 12:26:44 +08:00
feitianbubu 22692b3f87 feat: seedance support seconds 2026-04-02 12:26:37 +08:00
feitianbubu d36e892905 fix: seedance only one text 2026-04-02 12:26:33 +08:00
feitianbubu 3cd1ba4673 fix: seedance metadata override prompt 2026-04-02 12:26:27 +08:00
feitianbubu b7c0f754ad feat: add seedance2.0 video api 2026-04-02 12:24:24 +08:00
feitianbubu 62b9aaa520 feat: prevent metadata from overriding model fields 2026-03-27 15:31:41 +08:00
feitianbubu 2e20ede2a0 fix: kling risk fail return openAIVideo error 2026-03-06 16:32:52 +08:00
Calcium-Ion 728607b8f5 Merge pull request #2769 from feitianbubu/pr/3d0aaa75866f8d958a777a7e7ac8c1e4b5b3e537
feat: kling cost quota support use FinalUnitDeduction as totalToken
2026-03-06 11:46:59 +08:00
CaIon e22f59e449 feat(gemini): update request structures for Veo predictLongRunning
- Refactored the request URL and body construction methods to align with the Veo predictLongRunning endpoint.
- Introduced new data structures for Veo instances and parameters, replacing the previous Gemini video generation configurations.
- Updated the Vertex adaptor to utilize the new Veo request payload format.
2026-02-28 18:42:54 +08:00
CaIon 32d9ae1f83 feat(gemini): implement video generation configuration and billing estimation
- Added Gemini video generation configuration structures and payloads.
- Introduced functions for parsing and resolving video duration and resolution from metadata.
- Enhanced the Vertex adaptor to support Gemini video generation requests and billing estimation based on duration and resolution.
- Updated model pricing settings for new Gemini video models.
2026-02-28 17:37:08 +08:00
CaIon 8f14687d61 fix: improve multipart form data handling by detecting content type. fix #3007 2026-02-25 12:51:46 +08:00
CaIon c1b05d3b5a refactor: clean up unused code and improve error logging in adaptor and mjp modules 2026-02-22 22:11:05 +08:00
CaIon 06fe03e34c feat(task): add model redirection, per-call billing, and multipart retry fix for async tasks
1. Async task model redirection (aligned with sync tasks):
   - Integrate ModelMappedHelper in RelayTaskSubmit after model name
     determination, populating OriginModelName / UpstreamModelName on RelayInfo.
   - All task adaptors now send UpstreamModelName to upstream providers:
     - Gemini & Vertex: BuildRequestURL uses UpstreamModelName.
     - Doubao & Ali: BuildRequestBody conditionally overwrites body.Model.
     - Vidu, Kling, Hailuo, Jimeng: convertToRequestPayload accepts RelayInfo
       and unconditionally uses info.UpstreamModelName.
     - Sora: BuildRequestBody parses JSON and multipart bodies to replace
       the "model" field with UpstreamModelName.
   - Frontend log visibility: LogTaskConsumption and taskBillingOther now
     emit is_model_mapped / upstream_model_name in the "other" JSON field.
   - Billing safety: RecalculateTaskQuotaByTokens reads model name from
     BillingContext.OriginModelName (via taskModelName) instead of
     task.Data["model"], preventing billing leaks from upstream model names.

2. Per-call billing (TaskPricePatches lifecycle):
   - Rename TaskBillingContext.ModelName → OriginModelName; add PerCallBilling
     bool field, populated from TaskPricePatches at submission time.
   - settleTaskBillingOnComplete short-circuits when PerCallBilling is true,
     skipping both adaptor adjustments and token-based recalculation.
   - Remove ModelName from TaskSubmitResult; use relayInfo.OriginModelName
     consistently in controller/relay.go for billing context and logging.

3. Multipart retry boundary mismatch fix:
   - Root cause: after Sora (or OpenAI audio) rebuilds a multipart body with a
     new boundary and overwrites c.Request.Header["Content-Type"], subsequent
     calls to ParseMultipartFormReusable on retry would parse the cached
     original body with the wrong boundary, causing "NextPart: EOF".
   - Fix: ParseMultipartFormReusable now caches the original Content-Type in
     gin context key "_original_multipart_ct" on first call and reuses it for
     all subsequent parses, making multipart parsing retry-safe globally.
   - Sora adaptor reverted to the standard pattern (direct header set/get),
     which is now safe thanks to the root fix.

4. Tests:
   - task_billing_test.go: update makeTask to use OriginModelName; add
     PerCallBilling settlement tests (skip adaptor adjust, skip token recalc);
     add non-per-call adaptor adjustment test with refund verification.
2026-02-22 16:33:00 +08:00
CaIon 7d5fc3ff51 refactor(relay): rename RelayTask to RelayTaskFetch and update routing
- Renamed RelayTask function to RelayTaskFetch for clarity.
- Updated routing in relay-router.go and video-router.go to use RelayTaskFetch for fetch operations.
- Enhanced error handling in RelayTaskFetch function.
- Adjusted task data conversion in TaskAdaptor to include task ID.
2026-02-22 16:01:19 +08:00
CaIon 8374a83084 feat(task): add adaptor billing interface and async settlement framework
Add three billing lifecycle methods to the TaskAdaptor interface:
- EstimateBilling: compute OtherRatios from user request before pricing
- AdjustBillingOnSubmit: adjust ratios from upstream submit response
- AdjustBillingOnComplete: determine final quota at task terminal state

Introduce BaseBilling as embeddable no-op default for adaptors without
custom billing. Move Sora/Ali OtherRatios logic from shared validation
into per-adaptor EstimateBilling implementations.

Add TaskBillingContext to persist pricing params (model_price, group_ratio,
other_ratios) in task private data for async polling settlement.

Extract RecalculateTaskQuota as a general-purpose delta settlement
function and unify polling billing via settleTaskBillingOnComplete
(adaptor-first, then token-based fallback).
2026-02-22 16:00:27 +08:00
CaIon ba25ba88fe refactor(task): extract billing and polling logic from controller to service layer
Restructure the task relay system for better separation of concerns:
- Extract task billing into service/task_billing.go with unified settlement flow
- Move task polling loop from controller to service/task_polling.go (supports Suno + video platforms)
- Split RelayTask into fetch/submit paths with dedicated retry logic (taskSubmitWithRetry)
- Add TaskDto, TaskResponse generics, and FetchReq to dto/task.go
- Add taskcommon/helpers.go for shared task adaptor utilities
- Remove controller/task_video.go (logic consolidated into service layer)
- Update all task adaptors (ali, doubao, gemini, hailuo, jimeng, kling, sora, suno, vertex, vidu)
- Simplify frontend task logs to use new TaskDto response format
2026-02-22 16:00:27 +08:00
CaIon 29d48e262e feat: refactor request body handling to use BodyStorage for improved efficiency 2026-02-12 01:51:27 +08:00
feitianbubu 88b7322483 feat: kling cost quota support use FinalUnitDeduction as totalToken 2026-01-28 18:42:13 +08:00
feitianbubu 0823c16f97 feat: doubao add first and last image to video 2026-01-27 17:33:00 +08:00
feitianbubu d95f334c63 fix: jimeng i2v support multi image by metadata 2026-01-19 11:34:46 +08:00
feitianbubu c7ebd4408a feat: add doubao video 1.5 2026-01-10 22:23:31 +08:00
Seefs b2e52260e1 Merge pull request #2532 from feitianbubu/pr/620211e02bd55545f0fa4568f3d55c3b4d7f3305 2026-01-03 09:36:17 +08:00
CaIon 62020d00a4 feat(adaptor): update resolution handling for wan2.6 model 2025-12-31 00:44:06 +08:00
Seefs 5423d6ed8c feat: Add "wan2.6-i2v" video ratio configuration to Ali adaptor. 2025-12-29 14:13:33 +08:00
feitianbubu d014e0b471 fix: kling correct fail reason 2025-12-26 16:35:46 +08:00
t0ng7u c2ed76ddfd 🛡️ fix: prevent OOM on large/decompressed requests; skip heavy prompt meta when token count is disabled
Clamp request body size (including post-decompression) to avoid memory exhaustion caused by huge payloads/zip bombs, especially with large-context Claude requests. Add a configurable `MAX_REQUEST_BODY_MB` (default `32`) and document it.

- Enforce max request body size after gzip/br decompression via `http.MaxBytesReader`
- Add a secondary size guard in `common.GetRequestBody` and cache-safe handling
- Return **413 Request Entity Too Large** on oversized bodies in relay entry
- Avoid building large `TokenCountMeta.CombineText` when both token counting and sensitive check are disabled (use lightweight meta for pricing)
- Update READMEs (CN/EN/FR/JA) with `MAX_REQUEST_BODY_MB`
- Fix a handful of vet/formatting issues encountered during the change
- `go test ./...` passes
2025-12-16 17:00:19 +08:00
Seefs ee53a7b6bf Merge pull request #2412 from seefs001/pr-2372
feat: add openai video remix endpoint
2025-12-11 23:35:23 +08:00
Seefs 920e005048 fix: Use channel proxy settings for task query scenarios 2025-12-09 11:15:27 +08:00
CaIon d4fbe1cee9 fix(vertex): ensure sampleCount is a positive integer and update OtherRatios 2025-11-30 19:05:33 +08:00
Seefs b18704c320 Revert "Gemini Veo3.1[AI Studio]增加图生视频支持" 2025-11-30 18:45:37 +08:00
feitianbubu 70d74d74a4 feat: gemini video veo3.1 add task fail check 2025-11-26 21:56:14 +08:00
feitianbubu ff19082c84 feat: gemini video veo3.1 add i2v 2025-11-26 21:56:13 +08:00
feitianbubu aa2f027dea feat: add hailuo i2v fl2v r2v 2025-11-14 11:55:43 +08:00
feitianbubu c297f42c1a feat: get hailuo video url 2025-11-14 11:55:43 +08:00
feitianbubu b242d4c754 feat: add MiniMax Hailuo video 2025-11-14 11:55:43 +08:00
feitianbubu 29c719279a feat: vidu reference2video only viduq2 2025-11-10 16:37:27 +08:00
feitianbubu d0f13fab1c feat: vidu specify reference2video via metadata action 2025-11-10 16:37:26 +08:00
Seefs 06260228d4 Merge pull request #2168 from feitianbubu/pr/fix-jimeng-1080p-image
fix: trim suffix p for jimeng image model
2025-11-06 19:54:02 +08:00