Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions agent/app/api/v2/agents.go
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,47 @@ func (b *BaseApi) UpdateAgentWecomConfig(c *gin.Context) {
helper.Success(c)
}

// @Tags AI
// @Summary Get Agent DingTalk channel config
// @Accept json
// @Param request body dto.AgentDingTalkConfigReq true "request"
// @Success 200 {object} dto.AgentDingTalkConfig
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/agents/channel/dingtalk/get [post]
func (b *BaseApi) GetAgentDingTalkConfig(c *gin.Context) {
var req dto.AgentDingTalkConfigReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := agentService.GetDingTalkConfig(req)
if err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, data)
}

// @Tags AI
// @Summary Update Agent DingTalk channel config
// @Accept json
// @Param request body dto.AgentDingTalkConfigUpdateReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/agents/channel/dingtalk/update [post]
func (b *BaseApi) UpdateAgentDingTalkConfig(c *gin.Context) {
var req dto.AgentDingTalkConfigUpdateReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := agentService.UpdateDingTalkConfig(req); err != nil {
helper.BadRequest(c, err)
return
}
helper.Success(c)
}

// @Tags AI
// @Summary Get Agent QQ Bot channel config
// @Accept json
Expand Down
32 changes: 29 additions & 3 deletions agent/app/dto/agents.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,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 dingtalk-connector"`
PairingCode string `json:"pairingCode" validate:"required"`
}

Expand All @@ -239,6 +239,32 @@ type AgentWecomConfig struct {
Installed bool `json:"installed"`
}

type AgentDingTalkConfigReq struct {
AgentID uint `json:"agentId" validate:"required"`
}

type AgentDingTalkConfigUpdateReq struct {
AgentID uint `json:"agentId" validate:"required"`
Enabled bool `json:"enabled"`
ClientID string `json:"clientId" validate:"required"`
ClientSecret string `json:"clientSecret" validate:"required"`
DmPolicy string `json:"dmPolicy" validate:"required,oneof=pairing allowlist open disabled"`
AllowFrom []string `json:"allowFrom"`
GroupPolicy string `json:"groupPolicy" validate:"required,oneof=open allowlist disabled"`
GroupAllowFrom []string `json:"groupAllowFrom"`
}

type AgentDingTalkConfig struct {
Enabled bool `json:"enabled"`
ClientID string `json:"clientId"`
ClientSecret string `json:"clientSecret"`
DmPolicy string `json:"dmPolicy"`
AllowFrom []string `json:"allowFrom"`
GroupPolicy string `json:"groupPolicy"`
GroupAllowFrom []string `json:"groupAllowFrom"`
Installed bool `json:"installed"`
}

type AgentQQBotConfigReq struct {
AgentID uint `json:"agentId" validate:"required"`
}
Expand All @@ -259,13 +285,13 @@ type AgentQQBotConfig struct {

type AgentPluginInstallReq struct {
AgentID uint `json:"agentId" validate:"required"`
Type string `json:"type" validate:"required,oneof=qqbot wecom"`
Type string `json:"type" validate:"required,oneof=qqbot wecom dingtalk"`
TaskID string `json:"taskID" validate:"required"`
}

type AgentPluginCheckReq struct {
AgentID uint `json:"agentId" validate:"required"`
Type string `json:"type" validate:"required,oneof=qqbot wecom"`
Type string `json:"type" validate:"required,oneof=qqbot wecom dingtalk"`
}

type AgentPluginStatus struct {
Expand Down
28 changes: 28 additions & 0 deletions agent/app/service/agents.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ type IAgentService interface {
UpdateDiscordConfig(req dto.AgentDiscordConfigUpdateReq) error
GetWecomConfig(req dto.AgentWecomConfigReq) (*dto.AgentWecomConfig, error)
UpdateWecomConfig(req dto.AgentWecomConfigUpdateReq) error
GetDingTalkConfig(req dto.AgentDingTalkConfigReq) (*dto.AgentDingTalkConfig, error)
UpdateDingTalkConfig(req dto.AgentDingTalkConfigUpdateReq) error
GetQQBotConfig(req dto.AgentQQBotConfigReq) (*dto.AgentQQBotConfig, error)
UpdateQQBotConfig(req dto.AgentQQBotConfigUpdateReq) error
InstallPlugin(req dto.AgentPluginInstallReq) error
Expand Down Expand Up @@ -729,6 +731,32 @@ func (a AgentService) UpdateWecomConfig(req dto.AgentWecomConfigUpdateReq) error
})
}

func (a AgentService) GetDingTalkConfig(req dto.AgentDingTalkConfigReq) (*dto.AgentDingTalkConfig, error) {
_, install, conf, err := a.loadAgentConfig(req.AgentID)
if err != nil {
return nil, err
}
result := extractDingTalkConfig(conf)
installed, _ := checkPluginInstalled(install.ContainerName, "dingtalk")
result.Installed = installed
return &result, nil
}

