refactor: 将客服后端重构为宿主可嵌入模块
- 注入数据库、运行时配置、统一响应、文件存储和平台 AI 能力,补充业务读写工具与客户快捷操作契约。 - 移除模块内重复的组织、客户、工单、标签、技能、旧工作流、MCP 和迁移实现,将身份权限与业务主体交由宿主管理。 - 使用 libSQL 重构向量存储,并完善图片消息、访客身份、排队调度、企业微信和支持聊天页面。 - 统一 HTTP、DTO 与 WebSocket 的 snake_case 协议,补齐模块初始化、业务动作和公共载荷等回归测试。
This commit is contained in:
+126
-27
@@ -1,13 +1,138 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const SettingsPrefix = "ai_agent_"
|
||||
|
||||
func FromSettings(values map[string]string) (*Config, error) {
|
||||
cfg := defaultConfig()
|
||||
setString(values, SettingsPrefix+"language", &cfg.Language)
|
||||
setString(values, SettingsPrefix+"storage_default", (*string)(&cfg.Storage.Default))
|
||||
if err := setInt64(values, SettingsPrefix+"storage_max_upload_size_mb", &cfg.Storage.MaxUploadSizeMB); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setString(values, SettingsPrefix+"storage_local_root", &cfg.Storage.Local.Root)
|
||||
setString(values, SettingsPrefix+"storage_local_base_url", &cfg.Storage.Local.BaseURL)
|
||||
setString(values, SettingsPrefix+"storage_oss_endpoint", &cfg.Storage.OSS.Endpoint)
|
||||
setString(values, SettingsPrefix+"storage_oss_bucket", &cfg.Storage.OSS.Bucket)
|
||||
setString(values, SettingsPrefix+"storage_oss_access_key_id", &cfg.Storage.OSS.AccessKeyID)
|
||||
setString(values, SettingsPrefix+"storage_oss_access_key_secret", &cfg.Storage.OSS.AccessKeySecret)
|
||||
setString(values, SettingsPrefix+"storage_oss_base_url", &cfg.Storage.OSS.BaseURL)
|
||||
if err := setBool(values, SettingsPrefix+"storage_oss_private", &cfg.Storage.OSS.Private); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := setInt(values, SettingsPrefix+"storage_oss_signed_url_expire", &cfg.Storage.OSS.SignedURLExpire); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setString(values, "ai_vector_path", &cfg.VectorDB.Path)
|
||||
if err := setBool(values, SettingsPrefix+"wxwork_enabled", &cfg.WxWork.Enabled); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setString(values, SettingsPrefix+"wxwork_corp_id", &cfg.WxWork.CorpID)
|
||||
setString(values, SettingsPrefix+"wxwork_corp_secret", &cfg.WxWork.CorpSecret)
|
||||
setString(values, SettingsPrefix+"wxwork_agent_id", &cfg.WxWork.AgentID)
|
||||
setString(values, SettingsPrefix+"wxwork_rsa_private_key", &cfg.WxWork.RSAPrivateKey)
|
||||
setString(values, SettingsPrefix+"wxwork_token", &cfg.WxWork.Token)
|
||||
setString(values, SettingsPrefix+"wxwork_encoding_aes_key", &cfg.WxWork.EncodingAESKey)
|
||||
if err := setBool(values, SettingsPrefix+"wxwork_notify_enabled", &cfg.WxWork.Notify.Enabled); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := setJSON(values, SettingsPrefix+"wxwork_notify_to_users", &cfg.WxWork.Notify.ToUsers); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := setBool(values, SettingsPrefix+"wxwork_notify_safe", &cfg.WxWork.Notify.Safe); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := setBool(values, SettingsPrefix+"wxwork_notify_dedupe", &cfg.WxWork.Notify.EnableDuplicateCheck); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := setInt(values, SettingsPrefix+"wxwork_notify_dedupe_interval", &cfg.WxWork.Notify.DuplicateCheckInterval); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func defaultConfig() *Config {
|
||||
return &Config{
|
||||
Language: "zh-CN",
|
||||
Server: ServerConfig{Port: 8080},
|
||||
Storage: StorageConfig{
|
||||
Default: enums.AssetProviderLocal, MaxUploadSizeMB: 20,
|
||||
Local: LocalStorageConfig{Root: "data/storage", BaseURL: "/storage"},
|
||||
OSS: OSSStorageConfig{SignedURLExpire: 600},
|
||||
},
|
||||
VectorDB: VectorDBConfig{Path: "data/agent/vectors.db"},
|
||||
WxWork: WxWorkConfig{Notify: WxWorkNotifyConfig{
|
||||
EnableDuplicateCheck: true, DuplicateCheckInterval: 1800,
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
func setString(values map[string]string, key string, target *string) {
|
||||
if value, ok := values[key]; ok {
|
||||
*target = strings.TrimSpace(value)
|
||||
}
|
||||
}
|
||||
|
||||
func setInt(values map[string]string, key string, target *int) error {
|
||||
value, ok := values[key]
|
||||
if !ok || strings.TrimSpace(value) == "" {
|
||||
return nil
|
||||
}
|
||||
parsed, err := strconv.Atoi(strings.TrimSpace(value))
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid setting %s: %w", key, err)
|
||||
}
|
||||
*target = parsed
|
||||
return nil
|
||||
}
|
||||
|
||||
func setInt64(values map[string]string, key string, target *int64) error {
|
||||
value, ok := values[key]
|
||||
if !ok || strings.TrimSpace(value) == "" {
|
||||
return nil
|
||||
}
|
||||
parsed, err := strconv.ParseInt(strings.TrimSpace(value), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid setting %s: %w", key, err)
|
||||
}
|
||||
*target = parsed
|
||||
return nil
|
||||
}
|
||||
|
||||
func setBool(values map[string]string, key string, target *bool) error {
|
||||
value, ok := values[key]
|
||||
if !ok || strings.TrimSpace(value) == "" {
|
||||
return nil
|
||||
}
|
||||
parsed, err := strconv.ParseBool(strings.TrimSpace(value))
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid setting %s: %w", key, err)
|
||||
}
|
||||
*target = parsed
|
||||
return nil
|
||||
}
|
||||
|
||||
func setJSON(values map[string]string, key string, target any) error {
|
||||
value, ok := values[key]
|
||||
if !ok || strings.TrimSpace(value) == "" {
|
||||
return nil
|
||||
}
|
||||
if err := json.Unmarshal([]byte(value), target); err != nil {
|
||||
return fmt.Errorf("invalid setting %s: %w", key, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Language string `yaml:"language"`
|
||||
Server ServerConfig `yaml:"server"`
|
||||
@@ -15,7 +140,6 @@ type Config struct {
|
||||
Logger LoggerConfig `yaml:"logger"`
|
||||
Storage StorageConfig `yaml:"storage"`
|
||||
VectorDB VectorDBConfig `yaml:"vectorDB"`
|
||||
MCP MCPConfig `yaml:"mcp"`
|
||||
WxWork WxWorkConfig `yaml:"wxWork"`
|
||||
}
|
||||
|
||||
@@ -114,34 +238,9 @@ type OSSStorageConfig struct {
|
||||
}
|
||||
|
||||
type VectorDBConfig struct {
|
||||
Type string `yaml:"type"`
|
||||
Qdrant QdrantVectorDBConfig `yaml:"qdrant"`
|
||||
LanceDB LanceDBVectorDBConfig `yaml:"lancedb"`
|
||||
}
|
||||
|
||||
type QdrantVectorDBConfig struct {
|
||||
Host string `yaml:"host"`
|
||||
GrpcPort int `yaml:"grpcPort"`
|
||||
APIKey string `yaml:"apiKey"`
|
||||
UseTLS bool `yaml:"useTls"`
|
||||
}
|
||||
|
||||
type LanceDBVectorDBConfig struct {
|
||||
Path string `yaml:"path"`
|
||||
}
|
||||
|
||||
type MCPConfig struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Servers map[string]MCPServerConfig `yaml:"servers"`
|
||||
}
|
||||
|
||||
type MCPServerConfig struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Endpoint string `yaml:"endpoint"`
|
||||
TimeoutMS int `yaml:"timeoutMs"`
|
||||
Headers map[string]string `yaml:"headers"`
|
||||
}
|
||||
|
||||
// WxWorkConfig defines the WeCom application used for customer-service
|
||||
// callbacks and notifications. Dashboard login is owned by be-system.
|
||||
type WxWorkConfig struct {
|
||||
|
||||
@@ -41,23 +41,18 @@ func TestLoadOverridesValuesFromEnvironment(t *testing.T) {
|
||||
content := []byte(`server:
|
||||
port: 8083
|
||||
db:
|
||||
type: sqlite
|
||||
dsn: file:./data/app.db?_busy_timeout=5000
|
||||
type: postgres
|
||||
dsn: postgres-dsn
|
||||
storage:
|
||||
local:
|
||||
baseUrl: /storage
|
||||
mcp:
|
||||
servers:
|
||||
system:
|
||||
endpoint: http://127.0.0.1:8083/api/mcp
|
||||
`)
|
||||
if err := os.WriteFile(path, content, 0600); err != nil {
|
||||
t.Fatalf("WriteFile() error = %v", err)
|
||||
}
|
||||
t.Setenv("AGENT_DESK_SERVER_PORT", "8090")
|
||||
t.Setenv("AGENT_DESK_DB_DSN", "mysql-dsn")
|
||||
t.Setenv("AGENT_DESK_DB_DSN", "postgres-override-dsn")
|
||||
t.Setenv("AGENT_DESK_STORAGE_LOCAL_BASEURL", "/files")
|
||||
t.Setenv("AGENT_DESK_MCP_SERVERS_SYSTEM_ENDPOINT", "http://127.0.0.1:8090/api/mcp")
|
||||
|
||||
cfg, err := Load(path)
|
||||
if err != nil {
|
||||
@@ -67,16 +62,48 @@ mcp:
|
||||
if cfg.Server.Port != 8090 {
|
||||
t.Fatalf("Server.Port=%d want 8090", cfg.Server.Port)
|
||||
}
|
||||
if cfg.DB.Type != "sqlite" {
|
||||
t.Fatalf("DB.Type=%q want sqlite", cfg.DB.Type)
|
||||
if cfg.DB.Type != "postgres" {
|
||||
t.Fatalf("DB.Type=%q want postgres", cfg.DB.Type)
|
||||
}
|
||||
if cfg.DB.DSN != "mysql-dsn" {
|
||||
t.Fatalf("DB.DSN=%q want mysql-dsn", cfg.DB.DSN)
|
||||
if cfg.DB.DSN != "postgres-override-dsn" {
|
||||
t.Fatalf("DB.DSN=%q want postgres-override-dsn", cfg.DB.DSN)
|
||||
}
|
||||
if cfg.Storage.Local.BaseURL != "/files" {
|
||||
t.Fatalf("Storage.Local.BaseURL=%q want /files", cfg.Storage.Local.BaseURL)
|
||||
}
|
||||
if cfg.MCP.Servers["system"].Endpoint != "http://127.0.0.1:8090/api/mcp" {
|
||||
t.Fatalf("MCP system endpoint=%q", cfg.MCP.Servers["system"].Endpoint)
|
||||
}
|
||||
|
||||
func TestFromSettingsBuildsRuntimeConfig(t *testing.T) {
|
||||
cfg, err := FromSettings(map[string]string{
|
||||
"ai_agent_language": "en-US",
|
||||
"ai_agent_storage_default": "oss",
|
||||
"ai_agent_storage_max_upload_size_mb": "64",
|
||||
"ai_agent_storage_oss_private": "true",
|
||||
"ai_vector_path": "/var/lib/agent-desk/vectors.db",
|
||||
"ai_agent_wxwork_notify_to_users": `[1,2,3]`,
|
||||
"ai_agent_wxwork_notify_dedupe_interval": "900",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("FromSettings() error = %v", err)
|
||||
}
|
||||
if cfg.Language != "en-US" || cfg.Storage.Default != "oss" {
|
||||
t.Fatalf("unexpected basic config: %+v", cfg)
|
||||
}
|
||||
if cfg.Storage.MaxUploadSizeMB != 64 || !cfg.Storage.OSS.Private {
|
||||
t.Fatalf("unexpected storage config: %+v", cfg.Storage)
|
||||
}
|
||||
if cfg.VectorDB.Path != "/var/lib/agent-desk/vectors.db" {
|
||||
t.Fatalf("VectorDB.Path=%q", cfg.VectorDB.Path)
|
||||
}
|
||||
if len(cfg.WxWork.Notify.ToUsers) != 3 || cfg.WxWork.Notify.DuplicateCheckInterval != 900 {
|
||||
t.Fatalf("unexpected WeCom notify config: %+v", cfg.WxWork.Notify)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromSettingsRejectsInvalidValues(t *testing.T) {
|
||||
if _, err := FromSettings(map[string]string{
|
||||
"ai_agent_wxwork_notify_dedupe_interval": "not-a-number",
|
||||
}); err == nil {
|
||||
t.Fatal("FromSettings() error = nil, want invalid setting error")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,17 +6,10 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func TestVectorDBConfigUnmarshalNestedProviders(t *testing.T) {
|
||||
func TestVectorDBConfigUnmarshalLibSQLPath(t *testing.T) {
|
||||
raw := []byte(`
|
||||
vectorDB:
|
||||
type: lancedb
|
||||
qdrant:
|
||||
host: 127.0.0.1
|
||||
grpcPort: 6334
|
||||
apiKey: secret
|
||||
useTls: true
|
||||
lancedb:
|
||||
path: data/lancedb
|
||||
path: data/agent/vectors.db
|
||||
`)
|
||||
|
||||
var cfg Config
|
||||
@@ -24,22 +17,7 @@ vectorDB:
|
||||
t.Fatalf("yaml.Unmarshal() error = %v", err)
|
||||
}
|
||||
|
||||
if cfg.VectorDB.Type != "lancedb" {
|
||||
t.Fatalf("VectorDB.Type = %q, want %q", cfg.VectorDB.Type, "lancedb")
|
||||
}
|
||||
if cfg.VectorDB.Qdrant.Host != "127.0.0.1" {
|
||||
t.Fatalf("VectorDB.Qdrant.Host = %q, want %q", cfg.VectorDB.Qdrant.Host, "127.0.0.1")
|
||||
}
|
||||
if cfg.VectorDB.Qdrant.GrpcPort != 6334 {
|
||||
t.Fatalf("VectorDB.Qdrant.GrpcPort = %d, want %d", cfg.VectorDB.Qdrant.GrpcPort, 6334)
|
||||
}
|
||||
if cfg.VectorDB.Qdrant.APIKey != "secret" {
|
||||
t.Fatalf("VectorDB.Qdrant.APIKey = %q, want %q", cfg.VectorDB.Qdrant.APIKey, "secret")
|
||||
}
|
||||
if !cfg.VectorDB.Qdrant.UseTLS {
|
||||
t.Fatal("VectorDB.Qdrant.UseTLS = false, want true")
|
||||
}
|
||||
if cfg.VectorDB.LanceDB.Path != "data/lancedb" {
|
||||
t.Fatalf("VectorDB.LanceDB.Path = %q, want %q", cfg.VectorDB.LanceDB.Path, "data/lancedb")
|
||||
if cfg.VectorDB.Path != "data/agent/vectors.db" {
|
||||
t.Fatalf("VectorDB.Path = %q", cfg.VectorDB.Path)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,21 +14,11 @@ type Permission struct {
|
||||
// 权限常量定义
|
||||
var (
|
||||
// 客服会话相关权限
|
||||
PermissionConversationView = Permission{Name: "查看会话", Code: "conversation.view", Type: "api", GroupName: "conversation", Method: "ANY", APIPath: "/api/dashboard/conversation/list", SortNo: 410}
|
||||
PermissionConversationAssign = Permission{Name: "分配会话", Code: "conversation.assign", Type: "api", GroupName: "conversation", Method: "POST", APIPath: "/api/dashboard/conversation/assign", SortNo: 430}
|
||||
PermissionConversationTransfer = Permission{Name: "转接会话", Code: "conversation.transfer", Type: "api", GroupName: "conversation", Method: "POST", APIPath: "/api/dashboard/conversation/transfer", SortNo: 440}
|
||||
PermissionConversationClose = Permission{Name: "关闭会话", Code: "conversation.close", Type: "api", GroupName: "conversation", Method: "POST", APIPath: "/api/dashboard/conversation/close", SortNo: 450}
|
||||
PermissionConversationSend = Permission{Name: "发送会话消息", Code: "conversation.send", Type: "api", GroupName: "conversation", Method: "POST", APIPath: "/api/dashboard/conversation/send_message", SortNo: 460}
|
||||
PermissionConversationTag = Permission{Name: "管理会话标签", Code: "conversation.tag", Type: "api", GroupName: "conversation", Method: "POST", APIPath: "/api/dashboard/conversation/add_tag", SortNo: 470}
|
||||
PermissionConversationLinkCustomer = Permission{Name: "关联会话客户", Code: "conversation.linkCustomer", Type: "api", GroupName: "conversation", Method: "POST", APIPath: "/api/dashboard/conversation/link_customer", SortNo: 495}
|
||||
|
||||
// 工单相关权限
|
||||
PermissionTicketView = Permission{Name: "查看工单", Code: "ticket.view", Type: "api", GroupName: "ticket", Method: "ANY", APIPath: "/api/dashboard/ticket/list", SortNo: 500}
|
||||
PermissionTicketCreate = Permission{Name: "创建工单", Code: "ticket.create", Type: "api", GroupName: "ticket", Method: "POST", APIPath: "/api/dashboard/ticket/create", SortNo: 510}
|
||||
PermissionTicketUpdate = Permission{Name: "更新工单", Code: "ticket.update", Type: "api", GroupName: "ticket", Method: "POST", APIPath: "/api/dashboard/ticket/update", SortNo: 520}
|
||||
PermissionTicketAssign = Permission{Name: "指派工单", Code: "ticket.assign", Type: "api", GroupName: "ticket", Method: "POST", APIPath: "/api/dashboard/ticket/assign", SortNo: 530}
|
||||
PermissionTicketChangeStatus = Permission{Name: "变更工单状态", Code: "ticket.changeStatus", Type: "api", GroupName: "ticket", Method: "POST", APIPath: "/api/dashboard/ticket/change_status", SortNo: 540}
|
||||
PermissionTicketProgress = Permission{Name: "更新工单进展", Code: "ticket.progress", Type: "api", GroupName: "ticket", Method: "POST", APIPath: "/api/dashboard/ticket/progress/create", SortNo: 550}
|
||||
PermissionConversationView = Permission{Name: "查看会话", Code: "conversation.view", Type: "api", GroupName: "conversation", Method: "ANY", APIPath: "/api/dashboard/conversation/list", SortNo: 410}
|
||||
PermissionConversationAssign = Permission{Name: "分配会话", Code: "conversation.assign", Type: "api", GroupName: "conversation", Method: "POST", APIPath: "/api/dashboard/conversation/assign", SortNo: 430}
|
||||
PermissionConversationTransfer = Permission{Name: "转接会话", Code: "conversation.transfer", Type: "api", GroupName: "conversation", Method: "POST", APIPath: "/api/dashboard/conversation/transfer", SortNo: 440}
|
||||
PermissionConversationClose = Permission{Name: "关闭会话", Code: "conversation.close", Type: "api", GroupName: "conversation", Method: "POST", APIPath: "/api/dashboard/conversation/close", SortNo: 450}
|
||||
PermissionConversationSend = Permission{Name: "发送会话消息", Code: "conversation.send", Type: "api", GroupName: "conversation", Method: "POST", APIPath: "/api/dashboard/conversation/send_message", SortNo: 460}
|
||||
|
||||
// 通知相关权限
|
||||
PermissionNotificationView = Permission{Name: "查看通知", Code: "notification.view", Type: "api", GroupName: "notification", Method: "ANY", APIPath: "/api/dashboard/notification/list", SortNo: 680}
|
||||
@@ -40,18 +30,6 @@ var (
|
||||
PermissionQuickReplyUpdate = Permission{Name: "更新快捷回复", Code: "quickReply.update", Type: "api", GroupName: "quickReply", Method: "POST", APIPath: "/api/dashboard/quick-reply/update", SortNo: 630}
|
||||
PermissionQuickReplyDelete = Permission{Name: "删除快捷回复", Code: "quickReply.delete", Type: "api", GroupName: "quickReply", Method: "POST", APIPath: "/api/dashboard/quick-reply/delete", SortNo: 640}
|
||||
|
||||
// 标签相关权限
|
||||
PermissionTagView = Permission{Name: "查看标签", Code: "tag.view", Type: "api", GroupName: "tag", Method: "ANY", APIPath: "/api/dashboard/tag/list", SortNo: 550}
|
||||
PermissionTagCreate = Permission{Name: "创建标签", Code: "tag.create", Type: "api", GroupName: "tag", Method: "POST", APIPath: "/api/dashboard/tag/create", SortNo: 560}
|
||||
PermissionTagUpdate = Permission{Name: "更新标签", Code: "tag.update", Type: "api", GroupName: "tag", Method: "POST", APIPath: "/api/dashboard/tag/update", SortNo: 570}
|
||||
PermissionTagDelete = Permission{Name: "删除标签", Code: "tag.delete", Type: "api", GroupName: "tag", Method: "POST", APIPath: "/api/dashboard/tag/delete", SortNo: 580}
|
||||
|
||||
// 公司相关权限
|
||||
PermissionCompanyView = Permission{Name: "查看公司", Code: "company.view", Type: "api", GroupName: "company", Method: "ANY", APIPath: "/api/dashboard/company/list", SortNo: 590}
|
||||
PermissionCompanyCreate = Permission{Name: "创建公司", Code: "company.create", Type: "api", GroupName: "company", Method: "POST", APIPath: "/api/dashboard/company/create", SortNo: 600}
|
||||
PermissionCompanyUpdate = Permission{Name: "更新公司", Code: "company.update", Type: "api", GroupName: "company", Method: "POST", APIPath: "/api/dashboard/company/update", SortNo: 610}
|
||||
PermissionCompanyDelete = Permission{Name: "删除公司", Code: "company.delete", Type: "api", GroupName: "company", Method: "POST", APIPath: "/api/dashboard/company/delete", SortNo: 620}
|
||||
|
||||
// 接入渠道相关权限
|
||||
PermissionChannelView = Permission{Name: "查看接入渠道", Code: "channel.view", Type: "api", GroupName: "channel", Method: "ANY", APIPath: "/api/dashboard/channel/list", SortNo: 625}
|
||||
PermissionChannelCreate = Permission{Name: "创建接入渠道", Code: "channel.create", Type: "api", GroupName: "channel", Method: "POST", APIPath: "/api/dashboard/channel/create", SortNo: 626}
|
||||
@@ -62,17 +40,11 @@ var (
|
||||
PermissionWxWorkOutboxView = Permission{Name: "查看企微 Outbox", Code: "wxworkOutbox.view", Type: "api", GroupName: "wxworkOutbox", Method: "ANY", APIPath: "/api/dashboard/channel/wxwork/outbox/failed/list", SortNo: 629}
|
||||
PermissionWxWorkOutboxUpdate = Permission{Name: "处置企微 Outbox", Code: "wxworkOutbox.update", Type: "api", GroupName: "wxworkOutbox", Method: "POST", APIPath: "/api/dashboard/channel/wxwork/outbox/retry", SortNo: 630}
|
||||
|
||||
// 客户相关权限
|
||||
PermissionCustomerView = Permission{Name: "查看客户", Code: "customer.view", Type: "api", GroupName: "customer", Method: "POST", APIPath: "/api/dashboard/customer/list", SortNo: 630}
|
||||
PermissionCustomerCreate = Permission{Name: "创建客户", Code: "customer.create", Type: "api", GroupName: "customer", Method: "POST", APIPath: "/api/dashboard/customer/create", SortNo: 640}
|
||||
PermissionCustomerUpdate = Permission{Name: "更新客户", Code: "customer.update", Type: "api", GroupName: "customer", Method: "POST", APIPath: "/api/dashboard/customer/update", SortNo: 650}
|
||||
PermissionCustomerDelete = Permission{Name: "删除客户", Code: "customer.delete", Type: "api", GroupName: "customer", Method: "POST", APIPath: "/api/dashboard/customer/delete", SortNo: 660}
|
||||
|
||||
// 客服相关权限
|
||||
PermissionAgentView = Permission{Name: "查看客服", Code: "agent.view", Type: "api", GroupName: "agent", Method: "ANY", APIPath: "/api/dashboard/agent/list", SortNo: 610}
|
||||
PermissionAgentCreate = Permission{Name: "创建客服", Code: "agent.create", Type: "api", GroupName: "agent", Method: "POST", APIPath: "/api/dashboard/agent/create", SortNo: 620}
|
||||
PermissionAgentUpdate = Permission{Name: "更新客服", Code: "agent.update", Type: "api", GroupName: "agent", Method: "POST", APIPath: "/api/dashboard/agent/update", SortNo: 630}
|
||||
PermissionAgentDelete = Permission{Name: "删除客服", Code: "agent.delete", Type: "api", GroupName: "agent", Method: "POST", APIPath: "/api/dashboard/agent/delete", SortNo: 640}
|
||||
PermissionAgentView = Permission{Name: "查看客服", Code: "agent.view", Type: "api", GroupName: "agent", Method: "ANY", APIPath: "/api/dashboard/agent/list", SortNo: 610}
|
||||
PermissionAgentCreate = Permission{Name: "创建客服", Code: "agent.create", Type: "api", GroupName: "agent", Method: "POST", APIPath: "/api/dashboard/agent/create", SortNo: 620}
|
||||
PermissionAgentUpdate = Permission{Name: "更新客服", Code: "agent.update", Type: "api", GroupName: "agent", Method: "POST", APIPath: "/api/dashboard/agent/update", SortNo: 630}
|
||||
PermissionAgentDelete = Permission{Name: "删除客服", Code: "agent.delete", Type: "api", GroupName: "agent", Method: "POST", APIPath: "/api/dashboard/agent/delete", SortNo: 640}
|
||||
|
||||
// 客服组相关权限
|
||||
PermissionAgentTeamView = Permission{Name: "查看客服组", Code: "agentTeam.view", Type: "api", GroupName: "agentTeam", Method: "ANY", APIPath: "/api/dashboard/agent-team/list", SortNo: 710}
|
||||
@@ -119,14 +91,4 @@ var (
|
||||
PermissionKnowledgeFAQCreate = Permission{Name: "创建知识FAQ", Code: "knowledgeFAQ.create", Type: "api", GroupName: "knowledgeFAQ", Method: "POST", APIPath: "/api/dashboard/knowledge-faq/create", SortNo: 1560}
|
||||
PermissionKnowledgeFAQUpdate = Permission{Name: "更新知识FAQ", Code: "knowledgeFAQ.update", Type: "api", GroupName: "knowledgeFAQ", Method: "POST", APIPath: "/api/dashboard/knowledge-faq/update", SortNo: 1570}
|
||||
PermissionKnowledgeFAQDelete = Permission{Name: "删除知识FAQ", Code: "knowledgeFAQ.delete", Type: "api", GroupName: "knowledgeFAQ", Method: "POST", APIPath: "/api/dashboard/knowledge-faq/delete", SortNo: 1580}
|
||||
|
||||
// Skill 定义相关权限
|
||||
PermissionSkillDefinitionView = Permission{Name: "查看技能定义", Code: "skillDefinition.view", Type: "api", GroupName: "skillDefinition", Method: "ANY", APIPath: "/api/dashboard/skill-definition/list", SortNo: 1610}
|
||||
PermissionSkillDefinitionCreate = Permission{Name: "创建技能定义", Code: "skillDefinition.create", Type: "api", GroupName: "skillDefinition", Method: "POST", APIPath: "/api/dashboard/skill-definition/create", SortNo: 1620}
|
||||
PermissionSkillDefinitionUpdate = Permission{Name: "更新技能定义", Code: "skillDefinition.update", Type: "api", GroupName: "skillDefinition", Method: "POST", APIPath: "/api/dashboard/skill-definition/update", SortNo: 1630}
|
||||
PermissionSkillDefinitionDelete = Permission{Name: "删除技能定义", Code: "skillDefinition.delete", Type: "api", GroupName: "skillDefinition", Method: "POST", APIPath: "/api/dashboard/skill-definition/delete", SortNo: 1640}
|
||||
|
||||
// MCP 调试相关权限
|
||||
PermissionMCPView = Permission{Name: "查看MCP调试信息", Code: "mcp.view", Type: "api", GroupName: "mcp", Method: "POST", APIPath: "/api/dashboard/mcp/list_tools", SortNo: 1710}
|
||||
PermissionMCPCall = Permission{Name: "调用MCP工具", Code: "mcp.call", Type: "api", GroupName: "mcp", Method: "POST", APIPath: "/api/dashboard/mcp/call_tool", SortNo: 1720}
|
||||
)
|
||||
|
||||
@@ -17,13 +17,13 @@ type AuthPrincipal struct {
|
||||
}
|
||||
|
||||
type WxWorkKFChannelConfig struct {
|
||||
OpenKfID string `json:"openKfId"`
|
||||
OpenKfID string `json:"open_kf_id"`
|
||||
}
|
||||
|
||||
type WebChannelConfig struct {
|
||||
Title string `json:"title"`
|
||||
Subtitle string `json:"subtitle"`
|
||||
ThemeColor string `json:"themeColor"`
|
||||
ThemeColor string `json:"theme_color"`
|
||||
Position string `json:"position"`
|
||||
Width string `json:"width"`
|
||||
}
|
||||
@@ -31,5 +31,5 @@ type WebChannelConfig struct {
|
||||
type WechatMPChannelConfig struct {
|
||||
Title string `json:"title"`
|
||||
Subtitle string `json:"subtitle"`
|
||||
ThemeColor string `json:"themeColor"`
|
||||
ThemeColor string `json:"theme_color"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestChannelConfigJSONUsesSnakeCaseFields(t *testing.T) {
|
||||
payload, err := json.Marshal(struct {
|
||||
WxWork WxWorkKFChannelConfig `json:"wxwork"`
|
||||
Web WebChannelConfig `json:"web"`
|
||||
}{
|
||||
WxWork: WxWorkKFChannelConfig{OpenKfID: "wkf"},
|
||||
Web: WebChannelConfig{ThemeColor: "#fff"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("marshal channel config: %v", err)
|
||||
}
|
||||
|
||||
text := string(payload)
|
||||
for _, field := range []string{`"open_kf_id"`, `"theme_color"`} {
|
||||
if !strings.Contains(text, field) {
|
||||
t.Fatalf("expected %s in %s", field, text)
|
||||
}
|
||||
}
|
||||
for _, field := range []string{"openKfId", "themeColor"} {
|
||||
if strings.Contains(text, field) {
|
||||
t.Fatalf("unexpected camelCase field %q in %s", field, text)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package request
|
||||
|
||||
type RunAgentEvaluationRequest struct {
|
||||
AIAgentID int64 `json:"aiAgentId"`
|
||||
AIAgentID int64 `json:"ai_agent_id"`
|
||||
Cases []AgentEvaluationCase `json:"cases"`
|
||||
}
|
||||
|
||||
|
||||
@@ -3,16 +3,16 @@ package request
|
||||
import "code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
|
||||
type CreateAgentProfileRequest struct {
|
||||
UserID int64 `json:"userId"`
|
||||
TeamID int64 `json:"teamId"`
|
||||
AgentCode string `json:"agentCode"`
|
||||
DisplayName string `json:"displayName"`
|
||||
UserID int64 `json:"user_id"`
|
||||
TeamID int64 `json:"team_id"`
|
||||
AgentCode string `json:"agent_code"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Avatar string `json:"avatar"`
|
||||
ServiceStatus enums.ServiceStatus `json:"serviceStatus"`
|
||||
MaxConcurrentCount int `json:"maxConcurrentCount"`
|
||||
PriorityLevel int `json:"priorityLevel"`
|
||||
AutoAssignEnabled bool `json:"autoAssignEnabled"`
|
||||
ReceiveOfflineMessage bool `json:"receiveOfflineMessage"`
|
||||
ServiceStatus enums.ServiceStatus `json:"service_status"`
|
||||
MaxConcurrentCount int `json:"max_concurrent_count"`
|
||||
PriorityLevel int `json:"priority_level"`
|
||||
AutoAssignEnabled bool `json:"auto_assign_enabled"`
|
||||
ReceiveOfflineMessage bool `json:"receive_offline_message"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ type DeleteAgentProfileRequest struct {
|
||||
|
||||
type CreateAgentTeamRequest struct {
|
||||
Name string `json:"name"`
|
||||
LeaderUserID int64 `json:"leaderUserId"`
|
||||
LeaderUserID int64 `json:"leader_user_id"`
|
||||
Status int `json:"status"`
|
||||
Description string `json:"description"`
|
||||
Remark string `json:"remark"`
|
||||
@@ -36,7 +36,7 @@ type CreateAgentTeamRequest struct {
|
||||
type UpdateAgentTeamRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
LeaderUserID int64 `json:"leaderUserId"`
|
||||
LeaderUserID int64 `json:"leader_user_id"`
|
||||
Status int `json:"status"`
|
||||
Description string `json:"description"`
|
||||
Remark string `json:"remark"`
|
||||
@@ -47,9 +47,9 @@ type DeleteAgentTeamRequest struct {
|
||||
}
|
||||
|
||||
type CreateAgentTeamScheduleRequest struct {
|
||||
TeamID int64 `json:"teamId"`
|
||||
StartAt string `json:"startAt"`
|
||||
EndAt string `json:"endAt"`
|
||||
TeamID int64 `json:"team_id"`
|
||||
StartAt string `json:"start_at"`
|
||||
EndAt string `json:"end_at"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
@@ -63,18 +63,18 @@ type DeleteAgentTeamScheduleRequest struct {
|
||||
}
|
||||
|
||||
type AgentTeamScheduleCalendarRequest struct {
|
||||
StartAt string `json:"startAt"`
|
||||
EndAt string `json:"endAt"`
|
||||
TeamID int64 `json:"teamId"`
|
||||
StartAt string `json:"start_at"`
|
||||
EndAt string `json:"end_at"`
|
||||
TeamID int64 `json:"team_id"`
|
||||
}
|
||||
|
||||
type AgentTeamScheduleBatchRequest struct {
|
||||
TeamIDs []int64 `json:"teamIds"`
|
||||
StartDate string `json:"startDate"`
|
||||
EndDate string `json:"endDate"`
|
||||
TeamIDs []int64 `json:"team_ids"`
|
||||
StartDate string `json:"start_date"`
|
||||
EndDate string `json:"end_date"`
|
||||
Weekdays []int `json:"weekdays"`
|
||||
StartTime string `json:"startTime"`
|
||||
EndTime string `json:"endTime"`
|
||||
StartTime string `json:"start_time"`
|
||||
EndTime string `json:"end_time"`
|
||||
Remark string `json:"remark"`
|
||||
Locale string `json:"-"`
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package request
|
||||
import "code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
|
||||
type SaveAgentRunQualityFeedbackRequest struct {
|
||||
AgentRunID int64 `json:"agentRunId"`
|
||||
ResolutionStatus enums.AgentRunResolutionStatus `json:"resolutionStatus"`
|
||||
EvidenceStatus enums.AgentRunEvidenceStatus `json:"evidenceStatus"`
|
||||
AgentRunID int64 `json:"agent_run_id"`
|
||||
ResolutionStatus enums.AgentRunResolutionStatus `json:"resolution_status"`
|
||||
EvidenceStatus enums.AgentRunEvidenceStatus `json:"evidence_status"`
|
||||
Comment string `json:"comment"`
|
||||
}
|
||||
|
||||
@@ -2,39 +2,20 @@ package request
|
||||
|
||||
import "code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
|
||||
type AIAgentMCPToolRequest struct {
|
||||
ToolCode string `json:"toolCode"`
|
||||
ServerCode string `json:"serverCode"`
|
||||
ToolName string `json:"toolName"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
RiskLevel string `json:"riskLevel"`
|
||||
RequireConfirmation bool `json:"requireConfirmation"`
|
||||
Arguments map[string]string `json:"arguments"`
|
||||
}
|
||||
|
||||
type AIAgentWorkflowBindingRequest struct {
|
||||
WorkflowVersionID int64 `json:"workflowVersionId"`
|
||||
ToolName string `json:"toolName"`
|
||||
TriggerInstruction string `json:"triggerInstruction"`
|
||||
Priority int `json:"priority"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type CreateAIConfigRequest struct {
|
||||
Name string `json:"name"`
|
||||
Provider enums.AIProvider `json:"provider"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
APIKey string `json:"apiKey"`
|
||||
ModelType enums.AIModelType `json:"modelType"`
|
||||
ModelName string `json:"modelName"`
|
||||
BaseURL string `json:"base_url"`
|
||||
APIKey string `json:"api_key"`
|
||||
ModelType enums.AIModelType `json:"model_type"`
|
||||
ModelName string `json:"model_name"`
|
||||
Dimension int `json:"dimension"`
|
||||
MaxContextTokens int `json:"maxContextTokens"`
|
||||
MaxOutputTokens int `json:"maxOutputTokens"`
|
||||
TimeoutMS int `json:"timeoutMs"`
|
||||
MaxRetryCount int `json:"maxRetryCount"`
|
||||
RPMLimit int `json:"rpmLimit"`
|
||||
TPMLimit int `json:"tpmLimit"`
|
||||
MaxContextTokens int `json:"max_context_tokens"`
|
||||
MaxOutputTokens int `json:"max_output_tokens"`
|
||||
TimeoutMS int `json:"timeout_ms"`
|
||||
MaxRetryCount int `json:"max_retry_count"`
|
||||
RPMLimit int `json:"rpm_limit"`
|
||||
TPMLimit int `json:"tpm_limit"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
@@ -54,25 +35,23 @@ type UpdateAIConfigStatusRequest struct {
|
||||
|
||||
type CreateAIAgentRequest struct {
|
||||
Name string `json:"name"`
|
||||
Avatar string `json:"avatar"`
|
||||
Description string `json:"description"`
|
||||
AIConfigID int64 `json:"aiConfigId"`
|
||||
MaxSteps int `json:"maxSteps"`
|
||||
ContextWindow int `json:"contextWindow"`
|
||||
ToolPolicy string `json:"toolPolicy"`
|
||||
KnowledgePolicy string `json:"knowledgePolicy"`
|
||||
ServiceMode enums.IMConversationServiceMode `json:"serviceMode"`
|
||||
SystemPrompt string `json:"systemPrompt"`
|
||||
WelcomeMessage string `json:"welcomeMessage"`
|
||||
ReplyTimeoutSeconds int `json:"replyTimeoutSeconds"`
|
||||
RolloutPercent int `json:"rolloutPercent"`
|
||||
TeamIDs []int64 `json:"teamIds"`
|
||||
HandoffMode enums.AIAgentHandoffMode `json:"handoffMode"`
|
||||
FallbackMode enums.AIAgentFallbackMode `json:"fallbackMode"`
|
||||
FallbackMessage string `json:"fallbackMessage"`
|
||||
KnowledgeBaseIDs []int64 `json:"knowledgeBaseIds"`
|
||||
SkillIDs []int64 `json:"skillIds"`
|
||||
MCPTools []AIAgentMCPToolRequest `json:"mcpTools"`
|
||||
WorkflowBindings []AIAgentWorkflowBindingRequest `json:"workflowBindings"`
|
||||
AIConfigID int64 `json:"ai_config_id"`
|
||||
MaxSteps int `json:"max_steps"`
|
||||
ContextWindow int `json:"context_window"`
|
||||
ToolPolicy string `json:"tool_policy"`
|
||||
KnowledgePolicy string `json:"knowledge_policy"`
|
||||
ServiceMode enums.IMConversationServiceMode `json:"service_mode"`
|
||||
SystemPrompt string `json:"system_prompt"`
|
||||
WelcomeMessage string `json:"welcome_message"`
|
||||
ReplyTimeoutSeconds int `json:"reply_timeout_seconds"`
|
||||
RolloutPercent int `json:"rollout_percent"`
|
||||
TeamIDs []int64 `json:"team_ids"`
|
||||
HandoffMode enums.AIAgentHandoffMode `json:"handoff_mode"`
|
||||
FallbackMode enums.AIAgentFallbackMode `json:"fallback_mode"`
|
||||
FallbackMessage string `json:"fallback_message"`
|
||||
KnowledgeBaseIDs []int64 `json:"knowledge_base_ids"`
|
||||
}
|
||||
|
||||
type UpdateAIAgentRequest struct {
|
||||
@@ -90,7 +69,7 @@ type PublishAIAgentRequest struct {
|
||||
|
||||
type RollbackAIAgentRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
RevisionID int64 `json:"revisionId"`
|
||||
RevisionID int64 `json:"revision_id"`
|
||||
}
|
||||
|
||||
type RollbackAIAgentRolloutRequest struct {
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
package request
|
||||
|
||||
import "code.tczkiot.com/wlw/ai-agent/internal/ai/workflow/dsl"
|
||||
|
||||
type CreateAIWorkflowRequest struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Definition dsl.Definition `json:"definition"`
|
||||
}
|
||||
|
||||
type UpdateAIWorkflowRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
CreateAIWorkflowRequest
|
||||
}
|
||||
|
||||
type DeleteAIWorkflowRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
type ValidateAIWorkflowRequest struct {
|
||||
Definition dsl.Definition `json:"definition"`
|
||||
}
|
||||
|
||||
type PublishAIWorkflowRequest struct {
|
||||
WorkflowID int64 `json:"workflowId"`
|
||||
Definition dsl.Definition `json:"definition"`
|
||||
}
|
||||
|
||||
type AIWorkflowVersionListRequest struct {
|
||||
WorkflowID int64 `json:"workflowId"`
|
||||
}
|
||||
|
||||
type RestoreAIWorkflowVersionRequest struct {
|
||||
WorkflowID int64 `json:"workflowId"`
|
||||
WorkflowVersionID int64 `json:"workflowVersionId"`
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package request
|
||||
|
||||
type CreateChannelRequest struct {
|
||||
ChannelType string `json:"channelType"`
|
||||
AIAgentID int64 `json:"aiAgentId"`
|
||||
AIAgentRolloutPercent int `json:"aiAgentRolloutPercent"`
|
||||
ChannelType string `json:"channel_type"`
|
||||
AIAgentID int64 `json:"ai_agent_id"`
|
||||
AIAgentRolloutPercent int `json:"ai_agent_rollout_percent"`
|
||||
Name string `json:"name"`
|
||||
ConfigJSON string `json:"configJson"`
|
||||
ConfigJSON string `json:"config_json"`
|
||||
Status int `json:"status"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package request
|
||||
|
||||
type CreateCompanyRequest struct {
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type UpdateCompanyRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
CreateCompanyRequest
|
||||
}
|
||||
|
||||
type DeleteCompanyRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
type UpdateCompanyStatusRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
@@ -12,50 +12,39 @@ const (
|
||||
|
||||
type ConversationListRequest struct {
|
||||
Status int `json:"status"`
|
||||
ServiceMode int `json:"serviceMode"`
|
||||
CurrentAssigneeID int64 `json:"currentAssigneeId"`
|
||||
ServiceMode int `json:"service_mode"`
|
||||
CurrentAssigneeID int64 `json:"current_assignee_id"`
|
||||
Keyword string `json:"keyword"`
|
||||
TagID int64 `json:"tagId"`
|
||||
}
|
||||
|
||||
type AssignConversationRequest struct {
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
AssigneeID int64 `json:"assigneeId"`
|
||||
ConversationID int64 `json:"conversation_id"`
|
||||
AssigneeID int64 `json:"assignee_id"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
type DispatchConversationRequest struct {
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
ConversationID int64 `json:"conversation_id"`
|
||||
}
|
||||
|
||||
type TransferConversationRequest struct {
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
ToUserID int64 `json:"toUserId"`
|
||||
ConversationID int64 `json:"conversation_id"`
|
||||
ToUserID int64 `json:"to_user_id"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
type CloseConversationRequest struct {
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
CloseReason string `json:"closeReason"`
|
||||
ConversationID int64 `json:"conversation_id"`
|
||||
CloseReason string `json:"close_reason"`
|
||||
}
|
||||
|
||||
type ReadConversationRequest struct {
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
MessageID int64 `json:"messageId"`
|
||||
ConversationID int64 `json:"conversation_id"`
|
||||
MessageID int64 `json:"message_id"`
|
||||
}
|
||||
|
||||
type AddConversationTagRequest struct {
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
TagID int64 `json:"tagId"`
|
||||
}
|
||||
|
||||
type RemoveConversationTagRequest struct {
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
TagID int64 `json:"tagId"`
|
||||
}
|
||||
|
||||
// LinkConversationCustomerRequest 将客服会话关联到 CRM 客户(并同步访客身份映射)。
|
||||
type LinkConversationCustomerRequest struct {
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
CustomerID int64 `json:"customerId"`
|
||||
type ExecuteCustomerQuickActionRequest struct {
|
||||
ConversationID int64 `json:"conversation_id"`
|
||||
Code string `json:"code"`
|
||||
ClientMsgID string `json:"client_msg_id"`
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package request
|
||||
|
||||
type CreateCustomerContactRequest struct {
|
||||
CustomerID int64 `json:"customerId"`
|
||||
ContactType string `json:"contactType"`
|
||||
ContactValue string `json:"contactValue"`
|
||||
IsPrimary bool `json:"isPrimary"`
|
||||
IsVerified bool `json:"isVerified"`
|
||||
Source string `json:"source"`
|
||||
Status int `json:"status"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type UpdateCustomerContactRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
ContactType string `json:"contactType"`
|
||||
ContactValue string `json:"contactValue"`
|
||||
IsPrimary bool `json:"isPrimary"`
|
||||
IsVerified bool `json:"isVerified"`
|
||||
Source string `json:"source"`
|
||||
Status int `json:"status"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type DeleteCustomerContactRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package request
|
||||
|
||||
// CustomerListRequest 客户分页列表查询(POST /customer/list JSON Body)。
|
||||
type CustomerListRequest struct {
|
||||
Page int `json:"page"`
|
||||
Limit int `json:"limit"`
|
||||
Status *int `json:"status,omitempty"`
|
||||
Gender *int `json:"gender,omitempty"`
|
||||
CompanyID *int64 `json:"companyId,omitempty"`
|
||||
// Keyword 模糊匹配:客户姓名、主手机号、主邮箱、任意联系方式(t_customer_contact)、公司名称(t_company)。
|
||||
Keyword string `json:"keyword"`
|
||||
}
|
||||
|
||||
func (r CustomerListRequest) GetPage() int {
|
||||
if r.Page <= 0 {
|
||||
return 1
|
||||
}
|
||||
return r.Page
|
||||
}
|
||||
|
||||
func (r CustomerListRequest) GetLimit() int {
|
||||
if r.Limit <= 0 {
|
||||
return 20
|
||||
}
|
||||
return r.Limit
|
||||
}
|
||||
|
||||
func (r CustomerListRequest) Offset() int {
|
||||
return (r.GetPage() - 1) * r.GetLimit()
|
||||
}
|
||||
|
||||
type CreateCustomerRequest struct {
|
||||
Name string `json:"name"`
|
||||
Gender int `json:"gender"`
|
||||
CompanyID int64 `json:"companyId"`
|
||||
PrimaryMobile string `json:"primaryMobile"`
|
||||
PrimaryEmail string `json:"primaryEmail"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type UpdateCustomerRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
CreateCustomerRequest
|
||||
}
|
||||
|
||||
type DeleteCustomerRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
type UpdateCustomerStatusRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
// CustomerProfileContactItem 保存客户档案时的联系方式行(无 id 表示新建)。
|
||||
type CustomerProfileContactItem struct {
|
||||
ID *int64 `json:"id,omitempty"`
|
||||
ContactType string `json:"contactType"`
|
||||
ContactValue string `json:"contactValue"`
|
||||
IsPrimary bool `json:"isPrimary"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
// SaveCustomerProfileRequest 客户主信息与联系方式一并保存(单事务);id 为空或 0 表示新建客户。
|
||||
type SaveCustomerProfileRequest struct {
|
||||
ID *int64 `json:"id,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Gender int `json:"gender"`
|
||||
CompanyID int64 `json:"companyId"`
|
||||
Remark string `json:"remark"`
|
||||
Contacts []CustomerProfileContactItem `json:"contacts"`
|
||||
}
|
||||
@@ -9,15 +9,15 @@ import (
|
||||
type CreateKnowledgeBaseRequest struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
KnowledgeType string `json:"knowledgeType"`
|
||||
DefaultTopK int `json:"defaultTopK"`
|
||||
DefaultScoreThreshold float64 `json:"defaultScoreThreshold"`
|
||||
DefaultRerankLimit int `json:"defaultRerankLimit"`
|
||||
ChunkProvider string `json:"chunkProvider"`
|
||||
ChunkTargetTokens int `json:"chunkTargetTokens"`
|
||||
ChunkMaxTokens int `json:"chunkMaxTokens"`
|
||||
ChunkOverlapTokens int `json:"chunkOverlapTokens"`
|
||||
AnswerMode int `json:"answerMode"`
|
||||
KnowledgeType string `json:"knowledge_type"`
|
||||
DefaultTopK int `json:"default_top_k"`
|
||||
DefaultScoreThreshold float64 `json:"default_score_threshold"`
|
||||
DefaultRerankLimit int `json:"default_rerank_limit"`
|
||||
ChunkProvider string `json:"chunk_provider"`
|
||||
ChunkTargetTokens int `json:"chunk_target_tokens"`
|
||||
ChunkMaxTokens int `json:"chunk_max_tokens"`
|
||||
ChunkOverlapTokens int `json:"chunk_overlap_tokens"`
|
||||
AnswerMode int `json:"answer_mode"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ type UpdateKnowledgeBaseRequest struct {
|
||||
}
|
||||
|
||||
type CreateKnowledgeDirectoryRequest struct {
|
||||
KnowledgeBaseID int64 `json:"knowledgeBaseId"`
|
||||
ParentID int64 `json:"parentId"`
|
||||
KnowledgeBaseID int64 `json:"knowledge_base_id"`
|
||||
ParentID int64 `json:"parent_id"`
|
||||
Name string `json:"name"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
@@ -43,10 +43,10 @@ type DeleteKnowledgeDirectoryRequest struct {
|
||||
}
|
||||
|
||||
type CreateKnowledgeDocumentRequest struct {
|
||||
KnowledgeBaseID int64 `json:"knowledgeBaseId"`
|
||||
DirectoryID int64 `json:"directoryId"`
|
||||
KnowledgeBaseID int64 `json:"knowledge_base_id"`
|
||||
DirectoryID int64 `json:"directory_id"`
|
||||
Title string `json:"title"`
|
||||
ContentType enums.KnowledgeDocumentContentType `json:"contentType"`
|
||||
ContentType enums.KnowledgeDocumentContentType `json:"content_type"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
@@ -56,8 +56,8 @@ type UpdateKnowledgeDocumentRequest struct {
|
||||
}
|
||||
|
||||
type BatchMoveKnowledgeDocumentRequest struct {
|
||||
KnowledgeBaseID int64 `json:"knowledgeBaseId"`
|
||||
DirectoryID int64 `json:"directoryId"`
|
||||
KnowledgeBaseID int64 `json:"knowledge_base_id"`
|
||||
DirectoryID int64 `json:"directory_id"`
|
||||
IDs []int64 `json:"ids"`
|
||||
}
|
||||
|
||||
@@ -66,11 +66,11 @@ type BatchDeleteKnowledgeDocumentRequest struct {
|
||||
}
|
||||
|
||||
type CreateKnowledgeFAQRequest struct {
|
||||
KnowledgeBaseID int64 `json:"knowledgeBaseId"`
|
||||
DirectoryID int64 `json:"directoryId"`
|
||||
KnowledgeBaseID int64 `json:"knowledge_base_id"`
|
||||
DirectoryID int64 `json:"directory_id"`
|
||||
Question string `json:"question"`
|
||||
Answer string `json:"answer"`
|
||||
SimilarQuestions []string `json:"similarQuestions"`
|
||||
SimilarQuestions []string `json:"similar_questions"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
@@ -80,8 +80,8 @@ type UpdateKnowledgeFAQRequest struct {
|
||||
}
|
||||
|
||||
type BatchMoveKnowledgeFAQRequest struct {
|
||||
KnowledgeBaseID int64 `json:"knowledgeBaseId"`
|
||||
DirectoryID int64 `json:"directoryId"`
|
||||
KnowledgeBaseID int64 `json:"knowledge_base_id"`
|
||||
DirectoryID int64 `json:"directory_id"`
|
||||
IDs []int64 `json:"ids"`
|
||||
}
|
||||
|
||||
@@ -105,33 +105,33 @@ type ImportKnowledgeFAQRequest struct {
|
||||
}
|
||||
|
||||
type KnowledgeSearchRequest struct {
|
||||
KnowledgeBaseIDs []int64 `json:"knowledgeBaseIds"`
|
||||
KnowledgeBaseIDs []int64 `json:"knowledge_base_ids"`
|
||||
Question string `json:"question"`
|
||||
TopK int `json:"topK"`
|
||||
ScoreThreshold float64 `json:"scoreThreshold"`
|
||||
RerankLimit int `json:"rerankLimit"`
|
||||
TopK int `json:"top_k"`
|
||||
ScoreThreshold float64 `json:"score_threshold"`
|
||||
RerankLimit int `json:"rerank_limit"`
|
||||
Channel string `json:"channel"`
|
||||
Scene string `json:"scene"`
|
||||
SessionID string `json:"sessionId"`
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
SessionID string `json:"session_id"`
|
||||
ConversationID int64 `json:"conversation_id"`
|
||||
}
|
||||
|
||||
type KnowledgeAnswerRequest struct {
|
||||
KnowledgeBaseIDs []int64 `json:"knowledgeBaseIds"`
|
||||
KnowledgeBaseIDs []int64 `json:"knowledge_base_ids"`
|
||||
Question string `json:"question"`
|
||||
TopK int `json:"topK"`
|
||||
ScoreThreshold float64 `json:"scoreThreshold"`
|
||||
RerankLimit int `json:"rerankLimit"`
|
||||
TopK int `json:"top_k"`
|
||||
ScoreThreshold float64 `json:"score_threshold"`
|
||||
RerankLimit int `json:"rerank_limit"`
|
||||
Channel string `json:"channel"`
|
||||
Scene string `json:"scene"`
|
||||
SessionID string `json:"sessionId"`
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
AnswerMode int `json:"answerMode"`
|
||||
SessionID string `json:"session_id"`
|
||||
ConversationID int64 `json:"conversation_id"`
|
||||
AnswerMode int `json:"answer_mode"`
|
||||
}
|
||||
|
||||
type CreateKnowledgeFeedbackRequest struct {
|
||||
RetrieveLogID int64 `json:"retrieveLogId"`
|
||||
FeedbackType int `json:"feedbackType"`
|
||||
FeedbackReason string `json:"feedbackReason"`
|
||||
RetrieveLogID int64 `json:"retrieve_log_id"`
|
||||
FeedbackType int `json:"feedback_type"`
|
||||
FeedbackReason string `json:"feedback_reason"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package request
|
||||
|
||||
type MCPServerDebugRequest struct {
|
||||
ServerCode string `json:"serverCode"`
|
||||
}
|
||||
|
||||
type MCPCallToolRequest struct {
|
||||
ServerCode string `json:"serverCode"`
|
||||
ToolName string `json:"toolName"`
|
||||
Arguments map[string]any `json:"arguments"`
|
||||
}
|
||||
@@ -3,19 +3,19 @@ package request
|
||||
import "code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
|
||||
type MessageListRequest struct {
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
SenderType string `json:"senderType"`
|
||||
MessageType string `json:"messageType"`
|
||||
ConversationID int64 `json:"conversation_id"`
|
||||
SenderType string `json:"sender_type"`
|
||||
MessageType string `json:"message_type"`
|
||||
}
|
||||
|
||||
type SendConversationMessageRequest struct {
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
MessageType enums.IMMessageType `json:"messageType"`
|
||||
ConversationID int64 `json:"conversation_id"`
|
||||
MessageType enums.IMMessageType `json:"message_type"`
|
||||
Content string `json:"content"`
|
||||
Payload string `json:"payload"`
|
||||
ClientMsgID string `json:"clientMsgId"`
|
||||
ClientMsgID string `json:"client_msg_id"`
|
||||
}
|
||||
|
||||
type RecallConversationMessageRequest struct {
|
||||
MessageID int64 `json:"messageId"`
|
||||
MessageID int64 `json:"message_id"`
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package request
|
||||
|
||||
type CreateNotificationRequest struct {
|
||||
RecipientUserID int64 `json:"recipientUserId"`
|
||||
RecipientUserID int64 `json:"recipient_user_id"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
NotificationType string `json:"notificationType"`
|
||||
BizType string `json:"bizType"`
|
||||
BizID int64 `json:"bizId"`
|
||||
ActionURL string `json:"actionUrl"`
|
||||
NotificationType string `json:"notification_type"`
|
||||
BizType string `json:"biz_type"`
|
||||
BizID int64 `json:"biz_id"`
|
||||
ActionURL string `json:"action_url"`
|
||||
}
|
||||
|
||||
type MarkNotificationReadRequest struct {
|
||||
|
||||
@@ -3,17 +3,17 @@ package request
|
||||
import "code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
|
||||
type QuickReplyListRequest struct {
|
||||
GroupName string `json:"groupName"`
|
||||
GroupName string `json:"group_name"`
|
||||
Keyword string `json:"keyword"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
type CreateQuickReplyRequest struct {
|
||||
GroupName string `json:"groupName"`
|
||||
GroupName string `json:"group_name"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Status enums.Status `json:"status"`
|
||||
SortNo int `json:"sortNo"`
|
||||
SortNo int `json:"sort_no"`
|
||||
}
|
||||
|
||||
type UpdateQuickReplyRequest struct {
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package request
|
||||
|
||||
type SkillDefinitionListRequest struct {
|
||||
Name string `json:"name"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
type CreateSkillDefinitionRequest struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Instruction string `json:"instruction"`
|
||||
Examples []string `json:"examples"`
|
||||
ToolWhitelist []string `json:"toolWhitelist"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type UpdateSkillDefinitionRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
CreateSkillDefinitionRequest
|
||||
}
|
||||
|
||||
type DeleteSkillDefinitionRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
type RestoreSkillDefinitionRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
type UpdateSkillDefinitionStatusRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
type SkillDebugRunRequest struct {
|
||||
AIAgentID int64 `json:"aiAgentId"`
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
SkillDefinitionID int64 `json:"skillDefinitionId"`
|
||||
UserMessage string `json:"userMessage"`
|
||||
}
|
||||
|
||||
type SkillDebugResumeRequest struct {
|
||||
AIAgentID int64 `json:"aiAgentId"`
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
CheckPointID string `json:"checkPointId"`
|
||||
UserMessage string `json:"userMessage"`
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func TestRequestStructTagsUseSnakeCase(t *testing.T) {
|
||||
fset := token.NewFileSet()
|
||||
packages, err := parser.ParseDir(fset, ".", nil, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("parse request package: %v", err)
|
||||
}
|
||||
|
||||
for _, pkg := range packages {
|
||||
for filename, file := range pkg.Files {
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
field, ok := node.(*ast.Field)
|
||||
if !ok || field.Tag == nil {
|
||||
return true
|
||||
}
|
||||
rawTag, err := strconv.Unquote(field.Tag.Value)
|
||||
if err != nil {
|
||||
t.Errorf("%s: invalid struct tag %s: %v", filename, field.Tag.Value, err)
|
||||
return true
|
||||
}
|
||||
for _, key := range []string{"json", "form", "query", "uri"} {
|
||||
name := strings.Split(reflect.StructTag(rawTag).Get(key), ",")[0]
|
||||
if name == "" || name == "-" {
|
||||
continue
|
||||
}
|
||||
if !isSnakeCaseRequestName(name) {
|
||||
t.Errorf("%s: %s tag %q must use snake_case", filename, key, name)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isSnakeCaseRequestName(name string) bool {
|
||||
for _, r := range name {
|
||||
if unicode.IsLower(r) || unicode.IsDigit(r) || r == '_' {
|
||||
continue
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package request
|
||||
|
||||
type TagListRequest struct {
|
||||
ParentID int64 `json:"parentId"`
|
||||
Name string `json:"name"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
type CreateTagRequest struct {
|
||||
ParentID int64 `json:"parentId"`
|
||||
Name string `json:"name"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type UpdateTagRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
CreateTagRequest
|
||||
}
|
||||
|
||||
type DeleteTagRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
type UpdateTagStatusRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package request
|
||||
|
||||
type CreateTicketRequest struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Source string `json:"source"`
|
||||
Channel string `json:"channel"`
|
||||
CustomerID int64 `json:"customerId"`
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
TagIDs []int64 `json:"tagIds"`
|
||||
CurrentAssigneeID int64 `json:"currentAssigneeId"`
|
||||
}
|
||||
|
||||
type CreateTicketFromConversationRequest struct {
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
TagIDs []int64 `json:"tagIds"`
|
||||
CurrentAssigneeID int64 `json:"currentAssigneeId"`
|
||||
}
|
||||
|
||||
type UpdateTicketRequest struct {
|
||||
TicketID int64 `json:"ticketId"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
TagIDs []int64 `json:"tagIds"`
|
||||
CurrentAssigneeID int64 `json:"currentAssigneeId"`
|
||||
}
|
||||
|
||||
type LinkTicketCustomerRequest struct {
|
||||
TicketID int64 `json:"ticketId"`
|
||||
CustomerID int64 `json:"customerId"`
|
||||
}
|
||||
|
||||
type AssignTicketRequest struct {
|
||||
TicketID int64 `json:"ticketId"`
|
||||
ToUserID int64 `json:"toUserId"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
type ChangeTicketStatusRequest struct {
|
||||
TicketID int64 `json:"ticketId"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type CreateTicketProgressRequest struct {
|
||||
TicketID int64 `json:"ticketId"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type SaveTicketViewRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Filters map[string]any `json:"filters"`
|
||||
}
|
||||
|
||||
type DeleteTicketViewRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package response
|
||||
|
||||
type AgentEvaluationResultResponse struct {
|
||||
CaseID string `json:"caseId"`
|
||||
CaseID string `json:"case_id"`
|
||||
Category string `json:"category"`
|
||||
Passed bool `json:"passed"`
|
||||
ReplyText string `json:"replyText"`
|
||||
ReplyText string `json:"reply_text"`
|
||||
Interrupted bool `json:"interrupted"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Finding string `json:"finding,omitempty"`
|
||||
|
||||
@@ -11,30 +11,30 @@ type AgentUserOptionResponse struct {
|
||||
|
||||
type AgentProfileResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
UserID int64 `json:"userId"`
|
||||
TeamID int64 `json:"teamId"`
|
||||
TeamName string `json:"teamName,omitempty"`
|
||||
UserID int64 `json:"user_id"`
|
||||
TeamID int64 `json:"team_id"`
|
||||
TeamName string `json:"team_name,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Nickname string `json:"nickname,omitempty"`
|
||||
AgentCode string `json:"agentCode"`
|
||||
DisplayName string `json:"displayName"`
|
||||
AgentCode string `json:"agent_code"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Avatar string `json:"avatar"`
|
||||
ServiceStatus enums.ServiceStatus `json:"serviceStatus"`
|
||||
MaxConcurrentCount int `json:"maxConcurrentCount"`
|
||||
PriorityLevel int `json:"priorityLevel"`
|
||||
AutoAssignEnabled bool `json:"autoAssignEnabled"`
|
||||
ReceiveOfflineMessage bool `json:"receiveOfflineMessage"`
|
||||
LastOnlineAt string `json:"lastOnlineAt,omitempty"`
|
||||
LastStatusAt string `json:"lastStatusAt,omitempty"`
|
||||
ServiceStatus enums.ServiceStatus `json:"service_status"`
|
||||
MaxConcurrentCount int `json:"max_concurrent_count"`
|
||||
PriorityLevel int `json:"priority_level"`
|
||||
AutoAssignEnabled bool `json:"auto_assign_enabled"`
|
||||
ReceiveOfflineMessage bool `json:"receive_offline_message"`
|
||||
LastOnlineAt string `json:"last_online_at,omitempty"`
|
||||
LastStatusAt string `json:"last_status_at,omitempty"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type AgentTeamResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
LeaderUserID int64 `json:"leaderUserId"`
|
||||
LeaderUsername string `json:"leaderUsername,omitempty"`
|
||||
LeaderNickname string `json:"leaderNickname,omitempty"`
|
||||
LeaderUserID int64 `json:"leader_user_id"`
|
||||
LeaderUsername string `json:"leader_username,omitempty"`
|
||||
LeaderNickname string `json:"leader_nickname,omitempty"`
|
||||
Status enums.Status `json:"status"`
|
||||
Description string `json:"description"`
|
||||
Remark string `json:"remark"`
|
||||
@@ -42,10 +42,10 @@ type AgentTeamResponse struct {
|
||||
|
||||
type AgentTeamScheduleResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
TeamID int64 `json:"teamId"`
|
||||
TeamName string `json:"teamName,omitempty"`
|
||||
StartAt string `json:"startAt"`
|
||||
EndAt string `json:"endAt"`
|
||||
TeamID int64 `json:"team_id"`
|
||||
TeamName string `json:"team_name,omitempty"`
|
||||
StartAt string `json:"start_at"`
|
||||
EndAt string `json:"end_at"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
@@ -56,15 +56,15 @@ type AgentTeamScheduleBatchPreviewResponse struct {
|
||||
}
|
||||
|
||||
type AgentTeamScheduleBatchPreviewItem struct {
|
||||
TeamID int64 `json:"teamId"`
|
||||
TeamName string `json:"teamName"`
|
||||
TeamID int64 `json:"team_id"`
|
||||
TeamName string `json:"team_name"`
|
||||
Date string `json:"date"`
|
||||
Weekday int `json:"weekday"`
|
||||
StartAt string `json:"startAt"`
|
||||
EndAt string `json:"endAt"`
|
||||
StartAt string `json:"start_at"`
|
||||
EndAt string `json:"end_at"`
|
||||
Remark string `json:"remark"`
|
||||
Conflict bool `json:"conflict"`
|
||||
ConflictReason string `json:"conflictReason"`
|
||||
ConflictReason string `json:"conflict_reason"`
|
||||
}
|
||||
|
||||
type AgentTeamScheduleBatchGenerateResponse struct {
|
||||
|
||||
@@ -21,7 +21,7 @@ func TestAgentTeamScheduleResponseOmitsSourceType(t *testing.T) {
|
||||
if err := json.Unmarshal(payload, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal response error = %v", err)
|
||||
}
|
||||
if _, ok := decoded["sourceType"]; ok {
|
||||
t.Fatalf("sourceType should not be exposed: %s", payload)
|
||||
if _, ok := decoded["source_type"]; ok {
|
||||
t.Fatalf("source_type should not be exposed: %s", payload)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,62 +4,60 @@ import "code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
|
||||
type AgentRunResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
AIAgentID int64 `json:"aiAgentId"`
|
||||
AgentRevisionID int64 `json:"agentRevisionId"`
|
||||
SourceMessageID int64 `json:"sourceMessageId"`
|
||||
WorkflowRunID int64 `json:"workflowRunId"`
|
||||
ConversationID int64 `json:"conversation_id"`
|
||||
AIAgentID int64 `json:"ai_agent_id"`
|
||||
AgentRevisionID int64 `json:"agent_revision_id"`
|
||||
SourceMessageID int64 `json:"source_message_id"`
|
||||
Status string `json:"status"`
|
||||
PromptTokens int `json:"promptTokens"`
|
||||
CompletionTokens int `json:"completionTokens"`
|
||||
StartedAt string `json:"startedAt"`
|
||||
EndedAt string `json:"endedAt"`
|
||||
DurationMS int64 `json:"durationMs"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
TraceData string `json:"traceData"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
CompletionTokens int `json:"completion_tokens"`
|
||||
StartedAt string `json:"started_at"`
|
||||
EndedAt string `json:"ended_at"`
|
||||
DurationMS int64 `json:"duration_ms"`
|
||||
ErrorMessage string `json:"error_message"`
|
||||
TraceData string `json:"trace_data"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
Steps []AgentStepResponse `json:"steps,omitempty"`
|
||||
ToolCalls []AgentToolCallResponse `json:"toolCalls,omitempty"`
|
||||
QualityFeedback *AgentRunQualityFeedbackResponse `json:"qualityFeedback,omitempty"`
|
||||
ToolCalls []AgentToolCallResponse `json:"tool_calls,omitempty"`
|
||||
QualityFeedback *AgentRunQualityFeedbackResponse `json:"quality_feedback,omitempty"`
|
||||
}
|
||||
|
||||
type AgentRunQualityFeedbackResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
AgentRunID int64 `json:"agentRunId"`
|
||||
ResolutionStatus enums.AgentRunResolutionStatus `json:"resolutionStatus"`
|
||||
EvidenceStatus enums.AgentRunEvidenceStatus `json:"evidenceStatus"`
|
||||
AgentRunID int64 `json:"agent_run_id"`
|
||||
ResolutionStatus enums.AgentRunResolutionStatus `json:"resolution_status"`
|
||||
EvidenceStatus enums.AgentRunEvidenceStatus `json:"evidence_status"`
|
||||
Comment string `json:"comment"`
|
||||
UpdateUserName string `json:"updateUserName"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
UpdateUserName string `json:"update_user_name"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
type AgentStepResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
AgentRunID int64 `json:"agentRunId"`
|
||||
WorkflowRunID int64 `json:"workflowRunId"`
|
||||
StepType string `json:"stepType"`
|
||||
StepCode string `json:"stepCode"`
|
||||
AgentRunID int64 `json:"agent_run_id"`
|
||||
StepType string `json:"step_type"`
|
||||
StepCode string `json:"step_code"`
|
||||
Status string `json:"status"`
|
||||
InputPreview string `json:"inputPreview"`
|
||||
OutputPreview string `json:"outputPreview"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
StartedAt string `json:"startedAt"`
|
||||
EndedAt string `json:"endedAt"`
|
||||
DurationMS int `json:"durationMs"`
|
||||
InputPreview string `json:"input_preview"`
|
||||
OutputPreview string `json:"output_preview"`
|
||||
ErrorMessage string `json:"error_message"`
|
||||
StartedAt string `json:"started_at"`
|
||||
EndedAt string `json:"ended_at"`
|
||||
DurationMS int `json:"duration_ms"`
|
||||
}
|
||||
|
||||
type AgentToolCallResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
AgentRunID int64 `json:"agentRunId"`
|
||||
AgentStepID int64 `json:"agentStepId"`
|
||||
ToolCode string `json:"toolCode"`
|
||||
RiskLevel string `json:"riskLevel"`
|
||||
RequireConfirm bool `json:"requireConfirm"`
|
||||
AgentRunID int64 `json:"agent_run_id"`
|
||||
AgentStepID int64 `json:"agent_step_id"`
|
||||
ToolCode string `json:"tool_code"`
|
||||
RiskLevel string `json:"risk_level"`
|
||||
RequireConfirm bool `json:"require_confirm"`
|
||||
Status string `json:"status"`
|
||||
ArgumentsPreview string `json:"argumentsPreview"`
|
||||
ResultPreview string `json:"resultPreview"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
DurationMS int `json:"durationMs"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
ArgumentsPreview string `json:"arguments_preview"`
|
||||
ResultPreview string `json:"result_preview"`
|
||||
ErrorMessage string `json:"error_message"`
|
||||
DurationMS int `json:"duration_ms"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
@@ -10,65 +10,46 @@ type AIAgentTeamResponse struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type AIAgentSkillResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type AIAgentMCPToolResponse struct {
|
||||
ToolCode string `json:"toolCode"`
|
||||
ServerCode string `json:"serverCode"`
|
||||
ToolName string `json:"toolName"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
RiskLevel string `json:"riskLevel"`
|
||||
RequireConfirmation bool `json:"requireConfirmation"`
|
||||
Arguments map[string]string `json:"arguments"`
|
||||
}
|
||||
|
||||
type AIAgentWorkflowBindingResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
WorkflowID int64 `json:"workflowId"`
|
||||
WorkflowVersionID int64 `json:"workflowVersionId"`
|
||||
WorkflowName string `json:"workflowName"`
|
||||
WorkflowVersion int `json:"workflowVersion"`
|
||||
ToolName string `json:"toolName"`
|
||||
TriggerInstruction string `json:"triggerInstruction"`
|
||||
Priority int `json:"priority"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type AgentRevisionResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
AgentID int64 `json:"agentId"`
|
||||
AgentID int64 `json:"agent_id"`
|
||||
Revision int `json:"revision"`
|
||||
Status enums.Status `json:"status"`
|
||||
DefinitionHash string `json:"definitionHash"`
|
||||
PublishedAt string `json:"publishedAt"`
|
||||
PublishedByID int64 `json:"publishedById"`
|
||||
PublishedByName string `json:"publishedByName"`
|
||||
DefinitionHash string `json:"definition_hash"`
|
||||
PublishedAt string `json:"published_at"`
|
||||
PublishedByID int64 `json:"published_by_id"`
|
||||
PublishedByName string `json:"published_by_name"`
|
||||
}
|
||||
|
||||
type AIConfigResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Provider enums.AIProvider `json:"provider"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
HasAPIKey bool `json:"hasApiKey"`
|
||||
ModelType enums.AIModelType `json:"modelType"`
|
||||
ModelName string `json:"modelName"`
|
||||
BaseURL string `json:"base_url"`
|
||||
APIKey string `json:"api_key,omitempty"`
|
||||
HasAPIKey bool `json:"has_api_key"`
|
||||
ModelType enums.AIModelType `json:"model_type"`
|
||||
ModelName string `json:"model_name"`
|
||||
Dimension int `json:"dimension"`
|
||||
MaxContextTokens int `json:"maxContextTokens"`
|
||||
MaxOutputTokens int `json:"maxOutputTokens"`
|
||||
TimeoutMS int `json:"timeoutMs"`
|
||||
MaxRetryCount int `json:"maxRetryCount"`
|
||||
RPMLimit int `json:"rpmLimit"`
|
||||
TPMLimit int `json:"tpmLimit"`
|
||||
MaxContextTokens int `json:"max_context_tokens"`
|
||||
MaxOutputTokens int `json:"max_output_tokens"`
|
||||
TimeoutMS int `json:"timeout_ms"`
|
||||
MaxRetryCount int `json:"max_retry_count"`
|
||||
RPMLimit int `json:"rpm_limit"`
|
||||
TPMLimit int `json:"tpm_limit"`
|
||||
Status enums.Status `json:"status"`
|
||||
SortNo int `json:"sortNo"`
|
||||
SortNo int `json:"sort_no"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
func BuildAIConfigDetailResponse(item *models.AIConfig) AIConfigResponse {
|
||||
result := BuildAIConfigResponse(item)
|
||||
if item != nil {
|
||||
result.APIKey = item.APIKey
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func BuildAIConfigResponse(item *models.AIConfig) AIConfigResponse {
|
||||
return AIConfigResponse{
|
||||
ID: item.ID,
|
||||
@@ -92,39 +73,36 @@ func BuildAIConfigResponse(item *models.AIConfig) AIConfigResponse {
|
||||
}
|
||||
|
||||
type AIAgentResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Status enums.Status `json:"status"`
|
||||
StatusName string `json:"statusName"`
|
||||
AIConfigID int64 `json:"aiConfigId"`
|
||||
AIConfigName string `json:"aiConfigName"`
|
||||
MaxSteps int `json:"maxSteps"`
|
||||
ContextWindow int `json:"contextWindow"`
|
||||
ToolPolicy string `json:"toolPolicy"`
|
||||
KnowledgePolicy string `json:"knowledgePolicy"`
|
||||
ServiceMode enums.IMConversationServiceMode `json:"serviceMode"`
|
||||
ServiceModeName string `json:"serviceModeName"`
|
||||
SystemPrompt string `json:"systemPrompt"`
|
||||
WelcomeMessage string `json:"welcomeMessage"`
|
||||
ReplyTimeoutSeconds int `json:"replyTimeoutSeconds"`
|
||||
RolloutPercent int `json:"rolloutPercent"`
|
||||
PreviousRolloutPercent int `json:"previousRolloutPercent"`
|
||||
Teams []AIAgentTeamResponse `json:"teams"`
|
||||
HandoffMode enums.AIAgentHandoffMode `json:"handoffMode"`
|
||||
HandoffModeName string `json:"handoffModeName"`
|
||||
FallbackMode enums.AIAgentFallbackMode `json:"fallbackMode"`
|
||||
FallbackModeName string `json:"fallbackModeName"`
|
||||
FallbackMessage string `json:"fallbackMessage"`
|
||||
KnowledgeBaseIDs []int64 `json:"knowledgeBaseIds"`
|
||||
SkillIDs []int64 `json:"skillIds"`
|
||||
Skills []AIAgentSkillResponse `json:"skills"`
|
||||
MCPTools []AIAgentMCPToolResponse `json:"mcpTools"`
|
||||
WorkflowBindings []AIAgentWorkflowBindingResponse `json:"workflowBindings"`
|
||||
PublishedRevisionID int64 `json:"publishedRevisionId"`
|
||||
SortNo int `json:"sortNo"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
CreateUserName string `json:"createUserName"`
|
||||
UpdateUserName string `json:"updateUserName"`
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Avatar string `json:"avatar"`
|
||||
Description string `json:"description"`
|
||||
Status enums.Status `json:"status"`
|
||||
StatusName string `json:"status_name"`
|
||||
AIConfigID int64 `json:"ai_config_id"`
|
||||
AIConfigName string `json:"ai_config_name"`
|
||||
MaxSteps int `json:"max_steps"`
|
||||
ContextWindow int `json:"context_window"`
|
||||
ToolPolicy string `json:"tool_policy"`
|
||||
KnowledgePolicy string `json:"knowledge_policy"`
|
||||
ServiceMode enums.IMConversationServiceMode `json:"service_mode"`
|
||||
ServiceModeName string `json:"service_mode_name"`
|
||||
SystemPrompt string `json:"system_prompt"`
|
||||
WelcomeMessage string `json:"welcome_message"`
|
||||
ReplyTimeoutSeconds int `json:"reply_timeout_seconds"`
|
||||
RolloutPercent int `json:"rollout_percent"`
|
||||
PreviousRolloutPercent int `json:"previous_rollout_percent"`
|
||||
Teams []AIAgentTeamResponse `json:"teams"`
|
||||
HandoffMode enums.AIAgentHandoffMode `json:"handoff_mode"`
|
||||
HandoffModeName string `json:"handoff_mode_name"`
|
||||
FallbackMode enums.AIAgentFallbackMode `json:"fallback_mode"`
|
||||
FallbackModeName string `json:"fallback_mode_name"`
|
||||
FallbackMessage string `json:"fallback_message"`
|
||||
KnowledgeBaseIDs []int64 `json:"knowledge_base_ids"`
|
||||
PublishedRevisionID int64 `json:"published_revision_id"`
|
||||
SortNo int `json:"sort_no"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
CreateUserName string `json:"create_user_name"`
|
||||
UpdateUserName string `json:"update_user_name"`
|
||||
}
|
||||
|
||||
@@ -21,10 +21,29 @@ func TestBuildAIConfigResponseOmitsAPIKey(t *testing.T) {
|
||||
if err := json.Unmarshal(payload, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal response error = %v", err)
|
||||
}
|
||||
if _, ok := decoded["apiKey"]; ok {
|
||||
t.Fatalf("apiKey should not be exposed: %s", payload)
|
||||
if _, ok := decoded["api_key"]; ok {
|
||||
t.Fatalf("api_key should not be exposed: %s", payload)
|
||||
}
|
||||
if got, ok := decoded["hasApiKey"].(bool); !ok || !got {
|
||||
t.Fatalf("hasApiKey = %v, want true: %s", decoded["hasApiKey"], payload)
|
||||
if got, ok := decoded["has_api_key"].(bool); !ok || !got {
|
||||
t.Fatalf("has_api_key = %v, want true: %s", decoded["has_api_key"], payload)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildAIConfigDetailResponseIncludesAPIKey(t *testing.T) {
|
||||
payload, err := json.Marshal(BuildAIConfigDetailResponse(&models.AIConfig{
|
||||
ID: 1,
|
||||
Name: "test",
|
||||
APIKey: "sk-secret",
|
||||
}))
|
||||
if err != nil {
|
||||
t.Fatalf("marshal response error = %v", err)
|
||||
}
|
||||
|
||||
var decoded map[string]any
|
||||
if err := json.Unmarshal(payload, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal response error = %v", err)
|
||||
}
|
||||
if got := decoded["api_key"]; got != "sk-secret" {
|
||||
t.Fatalf("api_key = %v, want sk-secret: %s", got, payload)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/ai/workflow/dsl"
|
||||
workflowregistry "code.tczkiot.com/wlw/ai-agent/internal/ai/workflow/registry"
|
||||
workflowvalidator "code.tczkiot.com/wlw/ai-agent/internal/ai/workflow/validator"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
)
|
||||
|
||||
type AIWorkflowResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Status enums.Status `json:"status"`
|
||||
DraftDefinition dsl.Definition `json:"draftDefinition"`
|
||||
PublishedVersionID int64 `json:"publishedVersionId"`
|
||||
SortNo int `json:"sortNo"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
CreateUserName string `json:"createUserName"`
|
||||
UpdateUserName string `json:"updateUserName"`
|
||||
}
|
||||
|
||||
type AIWorkflowVersionResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
WorkflowID int64 `json:"workflowId"`
|
||||
Version int `json:"version"`
|
||||
Status enums.Status `json:"status"`
|
||||
Definition dsl.Definition `json:"definition"`
|
||||
DefinitionHash string `json:"definitionHash"`
|
||||
PublishedAt string `json:"publishedAt"`
|
||||
PublishedByID int64 `json:"publishedById"`
|
||||
PublishedByName string `json:"publishedByName"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type AIWorkflowValidationResponse struct {
|
||||
Valid bool `json:"valid"`
|
||||
Errors []workflowvalidator.Error `json:"errors"`
|
||||
}
|
||||
|
||||
type AIWorkflowTemplateResponse struct {
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Definition dsl.Definition `json:"definition"`
|
||||
}
|
||||
|
||||
type AIWorkflowUsageResponse struct {
|
||||
AIAgentID int64 `json:"aiAgentId"`
|
||||
AIAgentName string `json:"aiAgentName"`
|
||||
WorkflowVersionID int64 `json:"workflowVersionId"`
|
||||
WorkflowVersion int `json:"workflowVersion"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type AIWorkflowNodeSpecResponse struct {
|
||||
Type string `json:"type"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
Category string `json:"category"`
|
||||
Executable bool `json:"executable"`
|
||||
RiskLevel workflowregistry.NodeRiskLevel `json:"riskLevel"`
|
||||
Interruptible bool `json:"interruptible"`
|
||||
RequiresConfirmationPredecessor bool `json:"requiresConfirmationPredecessor"`
|
||||
ConfigSchema any `json:"configSchema,omitempty"`
|
||||
InputSchema []workflowregistry.VariableSpec `json:"inputSchema,omitempty"`
|
||||
OutputSchema []workflowregistry.VariableSpec `json:"outputSchema,omitempty"`
|
||||
DefaultInputs map[string]dsl.Value `json:"defaultInputs,omitempty"`
|
||||
}
|
||||
|
||||
type AIWorkflowRunResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
WorkflowID int64 `json:"workflowId"`
|
||||
WorkflowVersionID int64 `json:"workflowVersionId"`
|
||||
WorkflowVersion int `json:"workflowVersion"`
|
||||
WorkflowName string `json:"workflowName"`
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
AIAgentID int64 `json:"aiAgentId"`
|
||||
AIAgentName string `json:"aiAgentName"`
|
||||
MessageID int64 `json:"messageId"`
|
||||
Status int `json:"status"`
|
||||
StatusName string `json:"statusName"`
|
||||
StartedAt string `json:"startedAt"`
|
||||
EndedAt string `json:"endedAt"`
|
||||
DurationMS int64 `json:"durationMs"`
|
||||
InterruptType string `json:"interruptType"`
|
||||
InterruptNodeID string `json:"interruptNodeId"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
Definition dsl.Definition `json:"definition"`
|
||||
Nodes []AIWorkflowNodeRunResponse `json:"nodes,omitempty"`
|
||||
}
|
||||
|
||||
type AIWorkflowNodeRunResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
WorkflowRunID int64 `json:"workflowRunId"`
|
||||
NodeID string `json:"nodeId"`
|
||||
NodeType string `json:"nodeType"`
|
||||
Status int `json:"status"`
|
||||
StatusName string `json:"statusName"`
|
||||
InputPreview string `json:"inputPreview"`
|
||||
OutputPreview string `json:"outputPreview"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
StartedAt string `json:"startedAt"`
|
||||
EndedAt string `json:"endedAt"`
|
||||
DurationMS int `json:"durationMs"`
|
||||
}
|
||||
@@ -4,18 +4,18 @@ import "code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
|
||||
type AssetResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
AssetID string `json:"assetId"`
|
||||
AssetID string `json:"asset_id"`
|
||||
Provider enums.AssetProvider `json:"provider"`
|
||||
Filename string `json:"filename"`
|
||||
FileSize int64 `json:"fileSize"`
|
||||
MimeType string `json:"mimeType"`
|
||||
FileSize int64 `json:"file_size"`
|
||||
MimeType string `json:"mime_type"`
|
||||
Status enums.AssetStatus `json:"status"`
|
||||
StorageKey string `json:"storageKey"`
|
||||
StorageKey string `json:"storage_key"`
|
||||
URL string `json:"url"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
CreateUserID int64 `json:"createUserId"`
|
||||
CreateUserName string `json:"createUserName"`
|
||||
UpdateUserID int64 `json:"updateUserId"`
|
||||
UpdateUserName string `json:"updateUserName"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
CreateUserID int64 `json:"create_user_id"`
|
||||
CreateUserName string `json:"create_user_name"`
|
||||
UpdateUserID int64 `json:"update_user_id"`
|
||||
UpdateUserName string `json:"update_user_name"`
|
||||
}
|
||||
|
||||
@@ -8,40 +8,40 @@ import (
|
||||
|
||||
type ChannelResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
ChannelType string `json:"channelType"`
|
||||
ChannelID string `json:"channelId"`
|
||||
AIAgentID int64 `json:"aiAgentId"`
|
||||
AIAgentRolloutPercent int `json:"aiAgentRolloutPercent"`
|
||||
PreviousAIAgentRolloutPercent int `json:"previousAiAgentRolloutPercent"`
|
||||
AIAgentName string `json:"aiAgentName,omitempty"`
|
||||
ChannelType string `json:"channel_type"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
AIAgentID int64 `json:"ai_agent_id"`
|
||||
AIAgentRolloutPercent int `json:"ai_agent_rollout_percent"`
|
||||
PreviousAIAgentRolloutPercent int `json:"previous_ai_agent_rollout_percent"`
|
||||
AIAgentName string `json:"ai_agent_name,omitempty"`
|
||||
Name string `json:"name"`
|
||||
ConfigJSON string `json:"configJson"`
|
||||
ConfigJSON string `json:"config_json"`
|
||||
Status enums.Status `json:"status"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type WxWorkKFAccountResponse struct {
|
||||
OpenKfID string `json:"openKfId"`
|
||||
OpenKfID string `json:"open_kf_id"`
|
||||
Name string `json:"name"`
|
||||
Avatar string `json:"avatar"`
|
||||
ManagePrivilege bool `json:"managePrivilege"`
|
||||
ManagePrivilege bool `json:"manage_privilege"`
|
||||
}
|
||||
|
||||
type ChannelMessageOutboxResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
ChannelType string `json:"channelType"`
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
MessageID int64 `json:"messageId"`
|
||||
ChannelType string `json:"channel_type"`
|
||||
ConversationID int64 `json:"conversation_id"`
|
||||
MessageID int64 `json:"message_id"`
|
||||
Payload string `json:"payload"`
|
||||
SendStatus string `json:"sendStatus"`
|
||||
RetryCount int `json:"retryCount"`
|
||||
NextRetryAt string `json:"nextRetryAt"`
|
||||
LastError string `json:"lastError"`
|
||||
SentAt string `json:"sentAt"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
CreateUserName string `json:"createUserName"`
|
||||
UpdateUserName string `json:"updateUserName"`
|
||||
SendStatus string `json:"send_status"`
|
||||
RetryCount int `json:"retry_count"`
|
||||
NextRetryAt string `json:"next_retry_at"`
|
||||
LastError string `json:"last_error"`
|
||||
SentAt string `json:"sent_at"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
CreateUserName string `json:"create_user_name"`
|
||||
UpdateUserName string `json:"update_user_name"`
|
||||
}
|
||||
|
||||
func BuildChannelResponse(item *models.Channel) ChannelResponse {
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package response
|
||||
|
||||
import "code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
|
||||
type CompanyResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
CustomerCount int64 `json:"customerCount"`
|
||||
Status enums.Status `json:"status"`
|
||||
Remark string `json:"remark"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
@@ -2,49 +2,55 @@ package response
|
||||
|
||||
import "code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
|
||||
type ConversationTagResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type ConversationParticipantResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
ParticipantType string `json:"participantType"`
|
||||
ParticipantID int64 `json:"participantId"`
|
||||
ExternalParticipantID string `json:"externalParticipantId,omitempty"`
|
||||
JoinedAt string `json:"joinedAt,omitempty"`
|
||||
LeftAt string `json:"leftAt,omitempty"`
|
||||
ParticipantType string `json:"participant_type"`
|
||||
ParticipantID int64 `json:"participant_id"`
|
||||
ExternalParticipantID string `json:"external_participant_id,omitempty"`
|
||||
JoinedAt string `json:"joined_at,omitempty"`
|
||||
LeftAt string `json:"left_at,omitempty"`
|
||||
Status enums.Status `json:"status"`
|
||||
}
|
||||
|
||||
type ConversationResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
AIAgentID int64 `json:"aiAgentId"`
|
||||
ChannelID int64 `json:"channelId"`
|
||||
CustomerID int64 `json:"customerId"`
|
||||
CustomerName string `json:"customerName"`
|
||||
AIAgentID int64 `json:"ai_agent_id"`
|
||||
ChannelID int64 `json:"channel_id"`
|
||||
CustomerType string `json:"customer_type"`
|
||||
CustomerID int64 `json:"customer_id"`
|
||||
CustomerExternalID string `json:"customer_external_id"`
|
||||
CustomerName string `json:"customer_name"`
|
||||
Status enums.IMConversationStatus `json:"status"`
|
||||
ServiceMode enums.IMConversationServiceMode `json:"serviceMode"`
|
||||
ServiceMode enums.IMConversationServiceMode `json:"service_mode"`
|
||||
Priority int `json:"priority"`
|
||||
CurrentAssigneeID int64 `json:"currentAssigneeId"`
|
||||
CurrentAssigneeName string `json:"currentAssigneeName,omitempty"`
|
||||
CurrentTeamID int64 `json:"currentTeamId"`
|
||||
CurrentTeamName string `json:"currentTeamName,omitempty"`
|
||||
LastMessageID int64 `json:"lastMessageId"`
|
||||
LastMessageAt string `json:"lastMessageAt,omitempty"`
|
||||
LastActiveAt string `json:"lastActiveAt,omitempty"`
|
||||
LastMessageSummary string `json:"lastMessageSummary,omitempty"`
|
||||
CustomerUnreadCount int `json:"customerUnreadCount"`
|
||||
AgentUnreadCount int `json:"agentUnreadCount"`
|
||||
CustomerLastReadMessageID int64 `json:"customerLastReadMessageId"`
|
||||
CustomerLastReadAt string `json:"customerLastReadAt,omitempty"`
|
||||
AgentLastReadMessageID int64 `json:"agentLastReadMessageId"`
|
||||
AgentLastReadAt string `json:"agentLastReadAt,omitempty"`
|
||||
CustomerOnline bool `json:"customerOnline"`
|
||||
ClosedAt string `json:"closedAt,omitempty"`
|
||||
ClosedBy int64 `json:"closedBy"`
|
||||
ClosedByName string `json:"closedByName,omitempty"`
|
||||
CloseReason string `json:"closeReason,omitempty"`
|
||||
CurrentAssigneeID int64 `json:"current_assignee_id"`
|
||||
CurrentAssigneeName string `json:"current_assignee_name,omitempty"`
|
||||
CurrentTeamID int64 `json:"current_team_id"`
|
||||
CurrentTeamName string `json:"current_team_name,omitempty"`
|
||||
LastMessageID int64 `json:"last_message_id"`
|
||||
LastMessageAt string `json:"last_message_at,omitempty"`
|
||||
LastActiveAt string `json:"last_active_at,omitempty"`
|
||||
LastMessageSummary string `json:"last_message_summary,omitempty"`
|
||||
CustomerUnreadCount int `json:"customer_unread_count"`
|
||||
AgentUnreadCount int `json:"agent_unread_count"`
|
||||
CustomerLastReadMessageID int64 `json:"customer_last_read_message_id"`
|
||||
CustomerLastReadAt string `json:"customer_last_read_at,omitempty"`
|
||||
AgentLastReadMessageID int64 `json:"agent_last_read_message_id"`
|
||||
AgentLastReadAt string `json:"agent_last_read_at,omitempty"`
|
||||
CustomerOnline bool `json:"customer_online"`
|
||||
QueueEnteredAt string `json:"queue_entered_at,omitempty"`
|
||||
QueuePosition int `json:"queue_position"`
|
||||
QueueAheadCount int `json:"queue_ahead_count"`
|
||||
QueueWaitingCount int `json:"queue_waiting_count"`
|
||||
QueueWaitSeconds int64 `json:"queue_wait_seconds"`
|
||||
QueueEstimatedWaitSeconds int64 `json:"queue_estimated_wait_seconds"`
|
||||
QueueEscalationLevel int `json:"queue_escalation_level"`
|
||||
EffectivePriority int `json:"effective_priority"`
|
||||
QueueServiceOnline bool `json:"queue_service_online"`
|
||||
ClosedAt string `json:"closed_at,omitempty"`
|
||||
ClosedBy int64 `json:"closed_by"`
|
||||
ClosedByName string `json:"closed_by_name,omitempty"`
|
||||
CloseReason string `json:"close_reason,omitempty"`
|
||||
}
|
||||
|
||||
type ConversationDetailResponse struct {
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package response
|
||||
|
||||
import "code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
|
||||
type CustomerContactResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
CustomerID int64 `json:"customerId"`
|
||||
ContactType enums.ContactType `json:"contactType"`
|
||||
ContactValue string `json:"contactValue"`
|
||||
IsPrimary bool `json:"isPrimary"`
|
||||
IsVerified bool `json:"isVerified"`
|
||||
VerifiedAt string `json:"verifiedAt,omitempty"`
|
||||
Source string `json:"source"`
|
||||
Status enums.Status `json:"status"`
|
||||
Remark string `json:"remark"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package response
|
||||
|
||||
type CustomerQuickActionResponse struct {
|
||||
Code string `json:"code"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
type CustomerQuickActionExecutionResponse struct {
|
||||
CustomerMessage MessageResponse `json:"customer_message"`
|
||||
ReplyMessage MessageResponse `json:"reply_message"`
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package response
|
||||
|
||||
import "code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
|
||||
type CustomerResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Gender enums.Gender `json:"gender"`
|
||||
CompanyID int64 `json:"companyId"`
|
||||
Company *CompanyResponse `json:"company"`
|
||||
LastActiveAt string `json:"lastActiveAt"`
|
||||
PrimaryMobile string `json:"primaryMobile"`
|
||||
PrimaryEmail string `json:"primaryEmail"`
|
||||
Status enums.Status `json:"status"`
|
||||
Remark string `json:"remark"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
@@ -2,25 +2,25 @@ package response
|
||||
|
||||
type DashboardOverviewResponse struct {
|
||||
Range string `json:"range"`
|
||||
GeneratedAt string `json:"generatedAt"`
|
||||
GeneratedAt string `json:"generated_at"`
|
||||
Summary DashboardSummaryResponse `json:"summary"`
|
||||
ConversationStats DashboardSectionStatsResponse `json:"conversationStats"`
|
||||
AgentStats DashboardAgentStatsResponse `json:"agentStats"`
|
||||
AIStats DashboardAIStatsResponse `json:"aiStats"`
|
||||
ConversationStats DashboardSectionStatsResponse `json:"conversation_stats"`
|
||||
AgentStats DashboardAgentStatsResponse `json:"agent_stats"`
|
||||
AIStats DashboardAIStatsResponse `json:"ai_stats"`
|
||||
Alerts []DashboardAlertResponse `json:"alerts"`
|
||||
QuickLinks []DashboardQuickLinkResponse `json:"quickLinks"`
|
||||
QuickLinks []DashboardQuickLinkResponse `json:"quick_links"`
|
||||
}
|
||||
|
||||
type DashboardSummaryResponse struct {
|
||||
TodayNewConversations int64 `json:"todayNewConversations"`
|
||||
ProcessingConversations int64 `json:"processingConversations"`
|
||||
PendingDispatchConversations int64 `json:"pendingDispatchConversations"`
|
||||
OnlineAgents int64 `json:"onlineAgents"`
|
||||
AIServiceRate float64 `json:"aiServiceRate"`
|
||||
TodayNewConversations int64 `json:"today_new_conversations"`
|
||||
ProcessingConversations int64 `json:"processing_conversations"`
|
||||
PendingDispatchConversations int64 `json:"pending_dispatch_conversations"`
|
||||
OnlineAgents int64 `json:"online_agents"`
|
||||
AIServiceRate float64 `json:"ai_service_rate"`
|
||||
}
|
||||
|
||||
type DashboardSectionStatsResponse struct {
|
||||
StatusDistribution []DashboardStatusDistributionItem `json:"statusDistribution"`
|
||||
StatusDistribution []DashboardStatusDistributionItem `json:"status_distribution"`
|
||||
Trend []DashboardTrendItem `json:"trend"`
|
||||
}
|
||||
|
||||
@@ -32,39 +32,39 @@ type DashboardStatusDistributionItem struct {
|
||||
|
||||
type DashboardTrendItem struct {
|
||||
Date string `json:"date"`
|
||||
NewCount int64 `json:"newCount"`
|
||||
ClosedCount int64 `json:"closedCount"`
|
||||
NewCount int64 `json:"new_count"`
|
||||
ClosedCount int64 `json:"closed_count"`
|
||||
}
|
||||
|
||||
type DashboardAgentStatsResponse struct {
|
||||
OnlineAgents int64 `json:"onlineAgents"`
|
||||
BusyAgents int64 `json:"busyAgents"`
|
||||
OfflineAgents int64 `json:"offlineAgents"`
|
||||
TeamLoads []DashboardTeamLoadResponse `json:"teamLoads"`
|
||||
OnlineAgents int64 `json:"online_agents"`
|
||||
BusyAgents int64 `json:"busy_agents"`
|
||||
OfflineAgents int64 `json:"offline_agents"`
|
||||
TeamLoads []DashboardTeamLoadResponse `json:"team_loads"`
|
||||
}
|
||||
|
||||
type DashboardTeamLoadResponse struct {
|
||||
TeamID int64 `json:"teamId"`
|
||||
TeamName string `json:"teamName"`
|
||||
TotalAgents int64 `json:"totalAgents"`
|
||||
OnlineAgents int64 `json:"onlineAgents"`
|
||||
BusyAgents int64 `json:"busyAgents"`
|
||||
OfflineAgents int64 `json:"offlineAgents"`
|
||||
WaitingConversations int64 `json:"waitingConversations"`
|
||||
ProcessingConversations int64 `json:"processingConversations"`
|
||||
MaxConcurrentCapacity int64 `json:"maxConcurrentCapacity"`
|
||||
LoadRate float64 `json:"loadRate"`
|
||||
HasScheduleNow bool `json:"hasScheduleNow"`
|
||||
TeamID int64 `json:"team_id"`
|
||||
TeamName string `json:"team_name"`
|
||||
TotalAgents int64 `json:"total_agents"`
|
||||
OnlineAgents int64 `json:"online_agents"`
|
||||
BusyAgents int64 `json:"busy_agents"`
|
||||
OfflineAgents int64 `json:"offline_agents"`
|
||||
WaitingConversations int64 `json:"waiting_conversations"`
|
||||
ProcessingConversations int64 `json:"processing_conversations"`
|
||||
MaxConcurrentCapacity int64 `json:"max_concurrent_capacity"`
|
||||
LoadRate float64 `json:"load_rate"`
|
||||
HasScheduleNow bool `json:"has_schedule_now"`
|
||||
}
|
||||
|
||||
type DashboardAIStatsResponse struct {
|
||||
EnabledAIAgents int64 `json:"enabledAiAgents"`
|
||||
EnabledChannels int64 `json:"enabledChannels"`
|
||||
TodayKnowledgeRetrieves int64 `json:"todayKnowledgeRetrieves"`
|
||||
TodayKnowledgeRetrieveFailCount int64 `json:"todayKnowledgeRetrieveFailCount"`
|
||||
TodayKnowledgeRetrieveFailRate float64 `json:"todayKnowledgeRetrieveFailRate"`
|
||||
TodayAgentRunFailCount int64 `json:"todayAgentRunFailCount"`
|
||||
TodayAIHandoffCount int64 `json:"todayAiHandoffCount"`
|
||||
EnabledAIAgents int64 `json:"enabled_ai_agents"`
|
||||
EnabledChannels int64 `json:"enabled_channels"`
|
||||
TodayKnowledgeRetrieves int64 `json:"today_knowledge_retrieves"`
|
||||
TodayKnowledgeRetrieveFailCount int64 `json:"today_knowledge_retrieve_fail_count"`
|
||||
TodayKnowledgeRetrieveFailRate float64 `json:"today_knowledge_retrieve_fail_rate"`
|
||||
TodayAgentRunFailCount int64 `json:"today_agent_run_fail_count"`
|
||||
TodayAIHandoffCount int64 `json:"today_ai_handoff_count"`
|
||||
}
|
||||
|
||||
type DashboardAlertResponse struct {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func TestPublicResponseJSONTagsUseSnakeCase(t *testing.T) {
|
||||
packages, err := parser.ParseDir(token.NewFileSet(), ".", nil, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("parse response package: %v", err)
|
||||
}
|
||||
|
||||
pkg := packages["response"]
|
||||
if pkg == nil {
|
||||
t.Fatal("response package not found")
|
||||
}
|
||||
for filename, file := range pkg.Files {
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
field, ok := node.(*ast.Field)
|
||||
if !ok || field.Tag == nil {
|
||||
return true
|
||||
}
|
||||
tag := reflect.StructTag(strings.Trim(field.Tag.Value, "`"))
|
||||
name := strings.Split(tag.Get("json"), ",")[0]
|
||||
for _, char := range name {
|
||||
if unicode.IsUpper(char) {
|
||||
t.Errorf("%s has non-snake-case JSON tag %q", filename, name)
|
||||
break
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -9,109 +9,109 @@ type KnowledgeBaseResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
KnowledgeType string `json:"knowledgeType"`
|
||||
KnowledgeTypeName string `json:"knowledgeTypeName"`
|
||||
KnowledgeType string `json:"knowledge_type"`
|
||||
KnowledgeTypeName string `json:"knowledge_type_name"`
|
||||
Status enums.Status `json:"status"`
|
||||
StatusName string `json:"statusName"`
|
||||
DefaultTopK int `json:"defaultTopK"`
|
||||
DefaultScoreThreshold float64 `json:"defaultScoreThreshold"`
|
||||
DefaultRerankLimit int `json:"defaultRerankLimit"`
|
||||
ChunkProvider string `json:"chunkProvider"`
|
||||
ChunkTargetTokens int `json:"chunkTargetTokens"`
|
||||
ChunkMaxTokens int `json:"chunkMaxTokens"`
|
||||
ChunkOverlapTokens int `json:"chunkOverlapTokens"`
|
||||
AnswerMode int `json:"answerMode"`
|
||||
AnswerModeName string `json:"answerModeName"`
|
||||
DocumentCount int64 `json:"documentCount"`
|
||||
FAQCount int64 `json:"faqCount"`
|
||||
StatusName string `json:"status_name"`
|
||||
DefaultTopK int `json:"default_top_k"`
|
||||
DefaultScoreThreshold float64 `json:"default_score_threshold"`
|
||||
DefaultRerankLimit int `json:"default_rerank_limit"`
|
||||
ChunkProvider string `json:"chunk_provider"`
|
||||
ChunkTargetTokens int `json:"chunk_target_tokens"`
|
||||
ChunkMaxTokens int `json:"chunk_max_tokens"`
|
||||
ChunkOverlapTokens int `json:"chunk_overlap_tokens"`
|
||||
AnswerMode int `json:"answer_mode"`
|
||||
AnswerModeName string `json:"answer_mode_name"`
|
||||
DocumentCount int64 `json:"document_count"`
|
||||
FAQCount int64 `json:"faq_count"`
|
||||
Remark string `json:"remark"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
CreateUserName string `json:"createUserName"`
|
||||
UpdateUserName string `json:"updateUserName"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
CreateUserName string `json:"create_user_name"`
|
||||
UpdateUserName string `json:"update_user_name"`
|
||||
}
|
||||
|
||||
type KnowledgeDocumentResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
KnowledgeBaseID int64 `json:"knowledgeBaseId"`
|
||||
KnowledgeBaseName string `json:"knowledgeBaseName,omitempty"`
|
||||
DirectoryID int64 `json:"directoryId"`
|
||||
DirectoryName string `json:"directoryName,omitempty"`
|
||||
DirectoryPath string `json:"directoryPath,omitempty"`
|
||||
KnowledgeBaseID int64 `json:"knowledge_base_id"`
|
||||
KnowledgeBaseName string `json:"knowledge_base_name,omitempty"`
|
||||
DirectoryID int64 `json:"directory_id"`
|
||||
DirectoryName string `json:"directory_name,omitempty"`
|
||||
DirectoryPath string `json:"directory_path,omitempty"`
|
||||
Title string `json:"title"`
|
||||
ContentType enums.KnowledgeDocumentContentType `json:"contentType"`
|
||||
ContentType enums.KnowledgeDocumentContentType `json:"content_type"`
|
||||
Content string `json:"content"`
|
||||
Status enums.Status `json:"status"`
|
||||
StatusName string `json:"statusName"`
|
||||
IndexStatus enums.KnowledgeDocumentIndexStatus `json:"indexStatus"`
|
||||
IndexStatusName string `json:"indexStatusName"`
|
||||
IndexedAt *time.Time `json:"indexedAt"`
|
||||
IndexError string `json:"indexError"`
|
||||
ContentHash string `json:"contentHash"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
CreateUserName string `json:"createUserName"`
|
||||
UpdateUserName string `json:"updateUserName"`
|
||||
StatusName string `json:"status_name"`
|
||||
IndexStatus enums.KnowledgeDocumentIndexStatus `json:"index_status"`
|
||||
IndexStatusName string `json:"index_status_name"`
|
||||
IndexedAt *time.Time `json:"indexed_at"`
|
||||
IndexError string `json:"index_error"`
|
||||
ContentHash string `json:"content_hash"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
CreateUserName string `json:"create_user_name"`
|
||||
UpdateUserName string `json:"update_user_name"`
|
||||
}
|
||||
|
||||
type KnowledgeDocumentListResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
KnowledgeBaseID int64 `json:"knowledgeBaseId"`
|
||||
KnowledgeBaseName string `json:"knowledgeBaseName,omitempty"`
|
||||
DirectoryID int64 `json:"directoryId"`
|
||||
DirectoryName string `json:"directoryName,omitempty"`
|
||||
DirectoryPath string `json:"directoryPath,omitempty"`
|
||||
KnowledgeBaseID int64 `json:"knowledge_base_id"`
|
||||
KnowledgeBaseName string `json:"knowledge_base_name,omitempty"`
|
||||
DirectoryID int64 `json:"directory_id"`
|
||||
DirectoryName string `json:"directory_name,omitempty"`
|
||||
DirectoryPath string `json:"directory_path,omitempty"`
|
||||
Title string `json:"title"`
|
||||
ContentType enums.KnowledgeDocumentContentType `json:"contentType"`
|
||||
ContentType enums.KnowledgeDocumentContentType `json:"content_type"`
|
||||
Status enums.Status `json:"status"`
|
||||
StatusName string `json:"statusName"`
|
||||
IndexStatus enums.KnowledgeDocumentIndexStatus `json:"indexStatus"`
|
||||
IndexStatusName string `json:"indexStatusName"`
|
||||
IndexedAt *time.Time `json:"indexedAt"`
|
||||
IndexError string `json:"indexError"`
|
||||
ContentHash string `json:"contentHash"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
CreateUserName string `json:"createUserName"`
|
||||
UpdateUserName string `json:"updateUserName"`
|
||||
StatusName string `json:"status_name"`
|
||||
IndexStatus enums.KnowledgeDocumentIndexStatus `json:"index_status"`
|
||||
IndexStatusName string `json:"index_status_name"`
|
||||
IndexedAt *time.Time `json:"indexed_at"`
|
||||
IndexError string `json:"index_error"`
|
||||
ContentHash string `json:"content_hash"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
CreateUserName string `json:"create_user_name"`
|
||||
UpdateUserName string `json:"update_user_name"`
|
||||
}
|
||||
|
||||
type KnowledgeFAQResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
KnowledgeBaseID int64 `json:"knowledgeBaseId"`
|
||||
KnowledgeBaseName string `json:"knowledgeBaseName,omitempty"`
|
||||
DirectoryID int64 `json:"directoryId"`
|
||||
DirectoryName string `json:"directoryName,omitempty"`
|
||||
DirectoryPath string `json:"directoryPath,omitempty"`
|
||||
KnowledgeBaseID int64 `json:"knowledge_base_id"`
|
||||
KnowledgeBaseName string `json:"knowledge_base_name,omitempty"`
|
||||
DirectoryID int64 `json:"directory_id"`
|
||||
DirectoryName string `json:"directory_name,omitempty"`
|
||||
DirectoryPath string `json:"directory_path,omitempty"`
|
||||
Question string `json:"question"`
|
||||
Answer string `json:"answer"`
|
||||
SimilarQuestions []string `json:"similarQuestions"`
|
||||
SimilarQuestions []string `json:"similar_questions"`
|
||||
Status enums.Status `json:"status"`
|
||||
StatusName string `json:"statusName"`
|
||||
IndexStatus enums.KnowledgeDocumentIndexStatus `json:"indexStatus"`
|
||||
IndexStatusName string `json:"indexStatusName"`
|
||||
IndexedAt *time.Time `json:"indexedAt"`
|
||||
IndexError string `json:"indexError"`
|
||||
StatusName string `json:"status_name"`
|
||||
IndexStatus enums.KnowledgeDocumentIndexStatus `json:"index_status"`
|
||||
IndexStatusName string `json:"index_status_name"`
|
||||
IndexedAt *time.Time `json:"indexed_at"`
|
||||
IndexError string `json:"index_error"`
|
||||
Remark string `json:"remark"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
CreateUserName string `json:"createUserName"`
|
||||
UpdateUserName string `json:"updateUserName"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
CreateUserName string `json:"create_user_name"`
|
||||
UpdateUserName string `json:"update_user_name"`
|
||||
}
|
||||
|
||||
type KnowledgeDirectoryResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
KnowledgeBaseID int64 `json:"knowledgeBaseId"`
|
||||
ParentID int64 `json:"parentId"`
|
||||
KnowledgeBaseID int64 `json:"knowledge_base_id"`
|
||||
ParentID int64 `json:"parent_id"`
|
||||
Name string `json:"name"`
|
||||
SortNo int `json:"sortNo"`
|
||||
SortNo int `json:"sort_no"`
|
||||
Status enums.Status `json:"status"`
|
||||
StatusName string `json:"statusName"`
|
||||
StatusName string `json:"status_name"`
|
||||
Remark string `json:"remark"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
CreateUserName string `json:"createUserName"`
|
||||
UpdateUserName string `json:"updateUserName"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
CreateUserName string `json:"create_user_name"`
|
||||
UpdateUserName string `json:"update_user_name"`
|
||||
Children []KnowledgeDirectoryResponse `json:"children"`
|
||||
}
|
||||
|
||||
@@ -136,116 +136,116 @@ type KnowledgeFAQImportResult struct {
|
||||
}
|
||||
|
||||
type KnowledgeSearchResult struct {
|
||||
KnowledgeBaseID int64 `json:"knowledgeBaseId"`
|
||||
ChunkID int64 `json:"chunkId"`
|
||||
DocumentID int64 `json:"documentId"`
|
||||
DocumentTitle string `json:"documentTitle"`
|
||||
FaqID int64 `json:"faqId"`
|
||||
FaqQuestion string `json:"faqQuestion"`
|
||||
ChunkNo int `json:"chunkNo"`
|
||||
KnowledgeBaseID int64 `json:"knowledge_base_id"`
|
||||
ChunkID int64 `json:"chunk_id"`
|
||||
DocumentID int64 `json:"document_id"`
|
||||
DocumentTitle string `json:"document_title"`
|
||||
FaqID int64 `json:"faq_id"`
|
||||
FaqQuestion string `json:"faq_question"`
|
||||
ChunkNo int `json:"chunk_no"`
|
||||
Title string `json:"title"`
|
||||
SectionPath string `json:"sectionPath"`
|
||||
SectionPath string `json:"section_path"`
|
||||
Content string `json:"content"`
|
||||
Score float64 `json:"score"`
|
||||
RerankScore float64 `json:"rerankScore"`
|
||||
RerankScore float64 `json:"rerank_score"`
|
||||
}
|
||||
|
||||
type KnowledgeSearchResponse struct {
|
||||
Question string `json:"question"`
|
||||
Results []KnowledgeSearchResult `json:"results"`
|
||||
HitCount int `json:"hitCount"`
|
||||
LatencyMs int64 `json:"latencyMs"`
|
||||
HitCount int `json:"hit_count"`
|
||||
LatencyMs int64 `json:"latency_ms"`
|
||||
}
|
||||
|
||||
type KnowledgeAnswerResponse struct {
|
||||
Question string `json:"question"`
|
||||
RewriteQuestion string `json:"rewriteQuestion,omitempty"`
|
||||
RewriteQuestion string `json:"rewrite_question,omitempty"`
|
||||
Answer string `json:"answer"`
|
||||
AnswerStatus int `json:"answerStatus"`
|
||||
AnswerStatusName string `json:"answerStatusName"`
|
||||
AnswerStatus int `json:"answer_status"`
|
||||
AnswerStatusName string `json:"answer_status_name"`
|
||||
Citations []KnowledgeCitation `json:"citations"`
|
||||
Hits []KnowledgeSearchResult `json:"hits"`
|
||||
HitCount int `json:"hitCount"`
|
||||
TopScore float64 `json:"topScore"`
|
||||
LatencyMs int64 `json:"latencyMs"`
|
||||
RetrieveMs int64 `json:"retrieveMs"`
|
||||
GenerateMs int64 `json:"generateMs"`
|
||||
PromptTokens int `json:"promptTokens"`
|
||||
CompletionTokens int `json:"completionTokens"`
|
||||
ModelName string `json:"modelName"`
|
||||
RetrieveLogID int64 `json:"retrieveLogId"`
|
||||
HitCount int `json:"hit_count"`
|
||||
TopScore float64 `json:"top_score"`
|
||||
LatencyMs int64 `json:"latency_ms"`
|
||||
RetrieveMs int64 `json:"retrieve_ms"`
|
||||
GenerateMs int64 `json:"generate_ms"`
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
CompletionTokens int `json:"completion_tokens"`
|
||||
ModelName string `json:"model_name"`
|
||||
RetrieveLogID int64 `json:"retrieve_log_id"`
|
||||
}
|
||||
|
||||
type KnowledgeCitation struct {
|
||||
DocumentID int64 `json:"documentId"`
|
||||
DocumentTitle string `json:"documentTitle"`
|
||||
FaqID int64 `json:"faqId"`
|
||||
FaqQuestion string `json:"faqQuestion"`
|
||||
ChunkNo int `json:"chunkNo"`
|
||||
DocumentID int64 `json:"document_id"`
|
||||
DocumentTitle string `json:"document_title"`
|
||||
FaqID int64 `json:"faq_id"`
|
||||
FaqQuestion string `json:"faq_question"`
|
||||
ChunkNo int `json:"chunk_no"`
|
||||
Title string `json:"title"`
|
||||
SectionPath string `json:"sectionPath"`
|
||||
SectionPath string `json:"section_path"`
|
||||
Snippet string `json:"snippet"`
|
||||
Score float64 `json:"score"`
|
||||
}
|
||||
|
||||
type KnowledgeRetrieveLogResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
KnowledgeBaseID int64 `json:"knowledgeBaseId"`
|
||||
KnowledgeBaseName string `json:"knowledgeBaseName,omitempty"`
|
||||
KnowledgeBaseID int64 `json:"knowledge_base_id"`
|
||||
KnowledgeBaseName string `json:"knowledge_base_name,omitempty"`
|
||||
Channel string `json:"channel"`
|
||||
ChannelName string `json:"channelName"`
|
||||
ChannelName string `json:"channel_name"`
|
||||
Scene string `json:"scene"`
|
||||
SceneName string `json:"sceneName"`
|
||||
SessionID string `json:"sessionId"`
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
RequestID string `json:"requestId"`
|
||||
SceneName string `json:"scene_name"`
|
||||
SessionID string `json:"session_id"`
|
||||
ConversationID int64 `json:"conversation_id"`
|
||||
RequestID string `json:"request_id"`
|
||||
Question string `json:"question"`
|
||||
RewriteQuestion string `json:"rewriteQuestion"`
|
||||
RewriteQuestion string `json:"rewrite_question"`
|
||||
Answer string `json:"answer"`
|
||||
AnswerStatus int `json:"answerStatus"`
|
||||
AnswerStatusName string `json:"answerStatusName"`
|
||||
HitCount int `json:"hitCount"`
|
||||
TopScore float64 `json:"topScore"`
|
||||
ChunkProvider string `json:"chunkProvider"`
|
||||
ChunkTargetTokens int `json:"chunkTargetTokens"`
|
||||
ChunkMaxTokens int `json:"chunkMaxTokens"`
|
||||
ChunkOverlapTokens int `json:"chunkOverlapTokens"`
|
||||
RerankEnabled bool `json:"rerankEnabled"`
|
||||
RerankLimit int `json:"rerankLimit"`
|
||||
CitationCount int `json:"citationCount"`
|
||||
UsedChunkCount int `json:"usedChunkCount"`
|
||||
LatencyMs int64 `json:"latencyMs"`
|
||||
RetrieveMs int64 `json:"retrieveMs"`
|
||||
GenerateMs int64 `json:"generateMs"`
|
||||
PromptTokens int `json:"promptTokens"`
|
||||
CompletionTokens int `json:"completionTokens"`
|
||||
ModelName string `json:"modelName"`
|
||||
TraceData string `json:"traceData"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
AnswerStatus int `json:"answer_status"`
|
||||
AnswerStatusName string `json:"answer_status_name"`
|
||||
HitCount int `json:"hit_count"`
|
||||
TopScore float64 `json:"top_score"`
|
||||
ChunkProvider string `json:"chunk_provider"`
|
||||
ChunkTargetTokens int `json:"chunk_target_tokens"`
|
||||
ChunkMaxTokens int `json:"chunk_max_tokens"`
|
||||
ChunkOverlapTokens int `json:"chunk_overlap_tokens"`
|
||||
RerankEnabled bool `json:"rerank_enabled"`
|
||||
RerankLimit int `json:"rerank_limit"`
|
||||
CitationCount int `json:"citation_count"`
|
||||
UsedChunkCount int `json:"used_chunk_count"`
|
||||
LatencyMs int64 `json:"latency_ms"`
|
||||
RetrieveMs int64 `json:"retrieve_ms"`
|
||||
GenerateMs int64 `json:"generate_ms"`
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
CompletionTokens int `json:"completion_tokens"`
|
||||
ModelName string `json:"model_name"`
|
||||
TraceData string `json:"trace_data"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
type KnowledgeRetrieveHitResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
RetrieveLogID int64 `json:"retrieveLogId"`
|
||||
KnowledgeBaseID int64 `json:"knowledgeBaseId"`
|
||||
ChunkID int64 `json:"chunkId"`
|
||||
DocumentID int64 `json:"documentId"`
|
||||
DocumentTitle string `json:"documentTitle"`
|
||||
FaqID int64 `json:"faqId"`
|
||||
FaqQuestion string `json:"faqQuestion"`
|
||||
ChunkNo int `json:"chunkNo"`
|
||||
RetrieveLogID int64 `json:"retrieve_log_id"`
|
||||
KnowledgeBaseID int64 `json:"knowledge_base_id"`
|
||||
ChunkID int64 `json:"chunk_id"`
|
||||
DocumentID int64 `json:"document_id"`
|
||||
DocumentTitle string `json:"document_title"`
|
||||
FaqID int64 `json:"faq_id"`
|
||||
FaqQuestion string `json:"faq_question"`
|
||||
ChunkNo int `json:"chunk_no"`
|
||||
Title string `json:"title"`
|
||||
SectionPath string `json:"sectionPath"`
|
||||
ChunkType string `json:"chunkType"`
|
||||
ChunkTypeName string `json:"chunkTypeName"`
|
||||
SectionPath string `json:"section_path"`
|
||||
ChunkType string `json:"chunk_type"`
|
||||
ChunkTypeName string `json:"chunk_type_name"`
|
||||
Provider string `json:"provider"`
|
||||
RankNo int `json:"rankNo"`
|
||||
RankNo int `json:"rank_no"`
|
||||
Score float64 `json:"score"`
|
||||
RerankScore float64 `json:"rerankScore"`
|
||||
UsedInAnswer bool `json:"usedInAnswer"`
|
||||
IsCitation bool `json:"isCitation"`
|
||||
RerankScore float64 `json:"rerank_score"`
|
||||
UsedInAnswer bool `json:"used_in_answer"`
|
||||
IsCitation bool `json:"is_citation"`
|
||||
Snippet string `json:"snippet"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
type KnowledgeRetrieveLogDetailResponse struct {
|
||||
@@ -255,12 +255,12 @@ type KnowledgeRetrieveLogDetailResponse struct {
|
||||
|
||||
type KnowledgeFeedbackResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
RetrieveLogID int64 `json:"retrieveLogId"`
|
||||
FeedbackType int `json:"feedbackType"`
|
||||
FeedbackTypeName string `json:"feedbackTypeName"`
|
||||
FeedbackReason string `json:"feedbackReason"`
|
||||
UserID int64 `json:"userId"`
|
||||
AgentID int64 `json:"agentId"`
|
||||
RetrieveLogID int64 `json:"retrieve_log_id"`
|
||||
FeedbackType int `json:"feedback_type"`
|
||||
FeedbackTypeName string `json:"feedback_type_name"`
|
||||
FeedbackReason string `json:"feedback_reason"`
|
||||
UserID int64 `json:"user_id"`
|
||||
AgentID int64 `json:"agent_id"`
|
||||
Remark string `json:"remark"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/ai/mcps"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
)
|
||||
|
||||
type MCPConnectionResponse struct {
|
||||
ServerCode string `json:"serverCode"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
Protocol string `json:"protocol"`
|
||||
ServerName string `json:"serverName"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
func BuildMCPConnectionResponse(item *mcps.ConnectionResult) *MCPConnectionResponse {
|
||||
if item == nil {
|
||||
return nil
|
||||
}
|
||||
return &MCPConnectionResponse{
|
||||
ServerCode: item.ServerCode,
|
||||
Endpoint: item.Endpoint,
|
||||
Protocol: item.Protocol,
|
||||
ServerName: item.ServerName,
|
||||
Version: item.Version,
|
||||
}
|
||||
}
|
||||
|
||||
type MCPServerInfoResponse struct {
|
||||
Code string `json:"code"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
TimeoutMS int `json:"timeoutMs"`
|
||||
}
|
||||
|
||||
func BuildMCPServerInfoResponses(items []mcps.ServerInfo) []MCPServerInfoResponse {
|
||||
ret := make([]MCPServerInfoResponse, 0, len(items))
|
||||
for _, item := range items {
|
||||
ret = append(ret, MCPServerInfoResponse{
|
||||
Code: item.Code,
|
||||
Enabled: item.Enabled,
|
||||
Endpoint: item.Endpoint,
|
||||
TimeoutMS: item.TimeoutMS,
|
||||
})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
type MCPToolInfoResponse struct {
|
||||
Name string `json:"name"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
InputSchema any `json:"inputSchema"`
|
||||
OutputSchema any `json:"outputSchema,omitempty"`
|
||||
ReadOnlyHint bool `json:"readOnlyHint"`
|
||||
}
|
||||
|
||||
func BuildMCPToolInfoResponses(items []mcps.ToolInfo) []MCPToolInfoResponse {
|
||||
ret := make([]MCPToolInfoResponse, 0, len(items))
|
||||
for _, item := range items {
|
||||
ret = append(ret, MCPToolInfoResponse{
|
||||
Name: item.Name,
|
||||
Title: item.Title,
|
||||
Description: item.Description,
|
||||
InputSchema: item.InputSchema,
|
||||
OutputSchema: item.OutputSchema,
|
||||
ReadOnlyHint: item.ReadOnlyHint,
|
||||
})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
type MCPToolCatalogResponse struct {
|
||||
ToolCode string `json:"toolCode"`
|
||||
ServerCode string `json:"serverCode"`
|
||||
ToolName string `json:"toolName"`
|
||||
SourceType enums.ToolSourceType `json:"sourceType"`
|
||||
AutoInjected bool `json:"autoInjected"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
InputSchema any `json:"inputSchema"`
|
||||
OutputSchema any `json:"outputSchema,omitempty"`
|
||||
RiskLevel string `json:"riskLevel"`
|
||||
RequireConfirmation bool `json:"requireConfirmation"`
|
||||
RiskEditable bool `json:"riskEditable"`
|
||||
}
|
||||
|
||||
type MCPToolResultContentResponse struct {
|
||||
Type string `json:"type"`
|
||||
Text string `json:"text,omitempty"`
|
||||
Data any `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type MCPCallToolResponse struct {
|
||||
ServerCode string `json:"serverCode"`
|
||||
ToolName string `json:"toolName"`
|
||||
IsError bool `json:"isError"`
|
||||
Content []MCPToolResultContentResponse `json:"content"`
|
||||
StructuredContent any `json:"structuredContent,omitempty"`
|
||||
}
|
||||
|
||||
func BuildMCPCallToolResponse(item *mcps.ToolCallResult) *MCPCallToolResponse {
|
||||
if item == nil {
|
||||
return nil
|
||||
}
|
||||
content := make([]MCPToolResultContentResponse, 0, len(item.Content))
|
||||
for _, c := range item.Content {
|
||||
content = append(content, MCPToolResultContentResponse{
|
||||
Type: c.Type,
|
||||
Text: c.Text,
|
||||
Data: c.Data,
|
||||
})
|
||||
}
|
||||
return &MCPCallToolResponse{
|
||||
ServerCode: item.ServerCode,
|
||||
ToolName: item.ToolName,
|
||||
IsError: item.IsError,
|
||||
Content: content,
|
||||
StructuredContent: item.StructuredContent,
|
||||
}
|
||||
}
|
||||
@@ -4,25 +4,24 @@ import "code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
|
||||
type MessageResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
RequestID string `json:"requestId,omitempty"`
|
||||
WorkflowRunID int64 `json:"workflowRunId,omitempty"`
|
||||
ClientMsgID string `json:"clientMsgId,omitempty"`
|
||||
SenderType enums.IMSenderType `json:"senderType"`
|
||||
SenderID int64 `json:"senderId"`
|
||||
SenderName string `json:"senderName,omitempty"`
|
||||
SenderAvatar string `json:"senderAvatar,omitempty"`
|
||||
MessageType enums.IMMessageType `json:"messageType"`
|
||||
ConversationID int64 `json:"conversation_id"`
|
||||
RequestID string `json:"request_id,omitempty"`
|
||||
ClientMsgID string `json:"client_msg_id,omitempty"`
|
||||
SenderType enums.IMSenderType `json:"sender_type"`
|
||||
SenderID int64 `json:"sender_id"`
|
||||
SenderName string `json:"sender_name,omitempty"`
|
||||
SenderAvatar string `json:"sender_avatar,omitempty"`
|
||||
MessageType enums.IMMessageType `json:"message_type"`
|
||||
Content string `json:"content"`
|
||||
Payload string `json:"payload,omitempty"`
|
||||
SendStatus enums.IMMessageStatus `json:"sendStatus"`
|
||||
SentAt string `json:"sentAt,omitempty"`
|
||||
DeliveredAt string `json:"deliveredAt,omitempty"`
|
||||
ReadAt string `json:"readAt,omitempty"`
|
||||
CustomerRead bool `json:"customerRead"`
|
||||
CustomerReadAt string `json:"customerReadAt,omitempty"`
|
||||
AgentRead bool `json:"agentRead"`
|
||||
AgentReadAt string `json:"agentReadAt,omitempty"`
|
||||
RecalledAt string `json:"recalledAt,omitempty"`
|
||||
QuotedMessageID int64 `json:"quotedMessageId,omitempty"`
|
||||
SendStatus enums.IMMessageStatus `json:"send_status"`
|
||||
SentAt string `json:"sent_at,omitempty"`
|
||||
DeliveredAt string `json:"delivered_at,omitempty"`
|
||||
ReadAt string `json:"read_at,omitempty"`
|
||||
CustomerRead bool `json:"customer_read"`
|
||||
CustomerReadAt string `json:"customer_read_at,omitempty"`
|
||||
AgentRead bool `json:"agent_read"`
|
||||
AgentReadAt string `json:"agent_read_at,omitempty"`
|
||||
RecalledAt string `json:"recalled_at,omitempty"`
|
||||
QuotedMessageID int64 `json:"quoted_message_id,omitempty"`
|
||||
}
|
||||
|
||||
@@ -2,17 +2,17 @@ package response
|
||||
|
||||
type NotificationResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
RecipientUserID int64 `json:"recipientUserId"`
|
||||
RecipientUserID int64 `json:"recipient_user_id"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
NotificationType string `json:"notificationType"`
|
||||
BizType string `json:"bizType"`
|
||||
BizID int64 `json:"bizId"`
|
||||
ActionURL string `json:"actionUrl"`
|
||||
ReadAt string `json:"readAt,omitempty"`
|
||||
CreatedAt string `json:"createdAt,omitempty"`
|
||||
NotificationType string `json:"notification_type"`
|
||||
BizType string `json:"biz_type"`
|
||||
BizID int64 `json:"biz_id"`
|
||||
ActionURL string `json:"action_url"`
|
||||
ReadAt string `json:"read_at,omitempty"`
|
||||
CreatedAt string `json:"created_at,omitempty"`
|
||||
}
|
||||
|
||||
type NotificationUnreadCountResponse struct {
|
||||
UnreadCount int64 `json:"unreadCount"`
|
||||
UnreadCount int64 `json:"unread_count"`
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ import "code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
|
||||
type QuickReplyResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
GroupName string `json:"groupName"`
|
||||
GroupName string `json:"group_name"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Status enums.Status `json:"status"`
|
||||
SortNo int `json:"sortNo"`
|
||||
CreatedBy int64 `json:"createdBy"`
|
||||
SortNo int `json:"sort_no"`
|
||||
CreatedBy int64 `json:"created_by"`
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package response
|
||||
|
||||
import "time"
|
||||
|
||||
type SkillDefinitionResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Instruction string `json:"instruction"`
|
||||
Examples []string `json:"examples"`
|
||||
ToolWhitelist []string `json:"toolWhitelist"`
|
||||
Status int `json:"status"`
|
||||
StatusName string `json:"statusName"`
|
||||
Remark string `json:"remark"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
CreateUserName string `json:"createUserName"`
|
||||
UpdateUserName string `json:"updateUserName"`
|
||||
}
|
||||
|
||||
type SkillDebugRunResponse struct {
|
||||
SkillDefinitionID int64 `json:"skillDefinitionId"`
|
||||
SkillName string `json:"skillName"`
|
||||
ReplyText string `json:"replyText"`
|
||||
ToolWhitelist []string `json:"toolWhitelist"`
|
||||
InvokedToolCodes []string `json:"invokedToolCodes"`
|
||||
InterruptType string `json:"interruptType"`
|
||||
CheckPointID string `json:"checkPointId"`
|
||||
Interrupted bool `json:"interrupted"`
|
||||
TraceData string `json:"traceData"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
AIAgentID int64 `json:"aiAgentId"`
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package response
|
||||
|
||||
import "code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
|
||||
type TagResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
ParentID int64 `json:"parentId"`
|
||||
Name string `json:"name"`
|
||||
Remark string `json:"remark"`
|
||||
SortNo int `json:"sortNo"`
|
||||
Status enums.Status `json:"status"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type TagTreeResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
ParentID int64 `json:"parentId"`
|
||||
Name string `json:"name"`
|
||||
Remark string `json:"remark"`
|
||||
SortNo int `json:"sortNo"`
|
||||
Status enums.Status `json:"status"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
Children []*TagTreeResponse `json:"children"`
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package response
|
||||
|
||||
import "code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
|
||||
type TicketProgressResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
TicketID int64 `json:"ticketId"`
|
||||
Content string `json:"content"`
|
||||
AuthorID int64 `json:"authorId"`
|
||||
AuthorName string `json:"authorName,omitempty"`
|
||||
CreatedAt string `json:"createdAt,omitempty"`
|
||||
}
|
||||
|
||||
type TicketResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
TicketNo string `json:"ticketNo"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Source enums.TicketSource `json:"source"`
|
||||
Channel string `json:"channel"`
|
||||
CustomerID int64 `json:"customerId"`
|
||||
ConversationID int64 `json:"conversationId"`
|
||||
Tags []TagResponse `json:"tags,omitempty"`
|
||||
Status enums.TicketStatus `json:"status"`
|
||||
CurrentAssigneeID int64 `json:"currentAssigneeId"`
|
||||
CurrentAssigneeName string `json:"currentAssigneeName,omitempty"`
|
||||
CreatedBy int64 `json:"createdBy"`
|
||||
CreatedByName string `json:"createdByName,omitempty"`
|
||||
HandledAt string `json:"handledAt,omitempty"`
|
||||
CreatedAt string `json:"createdAt,omitempty"`
|
||||
UpdatedAt string `json:"updatedAt,omitempty"`
|
||||
Customer *CustomerResponse `json:"customer,omitempty"`
|
||||
}
|
||||
|
||||
type TicketDetailResponse struct {
|
||||
Ticket TicketResponse `json:"ticket"`
|
||||
Progresses []TicketProgressResponse `json:"progresses,omitempty"`
|
||||
}
|
||||
|
||||
type TicketSummaryResponse struct {
|
||||
All int64 `json:"all"`
|
||||
Pending int64 `json:"pending"`
|
||||
InProgress int64 `json:"inProgress"`
|
||||
Done int64 `json:"done"`
|
||||
Unassigned int64 `json:"unassigned"`
|
||||
Mine int64 `json:"mine"`
|
||||
Stale int64 `json:"stale"`
|
||||
}
|
||||
|
||||
type TicketViewResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Filters map[string]any `json:"filters,omitempty"`
|
||||
SortNo int `json:"sortNo"`
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package response
|
||||
|
||||
type WidgetConfigResponse struct {
|
||||
ChannelID string `json:"channelId"`
|
||||
ChannelType string `json:"channelType"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
ChannelType string `json:"channel_type"`
|
||||
Title string `json:"title"`
|
||||
Subtitle string `json:"subtitle"`
|
||||
ThemeColor string `json:"themeColor"`
|
||||
ThemeColor string `json:"theme_color"`
|
||||
Position string `json:"position"`
|
||||
Width string `json:"width"`
|
||||
}
|
||||
|
||||
+15
-14
@@ -240,20 +240,21 @@ func GetAIAgentFallbackModeLabel(mode AIAgentFallbackMode) string {
|
||||
}
|
||||
|
||||
const (
|
||||
IMRealtimeEventConnected = "connected"
|
||||
IMRealtimeEventPong = "pong"
|
||||
IMRealtimeEventSubscribed = "subscribed"
|
||||
IMRealtimeEventUnsubscribed = "unsubscribed"
|
||||
IMRealtimeEventResyncRequired = "resyncRequired"
|
||||
IMRealtimeEventMessageCreated = "message.created"
|
||||
IMRealtimeEventMessageRecalled = "message.recalled"
|
||||
IMRealtimeEventConversationCreated = "conversation.created"
|
||||
IMRealtimeEventConversationUpdated = "conversation.updated"
|
||||
IMRealtimeEventConversationAssigned = "conversation.assigned"
|
||||
IMRealtimeEventConversationTransferred = "conversation.transferred"
|
||||
IMRealtimeEventConversationClosed = "conversation.closed"
|
||||
IMRealtimeEventConversationRead = "conversation.read"
|
||||
IMRealtimeEventNotificationCreated = "notification.created"
|
||||
IMRealtimeEventConnected = "connected"
|
||||
IMRealtimeEventPong = "pong"
|
||||
IMRealtimeEventSubscribed = "subscribed"
|
||||
IMRealtimeEventUnsubscribed = "unsubscribed"
|
||||
IMRealtimeEventResyncRequired = "resyncRequired"
|
||||
IMRealtimeEventMessageCreated = "message.created"
|
||||
IMRealtimeEventMessageRecalled = "message.recalled"
|
||||
IMRealtimeEventConversationCreated = "conversation.created"
|
||||
IMRealtimeEventConversationUpdated = "conversation.updated"
|
||||
IMRealtimeEventConversationAssigned = "conversation.assigned"
|
||||
IMRealtimeEventConversationTransferred = "conversation.transferred"
|
||||
IMRealtimeEventConversationClosed = "conversation.closed"
|
||||
IMRealtimeEventConversationRead = "conversation.read"
|
||||
IMRealtimeEventConversationQueueUpdated = "conversation.queue_updated"
|
||||
IMRealtimeEventNotificationCreated = "notification.created"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -1,21 +1,5 @@
|
||||
package enums
|
||||
|
||||
type VectorDBType string
|
||||
|
||||
const (
|
||||
VectorDBTypeQdrant VectorDBType = "qdrant"
|
||||
VectorDBTypeLanceDB VectorDBType = "lancedb"
|
||||
)
|
||||
|
||||
var vectorDBTypeLabelMap = map[VectorDBType]string{
|
||||
VectorDBTypeQdrant: "Qdrant",
|
||||
VectorDBTypeLanceDB: "LanceDB",
|
||||
}
|
||||
|
||||
func GetVectorDBTypeLabel(dbType VectorDBType) string {
|
||||
return vectorDBTypeLabelMap[dbType]
|
||||
}
|
||||
|
||||
type KnowledgeDocumentContentType string
|
||||
|
||||
const (
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package enums
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestVectorDBTypeLabelIncludesLanceDB(t *testing.T) {
|
||||
if VectorDBTypeLanceDB != "lancedb" {
|
||||
t.Fatalf("VectorDBTypeLanceDB = %q, want %q", VectorDBTypeLanceDB, "lancedb")
|
||||
}
|
||||
if got := GetVectorDBTypeLabel(VectorDBTypeLanceDB); got != "LanceDB" {
|
||||
t.Fatalf("GetVectorDBTypeLabel(VectorDBTypeLanceDB) = %q, want %q", got, "LanceDB")
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package enums
|
||||
|
||||
type TicketStatus string
|
||||
|
||||
const (
|
||||
TicketStatusPending TicketStatus = "pending"
|
||||
TicketStatusInProgress TicketStatus = "in_progress"
|
||||
TicketStatusDone TicketStatus = "done"
|
||||
)
|
||||
|
||||
var TicketStatusValues = []TicketStatus{
|
||||
TicketStatusPending,
|
||||
TicketStatusInProgress,
|
||||
TicketStatusDone,
|
||||
}
|
||||
|
||||
var ticketStatusLabelMap = map[TicketStatus]string{
|
||||
TicketStatusPending: "待处理",
|
||||
TicketStatusInProgress: "处理中",
|
||||
TicketStatusDone: "已处理",
|
||||
}
|
||||
|
||||
func GetTicketStatusLabel(status TicketStatus) string {
|
||||
return ticketStatusLabelMap[status]
|
||||
}
|
||||
|
||||
func IsValidTicketStatus(status string) bool {
|
||||
for _, item := range TicketStatusValues {
|
||||
if string(item) == status {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type TicketSource string
|
||||
|
||||
const (
|
||||
TicketSourceManual TicketSource = "manual"
|
||||
TicketSourceConversation TicketSource = "conversation"
|
||||
)
|
||||
|
||||
var TicketSourceValues = []TicketSource{
|
||||
TicketSourceManual,
|
||||
TicketSourceConversation,
|
||||
}
|
||||
|
||||
func IsValidTicketSource(source string) bool {
|
||||
for _, item := range TicketSourceValues {
|
||||
if string(item) == source {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -27,7 +27,7 @@ func GetChannelID(ctx *gin.Context) string {
|
||||
if channelID := ctx.GetHeader("X-Channel-ID"); strs.IsNotBlank(channelID) {
|
||||
return channelID
|
||||
}
|
||||
if channelID, _ := params.Get(ctx, "channelId"); strs.IsNotBlank(channelID) {
|
||||
if channelID, _ := params.Get(ctx, "channel_id"); strs.IsNotBlank(channelID) {
|
||||
return channelID
|
||||
}
|
||||
return ""
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package httpx
|
||||
|
||||
import (
|
||||
"code.tczkiot.com/wlw/ai-agent/contract"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/errorsx"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/i18nx"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/mlogclub/simple/sqls"
|
||||
@@ -21,10 +23,32 @@ type pageData struct {
|
||||
paging *sqls.Paging
|
||||
}
|
||||
|
||||
type cursorResult struct {
|
||||
Results any `json:"results"`
|
||||
Cursor string `json:"cursor"`
|
||||
HasMore bool `json:"has_more"`
|
||||
}
|
||||
|
||||
type pageResult struct {
|
||||
Page *sqls.Paging `json:"page"`
|
||||
Results any `json:"results"`
|
||||
}
|
||||
|
||||
type localizedError interface {
|
||||
Message(locale string) string
|
||||
}
|
||||
|
||||
var (
|
||||
responseWriterMu sync.RWMutex
|
||||
responseWriter contract.ResponseWriter
|
||||
)
|
||||
|
||||
func SetResponseWriter(writer contract.ResponseWriter) {
|
||||
responseWriterMu.Lock()
|
||||
defer responseWriterMu.Unlock()
|
||||
responseWriter = writer
|
||||
}
|
||||
|
||||
func CursorData(results any, cursor string, hasMore bool) any {
|
||||
return cursorData{results: results, cursor: cursor, hasMore: hasMore}
|
||||
}
|
||||
@@ -34,11 +58,36 @@ func PageData(results any, paging *sqls.Paging) any {
|
||||
}
|
||||
|
||||
func WriteJSON(ctx *gin.Context, result any) {
|
||||
ctx.JSON(http.StatusOK, buildJSONResult(ctx, result))
|
||||
writeJSON(ctx, http.StatusOK, result)
|
||||
}
|
||||
|
||||
func WriteHttpStatusJSON(ctx *gin.Context, statusCode int, result any) {
|
||||
ctx.JSON(statusCode, buildJSONResult(ctx, result))
|
||||
writeJSON(ctx, statusCode, result)
|
||||
}
|
||||
|
||||
func AbortJSON(ctx *gin.Context, statusCode int, result any) {
|
||||
writeJSON(ctx, statusCode, result)
|
||||
ctx.Abort()
|
||||
}
|
||||
|
||||
func writeJSON(ctx *gin.Context, statusCode int, result any) {
|
||||
payload := buildJSONResult(ctx, result)
|
||||
responseWriterMu.RLock()
|
||||
writer := responseWriter
|
||||
responseWriterMu.RUnlock()
|
||||
if writer == nil {
|
||||
ctx.JSON(statusCode, payload)
|
||||
return
|
||||
}
|
||||
if err := writer(ctx.Writer, ctx.Request, contract.Response{
|
||||
StatusCode: statusCode,
|
||||
ErrorCode: payload.ErrorCode,
|
||||
Message: payload.Message,
|
||||
Data: payload.Data,
|
||||
Success: payload.Success,
|
||||
}); err != nil {
|
||||
_ = ctx.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func buildJSONResult(ctx *gin.Context, result any) *web.JsonResult {
|
||||
@@ -62,9 +111,24 @@ func buildJSONResult(ctx *gin.Context, result any) *web.JsonResult {
|
||||
case error:
|
||||
return web.JsonError(value)
|
||||
case cursorData:
|
||||
return web.JsonCursorData(value.results, value.cursor, value.hasMore)
|
||||
results := value.results
|
||||
if results == nil {
|
||||
results = []any{}
|
||||
}
|
||||
return web.JsonData(&cursorResult{
|
||||
Results: results,
|
||||
Cursor: value.cursor,
|
||||
HasMore: value.hasMore,
|
||||
})
|
||||
case pageData:
|
||||
return web.JsonPageData(value.results, value.paging)
|
||||
results := value.results
|
||||
if results == nil {
|
||||
results = []any{}
|
||||
}
|
||||
return web.JsonData(&pageResult{
|
||||
Page: value.paging,
|
||||
Results: results,
|
||||
})
|
||||
case web.RspBuilder:
|
||||
return value.JsonResult()
|
||||
case *web.RspBuilder:
|
||||
|
||||
@@ -85,6 +85,28 @@ func TestWriteJSONLocalizesI18nErrors(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteJSONUsesSnakeCaseCursorFields(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
ctx, recorder := testContext()
|
||||
|
||||
WriteJSON(ctx, CursorData([]string{"a"}, "next", true))
|
||||
|
||||
var body map[string]any
|
||||
if err := json.Unmarshal(recorder.Body.Bytes(), &body); err != nil {
|
||||
t.Fatalf("decode response: %v", err)
|
||||
}
|
||||
data, ok := body["data"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("data = %#v, want object", body["data"])
|
||||
}
|
||||
if got := data["has_more"]; got != true {
|
||||
t.Fatalf("has_more = %#v, want true", got)
|
||||
}
|
||||
if _, exists := data["hasMore"]; exists {
|
||||
t.Fatalf("camelCase hasMore should not be exposed: %s", recorder.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteHttpStatusJSONUsesProvidedStatus(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
ctx, recorder := testContext()
|
||||
|
||||
@@ -373,39 +373,18 @@ tool.builtin.toolSearch.appendix: |-
|
||||
tool.builtin.skill.title: "Load Skill Instructions"
|
||||
tool.builtin.skill.description: "Loads specialized skill instructions for the current agent when extra task-specific guidance is needed."
|
||||
tool.graph.triageServiceRequest.title: "Route Service Request"
|
||||
tool.graph.triageServiceRequest.description: "Analyzes the current conversation to decide whether to keep answering, prepare a ticket draft, or hand off to a human, including a ticket draft when ticket creation is appropriate."
|
||||
tool.graph.triageServiceRequest.description: "Analyzes the current conversation to decide whether to keep answering or hand off to a human."
|
||||
tool.graph.triageServiceRequest.appendix: |-
|
||||
When you need to decide between continuing the answer, creating a ticket, or handing off to a human, call triage_service_request first and follow these rules:
|
||||
1. The tool returns recommendedAction and includes ticketDraft when ticket creation is needed.
|
||||
2. If recommendedAction=continue_answering, continue clarifying or answering instead of escalating directly.
|
||||
3. If recommendedAction=prepare_ticket, use ticketDraft or collect missing fields before calling create_ticket_with_confirmation.
|
||||
4. If recommendedAction=handoff_to_human, confirm the reason is sufficient before calling handoff_to_human.
|
||||
5. When the escalation path is unclear, use this tool instead of making a complex routing decision from the main prompt alone.
|
||||
When you need to decide between continuing the answer or handing off to a human, call triage_service_request first.
|
||||
1. If recommendedAction=continue_answering, continue clarifying or answering.
|
||||
2. If recommendedAction=handoff_to_human, confirm the reason is sufficient before calling handoff_to_human.
|
||||
tool.graph.analyzeConversation.title: "Analyze Conversation Risk and Summary"
|
||||
tool.graph.analyzeConversation.description: "Summarizes the current conversation, identifies risk signals, and recommends whether to keep answering, create a ticket, or hand off to a human."
|
||||
tool.graph.analyzeConversation.description: "Summarizes the current conversation, identifies risk signals, and recommends whether to keep answering or hand off to a human."
|
||||
tool.graph.analyzeConversation.appendix: |-
|
||||
When the conversation may involve escalation, refunds, compensation, clear negative sentiment, ticket creation, or human handoff, call analyze_conversation first and follow these rules:
|
||||
1. This tool returns a structured summary, risk signals, and next-step recommendation. It does not create tickets or hand off to a human.
|
||||
When the conversation may involve escalation, refunds, compensation, clear negative sentiment, or human handoff, call analyze_conversation first.
|
||||
1. This tool returns a structured summary, risk signals, and next-step recommendation.
|
||||
2. If the tool recommends handoff_to_human, confirm the handoff conditions before calling handoff_to_human.
|
||||
3. If the tool recommends prepare_ticket, call prepare_ticket_draft or collect more information before creating a ticket.
|
||||
4. If the tool recommends continue_answering, continue clarifying and answering instead of escalating too early.
|
||||
tool.graph.prepareTicketDraft.title: "Prepare Ticket Draft"
|
||||
tool.graph.prepareTicketDraft.description: "Turns the current conversation and collected details into a ticket draft with a suggested title, description, missing fields, and follow-up questions."
|
||||
tool.graph.prepareTicketDraft.appendix: |-
|
||||
When the user has asked to create a ticket, file a complaint, report an issue, or request after-sales handling, but the title or description is still unclear, call prepare_ticket_draft first and follow these rules:
|
||||
1. This tool prepares a ticket draft and returns a suggested title, suggested description, missing fields, and follow-up questions.
|
||||
2. If ready=false, ask follow-up questions based on missingFields and followUpQuestions instead of creating a ticket directly.
|
||||
3. If ready=true, use the result to consider calling create_ticket_with_confirmation.
|
||||
4. This tool only prepares a draft. It does not create a ticket.
|
||||
tool.graph.createTicketConfirm.title: "Create Ticket With Confirmation"
|
||||
tool.graph.createTicketConfirm.description: "Guides ticket creation with parameter preparation, customer confirmation, actual ticket creation, and final result delivery."
|
||||
tool.graph.createTicketConfirm.appendix: |-
|
||||
You can call create_ticket_with_confirmation after enough information has been collected, but follow these rules:
|
||||
1. Only consider this tool when the user explicitly wants to submit a ticket, complaint, issue report, or after-sales request.
|
||||
2. Before calling it, prepare a clear ticket title and issue description. If the information is still scattered, call prepare_ticket_draft or ask follow-up questions first.
|
||||
3. Once you are ready to create a ticket, you must call create_ticket_with_confirmation. Do not simply claim in text that the ticket has been created.
|
||||
4. This Graph Tool asks the user for confirmation first. The ticket is created only after the user confirms; if the user cancels, the flow ends.
|
||||
5. If the user is only asking questions, complaining generally, or expressing dissatisfaction without explicitly requesting a ticket, continue clarifying instead of proactively creating one.
|
||||
3. If the tool recommends continue_answering, continue clarifying and answering instead of escalating too early.
|
||||
tool.graph.handoffConversation.title: "Handoff to Human With Confirmation"
|
||||
tool.graph.handoffConversation.description: "Guides human handoff with reason preparation, customer confirmation, actual transfer, and final result delivery."
|
||||
tool.graph.handoffConversation.appendix: |-
|
||||
@@ -416,20 +395,14 @@ tool.graph.handoffConversation.appendix: |-
|
||||
4. This Graph Tool asks the user for confirmation first. The handoff happens only after the user confirms; if the user cancels, the flow ends.
|
||||
5. If the issue can still be solved in the current conversation, continue helping instead of escalating too early.
|
||||
6. If the tool returns terminal=true and shouldRetry=false, the handoff flow has ended. Do not call it repeatedly.
|
||||
conversation.handoff.waiting: "We are connecting you to a human support agent. Please wait."
|
||||
conversation.handoff.waiting: "You are now in the human support queue. You can keep leaving messages, and the agent will see them when connected."
|
||||
conversation.handoff.offHours: "Human support is currently outside service hours. You can keep describing the issue and I will do my best to help. You can also request a human agent again when service hours resume."
|
||||
notification.ticketAssigned.title: "Ticket assigned"
|
||||
notification.ticketAssigned.line: "Ticket %s has been assigned to you."
|
||||
notification.ticketAssigned.reason: "Assignment reason: %s"
|
||||
notification.conversationTransferred.title: "Conversation transferred"
|
||||
notification.conversationAutoAssigned.title: "Conversation auto-assigned"
|
||||
notification.conversationAssigned.title: "Conversation assigned"
|
||||
notification.conversationAssigned.line: "Conversation #%s has been assigned to you."
|
||||
notification.conversationAssigned.reason: "Assignment reason: %s"
|
||||
notification.conversationTransferred.reason: "Transfer reason: %s"
|
||||
notification.ticketAssigned.wxwork.no: "Ticket no: %s"
|
||||
notification.ticketAssigned.wxwork.title: "Title: %s"
|
||||
notification.ticketAssigned.wxwork.status: "Status: %s"
|
||||
notification.assignee: "Assignee: %s"
|
||||
notification.conversationAssigned.wxwork.id: "Conversation ID: #%d"
|
||||
notification.conversationAssigned.wxwork.summary: "Summary: %s"
|
||||
@@ -439,41 +412,22 @@ notification.time: "Time: %s"
|
||||
graph.confirmOrCancel: "Please reply with \"Confirm\" or \"Cancel\"."
|
||||
graph.needExplicitConfirmation: "I need your explicit confirmation. Please reply with \"Confirm\" or \"Cancel\"."
|
||||
graph.confirmationExpired: "This confirmation has expired. Please start again."
|
||||
graph.cancelCreateTicket: "Ticket creation has been cancelled."
|
||||
graph.cancelHandoff: "Human handoff has been cancelled."
|
||||
graph.ticketCreated: "Ticket created. Ticket no: %s. Title: %s."
|
||||
graph.createTicketConfirmPrompt: |-
|
||||
I am ready to create a ticket for you.
|
||||
Title: %s
|
||||
Description: %s
|
||||
Please reply with "Confirm" or "Cancel".
|
||||
graph.defaultHandoffReason: "The user needs human support."
|
||||
graph.handoffConfirmPrompt: |-
|
||||
I am ready to connect you to a human support agent.
|
||||
Reason: %s
|
||||
Please reply with "Confirm" or "Cancel".
|
||||
conversation.interrupt.defaultPrompt: "Please provide more information and try again."
|
||||
ticket.defaultConversationTitle: "Conversation ticket"
|
||||
tool.graph.createTicketConfirm.info: "Graph Tool. Handles ticket parameter preparation, user confirmation, actual ticket creation, and result return. Use only when the user explicitly asks to create a ticket and the title and description are clear."
|
||||
tool.graph.createTicketConfirm.param.title: "Ticket title. Concisely summarizes the issue."
|
||||
tool.graph.createTicketConfirm.param.description: "Ticket description. Clearly captures the user's issue, symptoms, and request."
|
||||
tool.graph.handoffConversation.info: "Graph Tool. Handles handoff reason preparation, user confirmation, actual human handoff, and result return. Use only when the user explicitly asks for a human agent or you have confirmed that human handling is required. Do not repeat the call when the result has terminal=true and shouldRetry=false."
|
||||
tool.graph.handoffConversation.param.reason: "Handoff reason. Briefly explain why a human is needed, such as explicit user request, manual verification, or after-sales handling."
|
||||
tool.graph.triageServiceRequest.info: "Graph Tool. Analyzes the current conversation to decide whether to continue answering, prepare a ticket draft, or hand off to a human. When ticket creation is recommended, it returns a structured ticket draft suggestion."
|
||||
tool.graph.triageServiceRequest.param.goal: "Analysis goal, such as whether to escalate, create a ticket, or hand off to a human."
|
||||
tool.graph.triageServiceRequest.info: "Graph Tool. Analyzes the current conversation to decide whether to continue answering or hand off to a human."
|
||||
tool.graph.triageServiceRequest.param.goal: "Analysis goal, such as whether to escalate or hand off to a human."
|
||||
tool.graph.triageServiceRequest.param.observedIssue: "Main issue or dispute observed in the conversation."
|
||||
tool.graph.triageServiceRequest.param.needTicket: "Whether to focus on evaluating ticket creation."
|
||||
tool.graph.triageServiceRequest.param.needHumanHandoff: "Whether to focus on evaluating human handoff."
|
||||
tool.graph.triageServiceRequest.param.additionalContext: "Additional context, such as risk signals or constraints already identified."
|
||||
tool.graph.analyzeConversation.info: "Graph Tool. Summarizes the current conversation, identifies complaint/payment/sentiment risk signals, and recommends whether to continue answering, create a ticket, or hand off to a human."
|
||||
tool.graph.analyzeConversation.param.goal: "Analysis goal, such as whether to hand off to a human, create a ticket, or perform risk review."
|
||||
tool.graph.analyzeConversation.info: "Graph Tool. Summarizes the current conversation, identifies complaint/payment/sentiment risk signals, and recommends whether to continue answering or hand off to a human."
|
||||
tool.graph.analyzeConversation.param.goal: "Analysis goal, such as whether to hand off to a human or perform risk review."
|
||||
tool.graph.analyzeConversation.param.observedIssue: "Main issue or request observed in the conversation."
|
||||
tool.graph.analyzeConversation.param.needQualityCheck: "Whether to focus on risk or quality review."
|
||||
tool.graph.analyzeConversation.param.additionalContext: "Additional context, such as disputes, complaint points, or business constraints already identified."
|
||||
tool.graph.prepareTicketDraft.info: "Graph Tool. Prepares a ticket draft from the current conversation and collected information. Use it before create_ticket_with_confirmation when the ticket content needs to be organized."
|
||||
tool.graph.prepareTicketDraft.param.title: "Prepared ticket title. Optional."
|
||||
tool.graph.prepareTicketDraft.param.description: "Prepared ticket description. Optional."
|
||||
tool.graph.prepareTicketDraft.param.issue: "The issue or error message the user is experiencing."
|
||||
tool.graph.prepareTicketDraft.param.impact: "Impact scope, such as unable to sign in, unable to place an order, or business interruption."
|
||||
tool.graph.prepareTicketDraft.param.expectedOutcome: "The user's expected outcome or request."
|
||||
tool.graph.prepareTicketDraft.param.currentAttempt: "Current attempted troubleshooting steps. Optional."
|
||||
|
||||
@@ -373,39 +373,18 @@ tool.builtin.toolSearch.appendix: |-
|
||||
tool.builtin.skill.title: "加载专项技能说明"
|
||||
tool.builtin.skill.description: "用于按需加载当前 Agent 可用的专项技能说明文档,适合在需要专项处理规则时再注入上下文。"
|
||||
tool.graph.triageServiceRequest.title: "服务请求分流"
|
||||
tool.graph.triageServiceRequest.description: "Graph Tool。分析当前会话,判断应继续回答、准备工单草稿还是转人工;当适合创建工单时,也会准备工单草稿。"
|
||||
tool.graph.triageServiceRequest.description: "Graph Tool。分析当前会话,判断应继续回答还是转人工。"
|
||||
tool.graph.triageServiceRequest.appendix: |-
|
||||
当你需要在继续回答、创建工单、转人工之间做判断时,优先调用 triage_service_request,并遵守以下规则:
|
||||
1. 工具会返回 recommendedAction,并在需要创建工单时包含 ticketDraft。
|
||||
2. 如果 recommendedAction=continue_answering,继续澄清或回答,不要直接升级。
|
||||
3. 如果 recommendedAction=prepare_ticket,使用 ticketDraft 或补充缺失字段后,再考虑调用 create_ticket_with_confirmation。
|
||||
4. 如果 recommendedAction=handoff_to_human,先确认转人工原因足够明确,再调用 handoff_to_human。
|
||||
5. 当升级路径不明确时,使用此工具,而不是只依赖主提示词做复杂路由判断。
|
||||
当你需要在继续回答和转人工之间做判断时,优先调用 triage_service_request。
|
||||
1. 如果 recommendedAction=continue_answering,继续澄清或回答。
|
||||
2. 如果 recommendedAction=handoff_to_human,先确认转人工原因足够明确,再调用 handoff_to_human。
|
||||
tool.graph.analyzeConversation.title: "分析会话风险和摘要"
|
||||
tool.graph.analyzeConversation.description: "Graph Tool。总结当前会话,识别风险信号,并建议继续回答、创建工单或转人工。"
|
||||
tool.graph.analyzeConversation.description: "Graph Tool。总结当前会话,识别风险信号,并建议继续回答或转人工。"
|
||||
tool.graph.analyzeConversation.appendix: |-
|
||||
当会话可能涉及升级、退款、赔偿、明显负面情绪、创建工单或转人工时,优先调用 analyze_conversation,并遵守以下规则:
|
||||
1. 此工具返回结构化摘要、风险信号和下一步建议;它不会创建工单或执行转人工。
|
||||
当会话可能涉及升级、退款、赔偿、明显负面情绪或转人工时,优先调用 analyze_conversation。
|
||||
1. 此工具返回结构化摘要、风险信号和下一步建议。
|
||||
2. 如果工具建议 handoff_to_human,先确认转人工条件,再调用 handoff_to_human。
|
||||
3. 如果工具建议 prepare_ticket,调用 prepare_ticket_draft 或继续收集信息后再创建工单。
|
||||
4. 如果工具建议 continue_answering,继续澄清和回答,不要过早升级。
|
||||
tool.graph.prepareTicketDraft.title: "准备工单草稿"
|
||||
tool.graph.prepareTicketDraft.description: "Graph Tool。基于当前会话和已收集信息准备工单草稿,包括建议标题、描述、缺失字段和追问问题。"
|
||||
tool.graph.prepareTicketDraft.appendix: |-
|
||||
当用户要求创建工单、投诉、报障或售后处理,但标题或描述仍不清晰时,优先调用 prepare_ticket_draft,并遵守以下规则:
|
||||
1. 此工具会准备工单草稿,并返回建议标题、建议描述、缺失字段和追问问题。
|
||||
2. 如果 ready=false,根据 missingFields 和 followUpQuestions 继续追问,不要直接创建工单。
|
||||
3. 如果 ready=true,使用结果考虑调用 create_ticket_with_confirmation。
|
||||
4. 此工具只准备草稿,不会创建工单。
|
||||
tool.graph.createTicketConfirm.title: "创建工单确认流程"
|
||||
tool.graph.createTicketConfirm.description: "Graph Tool。处理工单参数准备、用户确认、实际创建工单和结果返回。"
|
||||
tool.graph.createTicketConfirm.appendix: |-
|
||||
收集到足够信息后,可以调用 create_ticket_with_confirmation,但必须遵守以下规则:
|
||||
1. 只有当用户明确想提交工单、投诉、问题报告或售后请求时,才考虑使用此工具。
|
||||
2. 调用前准备清晰的工单标题和问题描述;如果信息仍然分散,先调用 prepare_ticket_draft 或继续追问。
|
||||
3. 一旦准备创建工单,必须调用 create_ticket_with_confirmation,不要只用文本声称工单已创建。
|
||||
4. 此 Graph Tool 会先请求用户确认;只有用户确认后才会创建工单,用户取消则流程结束。
|
||||
5. 如果用户只是提问、泛泛抱怨或表达不满但没有明确要求创建工单,应继续澄清,而不是主动创建。
|
||||
3. 如果工具建议 continue_answering,继续澄清和回答,不要过早升级。
|
||||
tool.graph.handoffConversation.title: "转人工确认流程"
|
||||
tool.graph.handoffConversation.description: "Graph Tool。处理转人工原因准备、用户确认、实际转人工和结果返回。"
|
||||
tool.graph.handoffConversation.appendix: |-
|
||||
@@ -416,20 +395,14 @@ tool.graph.handoffConversation.appendix: |-
|
||||
4. 此 Graph Tool 会先请求用户确认;只有用户确认后才会转人工,用户取消则流程结束。
|
||||
5. 如果问题仍可在当前会话解决,继续帮助用户,不要过早升级。
|
||||
6. 如果工具返回 terminal=true 且 shouldRetry=false,说明转人工流程已结束,不要重复调用。
|
||||
conversation.handoff.waiting: "正在为你接入人工客服,请稍候。"
|
||||
conversation.handoff.waiting: "已为你转入人工客服队列,你可以继续留言,客服接入后会看到。"
|
||||
conversation.handoff.offHours: "当前不在人工客服服务时间内。你可以继续描述问题,我会尽力协助;也可以在服务时间恢复后再次申请人工客服。"
|
||||
notification.ticketAssigned.title: "工单指派提醒"
|
||||
notification.ticketAssigned.line: "工单 %s 已指派给你"
|
||||
notification.ticketAssigned.reason: "指派原因: %s"
|
||||
notification.conversationTransferred.title: "会话转接提醒"
|
||||
notification.conversationAutoAssigned.title: "会话自动分配提醒"
|
||||
notification.conversationAssigned.title: "会话分配提醒"
|
||||
notification.conversationAssigned.line: "会话 #%s 已分配给你"
|
||||
notification.conversationAssigned.reason: "分配原因: %s"
|
||||
notification.conversationTransferred.reason: "转接原因: %s"
|
||||
notification.ticketAssigned.wxwork.no: "工单号: %s"
|
||||
notification.ticketAssigned.wxwork.title: "工单标题: %s"
|
||||
notification.ticketAssigned.wxwork.status: "当前状态: %s"
|
||||
notification.assignee: "处理人: %s"
|
||||
notification.conversationAssigned.wxwork.id: "会话ID: #%d"
|
||||
notification.conversationAssigned.wxwork.summary: "摘要: %s"
|
||||
@@ -439,41 +412,22 @@ notification.time: "时间: %s"
|
||||
graph.confirmOrCancel: "请回复“确认”或“取消”。"
|
||||
graph.needExplicitConfirmation: "需要你明确确认。请回复“确认”或“取消”。"
|
||||
graph.confirmationExpired: "本次确认已过期,请重新发起。"
|
||||
graph.cancelCreateTicket: "已取消本次工单创建。"
|
||||
graph.cancelHandoff: "已取消本次转人工。"
|
||||
graph.ticketCreated: "工单已创建。工单号:%s。标题:%s。"
|
||||
graph.createTicketConfirmPrompt: |-
|
||||
我已准备好为你创建工单。
|
||||
标题:%s
|
||||
描述:%s
|
||||
请回复“确认”或“取消”。
|
||||
graph.defaultHandoffReason: "用户需要人工客服支持。"
|
||||
graph.handoffConfirmPrompt: |-
|
||||
我已准备好为你接入人工客服。
|
||||
原因:%s
|
||||
请回复“确认”或“取消”。
|
||||
conversation.interrupt.defaultPrompt: "请补充更多信息后再试。"
|
||||
ticket.defaultConversationTitle: "会话工单"
|
||||
tool.graph.createTicketConfirm.info: "Graph Tool。处理工单参数准备、用户确认、实际创建工单和结果返回。仅当用户明确要求创建工单且标题、描述清晰时使用。"
|
||||
tool.graph.createTicketConfirm.param.title: "工单标题。简洁概括问题。"
|
||||
tool.graph.createTicketConfirm.param.description: "工单描述。清晰记录用户的问题、现象和诉求。"
|
||||
tool.graph.handoffConversation.info: "Graph Tool。处理转人工原因准备、用户确认、实际转人工和结果返回。仅当用户明确要求人工客服,或你确认需要人工处理时使用。当结果 terminal=true 且 shouldRetry=false 时不要重复调用。"
|
||||
tool.graph.handoffConversation.param.reason: "转人工原因。简要说明为什么需要人工,例如用户明确要求、需要人工核验或售后处理。"
|
||||
tool.graph.triageServiceRequest.info: "Graph Tool。分析当前会话,判断应继续回答、准备工单草稿还是转人工。建议创建工单时,会返回结构化工单草稿建议。"
|
||||
tool.graph.triageServiceRequest.param.goal: "分析目标,例如是否升级、创建工单或转人工。"
|
||||
tool.graph.triageServiceRequest.info: "Graph Tool。分析当前会话,判断应继续回答还是转人工。"
|
||||
tool.graph.triageServiceRequest.param.goal: "分析目标,例如是否升级或转人工。"
|
||||
tool.graph.triageServiceRequest.param.observedIssue: "当前会话中观察到的主要问题或争议。"
|
||||
tool.graph.triageServiceRequest.param.needTicket: "是否重点评估创建工单。"
|
||||
tool.graph.triageServiceRequest.param.needHumanHandoff: "是否重点评估转人工。"
|
||||
tool.graph.triageServiceRequest.param.additionalContext: "补充上下文,例如已经识别的风险信号或约束。"
|
||||
tool.graph.analyzeConversation.info: "Graph Tool。总结当前会话,识别投诉、支付、情绪等风险信号,并建议继续回答、创建工单或转人工。"
|
||||
tool.graph.analyzeConversation.param.goal: "分析目标,例如是否转人工、创建工单或执行风险复核。"
|
||||
tool.graph.analyzeConversation.info: "Graph Tool。总结当前会话,识别投诉、支付、情绪等风险信号,并建议继续回答或转人工。"
|
||||
tool.graph.analyzeConversation.param.goal: "分析目标,例如是否转人工或执行风险复核。"
|
||||
tool.graph.analyzeConversation.param.observedIssue: "当前会话中观察到的主要问题或请求。"
|
||||
tool.graph.analyzeConversation.param.needQualityCheck: "是否重点进行风险或质量复核。"
|
||||
tool.graph.analyzeConversation.param.additionalContext: "补充上下文,例如争议点、投诉点或已识别的业务约束。"
|
||||
tool.graph.prepareTicketDraft.info: "Graph Tool。根据当前会话和已收集信息准备工单草稿。当工单内容需要整理时,应在 create_ticket_with_confirmation 之前使用。"
|
||||
tool.graph.prepareTicketDraft.param.title: "准备好的工单标题,可选。"
|
||||
tool.graph.prepareTicketDraft.param.description: "准备好的工单描述,可选。"
|
||||
tool.graph.prepareTicketDraft.param.issue: "用户正在遇到的问题或报错信息。"
|
||||
tool.graph.prepareTicketDraft.param.impact: "影响范围,例如无法登录、无法下单或业务中断。"
|
||||
tool.graph.prepareTicketDraft.param.expectedOutcome: "用户期望的处理结果或诉求。"
|
||||
tool.graph.prepareTicketDraft.param.currentAttempt: "当前已尝试过的处理步骤,可选。"
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package openidentity
|
||||
|
||||
import "code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
import (
|
||||
"code.tczkiot.com/wlw/ai-agent/identity"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
)
|
||||
|
||||
// ExternalUser is a be-system user identity adapted for customer-service
|
||||
// conversations. Authentication is completed by the host before this value is
|
||||
// created; this package contains no token parsing or verification.
|
||||
type ExternalUser struct {
|
||||
ExternalSource enums.ExternalSource `json:"externalSource"`
|
||||
ExternalID string `json:"externalId"`
|
||||
ExternalName string `json:"externalName"`
|
||||
ExternalSource enums.ExternalSource `json:"external_source"`
|
||||
ExternalID string `json:"external_id"`
|
||||
ExternalName string `json:"external_name"`
|
||||
SubjectType identity.SubjectType `json:"subject_type,omitempty"`
|
||||
SubjectID int64 `json:"subject_id,omitempty"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package openidentity
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExternalUserJSONUsesSnakeCaseFields(t *testing.T) {
|
||||
payload, err := json.Marshal(ExternalUser{
|
||||
ExternalID: "user:1",
|
||||
ExternalName: "test",
|
||||
SubjectID: 1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("marshal external user: %v", err)
|
||||
}
|
||||
|
||||
text := string(payload)
|
||||
for _, field := range []string{`"external_id"`, `"external_name"`, `"subject_id"`} {
|
||||
if !strings.Contains(text, field) {
|
||||
t.Fatalf("expected %s in %s", field, text)
|
||||
}
|
||||
}
|
||||
for _, field := range []string{"externalId", "externalName", "subjectId"} {
|
||||
if strings.Contains(text, field) {
|
||||
t.Fatalf("unexpected camelCase field %q in %s", field, text)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,34 +103,6 @@ var (
|
||||
Appendix: i18nx.Get("tool.graph.analyzeConversation.appendix"),
|
||||
AppendixKey: "tool.graph.analyzeConversation.appendix",
|
||||
}
|
||||
GraphPrepareTicketDraft = ToolSpec{
|
||||
Code: "graph/prepare_ticket_draft",
|
||||
ServerCode: "graph",
|
||||
Name: "prepare_ticket_draft",
|
||||
Title: i18nx.Get("tool.graph.prepareTicketDraft.title"),
|
||||
TitleKey: "tool.graph.prepareTicketDraft.title",
|
||||
Description: i18nx.Get("tool.graph.prepareTicketDraft.description"),
|
||||
DescriptionKey: "tool.graph.prepareTicketDraft.description",
|
||||
SourceType: enums.ToolSourceTypeGraph,
|
||||
DirectAccess: true,
|
||||
RuntimeStatic: true,
|
||||
Appendix: i18nx.Get("tool.graph.prepareTicketDraft.appendix"),
|
||||
AppendixKey: "tool.graph.prepareTicketDraft.appendix",
|
||||
}
|
||||
GraphCreateTicketConfirm = ToolSpec{
|
||||
Code: "graph/create_ticket_with_confirmation",
|
||||
ServerCode: "graph",
|
||||
Name: "create_ticket_with_confirmation",
|
||||
Title: i18nx.Get("tool.graph.createTicketConfirm.title"),
|
||||
TitleKey: "tool.graph.createTicketConfirm.title",
|
||||
Description: i18nx.Get("tool.graph.createTicketConfirm.description"),
|
||||
DescriptionKey: "tool.graph.createTicketConfirm.description",
|
||||
SourceType: enums.ToolSourceTypeGraph,
|
||||
RuntimeStatic: true,
|
||||
Aliases: []string{"builtin/create_ticket_with_confirmation"},
|
||||
Appendix: i18nx.Get("tool.graph.createTicketConfirm.appendix"),
|
||||
AppendixKey: "tool.graph.createTicketConfirm.appendix",
|
||||
}
|
||||
GraphHandoffConversation = ToolSpec{
|
||||
Code: "graph/handoff_to_human",
|
||||
ServerCode: "graph",
|
||||
@@ -151,8 +123,6 @@ var (
|
||||
BuiltinKnowledgeRetrieve,
|
||||
GraphTriageServiceRequest,
|
||||
GraphAnalyzeConversation,
|
||||
GraphPrepareTicketDraft,
|
||||
GraphCreateTicketConfirm,
|
||||
GraphHandoffConversation,
|
||||
}
|
||||
)
|
||||
@@ -401,15 +371,9 @@ func IsImpliedAllowedToolCode(toolCode string, allowedToolCodes map[string]struc
|
||||
}
|
||||
switch toolCode {
|
||||
case GraphTriageServiceRequest.Code, GraphAnalyzeConversation.Code:
|
||||
if _, ok := allowedToolCodes[GraphCreateTicketConfirm.Code]; ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := allowedToolCodes[GraphHandoffConversation.Code]; ok {
|
||||
return true
|
||||
}
|
||||
case GraphPrepareTicketDraft.Code:
|
||||
_, ok := allowedToolCodes[GraphCreateTicketConfirm.Code]
|
||||
return ok
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
package toolx
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/i18nx"
|
||||
)
|
||||
import "testing"
|
||||
|
||||
func TestResolveToolMetadata(t *testing.T) {
|
||||
item := ResolveToolMetadata("builtin/create_ticket_with_confirmation", "")
|
||||
if item.ToolCode != GraphCreateTicketConfirm.Code {
|
||||
item := ResolveToolMetadata(GraphHandoffConversation.Code, "")
|
||||
if item.ToolCode != GraphHandoffConversation.Code {
|
||||
t.Fatalf("unexpected tool code: %s", item.ToolCode)
|
||||
}
|
||||
if item.ServerCode != GraphCreateTicketConfirm.ServerCode {
|
||||
if item.ServerCode != GraphHandoffConversation.ServerCode {
|
||||
t.Fatalf("unexpected server code: %s", item.ServerCode)
|
||||
}
|
||||
if item.ToolName != GraphCreateTicketConfirm.Name {
|
||||
if item.ToolName != GraphHandoffConversation.Name {
|
||||
t.Fatalf("unexpected tool name: %s", item.ToolName)
|
||||
}
|
||||
if item.SourceType != GraphCreateTicketConfirm.SourceType {
|
||||
if item.SourceType != GraphHandoffConversation.SourceType {
|
||||
t.Fatalf("unexpected source type: %s", item.SourceType)
|
||||
}
|
||||
}
|
||||
@@ -37,28 +33,3 @@ func TestResolveToolMetadataFallsBackToName(t *testing.T) {
|
||||
t.Fatalf("unexpected source type: %s", item.SourceType)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisteredToolTextUsesEnglishLocale(t *testing.T) {
|
||||
title := GetRegisteredToolTitleLocale(GraphCreateTicketConfirm.Code, i18nx.LocaleEnUS)
|
||||
if title != "Create Ticket With Confirmation" {
|
||||
t.Fatalf("unexpected english title: %q", title)
|
||||
}
|
||||
|
||||
description := GetRegisteredToolDescriptionLocale(GraphCreateTicketConfirm.Code, i18nx.LocaleEnUS)
|
||||
want := "Guides ticket creation with parameter preparation, customer confirmation, actual ticket creation, and final result delivery."
|
||||
if description != want {
|
||||
t.Fatalf("unexpected english description: %q", description)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisteredToolTextKeepsChineseLocale(t *testing.T) {
|
||||
title := GetRegisteredToolTitleLocale(GraphCreateTicketConfirm.Code, i18nx.LocaleZhCN)
|
||||
if title != "创建工单确认流程" {
|
||||
t.Fatalf("unexpected chinese title: %q", title)
|
||||
}
|
||||
|
||||
description := GetRegisteredToolDescriptionLocale(GraphCreateTicketConfirm.Code, i18nx.LocaleZhCN)
|
||||
if description != "Graph Tool。处理工单参数准备、用户确认、实际创建工单和结果返回。" {
|
||||
t.Fatalf("unexpected chinese description: %q", description)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
package toolx
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/dto/request"
|
||||
)
|
||||
|
||||
const (
|
||||
MCPRiskLevelRead = "read"
|
||||
MCPRiskLevelWrite = "write"
|
||||
)
|
||||
|
||||
type TrustedMCPToolPolicy struct {
|
||||
ToolCode string
|
||||
Title string
|
||||
RiskLevel string
|
||||
RequireConfirmation bool
|
||||
}
|
||||
|
||||
var trustedMCPToolPolicies = map[string]TrustedMCPToolPolicy{
|
||||
"system/server_time": {
|
||||
ToolCode: "system/server_time",
|
||||
Title: "获取当前时间",
|
||||
RiskLevel: MCPRiskLevelRead,
|
||||
RequireConfirmation: false,
|
||||
},
|
||||
"system/service_info": {
|
||||
ToolCode: "system/service_info",
|
||||
Title: "查看服务信息",
|
||||
RiskLevel: MCPRiskLevelRead,
|
||||
RequireConfirmation: false,
|
||||
},
|
||||
}
|
||||
|
||||
func GetTrustedMCPToolPolicy(toolCode string) (TrustedMCPToolPolicy, bool) {
|
||||
policy, ok := trustedMCPToolPolicies[NormalizeToolCodeAlias(strings.TrimSpace(toolCode))]
|
||||
return policy, ok
|
||||
}
|
||||
|
||||
func ApplyTrustedMCPToolPolicy(item request.AIAgentMCPToolRequest) request.AIAgentMCPToolRequest {
|
||||
policy, ok := GetTrustedMCPToolPolicy(item.ToolCode)
|
||||
if !ok {
|
||||
return item
|
||||
}
|
||||
item.ToolCode = policy.ToolCode
|
||||
item.Title = policy.Title
|
||||
item.RiskLevel = policy.RiskLevel
|
||||
item.RequireConfirmation = policy.RequireConfirmation
|
||||
return item
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
package toolx
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/dto/request"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/errorsx"
|
||||
)
|
||||
|
||||
func BuildMCPToolCode(serverCode, toolName string) string {
|
||||
serverCode = strings.TrimSpace(serverCode)
|
||||
toolName = strings.TrimSpace(toolName)
|
||||
if serverCode == "" || toolName == "" {
|
||||
return ""
|
||||
}
|
||||
return serverCode + "/" + toolName
|
||||
}
|
||||
|
||||
func SplitMCPToolCode(toolCode string) (string, string) {
|
||||
toolCode = strings.TrimSpace(toolCode)
|
||||
if toolCode == "" {
|
||||
return "", ""
|
||||
}
|
||||
idx := strings.Index(toolCode, "/")
|
||||
if idx <= 0 || idx >= len(toolCode)-1 {
|
||||
return "", ""
|
||||
}
|
||||
return strings.TrimSpace(toolCode[:idx]), strings.TrimSpace(toolCode[idx+1:])
|
||||
}
|
||||
|
||||
func NormalizeMCPToolRequest(item request.AIAgentMCPToolRequest) (request.AIAgentMCPToolRequest, error) {
|
||||
toolCode := strings.TrimSpace(item.ToolCode)
|
||||
toolCode = NormalizeToolCodeAlias(toolCode)
|
||||
serverCode := strings.TrimSpace(item.ServerCode)
|
||||
toolName := strings.TrimSpace(item.ToolName)
|
||||
if toolCode != "" {
|
||||
parsedServerCode, parsedToolName := SplitMCPToolCode(toolCode)
|
||||
if parsedServerCode != "" && parsedToolName != "" {
|
||||
if serverCode != "" && !strings.EqualFold(serverCode, parsedServerCode) {
|
||||
return request.AIAgentMCPToolRequest{}, errorsx.InvalidParamI18n("error.e0016")
|
||||
}
|
||||
if toolName != "" && !strings.EqualFold(toolName, parsedToolName) {
|
||||
return request.AIAgentMCPToolRequest{}, errorsx.InvalidParamI18n("error.e0017")
|
||||
}
|
||||
serverCode = parsedServerCode
|
||||
toolName = parsedToolName
|
||||
} else {
|
||||
serverCode = ""
|
||||
toolName = ""
|
||||
}
|
||||
} else {
|
||||
toolCode = BuildMCPToolCode(serverCode, toolName)
|
||||
}
|
||||
if toolCode == "" {
|
||||
return request.AIAgentMCPToolRequest{}, errorsx.InvalidParamI18n("error.e0019")
|
||||
}
|
||||
if parsedServerCode, parsedToolName := SplitMCPToolCode(toolCode); parsedServerCode != "" && parsedToolName != "" {
|
||||
serverCode = parsedServerCode
|
||||
toolName = parsedToolName
|
||||
}
|
||||
if serverCode == "" && toolName == "" && strings.Contains(toolCode, "/") && !strings.HasPrefix(toolCode, "builtin/") {
|
||||
return request.AIAgentMCPToolRequest{}, errorsx.InvalidParamI18n("error.e0018")
|
||||
}
|
||||
ret := request.AIAgentMCPToolRequest{
|
||||
ToolCode: toolCode,
|
||||
ServerCode: serverCode,
|
||||
ToolName: toolName,
|
||||
Title: strings.TrimSpace(item.Title),
|
||||
Description: strings.TrimSpace(item.Description),
|
||||
RiskLevel: strings.ToLower(strings.TrimSpace(item.RiskLevel)),
|
||||
RequireConfirmation: item.RequireConfirmation,
|
||||
}
|
||||
if len(item.Arguments) > 0 {
|
||||
ret.Arguments = make(map[string]string, len(item.Arguments))
|
||||
for key, value := range item.Arguments {
|
||||
key = strings.TrimSpace(key)
|
||||
value = strings.TrimSpace(value)
|
||||
if key == "" || value == "" {
|
||||
continue
|
||||
}
|
||||
ret.Arguments[key] = value
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func ParseAgentMCPToolsJSON(raw string) ([]request.AIAgentMCPToolRequest, error) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return nil, nil
|
||||
}
|
||||
var ret []request.AIAgentMCPToolRequest
|
||||
if err := json.Unmarshal([]byte(raw), &ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/models"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/repositories"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/services/storage"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
@@ -16,13 +16,28 @@ import (
|
||||
)
|
||||
|
||||
type imMessageAssetPayload struct {
|
||||
AssetID string `json:"assetId"`
|
||||
Provider enums.AssetProvider `json:"provider,omitempty"`
|
||||
StorageKey string `json:"storageKey,omitempty"`
|
||||
Filename string `json:"filename,omitempty"`
|
||||
FileSize int64 `json:"fileSize,omitempty"`
|
||||
MimeType string `json:"mimeType,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
AssetID string `json:"asset_id,omitempty"`
|
||||
Provider enums.AssetProvider `json:"provider,omitempty"`
|
||||
StorageKey string `json:"storage_key,omitempty"`
|
||||
Filename string `json:"filename,omitempty"`
|
||||
FileSize int64 `json:"file_size,omitempty"`
|
||||
MimeType string `json:"mime_type,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Assets []imMessageAssetPayload `json:"assets,omitempty"`
|
||||
}
|
||||
|
||||
func (p *imMessageAssetPayload) items() []*imMessageAssetPayload {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
if len(p.Assets) == 0 {
|
||||
return []*imMessageAssetPayload{p}
|
||||
}
|
||||
items := make([]*imMessageAssetPayload, 0, len(p.Assets))
|
||||
for index := range p.Assets {
|
||||
items = append(items, &p.Assets[index])
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func SanitizeMessageHTML(content string) string {
|
||||
@@ -111,8 +126,8 @@ func BuildRuntimeMessageText(messageType enums.IMMessageType, content string) st
|
||||
case enums.IMMessageTypeHTML:
|
||||
return BuildHTMLSummary(content)
|
||||
case enums.IMMessageTypeImage:
|
||||
if content != "" {
|
||||
return "[图片] " + content
|
||||
if text := BuildHTMLSummary(content); text != "" {
|
||||
return "[图片] " + text
|
||||
}
|
||||
return "[图片]"
|
||||
case enums.IMMessageTypeAttachment:
|
||||
@@ -286,10 +301,12 @@ func buildIMMessageAssetPayloadForResponse(payload string) string {
|
||||
if err != nil {
|
||||
return strings.TrimSpace(payload)
|
||||
}
|
||||
assetPayload = hydrateIMMessageAssetPayload(assetPayload)
|
||||
if assetPayload.Provider != "" && assetPayload.StorageKey != "" {
|
||||
if provider, err := storage.NewProvider(assetPayload.Provider); err == nil {
|
||||
assetPayload.URL = provider.GetSignedURL(assetPayload.StorageKey)
|
||||
for _, item := range assetPayload.items() {
|
||||
hydrateIMMessageAssetPayload(item)
|
||||
if item.Provider != "" && item.StorageKey != "" {
|
||||
if provider, err := storage.NewProvider(item.Provider); err == nil {
|
||||
item.URL = provider.GetSignedURL(item.StorageKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
data, err := json.Marshal(assetPayload)
|
||||
@@ -308,9 +325,11 @@ func parseIMMessageAssetPayload(payload string) (*imMessageAssetPayload, error)
|
||||
if err := json.Unmarshal([]byte(payload), ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret.AssetID = strings.TrimSpace(ret.AssetID)
|
||||
ret.Provider = enums.AssetProvider(strings.TrimSpace(string(ret.Provider)))
|
||||
ret.StorageKey = strings.TrimSpace(ret.StorageKey)
|
||||
for _, item := range ret.items() {
|
||||
item.AssetID = strings.TrimSpace(item.AssetID)
|
||||
item.Provider = enums.AssetProvider(strings.TrimSpace(string(item.Provider)))
|
||||
item.StorageKey = strings.TrimSpace(item.StorageKey)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -23,14 +23,14 @@ func TestBuildIMMessageAssetPayloadForResponseAddsSignedURL(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
payload := `{"assetId":"asset_1","provider":"local","storageKey":"attachments/demo.png","filename":"demo.png"}`
|
||||
payload := `{"asset_id":"asset_1","provider":"local","storage_key":"attachments/demo.png","filename":"demo.png"}`
|
||||
got := buildIMMessageAssetPayloadForResponse(payload)
|
||||
|
||||
if !strings.Contains(got, `"provider":"local"`) {
|
||||
t.Fatalf("expected provider in payload, got: %s", got)
|
||||
}
|
||||
if !strings.Contains(got, `"storageKey":"attachments/demo.png"`) {
|
||||
t.Fatalf("expected storageKey in payload, got: %s", got)
|
||||
if !strings.Contains(got, `"storage_key":"attachments/demo.png"`) {
|
||||
t.Fatalf("expected storage_key in payload, got: %s", got)
|
||||
}
|
||||
if !strings.Contains(got, `"url":"https://files.example.com/attachments/demo.png"`) {
|
||||
t.Fatalf("expected signed url in payload, got: %s", got)
|
||||
@@ -99,7 +99,7 @@ func TestBuildRenderableMessageTransformsPayloadAndHTML(t *testing.T) {
|
||||
|
||||
image := &models.Message{
|
||||
MessageType: enums.IMMessageTypeImage,
|
||||
Payload: `{"assetId":"asset_1","provider":"local","storageKey":"attachments/demo.png","filename":"demo.png"}`,
|
||||
Payload: `{"asset_id":"asset_1","provider":"local","storage_key":"attachments/demo.png","filename":"demo.png"}`,
|
||||
}
|
||||
_, imagePayload := BuildRenderableMessage(image)
|
||||
if !strings.Contains(imagePayload, `"url":"https://files.example.com/attachments/demo.png"`) {
|
||||
|
||||
Reference in New Issue
Block a user