The taskdoubao adaptor expected snake_case JSON keys (video_url,
completion_tokens, total_tokens, framespersecond, service_tier,
created_at, updated_at) but Volcengine returns camelCase (videoUrl,
completionTokens, totalTokens, framesPerSecond, serviceTier,
createdAt, updatedAt). As a result the video URL was never parsed:
successful tasks fell back to the platform proxy URL
(BuildProxyURL) as result_url, which broke the video proxy in
containerized deployments (localhost:port not reachable inside the
container) and zeroed the usage tokens used for billing.
Align the responseTask JSON tags with the real Volcengine response
shape and add a regression test with the exact production payload.
Co-Authored-By: ZCode <noreply@anthropic.com>
The native /api/v3/contents/generations/tasks route was restricted to
the seedance asset channel family (58/60/61); official Volcengine Ark
channels (DoubaoVideo, type 54) could only be reached via the
standardized /v1/video/generations path. Allow type 54 on the native
route in both the distributor and the task retry channel selection,
while keeping it out of the seedance asset binding system.
The taskdoubao adaptor now reuses a pre-parsed task request from the
context (native path) instead of re-parsing the body as TaskSubmitReq,
whose prompt validation would reject the Volcengine-native format.
When req.Prompt is empty (native path), content text items from
metadata are preserved instead of being replaced with an empty prompt;
the standardized path behavior (prompt replaces metadata text) is
unchanged and covered by existing tests.
Co-Authored-By: ZCode <noreply@anthropic.com>
Register the Seedance mini model in the matrix usage capability table
(both native name and doubao-seedance-2.0 alias) and keep
SupportsAnyMatrixUsageBillingModel in sync so the model bills via
matrix usage on the ChinaMobile Seedance channel.
Co-Authored-By: ZCode <noreply@anthropic.com>
Add KlingAiping usage-token settlement from upstream final_unit_deduction and duration fallback handling.
Normalize model endpoint config persistence and convert model pricing values between stored USD and display currency in the editor.
Co-Authored-By: Codex <noreply@anthropic.com>
Add unit tests for group ratio, cross-group retry, and price calculation:
- Group ratio CRUD and special ratio override (12 tests)
- HandleGroupRatio with auto-group and special ratios (5 tests)
- ModelPriceHelper with group ratio in price/ratio modes (3 tests)
- Channel selection priority, groups, weighted LB (6 tests)
- RetryParam and AutoGroup service logic (7 tests)
Also restore the group pricing card in model detail SideSheet.
Replace bytes.Replace with gjson.GetBytes+sjson.SetBytes in passthrough
mode to correctly handle JSON whitespace variations and avoid silently
sending empty body on read errors. Also add log search count limit to
prevent slow COUNT queries on large log tables.
Use adaptive thinking for Claude requests and rewrite passthrough bodies that still contain the legacy enabled type.
Co-Authored-By: Codex <noreply@anthropic.com>
Delete leftover channel pricing controllers and routes after removing channel pricing, and align relay price helpers with global pricing plus user channel ratio only.
Co-Authored-By: Codex <noreply@anthropic.com>
gpt-5.4 returns `arguments` as a JSON object for some tools (e.g.
apply_patch, tool_search), but ResponsesOutput.Arguments was typed as
string, causing json.Unmarshal to fail on every such SSE event.
The failure silently dropped all output_item events and the
response.completed event from the forwarded stream, so:
- the client never received tool call content
- usage/token counts could not be extracted (billed as 0)
- client reported "stream closed before response.completed"
Fix: change Arguments to json.RawMessage and add GetArguments() which
normalises both forms to a plain string (unescapes JSON strings,
returns raw bytes for objects).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DoApiRequest previously called io.ReadAll on every request body to apply
passthrough model mapping, even when the feature was disabled. This
caused unnecessary memory pressure for large request bodies (long
conversations, multipart image uploads).
Split the guard logic into needsPassthroughModelMapping() so that
ReadAll is only called when all conditions are met (feature enabled +
passthrough mode + model mapped). Non-passthrough requests now pass
the original io.Reader directly to http.NewRequest without buffering.
Rewrite tests to verify combined guard + modify behavior.