Skip to content

Commit 4b69699

Browse files
committed
Merge branch 'main' of github.com:aliyun/sop-chat
2 parents 87e896b + f08852d commit 4b69699

File tree

16 files changed

+379
-99
lines changed

16 files changed

+379
-99
lines changed

backend/config.yaml.example

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ global:
1717
# project 与 workspace 根据产品类型二选一
1818
# project: "etl-dev" # SLS 产品对应的 Project
1919
# workspace: "" # CMS 产品对应的 Workspace
20+
# region: "" # CMS 产品对应的 Region
2021

2122
# ─── 认证配置 ─────────────────────────────────────────────────────────────────
2223
auth:
@@ -85,6 +86,7 @@ auth:
8586
# product: "sls" # 数字员工对应的product(为空则使用 global.product)
8687
# project: "" # SLS 产品:数字员工所属 project(product=sls 时生效,写入 Thread Variables)
8788
# workspace: "" # CMS 产品:数字员工所属 workspace(product=cms 时生效,写入 Thread Variables)
89+
# region: "" # CMS 产品:数字员工所属 region(product=cms 时生效,写入 Thread Variables)
8890
# allowedGroupUsers: # 群用户白名单(钉钉 senderNick):限制群聊中可 @ 机器人提问的用户;为空则允许所有群成员
8991
# - "张三"
9092
# - "李四"
@@ -102,6 +104,7 @@ auth:
102104
# product: "sls" # 数字员工对应的product(为空则使用 global.product)
103105
# project: "" # SLS 产品:数字员工所属 project(product=sls 时生效,写入 Thread Variables)
104106
# workspace: "" # CMS 产品:数字员工所属 workspace(product=cms 时生效,写入 Thread Variables)
107+
# region: "" # CMS 产品:数字员工所属 region(product=cms 时生效,写入 Thread Variables)
105108
# verificationToken: "" # WebSocket 模式可留空
106109
# eventEncryptKey: "" # WebSocket 模式可留空
107110
# allowedUsers: [] # 飞书用户 open_id 白名单,为空则允许所有用户
@@ -119,6 +122,7 @@ auth:
119122
# product: "sls" # 数字员工对应的product(为空则使用 global.product)
120123
# project: "" # SLS 产品:数字员工所属 project(product=sls 时生效,写入 Thread Variables)
121124
# workspace: "" # CMS 产品:数字员工所属 workspace(product=cms 时生效,写入 Thread Variables)
125+
# region: "" # CMS 产品:数字员工所属 region(product=cms 时生效,写入 Thread Variables)
122126
# allowedUsers: [] # 企业微信 userid 白名单,为空则允许所有用户
123127
# webhookUrl: "" # 群机器人 Webhook URL(可选);配置后 AI 回复会同步推送到群聊
124128
# # 旧版嵌套配置(向后兼容,推荐使用下方独立 wecomBot 节点)
@@ -135,6 +139,7 @@ auth:
135139
# product: "sls" # 数字员工对应的product(为空则使用 global.product)
136140
# project: "" # SLS 产品:数字员工所属 project(product=sls 时生效,写入 Thread Variables)
137141
# workspace: "" # CMS 产品:数字员工所属 workspace(product=cms 时生效,写入 Thread Variables)
142+
# region: "" # CMS 产品:数字员工所属 region(product=cms 时生效,写入 Thread Variables)
138143
# # url: "wss://openws.work.weixin.qq.com" # WebSocket 地址(默认值,通常无需修改)
139144
# # pingIntervalSec: 30 # 心跳间隔(秒)
140145
# # reconnectDelaySec: 5 # 重连基础延迟(秒,指数退避)
@@ -166,10 +171,11 @@ auth:
166171
# cron: "0 9 * * 1-5" # 工作日每天 9:00
167172
# prompt: "请总结昨日工作进展和今日计划"
168173
# employeeName: "my-sop-employee"
169-
# # product/project/workspace 可选,为空时使用全局配置
174+
# # product/project/workspace/region 可选,为空时使用全局配置
170175
# # product: sls # sls 或 cms
171176
# # project: "etl-dev" # SLS 产品对应的 Project
172177
# # workspace: "" # CMS 产品对应的 Workspace
178+
# # region: "" # CMS 产品对应的 Region
173179
# webhook:
174180
# type: dingtalk
175181
# url: "https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN"

backend/internal/api/chat.go

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
package api
22

