mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-22 16:00:51 +00:00
feat: Add support for text-to-image APIs. (#13380)
* feat: Add support for text-to-image APIs. * feat: Add support for text-to-image APIs.
This commit is contained in:
+31
-25
@@ -322,29 +322,31 @@ type AgentAccountModelDeleteReq struct {
|
||||
}
|
||||
|
||||
type AgentAccountCreateReq struct {
|
||||
Provider string `json:"provider" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
APIKey string `json:"apiKey" validate:"required"`
|
||||
RememberAPIKey bool `json:"rememberApiKey"`
|
||||
BaseURL string `json:"baseURL"`
|
||||
Models []AgentAccountModel `json:"models"`
|
||||
APIType string `json:"apiType" validate:"required"`
|
||||
AuthMode string `json:"authMode"`
|
||||
VerifyModel string `json:"verifyModel"`
|
||||
Remark string `json:"remark"`
|
||||
Provider string `json:"provider" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
APIKey string `json:"apiKey" validate:"required"`
|
||||
RememberAPIKey bool `json:"rememberApiKey"`
|
||||
BaseURL string `json:"baseURL"`
|
||||
Models []AgentAccountModel `json:"models"`
|
||||
APIType string `json:"apiType" validate:"required"`
|
||||
AuthMode string `json:"authMode"`
|
||||
VerifyModel string `json:"verifyModel"`
|
||||
ValidateAvailability *bool `json:"validateAvailability"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type AgentAccountUpdateReq struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
APIKey string `json:"apiKey" validate:"required"`
|
||||
RememberAPIKey bool `json:"rememberApiKey"`
|
||||
BaseURL string `json:"baseURL"`
|
||||
APIType string `json:"apiType" validate:"required"`
|
||||
AuthMode string `json:"authMode"`
|
||||
VerifyModel string `json:"verifyModel"`
|
||||
Remark string `json:"remark"`
|
||||
SyncAgents bool `json:"syncAgents"`
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
APIKey string `json:"apiKey" validate:"required"`
|
||||
RememberAPIKey bool `json:"rememberApiKey"`
|
||||
BaseURL string `json:"baseURL"`
|
||||
APIType string `json:"apiType" validate:"required"`
|
||||
AuthMode string `json:"authMode"`
|
||||
VerifyModel string `json:"verifyModel"`
|
||||
ValidateAvailability *bool `json:"validateAvailability"`
|
||||
Remark string `json:"remark"`
|
||||
SyncAgents bool `json:"syncAgents"`
|
||||
}
|
||||
|
||||
type AgentAccountVerifyReq struct {
|
||||
@@ -363,6 +365,8 @@ type AgentAccountDeleteReq struct {
|
||||
type AgentAccountSearch struct {
|
||||
PageInfo
|
||||
Provider string `json:"provider"`
|
||||
APIType string `json:"apiType"`
|
||||
TextOnly bool `json:"textOnly"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
@@ -394,11 +398,13 @@ type ProviderModelInfo struct {
|
||||
}
|
||||
|
||||
type ProviderAPIInfo struct {
|
||||
APIType string `json:"apiType"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
EditableBaseURL bool `json:"editableBaseUrl"`
|
||||
DefaultAuthMode string `json:"defaultAuthMode"`
|
||||
AuthModes []string `json:"authModes"`
|
||||
APIType string `json:"apiType"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
EditableBaseURL bool `json:"editableBaseUrl"`
|
||||
SupportsModelDiscovery bool `json:"supportsModelDiscovery"`
|
||||
DefaultAuthMode string `json:"defaultAuthMode"`
|
||||
AuthModes []string `json:"authModes"`
|
||||
Models []ProviderModelInfo `json:"models"`
|
||||
}
|
||||
|
||||
type ProviderInfo struct {
|
||||
|
||||
+129
-11
@@ -10,8 +10,10 @@ type APIConfig struct {
|
||||
APIType string
|
||||
BaseURL string
|
||||
EditableBaseURL bool
|
||||
DiscoverModels bool
|
||||
DefaultAuthMode string
|
||||
AuthModes []string
|
||||
Models []Model
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -27,6 +29,7 @@ type Model struct {
|
||||
type Meta struct {
|
||||
Key string
|
||||
DisplayName string
|
||||
DisplayNameKey string
|
||||
Sort uint
|
||||
DefaultAPIType string
|
||||
APIConfigs []APIConfig
|
||||
@@ -37,15 +40,15 @@ type Meta struct {
|
||||
var catalog = map[string]Meta{
|
||||
"custom": {
|
||||
Key: "custom", DisplayName: "Custom", Sort: 10, DefaultAPIType: "openai-completions", EnvKey: "CUSTOM_API_KEY",
|
||||
APIConfigs: editableAPIConfigs("openai-completions", "openai-responses", "anthropic-messages"),
|
||||
APIConfigs: editableAPIConfigs(true, "openai-completions", "openai-responses", "anthropic-messages", "openai-images", "dashscope-images", "minimax-images", "openrouter-images"),
|
||||
},
|
||||
"ollama": {
|
||||
Key: "ollama", DisplayName: "Ollama", Sort: 15, DefaultAPIType: "openai-responses",
|
||||
APIConfigs: editableAPIConfigs("openai-responses", "openai-completions"),
|
||||
APIConfigs: editableAPIConfigs(false, "openai-responses", "openai-completions"),
|
||||
},
|
||||
"vllm": {
|
||||
Key: "vllm", DisplayName: "vLLM", Sort: 20, DefaultAPIType: "openai-completions", EnvKey: "VLLM_API_KEY",
|
||||
APIConfigs: editableAPIConfigs("openai-completions", "openai-responses", "anthropic-messages"),
|
||||
APIConfigs: editableAPIConfigs(false, "openai-completions", "openai-responses", "anthropic-messages", "openai-images"),
|
||||
},
|
||||
"deepseek": {
|
||||
Key: "deepseek", DisplayName: "DeepSeek", Sort: 25, DefaultAPIType: "openai-completions", EnvKey: "DEEPSEEK_API_KEY",
|
||||
@@ -56,7 +59,7 @@ var catalog = map[string]Meta{
|
||||
Models: []Model{{ID: "deepseek-v4-flash", Name: "deepseek-v4-flash"}, {ID: "deepseek-v4-pro", Name: "deepseek-v4-pro"}},
|
||||
},
|
||||
"bailian-coding-plan": {
|
||||
Key: "bailian-coding-plan", DisplayName: "阿里云百炼 Coding Plan", Sort: 30, DefaultAPIType: "openai-completions", EnvKey: "QWEN_API_KEY",
|
||||
Key: "bailian-coding-plan", DisplayNameKey: "AIProviderBailianCodingPlan", Sort: 30, DefaultAPIType: "openai-completions", EnvKey: "QWEN_API_KEY",
|
||||
APIConfigs: []APIConfig{
|
||||
{APIType: "openai-completions", BaseURL: "https://coding.dashscope.aliyuncs.com/v1"},
|
||||
anthropicAPIConfig("https://coding.dashscope.aliyuncs.com/apps/anthropic", AuthModeBearer),
|
||||
@@ -75,7 +78,7 @@ var catalog = map[string]Meta{
|
||||
},
|
||||
},
|
||||
"ark-coding-plan": {
|
||||
Key: "ark-coding-plan", DisplayName: "方舟 Coding Plan", Sort: 35, DefaultAPIType: "openai-completions", EnvKey: "ARK_API_KEY",
|
||||
Key: "ark-coding-plan", DisplayNameKey: "AIProviderArkCodingPlan", Sort: 35, DefaultAPIType: "openai-completions", EnvKey: "ARK_API_KEY",
|
||||
APIConfigs: []APIConfig{
|
||||
{APIType: "openai-completions", BaseURL: "https://ark.cn-beijing.volces.com/api/coding/v3"},
|
||||
anthropicAPIConfig("https://ark.cn-beijing.volces.com/api/coding", AuthModeBearer),
|
||||
@@ -88,14 +91,18 @@ var catalog = map[string]Meta{
|
||||
},
|
||||
"zai": {
|
||||
Key: "zai", DisplayName: "Z.ai", Sort: 40, DefaultAPIType: "openai-completions", EnvKey: "ZAI_API_KEY",
|
||||
APIConfigs: []APIConfig{{APIType: "openai-completions", BaseURL: "https://open.bigmodel.cn/api/paas/v4", EditableBaseURL: true}},
|
||||
Models: []Model{{ID: "glm-5", Name: "GLM-5"}, {ID: "glm-4.7", Name: "GLM-4.7"}, {ID: "glm-4.7-flash", Name: "GLM-4.7-Flash"}, {ID: "glm-4.7-flashx", Name: "GLM-4.7-FlashX"}},
|
||||
APIConfigs: []APIConfig{
|
||||
{APIType: "openai-completions", BaseURL: "https://open.bigmodel.cn/api/paas/v4", EditableBaseURL: true},
|
||||
{APIType: "openai-images", BaseURL: "https://open.bigmodel.cn/api/paas/v4", EditableBaseURL: true},
|
||||
},
|
||||
Models: []Model{{ID: "glm-5", Name: "GLM-5"}, {ID: "glm-4.7", Name: "GLM-4.7"}, {ID: "glm-4.7-flash", Name: "GLM-4.7-Flash"}, {ID: "glm-4.7-flashx", Name: "GLM-4.7-FlashX"}},
|
||||
},
|
||||
"minimax": {
|
||||
Key: "minimax", DisplayName: "MiniMax (CN)", Sort: 45, DefaultAPIType: "anthropic-messages", EnvKey: "MINIMAX_API_KEY",
|
||||
APIConfigs: []APIConfig{
|
||||
anthropicAPIConfig("https://api.minimaxi.com/anthropic", AuthModeXAPIKey, AuthModeBearer),
|
||||
{APIType: "openai-completions", BaseURL: "https://api.minimaxi.com/v1"},
|
||||
{APIType: "minimax-images", BaseURL: "https://api.minimaxi.com"},
|
||||
},
|
||||
Models: []Model{{ID: "MiniMax-M3", Name: "MiniMax M3"}, {ID: "MiniMax-M2.7", Name: "MiniMax M2.7"}, {ID: "MiniMax-M2.7-highspeed", Name: "MiniMax M2.7 highspeed"}},
|
||||
},
|
||||
@@ -122,13 +129,17 @@ var catalog = map[string]Meta{
|
||||
APIConfigs: []APIConfig{
|
||||
{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"},
|
||||
},
|
||||
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"}},
|
||||
},
|
||||
"openrouter": {
|
||||
Key: "openrouter", DisplayName: "OpenRouter", Sort: 56, DefaultAPIType: "openai-completions", EnvKey: "OPENROUTER_API_KEY",
|
||||
APIConfigs: []APIConfig{{APIType: "openai-completions", BaseURL: "https://openrouter.ai/api/v1"}},
|
||||
Models: []Model{{ID: "openrouter/free", Name: "openrouter/free"}, {ID: "openrouter/auto", Name: "openrouter/auto"}},
|
||||
APIConfigs: []APIConfig{
|
||||
{APIType: "openai-completions", BaseURL: "https://openrouter.ai/api/v1"},
|
||||
{APIType: "openrouter-images", BaseURL: "https://openrouter.ai"},
|
||||
},
|
||||
Models: []Model{{ID: "openrouter/free", Name: "openrouter/free"}, {ID: "openrouter/auto", Name: "openrouter/auto"}},
|
||||
},
|
||||
"anthropic": {
|
||||
Key: "anthropic", DisplayName: "Anthropic", Sort: 60, DefaultAPIType: "anthropic-messages", EnvKey: "ANTHROPIC_API_KEY",
|
||||
@@ -145,9 +156,62 @@ var catalog = map[string]Meta{
|
||||
APIConfigs: []APIConfig{{APIType: "openai-completions", BaseURL: "https://api.moonshot.ai/v1"}},
|
||||
Models: []Model{{ID: "kimi-k2.5", Name: "Kimi K2.5"}, {ID: "kimi-k2-0905-preview", Name: "Kimi K2 0905 Preview"}, {ID: "kimi-k2-thinking", Name: "Kimi K2 Thinking"}},
|
||||
},
|
||||
"bailian": {
|
||||
Key: "bailian", DisplayNameKey: "AIProviderBailian", Sort: 31, DefaultAPIType: "openai-completions", EnvKey: "DASHSCOPE_API_KEY",
|
||||
APIConfigs: []APIConfig{
|
||||
{
|
||||
APIType: "openai-completions", BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
||||
DiscoverModels: true,
|
||||
Models: []Model{{ID: "qwen3.7-plus", Name: "qwen3.7-plus"}, {ID: "qwen3.6-plus", Name: "qwen3.6-plus"}, {ID: "qwen3.6-flash", Name: "qwen3.6-flash"}},
|
||||
},
|
||||
{
|
||||
APIType: "openai-responses", BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
||||
DiscoverModels: true,
|
||||
Models: []Model{{ID: "qwen3.7-plus", Name: "qwen3.7-plus"}, {ID: "qwen3.6-plus", Name: "qwen3.6-plus"}, {ID: "qwen3.6-flash", Name: "qwen3.6-flash"}},
|
||||
},
|
||||
{
|
||||
APIType: "anthropic-messages", BaseURL: "https://dashscope.aliyuncs.com/apps/anthropic",
|
||||
DefaultAuthMode: AuthModeBearer,
|
||||
AuthModes: []string{AuthModeBearer},
|
||||
Models: []Model{{ID: "qwen3.7-plus", Name: "qwen3.7-plus"}, {ID: "qwen3.6-plus", Name: "qwen3.6-plus"}, {ID: "qwen3.6-flash", Name: "qwen3.6-flash"}},
|
||||
},
|
||||
{
|
||||
APIType: "dashscope-images", BaseURL: "https://dashscope.aliyuncs.com",
|
||||
Models: []Model{
|
||||
{ID: "qwen-image-2.0-pro", Name: "qwen-image-2.0-pro"},
|
||||
{ID: "qwen-image-2.0", Name: "qwen-image-2.0"},
|
||||
{ID: "wan2.7-image-pro", Name: "wan2.7-image-pro"},
|
||||
{ID: "wan2.7-image", Name: "wan2.7-image"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"ark": {
|
||||
Key: "ark", DisplayNameKey: "AIProviderArk", Sort: 36, DefaultAPIType: "openai-completions", EnvKey: "ARK_API_KEY",
|
||||
APIConfigs: []APIConfig{
|
||||
{
|
||||
APIType: "openai-completions", BaseURL: "https://ark.cn-beijing.volces.com/api/v3",
|
||||
DiscoverModels: true,
|
||||
Models: []Model{{ID: "doubao-seed-2-0-pro-260215", Name: "doubao-seed-2-0-pro-260215"}, {ID: "doubao-seed-2-0-lite-260215", Name: "doubao-seed-2-0-lite-260215"}},
|
||||
},
|
||||
{
|
||||
APIType: "openai-responses", BaseURL: "https://ark.cn-beijing.volces.com/api/v3",
|
||||
DiscoverModels: true,
|
||||
Models: []Model{{ID: "doubao-seed-2-0-pro-260215", Name: "doubao-seed-2-0-pro-260215"}, {ID: "doubao-seed-2-0-lite-260215", Name: "doubao-seed-2-0-lite-260215"}},
|
||||
},
|
||||
{
|
||||
APIType: "openai-images", BaseURL: "https://ark.cn-beijing.volces.com/api/v3",
|
||||
Models: []Model{
|
||||
{ID: "doubao-seedream-5-0-260128", Name: "doubao-seedream-5-0-260128"},
|
||||
{ID: "doubao-seedream-5-0-lite-260128", Name: "doubao-seedream-5-0-lite-260128"},
|
||||
{ID: "doubao-seedream-4-5-251128", Name: "doubao-seedream-4-5-251128"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func editableAPIConfigs(apiTypes ...string) []APIConfig {
|
||||
func editableAPIConfigs(discoverModels bool, apiTypes ...string) []APIConfig {
|
||||
configs := make([]APIConfig, 0, len(apiTypes))
|
||||
for _, apiType := range apiTypes {
|
||||
if apiType == "anthropic-messages" {
|
||||
@@ -156,7 +220,11 @@ func editableAPIConfigs(apiTypes ...string) []APIConfig {
|
||||
configs = append(configs, config)
|
||||
continue
|
||||
}
|
||||
configs = append(configs, APIConfig{APIType: apiType, EditableBaseURL: true})
|
||||
configs = append(configs, APIConfig{
|
||||
APIType: apiType,
|
||||
EditableBaseURL: true,
|
||||
DiscoverModels: discoverModels && (apiType == "openai-completions" || apiType == "openai-responses"),
|
||||
})
|
||||
}
|
||||
return configs
|
||||
}
|
||||
@@ -203,12 +271,38 @@ func FindAPIConfig(key, apiType string) (APIConfig, bool) {
|
||||
}
|
||||
for _, config := range meta.APIConfigs {
|
||||
if config.APIType == target {
|
||||
config.AuthModes = append([]string(nil), config.AuthModes...)
|
||||
config.Models = append([]Model(nil), config.Models...)
|
||||
return config, true
|
||||
}
|
||||
}
|
||||
return APIConfig{}, false
|
||||
}
|
||||
|
||||
func DefaultModels(key, apiType string) []Model {
|
||||
meta, ok := catalog[key]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
target := strings.TrimSpace(apiType)
|
||||
if target == "" {
|
||||
target = meta.DefaultAPIType
|
||||
}
|
||||
for _, config := range meta.APIConfigs {
|
||||
if config.APIType != target {
|
||||
continue
|
||||
}
|
||||
if len(config.Models) > 0 {
|
||||
return append([]Model(nil), config.Models...)
|
||||
}
|
||||
if IsImageAPIType(config.APIType) {
|
||||
return nil
|
||||
}
|
||||
break
|
||||
}
|
||||
return append([]Model(nil), meta.Models...)
|
||||
}
|
||||
|
||||
func ResolveAuthMode(provider, apiType, requested string) (string, error) {
|
||||
config, ok := FindAPIConfig(provider, apiType)
|
||||
if !ok {
|
||||
@@ -264,6 +358,9 @@ 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) {
|
||||
return baseURL, nil
|
||||
}
|
||||
parsed.Path = normalizeEndpointPath(config.APIType, parsed.Path)
|
||||
parsed.RawQuery = ""
|
||||
parsed.Fragment = ""
|
||||
@@ -271,6 +368,9 @@ func ResolveBaseURL(key, apiType, requested string) (string, error) {
|
||||
}
|
||||
|
||||
func normalizeEndpointPath(apiType, value string) string {
|
||||
if IsImageAPIType(apiType) {
|
||||
return strings.TrimRight(value, "/")
|
||||
}
|
||||
path := strings.TrimRight(value, "/")
|
||||
suffixes := []string{}
|
||||
switch apiType {
|
||||
@@ -289,6 +389,15 @@ func normalizeEndpointPath(apiType, value string) string {
|
||||
return path
|
||||
}
|
||||
|
||||
func IsImageAPIType(apiType string) bool {
|
||||
switch apiType {
|
||||
case "openai-images", "dashscope-images", "minimax-images", "openrouter-images":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func EnvKey(key string) string {
|
||||
meta, ok := catalog[key]
|
||||
if !ok {
|
||||
@@ -305,6 +414,14 @@ func DisplayName(key string) string {
|
||||
return meta.DisplayName
|
||||
}
|
||||
|
||||
func DisplayNameKey(key string) string {
|
||||
meta, ok := catalog[key]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return meta.DisplayNameKey
|
||||
}
|
||||
|
||||
func NormalizeModelID(provider, modelID string) string {
|
||||
target := strings.TrimLeft(strings.TrimSpace(modelID), "/")
|
||||
for _, prefix := range legacyModelPrefixes[provider] {
|
||||
@@ -344,6 +461,7 @@ func cloneMeta(meta Meta) Meta {
|
||||
for index, config := range meta.APIConfigs {
|
||||
clone.APIConfigs[index] = config
|
||||
clone.APIConfigs[index].AuthModes = append([]string(nil), config.AuthModes...)
|
||||
clone.APIConfigs[index].Models = append([]Model(nil), config.Models...)
|
||||
}
|
||||
clone.Models = append([]Model(nil), meta.Models...)
|
||||
return clone
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -54,11 +55,20 @@ func buildModelDiscoveryURL(baseURL string) string {
|
||||
base = normalizeEndpointPath(apiType, base)
|
||||
}
|
||||
switch {
|
||||
case strings.HasSuffix(base, "/v1/models"):
|
||||
case strings.HasSuffix(base, "/models"):
|
||||
return base
|
||||
case strings.HasSuffix(base, "/v1"):
|
||||
case hasAPIVersionSuffix(base):
|
||||
return base + "/models"
|
||||
default:
|
||||
return base + "/v1/models"
|
||||
}
|
||||
}
|
||||
|
||||
func hasAPIVersionSuffix(value string) bool {
|
||||
segment := value[strings.LastIndex(value, "/")+1:]
|
||||
if len(segment) < 2 || segment[0] != 'v' {
|
||||
return false
|
||||
}
|
||||
_, err := strconv.Atoi(segment[1:])
|
||||
return err == nil
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ func BuildOpenClawProviderPatch(provider, modelName, apiType, authMode, baseURL,
|
||||
if _, ok := FindAPIConfig(provider, resolvedAPIType); !ok {
|
||||
resolvedAPIType = DefaultAPIType(provider)
|
||||
}
|
||||
if IsImageAPIType(resolvedAPIType) {
|
||||
return nil, fmt.Errorf("api type %s does not support text generation", resolvedAPIType)
|
||||
}
|
||||
resolvedAuthMode, err := ResolveAuthMode(provider, resolvedAPIType, authMode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -64,7 +64,10 @@ func VerifyAccount(provider, apiType, authMode, baseURL, apiKey, model string) e
|
||||
}
|
||||
|
||||
func BuildVerifyRequest(provider, apiType, authMode, baseURL, apiKey, model string) VerifyRequest {
|
||||
baseURL = strings.TrimRight(strings.TrimSpace(baseURL), "/")
|
||||
baseURL = strings.TrimSpace(baseURL)
|
||||
if provider != "custom" || !IsImageAPIType(apiType) {
|
||||
baseURL = strings.TrimRight(baseURL, "/")
|
||||
}
|
||||
headers := map[string]string{"Content-Type": "application/json"}
|
||||
request := VerifyRequest{Method: http.MethodPost, Headers: headers}
|
||||
|
||||
@@ -78,6 +81,28 @@ func BuildVerifyRequest(provider, apiType, authMode, baseURL, apiKey, model stri
|
||||
}
|
||||
|
||||
switch apiType {
|
||||
case "openai-images":
|
||||
request.URL = imageVerifyURL(provider, baseURL, "/images/generations")
|
||||
headers["Authorization"] = "Bearer " + apiKey
|
||||
request.Body = mustJSON(map[string]interface{}{"model": model, "prompt": "test", "n": 1, "response_format": "url"})
|
||||
case "dashscope-images":
|
||||
request.URL = imageVerifyURL(provider, baseURL, "/api/v1/services/aigc/multimodal-generation/generation")
|
||||
headers["Authorization"] = "Bearer " + apiKey
|
||||
request.Body = mustJSON(map[string]interface{}{
|
||||
"model": model,
|
||||
"input": map[string]interface{}{"messages": []map[string]interface{}{
|
||||
{"role": "user", "content": []map[string]string{{"text": "test"}}},
|
||||
}},
|
||||
"parameters": map[string]interface{}{"n": 1},
|
||||
})
|
||||
case "minimax-images":
|
||||
request.URL = imageVerifyURL(provider, baseURL, "/v1/image_generation")
|
||||
headers["Authorization"] = "Bearer " + apiKey
|
||||
request.Body = mustJSON(map[string]interface{}{"model": model, "prompt": "test", "n": 1, "response_format": "url"})
|
||||
case "openrouter-images":
|
||||
request.URL = imageVerifyURL(provider, baseURL, "/api/v1/images")
|
||||
headers["Authorization"] = "Bearer " + apiKey
|
||||
request.Body = mustJSON(map[string]interface{}{"model": model, "prompt": "test", "n": 1, "response_format": "url"})
|
||||
case "anthropic-messages":
|
||||
request.URL = baseURL + "/v1/messages"
|
||||
if authMode == AuthModeBearer {
|
||||
@@ -106,6 +131,13 @@ func BuildVerifyRequest(provider, apiType, authMode, baseURL, apiKey, model stri
|
||||
return request
|
||||
}
|
||||
|
||||
func imageVerifyURL(provider, baseURL, endpoint string) string {
|
||||
if provider == "custom" || strings.HasSuffix(strings.ToLower(baseURL), endpoint) {
|
||||
return baseURL
|
||||
}
|
||||
return baseURL + endpoint
|
||||
}
|
||||
|
||||
func verifyHTTPError(statusCode int, body []byte) string {
|
||||
message := strings.TrimSpace(string(body))
|
||||
var payload verifyErrorResponse
|
||||
|
||||
@@ -17,7 +17,7 @@ type IAgentAccountRepo interface {
|
||||
Save(account *model.AgentAccount) error
|
||||
DeleteByID(id uint) error
|
||||
List(opts ...DBOption) ([]model.AgentAccount, error)
|
||||
CountByProviders(providers []string) (map[string]int64, error)
|
||||
CountTextByProviders(providers []string) (map[string]int64, error)
|
||||
}
|
||||
|
||||
func NewIAgentAccountRepo() IAgentAccountRepo {
|
||||
@@ -67,7 +67,7 @@ func (a AgentAccountRepo) List(opts ...DBOption) ([]model.AgentAccount, error) {
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
func (a AgentAccountRepo) CountByProviders(providers []string) (map[string]int64, error) {
|
||||
func (a AgentAccountRepo) CountTextByProviders(providers []string) (map[string]int64, error) {
|
||||
normalizedProviders := normalizeProviders(providers)
|
||||
counts := make(map[string]int64, len(normalizedProviders))
|
||||
for _, provider := range normalizedProviders {
|
||||
@@ -86,6 +86,7 @@ func (a AgentAccountRepo) CountByProviders(providers []string) (map[string]int64
|
||||
Model(&model.AgentAccount{}).
|
||||
Select("provider, COUNT(*) as count").
|
||||
Where("provider IN ?", normalizedProviders).
|
||||
Where("api_type NOT LIKE ?", "%-images").
|
||||
Group("provider").
|
||||
Scan(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -94,6 +94,18 @@ func WithByProvider(provider string) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func WithByAPIType(apiType string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("api_type = ?", apiType)
|
||||
}
|
||||
}
|
||||
|
||||
func WithTextAPIType() DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("api_type NOT LIKE ?", "%-images")
|
||||
}
|
||||
}
|
||||
|
||||
func WithByModel(model string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(model) == 0 {
|
||||
|
||||
+34
-13
@@ -984,19 +984,28 @@ func (a AgentService) GetProviders() ([]dto.ProviderInfo, error) {
|
||||
}
|
||||
apiTypes := make([]dto.ProviderAPIInfo, 0, len(def.APIConfigs))
|
||||
for _, item := range def.APIConfigs {
|
||||
apiModels := make([]dto.ProviderModelInfo, 0, len(item.Models))
|
||||
for _, model := range item.Models {
|
||||
apiModels = append(apiModels, dto.ProviderModelInfo{
|
||||
ID: model.ID,
|
||||
Name: model.Name,
|
||||
})
|
||||
}
|
||||
apiTypes = append(apiTypes, dto.ProviderAPIInfo{
|
||||
APIType: item.APIType,
|
||||
BaseURL: item.BaseURL,
|
||||
EditableBaseURL: item.EditableBaseURL,
|
||||
DefaultAuthMode: item.DefaultAuthMode,
|
||||
AuthModes: item.AuthModes,
|
||||
APIType: item.APIType,
|
||||
BaseURL: item.BaseURL,
|
||||
EditableBaseURL: item.EditableBaseURL,
|
||||
SupportsModelDiscovery: item.DiscoverModels,
|
||||
DefaultAuthMode: item.DefaultAuthMode,
|
||||
AuthModes: item.AuthModes,
|
||||
Models: apiModels,
|
||||
})
|
||||
}
|
||||
baseURL, _ := providercatalog.DefaultBaseURL(key)
|
||||
providers = append(providers, dto.ProviderInfo{
|
||||
Sort: def.Sort,
|
||||
Provider: key,
|
||||
DisplayName: def.DisplayName,
|
||||
DisplayName: localizedAgentProviderName(key),
|
||||
BaseURL: baseURL,
|
||||
DefaultAPIType: def.DefaultAPIType,
|
||||
APITypes: apiTypes,
|
||||
@@ -1016,7 +1025,7 @@ func (a AgentService) CreateAccount(req dto.AgentAccountCreateReq) error {
|
||||
if err := ensureAgentAccountNameAvailable(provider, req.Name, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
initialModels, err := buildInitialAgentAccountModels(&model.AgentAccount{Provider: provider}, req.Models)
|
||||
initialModels, err := buildInitialAgentAccountModels(&model.AgentAccount{Provider: provider, APIType: req.APIType}, req.Models)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1024,7 +1033,8 @@ func (a AgentService) CreateAccount(req dto.AgentAccountCreateReq) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resolvedInput, err := resolveAgentAccountInput(provider, req.APIType, req.AuthMode, req.APIKey, req.BaseURL, verifyModel)
|
||||
validateAvailability := req.ValidateAvailability == nil || *req.ValidateAvailability
|
||||
resolvedInput, err := resolveAgentAccountInput(provider, req.APIType, req.AuthMode, req.APIKey, req.BaseURL, verifyModel, validateAvailability)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1062,6 +1072,9 @@ func (a AgentService) UpdateAccount(req dto.AgentAccountUpdateReq) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if req.APIType != account.APIType {
|
||||
return buserr.WithDetail("ErrInvalidParams", "API type cannot be changed", nil)
|
||||
}
|
||||
provider := account.Provider
|
||||
if err := ensureAgentAccountNameAvailable(provider, req.Name, account.ID); err != nil {
|
||||
return err
|
||||
@@ -1078,7 +1091,8 @@ func (a AgentService) UpdateAccount(req dto.AgentAccountUpdateReq) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resolvedInput, err := resolveAgentAccountInput(provider, req.APIType, req.AuthMode, req.APIKey, req.BaseURL, verifyModel)
|
||||
validateAvailability := req.ValidateAvailability == nil || *req.ValidateAvailability
|
||||
resolvedInput, err := resolveAgentAccountInput(provider, req.APIType, req.AuthMode, req.APIKey, req.BaseURL, verifyModel, validateAvailability)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1110,6 +1124,12 @@ func (a AgentService) PageAccounts(req dto.AgentAccountSearch) (int64, []dto.Age
|
||||
if strings.TrimSpace(req.Provider) != "" {
|
||||
opts = append(opts, repo.WithByProvider(req.Provider))
|
||||
}
|
||||
if apiType := strings.TrimSpace(req.APIType); apiType != "" {
|
||||
opts = append(opts, repo.WithByAPIType(apiType))
|
||||
}
|
||||
if req.TextOnly {
|
||||
opts = append(opts, repo.WithTextAPIType())
|
||||
}
|
||||
if strings.TrimSpace(req.Name) != "" {
|
||||
opts = append(opts, repo.WithByLikeName(req.Name))
|
||||
}
|
||||
@@ -1127,7 +1147,7 @@ func (a AgentService) PageAccounts(req dto.AgentAccountSearch) (int64, []dto.Age
|
||||
ID: item.ID,
|
||||
MasterAccountID: item.MasterAccountID,
|
||||
Provider: item.Provider,
|
||||
ProviderName: providercatalog.DisplayName(item.Provider),
|
||||
ProviderName: localizedAgentProviderName(item.Provider),
|
||||
Name: item.Name,
|
||||
APIKey: apiKey,
|
||||
RememberAPIKey: item.RememberAPIKey,
|
||||
@@ -1166,7 +1186,7 @@ func (a AgentService) PageAccounts(req dto.AgentAccountSearch) (int64, []dto.Age
|
||||
}
|
||||
|
||||
func (a AgentService) CountAccountsByProviders(req dto.AgentAccountProviderCountReq) (map[string]int64, error) {
|
||||
return agentAccountRepo.CountByProviders(req.Providers)
|
||||
return agentAccountRepo.CountTextByProviders(req.Providers)
|
||||
}
|
||||
|
||||
func (a AgentService) GetAccountModels(req dto.AgentAccountModelReq) ([]dto.AgentAccountModel, error) {
|
||||
@@ -1178,7 +1198,8 @@ func (a AgentService) GetAccountModels(req dto.AgentAccountModelReq) ([]dto.Agen
|
||||
}
|
||||
|
||||
func (a AgentService) DiscoverAccountModels(req dto.AgentAccountModelDiscoverReq) ([]dto.AgentAccountModel, error) {
|
||||
if req.APIType != "openai-completions" && req.APIType != "openai-responses" {
|
||||
config, ok := providercatalog.FindAPIConfig(req.Provider, req.APIType)
|
||||
if !ok || !config.DiscoverModels {
|
||||
return nil, buserr.New("ErrAgentAccountModelsRequired")
|
||||
}
|
||||
baseURL, err := providercatalog.ResolveBaseURL(req.Provider, req.APIType, req.BaseURL)
|
||||
@@ -1320,7 +1341,7 @@ func (a AgentService) SyncAgentsByAccount(account *model.AgentAccount) error {
|
||||
}
|
||||
|
||||
func (a AgentService) VerifyAccount(req dto.AgentAccountVerifyReq) error {
|
||||
_, err := resolveAgentAccountInput(req.Provider, req.APIType, req.AuthMode, req.APIKey, req.BaseURL, req.Model)
|
||||
_, err := resolveAgentAccountInput(req.Provider, req.APIType, req.AuthMode, req.APIKey, req.BaseURL, req.Model, true)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
agentenv "github.com/1Panel-dev/1Panel/agent/utils/env"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
@@ -96,7 +97,7 @@ func ensureContainerRunning(containerName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveAgentAccountInput(provider, apiType, authMode, apiKey, baseURL, modelID string) (resolvedAgentAccountInput, error) {
|
||||
func resolveAgentAccountInput(provider, apiType, authMode, apiKey, baseURL, modelID string, validateAvailability bool) (resolvedAgentAccountInput, error) {
|
||||
resolvedAPIKey := strings.TrimSpace(apiKey)
|
||||
resolvedAPIType := strings.TrimSpace(apiType)
|
||||
resolvedAuthMode, err := providercatalog.ResolveAuthMode(provider, resolvedAPIType, authMode)
|
||||
@@ -114,7 +115,8 @@ func resolveAgentAccountInput(provider, apiType, authMode, apiKey, baseURL, mode
|
||||
if modelID == "" {
|
||||
return resolvedAgentAccountInput{}, buserr.New("ErrAgentAccountModelsRequired")
|
||||
}
|
||||
if !providercatalog.SkipVerification(provider) {
|
||||
imageAPI := providercatalog.IsImageAPIType(resolvedAPIType)
|
||||
if validateAvailability && (imageAPI || !providercatalog.SkipVerification(provider)) {
|
||||
if err := providercatalog.VerifyAccount(provider, resolvedAPIType, resolvedAuthMode, resolvedBaseURL, resolvedAPIKey, modelID); err != nil {
|
||||
return resolvedAgentAccountInput{}, err
|
||||
}
|
||||
@@ -386,7 +388,7 @@ func buildAgentItem(agent *model.Agent, appInstall *model.AppInstall, envMap map
|
||||
Remark: agent.Remark,
|
||||
AgentType: agentType,
|
||||
Provider: agent.Provider,
|
||||
ProviderName: providercatalog.DisplayName(agent.Provider),
|
||||
ProviderName: localizedAgentProviderName(agent.Provider),
|
||||
Model: agent.Model,
|
||||
APIType: agent.APIType,
|
||||
BaseURL: agent.BaseURL,
|
||||
@@ -429,6 +431,15 @@ func buildAgentItem(agent *model.Agent, appInstall *model.AppInstall, envMap map
|
||||
return item
|
||||
}
|
||||
|
||||
func localizedAgentProviderName(provider string) string {
|
||||
if key := providercatalog.DisplayNameKey(provider); key != "" {
|
||||
if name := strings.TrimSpace(i18n.GetMsgByKey(key)); name != "" {
|
||||
return name
|
||||
}
|
||||
}
|
||||
return providercatalog.DisplayName(provider)
|
||||
}
|
||||
|
||||
func isAgentAppKey(appKey string) bool {
|
||||
return appKey == constant.AppOpenclaw || appKey == constant.AppCopaw || appKey == constant.AppHermesAgent
|
||||
}
|
||||
@@ -1159,15 +1170,15 @@ func buildInitialAgentAccountModels(account *model.AgentAccount, requested []dto
|
||||
if len(requested) > 0 {
|
||||
return normalizeAgentAccountModels(account, requested)
|
||||
}
|
||||
meta, ok := providercatalog.Get(account.Provider)
|
||||
if !ok || len(meta.Models) == 0 {
|
||||
defaultModels := providercatalog.DefaultModels(account.Provider, account.APIType)
|
||||
if len(defaultModels) == 0 {
|
||||
if requiresInitialAgentAccountModels(account.Provider) {
|
||||
return nil, buserr.New("ErrAgentAccountModelsRequired")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
requested = make([]dto.AgentAccountModel, 0, len(meta.Models))
|
||||
for _, item := range meta.Models {
|
||||
requested = make([]dto.AgentAccountModel, 0, len(defaultModels))
|
||||
for _, item := range defaultModels {
|
||||
requested = append(requested, dto.AgentAccountModel{
|
||||
ID: item.ID,
|
||||
Name: item.Name,
|
||||
|
||||
@@ -649,3 +649,7 @@ ErrAIProxyModelMapEmpty: 'Model mapping cannot be empty'
|
||||
ErrAIProxyModelMapEmptyExternalModel: 'Model mapping contains an empty request model'
|
||||
ErrAIProxyModelMapEmptyUpstreamModel: 'Upstream model for {{ .model }} cannot be empty'
|
||||
AIProxyUserFallback: 'Deleted user (ID: {{ .id }})'
|
||||
AIProviderBailian: 'Alibaba Cloud Model Studio'
|
||||
AIProviderBailianCodingPlan: 'Alibaba Cloud Model Studio Coding Plan'
|
||||
AIProviderArk: 'Volcengine Ark'
|
||||
AIProviderArkCodingPlan: 'Volcengine Ark Coding Plan'
|
||||
|
||||
@@ -649,3 +649,7 @@ ErrAIProxyModelMapEmpty: 'El mapeo de modelos no puede estar vacío'
|
||||
ErrAIProxyModelMapEmptyExternalModel: 'El mapeo de modelos contiene un modelo solicitado vacío'
|
||||
ErrAIProxyModelMapEmptyUpstreamModel: 'El modelo upstream de {{ .model }} no puede estar vacío'
|
||||
AIProxyUserFallback: 'Usuario eliminado (ID: {{ .id }})'
|
||||
AIProviderBailian: 'Alibaba Cloud Model Studio'
|
||||
AIProviderBailianCodingPlan: 'Alibaba Cloud Model Studio Coding Plan'
|
||||
AIProviderArk: 'Volcengine Ark'
|
||||
AIProviderArkCodingPlan: 'Volcengine Ark Coding Plan'
|
||||
|
||||
@@ -649,3 +649,7 @@ ErrAIProxyModelMapEmpty: 'نگاشت مدل نمیتواند خالی باش
|
||||
ErrAIProxyModelMapEmptyExternalModel: 'نگاشت مدل شامل یک مدل درخواست خالی است'
|
||||
ErrAIProxyModelMapEmptyUpstreamModel: 'مدل بالادست برای {{ .model }} نمیتواند خالی باشد'
|
||||
AIProxyUserFallback: 'کاربر حذف شده (شناسه: {{ .id }})'
|
||||
AIProviderBailian: 'Alibaba Cloud Model Studio'
|
||||
AIProviderBailianCodingPlan: 'Alibaba Cloud Model Studio Coding Plan'
|
||||
AIProviderArk: 'Volcengine Ark'
|
||||
AIProviderArkCodingPlan: 'Volcengine Ark Coding Plan'
|
||||
|
||||
@@ -649,3 +649,7 @@ ErrAIProxyModelMapEmpty: 'モデルマッピングは空にできません'
|
||||
ErrAIProxyModelMapEmptyExternalModel: 'モデルマッピングに空のリクエストモデルがあります'
|
||||
ErrAIProxyModelMapEmptyUpstreamModel: '{{ .model }} の上流モデルは空にできません'
|
||||
AIProxyUserFallback: '削除済みユーザー (ID: {{ .id }})'
|
||||
AIProviderBailian: 'Alibaba Cloud Model Studio'
|
||||
AIProviderBailianCodingPlan: 'Alibaba Cloud Model Studio Coding Plan'
|
||||
AIProviderArk: 'Volcengine Ark'
|
||||
AIProviderArkCodingPlan: 'Volcengine Ark Coding Plan'
|
||||
|
||||
@@ -649,3 +649,7 @@ ErrAIProxyModelMapEmpty: '모델 매핑은 비워 둘 수 없습니다'
|
||||
ErrAIProxyModelMapEmptyExternalModel: '모델 매핑에 빈 요청 모델이 있습니다'
|
||||
ErrAIProxyModelMapEmptyUpstreamModel: '{{ .model }}의 업스트림 모델은 비워 둘 수 없습니다'
|
||||
AIProxyUserFallback: '삭제된 사용자 (ID: {{ .id }})'
|
||||
AIProviderBailian: 'Alibaba Cloud Model Studio'
|
||||
AIProviderBailianCodingPlan: 'Alibaba Cloud Model Studio Coding Plan'
|
||||
AIProviderArk: 'Volcengine Ark'
|
||||
AIProviderArkCodingPlan: 'Volcengine Ark Coding Plan'
|
||||
|
||||
@@ -640,3 +640,7 @@ ErrAIProxyModelMapEmpty: 'ການຈັບຄູ່ໂມເດລບໍ່ສ
|
||||
ErrAIProxyModelMapEmptyExternalModel: 'ການຈັບຄູ່ໂມເດລມີໂມເດລຄຳຮ້ອງຂໍທີ່ຫວ່າງເປົ່າ'
|
||||
ErrAIProxyModelMapEmptyUpstreamModel: 'ໂມເດລ Upstream ສຳລັບ {{ .model }} ບໍ່ສາມາດຫວ່າງເປົ່າໄດ້'
|
||||
AIProxyUserFallback: 'ຜູ້ໃຊ້ທີ່ຖືກລຶບ (ID: {{ .id }})'
|
||||
AIProviderBailian: 'Alibaba Cloud Model Studio'
|
||||
AIProviderBailianCodingPlan: 'Alibaba Cloud Model Studio Coding Plan'
|
||||
AIProviderArk: 'Volcengine Ark'
|
||||
AIProviderArkCodingPlan: 'Volcengine Ark Coding Plan'
|
||||
|
||||
@@ -649,3 +649,7 @@ ErrAIProxyModelMapEmpty: 'Pemetaan model tidak boleh kosong'
|
||||
ErrAIProxyModelMapEmptyExternalModel: 'Pemetaan model mengandungi model permintaan kosong'
|
||||
ErrAIProxyModelMapEmptyUpstreamModel: 'Model upstream untuk {{ .model }} tidak boleh kosong'
|
||||
AIProxyUserFallback: 'Pengguna dipadam (ID: {{ .id }})'
|
||||
AIProviderBailian: 'Alibaba Cloud Model Studio'
|
||||
AIProviderBailianCodingPlan: 'Alibaba Cloud Model Studio Coding Plan'
|
||||
AIProviderArk: 'Volcengine Ark'
|
||||
AIProviderArkCodingPlan: 'Volcengine Ark Coding Plan'
|
||||
|
||||
@@ -649,3 +649,7 @@ ErrAIProxyModelMapEmpty: 'O mapeamento de modelos não pode estar vazio'
|
||||
ErrAIProxyModelMapEmptyExternalModel: 'O mapeamento de modelos contém um modelo solicitado vazio'
|
||||
ErrAIProxyModelMapEmptyUpstreamModel: 'O modelo upstream de {{ .model }} não pode estar vazio'
|
||||
AIProxyUserFallback: 'Usuário excluído (ID: {{ .id }})'
|
||||
AIProviderBailian: 'Alibaba Cloud Model Studio'
|
||||
AIProviderBailianCodingPlan: 'Alibaba Cloud Model Studio Coding Plan'
|
||||
AIProviderArk: 'Volcengine Ark'
|
||||
AIProviderArkCodingPlan: 'Volcengine Ark Coding Plan'
|
||||
|
||||
@@ -649,3 +649,7 @@ ErrAIProxyModelMapEmpty: 'Сопоставление моделей не мож
|
||||
ErrAIProxyModelMapEmptyExternalModel: 'Сопоставление моделей содержит пустую запрошенную модель'
|
||||
ErrAIProxyModelMapEmptyUpstreamModel: 'Upstream-модель для {{ .model }} не может быть пустой'
|
||||
AIProxyUserFallback: 'Удаленный пользователь (ID: {{ .id }})'
|
||||
AIProviderBailian: 'Alibaba Cloud Model Studio'
|
||||
AIProviderBailianCodingPlan: 'Alibaba Cloud Model Studio Coding Plan'
|
||||
AIProviderArk: 'Volcengine Ark'
|
||||
AIProviderArkCodingPlan: 'Volcengine Ark Coding Plan'
|
||||
|
||||
@@ -649,3 +649,7 @@ ErrAIProxyModelMapEmpty: 'Model eşlemesi boş olamaz'
|
||||
ErrAIProxyModelMapEmptyExternalModel: 'Model eşlemesi boş bir istek modeli içeriyor'
|
||||
ErrAIProxyModelMapEmptyUpstreamModel: '{{ .model }} için upstream model boş olamaz'
|
||||
AIProxyUserFallback: 'Silinen kullanıcı (ID: {{ .id }})'
|
||||
AIProviderBailian: 'Alibaba Cloud Model Studio'
|
||||
AIProviderBailianCodingPlan: 'Alibaba Cloud Model Studio Coding Plan'
|
||||
AIProviderArk: 'Volcengine Ark'
|
||||
AIProviderArkCodingPlan: 'Volcengine Ark Coding Plan'
|
||||
|
||||
@@ -649,3 +649,7 @@ ErrAIProxyModelMapEmpty: '模型映射不能為空'
|
||||
ErrAIProxyModelMapEmptyExternalModel: '模型映射中存在空的請求模型'
|
||||
ErrAIProxyModelMapEmptyUpstreamModel: '模型 {{ .model }} 的上游模型不能為空'
|
||||
AIProxyUserFallback: '已刪除使用者 (ID: {{ .id }})'
|
||||
AIProviderBailian: '阿里雲百煉'
|
||||
AIProviderBailianCodingPlan: '阿里雲百煉 Coding Plan'
|
||||
AIProviderArk: '火山方舟'
|
||||
AIProviderArkCodingPlan: '火山方舟 Coding Plan'
|
||||
|
||||
@@ -649,3 +649,7 @@ ErrAIProxyModelMapEmpty: "模型映射不能为空"
|
||||
ErrAIProxyModelMapEmptyExternalModel: "模型映射中存在空的请求模型"
|
||||
ErrAIProxyModelMapEmptyUpstreamModel: "模型 {{ .model }} 的上游模型不能为空"
|
||||
AIProxyUserFallback: "已删除用户 (ID: {{ .id }})"
|
||||
AIProviderBailian: "阿里云百炼"
|
||||
AIProviderBailianCodingPlan: "阿里云百炼 Coding Plan"
|
||||
AIProviderArk: "火山方舟"
|
||||
AIProviderArkCodingPlan: "火山方舟 Coding Plan"
|
||||
|
||||
@@ -50,6 +50,10 @@ func ResolveGeneratorConfig(accountID uint) (GeneratorConfig, time.Duration, err
|
||||
if provider == "" {
|
||||
return GeneratorConfig{}, 0, fmt.Errorf("agent account provider is required")
|
||||
}
|
||||
apiType := strings.TrimSpace(account.APIType)
|
||||
if providercatalog.IsImageAPIType(apiType) {
|
||||
return GeneratorConfig{}, 0, fmt.Errorf("api type %s does not support text generation", apiType)
|
||||
}
|
||||
model, err := resolveAccountModelConfig(account.ID, provider)
|
||||
if err != nil {
|
||||
return GeneratorConfig{}, 0, err
|
||||
@@ -72,7 +76,7 @@ func ResolveGeneratorConfig(accountID uint) (GeneratorConfig, time.Duration, err
|
||||
BaseURL: baseURL,
|
||||
APIKey: strings.TrimSpace(apiKey),
|
||||
Model: model,
|
||||
APIType: strings.TrimSpace(account.APIType),
|
||||
APIType: apiType,
|
||||
AuthMode: strings.TrimSpace(account.AuthMode),
|
||||
MaxTokens: defaultTerminalTokens,
|
||||
}, 30 * time.Second, nil
|
||||
|
||||
Reference in New Issue
Block a user