diff --git a/common/constants.go b/common/constants.go
index 6d6c9ca7..7b09152b 100644
--- a/common/constants.go
+++ b/common/constants.go
@@ -172,6 +172,7 @@ const (
ChannelTypeBedrock = 32
ChannelTypeLingyi = 33
ChannelTypeMidjourney = 34
+ ChannelTypeCloudflareAI = 35
)
var ChannelBaseURLs = []string{
@@ -210,6 +211,7 @@ var ChannelBaseURLs = []string{
"", //32
"https://api.lingyiwanwu.com", //33
"", //34
+ "", //35
}
const (
diff --git a/model/main.go b/model/main.go
index 72169a34..1b74c251 100644
--- a/model/main.go
+++ b/model/main.go
@@ -104,6 +104,10 @@ func InitDB() (err error) {
return nil
}
common.SysLog("database migration started")
+ // err = MigrateDB(DB)
+ // if err != nil {
+ // return err
+ // }
err = db.AutoMigrate(&Channel{})
if err != nil {
return err
@@ -157,6 +161,24 @@ func InitDB() (err error) {
return err
}
+// func MigrateDB(db *gorm.DB) error {
+// if DB.Migrator().HasConstraint(&Price{}, "model") {
+// fmt.Println("----Price model has constraint----")
+// // 如果是主键,移除主键约束
+// err := db.Migrator().DropConstraint(&Price{}, "model")
+// if err != nil {
+// return err
+// }
+// // 修改字段长度
+// err = db.Migrator().AlterColumn(&Price{}, "model")
+// if err != nil {
+// return err
+// }
+// }
+
+// return nil
+// }
+
func CloseDB() error {
sqlDB, err := DB.DB()
if err != nil {
diff --git a/model/price.go b/model/price.go
index 9961d334..dc44820f 100644
--- a/model/price.go
+++ b/model/price.go
@@ -15,7 +15,7 @@ const (
)
type Price struct {
- Model string `json:"model" gorm:"type:varchar(30);primaryKey" binding:"required"`
+ Model string `json:"model" gorm:"type:varchar(100)" binding:"required"`
Type string `json:"type" gorm:"default:'tokens'" binding:"required"`
ChannelType int `json:"channel_type" gorm:"default:0" binding:"gte=0"`
Input float64 `json:"input" gorm:"default:0" binding:"gte=0"`
@@ -98,11 +98,7 @@ func DeleteAllPrices(tx *gorm.DB) error {
}
func (price *Price) Delete() error {
- err := DB.Delete(price).Error
- if err != nil {
- return err
- }
- return err
+ return DB.Where("model = ?", price.Model).Delete(&Price{}).Error
}
type ModelType struct {
@@ -289,6 +285,16 @@ func GetDefaultPrice() []*Price {
"yi-34b-chat-200k": {[]float64{0.8571, 0.8571}, common.ChannelTypeLingyi},
// 6 元 / 1M tokens 0.006 / 1k tokens
"yi-vl-plus": {[]float64{0.4286, 0.4286}, common.ChannelTypeLingyi},
+
+ "@cf/stabilityai/stable-diffusion-xl-base-1.0": {[]float64{0, 0}, common.ChannelTypeCloudflareAI},
+ "@cf/lykon/dreamshaper-8-lcm": {[]float64{0, 0}, common.ChannelTypeCloudflareAI},
+ "@cf/bytedance/stable-diffusion-xl-lightning": {[]float64{0, 0}, common.ChannelTypeCloudflareAI},
+ "@cf/qwen/qwen1.5-7b-chat-awq": {[]float64{0, 0}, common.ChannelTypeCloudflareAI},
+ "@cf/qwen/qwen1.5-14b-chat-awq": {[]float64{0, 0}, common.ChannelTypeCloudflareAI},
+ "@hf/thebloke/deepseek-coder-6.7b-base-awq": {[]float64{0, 0}, common.ChannelTypeCloudflareAI},
+ "@hf/google/gemma-7b-it": {[]float64{0, 0}, common.ChannelTypeCloudflareAI},
+ "@hf/thebloke/llama-2-13b-chat-awq": {[]float64{0, 0}, common.ChannelTypeCloudflareAI},
+ "@cf/openai/whisper": {[]float64{0, 0}, common.ChannelTypeCloudflareAI},
}
var prices []*Price
diff --git a/providers/cloudflareAI/base.go b/providers/cloudflareAI/base.go
new file mode 100644
index 00000000..a0399c71
--- /dev/null
+++ b/providers/cloudflareAI/base.go
@@ -0,0 +1,86 @@
+package cloudflareAI
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "one-api/common/requester"
+ "one-api/model"
+ "one-api/providers/base"
+ "one-api/types"
+ "strings"
+)
+
+type CloudflareAIProviderFactory struct{}
+
+// 创建 CloudflareAIProvider
+func (f CloudflareAIProviderFactory) Create(channel *model.Channel) base.ProviderInterface {
+ cf := &CloudflareAIProvider{
+ BaseProvider: base.BaseProvider{
+ Config: getConfig(),
+ Channel: channel,
+ Requester: requester.NewHTTPRequester(*channel.Proxy, requestErrorHandle),
+ },
+ }
+
+ tokens := strings.Split(channel.Key, "|")
+ if len(tokens) == 2 {
+ cf.AccountID = tokens[0]
+ cf.CFToken = tokens[1]
+ }
+
+ return cf
+}
+
+type CloudflareAIProvider struct {
+ base.BaseProvider
+ AccountID string
+ CFToken string
+}
+
+func getConfig() base.ProviderConfig {
+ return base.ProviderConfig{
+ BaseURL: "https://api.cloudflare.com/client/v4/accounts/%s/ai/run/%s",
+ ImagesGenerations: "true",
+ ChatCompletions: "true",
+ AudioTranscriptions: "true",
+ }
+}
+
+// 请求错误处理
+func requestErrorHandle(resp *http.Response) *types.OpenAIError {
+ CloudflareAIError := &CloudflareAIError{}
+ err := json.NewDecoder(resp.Body).Decode(CloudflareAIError)
+ if err != nil {
+ return nil
+ }
+
+ return errorHandle(CloudflareAIError)
+}
+
+// 错误处理
+func errorHandle(CloudflareAIError *CloudflareAIError) *types.OpenAIError {
+ if CloudflareAIError.Success || len(CloudflareAIError.Error) == 0 {
+ return nil
+ }
+ return &types.OpenAIError{
+ Message: CloudflareAIError.Error[0].Message,
+ Type: "CloudflareAI error",
+ Code: CloudflareAIError.Error[0].Code,
+ }
+}
+
+// 获取请求头
+func (p *CloudflareAIProvider) GetRequestHeaders() (headers map[string]string) {
+ headers = make(map[string]string)
+ p.CommonRequestHeaders(headers)
+ headers["Authorization"] = fmt.Sprintf("Bearer %s", p.CFToken)
+
+ return headers
+}
+
+func (p *CloudflareAIProvider) GetFullRequestURL(modelName string) string {
+ baseURL := strings.TrimSuffix(p.GetBaseURL(), "/")
+
+ return fmt.Sprintf(baseURL, p.AccountID, modelName)
+}
diff --git a/providers/cloudflareAI/chat.go b/providers/cloudflareAI/chat.go
new file mode 100644
index 00000000..7ae1f8ce
--- /dev/null
+++ b/providers/cloudflareAI/chat.go
@@ -0,0 +1,184 @@
+package cloudflareAI
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "one-api/common"
+ "one-api/common/requester"
+ "one-api/types"
+ "strings"
+)
+
+type CloudflareAIStreamHandler struct {
+ Usage *types.Usage
+ Request *types.ChatCompletionRequest
+}
+
+func (p *CloudflareAIProvider) CreateChatCompletion(request *types.ChatCompletionRequest) (*types.ChatCompletionResponse, *types.OpenAIErrorWithStatusCode) {
+ req, errWithCode := p.getChatRequest(request)
+ if errWithCode != nil {
+ return nil, errWithCode
+ }
+ defer req.Body.Close()
+
+ chatResponse := &ChatRespone{}
+ // 发送请求
+ _, errWithCode = p.Requester.SendRequest(req, chatResponse, false)
+ if errWithCode != nil {
+ return nil, errWithCode
+ }
+
+ return p.convertToChatOpenai(chatResponse, request)
+}
+
+func (p *CloudflareAIProvider) CreateChatCompletionStream(request *types.ChatCompletionRequest) (requester.StreamReaderInterface[string], *types.OpenAIErrorWithStatusCode) {
+ req, errWithCode := p.getChatRequest(request)
+ if errWithCode != nil {
+ return nil, errWithCode
+ }
+ defer req.Body.Close()
+
+ // 发送请求
+ resp, errWithCode := p.Requester.SendRequestRaw(req)
+ if errWithCode != nil {
+ return nil, errWithCode
+ }
+
+ chatHandler := &CloudflareAIStreamHandler{
+ Usage: p.Usage,
+ Request: request,
+ }
+
+ return requester.RequestStream[string](p.Requester, resp, chatHandler.handlerStream)
+}
+
+func (p *CloudflareAIProvider) getChatRequest(request *types.ChatCompletionRequest) (*http.Request, *types.OpenAIErrorWithStatusCode) {
+ // 获取请求地址
+ fullRequestURL := p.GetFullRequestURL(request.Model)
+ if fullRequestURL == "" {
+ return nil, common.ErrorWrapper(nil, "invalid_cloudflare_ai_config", http.StatusInternalServerError)
+ }
+
+ // 获取请求头
+ headers := p.GetRequestHeaders()
+ chatRequest := p.convertFromChatOpenai(request)
+
+ // 创建请求
+ req, err := p.Requester.NewRequest(http.MethodPost, fullRequestURL, p.Requester.WithBody(chatRequest), p.Requester.WithHeader(headers))
+ if err != nil {
+ return nil, common.ErrorWrapper(err, "new_request_failed", http.StatusInternalServerError)
+ }
+
+ return req, nil
+}
+
+func (p *CloudflareAIProvider) convertToChatOpenai(response *ChatRespone, request *types.ChatCompletionRequest) (openaiResponse *types.ChatCompletionResponse, errWithCode *types.OpenAIErrorWithStatusCode) {
+ err := errorHandle(&response.CloudflareAIError)
+ if err != nil {
+ errWithCode = &types.OpenAIErrorWithStatusCode{
+ OpenAIError: *err,
+ StatusCode: http.StatusBadRequest,
+ }
+ return
+ }
+
+ openaiResponse = &types.ChatCompletionResponse{
+ ID: fmt.Sprintf("chatcmpl-%s", common.GetUUID()),
+ Object: "chat.completion",
+ Created: common.GetTimestamp(),
+ Model: request.Model,
+ Choices: []types.ChatCompletionChoice{{
+ Index: 0,
+ Message: types.ChatCompletionMessage{
+ Role: types.ChatMessageRoleAssistant,
+ Content: response.Result.Response,
+ },
+ FinishReason: types.FinishReasonStop,
+ }},
+ }
+
+ completionTokens := common.CountTokenText(response.Result.Response, request.Model)
+
+ p.Usage.CompletionTokens = completionTokens
+ p.Usage.TotalTokens = p.Usage.PromptTokens + completionTokens
+ openaiResponse.Usage = p.Usage
+
+ return
+}
+
+func (p *CloudflareAIProvider) convertFromChatOpenai(request *types.ChatCompletionRequest) *ChatRequest {
+ chatRequest := &ChatRequest{
+ Stream: request.Stream,
+ MaxTokens: request.MaxTokens,
+ Messages: make([]Message, 0, len(request.Messages)),
+ }
+
+ for _, message := range request.Messages {
+ chatRequest.Messages = append(chatRequest.Messages, Message{
+ Role: message.Role,
+ Content: message.StringContent(),
+ })
+ }
+
+ return chatRequest
+}
+
+// 转换为OpenAI聊天流式请求体
+func (h *CloudflareAIStreamHandler) handlerStream(rawLine *[]byte, dataChan chan string, errChan chan error) {
+ // 如果rawLine 前缀不为data: 或者 meta:,则直接返回
+ if !strings.HasPrefix(string(*rawLine), "data: ") {
+ *rawLine = nil
+ return
+ }
+
+ *rawLine = (*rawLine)[6:]
+
+ if strings.HasPrefix(string(*rawLine), "[DONE]") {
+ h.convertToOpenaiStream(nil, dataChan, true)
+ errChan <- io.EOF
+ *rawLine = requester.StreamClosed
+ return
+ }
+
+ chatResponse := &ChatResult{}
+ err := json.Unmarshal(*rawLine, chatResponse)
+ if err != nil {
+ errChan <- common.ErrorToOpenAIError(err)
+ return
+ }
+
+ h.convertToOpenaiStream(chatResponse, dataChan, false)
+}
+
+func (h *CloudflareAIStreamHandler) convertToOpenaiStream(chatResponse *ChatResult, dataChan chan string, isStop bool) {
+ streamResponse := types.ChatCompletionStreamResponse{
+ ID: fmt.Sprintf("chatcmpl-%s", common.GetUUID()),
+ Object: "chat.completion.chunk",
+ Created: common.GetTimestamp(),
+ Model: h.Request.Model,
+ }
+
+ choice := types.ChatCompletionStreamChoice{
+ Index: 0,
+ Delta: types.ChatCompletionStreamChoiceDelta{
+ Role: types.ChatMessageRoleAssistant,
+ Content: "",
+ },
+ }
+
+ if isStop {
+ choice.FinishReason = types.FinishReasonStop
+ } else {
+ choice.Delta.Content = chatResponse.Response
+
+ h.Usage.CompletionTokens += common.CountTokenText(chatResponse.Response, h.Request.Model)
+ h.Usage.TotalTokens = h.Usage.PromptTokens + h.Usage.CompletionTokens
+ }
+
+ streamResponse.Choices = []types.ChatCompletionStreamChoice{choice}
+ responseBody, _ := json.Marshal(streamResponse)
+ dataChan <- string(responseBody)
+
+}
diff --git a/providers/cloudflareAI/image_generations.go b/providers/cloudflareAI/image_generations.go
new file mode 100644
index 00000000..feb475db
--- /dev/null
+++ b/providers/cloudflareAI/image_generations.go
@@ -0,0 +1,62 @@
+package cloudflareAI
+
+import (
+ "encoding/base64"
+ "io"
+ "net/http"
+ "one-api/common"
+ "one-api/types"
+ "time"
+)
+
+func (p *CloudflareAIProvider) CreateImageGenerations(request *types.ImageRequest) (*types.ImageResponse, *types.OpenAIErrorWithStatusCode) {
+ // 获取请求地址
+ fullRequestURL := p.GetFullRequestURL(request.Model)
+ if fullRequestURL == "" {
+ return nil, common.ErrorWrapper(nil, "invalid_cloudflare_ai_config", http.StatusInternalServerError)
+ }
+
+ // 获取请求头
+ headers := p.GetRequestHeaders()
+ cfRequest := convertFromIamgeOpenai(request)
+
+ // 创建请求
+ req, err := p.Requester.NewRequest(http.MethodPost, fullRequestURL, p.Requester.WithBody(cfRequest), p.Requester.WithHeader(headers))
+ if err != nil {
+ return nil, common.ErrorWrapper(err, "new_request_failed", http.StatusInternalServerError)
+ }
+ defer req.Body.Close()
+
+ resp, errWithCode := p.Requester.SendRequestRaw(req)
+ if errWithCode != nil {
+ return nil, errWithCode
+ }
+
+ defer resp.Body.Close()
+
+ if resp.Header.Get("Content-Type") != "image/png" {
+ return nil, common.StringErrorWrapper("invalid_image_response", "invalid_image_response", http.StatusInternalServerError)
+ }
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return nil, common.ErrorWrapper(err, "read_response_failed", http.StatusInternalServerError)
+ }
+
+ base64Image := base64.StdEncoding.EncodeToString(body)
+ openaiResponse := &types.ImageResponse{
+ Created: time.Now().Unix(),
+ Data: []types.ImageResponseDataInner{{B64JSON: base64Image}},
+ }
+
+ p.Usage.PromptTokens = 1000
+
+ return openaiResponse, nil
+
+}
+
+func convertFromIamgeOpenai(request *types.ImageRequest) *ImageRequest {
+ return &ImageRequest{
+ Prompt: request.Prompt,
+ }
+}
diff --git a/providers/cloudflareAI/transcriptions.go b/providers/cloudflareAI/transcriptions.go
new file mode 100644
index 00000000..6e94ccc3
--- /dev/null
+++ b/providers/cloudflareAI/transcriptions.go
@@ -0,0 +1,94 @@
+package cloudflareAI
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "one-api/common"
+ "one-api/common/requester"
+ "one-api/types"
+)
+
+func (p *CloudflareAIProvider) CreateTranscriptions(request *types.AudioRequest) (*types.AudioResponseWrapper, *types.OpenAIErrorWithStatusCode) {
+ req, errWithCode := p.getRequestAudioBody(request.Model, request)
+ if errWithCode != nil {
+ return nil, errWithCode
+ }
+ defer req.Body.Close()
+
+ var resp *http.Response
+ var err error
+
+ audioResponse := &AudioResponse{}
+ resp, errWithCode = p.Requester.SendRequest(req, audioResponse, false)
+ if errWithCode != nil {
+ return nil, errWithCode
+ }
+
+ errWithOP := errorHandle(&audioResponse.CloudflareAIError)
+ if errWithOP != nil {
+ errWithCode = &types.OpenAIErrorWithStatusCode{
+ OpenAIError: *errWithOP,
+ StatusCode: http.StatusBadRequest,
+ }
+ return nil, errWithCode
+ }
+
+ chatResult := audioResponse.Result
+
+ audioResponseWrapper := &types.AudioResponseWrapper{}
+ audioResponseWrapper.Headers = map[string]string{
+ "Content-Type": resp.Header.Get("Content-Type"),
+ }
+
+ audioResponseWrapper.Body, err = json.Marshal(&chatResult)
+ if err != nil {
+ return nil, common.ErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError)
+ }
+
+ completionTokens := common.CountTokenText(chatResult.Text, request.Model)
+
+ p.Usage.CompletionTokens = completionTokens
+ p.Usage.TotalTokens = p.Usage.PromptTokens + p.Usage.CompletionTokens
+
+ return audioResponseWrapper, nil
+}
+
+func (p *CloudflareAIProvider) getRequestAudioBody(ModelName string, request *types.AudioRequest) (*http.Request, *types.OpenAIErrorWithStatusCode) {
+ // 获取请求地址
+ fullRequestURL := p.GetFullRequestURL(ModelName)
+
+ // 获取请求头
+ headers := p.GetRequestHeaders()
+ // 创建请求
+ var req *http.Request
+ var err error
+
+ var formBody bytes.Buffer
+ builder := p.Requester.CreateFormBuilder(&formBody)
+ if err := audioMultipartForm(request, builder); err != nil {
+ return nil, common.ErrorWrapper(err, "create_form_builder_failed", http.StatusInternalServerError)
+ }
+ req, err = p.Requester.NewRequest(
+ http.MethodPost,
+ fullRequestURL,
+ p.Requester.WithBody(&formBody),
+ p.Requester.WithHeader(headers),
+ p.Requester.WithContentType(builder.FormDataContentType()))
+ req.ContentLength = int64(formBody.Len())
+
+ if err != nil {
+ return nil, common.ErrorWrapper(err, "new_request_failed", http.StatusInternalServerError)
+ }
+
+ return req, nil
+}
+
+func audioMultipartForm(request *types.AudioRequest, b requester.FormBuilder) error {
+ err := b.CreateFormFile("file", request.File)
+ if err != nil {
+ return fmt.Errorf("creating form file: %w", err)
+ }
+ return b.Close()
+}
diff --git a/providers/cloudflareAI/type.go b/providers/cloudflareAI/type.go
new file mode 100644
index 00000000..6687aef6
--- /dev/null
+++ b/providers/cloudflareAI/type.go
@@ -0,0 +1,60 @@
+package cloudflareAI
+
+import "one-api/types"
+
+type CloudflareAIError struct {
+ Error []struct {
+ Code int `json:"code"`
+ Message string `json:"message"`
+ } `json:"errors,omitempty"`
+ Success bool `json:"success"`
+}
+
+type ImageRequest struct {
+ Prompt string `json:"prompt"`
+ Image interface{} `json:"image,omitempty"` // 可以是 string 或者 ImageObject
+ Mask interface{} `json:"mask,omitempty"` // 可以是 string 或者 MaskObject
+ NumSteps int `json:"num_steps,omitempty"`
+ Strength float64 `json:"strength,omitempty"`
+ Guidance float64 `json:"guidance,omitempty"`
+}
+
+type ImageObject struct {
+ Image []float64 `json:"image"`
+}
+
+type MaskObject struct {
+ Mask []float64 `json:"mask"`
+}
+
+type ChatRequest struct {
+ Messages []Message `json:"messages"`
+ Stream bool `json:"stream,omitempty"`
+ MaxTokens int `json:"max_tokens,omitempty"`
+}
+
+type Message struct {
+ Role string `json:"role"`
+ Content string `json:"content"`
+}
+
+type ChatRespone struct {
+ Result ChatResult `json:"result,omitempty"`
+ CloudflareAIError
+}
+
+type ChatResult struct {
+ Response string `json:"response"`
+}
+
+type AudioResponse struct {
+ Result AudioResult `json:"result,omitempty"`
+ CloudflareAIError
+}
+
+type AudioResult struct {
+ Text string `json:"text,omitempty"`
+ WordCount int `json:"word_count,omitempty"`
+ Words []types.AudioWordsList `json:"words,omitempty"`
+ Vtt string `json:"vtt,omitempty"`
+}
diff --git a/providers/providers.go b/providers/providers.go
index a28cf708..875ed859 100644
--- a/providers/providers.go
+++ b/providers/providers.go
@@ -11,6 +11,7 @@ import (
"one-api/providers/base"
"one-api/providers/bedrock"
"one-api/providers/claude"
+ "one-api/providers/cloudflareAI"
"one-api/providers/deepseek"
"one-api/providers/gemini"
"one-api/providers/groq"
@@ -54,6 +55,7 @@ func init() {
providerFactories[common.ChannelTypeGroq] = groq.GroqProviderFactory{}
providerFactories[common.ChannelTypeBedrock] = bedrock.BedrockProviderFactory{}
providerFactories[common.ChannelTypeMidjourney] = midjourney.MidjourneyProviderFactory{}
+ providerFactories[common.ChannelTypeCloudflareAI] = cloudflareAI.CloudflareAIProviderFactory{}
}
diff --git a/relay/util/type.go b/relay/util/type.go
index a3ea0300..d644543c 100644
--- a/relay/util/type.go
+++ b/relay/util/type.go
@@ -7,23 +7,24 @@ var ModelOwnedBy map[int]string
func init() {
ModelOwnedBy = map[int]string{
- common.ChannelTypeOpenAI: "OpenAI",
- common.ChannelTypeAnthropic: "Anthropic",
- common.ChannelTypeBaidu: "Baidu",
- common.ChannelTypePaLM: "Google PaLM",
- common.ChannelTypeGemini: "Google Gemini",
- common.ChannelTypeZhipu: "Zhipu",
- common.ChannelTypeAli: "Ali",
- common.ChannelTypeXunfei: "Xunfei",
- common.ChannelType360: "360",
- common.ChannelTypeTencent: "Tencent",
- common.ChannelTypeBaichuan: "Baichuan",
- common.ChannelTypeMiniMax: "MiniMax",
- common.ChannelTypeDeepseek: "Deepseek",
- common.ChannelTypeMoonshot: "Moonshot",
- common.ChannelTypeMistral: "Mistral",
- common.ChannelTypeGroq: "Groq",
- common.ChannelTypeLingyi: "Lingyiwanwu",
- common.ChannelTypeMidjourney: "Midjourney",
+ common.ChannelTypeOpenAI: "OpenAI",
+ common.ChannelTypeAnthropic: "Anthropic",
+ common.ChannelTypeBaidu: "Baidu",
+ common.ChannelTypePaLM: "Google PaLM",
+ common.ChannelTypeGemini: "Google Gemini",
+ common.ChannelTypeZhipu: "Zhipu",
+ common.ChannelTypeAli: "Ali",
+ common.ChannelTypeXunfei: "Xunfei",
+ common.ChannelType360: "360",
+ common.ChannelTypeTencent: "Tencent",
+ common.ChannelTypeBaichuan: "Baichuan",
+ common.ChannelTypeMiniMax: "MiniMax",
+ common.ChannelTypeDeepseek: "Deepseek",
+ common.ChannelTypeMoonshot: "Moonshot",
+ common.ChannelTypeMistral: "Mistral",
+ common.ChannelTypeGroq: "Groq",
+ common.ChannelTypeLingyi: "Lingyiwanwu",
+ common.ChannelTypeMidjourney: "Midjourney",
+ common.ChannelTypeCloudflareAI: "Cloudflare AI",
}
}
diff --git a/types/audio.go b/types/audio.go
index d1a1ede9..52214bad 100644
--- a/types/audio.go
+++ b/types/audio.go
@@ -20,11 +20,18 @@ type AudioRequest struct {
}
type AudioResponse struct {
- Task string `json:"task,omitempty"`
- Language string `json:"language,omitempty"`
- Duration float64 `json:"duration,omitempty"`
- Segments any `json:"segments,omitempty"`
- Text string `json:"text"`
+ Task string `json:"task,omitempty"`
+ Language string `json:"language,omitempty"`
+ Duration float64 `json:"duration,omitempty"`
+ Segments any `json:"segments,omitempty"`
+ Text string `json:"text"`
+ Words []AudioWordsList `json:"words,omitempty"`
+}
+
+type AudioWordsList struct {
+ Word string `json:"word"`
+ Start float64 `json:"start"`
+ End float64 `json:"end"`
}
type AudioResponseWrapper struct {
diff --git a/web/src/constants/ChannelConstants.js b/web/src/constants/ChannelConstants.js
index 376ea6ed..e66617d4 100644
--- a/web/src/constants/ChannelConstants.js
+++ b/web/src/constants/ChannelConstants.js
@@ -139,6 +139,13 @@ export const CHANNEL_OPTIONS = {
color: 'orange',
url: ''
},
+ 35: {
+ key: 35,
+ text: 'Cloudflare AI',
+ value: 35,
+ color: 'orange',
+ url: ''
+ },
24: {
key: 24,
text: 'Azure Speech',
diff --git a/web/src/views/Channel/component/EditModal.js b/web/src/views/Channel/component/EditModal.js
index fd75c738..36d24719 100644
--- a/web/src/views/Channel/component/EditModal.js
+++ b/web/src/views/Channel/component/EditModal.js
@@ -351,27 +351,29 @@ const EditModal = ({ open, channelId, onCancel, onOk, groupOptions }) => {
)}
-
- {inputLabel.base_url}
-
- {touched.base_url && errors.base_url ? (
-
- {errors.base_url}
-
- ) : (
- {inputPrompt.base_url}
- )}
-
+ {inputPrompt.base_url && (
+
+ {inputLabel.base_url}
+
+ {touched.base_url && errors.base_url ? (
+
+ {errors.base_url}
+
+ ) : (
+ {inputPrompt.base_url}
+ )}
+
+ )}
{inputPrompt.other && (
diff --git a/web/src/views/Channel/type/Config.js b/web/src/views/Channel/type/Config.js
index 1a7b9d2a..bd60f4b8 100644
--- a/web/src/views/Channel/type/Config.js
+++ b/web/src/views/Channel/type/Config.js
@@ -262,6 +262,27 @@ const typeConfig = {
model_mapping: ''
},
modelGroup: 'Midjourney'
+ },
+ 35: {
+ input: {
+ models: [
+ '@cf/stabilityai/stable-diffusion-xl-base-1.0',
+ '@cf/lykon/dreamshaper-8-lcm',
+ '@cf/bytedance/stable-diffusion-xl-lightning',
+ '@cf/qwen/qwen1.5-7b-chat-awq',
+ '@cf/qwen/qwen1.5-14b-chat-awq',
+ '@hf/google/gemma-7b-it',
+ '@hf/thebloke/deepseek-coder-6.7b-base-awq',
+ '@hf/thebloke/llama-2-13b-chat-awq',
+ '@cf/openai/whisper'
+ ],
+ test_model: '@hf/google/gemma-7b-it'
+ },
+ prompt: {
+ key: '按照如下格式输入:CLOUDFLARE_ACCOUNT_ID|CLOUDFLARE_API_TOKEN',
+ base_url: ''
+ },
+ modelGroup: 'Cloudflare AI'
}
};