mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-05-03 23:44:37 +00:00
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.
This commit is contained in:
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/dto"
|
||||
"github.com/QuantumNous/new-api/relay/channel"
|
||||
geminitask "github.com/QuantumNous/new-api/relay/channel/task/gemini"
|
||||
taskcommon "github.com/QuantumNous/new-api/relay/channel/task/taskcommon"
|
||||
vertexcore "github.com/QuantumNous/new-api/relay/channel/vertex"
|
||||
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
||||
@@ -26,9 +27,34 @@ import (
|
||||
// Request / Response structures
|
||||
// ============================
|
||||
|
||||
type veoInstance struct {
|
||||
Prompt string `json:"prompt"`
|
||||
Image *geminitask.VeoImageInput `json:"image,omitempty"`
|
||||
// TODO: support referenceImages (style/asset references, up to 3 images)
|
||||
// TODO: support lastFrame (first+last frame interpolation, Veo 3.1)
|
||||
}
|
||||
|
||||
type veoParameters struct {
|
||||
SampleCount int `json:"sampleCount"`
|
||||
DurationSeconds int `json:"durationSeconds,omitempty"`
|
||||
AspectRatio string `json:"aspectRatio,omitempty"`
|
||||
Resolution string `json:"resolution,omitempty"`
|
||||
NegativePrompt string `json:"negativePrompt,omitempty"`
|
||||
PersonGeneration string `json:"personGeneration,omitempty"`
|
||||
StorageUri string `json:"storageUri,omitempty"`
|
||||
CompressionQuality string `json:"compressionQuality,omitempty"`
|
||||
ResizeMode string `json:"resizeMode,omitempty"`
|
||||
Seed *int `json:"seed,omitempty"`
|
||||
GenerateAudio *bool `json:"generateAudio,omitempty"`
|
||||
}
|
||||
|
||||
type requestPayload struct {
|
||||
Instances []map[string]any `json:"instances"`
|
||||
Parameters map[string]any `json:"parameters,omitempty"`
|
||||
Instances []veoInstance `json:"instances"`
|
||||
Parameters *veoParameters `json:"parameters,omitempty"`
|
||||
}
|
||||
|
||||
type fetchOperationPayload struct {
|
||||
OperationName string `json:"operationName"`
|
||||
}
|
||||
|
||||
type submitResponse struct {
|
||||
@@ -134,25 +160,21 @@ func (a *TaskAdaptor) BuildRequestHeader(c *gin.Context, req *http.Request, info
|
||||
return nil
|
||||
}
|
||||
|
||||
// EstimateBilling 根据用户请求中的 sampleCount 计算 OtherRatios。
|
||||
func (a *TaskAdaptor) EstimateBilling(c *gin.Context, _ *relaycommon.RelayInfo) map[string]float64 {
|
||||
sampleCount := 1
|
||||
// EstimateBilling returns OtherRatios based on durationSeconds and resolution.
|
||||
func (a *TaskAdaptor) EstimateBilling(c *gin.Context, info *relaycommon.RelayInfo) map[string]float64 {
|
||||
v, ok := c.Get("task_request")
|
||||
if ok {
|
||||
req := v.(relaycommon.TaskSubmitReq)
|
||||
if req.Metadata != nil {
|
||||
if sc, exists := req.Metadata["sampleCount"]; exists {
|
||||
if i, ok := sc.(int); ok && i > 0 {
|
||||
sampleCount = i
|
||||
}
|
||||
if f, ok := sc.(float64); ok && int(f) > 0 {
|
||||
sampleCount = int(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
req := v.(relaycommon.TaskSubmitReq)
|
||||
|
||||
seconds := geminitask.ResolveVeoDuration(req.Metadata, req.Duration, req.Seconds)
|
||||
resolution := geminitask.ResolveVeoResolution(req.Metadata, req.Size)
|
||||
resRatio := geminitask.VeoResolutionRatio(info.UpstreamModelName, resolution)
|
||||
|
||||
return map[string]float64{
|
||||
"sampleCount": float64(sampleCount),
|
||||
"seconds": float64(seconds),
|
||||
"resolution": resRatio,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,29 +186,35 @@ func (a *TaskAdaptor) BuildRequestBody(c *gin.Context, info *relaycommon.RelayIn
|
||||
}
|
||||
req := v.(relaycommon.TaskSubmitReq)
|
||||
|
||||
body := requestPayload{
|
||||
Instances: []map[string]any{{"prompt": req.Prompt}},
|
||||
Parameters: map[string]any{},
|
||||
}
|
||||
if req.Metadata != nil {
|
||||
if v, ok := req.Metadata["storageUri"]; ok {
|
||||
body.Parameters["storageUri"] = v
|
||||
instance := veoInstance{Prompt: req.Prompt}
|
||||
if img := geminitask.ExtractMultipartImage(c, info); img != nil {
|
||||
instance.Image = img
|
||||
} else if len(req.Images) > 0 {
|
||||
if parsed := geminitask.ParseImageInput(req.Images[0]); parsed != nil {
|
||||
instance.Image = parsed
|
||||
info.Action = constant.TaskActionGenerate
|
||||
}
|
||||
if v, ok := req.Metadata["sampleCount"]; ok {
|
||||
if i, ok := v.(int); ok {
|
||||
body.Parameters["sampleCount"] = i
|
||||
}
|
||||
if f, ok := v.(float64); ok {
|
||||
body.Parameters["sampleCount"] = int(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, ok := body.Parameters["sampleCount"]; !ok {
|
||||
body.Parameters["sampleCount"] = 1
|
||||
}
|
||||
|
||||
if body.Parameters["sampleCount"].(int) <= 0 {
|
||||
return nil, fmt.Errorf("sampleCount must be greater than 0")
|
||||
params := &veoParameters{}
|
||||
if err := taskcommon.UnmarshalMetadata(req.Metadata, params); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal metadata failed: %w", err)
|
||||
}
|
||||
if params.DurationSeconds == 0 && req.Duration > 0 {
|
||||
params.DurationSeconds = req.Duration
|
||||
}
|
||||
if params.Resolution == "" && req.Size != "" {
|
||||
params.Resolution = geminitask.SizeToVeoResolution(req.Size)
|
||||
}
|
||||
if params.AspectRatio == "" && req.Size != "" {
|
||||
params.AspectRatio = geminitask.SizeToVeoAspectRatio(req.Size)
|
||||
}
|
||||
params.Resolution = strings.ToLower(params.Resolution)
|
||||
params.SampleCount = 1
|
||||
|
||||
body := requestPayload{
|
||||
Instances: []veoInstance{instance},
|
||||
Parameters: params,
|
||||
}
|
||||
|
||||
data, err := common.Marshal(body)
|
||||
@@ -226,7 +254,14 @@ func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *rela
|
||||
return localID, responseBody, nil
|
||||
}
|
||||
|
||||
func (a *TaskAdaptor) GetModelList() []string { return []string{"veo-3.0-generate-001"} }
|
||||
func (a *TaskAdaptor) GetModelList() []string {
|
||||
return []string{
|
||||
"veo-3.0-generate-001",
|
||||
"veo-3.0-fast-generate-001",
|
||||
"veo-3.1-generate-preview",
|
||||
"veo-3.1-fast-generate-preview",
|
||||
}
|
||||
}
|
||||
func (a *TaskAdaptor) GetChannelName() string { return "vertex" }
|
||||
|
||||
// FetchTask fetch task status
|
||||
@@ -254,7 +289,7 @@ func (a *TaskAdaptor) FetchTask(baseUrl, key string, body map[string]any, proxy
|
||||
} else {
|
||||
url = fmt.Sprintf("https://%s-aiplatform.googleapis.com/v1/projects/%s/locations/%s/publishers/google/models/%s:fetchPredictOperation", region, project, region, modelName)
|
||||
}
|
||||
payload := map[string]string{"operationName": upstreamName}
|
||||
payload := fetchOperationPayload{OperationName: upstreamName}
|
||||
data, err := common.Marshal(payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in New Issue
Block a user