33
import (
4+
"context"
45
"encoding/json"
56
"fmt"
67
"log"
78
"net/http"
89
"time"
910

1011
cmsclient "github.com/alibabacloud-go/cms-20240330/v6/client"
11-
"github.com/alibabacloud-go/tea/dara"
1212
"github.com/alibabacloud-go/tea/tea"
1313
"github.com/gin-gonic/gin"
14+
15+
"sop-chat/pkg/sopchat"
1416
)
1517

1618
// ChatStreamRequest 聊天流式请求
@@ -72,9 +74,25 @@ func (s *Server) handleChatStream(c *gin.Context) {
7274
"timeZone": timeZone,
7375
"language": language,
7476
}
75-
// 根据全局 product 配置决定是否附加 skill=sop
77+
// 根据全局 product 配置决定附加的变量
7678
if s.isSlsProduct() {
7779
variables["skill"] = "sop"
80+
if s.globalConfig != nil && s.globalConfig.Global.Project != "" {
81+
variables["project"] = s.globalConfig.Global.Project
82+
}
83+
} else {
84+
if s.globalConfig != nil {
85+
if s.globalConfig.Global.Workspace != "" {
86+
variables["workspace"] = s.globalConfig.Global.Workspace
87+
}
88+
if s.globalConfig.Global.Region != "" {
89+
variables["region"] = s.globalConfig.Global.Region
90+
}
91+
}
92+
// CMS 产品:添加 fromTime/toTime(15 分钟窗口)
93+
now := time.Now()
94+
variables["fromTime"] = now.Add(-15 * time.Minute).Unix()
95+
variables["toTime"] = now.Unix()
7896
}
7997

8098
// 创建聊天请求
@@ -100,8 +118,11 @@ func (s *Server) handleChatStream(c *gin.Context) {
100118
responseChan := make(chan *cmsclient.CreateChatResponse)
101119
errorChan := make(chan error)
102120

103-
// 在 goroutine 中调用 SDK 的 CreateChatWithSSE
104-
go cmsClient.CreateChatWithSSE(request, make(map[string]*string), &dara.RuntimeOptions{}, responseChan, errorChan)
121+
// 使用带 Context 的 SSE 调用,支持客户端断开时取消
122+
ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Minute)
123+
defer cancel()
124+
runtime := sopchat.NewSSERuntimeOptions()
125+
go cmsClient.CreateChatWithSSECtx(ctx, request, make(map[string]*string), runtime, responseChan, errorChan)
105126

106127
// 用于保存元数据
107128
var requestId, threadId string
@@ -153,6 +174,12 @@ func (s *Server) handleChatStream(c *gin.Context) {
153174
}
154175
// ThreadId 不在 Body 中,使用请求中的 threadId
155176