func (a AgentService) UpdateDingTalkConfig(req dto.AgentDingTalkConfigUpdateReq) error {
return a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {
setDingTalkConfig(conf, dto.AgentDingTalkConfig{
Enabled: req.Enabled,
ClientID: req.ClientID,
ClientSecret: req.ClientSecret,
DmPolicy: req.DmPolicy,
AllowFrom: req.AllowFrom,
GroupPolicy: req.GroupPolicy,
GroupAllowFrom: req.GroupAllowFrom,
})
return nil
})
}

func (a AgentService) InstallPlugin(req dto.AgentPluginInstallReq) error {
_, install, err := a.loadAgentAndInstall(req.AgentID)
if err != nil {
Expand Down
116 changes: 116 additions & 0 deletions agent/app/service/agents_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,42 @@ func extractWecomConfig(conf map[string]interface{}) dto.AgentWecomConfig {
return result
}

func extractDingTalkConfig(conf map[string]interface{}) dto.AgentDingTalkConfig {
result := dto.AgentDingTalkConfig{
Enabled: true,
DmPolicy: "pairing",
GroupPolicy: "disabled",
AllowFrom: []string{},
GroupAllowFrom: []string{},
}
channels, ok := conf["channels"].(map[string]interface{})
if !ok {
return result
}
dingtalk, ok := channels["dingtalk-connector"].(map[string]interface{})
if !ok {
return result
}
if enabled, ok := dingtalk["enabled"].(bool); ok {
result.Enabled = enabled
}
if clientID, ok := dingtalk["clientId"].(string); ok {
result.ClientID = clientID
}
if clientSecret, ok := dingtalk["clientSecret"].(string); ok {
result.ClientSecret = clientSecret
}
if dmPolicy, ok := dingtalk["dmPolicy"].(string); ok && strings.TrimSpace(dmPolicy) != "" {
result.DmPolicy = dmPolicy
}
if groupPolicy, ok := dingtalk["groupPolicy"].(string); ok && strings.TrimSpace(groupPolicy) != "" {
result.GroupPolicy = groupPolicy
}
result.AllowFrom = extractStringList(dingtalk["allowFrom"])
result.GroupAllowFrom = extractStringList(dingtalk["groupAllowFrom"])
return result
}

func setWecomConfig(conf map[string]interface{}, config dto.AgentWecomConfig) {
channels := ensureChildMap(conf, "channels")
wecom := ensureChildMap(channels, "wecom")
Expand All @@ -563,6 +599,44 @@ func setWecomConfig(conf map[string]interface{}, config dto.AgentWecomConfig) {
wecomEntry["enabled"] = config.Enabled
}

func setDingTalkConfig(conf map[string]interface{}, config dto.AgentDingTalkConfig) {
channels := ensureChildMap(conf, "channels")
dingtalk := ensureChildMap(channels, "dingtalk-connector")
dingtalk["enabled"] = config.Enabled
dingtalk["clientId"] = strings.TrimSpace(config.ClientID)
dingtalk["clientSecret"] = strings.TrimSpace(config.ClientSecret)
dingtalk["dmPolicy"] = config.DmPolicy
dingtalk["groupPolicy"] = config.GroupPolicy
dingtalk["gatewayToken"] = extractGatewayToken(conf)
switch config.DmPolicy {
case "open":
dingtalk["allowFrom"] = []string{"*"}
case "allowlist":
dingtalk["allowFrom"] = append([]string(nil), config.AllowFrom...)
default:
delete(dingtalk, "allowFrom")
}
switch config.GroupPolicy {
case "open":
dingtalk["groupAllowFrom"] = []string{"*"}
case "allowlist":
dingtalk["groupAllowFrom"] = append([]string(nil), config.GroupAllowFrom...)
default:
delete(dingtalk, "groupAllowFrom")
}

plugins := ensureChildMap(conf, "plugins")
entries := ensureChildMap(plugins, "entries")
dingtalkEntry := ensureChildMap(entries, "dingtalk-connector")
dingtalkEntry["enabled"] = config.Enabled

gateway := ensureChildMap(conf, "gateway")
httpMap := ensureChildMap(gateway, "http")
endpoints := ensureChildMap(httpMap, "endpoints")
chatCompletions := ensureChildMap(endpoints, "chatCompletions")
chatCompletions["enabled"] = true
}

func setQQBotConfig(conf map[string]interface{}, config dto.AgentQQBotConfig) {
channels := ensureChildMap(conf, "channels")
qqbot := ensureChildMap(channels, "qqbot")
Expand All @@ -583,6 +657,8 @@ func resolvePluginMeta(pluginType string) (string, string, error) {
return "@sliverp/qqbot@latest", "qqbot", nil
case "wecom":
return "@wecom/wecom-openclaw-plugin", "wecom-openclaw-plugin", nil
case "dingtalk":
return "@dingtalk-real-ai/dingtalk-connector", "dingtalk-connector", nil
default:
return "", "", fmt.Errorf("unsupported plugin type")
}
Expand Down Expand Up @@ -1717,6 +1793,46 @@ func resolveServerTimezone() string {
return timezone
}

func extractStringList(value interface{}) []string {
switch values := value.(type) {
case []interface{}:
result := make([]string, 0, len(values))
for _, value := range values {
text := strings.TrimSpace(fmt.Sprintf("%v", value))
if text == "" {
continue
}
result = append(result, text)
}
return result
case []string:
result := make([]string, 0, len(values))
for _, value := range values {
text := strings.TrimSpace(value)
if text == "" {
continue
}
result = append(result, text)
}
return result
default:
return []string{}
}
}

func extractGatewayToken(conf map[string]interface{}) string {
gateway, ok := conf["gateway"].(map[string]interface{})
if !ok {
return ""
}
auth, ok := gateway["auth"].(map[string]interface{})
if !ok {
return ""
}
token, _ := auth["token"].(string)
return token
}

func ensureChildMap(parent map[string]interface{}, key string) map[string]interface{} {
if child, ok := parent[key].(map[string]interface{}); ok {
return child
Expand Down
2 changes: 2 additions & 0 deletions agent/router/ro_ai.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) {
aiToolsRouter.POST("/agents/channel/discord/update", baseApi.UpdateAgentDiscordConfig)
aiToolsRouter.POST("/agents/channel/wecom/get", baseApi.GetAgentWecomConfig)
aiToolsRouter.POST("/agents/channel/wecom/update", baseApi.UpdateAgentWecomConfig)
aiToolsRouter.POST("/agents/channel/dingtalk/get", baseApi.GetAgentDingTalkConfig)
aiToolsRouter.POST("/agents/channel/dingtalk/update", baseApi.UpdateAgentDingTalkConfig)
aiToolsRouter.POST("/agents/channel/qqbot/get", baseApi.GetAgentQQBotConfig)
aiToolsRouter.POST("/agents/channel/qqbot/update", baseApi.UpdateAgentQQBotConfig)
aiToolsRouter.POST("/agents/plugin/install", baseApi.InstallAgentPlugin)
Expand Down
29 changes: 22 additions & 7 deletions agent/utils/terminal/ai/config_runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
)

var agentAccountRepo = repo.NewIAgentAccountRepo()
var agentAccountModelRepo = repo.NewIAgentAccountModelRepo()

type TerminalRuntimeSettings struct {
AccountID uint
Expand Down Expand Up @@ -48,9 +49,9 @@ func ResolveGeneratorConfig(accountID uint) (GeneratorConfig, time.Duration, err
if provider == "" {
return GeneratorConfig{}, 0, fmt.Errorf("agent account provider is required")
}
model := strings.TrimSpace(account.Model)
if model == "" {
model = defaultModelForProvider(provider)
model, maxTokens, err := resolveAccountModelConfig(account.ID, provider)
if err != nil {
return GeneratorConfig{}, 0, err
}
baseURL := strings.TrimSpace(account.BaseURL)
if baseURL == "" {
Expand All @@ -71,7 +72,7 @@ func ResolveGeneratorConfig(accountID uint) (GeneratorConfig, time.Duration, err
APIKey: strings.TrimSpace(apiKey),
Model: model,
APIType: strings.TrimSpace(account.APIType),
MaxTokens: account.MaxTokens,
MaxTokens: maxTokens,
}, 30 * time.Second, nil
}

Expand All @@ -83,12 +84,26 @@ func lookupProviderAPIKey(provider string) string {
return strings.TrimSpace(os.Getenv(envKey))
}

func defaultModelForProvider(provider string) string {
func defaultModelForProvider(provider string) (string, int) {
meta, ok := providercatalog.Get(provider)
if !ok || len(meta.Models) == 0 {
return ""
return "", 0
}
return meta.Models[0].ID, meta.Models[0].MaxTokens
}

func resolveAccountModelConfig(accountID uint, provider string) (string, int, error) {
if accountID > 0 {
rows, err := agentAccountModelRepo.List(repo.WithByAccountID(accountID), repo.WithOrderAsc("sort_order"), repo.WithOrderAsc("id"))
if err != nil {
return "", 0, err
}
if len(rows) > 0 {
return strings.TrimSpace(rows[0].Model), rows[0].MaxTokens, nil
}
}
return meta.Models[0].ID
model, maxTokens := defaultModelForProvider(provider)
return model, maxTokens, nil
}

func ResolveGeneratorConfigFromAgentSettings() (GeneratorConfig, uint, time.Duration, error) {
Expand Down
Loading
Loading