Skip to content

feat: add Replicate video task adaptor - #3019

Closed
smartchainark wants to merge 5279 commits into
QuantumNous:mainfrom
smartchainark:contrib/replicate-video
Closed

feat: add Replicate video task adaptor#3019
smartchainark wants to merge 5279 commits into
QuantumNous:mainfrom
smartchainark:contrib/replicate-video

Conversation

@smartchainark

@smartchainark smartchainark commented Feb 25, 2026

Copy link
Copy Markdown

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 Machine
  • minimax/video-01 - MiniMax Video

API Flow

  1. POST /v1/models/{model}/predictions - Create prediction task
  2. GET /v1/predictions/{id} - Poll for status
  3. Return video URL on success

Changes

  • New: relay/channel/task/replicate/adaptor.go - Task adaptor implementation (~300 lines)
  • New: relay/channel/task/replicate/constants.go - Model list and channel name
  • Modified: relay/relay_adaptor.go - Register Replicate task adaptor (Type 56)

Testing

  • Code compiles successfully
  • Follows existing adaptor patterns (vidu, gemini, kling)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added Replicate integration for video generation tasks. Users can submit video generation requests through Replicate with support for multiple models, automatic status tracking, and response conversion to standard OpenAI-compatible formats. Includes built-in error handling and progress monitoring for generated content.

seefs001 and others added 30 commits January 26, 2026 19:57
* 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
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
seefs001 and others added 25 commits February 22, 2026 23:30
* 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
…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.
…-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>
@coderabbitai

coderabbitai Bot commented Feb 25, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Adds 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

Cohort / File(s) Summary
Replicate Adaptor Implementation
relay/channel/task/replicate/adaptor.go
New TaskAdaptor struct with lifecycle methods (Init, ValidateRequestAndSetAction), request building (BuildRequestURL, BuildRequestHeader, BuildRequestBody), API interaction (DoRequest, FetchTask), response handling (DoResponse, ParseTaskResult), and data conversion (ConvertToOpenAIVideo). Includes helper functions for URL extraction and aspect ratio mapping.
Configuration Constants
relay/channel/task/replicate/constants.go
Introduces ModelList with four Replicate model identifiers and ChannelName constant set to "replicate".
Routing and Registration
relay/relay_adaptor.go
Wires Replicate channel support into GetAdaptor and GetTaskAdaptor dispatcher methods to instantiate appropriate Replicate adaptor instances based on API type and channel type.

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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • seefs001
  • Calcium-Ion
  • creamlike1024

Poem

🐰 Hop, hop, hooray! A Replicate gateway,
Where Replicate flows through our relay,
Headers and bodies dance in delight,
Converting predictions to OpenAI bright,
New models await on the furry highway! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a Replicate video task adaptor implementation with complete request/response handling and model support.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between 982dc5c and e14727d.

📒 Files selected for processing (3)
  • relay/channel/task/replicate/adaptor.go
  • relay/channel/task/replicate/constants.go
  • relay/relay_adaptor.go

Comment on lines +92 to +93
req := v.(relaycommon.TaskSubmitReq)

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.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
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.

Comment on lines +130 to +170
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

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.

⚠️ Potential issue | 🔴 Critical

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.

Comment on lines +262 to +267
if prediction.Status == "failed" && prediction.Error != "" {
openAIVideo.Error = &dto.OpenAIVideoError{
Message: prediction.Error,
Code: "replicate_error",
}
}

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.

⚠️ Potential issue | 🟡 Minor

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.

@Calcium-Ion

Copy link
Copy Markdown
Member

Has billing been adapted?

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.