mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/1Panel-dev/1Panel.git
synced 2026-09-20 08:03:55 +08:00
feat: Optimize the openclaw channel bot add flow (#12507)
This commit is contained in:
@@ -371,7 +371,7 @@ type AgentTelegramConfig struct {
|
||||
|
||||
type AgentChannelPairingApproveReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=feishu telegram discord wecom"`
|
||||
Type string `json:"type" validate:"required,oneof=feishu telegram discord wecom qqbot"`
|
||||
PairingCode string `json:"pairingCode" validate:"required"`
|
||||
AccountID string `json:"accountId"`
|
||||
}
|
||||
|
||||
@@ -5,11 +5,6 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type OpenClawPatch struct {
|
||||
PrimaryModel string
|
||||
Models map[string]interface{}
|
||||
}
|
||||
|
||||
type OpenClawProviderPatch struct {
|
||||
PrimaryModel string
|
||||
ProviderKey string
|
||||
@@ -20,24 +15,7 @@ type OpenClawProviderPatch struct {
|
||||
AuthHeader bool
|
||||
}
|
||||
|
||||
func BuildOpenClawPatch(provider, modelName, apiType string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) (*OpenClawPatch, error) {
|
||||
providerPatch, err := BuildOpenClawProviderPatch(provider, modelName, apiType, baseURL, apiKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, maxTokens, contextWindow = ResolveRuntimeParams(provider, apiType, maxTokens, contextWindow)
|
||||
return newOpenClawPatch(
|
||||
providerPatch,
|
||||
resolveOpenClawModelName(provider, providerPatch.ModelID),
|
||||
resolveOpenClawModelInput(provider, providerPatch.ModelID),
|
||||
reasoning,
|
||||
contextWindow,
|
||||
maxTokens,
|
||||
), nil
|
||||
}
|
||||
|
||||
func BuildOpenClawProviderPatch(provider, modelName, apiType, baseURL, apiKey string) (*OpenClawProviderPatch, error) {
|
||||
modelName = strings.TrimSpace(modelName)
|
||||
if modelName == "" {
|
||||
return nil, fmt.Errorf("model is required")
|
||||
}
|
||||
@@ -45,29 +23,29 @@ func BuildOpenClawProviderPatch(provider, modelName, apiType, baseURL, apiKey st
|
||||
modelID := resolveOpenClawModelID(provider, modelName)
|
||||
switch provider {
|
||||
case "deepseek":
|
||||
return newOpenClawProviderPatch(modelName, "deepseek", modelID, strings.TrimSpace(apiKey), baseURL, "openai-completions", false), nil
|
||||
return newOpenClawProviderPatch(modelName, "deepseek", modelID, apiKey, baseURL, "openai-completions", false), nil
|
||||
case "gemini":
|
||||
return newOpenClawProviderPatch("google/"+modelID, "google", modelID, strings.TrimSpace(apiKey), baseURL, resolvedAPIType, false), nil
|
||||
return newOpenClawProviderPatch("google/"+modelID, "google", modelID, apiKey, baseURL, resolvedAPIType, false), nil
|
||||
case "moonshot", "kimi":
|
||||
return buildMoonshotProviderPatch(provider, modelName, modelID, baseURL, apiKey), nil
|
||||
case "bailian-coding-plan":
|
||||
return newOpenClawProviderPatch("bailian-coding-plan/"+modelID, "bailian-coding-plan", modelID, strings.TrimSpace(apiKey), baseURL, "openai-completions", false), nil
|
||||
return newOpenClawProviderPatch("bailian-coding-plan/"+modelID, "bailian-coding-plan", modelID, apiKey, baseURL, "openai-completions", false), nil
|
||||
case "ark-coding-plan":
|
||||
return newOpenClawProviderPatch("ark-coding-plan/"+modelID, "ark-coding-plan", modelID, strings.TrimSpace(apiKey), baseURL, "openai-completions", false), nil
|
||||
return newOpenClawProviderPatch("ark-coding-plan/"+modelID, "ark-coding-plan", modelID, apiKey, baseURL, "openai-completions", false), nil
|
||||
case "minimax":
|
||||
return newOpenClawProviderPatch("minimax/"+modelID, "minimax", modelID, strings.TrimSpace(apiKey), baseURL, "anthropic-messages", true), nil
|
||||
return newOpenClawProviderPatch("minimax/"+modelID, "minimax", modelID, apiKey, baseURL, "anthropic-messages", true), nil
|
||||
case "xiaomi":
|
||||
return newOpenClawProviderPatch("xiaomi/"+modelID, "xiaomi", modelID, strings.TrimSpace(apiKey), baseURL, "openai-completions", false), nil
|
||||
return newOpenClawProviderPatch("xiaomi/"+modelID, "xiaomi", modelID, apiKey, baseURL, "openai-completions", false), nil
|
||||
case "custom", "vllm":
|
||||
return newOpenClawProviderPatch(provider+"/"+modelID, provider, modelID, strings.TrimSpace(apiKey), strings.TrimSpace(baseURL), resolvedAPIType, false), nil
|
||||
return newOpenClawProviderPatch(provider+"/"+modelID, provider, modelID, apiKey, baseURL, resolvedAPIType, false), nil
|
||||
case "ollama":
|
||||
return newOpenClawProviderPatch(modelName, "ollama", modelID, "ollama", strings.TrimSpace(baseURL), resolvedAPIType, false), nil
|
||||
return newOpenClawProviderPatch(modelName, "ollama", modelID, "ollama", baseURL, resolvedAPIType, false), nil
|
||||
case "kimi-coding":
|
||||
return newOpenClawProviderPatch(modelName, "kimi-coding", modelID, strings.TrimSpace(apiKey), baseURL, "anthropic-messages", false), nil
|
||||
return newOpenClawProviderPatch(modelName, "kimi-coding", modelID, apiKey, baseURL, "anthropic-messages", false), nil
|
||||
case "zai":
|
||||
return newOpenClawProviderPatch("zai/"+modelID, "zai", modelID, strings.TrimSpace(apiKey), baseURL, "openai-completions", false), nil
|
||||
return newOpenClawProviderPatch("zai/"+modelID, "zai", modelID, apiKey, baseURL, "openai-completions", false), nil
|
||||
default:
|
||||
return newOpenClawProviderPatch(modelName, provider, modelID, strings.TrimSpace(apiKey), baseURL, resolvedAPIType, false), nil
|
||||
return newOpenClawProviderPatch(modelName, provider, modelID, apiKey, baseURL, resolvedAPIType, false), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +56,7 @@ func buildMoonshotProviderPatch(provider, modelName, modelID, baseURL, apiKey st
|
||||
providerKey = "moonshot"
|
||||
primaryModel = "moonshot/" + modelID
|
||||
}
|
||||
return newOpenClawProviderPatch(primaryModel, providerKey, modelID, strings.TrimSpace(apiKey), baseURL, "openai-completions", false)
|
||||
return newOpenClawProviderPatch(primaryModel, providerKey, modelID, apiKey, baseURL, "openai-completions", false)
|
||||
}
|
||||
|
||||
func newOpenClawProviderPatch(primaryModel, providerKey, modelID, apiKey, baseURL, apiType string, authHeader bool) *OpenClawProviderPatch {
|
||||
@@ -93,84 +71,16 @@ func newOpenClawProviderPatch(primaryModel, providerKey, modelID, apiKey, baseUR
|
||||
}
|
||||
}
|
||||
|
||||
func newOpenClawPatch(providerPatch *OpenClawProviderPatch, modelName string, input []string, reasoning bool, contextWindow, maxTokens int) *OpenClawPatch {
|
||||
model := map[string]interface{}{
|
||||
"id": providerPatch.ModelID,
|
||||
"name": modelName,
|
||||
"reasoning": reasoning,
|
||||
"input": input,
|
||||
"contextWindow": contextWindow,
|
||||
"maxTokens": maxTokens,
|
||||
"cost": map[string]interface{}{},
|
||||
}
|
||||
providerConfig := map[string]interface{}{
|
||||
"apiKey": providerPatch.APIKey,
|
||||
"baseUrl": providerPatch.BaseURL,
|
||||
"api": providerPatch.APIType,
|
||||
"models": []map[string]interface{}{model},
|
||||
}
|
||||
if providerPatch.AuthHeader {
|
||||
providerConfig["authHeader"] = true
|
||||
}
|
||||
return &OpenClawPatch{
|
||||
PrimaryModel: providerPatch.PrimaryModel,
|
||||
Models: map[string]interface{}{
|
||||
"mode": "merge",
|
||||
"providers": map[string]interface{}{
|
||||
providerPatch.ProviderKey: providerConfig,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resolveOpenClawModelID(provider, modelName string) string {
|
||||
if provider == "custom" || provider == "vllm" {
|
||||
return normalizeCustomModel(modelName)
|
||||
target := strings.TrimLeft(modelName, "/")
|
||||
if parts := strings.SplitN(target, "/", 2); len(parts) == 2 && parts[0] == "custom" {
|
||||
return strings.TrimLeft(parts[1], "/")
|
||||
}
|
||||
return target
|
||||
}
|
||||
if parts := strings.SplitN(modelName, "/", 2); len(parts) == 2 {
|
||||
return parts[1]
|
||||
}
|
||||
return modelName
|
||||
}
|
||||
|
||||
func resolveOpenClawModelName(provider, modelID string) string {
|
||||
if item, ok := resolveOpenClawCatalogModel(provider, modelID); ok {
|
||||
return item.Name
|
||||
}
|
||||
if provider == "kimi-coding" {
|
||||
return "Kimi for Coding"
|
||||
}
|
||||
return modelID
|
||||
}
|
||||
|
||||
func resolveOpenClawModelInput(provider, modelID string) []string {
|
||||
if item, ok := resolveOpenClawCatalogModel(provider, modelID); ok && len(item.Input) > 0 {
|
||||
return item.Input
|
||||
}
|
||||
if provider == "kimi-coding" {
|
||||
return []string{"text", "image"}
|
||||
}
|
||||
return []string{"text"}
|
||||
}
|
||||
|
||||
func resolveOpenClawCatalogModel(provider, modelID string) (Model, bool) {
|
||||
candidates := []string{provider + "/" + modelID}
|
||||
if provider == "gemini" {
|
||||
candidates = append(candidates, "google/"+modelID)
|
||||
}
|
||||
for _, candidate := range candidates {
|
||||
if item, ok := FindModel(provider, candidate); ok {
|
||||
return item, true
|
||||
}
|
||||
}
|
||||
return Model{}, false
|
||||
}
|
||||
|
||||
func normalizeCustomModel(modelName string) string {
|
||||
trim := strings.TrimSpace(modelName)
|
||||
trim = strings.TrimLeft(trim, "/")
|
||||
if parts := strings.SplitN(trim, "/", 2); len(parts) == 2 && strings.EqualFold(parts[0], "custom") {
|
||||
return strings.TrimLeft(strings.TrimSpace(parts[1]), "/")
|
||||
}
|
||||
return trim
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ type IAgentService interface {
|
||||
}
|
||||
|
||||
const (
|
||||
defaultBrowserExecutablePath = "/home/node/.cache/ms-playwright/chromium-1208/chrome-linux64/chrome"
|
||||
defaultBrowserExecutablePath = "/home/node/.cache/ms-playwright/openclaw-browser"
|
||||
defaultBrowserProfile = "openclaw"
|
||||
defaultUserTimezone = "Asia/Shanghai"
|
||||
defaultToolsProfile = "full"
|
||||
@@ -114,9 +114,6 @@ const (
|
||||
|
||||
func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
|
||||
agentType := req.AgentType
|
||||
if agentType != constant.AppOpenclaw && agentType != constant.AppCopaw && agentType != constant.AppHermesAgent {
|
||||
return nil, fmt.Errorf("unsupported agent type: %s", agentType)
|
||||
}
|
||||
if err := checkPortExist(req.WebUIPort); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -516,8 +513,8 @@ func (a AgentService) UpdateModelConfig(req dto.AgentModelConfigUpdateReq) error
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if agent, err = loadOpenclawAgentByID(req.AgentID); err != nil {
|
||||
return err
|
||||
if agent.AgentType != constant.AppOpenclaw {
|
||||
return fmt.Errorf("%s does not support", agent.AgentType)
|
||||
}
|
||||
if err := writeOpenclawConfig(confDir, account, modelName, agent.Token, nil, req.Fallbacks); err != nil {
|
||||
return err
|
||||
@@ -1068,21 +1065,15 @@ func (a AgentService) syncAgentsByAccount(account *model.AgentAccount) error {
|
||||
return nil
|
||||
}
|
||||
for _, agent := range agents {
|
||||
modelName := strings.TrimSpace(agent.Model)
|
||||
var selectedAccountModel dto.AgentAccountModel
|
||||
if modelName != "" {
|
||||
selectedAccountModel, err = requireAgentAccountModelForProvider(account.Provider, accountModels, modelName)
|
||||
if err != nil {
|
||||
return buserr.WithName("ErrAgentModelInUse", agent.Name)
|
||||
}
|
||||
} else {
|
||||
selectedAccountModel = accountModels[0]
|
||||
selectedAccountModel, err := requireAgentAccountModelForProvider(account.Provider, accountModels, agent.Model)
|
||||
if err != nil {
|
||||
return buserr.WithName("ErrAgentModelInUse", agent.Name)
|
||||
}
|
||||
resolvedRuntime, err := buildOpenclawAccountModelRuntime(account, selectedAccountModel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
modelName = resolvedRuntime.StoredModel
|
||||
modelName := resolvedRuntime.StoredModel
|
||||
apiType, maxTokens, contextWindow := resolvedRuntime.APIType, resolvedRuntime.MaxTokens, resolvedRuntime.ContextWindow
|
||||
confDir := path.Dir(agent.ConfigPath)
|
||||
switch agent.AgentType {
|
||||
|
||||
@@ -570,7 +570,7 @@ func extractFeishuConfig(conf map[string]interface{}) dto.AgentFeishuConfig {
|
||||
RequireMention: "true",
|
||||
GroupPolicy: "open",
|
||||
GroupAllowFrom: []string{},
|
||||
Bots: []dto.AgentFeishuBot{defaultFeishuBot()},
|
||||
Bots: []dto.AgentFeishuBot{},
|
||||
}
|
||||
feishu := getChannelConfig(conf, "feishu")
|
||||
if len(feishu) == 0 {
|
||||
@@ -620,7 +620,10 @@ func extractFeishuConfig(conf map[string]interface{}) dto.AgentFeishuConfig {
|
||||
baseEnabled := defaultBot.Enabled
|
||||
baseDmPolicy := defaultBot.DmPolicy
|
||||
baseAllowFrom := append([]string(nil), defaultBot.AllowFrom...)
|
||||
bots := []dto.AgentFeishuBot{defaultBot}
|
||||
bots := make([]dto.AgentFeishuBot, 0, len(accounts)+1)
|
||||
if defaultBot.AppID != "" || defaultBot.AppSecret != "" {
|
||||
bots = append(bots, defaultBot)
|
||||
}
|
||||
for _, accountID := range sortedChildKeys(accounts) {
|
||||
if accountID == "default" {
|
||||
continue
|
||||
@@ -942,31 +945,34 @@ func extractQQBotConfig(conf map[string]interface{}) dto.AgentQQBotConfig {
|
||||
result := dto.AgentQQBotConfig{Enabled: true}
|
||||
qqbot := getChannelConfig(conf, "qqbot")
|
||||
if len(qqbot) == 0 {
|
||||
result.Bots = []dto.AgentQQBotBot{defaultQQBot()}
|
||||
result.Bots = []dto.AgentQQBotBot{}
|
||||
return result
|
||||
}
|
||||
if enabled, ok := qqbot["enabled"].(bool); ok {
|
||||
result.Enabled = enabled
|
||||
}
|
||||
bots := []dto.AgentQQBotBot{
|
||||
{
|
||||
AgentChannelBotBase: dto.AgentChannelBotBase{
|
||||
AccountID: "default",
|
||||
Name: extractStringValue(qqbot["name"]),
|
||||
Enabled: extractBoolValue(qqbot["enabled"], true),
|
||||
IsDefault: true,
|
||||
},
|
||||
AppID: extractStringValue(qqbot["appId"]),
|
||||
ClientSecret: extractStringValue(qqbot["clientSecret"]),
|
||||
AllowFrom: extractStringList(qqbot["allowFrom"]),
|
||||
SystemPrompt: extractStringValue(qqbot["systemPrompt"]),
|
||||
bots := make([]dto.AgentQQBotBot, 0, len(childMap(qqbot, "accounts"))+1)
|
||||
defaultBot := dto.AgentQQBotBot{
|
||||
AgentChannelBotBase: dto.AgentChannelBotBase{
|
||||
AccountID: "default",
|
||||
Name: extractStringValue(qqbot["name"]),
|
||||
Enabled: extractBoolValue(qqbot["enabled"], true),
|
||||
IsDefault: true,
|
||||
},
|
||||
AppID: extractStringValue(qqbot["appId"]),
|
||||
ClientSecret: extractStringValue(qqbot["clientSecret"]),
|
||||
AllowFrom: extractStringList(qqbot["allowFrom"]),
|
||||
SystemPrompt: extractStringValue(qqbot["systemPrompt"]),
|
||||
}
|
||||
if bots[0].Name == "" {
|
||||
bots[0].Name = "Default"
|
||||
if defaultBot.Name == "" {
|
||||
defaultBot.Name = "Default"
|
||||
}
|
||||
for _, accountID := range sortedChildKeys(childMap(qqbot, "accounts")) {
|
||||
account := childMap(childMap(qqbot, "accounts"), accountID)
|
||||
if defaultBot.AppID != "" || defaultBot.ClientSecret != "" {
|
||||
bots = append(bots, defaultBot)
|
||||
}
|
||||
accounts := childMap(qqbot, "accounts")
|
||||
for _, accountID := range sortedChildKeys(accounts) {
|
||||
account := childMap(accounts, accountID)
|
||||
bots = append(bots, dto.AgentQQBotBot{
|
||||
AgentChannelBotBase: dto.AgentChannelBotBase{
|
||||
AccountID: accountID,
|
||||
|
||||
@@ -78,7 +78,7 @@ func writeHermesConfig(confDir string, account *model.AgentAccount, modelName st
|
||||
cfg["model"] = map[string]interface{}{
|
||||
"default": resolveHermesModel(account.Provider, provider, modelName),
|
||||
"provider": provider,
|
||||
"base_url": strings.TrimSpace(account.BaseURL),
|
||||
"base_url": account.BaseURL,
|
||||
}
|
||||
cfg["terminal"] = map[string]interface{}{
|
||||
"backend": "local",
|
||||
@@ -390,8 +390,8 @@ func resolveHermesEnvEntries(account *model.AgentAccount) []hermesEnvEntry {
|
||||
if account == nil {
|
||||
return nil
|
||||
}
|
||||
apiKey := strings.TrimSpace(account.APIKey)
|
||||
baseURL := strings.TrimSpace(account.BaseURL)
|
||||
apiKey := account.APIKey
|
||||
baseURL := account.BaseURL
|
||||
entries := make([]hermesEnvEntry, 0, 4)
|
||||
|
||||
appendEntry := func(key, value string) {
|
||||
|
||||
@@ -1006,14 +1006,10 @@ func buildOpenclawModelsFromAccount(account *model.AgentAccount, selectedModel s
|
||||
return "", nil, nil, err
|
||||
}
|
||||
if len(accountModels) == 0 {
|
||||
return "", nil, nil, fmt.Errorf("model is required")
|
||||
}
|
||||
selectedModel = strings.TrimSpace(selectedModel)
|
||||
if selectedModel == "" {
|
||||
selectedModel = strings.TrimSpace(accountModels[0].ID)
|
||||
return "", nil, nil, buserr.New("ErrAgentModelNotInAccount")
|
||||
}
|
||||
if selectedModel == "" {
|
||||
return "", nil, nil, fmt.Errorf("model is required")
|
||||
return "", nil, nil, buserr.New("ErrAgentModelNotInAccount")
|
||||
}
|
||||
selectedAccountModel, err := requireAgentAccountModelForProvider(account.Provider, accountModels, selectedModel)
|
||||
if err != nil {
|
||||
@@ -1464,9 +1460,6 @@ func ensureAccountModelsNotBound(account *model.AgentAccount, models []dto.Agent
|
||||
return err
|
||||
}
|
||||
for _, agent := range agents {
|
||||
if strings.TrimSpace(agent.Model) == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := findAgentAccountModelForProvider(account.Provider, models, agent.Model); !ok {
|
||||
return buserr.WithName("ErrAgentModelInUse", agent.Name)
|
||||
}
|
||||
|
||||
@@ -704,7 +704,7 @@ export namespace AI {
|
||||
|
||||
export interface AgentQQBotConfig {
|
||||
enabled: boolean;
|
||||
dmPolicy?: 'open' | 'allowlist' | 'disabled';
|
||||
dmPolicy?: 'pairing' | 'open' | 'allowlist' | 'disabled';
|
||||
allowFrom?: string[];
|
||||
groupPolicy?: 'open' | 'allowlist' | 'disabled';
|
||||
groupAllowFrom?: string[];
|
||||
@@ -715,7 +715,7 @@ export namespace AI {
|
||||
export interface AgentQQBotConfigUpdateReq {
|
||||
agentId: number;
|
||||
enabled: boolean;
|
||||
dmPolicy?: 'open' | 'allowlist' | 'disabled';
|
||||
dmPolicy?: 'pairing' | 'open' | 'allowlist' | 'disabled';
|
||||
allowFrom?: string[];
|
||||
groupPolicy?: 'open' | 'allowlist' | 'disabled';
|
||||
groupAllowFrom?: string[];
|
||||
|
||||
@@ -801,8 +801,10 @@ const message = {
|
||||
bots: 'Bots',
|
||||
addBot: 'Add Bot',
|
||||
accountId: 'Account ID',
|
||||
accountIdPlaceholder: 'A unique ID used to distinguish bots and for later config and routing.',
|
||||
setDefaultBot: 'Set as Default',
|
||||
botDuplicateAccountId: 'Account ID already exists',
|
||||
botDuplicateField: '{field} already exists',
|
||||
botRequired: 'Add at least one bot',
|
||||
botId: 'Bot ID',
|
||||
allowFrom: 'DM Allowlist',
|
||||
|
||||
@@ -811,8 +811,11 @@ const message = {
|
||||
bots: 'Bots',
|
||||
addBot: 'Agregar bot',
|
||||
accountId: 'ID de cuenta',
|
||||
accountIdPlaceholder:
|
||||
'Un ID único para distinguir bots y usarlo después en la configuración y el enrutamiento.',
|
||||
setDefaultBot: 'Establecer como predeterminado',
|
||||
botDuplicateAccountId: 'El ID de cuenta ya existe',
|
||||
botDuplicateField: '{field} ya existe',
|
||||
botRequired: 'Agregue al menos un bot',
|
||||
botId: 'ID del bot',
|
||||
allowFrom: 'Lista permitida de MD',
|
||||
|
||||
@@ -804,8 +804,10 @@ const message = {
|
||||
bots: 'Bot',
|
||||
addBot: 'Bot を追加',
|
||||
accountId: 'アカウント ID',
|
||||
accountIdPlaceholder: 'Bot を識別するための一意 ID です。保存後は設定とルーティング識別に使用されます。',
|
||||
setDefaultBot: 'デフォルトに設定',
|
||||
botDuplicateAccountId: 'アカウント ID は既に存在します',
|
||||
botDuplicateField: '{field} は既に存在します',
|
||||
botRequired: '少なくとも 1 つの Bot を追加してください',
|
||||
botId: 'Bot ID',
|
||||
allowFrom: 'DM 許可リスト',
|
||||
|
||||
@@ -790,8 +790,10 @@ const message = {
|
||||
bots: '봇',
|
||||
addBot: '봇 추가',
|
||||
accountId: '계정 ID',
|
||||
accountIdPlaceholder: '봇을 구분하는 고유 ID이며 저장 후 설정과 라우팅 식별에 사용됩니다.',
|
||||
setDefaultBot: '기본값으로 설정',
|
||||
botDuplicateAccountId: '계정 ID가 이미 존재합니다',
|
||||
botDuplicateField: '{field}이(가) 이미 존재합니다',
|
||||
botRequired: '봇을 하나 이상 추가하세요',
|
||||
botId: '봇 ID',
|
||||
allowFrom: 'DM 허용 목록',
|
||||
|
||||
@@ -809,8 +809,11 @@ const message = {
|
||||
bots: 'Bot',
|
||||
addBot: 'Tambah bot',
|
||||
accountId: 'ID akaun',
|
||||
accountIdPlaceholder:
|
||||
'ID unik untuk membezakan bot dan digunakan untuk konfigurasi serta penghalaan selepas disimpan.',
|
||||
setDefaultBot: 'Tetapkan sebagai lalai',
|
||||
botDuplicateAccountId: 'ID akaun sudah wujud',
|
||||
botDuplicateField: '{field} sudah wujud',
|
||||
botRequired: 'Tambah sekurang-kurangnya satu bot',
|
||||
botId: 'ID bot',
|
||||
allowFrom: 'Senarai benarkan DM',
|
||||
|
||||
@@ -806,8 +806,10 @@ const message = {
|
||||
bots: 'Bots',
|
||||
addBot: 'Adicionar bot',
|
||||
accountId: 'ID da conta',
|
||||
accountIdPlaceholder: 'Um ID único para diferenciar bots e usá-los depois na configuração e no roteamento.',
|
||||
setDefaultBot: 'Definir como padrão',
|
||||
botDuplicateAccountId: 'O ID da conta já existe',
|
||||
botDuplicateField: '{field} já existe',
|
||||
botRequired: 'Adicione pelo menos um bot',
|
||||
botId: 'ID do bot',
|
||||
allowFrom: 'Lista de permissões de DM',
|
||||
|
||||
@@ -801,8 +801,11 @@ const message = {
|
||||
bots: 'Боты',
|
||||
addBot: 'Добавить бота',
|
||||
accountId: 'ID аккаунта',
|
||||
accountIdPlaceholder:
|
||||
'Уникальный ID для различения ботов, который после сохранения используется в настройке и маршрутизации.',
|
||||
setDefaultBot: 'Сделать по умолчанию',
|
||||
botDuplicateAccountId: 'ID аккаунта уже существует',
|
||||
botDuplicateField: '{field} уже существует',
|
||||
botRequired: 'Добавьте хотя бы одного бота',
|
||||
botId: 'ID бота',
|
||||
allowFrom: 'Белый список DM',
|
||||
|
||||
@@ -808,8 +808,11 @@ const message = {
|
||||
bots: 'Botlar',
|
||||
addBot: 'Bot ekle',
|
||||
accountId: 'Hesap ID',
|
||||
accountIdPlaceholder:
|
||||
'Botlari ayirt etmek ve sonraki yapılandırma ile yönlendirme için kullanılan benzersiz kimlik.',
|
||||
setDefaultBot: 'Varsayılan yap',
|
||||
botDuplicateAccountId: 'Hesap ID zaten mevcut',
|
||||
botDuplicateField: '{field} zaten mevcut',
|
||||
botRequired: 'En az bir bot ekleyin',
|
||||
botId: 'Bot ID',
|
||||
allowFrom: 'DM izin listesi',
|
||||
|
||||
@@ -744,7 +744,7 @@ const message = {
|
||||
dmPolicy: '私聊策略',
|
||||
threadSession: '執行緒會話',
|
||||
replyMode: '回覆模式',
|
||||
requireMention: '提及要求',
|
||||
requireMention: '群聊需 @ 機器人',
|
||||
requireMentionTrue: '需要 @',
|
||||
requireMentionFalse: '無需 @',
|
||||
requireMentionOpen: '依群組設定',
|
||||
@@ -755,8 +755,10 @@ const message = {
|
||||
bots: 'Bot 列表',
|
||||
addBot: '新增 Bot',
|
||||
accountId: '帳戶 ID',
|
||||
accountIdPlaceholder: '用於區分不同 Bot 的唯一標識,儲存後用於配置和路由識別',
|
||||
setDefaultBot: '設為預設 Bot',
|
||||
botDuplicateAccountId: '帳戶 ID 不能重複',
|
||||
botDuplicateField: '{field} 不能重複',
|
||||
botRequired: '請至少新增一個 Bot',
|
||||
botId: 'Bot ID',
|
||||
allowFrom: '私聊白名單',
|
||||
|
||||
@@ -739,7 +739,7 @@ const message = {
|
||||
dmPolicy: '私聊策略',
|
||||
threadSession: '线程会话',
|
||||
replyMode: '回复模式',
|
||||
requireMention: '提及要求',
|
||||
requireMention: '群聊需 @ 机器人',
|
||||
requireMentionTrue: '需要 @',
|
||||
requireMentionFalse: '无需 @',
|
||||
requireMentionOpen: '按群组配置',
|
||||
@@ -750,8 +750,10 @@ const message = {
|
||||
bots: 'Bot 列表',
|
||||
addBot: '新增 Bot',
|
||||
accountId: '账户 ID',
|
||||
accountIdPlaceholder: '用于区分不同 Bot 的唯一标识,保存后用于配置和路由识别',
|
||||
setDefaultBot: '设为默认 Bot',
|
||||
botDuplicateAccountId: '账户 ID 不能重复',
|
||||
botDuplicateField: '{field} 不能重复',
|
||||
botRequired: '请至少添加一个 Bot',
|
||||
botId: 'Bot ID',
|
||||
allowFrom: '私聊白名单',
|
||||
|
||||
@@ -23,7 +23,13 @@
|
||||
<span>{{ summaryFormatter(row) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('commons.table.status')" width="120">
|
||||
<el-table-column
|
||||
:label="t('commons.table.status')"
|
||||
width="120"
|
||||
fixed="right"
|
||||
class-name="channel-bots__status-column"
|
||||
label-class-name="channel-bots__status-column"
|
||||
>
|
||||
<template #default="{ row, $index }">
|
||||
<el-switch
|
||||
:model-value="row.enabled"
|
||||
@@ -32,12 +38,26 @@
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('commons.table.operate')" width="180" fixed="right">
|
||||
<el-table-column :label="t('commons.table.operate')" width="240" fixed="right">
|
||||
<template #default="{ row, $index }">
|
||||
<div class="channel-bots__actions">
|
||||
<el-button link type="primary" :disabled="disabled" @click="openEdit(row, $index)">
|
||||
{{ t('commons.button.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="approvable"
|
||||
link
|
||||
type="primary"
|
||||
:disabled="
|
||||
disabled ||
|
||||
!row.enabled ||
|
||||
isBotActionDisabled(row) ||
|
||||
(approveDisabled ? approveDisabled(row) : false)
|
||||
"
|
||||
@click="emit('approve', row)"
|
||||
>
|
||||
{{ t('aiTools.agents.approvePairing') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
:disabled="disabled || undeletableAccountIds.includes(row.accountId)"
|
||||
link
|
||||
@@ -63,18 +83,6 @@
|
||||
>
|
||||
{{ t('aiTools.agents.setDefaultBot') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="approvable"
|
||||
command="approve"
|
||||
:disabled="
|
||||
disabled ||
|
||||
!row.enabled ||
|
||||
isBotActionDisabled(row) ||
|
||||
(approveDisabled ? approveDisabled(row) : false)
|
||||
"
|
||||
>
|
||||
{{ t('aiTools.agents.approvePairing') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
@@ -89,7 +97,11 @@
|
||||
<el-input v-model="form.name" :disabled="disabled" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.accountId')" prop="accountId">
|
||||
<el-input v-model="form.accountId" :disabled="disabled || accountIdLocked" />
|
||||
<el-input
|
||||
v-model="form.accountId"
|
||||
:disabled="disabled || accountIdLocked"
|
||||
:placeholder="t('aiTools.agents.accountIdPlaceholder')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('commons.table.status')">
|
||||
<el-switch v-model="form.enabled" :disabled="disabled" />
|
||||
@@ -222,6 +234,18 @@ const props = defineProps({
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => [],
|
||||
},
|
||||
uniqueFieldProp: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
uniqueFieldLabel: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
createPreset: {
|
||||
type: Object as PropType<Partial<ChannelBotItem>>,
|
||||
default: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -238,7 +262,9 @@ const formRef = ref<FormInstance>();
|
||||
|
||||
const form = reactive<ChannelBotItem>(props.createBot());
|
||||
|
||||
const accountIdLocked = computed(() => props.fixedAccountIds.includes(editingAccountId.value));
|
||||
const accountIdLocked = computed(() =>
|
||||
props.fixedAccountIds.includes(editingAccountId.value || String(form.accountId || '')),
|
||||
);
|
||||
const dialogTitle = computed(() => (editIndex.value >= 0 ? t('commons.button.edit') : t('commons.button.create')));
|
||||
|
||||
const rules = computed<FormRules>(() => {
|
||||
@@ -269,6 +295,30 @@ const rules = computed<FormRules>(() => {
|
||||
}
|
||||
config[field.prop] = [Rules.requiredInput];
|
||||
}
|
||||
if (props.uniqueFieldProp) {
|
||||
config[props.uniqueFieldProp] = [
|
||||
...(config[props.uniqueFieldProp] || []),
|
||||
{
|
||||
validator: (_rule, value, callback) => {
|
||||
const exists = props.bots.some(
|
||||
(bot, index) => index !== editIndex.value && bot[props.uniqueFieldProp] === value,
|
||||
);
|
||||
if (exists) {
|
||||
callback(
|
||||
new Error(
|
||||
t('aiTools.agents.botDuplicateField', {
|
||||
field: props.uniqueFieldLabel || props.uniqueFieldProp,
|
||||
}),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
];
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
||||
@@ -276,7 +326,7 @@ const resetForm = (bot?: ChannelBotItem) => {
|
||||
Object.keys(form).forEach((key) => {
|
||||
delete form[key];
|
||||
});
|
||||
Object.assign(form, props.createBot(), bot ? { ...bot } : {});
|
||||
Object.assign(form, props.createBot(), bot ? { ...bot } : { ...(props.createPreset || {}) });
|
||||
};
|
||||
|
||||
const emitBots = (bots: ChannelBotItem[]) => {
|
||||
@@ -292,7 +342,7 @@ const isBotActionDisabled = (bot: ChannelBotItem) => {
|
||||
};
|
||||
|
||||
const hasMoreActions = (bot: ChannelBotItem) => {
|
||||
return (props.defaultable && !bot.isDefault) || props.approvable;
|
||||
return props.defaultable && !bot.isDefault;
|
||||
};
|
||||
|
||||
const openCreate = () => {
|
||||
@@ -361,10 +411,6 @@ const updateEnabled = (index: number, enabled: boolean | string | number) => {
|
||||
const handleMoreCommand = (bot: ChannelBotItem, _index: number, command: string | number | object) => {
|
||||
if (command === 'default') {
|
||||
setDefault(bot.accountId);
|
||||
return;
|
||||
}
|
||||
if (command === 'approve') {
|
||||
emit('approve', bot);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -403,4 +449,8 @@ const handleMoreCommand = (bot: ChannelBotItem, _index: number, command: string
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.channel-bots__status-column) {
|
||||
box-shadow: inset 1px 0 0 var(--el-table-border-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.dmPolicy')" prop="dmPolicy">
|
||||
<el-select v-model="form.dmPolicy">
|
||||
<el-option :label="t('aiTools.agents.pairingCode')" value="pairing" />
|
||||
<el-option :label="t('aiTools.agents.policyOpen')" value="open" />
|
||||
<el-option :label="t('aiTools.agents.policyAllowlist')" value="allowlist" />
|
||||
<el-option :label="t('aiTools.agents.policyDisabled')" value="disabled" />
|
||||
@@ -51,6 +52,16 @@
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<el-alert type="info" :closable="false" :title="t('aiTools.agents.channelAutoRestartHelper')" />
|
||||
<template v-if="form.dmPolicy === 'pairing'">
|
||||
<el-form-item :label="t('aiTools.agents.pairingCode')">
|
||||
<el-input v-model="pairingCode" :placeholder="t('aiTools.agents.pairingCodePlaceholder')" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" plain :loading="approving" @click="approvePairing">
|
||||
{{ t('aiTools.agents.approvePairing') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
@@ -58,15 +69,15 @@
|
||||
import { reactive, ref } from 'vue';
|
||||
import type { FormInstance } from 'element-plus';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { getAgentQQBotConfig, updateAgentQQBotConfig } from '@/api/modules/ai';
|
||||
import { approveAgentChannelPairing, getAgentQQBotConfig, updateAgentQQBotConfig } from '@/api/modules/ai';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { MsgSuccess, MsgWarning } from '@/utils/message';
|
||||
|
||||
interface QQBotForm {
|
||||
enabled: boolean;
|
||||
appId: string;
|
||||
clientSecret: string;
|
||||
dmPolicy: 'open' | 'allowlist' | 'disabled';
|
||||
dmPolicy: 'pairing' | 'open' | 'allowlist' | 'disabled';
|
||||
allowFromText: string;
|
||||
groupPolicy: 'open' | 'allowlist' | 'disabled';
|
||||
groupAllowFromText: string;
|
||||
@@ -75,7 +86,9 @@ interface QQBotForm {
|
||||
const { t } = useI18n();
|
||||
const formRef = ref<FormInstance>();
|
||||
const saving = ref(false);
|
||||
const approving = ref(false);
|
||||
const agentId = ref(0);
|
||||
const pairingCode = ref('');
|
||||
const form = reactive<QQBotForm>({
|
||||
enabled: true,
|
||||
appId: '',
|
||||
@@ -130,6 +143,7 @@ const rules = reactive({
|
||||
|
||||
const load = async (id: number) => {
|
||||
agentId.value = id;
|
||||
pairingCode.value = '';
|
||||
const res = await getAgentQQBotConfig({ agentId: id });
|
||||
form.enabled = res.data?.enabled ?? true;
|
||||
form.appId = res.data?.bots?.[0]?.appId || '';
|
||||
@@ -173,6 +187,28 @@ const save = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
const approvePairing = async () => {
|
||||
if (!agentId.value) {
|
||||
return;
|
||||
}
|
||||
if (!pairingCode.value) {
|
||||
MsgWarning(t('aiTools.agents.pairingCodePlaceholder'));
|
||||
return;
|
||||
}
|
||||
approving.value = true;
|
||||
try {
|
||||
await approveAgentChannelPairing({
|
||||
agentId: agentId.value,
|
||||
type: 'qqbot',
|
||||
pairingCode: pairingCode.value,
|
||||
});
|
||||
pairingCode.value = '';
|
||||
MsgSuccess(t('aiTools.agents.pairingApproveSuccess'));
|
||||
} finally {
|
||||
approving.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
load,
|
||||
});
|
||||
|
||||
@@ -18,70 +18,74 @@
|
||||
<el-form-item :label="t('commons.table.status')" class="mt-4">
|
||||
<el-switch v-model="form.enabled" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.dmPolicy')" prop="dmPolicy">
|
||||
<el-select v-model="form.dmPolicy">
|
||||
<el-option :label="t('aiTools.agents.policyAllowlist')" value="allowlist" />
|
||||
<el-option :label="t('aiTools.agents.policyOpen')" value="open" />
|
||||
<el-option :label="t('aiTools.agents.policyDisabled')" value="disabled" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="form.dmPolicy === 'allowlist'"
|
||||
:label="t('aiTools.agents.allowFrom')"
|
||||
prop="allowFromText"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.allowFromText"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
:placeholder="t('aiTools.agents.allowFromPlaceholder')"
|
||||
/>
|
||||
<span class="input-help">{{ t('aiTools.agents.allowFromHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.groupPolicy')" prop="groupPolicy">
|
||||
<el-select v-model="form.groupPolicy">
|
||||
<el-option :label="t('aiTools.agents.policyOpen')" value="open" />
|
||||
<el-option :label="t('aiTools.agents.policyAllowlist')" value="allowlist" />
|
||||
<el-option :label="t('aiTools.agents.policyDisabled')" value="disabled" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="form.groupPolicy === 'allowlist'"
|
||||
:label="t('aiTools.agents.groupAllowFrom')"
|
||||
prop="groupAllowFromText"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.groupAllowFromText"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
:placeholder="t('aiTools.agents.groupAllowFromPlaceholder')"
|
||||
/>
|
||||
<span class="input-help">{{ t('aiTools.agents.groupAllowFromHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.separateSessionByConversation')">
|
||||
<el-switch v-model="form.separateSessionByConversation" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.groupSessionScope')" prop="groupSessionScope">
|
||||
<el-select v-model="form.groupSessionScope">
|
||||
<el-option :label="t('aiTools.agents.groupSessionScopeGroup')" value="group" />
|
||||
<el-option :label="t('aiTools.agents.groupSessionScopeGroupSender')" value="group_sender" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.sharedMemoryAcrossConversations')">
|
||||
<el-switch v-model="form.sharedMemoryAcrossConversations" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.asyncMode')">
|
||||
<el-switch v-model="form.asyncMode" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.asyncMode" :label="t('aiTools.agents.ackText')">
|
||||
<el-input v-model="form.ackText" />
|
||||
</el-form-item>
|
||||
<div>
|
||||
<el-form-item :label="t('aiTools.agents.dmPolicy')" prop="dmPolicy">
|
||||
<el-select v-model="form.dmPolicy">
|
||||
<el-option :label="t('aiTools.agents.policyAllowlist')" value="allowlist" />
|
||||
<el-option :label="t('aiTools.agents.policyOpen')" value="open" />
|
||||
<el-option :label="t('aiTools.agents.policyDisabled')" value="disabled" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="form.dmPolicy === 'allowlist'"
|
||||
:label="t('aiTools.agents.allowFrom')"
|
||||
prop="allowFromText"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.allowFromText"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
:placeholder="t('aiTools.agents.allowFromPlaceholder')"
|
||||
/>
|
||||
<span class="input-help">{{ t('aiTools.agents.allowFromHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.groupPolicy')" prop="groupPolicy">
|
||||
<el-select v-model="form.groupPolicy">
|
||||
<el-option :label="t('aiTools.agents.policyOpen')" value="open" />
|
||||
<el-option :label="t('aiTools.agents.policyAllowlist')" value="allowlist" />
|
||||
<el-option :label="t('aiTools.agents.policyDisabled')" value="disabled" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="form.groupPolicy === 'allowlist'"
|
||||
:label="t('aiTools.agents.groupAllowFrom')"
|
||||
prop="groupAllowFromText"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.groupAllowFromText"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
:placeholder="t('aiTools.agents.groupAllowFromPlaceholder')"
|
||||
/>
|
||||
<span class="input-help">{{ t('aiTools.agents.groupAllowFromHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.separateSessionByConversation')">
|
||||
<el-switch v-model="form.separateSessionByConversation" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.groupSessionScope')" prop="groupSessionScope">
|
||||
<el-select v-model="form.groupSessionScope">
|
||||
<el-option :label="t('aiTools.agents.groupSessionScopeGroup')" value="group" />
|
||||
<el-option :label="t('aiTools.agents.groupSessionScopeGroupSender')" value="group_sender" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.sharedMemoryAcrossConversations')">
|
||||
<el-switch v-model="form.sharedMemoryAcrossConversations" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.asyncMode')">
|
||||
<el-switch v-model="form.asyncMode" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.asyncMode" :label="t('aiTools.agents.ackText')">
|
||||
<el-input v-model="form.ackText" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<ChannelBots
|
||||
:bots="form.bots"
|
||||
:fields="botFields"
|
||||
:create-bot="createBot"
|
||||
:show-name-field="false"
|
||||
summary-label="Client ID"
|
||||
unique-field-prop="clientId"
|
||||
unique-field-label="Client ID"
|
||||
:summary-formatter="getBotSummary"
|
||||
:add-disabled="!installed"
|
||||
:disabled="!installed"
|
||||
@@ -280,6 +284,9 @@ const saveChannel = async (action: 'delete' | 'save' = 'save') => {
|
||||
bots: form.bots,
|
||||
});
|
||||
MsgSuccess(action === 'delete' ? t('commons.msg.deleteSuccess') : t('aiTools.agents.saveSuccess'));
|
||||
} catch (error) {
|
||||
await reload();
|
||||
throw error;
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
:fields="botFields"
|
||||
:create-bot="createBot"
|
||||
summary-label="Token"
|
||||
unique-field-prop="token"
|
||||
unique-field-label="Token"
|
||||
:summary-formatter="getBotSummary"
|
||||
defaultable
|
||||
approvable
|
||||
|
||||
@@ -17,52 +17,57 @@
|
||||
<el-form-item :label="t('commons.table.status')" class="mt-4">
|
||||
<el-switch v-model="form.enabled" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.threadSession')">
|
||||
<el-switch v-model="form.threadSession" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.streaming')">
|
||||
<el-switch v-model="form.streaming" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.replyMode')" prop="replyMode">
|
||||
<el-input v-model="form.replyMode" placeholder="auto" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.requireMention')" prop="requireMention">
|
||||
<el-select v-model="form.requireMention">
|
||||
<el-option :label="t('aiTools.agents.requireMentionTrue')" value="true" />
|
||||
<el-option :label="t('aiTools.agents.requireMentionFalse')" value="false" />
|
||||
<el-option :label="t('aiTools.agents.requireMentionOpen')" value="open" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.groupPolicy')" prop="groupPolicy">
|
||||
<el-select v-model="form.groupPolicy">
|
||||
<el-option :label="t('aiTools.agents.policyOpen')" value="open" />
|
||||
<el-option :label="t('aiTools.agents.policyAllowlist')" value="allowlist" />
|
||||
<el-option :label="t('aiTools.agents.policyDisabled')" value="disabled" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="form.groupPolicy === 'allowlist'"
|
||||
:label="t('aiTools.agents.groupAllowFrom')"
|
||||
prop="groupAllowFromText"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.groupAllowFromText"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
:placeholder="t('aiTools.agents.groupAllowFromPlaceholder')"
|
||||
/>
|
||||
<span class="input-help">{{ t('aiTools.agents.groupAllowFromHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-link type="primary" icon="Position" @click="toFeishuDoc">
|
||||
{{ t('container.mirrorsHelper2') }}
|
||||
</el-link>
|
||||
</el-form-item>
|
||||
<div>
|
||||
<el-form-item :label="t('aiTools.agents.threadSession')">
|
||||
<el-switch v-model="form.threadSession" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.streaming')">
|
||||
<el-switch v-model="form.streaming" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.replyMode')" prop="replyMode">
|
||||
<el-input v-model="form.replyMode" placeholder="auto" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.requireMention')" prop="requireMention">
|
||||
<el-select v-model="form.requireMention">
|
||||
<el-option :label="t('aiTools.agents.requireMentionTrue')" value="true" />
|
||||
<el-option :label="t('aiTools.agents.requireMentionFalse')" value="false" />
|
||||
<el-option :label="t('aiTools.agents.requireMentionOpen')" value="open" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('aiTools.agents.groupPolicy')" prop="groupPolicy">
|
||||
<el-select v-model="form.groupPolicy">
|
||||
<el-option :label="t('aiTools.agents.policyOpen')" value="open" />
|
||||
<el-option :label="t('aiTools.agents.policyAllowlist')" value="allowlist" />
|
||||
<el-option :label="t('aiTools.agents.policyDisabled')" value="disabled" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="form.groupPolicy === 'allowlist'"
|
||||
:label="t('aiTools.agents.groupAllowFrom')"
|
||||
prop="groupAllowFromText"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.groupAllowFromText"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
:placeholder="t('aiTools.agents.groupAllowFromPlaceholder')"
|
||||
/>
|
||||
<span class="input-help">{{ t('aiTools.agents.groupAllowFromHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-link type="primary" icon="Position" @click="toFeishuDoc">
|
||||
{{ t('container.mirrorsHelper2') }}
|
||||
</el-link>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<ChannelBots
|
||||
:bots="form.bots"
|
||||
:fields="botFields"
|
||||
:create-bot="createBot"
|
||||
:create-preset="defaultBotCreatePreset"
|
||||
summary-label="App ID"
|
||||
unique-field-prop="appId"
|
||||
unique-field-label="App ID"
|
||||
:summary-formatter="getBotSummary"
|
||||
:add-disabled="!installed"
|
||||
:disabled="!installed"
|
||||
@@ -84,7 +89,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue';
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import type { FormInstance } from 'element-plus';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
@@ -150,6 +155,16 @@ const form = reactive<FeishuForm>({
|
||||
bots: [],
|
||||
installed: false,
|
||||
});
|
||||
const defaultBotCreatePreset = computed(() => {
|
||||
if (form.bots.length > 0) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
accountId: 'default',
|
||||
name: 'Default',
|
||||
isDefault: true,
|
||||
};
|
||||
});
|
||||
|
||||
const parseTextList = (value: string): string[] => {
|
||||
return Array.from(
|
||||
@@ -309,6 +324,9 @@ const saveChannel = async (action: 'delete' | 'save' = 'save') => {
|
||||
bots: buildPayloadBots(),
|
||||
});
|
||||
MsgSuccess(action === 'delete' ? t('commons.msg.deleteSuccess') : t('aiTools.agents.saveSuccess'));
|
||||
} catch (error) {
|
||||
await reload();
|
||||
throw error;
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,10 @@
|
||||
:bots="form.bots"
|
||||
:fields="botFields"
|
||||
:create-bot="createBot"
|
||||
:create-preset="defaultBotCreatePreset"
|
||||
summary-label="App ID"
|
||||
unique-field-prop="appId"
|
||||
unique-field-label="App ID"
|
||||
:summary-formatter="getBotSummary"
|
||||
:add-disabled="!installed"
|
||||
:disabled="!installed"
|
||||
@@ -40,7 +43,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue';
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { AI } from '@/api/interface/ai';
|
||||
import { getAgentQQBotConfig, updateAgentQQBotConfig } from '@/api/modules/ai';
|
||||
@@ -88,6 +91,16 @@ const form = reactive<QQBotForm>({
|
||||
enabled: true,
|
||||
bots: [],
|
||||
});
|
||||
const defaultBotCreatePreset = computed(() => {
|
||||
if (form.bots.length > 0) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
accountId: 'default',
|
||||
name: 'Default',
|
||||
isDefault: true,
|
||||
};
|
||||
});
|
||||
|
||||
const parseTextList = (value: string): string[] => {
|
||||
return Array.from(
|
||||
@@ -187,6 +200,9 @@ const saveChannel = async (action: 'delete' | 'save' = 'save') => {
|
||||
})),
|
||||
});
|
||||
MsgSuccess(action === 'delete' ? t('commons.msg.deleteSuccess') : t('aiTools.agents.saveSuccess'));
|
||||
} catch (error) {
|
||||
await reload();
|
||||
throw error;
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,8 @@
|
||||
:fields="botFields"
|
||||
:create-bot="createBot"
|
||||
summary-label="Token"
|
||||
unique-field-prop="botToken"
|
||||
unique-field-label="Bot Token"
|
||||
:summary-formatter="getBotSummary"
|
||||
defaultable
|
||||
approvable
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
prop="name"
|
||||
min-width="120"
|
||||
/>
|
||||
<el-table-column :label="$t('commons.table.type')" prop="agentType" min-width="120">
|
||||
<el-table-column :label="$t('commons.table.type')" prop="agentType" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<div class="agent-type-cell">
|
||||
<img
|
||||
@@ -702,6 +702,7 @@ onMounted(async () => {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.agent-type-icon {
|
||||
|
||||
Reference in New Issue
Block a user