177+
// 检测 done 消息,提前结束 SSE 循环
178+
if sopchat.IsDoneMessage(response.Body) {
179+
done = true
180+
break
181+
}
182+
156183
// 处理消息:直接将消息序列化为 JSON 并转发
157184
if response.Body.Messages != nil {
158185
for _, msg := range response.Body.Messages {

backend/internal/api/configui.go

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ type configUIScheduledTask struct {
8282
Product string `json:"product"`
8383
Project string `json:"project"`
8484
Workspace string `json:"workspace"`
85+
Region string `json:"region"`
8586
Webhook configUIWebhook `json:"webhook"`
8687
}
8788

@@ -104,6 +105,7 @@ type configUIGlobal struct {
104105
Product string `json:"product"`
105106
Project string `json:"project"`
106107
Workspace string `json:"workspace"`
108+
Region string `json:"region"`
107109
}
108110

109111
type configUIAuth struct {
@@ -135,6 +137,7 @@ type configUIConversationRoute struct {
135137
Product string `json:"product"`
136138
Project string `json:"project"`
137139
Workspace string `json:"workspace"`
140+
Region string `json:"region"`
138141
}
139142

140143
type configUIDingTalk struct {
@@ -147,6 +150,7 @@ type configUIDingTalk struct {
147150
Product string `json:"product"`
148151
Project string `json:"project"`
149152
Workspace string `json:"workspace"`
153+
Region string `json:"region"`
150154
AllowedGroupUsers []string `json:"allowedGroupUsers"`
151155
AllowedDirectUsers []string `json:"allowedDirectUsers"`
152156
AllowedConversations []string `json:"allowedConversations"`
@@ -165,6 +169,7 @@ type configUIFeishu struct {
165169
Product string `json:"product"`
166170
Project string `json:"project"`
167171
Workspace string `json:"workspace"`
172+
Region string `json:"region"`
168173
AllowedUsers []string `json:"allowedUsers"`
169174
AllowedChats []string `json:"allowedChats"`
170175
}
@@ -184,18 +189,31 @@ type configUIWeCom struct {
184189
Product string `json:"product"`
185190
Project string `json:"project"`
186191
Workspace string `json:"workspace"`
192+
Region string `json:"region"`
187193
AllowedUsers []string `json:"allowedUsers"`
194+
WebhookURL string `json:"webhookUrl"`
195+
BotLongConn struct {
196+
Enabled bool `json:"enabled"`
197+
BotID string `json:"botId"`
198+
BotSecret string `json:"botSecret"`
199+
URL string `json:"url"`
200+
PingIntervalSec int `json:"pingIntervalSec"`
201+
ReconnectDelaySec int `json:"reconnectDelaySec"`
202+
MaxReconnectDelaySec int `json:"maxReconnectDelaySec"`
203+
} `json:"botLongConn"`
188204
}
189205

190206
type configUIWeComBot struct {
191207
Enabled bool `json:"enabled"`
208+
Name string `json:"name"`
192209
BotID string `json:"botId"`
193210
BotSecret string `json:"botSecret"`
194211
EmployeeName string `json:"employeeName"`
195212
ConciseReply bool `json:"conciseReply"`
196213
Product string `json:"product"`
197214
Project string `json:"project"`
198215
Workspace string `json:"workspace"`
216+
Region string `json:"region"`
199217
}
200218

201219
type configUIOpenAI struct {
@@ -233,6 +251,7 @@ func (s *Server) handleGetConfig(c *gin.Context) {
233251
Product: cfg.Global.Product,
234252
Project: cfg.Global.Project,
235253
Workspace: cfg.Global.Workspace,
254+
Region: cfg.Global.Region,
236255
},
237256
Auth: configUIAuth{
238257
Methods: cfg.Auth.Methods,
@@ -273,6 +292,7 @@ func (s *Server) handleGetConfig(c *gin.Context) {
273292
Product: r.Product,
274293
Project: r.Project,
275294
Workspace: r.Workspace,
295+
Region: r.Region,
276296
}
277297
}
278298
resp.DingTalk[i] = configUIDingTalk{
@@ -285,6 +305,7 @@ func (s *Server) handleGetConfig(c *gin.Context) {
285305
Product: dt.Product,
286306
Project: dt.Project,
287307
Workspace: dt.Workspace,
308+
Region: dt.Region,
288309
AllowedGroupUsers: dt.AllowedGroupUsers,
289310
AllowedDirectUsers: dt.AllowedDirectUsers,
290311
AllowedConversations: dt.AllowedConversations,
@@ -311,6 +332,7 @@ func (s *Server) handleGetConfig(c *gin.Context) {
311332
Product: ft.Product,
312333
Project: ft.Project,
313334
Workspace: ft.Workspace,
335+
Region: ft.Region,
314336
AllowedUsers: ft.AllowedUsers,
315337
AllowedChats: ft.AllowedChats,
316338
}
@@ -338,6 +360,7 @@ func (s *Server) handleGetConfig(c *gin.Context) {
338360
Product: wc.Product,
339361
Project: wc.Project,
340362
Workspace: wc.Workspace,
363+
Region: wc.Region,
341364
AllowedUsers: wc.AllowedUsers,
342365
}
343366
}
@@ -351,13 +374,15 @@ func (s *Server) handleGetConfig(c *gin.Context) {
351374
for i, wb := range cfg.Channels.WeComBot {
352375
resp.WeComBot[i] = configUIWeComBot{
353376
Enabled: wb.Enabled,
377+
Name: wb.Name,
354378
BotID: wb.BotID,
355379
BotSecret: wb.BotSecret,
356380
EmployeeName: wb.EmployeeName,
357381
ConciseReply: wb.ConciseReply,
358382
Product: wb.Product,
359383
Project: wb.Project,
360384
Workspace: wb.Workspace,
385+
Region: wb.Region,
361386
}
362387
}
363388
} else {
@@ -389,6 +414,7 @@ func (s *Server) handleGetConfig(c *gin.Context) {
389414
Product: t.Product,
390415
Project: t.Project,
391416
Workspace: t.Workspace,
417+
Region: t.Region,
392418
Webhook: configUIWebhook{
393419
Type: t.Webhook.Type,
394420
URL: t.Webhook.URL,
@@ -431,6 +457,7 @@ func (s *Server) handleSaveConfig(c *gin.Context) {
431457
Product: req.Global.Product,
432458
Project: req.Global.Project,
433459
Workspace: req.Global.Workspace,
460+
Region: req.Global.Region,
434461
},
435462
Auth: config.AuthConfig{
436463
Methods: req.Auth.Methods,
@@ -471,6 +498,7 @@ func (s *Server) handleSaveConfig(c *gin.Context) {
471498
Product: r.Product,
472499
Project: r.Project,
473500
Workspace: r.Workspace,
501+
Region: r.Region,
474502
})
475503
}
476504
}
@@ -484,6 +512,7 @@ func (s *Server) handleSaveConfig(c *gin.Context) {
484512
Product: dt.Product,
485513
Project: dt.Project,
486514
Workspace: dt.Workspace,
515+
Region: dt.Region,
487516
AllowedGroupUsers: dt.AllowedGroupUsers,
488517
AllowedDirectUsers: dt.AllowedDirectUsers,
489518
AllowedConversations: dt.AllowedConversations,
@@ -521,6 +550,7 @@ func (s *Server) handleSaveConfig(c *gin.Context) {
521550
Product: ft.Product,
522551
Project: ft.Project,
523552
Workspace: ft.Workspace,
553+
Region: ft.Region,
524554
AllowedUsers: allowedUsers,
525555
AllowedChats: allowedChats,
526556
})
@@ -555,6 +585,7 @@ func (s *Server) handleSaveConfig(c *gin.Context) {
555585
Product: wc.Product,
556586
Project: wc.Project,
557587
Workspace: wc.Workspace,
588+
Region: wc.Region,
558589
AllowedUsers: allowedUsers,
559590
})
560591
}
@@ -571,13 +602,15 @@ func (s *Server) handleSaveConfig(c *gin.Context) {
571602
if wb.BotID != "" || wb.BotSecret != "" || wb.EmployeeName != "" {
572603
cfg.Channels.WeComBot = append(cfg.Channels.WeComBot, config.WeComBotConfig{
573604
Enabled: wb.Enabled,
605+
Name: wb.Name,
574606
BotID: wb.BotID,
575607
BotSecret: wb.BotSecret,
576608
EmployeeName: wb.EmployeeName,
577609
ConciseReply: wb.ConciseReply,
578610
Product: wb.Product,
579611
Project: wb.Project,
580612
Workspace: wb.Workspace,
613+
Region: wb.Region,
581614
})
582615
}
583616
}
@@ -607,6 +640,7 @@ func (s *Server) handleSaveConfig(c *gin.Context) {
607640
Product: t.Product,
608641
Project: t.Project,
609642
Workspace: t.Workspace,
643+
Region: t.Region,
610644
Webhook: config.WebhookConfig{
611645
Type: t.Webhook.Type,
612646
URL: t.Webhook.URL,
@@ -673,13 +707,14 @@ func (s *Server) handleTriggerTask(c *gin.Context) {
673707
clientCfg.Product = globalCfg.Global.Product
674708
}
675709

676-
// 确定任务使用的 product/project/workspace
710+
// 确定任务使用的 product/project/workspace/region
677711
taskProduct := req.Product
678712
if taskProduct == "" {
679713
taskProduct = clientCfg.Product // 使用全局配置
680714
}
681715
taskProject := req.Project
682716
taskWorkspace := req.Workspace
717+
taskRegion := req.Region
683718

684719
type triggerResult struct {
685720
reply string
@@ -692,7 +727,7 @@ func (s *Server) handleTriggerTask(c *gin.Context) {
692727
if req.ConciseReply {
693728
prompt += "\n\n简化最终输出 适合聊天工具上阅读"
694729
}
695-
reply, err := scheduler.QueryEmployeeWithVariables(clientCfg, req.EmployeeName, prompt, taskProduct, taskProject, taskWorkspace)
730+
reply, err := scheduler.QueryEmployeeWithVariables(clientCfg, req.EmployeeName, prompt, taskProduct, taskProject, taskWorkspace, taskRegion)
696731
done <- triggerResult{reply: reply, err: err}
697732
}()
698733

0 commit comments

Comments
 (0)