Commit cf8b021b by Sh1n3zZ

feat: vertex veo (#1450)

parent f249cf9a
...@@ -12,4 +12,4 @@ var LogSqlType = DatabaseTypeSQLite // Default to SQLite for logging SQL queries ...@@ -12,4 +12,4 @@ var LogSqlType = DatabaseTypeSQLite // Default to SQLite for logging SQL queries
var UsingMySQL = false var UsingMySQL = false
var UsingClickHouse = false var UsingClickHouse = false
var SQLitePath = "one-api.db?_busy_timeout=30000" var SQLitePath = "one-api.db?_busy_timeout=30000"
\ No newline at end of file
...@@ -178,4 +178,4 @@ func boolToString(b bool) string { ...@@ -178,4 +178,4 @@ func boolToString(b bool) string {
return "true" return "true"
} }
return "false" return "false"
} }
\ No newline at end of file
...@@ -94,7 +94,7 @@ func updateVideoSingleTask(ctx context.Context, adaptor channel.TaskAdaptor, cha ...@@ -94,7 +94,7 @@ func updateVideoSingleTask(ctx context.Context, adaptor channel.TaskAdaptor, cha
} else if taskResult, err = adaptor.ParseTaskResult(responseBody); err != nil { } else if taskResult, err = adaptor.ParseTaskResult(responseBody); err != nil {
return fmt.Errorf("parseTaskResult failed for task %s: %w", taskId, err) return fmt.Errorf("parseTaskResult failed for task %s: %w", taskId, err)
} else { } else {
task.Data = responseBody task.Data = redactVideoResponseBody(responseBody)
} }
now := time.Now().Unix() now := time.Now().Unix()
...@@ -113,11 +113,13 @@ func updateVideoSingleTask(ctx context.Context, adaptor channel.TaskAdaptor, cha ...@@ -113,11 +113,13 @@ func updateVideoSingleTask(ctx context.Context, adaptor channel.TaskAdaptor, cha
task.StartTime = now task.StartTime = now
} }
case model.TaskStatusSuccess: case model.TaskStatusSuccess:
task.Progress = "100%" task.Progress = "100%"
if task.FinishTime == 0 { if task.FinishTime == 0 {
task.FinishTime = now task.FinishTime = now
} }
task.FailReason = taskResult.Url if !(len(taskResult.Url) > 5 && taskResult.Url[:5] == "data:") {
task.FailReason = taskResult.Url
}
case model.TaskStatusFailure: case model.TaskStatusFailure:
task.Status = model.TaskStatusFailure task.Status = model.TaskStatusFailure
task.Progress = "100%" task.Progress = "100%"
...@@ -146,3 +148,37 @@ func updateVideoSingleTask(ctx context.Context, adaptor channel.TaskAdaptor, cha ...@@ -146,3 +148,37 @@ func updateVideoSingleTask(ctx context.Context, adaptor channel.TaskAdaptor, cha
return nil return nil
} }
func redactVideoResponseBody(body []byte) []byte {
var m map[string]any
if err := json.Unmarshal(body, &m); err != nil {
return body
}
resp, _ := m["response"].(map[string]any)
if resp != nil {
delete(resp, "bytesBase64Encoded")
if v, ok := resp["video"].(string); ok {
resp["video"] = truncateBase64(v)
}
if vs, ok := resp["videos"].([]any); ok {
for i := range vs {
if vm, ok := vs[i].(map[string]any); ok {
delete(vm, "bytesBase64Encoded")
}
}
}
}
b, err := json.Marshal(m)
if err != nil {
return body
}
return b
}
func truncateBase64(s string) string {
const maxKeep = 256
if len(s) <= maxKeep {
return s
}
return s[:maxKeep] + "..."
}
...@@ -208,4 +208,4 @@ func InitResources() error { ...@@ -208,4 +208,4 @@ func InitResources() error {
return err return err
} }
return nil return nil
} }
\ No newline at end of file
...@@ -166,9 +166,9 @@ func getModelRequest(c *gin.Context) (*ModelRequest, bool, error) { ...@@ -166,9 +166,9 @@ func getModelRequest(c *gin.Context) (*ModelRequest, bool, error) {
c.Set("platform", string(constant.TaskPlatformSuno)) c.Set("platform", string(constant.TaskPlatformSuno))
c.Set("relay_mode", relayMode) c.Set("relay_mode", relayMode)
} else if strings.Contains(c.Request.URL.Path, "/v1/video/generations") { } else if strings.Contains(c.Request.URL.Path, "/v1/video/generations") {
err = common.UnmarshalBodyReusable(c, &modelRequest)
relayMode := relayconstant.RelayModeUnknown relayMode := relayconstant.RelayModeUnknown
if c.Request.Method == http.MethodPost { if c.Request.Method == http.MethodPost {
err = common.UnmarshalBodyReusable(c, &modelRequest)
relayMode = relayconstant.RelayModeVideoSubmit relayMode = relayconstant.RelayModeVideoSubmit
} else if c.Request.Method == http.MethodGet { } else if c.Request.Method == http.MethodGet {
relayMode = relayconstant.RelayModeVideoFetchByID relayMode = relayconstant.RelayModeVideoFetchByID
......
...@@ -174,6 +174,7 @@ func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Header, info *rel ...@@ -174,6 +174,7 @@ func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Header, info *rel
return err return err
} }
req.Set("Authorization", "Bearer "+accessToken) req.Set("Authorization", "Bearer "+accessToken)
req.Set("x-goog-user-project", a.AccountCredentials.ProjectID)
return nil return nil
} }
......
...@@ -12,7 +12,10 @@ func GetModelRegion(other string, localModelName string) string { ...@@ -12,7 +12,10 @@ func GetModelRegion(other string, localModelName string) string {
if m[localModelName] != nil { if m[localModelName] != nil {
return m[localModelName].(string) return m[localModelName].(string)
} else { } else {
return m["default"].(string) if v, ok := m["default"]; ok {
return v.(string)
}
return "global"
} }
} }
return other return other
......
...@@ -6,14 +6,15 @@ import ( ...@@ -6,14 +6,15 @@ import (
"encoding/json" "encoding/json"
"encoding/pem" "encoding/pem"
"errors" "errors"
"github.com/bytedance/gopkg/cache/asynccache"
"github.com/golang-jwt/jwt"
"net/http" "net/http"
"net/url" "net/url"
relaycommon "one-api/relay/common" relaycommon "one-api/relay/common"
"one-api/service" "one-api/service"
"strings" "strings"
"github.com/bytedance/gopkg/cache/asynccache"
"github.com/golang-jwt/jwt"
"fmt" "fmt"
"time" "time"
) )
...@@ -137,3 +138,45 @@ func exchangeJwtForAccessToken(signedJWT string, info *relaycommon.RelayInfo) (s ...@@ -137,3 +138,45 @@ func exchangeJwtForAccessToken(signedJWT string, info *relaycommon.RelayInfo) (s
return "", fmt.Errorf("failed to get access token: %v", result) return "", fmt.Errorf("failed to get access token: %v", result)
} }
func AcquireAccessToken(creds Credentials, proxy string) (string, error) {
signedJWT, err := createSignedJWT(creds.ClientEmail, creds.PrivateKey)
if err != nil {
return "", fmt.Errorf("failed to create signed JWT: %w", err)
}
return exchangeJwtForAccessTokenWithProxy(signedJWT, proxy)
}
func exchangeJwtForAccessTokenWithProxy(signedJWT string, proxy string) (string, error) {
authURL := "https://www.googleapis.com/oauth2/v4/token"
data := url.Values{}
data.Set("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer")
data.Set("assertion", signedJWT)
var client *http.Client
var err error
if proxy != "" {
client, err = service.NewProxyHttpClient(proxy)
if err != nil {
return "", fmt.Errorf("new proxy http client failed: %w", err)
}
} else {
client = service.GetHttpClient()
}
resp, err := client.PostForm(authURL, data)
if err != nil {
return "", err
}
defer resp.Body.Close()
var result map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return "", err
}
if accessToken, ok := result["access_token"].(string); ok {
return accessToken, nil
}
return "", fmt.Errorf("failed to get access token: %v", result)
}
package relay package relay
import ( import (
"github.com/gin-gonic/gin"
"one-api/constant" "one-api/constant"
"one-api/relay/channel" "one-api/relay/channel"
"one-api/relay/channel/ali" "one-api/relay/channel/ali"
...@@ -28,6 +27,7 @@ import ( ...@@ -28,6 +27,7 @@ import (
taskjimeng "one-api/relay/channel/task/jimeng" taskjimeng "one-api/relay/channel/task/jimeng"
"one-api/relay/channel/task/kling" "one-api/relay/channel/task/kling"
"one-api/relay/channel/task/suno" "one-api/relay/channel/task/suno"
taskvertex "one-api/relay/channel/task/vertex"
taskVidu "one-api/relay/channel/task/vidu" taskVidu "one-api/relay/channel/task/vidu"
"one-api/relay/channel/tencent" "one-api/relay/channel/tencent"
"one-api/relay/channel/vertex" "one-api/relay/channel/vertex"
...@@ -37,6 +37,8 @@ import ( ...@@ -37,6 +37,8 @@ import (
"one-api/relay/channel/zhipu" "one-api/relay/channel/zhipu"
"one-api/relay/channel/zhipu_4v" "one-api/relay/channel/zhipu_4v"
"strconv" "strconv"
"github.com/gin-gonic/gin"
) )
func GetAdaptor(apiType int) channel.Adaptor { func GetAdaptor(apiType int) channel.Adaptor {
...@@ -126,6 +128,8 @@ func GetTaskAdaptor(platform constant.TaskPlatform) channel.TaskAdaptor { ...@@ -126,6 +128,8 @@ func GetTaskAdaptor(platform constant.TaskPlatform) channel.TaskAdaptor {
return &kling.TaskAdaptor{} return &kling.TaskAdaptor{}
case constant.ChannelTypeJimeng: case constant.ChannelTypeJimeng:
return &taskjimeng.TaskAdaptor{} return &taskjimeng.TaskAdaptor{}
case constant.ChannelTypeVertexAi:
return &taskvertex.TaskAdaptor{}
case constant.ChannelTypeVidu: case constant.ChannelTypeVidu:
return &taskVidu.TaskAdaptor{} return &taskVidu.TaskAdaptor{}
} }
......
...@@ -15,6 +15,8 @@ import ( ...@@ -15,6 +15,8 @@ import (
relayconstant "one-api/relay/constant" relayconstant "one-api/relay/constant"
"one-api/service" "one-api/service"
"one-api/setting/ratio_setting" "one-api/setting/ratio_setting"
"strconv"
"strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
...@@ -32,6 +34,7 @@ func RelayTaskSubmit(c *gin.Context, relayMode int) (taskErr *dto.TaskError) { ...@@ -32,6 +34,7 @@ func RelayTaskSubmit(c *gin.Context, relayMode int) (taskErr *dto.TaskError) {
if err != nil { if err != nil {
return service.TaskErrorWrapper(err, "gen_relay_info_failed", http.StatusInternalServerError) return service.TaskErrorWrapper(err, "gen_relay_info_failed", http.StatusInternalServerError)
} }
relayInfo.InitChannelMeta(c)
adaptor := GetTaskAdaptor(platform) adaptor := GetTaskAdaptor(platform)
if adaptor == nil { if adaptor == nil {
...@@ -197,6 +200,9 @@ func RelayTaskFetch(c *gin.Context, relayMode int) (taskResp *dto.TaskError) { ...@@ -197,6 +200,9 @@ func RelayTaskFetch(c *gin.Context, relayMode int) (taskResp *dto.TaskError) {
if taskErr != nil { if taskErr != nil {
return taskErr return taskErr
} }
if len(respBody) == 0 {
respBody = []byte("{\"code\":\"success\",\"data\":null}")
}
c.Writer.Header().Set("Content-Type", "application/json") c.Writer.Header().Set("Content-Type", "application/json")
_, err := io.Copy(c.Writer, bytes.NewBuffer(respBody)) _, err := io.Copy(c.Writer, bytes.NewBuffer(respBody))
...@@ -276,10 +282,92 @@ func videoFetchByIDRespBodyBuilder(c *gin.Context) (respBody []byte, taskResp *d ...@@ -276,10 +282,92 @@ func videoFetchByIDRespBodyBuilder(c *gin.Context) (respBody []byte, taskResp *d
return return
} }
respBody, err = json.Marshal(dto.TaskResponse[any]{ func() {
Code: "success", channelModel, err2 := model.GetChannelById(originTask.ChannelId, true)
Data: TaskModel2Dto(originTask), if err2 != nil {
}) return
}
if channelModel.Type != constant.ChannelTypeVertexAi {
return
}
baseURL := constant.ChannelBaseURLs[channelModel.Type]
if channelModel.GetBaseURL() != "" {
baseURL = channelModel.GetBaseURL()
}
adaptor := GetTaskAdaptor(constant.TaskPlatform(strconv.Itoa(channelModel.Type)))
if adaptor == nil {
return
}
resp, err2 := adaptor.FetchTask(baseURL, channelModel.Key, map[string]any{
"task_id": originTask.TaskID,
"action": originTask.Action,
})
if err2 != nil || resp == nil {
return
}
defer resp.Body.Close()
body, err2 := io.ReadAll(resp.Body)
if err2 != nil {
return
}
ti, err2 := adaptor.ParseTaskResult(body)
if err2 == nil && ti != nil {
if ti.Status != "" {
originTask.Status = model.TaskStatus(ti.Status)
}
if ti.Progress != "" {
originTask.Progress = ti.Progress
}
if ti.Url != "" {
originTask.FailReason = ti.Url
}
_ = originTask.Update()
var raw map[string]any
_ = json.Unmarshal(body, &raw)
format := "mp4"
if respObj, ok := raw["response"].(map[string]any); ok {
if vids, ok := respObj["videos"].([]any); ok && len(vids) > 0 {
if v0, ok := vids[0].(map[string]any); ok {
if mt, ok := v0["mimeType"].(string); ok && mt != "" {
if strings.Contains(mt, "mp4") {
format = "mp4"
} else {
format = mt
}
}
}
}
}
status := "processing"
switch originTask.Status {
case model.TaskStatusSuccess:
status = "succeeded"
case model.TaskStatusFailure:
status = "failed"
case model.TaskStatusQueued, model.TaskStatusSubmitted:
status = "queued"
}
out := map[string]any{
"error": nil,
"format": format,
"metadata": nil,
"status": status,
"task_id": originTask.TaskID,
"url": originTask.FailReason,
}
respBody, _ = json.Marshal(dto.TaskResponse[any]{
Code: "success",
Data: out,
})
}
}()
if len(respBody) == 0 {
respBody, err = json.Marshal(dto.TaskResponse[any]{
Code: "success",
Data: TaskModel2Dto(originTask),
})
}
return return
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment