mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-04-27 00:58:38 +00:00
feat: add hailuo i2v fl2v r2v
This commit is contained in:
@@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/QuantumNous/new-api/service"
|
"github.com/QuantumNous/new-api/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// https://platform.minimaxi.com/docs/api-reference/video-generation-intro
|
||||||
type TaskAdaptor struct {
|
type TaskAdaptor struct {
|
||||||
ChannelType int
|
ChannelType int
|
||||||
apiKey string
|
apiKey string
|
||||||
@@ -84,7 +85,7 @@ func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *rela
|
|||||||
}
|
}
|
||||||
_ = resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
|
|
||||||
var hResp TextToVideoResponse
|
var hResp VideoResponse
|
||||||
if err := json.Unmarshal(responseBody, &hResp); err != nil {
|
if err := json.Unmarshal(responseBody, &hResp); err != nil {
|
||||||
taskErr = service.TaskErrorWrapper(errors.Wrapf(err, "body: %s", responseBody), "unmarshal_response_body_failed", http.StatusInternalServerError)
|
taskErr = service.TaskErrorWrapper(errors.Wrapf(err, "body: %s", responseBody), "unmarshal_response_body_failed", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
@@ -136,86 +137,28 @@ func (a *TaskAdaptor) GetChannelName() string {
|
|||||||
return ChannelName
|
return ChannelName
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *TaskAdaptor) convertToRequestPayload(req *relaycommon.TaskSubmitReq) (*TextToVideoRequest, error) {
|
func (a *TaskAdaptor) convertToRequestPayload(req *relaycommon.TaskSubmitReq) (*VideoRequest, error) {
|
||||||
modelConfig := GetModelConfig(req.Model)
|
modelConfig := GetModelConfig(req.Model)
|
||||||
if !contains(ModelList, req.Model) {
|
|
||||||
return nil, fmt.Errorf("unsupported model: %s", req.Model)
|
|
||||||
}
|
|
||||||
|
|
||||||
duration := DefaultDuration
|
duration := DefaultDuration
|
||||||
if req.Duration > 0 {
|
if req.Duration > 0 {
|
||||||
duration = req.Duration
|
duration = req.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
if !containsInt(modelConfig.SupportedDurations, duration) {
|
|
||||||
return nil, fmt.Errorf("duration %d is not supported by model %s, supported durations: %v",
|
|
||||||
duration, req.Model, modelConfig.SupportedDurations)
|
|
||||||
}
|
|
||||||
|
|
||||||
resolution := modelConfig.DefaultResolution
|
resolution := modelConfig.DefaultResolution
|
||||||
if req.Size != "" {
|
if req.Size != "" {
|
||||||
resolution = a.parseResolutionFromSize(req.Size, modelConfig)
|
resolution = a.parseResolutionFromSize(req.Size, modelConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !contains(modelConfig.SupportedResolutions, resolution) {
|
videoRequest := &VideoRequest{
|
||||||
return nil, fmt.Errorf("resolution %s is not supported by model %s, supported resolutions: %v",
|
|
||||||
resolution, req.Model, modelConfig.SupportedResolutions)
|
|
||||||
}
|
|
||||||
|
|
||||||
hailuoReq := &TextToVideoRequest{
|
|
||||||
Model: req.Model,
|
Model: req.Model,
|
||||||
Prompt: req.Prompt,
|
Prompt: req.Prompt,
|
||||||
Duration: &duration,
|
Duration: &duration,
|
||||||
Resolution: resolution,
|
Resolution: resolution,
|
||||||
}
|
}
|
||||||
|
if err := req.UnmarshalMetadata(&videoRequest); err != nil {
|
||||||
promptOptimizer := DefaultPromptOptimizer
|
return nil, errors.Wrap(err, "unmarshal metadata to video request failed")
|
||||||
hailuoReq.PromptOptimizer = &promptOptimizer
|
|
||||||
|
|
||||||
metadata := req.Metadata
|
|
||||||
if metadata != nil {
|
|
||||||
metadataBytes, err := json.Marshal(metadata)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "marshal metadata failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
var metadataMap map[string]interface{}
|
|
||||||
if err := json.Unmarshal(metadataBytes, &metadataMap); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "unmarshal metadata failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
if val, exists := metadataMap["prompt_optimizer"]; exists {
|
|
||||||
if boolVal, ok := val.(bool); ok {
|
|
||||||
hailuoReq.PromptOptimizer = &boolVal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if modelConfig.HasFastPretreatment {
|
|
||||||
if val, exists := metadataMap["fast_pretreatment"]; exists {
|
|
||||||
if boolVal, ok := val.(bool); ok {
|
|
||||||
hailuoReq.FastPretreatment = &boolVal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if val, exists := metadataMap["callback_url"]; exists {
|
|
||||||
if strVal, ok := val.(string); ok {
|
|
||||||
hailuoReq.CallbackURL = strVal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if val, exists := metadataMap["aigc_watermark"]; exists {
|
|
||||||
if boolVal, ok := val.(bool); ok {
|
|
||||||
hailuoReq.AigcWatermark = &boolVal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.HasImage() {
|
return videoRequest, nil
|
||||||
return nil, fmt.Errorf("image input is not supported by hailuo video generation")
|
|
||||||
}
|
|
||||||
|
|
||||||
return hailuoReq, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *TaskAdaptor) parseResolutionFromSize(size string, modelConfig ModelConfig) string {
|
func (a *TaskAdaptor) parseResolutionFromSize(size string, modelConfig ModelConfig) string {
|
||||||
@@ -226,6 +169,8 @@ func (a *TaskAdaptor) parseResolutionFromSize(size string, modelConfig ModelConf
|
|||||||
return Resolution768P
|
return Resolution768P
|
||||||
case strings.Contains(size, "720"):
|
case strings.Contains(size, "720"):
|
||||||
return Resolution720P
|
return Resolution720P
|
||||||
|
case strings.Contains(size, "512"):
|
||||||
|
return Resolution512P
|
||||||
default:
|
default:
|
||||||
return modelConfig.DefaultResolution
|
return modelConfig.DefaultResolution
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,14 @@ const (
|
|||||||
|
|
||||||
var ModelList = []string{
|
var ModelList = []string{
|
||||||
"MiniMax-Hailuo-2.3",
|
"MiniMax-Hailuo-2.3",
|
||||||
|
"MiniMax-Hailuo-2.3-Fast",
|
||||||
"MiniMax-Hailuo-02",
|
"MiniMax-Hailuo-02",
|
||||||
"T2V-01-Director",
|
"T2V-01-Director",
|
||||||
"T2V-01",
|
"T2V-01",
|
||||||
|
"I2V-01-Director",
|
||||||
|
"I2V-01-live",
|
||||||
|
"I2V-01",
|
||||||
|
"S2V-01",
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -35,13 +40,13 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
Resolution512P = "512P"
|
||||||
Resolution720P = "720P"
|
Resolution720P = "720P"
|
||||||
Resolution768P = "768P"
|
Resolution768P = "768P"
|
||||||
Resolution1080P = "1080P"
|
Resolution1080P = "1080P"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultDuration = 6
|
DefaultDuration = 6
|
||||||
DefaultResolution = Resolution768P
|
DefaultResolution = Resolution720P
|
||||||
DefaultPromptOptimizer = true
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,17 +1,25 @@
|
|||||||
package hailuo
|
package hailuo
|
||||||
|
|
||||||
type TextToVideoRequest struct {
|
type SubjectReference struct {
|
||||||
Model string `json:"model"`
|
Type string `json:"type"` // Subject type, currently only supports "character"
|
||||||
Prompt string `json:"prompt"`
|
Image []string `json:"image"` // Array of subject reference images (currently only supports single image)
|
||||||
PromptOptimizer *bool `json:"prompt_optimizer,omitempty"`
|
|
||||||
FastPretreatment *bool `json:"fast_pretreatment,omitempty"`
|
|
||||||
Duration *int `json:"duration,omitempty"`
|
|
||||||
Resolution string `json:"resolution,omitempty"`
|
|
||||||
CallbackURL string `json:"callback_url,omitempty"`
|
|
||||||
AigcWatermark *bool `json:"aigc_watermark,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type TextToVideoResponse struct {
|
type VideoRequest struct {
|
||||||
|
Model string `json:"model"`
|
||||||
|
Prompt string `json:"prompt,omitempty"`
|
||||||
|
PromptOptimizer *bool `json:"prompt_optimizer,omitempty"`
|
||||||
|
FastPretreatment *bool `json:"fast_pretreatment,omitempty"`
|
||||||
|
Duration *int `json:"duration,omitempty"`
|
||||||
|
Resolution string `json:"resolution,omitempty"`
|
||||||
|
CallbackURL string `json:"callback_url,omitempty"`
|
||||||
|
AigcWatermark *bool `json:"aigc_watermark,omitempty"`
|
||||||
|
FirstFrameImage string `json:"first_frame_image,omitempty"` // For image-to-video and start-end-to-video
|
||||||
|
LastFrameImage string `json:"last_frame_image,omitempty"` // For start-end-to-video
|
||||||
|
SubjectReference []SubjectReference `json:"subject_reference,omitempty"` // For subject-reference-to-video
|
||||||
|
}
|
||||||
|
|
||||||
|
type VideoResponse struct {
|
||||||
TaskID string `json:"task_id"`
|
TaskID string `json:"task_id"`
|
||||||
BaseResp BaseResp `json:"base_resp"`
|
BaseResp BaseResp `json:"base_resp"`
|
||||||
}
|
}
|
||||||
@@ -81,11 +89,19 @@ func GetModelConfig(model string) ModelConfig {
|
|||||||
HasPromptOptimizer: true,
|
HasPromptOptimizer: true,
|
||||||
HasFastPretreatment: true,
|
HasFastPretreatment: true,
|
||||||
},
|
},
|
||||||
|
"MiniMax-Hailuo-2.3-Fast": {
|
||||||
|
Name: "MiniMax-Hailuo-2.3-Fast",
|
||||||
|
DefaultResolution: Resolution768P,
|
||||||
|
SupportedDurations: []int{6, 10},
|
||||||
|
SupportedResolutions: []string{Resolution768P, Resolution1080P},
|
||||||
|
HasPromptOptimizer: true,
|
||||||
|
HasFastPretreatment: true,
|
||||||
|
},
|
||||||
"MiniMax-Hailuo-02": {
|
"MiniMax-Hailuo-02": {
|
||||||
Name: "MiniMax-Hailuo-02",
|
Name: "MiniMax-Hailuo-02",
|
||||||
DefaultResolution: Resolution768P,
|
DefaultResolution: Resolution768P,
|
||||||
SupportedDurations: []int{6, 10},
|
SupportedDurations: []int{6, 10},
|
||||||
SupportedResolutions: []string{Resolution768P, Resolution1080P},
|
SupportedResolutions: []string{Resolution512P, Resolution768P, Resolution1080P},
|
||||||
HasPromptOptimizer: true,
|
HasPromptOptimizer: true,
|
||||||
HasFastPretreatment: true,
|
HasFastPretreatment: true,
|
||||||
},
|
},
|
||||||
@@ -105,6 +121,38 @@ func GetModelConfig(model string) ModelConfig {
|
|||||||
HasPromptOptimizer: true,
|
HasPromptOptimizer: true,
|
||||||
HasFastPretreatment: false,
|
HasFastPretreatment: false,
|
||||||
},
|
},
|
||||||
|
"I2V-01-Director": {
|
||||||
|
Name: "I2V-01-Director",
|
||||||
|
DefaultResolution: Resolution720P,
|
||||||
|
SupportedDurations: []int{6},
|
||||||
|
SupportedResolutions: []string{Resolution720P, Resolution1080P},
|
||||||
|
HasPromptOptimizer: true,
|
||||||
|
HasFastPretreatment: false,
|
||||||
|
},
|
||||||
|
"I2V-01-live": {
|
||||||
|
Name: "I2V-01-live",
|
||||||
|
DefaultResolution: Resolution720P,
|
||||||
|
SupportedDurations: []int{6},
|
||||||
|
SupportedResolutions: []string{Resolution720P, Resolution1080P},
|
||||||
|
HasPromptOptimizer: true,
|
||||||
|
HasFastPretreatment: false,
|
||||||
|
},
|
||||||
|
"I2V-01": {
|
||||||
|
Name: "I2V-01",
|
||||||
|
DefaultResolution: Resolution720P,
|
||||||
|
SupportedDurations: []int{6},
|
||||||
|
SupportedResolutions: []string{Resolution720P, Resolution1080P},
|
||||||
|
HasPromptOptimizer: true,
|
||||||
|
HasFastPretreatment: false,
|
||||||
|
},
|
||||||
|
"S2V-01": {
|
||||||
|
Name: "S2V-01",
|
||||||
|
DefaultResolution: Resolution720P,
|
||||||
|
SupportedDurations: []int{6},
|
||||||
|
SupportedResolutions: []string{Resolution720P},
|
||||||
|
HasPromptOptimizer: true,
|
||||||
|
HasFastPretreatment: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if config, exists := configs[model]; exists {
|
if config, exists := configs[model]; exists {
|
||||||
@@ -113,9 +161,9 @@ func GetModelConfig(model string) ModelConfig {
|
|||||||
|
|
||||||
return ModelConfig{
|
return ModelConfig{
|
||||||
Name: model,
|
Name: model,
|
||||||
DefaultResolution: Resolution720P,
|
DefaultResolution: DefaultResolution,
|
||||||
SupportedDurations: []int{6},
|
SupportedDurations: []int{6},
|
||||||
SupportedResolutions: []string{Resolution720P},
|
SupportedResolutions: []string{DefaultResolution},
|
||||||
HasPromptOptimizer: true,
|
HasPromptOptimizer: true,
|
||||||
HasFastPretreatment: false,
|
HasFastPretreatment: false,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -498,11 +498,11 @@ type TaskSubmitReq struct {
|
|||||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t TaskSubmitReq) GetPrompt() string {
|
func (t *TaskSubmitReq) GetPrompt() string {
|
||||||
return t.Prompt
|
return t.Prompt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t TaskSubmitReq) HasImage() bool {
|
func (t *TaskSubmitReq) HasImage() bool {
|
||||||
return len(t.Images) > 0
|
return len(t.Images) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -537,6 +537,20 @@ func (t *TaskSubmitReq) UnmarshalJSON(data []byte) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (t *TaskSubmitReq) UnmarshalMetadata(v any) error {
|
||||||
|
metadata := t.Metadata
|
||||||
|
if metadata != nil {
|
||||||
|
metadataBytes, err := json.Marshal(metadata)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("marshal metadata failed: %w", err)
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(metadataBytes, v)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unmarshal metadata to target failed: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type TaskInfo struct {
|
type TaskInfo struct {
|
||||||
Code int `json:"code"`
|
Code int `json:"code"`
|
||||||
|
|||||||
Reference in New Issue
Block a user