feat: add Replicate video task adaptor - #3019
Conversation
* fix: test using the correct path for rerank. * fix: The `input` parameter for testing responses uses an array to accommodate certain channels, such as Codex, which are incompatible with single strings.
* feat: channel affinity * feat: channel affinity -> model setting * fix: channel affinity * feat: channel affinity op * feat: channel_type setting * feat: clean * feat: cache supports both memory and Redis. * feat: Optimise ui/ux * feat: Optimise ui/ux * feat: Optimise codex usage ui/ux * feat: Optimise ui/ux * feat: Optimise ui/ux * feat: Optimise ui/ux * feat: If the affinitized channel fails and a retry succeeds on another channel, update the affinity to the successful channel
* feat: openai response /v1/response/compact * feat: /v1/response/compact bill * feat: /v1/response/compact * feat: /v1/responses/compact -> codex channel * feat: /v1/responses/compact -> codex channel * feat: /v1/responses/compact -> codex channel * feat: codex channel default models * feat: compact model price * feat: /v1/responses/comapct test
* feat: grok Usage Guidelines Violation Fee ui setting * feat: grok Usage Guidelines Violation Fee consume log * fix: grok Usage Guidelines Violation Fee log detail
…nes and vulnerability reporting
Updated the reporting method for security issues to include a link to the draft security advisory.
feat(gemini): map OpenAI stop to Gemini stopSequences
fix: remove disable_parallel_tool_use if tool_choice=none
…t-price fix: /v1/responses/compact default billing
* feat: 引入通用 HTTP BodyStorage/DiskCache 缓存配置与管理 - 新增 common/body_storage.go 提供 HTTP 请求体存储抽象和文件缓存能力 - 增加 common/disk_cache_config.go 支持全局磁盘缓存配置 - main.go 挂载缓存初始化流程 - 新增和补充 controller/performance.go (及 unix/windows) 用于缓存性能监控接口 - middleware/body_cleanup.go 自动清理缓存文件 - router 挂载相关接口 - 前端 settings 页面新增性能监控设置 PerformanceSetting - 优化缓存开关状态和模块热插拔能力 - 其他相关文件同步适配缓存扩展 * fix: 修复 BodyStorage 并发安全和错误处理问题 - 修复 diskStorage.Close() 竞态条件,先获取锁再执行 CAS - 为 memoryStorage 添加互斥锁和 closed 状态检查 - 修复 CreateBodyStorageFromReader 在磁盘存储失败时的回退逻辑 - 添加缓存命中统计调用 (IncrementDiskCacheHits/IncrementMemoryCacheHits) - 修复 gin.go 中 Seek 错误被忽略的问题 - 在 api-router 添加 BodyStorageCleanup 中间件 - 修复前端 formatBytes 对异常值的处理 Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
…uantumNous#2793) Explicitly cast Blocks, Bavail, and Bfree to uint64 for cross-platform compatibility, as these fields are int64 on FreeBSD but uint64 on Linux.
feat: Support customizing the success and cancel url of Stripe.
…65bf72572ff8684dd7ef068e576 feat: doubao add first and last image to video
…d7331d0987d96dc78bae181e331 feat: task pre consume modelPrice default use setting value
…3b7b97dbdbd98ade9b372bd6f63 feat: CodeViewer click link and auto wrap
…ling feat(gemini): support cached token billing
fix(ui): use distinct color palette for group tags
* fix: claude affinity cache counter * fix: claude affinity cache counter * fix: stabilize cache usage stats format and simplify modal rendering
* feat: add missing OpenAI/Claude/Gemini request fields and responses stream options * fix: skip field filtering when request passthrough is enabled * fix: include subscription in personal sidebar module controls * feat: gate Claude inference_geo passthrough behind channel setting and add field docs
…-with-proxy feat: codex oauth proxy
…ive-claude feat: minimax native /v1/messages
…-token fix: unify usage mapping and include toolUsePromptTokenCount
- Added caching for the original Content-Type header in the parseMultipartFormData function. - This change ensures that the Content-Type is retrieved from the context if previously set, enhancing performance and consistency.
…heck fix: violation fee check
…-encoding-passthrough fix: skip Accept-Encoding during header passthrough (QuantumNous#2214)
…detail feat: move user bindings to dedicated management modal
- Introduced a new test file for StreamScannerHandler, covering various scenarios including nil inputs, empty bodies, chunk processing, order preservation, and handler failures. - Enhanced error handling and data processing logic in StreamScannerHandler to improve robustness and performance.
feat(web): add custom-model create hint and i18n translations
…uting - Introduced RouteTag middleware to set route tags for different API endpoints. - Updated logger to include route tags in log output. - Applied RouteTag middleware across various routers including API, dashboard, relay, video, and web routers for consistent logging.
Support video generation through Replicate platform for models:
- kwaivgi/kling-v2.5-turbo-pro (Kling 2.5)
- wan-video/wan-2.2-t2v-fast (Wan 2.2)
- luma/ray (Luma Dream Machine)
- minimax/video-01 (MiniMax Video)
API flow:
1. POST /v1/models/{model}/predictions - create task
2. GET /v1/predictions/{id} - poll status
3. Return video URL on success
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
WalkthroughAdds a complete Replicate channel integration for task handling, including a new TaskAdaptor implementation that manages request construction, API communication, response parsing, and conversion to OpenAI-compatible video format, alongside supporting constants and routing wiring. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client Request
participant Adaptor as Replicate TaskAdaptor
participant ReplicateAPI as Replicate API
participant Response as Response Handler
Client->>Adaptor: ValidateRequestAndSetAction()
Adaptor->>Adaptor: Validate task request
Client->>Adaptor: BuildRequestURL()
Adaptor-->>Client: Replicate API URL
Client->>Adaptor: BuildRequestHeader()
Adaptor-->>Client: Auth + Content-Type headers
Client->>Adaptor: BuildRequestBody()
Adaptor->>Adaptor: Map size to aspect_ratio<br/>Build input payload
Adaptor-->>Client: JSON request body
Client->>Adaptor: DoRequest()
Adaptor->>ReplicateAPI: POST with request
ReplicateAPI-->>Adaptor: Prediction response
Adaptor->>Response: DoResponse()
Response->>Response: Parse predictionResponse<br/>Extract output URL
Response->>Adaptor: ConvertToOpenAIVideo()
Adaptor->>Adaptor: Map to OpenAI format
Adaptor-->>Client: OpenAI-compatible video response
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@relay/channel/task/replicate/adaptor.go`:
- Around line 92-93: The direct type assertion req :=
v.(relaycommon.TaskSubmitReq) can panic if v is a pointer or another type;
change it to a safe check that handles both value and pointer forms and returns
an error instead of panicking: use a type switch or two-step assertion (e.g., if
r, ok := v.(relaycommon.TaskSubmitReq); ok { req = r } else if rp, ok :=
v.(*relaycommon.TaskSubmitReq); ok { req = *rp } else { return
fmt.Errorf("unexpected task_request type: %T", v) }), updating the function that
currently performs this assertion so it returns the error path when the type is
not recognized.
- Around line 262-267: The current mapping only sets openAIVideo.Error when
prediction.Status == "failed"; update the condition in the replicate result
handling (the block that assigns to openAIVideo and dto.OpenAIVideoError) to
treat canceled tasks as failures as well (e.g., check prediction.Status ==
"failed" || prediction.Status == "canceled"), and ensure the
dto.OpenAIVideoError.Message uses prediction.Error or a sensible fallback like
"task canceled" when prediction.Error is empty so canceled tasks surface an
error payload.
- Around line 130-170: In TaskAdaptor.DoResponse validate the upstream HTTP
status and that the parsed prediction.ID is present before sending 200 to the
client: check resp.StatusCode is in the 2xx range and prediction.ID is not
empty, and if either check fails produce a TaskError (use
service.TaskErrorWrapper or TaskErrorWrapperLocal with an appropriate code like
"task_failed" or "upstream_error") and return without calling c.JSON; only
create and return dto.NewOpenAIVideo (using info.PublicTaskID and
info.OriginModelName) when both the upstream status and prediction.ID indicate a
successful, pollable task.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
relay/channel/task/replicate/adaptor.gorelay/channel/task/replicate/constants.gorelay/relay_adaptor.go
| req := v.(relaycommon.TaskSubmitReq) | ||
|
|
There was a problem hiding this comment.
Guard task_request assertion to prevent panic.
Line 92 can panic if upstream middleware stores a pointer/value variant. Return an error instead of crashing request handling.
Suggested fix
- req := v.(relaycommon.TaskSubmitReq)
+ var req relaycommon.TaskSubmitReq
+ switch typed := v.(type) {
+ case relaycommon.TaskSubmitReq:
+ req = typed
+ case *relaycommon.TaskSubmitReq:
+ if typed == nil {
+ return nil, fmt.Errorf("invalid task_request type")
+ }
+ req = *typed
+ default:
+ return nil, fmt.Errorf("invalid task_request type")
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| req := v.(relaycommon.TaskSubmitReq) | |
| var req relaycommon.TaskSubmitReq | |
| switch typed := v.(type) { | |
| case relaycommon.TaskSubmitReq: | |
| req = typed | |
| case *relaycommon.TaskSubmitReq: | |
| if typed == nil { | |
| return nil, fmt.Errorf("invalid task_request type") | |
| } | |
| req = *typed | |
| default: | |
| return nil, fmt.Errorf("invalid task_request type") | |
| } | |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@relay/channel/task/replicate/adaptor.go` around lines 92 - 93, The direct
type assertion req := v.(relaycommon.TaskSubmitReq) can panic if v is a pointer
or another type; change it to a safe check that handles both value and pointer
forms and returns an error instead of panicking: use a type switch or two-step
assertion (e.g., if r, ok := v.(relaycommon.TaskSubmitReq); ok { req = r } else
if rp, ok := v.(*relaycommon.TaskSubmitReq); ok { req = *rp } else { return
fmt.Errorf("unexpected task_request type: %T", v) }), updating the function that
currently performs this assertion so it returns the error path when the type is
not recognized.
| func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, taskErr *dto.TaskError) { | ||
| responseBody, err := io.ReadAll(resp.Body) | ||
| if err != nil { | ||
| taskErr = service.TaskErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError) | ||
| return | ||
| } | ||
| _ = resp.Body.Close() | ||
|
|
||
| var prediction predictionResponse | ||
| err = common.Unmarshal(responseBody, &prediction) | ||
| if err != nil { | ||
| taskErr = service.TaskErrorWrapper(errors.Wrap(err, fmt.Sprintf("%s", responseBody)), "unmarshal_response_failed", http.StatusInternalServerError) | ||
| return | ||
| } | ||
|
|
||
| // Check for error in response | ||
| if prediction.Error != "" { | ||
| taskErr = service.TaskErrorWrapperLocal(fmt.Errorf("replicate error: %s", prediction.Error), "task_failed", http.StatusBadRequest) | ||
| return | ||
| } | ||
|
|
||
| // Check if task already failed | ||
| if prediction.Status == "failed" || prediction.Status == "canceled" { | ||
| errMsg := prediction.Error | ||
| if errMsg == "" { | ||
| errMsg = fmt.Sprintf("task status: %s", prediction.Status) | ||
| } | ||
| taskErr = service.TaskErrorWrapperLocal(fmt.Errorf(errMsg), "task_failed", http.StatusBadRequest) | ||
| return | ||
| } | ||
|
|
||
| // Return success response to client | ||
| ov := dto.NewOpenAIVideo() | ||
| ov.ID = info.PublicTaskID | ||
| ov.TaskID = info.PublicTaskID | ||
| ov.CreatedAt = time.Now().Unix() | ||
| ov.Model = info.OriginModelName | ||
| c.JSON(http.StatusOK, ov) | ||
|
|
||
| // Return upstream task ID and response body | ||
| return prediction.ID, responseBody, nil |
There was a problem hiding this comment.
Validate upstream success before acknowledging task creation.
This path can return 200 to the client even when upstream returned non-2xx or no prediction ID, which creates unpollable tasks.
Suggested fix
_ = resp.Body.Close()
+
+ if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
+ taskErr = service.TaskErrorWrapperLocal(
+ fmt.Errorf("replicate create prediction failed: status=%d body=%s", resp.StatusCode, string(responseBody)),
+ "upstream_request_failed",
+ http.StatusBadGateway,
+ )
+ return
+ }
var prediction predictionResponse
err = common.Unmarshal(responseBody, &prediction)
@@
// Check if task already failed
if prediction.Status == "failed" || prediction.Status == "canceled" {
@@
return
}
+
+ if prediction.ID == "" {
+ taskErr = service.TaskErrorWrapperLocal(
+ fmt.Errorf("replicate response missing prediction id"),
+ "invalid_upstream_response",
+ http.StatusBadGateway,
+ )
+ return
+ }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@relay/channel/task/replicate/adaptor.go` around lines 130 - 170, In
TaskAdaptor.DoResponse validate the upstream HTTP status and that the parsed
prediction.ID is present before sending 200 to the client: check resp.StatusCode
is in the 2xx range and prediction.ID is not empty, and if either check fails
produce a TaskError (use service.TaskErrorWrapper or TaskErrorWrapperLocal with
an appropriate code like "task_failed" or "upstream_error") and return without
calling c.JSON; only create and return dto.NewOpenAIVideo (using
info.PublicTaskID and info.OriginModelName) when both the upstream status and
prediction.ID indicate a successful, pollable task.
| if prediction.Status == "failed" && prediction.Error != "" { | ||
| openAIVideo.Error = &dto.OpenAIVideoError{ | ||
| Message: prediction.Error, | ||
| Code: "replicate_error", | ||
| } | ||
| } |
There was a problem hiding this comment.
Include canceled-task failures in OpenAI error payload.
Line 262 only maps failed; canceled tasks are also failure states and should surface an error message.
Suggested fix
- // Set error info if failed
- if prediction.Status == "failed" && prediction.Error != "" {
- openAIVideo.Error = &dto.OpenAIVideoError{
- Message: prediction.Error,
- Code: "replicate_error",
- }
- }
+ // Set error info for all failure states
+ if originTask.Status == model.TaskStatusFailure {
+ msg := prediction.Error
+ if msg == "" {
+ msg = originTask.Reason
+ }
+ if msg != "" {
+ openAIVideo.Error = &dto.OpenAIVideoError{
+ Message: msg,
+ Code: "replicate_error",
+ }
+ }
+ }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@relay/channel/task/replicate/adaptor.go` around lines 262 - 267, The current
mapping only sets openAIVideo.Error when prediction.Status == "failed"; update
the condition in the replicate result handling (the block that assigns to
openAIVideo and dto.OpenAIVideoError) to treat canceled tasks as failures as
well (e.g., check prediction.Status == "failed" || prediction.Status ==
"canceled"), and ensure the dto.OpenAIVideoError.Message uses prediction.Error
or a sensible fallback like "task canceled" when prediction.Error is empty so
canceled tasks surface an error payload.
|
Has billing been adapted? |
Summary
Add support for video generation through Replicate platform, enabling access to multiple video models via a single channel.
Supported Models
kwaivgi/kling-v2.5-turbo-pro- Kling 2.5 Turbo Pro (high quality, ~$0.30/video)wan-video/wan-2.2-t2v-fast- Wan 2.2 T2V Fast (fast & cheap, ~$0.04/video)luma/ray- Luma Dream Machineminimax/video-01- MiniMax VideoAPI Flow
POST /v1/models/{model}/predictions- Create prediction taskGET /v1/predictions/{id}- Poll for statusChanges
relay/channel/task/replicate/adaptor.go- Task adaptor implementation (~300 lines)relay/channel/task/replicate/constants.go- Model list and channel namerelay/relay_adaptor.go- Register Replicate task adaptor (Type 56)Testing
🤖 Generated with Claude Code
Summary by CodeRabbit