mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/1Panel-dev/1Panel.git
synced 2026-09-20 08:03:55 +08:00
feat: Optimize application upgrade logic (#13549)
* feat: Optimize application upgrade logic * feat: Optimize application upgrade logic * feat: Optimize application upgrade logic
This commit is contained in:
@@ -40,20 +40,21 @@ type Meta struct {
|
||||
var catalog = map[string]Meta{
|
||||
"custom": {
|
||||
Key: "custom", DisplayName: "Custom", Sort: 10, DefaultAPIType: "openai-completions", EnvKey: "CUSTOM_API_KEY",
|
||||
APIConfigs: editableAPIConfigs(true, "openai-completions", "openai-responses", "anthropic-messages", "openai-images"),
|
||||
APIConfigs: editableAPIConfigs(true, "openai-completions", "openai-responses", "anthropic-messages", "openai-images", "openai-embeddings"),
|
||||
},
|
||||
"ollama": {
|
||||
Key: "ollama", DisplayName: "Ollama", Sort: 15, DefaultAPIType: "openai-responses",
|
||||
APIConfigs: editableAPIConfigs(false, "openai-responses", "openai-completions"),
|
||||
APIConfigs: editableAPIConfigs(false, "openai-responses", "openai-completions", "openai-embeddings"),
|
||||
},
|
||||
"vllm": {
|
||||
Key: "vllm", DisplayName: "vLLM", Sort: 20, DefaultAPIType: "openai-completions", EnvKey: "VLLM_API_KEY",
|
||||
APIConfigs: editableAPIConfigs(false, "openai-completions", "openai-responses", "anthropic-messages", "openai-images"),
|
||||
APIConfigs: editableAPIConfigs(false, "openai-completions", "openai-responses", "anthropic-messages", "openai-images", "openai-embeddings"),
|
||||
},
|
||||
"deepseek": {
|
||||
Key: "deepseek", DisplayName: "DeepSeek", Sort: 25, DefaultAPIType: "openai-completions", EnvKey: "DEEPSEEK_API_KEY",
|
||||
APIConfigs: []APIConfig{
|
||||
{APIType: "openai-completions", BaseURL: "https://api.deepseek.com"},
|
||||
{APIType: "openai-responses", BaseURL: "https://api.deepseek.com"},
|
||||
anthropicAPIConfig("https://api.deepseek.com/anthropic", AuthModeXAPIKey),
|
||||
},
|
||||
Models: []Model{{ID: "deepseek-v4-flash", Name: "deepseek-v4-flash"}, {ID: "deepseek-v4-pro", Name: "deepseek-v4-pro"}},
|
||||
@@ -131,6 +132,10 @@ var catalog = map[string]Meta{
|
||||
{APIType: "openai-responses", BaseURL: "https://api.openai.com/v1"},
|
||||
{APIType: "openai-completions", BaseURL: "https://api.openai.com/v1"},
|
||||
{APIType: "openai-images", BaseURL: "https://api.openai.com/v1"},
|
||||
{APIType: "openai-embeddings", BaseURL: "https://api.openai.com/v1", Models: []Model{
|
||||
{ID: "text-embedding-3-small", Name: "text-embedding-3-small"},
|
||||
{ID: "text-embedding-3-large", Name: "text-embedding-3-large"},
|
||||
}},
|
||||
},
|
||||
Models: []Model{{ID: "gpt-5.4", Name: "gpt-5.4"}, {ID: "gpt-5.4-pro", Name: "gpt-5.4-pro"}, {ID: "gpt-5.4-mini", Name: "gpt-5.4-mini"}, {ID: "gpt-5.4-nano", Name: "gpt-5.4-nano"}},
|
||||
},
|
||||
@@ -296,7 +301,7 @@ func DefaultModels(key, apiType string) []Model {
|
||||
if len(config.Models) > 0 {
|
||||
return append([]Model(nil), config.Models...)
|
||||
}
|
||||
if IsImageAPIType(config.APIType) {
|
||||
if IsImageAPIType(config.APIType) || IsEmbeddingAPIType(config.APIType) {
|
||||
return nil
|
||||
}
|
||||
break
|
||||
@@ -359,7 +364,7 @@ func ResolveBaseURL(key, apiType, requested string) (string, error) {
|
||||
if err != nil || parsed.Scheme == "" || parsed.Host == "" {
|
||||
return "", fmt.Errorf("invalid base url")
|
||||
}
|
||||
if key == "custom" && IsImageAPIType(config.APIType) {
|
||||
if key == "custom" && (IsImageAPIType(config.APIType) || IsEmbeddingAPIType(config.APIType)) {
|
||||
return baseURL, nil
|
||||
}
|
||||
parsed.Path = normalizeEndpointPath(config.APIType, parsed.Path)
|
||||
@@ -381,6 +386,8 @@ func normalizeEndpointPath(apiType, value string) string {
|
||||
suffixes = []string{"/responses"}
|
||||
case "anthropic-messages":
|
||||
suffixes = []string{"/v1/messages", "/messages"}
|
||||
case "openai-embeddings":
|
||||
suffixes = []string{"/v1/embeddings", "/embeddings"}
|
||||
}
|
||||
for _, suffix := range suffixes {
|
||||
if strings.HasSuffix(strings.ToLower(path), suffix) {
|
||||
@@ -390,6 +397,10 @@ func normalizeEndpointPath(apiType, value string) string {
|
||||
return path
|
||||
}
|
||||
|
||||
func IsEmbeddingAPIType(apiType string) bool {
|
||||
return apiType == "openai-embeddings"
|
||||
}
|
||||
|
||||
func IsImageAPIType(apiType string) bool {
|
||||
switch apiType {
|
||||
case "openai-images", "dashscope-images", "minimax-images", "openrouter-images":
|
||||
|
||||
@@ -23,7 +23,7 @@ func BuildOpenClawProviderPatch(provider, modelName, apiType, authMode, baseURL,
|
||||
if _, ok := FindAPIConfig(provider, resolvedAPIType); !ok {
|
||||
resolvedAPIType = DefaultAPIType(provider)
|
||||
}
|
||||
if IsImageAPIType(resolvedAPIType) {
|
||||
if IsImageAPIType(resolvedAPIType) || IsEmbeddingAPIType(resolvedAPIType) {
|
||||
return nil, fmt.Errorf("api type %s does not support text generation", resolvedAPIType)
|
||||
}
|
||||
resolvedAuthMode, err := ResolveAuthMode(provider, resolvedAPIType, authMode)
|
||||
|
||||
@@ -81,6 +81,10 @@ func BuildVerifyRequest(provider, apiType, authMode, baseURL, apiKey, model stri
|
||||
}
|
||||
|
||||
switch apiType {
|
||||
case "openai-embeddings":
|
||||
request.URL = embeddingVerifyURL(baseURL)
|
||||
headers["Authorization"] = "Bearer " + apiKey
|
||||
request.Body = mustJSON(map[string]interface{}{"model": model, "input": "ping"})
|
||||
case "openai-images":
|
||||
request.URL = imageVerifyURL(provider, baseURL, "/images/generations")
|
||||
headers["Authorization"] = "Bearer " + apiKey
|
||||
@@ -131,6 +135,17 @@ func BuildVerifyRequest(provider, apiType, authMode, baseURL, apiKey, model stri
|
||||
return request
|
||||
}
|
||||
|
||||
func embeddingVerifyURL(baseURL string) string {
|
||||
lowerBaseURL := strings.ToLower(baseURL)
|
||||
if strings.HasSuffix(lowerBaseURL, "/embeddings") {
|
||||
return baseURL
|
||||
}
|
||||
if strings.HasSuffix(lowerBaseURL, "/v1") {
|
||||
return baseURL + "/embeddings"
|
||||
}
|
||||
return baseURL + "/v1/embeddings"
|
||||
}
|
||||
|
||||
func imageVerifyURL(provider, baseURL, endpoint string) string {
|
||||
if provider == "custom" || strings.HasSuffix(strings.ToLower(baseURL), endpoint) {
|
||||
return baseURL
|
||||
|
||||
@@ -86,7 +86,7 @@ func (a AgentAccountRepo) CountTextByProviders(providers []string) (map[string]i
|
||||
Model(&model.AgentAccount{}).
|
||||
Select("provider, COUNT(*) as count").
|
||||
Where("provider IN ?", normalizedProviders).
|
||||
Where("api_type NOT LIKE ?", "%-images").
|
||||
Scopes(WithTextAPIType()).
|
||||
Group("provider").
|
||||
Scan(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -108,7 +108,7 @@ func WithByAPIType(apiType string) DBOption {
|
||||
|
||||
func WithTextAPIType() DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("api_type NOT LIKE ?", "%-images")
|
||||
return g.Where("api_type NOT LIKE ? AND api_type <> ?", "%-images", "openai-embeddings")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ func resolveAgentAccountInput(provider, apiType, authMode, apiKey, baseURL, mode
|
||||
return resolvedAgentAccountInput{}, buserr.New("ErrAgentAccountModelsRequired")
|
||||
}
|
||||
imageAPI := providercatalog.IsImageAPIType(resolvedAPIType)
|
||||
if validateAvailability && (imageAPI || !providercatalog.SkipVerification(provider)) {
|
||||
if validateAvailability && (imageAPI || providercatalog.IsEmbeddingAPIType(resolvedAPIType) || !providercatalog.SkipVerification(provider)) {
|
||||
if err := providercatalog.VerifyAccount(provider, resolvedAPIType, resolvedAuthMode, resolvedBaseURL, resolvedAPIKey, modelID); err != nil {
|
||||
return resolvedAgentAccountInput{}, err
|
||||
}
|
||||
|
||||
@@ -426,7 +426,7 @@ func (u *appUpgradeContext) cutover(t *task.Task) error {
|
||||
|
||||
logStr := fmt.Sprintf("%s %s", i18n.GetMsgByKey("Run"), i18n.GetMsgByKey("App"))
|
||||
t.LogStart(logStr)
|
||||
if out, upErr := compose.UpWithoutPull(u.original.GetComposePath()); upErr != nil {
|
||||
if out, upErr := compose.UpWithoutBuild(u.original.GetComposePath()); upErr != nil {
|
||||
if out != "" {
|
||||
upErr = fmt.Errorf("%s: %w", out, upErr)
|
||||
}
|
||||
@@ -545,7 +545,7 @@ func (u *appUpgradeContext) rollback(t *task.Task) (rollbackErr error) {
|
||||
return u.finishRollback()
|
||||
}
|
||||
if u.phase < appUpgradeMutated {
|
||||
if out, err := compose.UpWithoutPull(u.original.GetComposePath()); err != nil {
|
||||
if out, err := compose.UpWithoutBuild(u.original.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
err = fmt.Errorf("%s: %w", out, err)
|
||||
}
|
||||
@@ -563,14 +563,14 @@ func (u *appUpgradeContext) rollback(t *task.Task) (rollbackErr error) {
|
||||
if u.backupFile != "" {
|
||||
_ = u.restoreManagedFiles()
|
||||
if err := handleAppRecover(&u.original, t, u.backupFile, true, "", ""); err != nil {
|
||||
_, _ = compose.UpWithoutPull(u.original.GetComposePath())
|
||||
_, _ = compose.UpWithoutBuild(u.original.GetComposePath())
|
||||
return errors.Join(rollbackErr, err)
|
||||
}
|
||||
} else {
|
||||
if err := u.restoreManagedFiles(); err != nil {
|
||||
return errors.Join(rollbackErr, err)
|
||||
}
|
||||
if out, err := compose.UpWithoutPull(u.original.GetComposePath()); err != nil {
|
||||
if out, err := compose.UpWithoutBuild(u.original.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
err = fmt.Errorf("%s: %w", out, err)
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverF
|
||||
}
|
||||
defer func() {
|
||||
if isRollback {
|
||||
_, _ = compose.UpWithoutPull(install.GetComposePath())
|
||||
_, _ = compose.UpWithoutBuild(install.GetComposePath())
|
||||
} else {
|
||||
_, _ = compose.Up(install.GetComposePath())
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ func Up(filePath string, projectName ...string) (string, error) {
|
||||
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(base, args...)
|
||||
}
|
||||
|
||||
func UpWithoutPull(filePath string, projectName ...string) (string, error) {
|
||||
func UpWithoutBuild(filePath string, projectName ...string) (string, error) {
|
||||
if err := checkCmd(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -58,11 +58,11 @@ func UpWithoutPull(filePath string, projectName ...string) (string, error) {
|
||||
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(base, args...)
|
||||
}
|
||||
|
||||
func upArgs(filePath string, withoutPull bool) []string {
|
||||
func upArgs(filePath string, withoutBuild bool) []string {
|
||||
args := loadFiles(filePath)
|
||||
args = append(args, "up", "-d")
|
||||
if withoutPull {
|
||||
args = append(args, "--pull", "never", "--no-build")
|
||||
if withoutBuild {
|
||||
args = append(args, "--no-build")
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/dotenv"
|
||||
"github.com/compose-spec/compose-go/v2/loader"
|
||||
"github.com/compose-spec/compose-go/v2/template"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/joho/godotenv"
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -91,7 +92,7 @@ func (e *Environment) UnmarshalYAML(value *yaml.Node) error {
|
||||
}
|
||||
|
||||
func GetImagesFromDockerCompose(env, yml []byte) ([]string, error) {
|
||||
envVars, err := loadEnvFile(env)
|
||||
envVars, err := dotenv.Parse(bytes.NewReader(env))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load env failed: %v", err)
|
||||
}
|
||||
@@ -104,43 +105,16 @@ func GetImagesFromDockerCompose(env, yml []byte) ([]string, error) {
|
||||
var images []string
|
||||
for _, service := range compose.Services {
|
||||
if service.Image != "" {
|
||||
resolvedImage := replaceEnvVars(service.Image, envVars)
|
||||
resolvedImage, err := template.Substitute(service.Image, func(key string) (string, bool) {
|
||||
value, ok := envVars[key]
|
||||
return value, ok
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resolve image failed: %v", err)
|
||||
}
|
||||
images = append(images, resolvedImage)
|
||||
}
|
||||
}
|
||||
|
||||
return images, nil
|
||||
}
|
||||
|
||||
func loadEnvFile(env []byte) (map[string]string, error) {
|
||||
envVars := make(map[string]string)
|
||||
|
||||
scanner := bufio.NewScanner(bytes.NewReader(env))
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
if len(parts) == 2 {
|
||||
key := strings.TrimSpace(parts[0])
|
||||
value := strings.TrimSpace(parts[1])
|
||||
value = strings.Trim(value, `"'`)
|
||||
envVars[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return envVars, scanner.Err()
|
||||
}
|
||||
|
||||
func replaceEnvVars(input string, envVars map[string]string) string {
|
||||
return re.GetRegex(re.ComposeEnvVarPattern).ReplaceAllStringFunc(input, func(match string) string {
|
||||
varName := match[2 : len(match)-1]
|
||||
if value, exists := envVars[varName]; exists {
|
||||
return value
|
||||
}
|
||||
return match
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<template>
|
||||
<el-tag
|
||||
size="small"
|
||||
effect="dark"
|
||||
effect="light"
|
||||
:type="tagType"
|
||||
:class="{ 'api-type-tag--responses': apiType === 'openai-responses' }"
|
||||
:class="{
|
||||
'api-type-tag--responses': apiType === 'openai-responses',
|
||||
'api-type-tag--embedding': apiType === 'openai-embeddings',
|
||||
}"
|
||||
>
|
||||
{{ apiType || '-' }}
|
||||
</el-tag>
|
||||
@@ -37,8 +40,14 @@ const tagType = computed<TagType>(() => {
|
||||
|
||||
<style scoped lang="scss">
|
||||
.api-type-tag--responses {
|
||||
--el-tag-bg-color: #7c3aed;
|
||||
--el-tag-border-color: #7c3aed;
|
||||
--el-tag-text-color: #ffffff;
|
||||
--el-tag-bg-color: #f3e8ff;
|
||||
--el-tag-border-color: #d8b4fe;
|
||||
--el-tag-text-color: #7e22ce;
|
||||
}
|
||||
|
||||
.api-type-tag--embedding {
|
||||
--el-tag-bg-color: #e6f4f1;
|
||||
--el-tag-border-color: #a7d9d1;
|
||||
--el-tag-text-color: #0f766e;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1159,8 +1159,10 @@ const message = {
|
||||
imageGeneration: 'Image Generation',
|
||||
textModels: 'Text Models',
|
||||
textToImageModels: 'Text-to-Image Models',
|
||||
embeddingModels: 'Embedding Models',
|
||||
unauthorized: 'Unauthorized',
|
||||
imageCount: 'Image Count',
|
||||
requestExample: 'Request Example',
|
||||
availableModels: 'Available Models',
|
||||
modelGroupModels: 'Request Models',
|
||||
modelGroupModelsPlaceholder: 'Select or enter request model names',
|
||||
|
||||
@@ -1165,8 +1165,10 @@ const message = {
|
||||
imageGeneration: 'Generación de imágenes',
|
||||
textModels: 'Modelos de texto',
|
||||
textToImageModels: 'Modelos de texto a imagen',
|
||||
embeddingModels: 'Modelos de embeddings',
|
||||
unauthorized: 'Sin autorización',
|
||||
imageCount: 'Cantidad de imágenes',
|
||||
requestExample: 'Ejemplo de solicitud',
|
||||
availableModels: 'Modelos disponibles',
|
||||
modelGroupModels: 'Modelos solicitados',
|
||||
modelGroupModelsPlaceholder: 'Seleccione o ingrese nombres de modelos solicitados',
|
||||
|
||||
@@ -1145,8 +1145,10 @@ const message = {
|
||||
imageGeneration: 'تولید تصویر',
|
||||
textModels: 'مدلهای متنی',
|
||||
textToImageModels: 'مدلهای متن به تصویر',
|
||||
embeddingModels: 'مدلهای امبدینگ',
|
||||
unauthorized: 'بدون مجوز',
|
||||
imageCount: 'تعداد تصاویر',
|
||||
requestExample: 'نمونه درخواست',
|
||||
availableModels: 'مدلهای موجود',
|
||||
modelGroupModels: 'مدلهای درخواست',
|
||||
modelGroupModelsPlaceholder: 'نامهای مدل درخواست را انتخاب یا وارد کنید',
|
||||
|
||||
@@ -1148,8 +1148,10 @@ const message = {
|
||||
imageGeneration: '画像生成',
|
||||
textModels: 'テキストモデル',
|
||||
textToImageModels: 'テキスト画像生成モデル',
|
||||
embeddingModels: 'Embeddingモデル',
|
||||
unauthorized: '未承認',
|
||||
imageCount: '画像数',
|
||||
requestExample: 'リクエスト例',
|
||||
availableModels: '利用可能なモデル',
|
||||
modelGroupModels: 'リクエストモデル',
|
||||
modelGroupModelsPlaceholder: 'リクエストモデル名を選択または入力してください',
|
||||
|
||||
@@ -1137,8 +1137,10 @@ const message = {
|
||||
imageGeneration: '이미지 생성',
|
||||
textModels: '텍스트 모델',
|
||||
textToImageModels: '텍스트-이미지 모델',
|
||||
embeddingModels: '임베딩 모델',
|
||||
unauthorized: '권한 없음',
|
||||
imageCount: '이미지 수',
|
||||
requestExample: '요청 예시',
|
||||
availableModels: '사용 가능한 모델',
|
||||
modelGroupModels: '요청 모델',
|
||||
modelGroupModelsPlaceholder: '요청 모델 이름을 선택하거나 입력하세요',
|
||||
|
||||
@@ -1138,8 +1138,10 @@ const message = {
|
||||
imageGeneration: 'ການສ້າງຮູບພາບ',
|
||||
textModels: 'ໂມເດວຂໍ້ຄວາມ',
|
||||
textToImageModels: 'ໂມເດວສ້າງຮູບຈາກຂໍ້ຄວາມ',
|
||||
embeddingModels: 'ໂມເດວ Embedding',
|
||||
unauthorized: 'ບໍ່ມີສິດ',
|
||||
imageCount: 'ຈຳນວນຮູບ',
|
||||
requestExample: 'ຕົວຢ່າງຄຳຂໍ',
|
||||
availableModels: 'ໂມເດວທີ່ມີໃຫ້ໃຊ້',
|
||||
modelGroupModels: 'ໂມເດວທີ່ຮ້ອງຂໍ',
|
||||
modelGroupModelsPlaceholder: 'ເລືອກ ຫຼື ປ້ອນຊື່ໂມເດວທີ່ຮ້ອງຂໍ',
|
||||
|
||||
@@ -1170,8 +1170,10 @@ const message = {
|
||||
imageGeneration: 'Penjanaan Imej',
|
||||
textModels: 'Model Teks',
|
||||
textToImageModels: 'Model Teks-ke-Imej',
|
||||
embeddingModels: 'Model Embedding',
|
||||
unauthorized: 'Tidak dibenarkan',
|
||||
imageCount: 'Bilangan Imej',
|
||||
requestExample: 'Contoh Permintaan',
|
||||
availableModels: 'Model Tersedia',
|
||||
modelGroupModels: 'Model Permintaan',
|
||||
modelGroupModelsPlaceholder: 'Pilih atau masukkan nama model permintaan',
|
||||
|
||||
@@ -1167,8 +1167,10 @@ const message = {
|
||||
imageGeneration: 'Geração de imagens',
|
||||
textModels: 'Modelos de texto',
|
||||
textToImageModels: 'Modelos de texto para imagem',
|
||||
embeddingModels: 'Modelos de embedding',
|
||||
unauthorized: 'Não autorizado',
|
||||
imageCount: 'Quantidade de imagens',
|
||||
requestExample: 'Exemplo de solicitação',
|
||||
availableModels: 'Modelos disponíveis',
|
||||
modelGroupModels: 'Modelos solicitados',
|
||||
modelGroupModelsPlaceholder: 'Selecione ou informe nomes de modelos solicitados',
|
||||
|
||||
@@ -1158,8 +1158,10 @@ const message = {
|
||||
imageGeneration: 'Генерация изображений',
|
||||
textModels: 'Текстовые модели',
|
||||
textToImageModels: 'Модели генерации изображений по тексту',
|
||||
embeddingModels: 'Модели эмбеддингов',
|
||||
unauthorized: 'Нет доступа',
|
||||
imageCount: 'Количество изображений',
|
||||
requestExample: 'Пример запроса',
|
||||
availableModels: 'Доступные модели',
|
||||
modelGroupModels: 'Запрошенные модели',
|
||||
modelGroupModelsPlaceholder: 'Выберите или введите имена запрошенных моделей',
|
||||
|
||||
@@ -1167,8 +1167,10 @@ const message = {
|
||||
imageGeneration: 'Görsel Üretimi',
|
||||
textModels: 'Metin Modelleri',
|
||||
textToImageModels: 'Metinden Görsele Modeller',
|
||||
embeddingModels: 'Embedding Modelleri',
|
||||
unauthorized: 'Yetkisiz',
|
||||
imageCount: 'Görsel Sayısı',
|
||||
requestExample: 'İstek Örneği',
|
||||
availableModels: 'Kullanılabilir Modeller',
|
||||
modelGroupModels: 'İstek Modelleri',
|
||||
modelGroupModelsPlaceholder: 'İstek modeli adlarını seçin veya girin',
|
||||
|
||||
@@ -1102,8 +1102,10 @@ const message = {
|
||||
imageGeneration: '圖片生成',
|
||||
textModels: '文字模型',
|
||||
textToImageModels: '文生圖模型',
|
||||
embeddingModels: 'Embedding 模型',
|
||||
unauthorized: '未授權',
|
||||
imageCount: '圖片數量',
|
||||
requestExample: '請求範例',
|
||||
availableModels: '可用模型',
|
||||
modelGroupModels: '請求模型',
|
||||
modelGroupModelsPlaceholder: '請選擇或輸入請求模型名稱',
|
||||
|
||||
@@ -1116,8 +1116,10 @@ const message = {
|
||||
imageGeneration: '图片生成',
|
||||
textModels: '文本模型',
|
||||
textToImageModels: '文生图模型',
|
||||
embeddingModels: 'Embedding 模型',
|
||||
unauthorized: '未授权',
|
||||
imageCount: '图片数量',
|
||||
requestExample: '请求示例',
|
||||
availableModels: '可用模型',
|
||||
modelGroupModels: '请求模型',
|
||||
modelGroupModelsPlaceholder: '请选择或输入请求模型名',
|
||||
|
||||
@@ -299,6 +299,7 @@ const selectedAPIConfig = computed(() =>
|
||||
selectedProvider.value?.apiTypes.find((item) => item.apiType === form.apiType),
|
||||
);
|
||||
const isImageAPIType = computed(() => form.apiType.endsWith('-images'));
|
||||
const isEmbeddingAPIType = computed(() => form.apiType === 'openai-embeddings');
|
||||
const supportsModelDiscovery = computed(() => !form.id && Boolean(selectedAPIConfig.value?.supportsModelDiscovery));
|
||||
const defaultModels = computed(() => {
|
||||
if (selectedAPIConfig.value?.models.length) {
|
||||
@@ -314,7 +315,7 @@ const showInitialModel = computed(
|
||||
!showModelDiscovery.value,
|
||||
);
|
||||
const requiresVerifyModel = computed(
|
||||
() => form.validateAvailability && !isAgentAccountVerificationSkipped(form.provider),
|
||||
() => form.validateAvailability && (isEmbeddingAPIType.value || !isAgentAccountVerificationSkipped(form.provider)),
|
||||
);
|
||||
const verifyModelOptions = computed(() => (form.id ? editModels.value : defaultModels.value));
|
||||
const showVerifyModelSelect = computed(
|
||||
@@ -361,6 +362,11 @@ const apiTypeURLHints: Record<string, { requestPath: string; example: string; en
|
||||
example: 'https://openrouter.ai',
|
||||
endpointSuffixes: ['/api/v1/images'],
|
||||
},
|
||||
'openai-embeddings': {
|
||||
requestPath: '/v1/embeddings',
|
||||
example: 'http://127.0.0.1:8000/v1',
|
||||
endpointSuffixes: ['/v1/embeddings', '/embeddings'],
|
||||
},
|
||||
};
|
||||
const showAPITypeBaseURLTips = computed(() => Boolean(selectedAPIConfig.value?.editableBaseUrl));
|
||||
const apiTypeBaseURLHelper = computed(() => {
|
||||
|
||||
Reference in New Issue
Block a user