Add files via upload

This commit is contained in:
公明
2025-11-23 19:46:18 +08:00
committed by GitHub
parent 3304e1996a
commit a8bc32aefb
7 changed files with 138 additions and 45 deletions
+6
View File
@@ -1283,6 +1283,12 @@ func (a *Agent) UpdateConfig(cfg *config.OpenAIConfig) {
a.mu.Lock() a.mu.Lock()
defer a.mu.Unlock() defer a.mu.Unlock()
a.config = cfg a.config = cfg
// 同时更新MemoryCompressor的配置(如果存在)
if a.memoryCompressor != nil {
a.memoryCompressor.UpdateConfig(cfg)
}
a.logger.Info("Agent配置已更新", a.logger.Info("Agent配置已更新",
zap.String("base_url", cfg.BaseURL), zap.String("base_url", cfg.BaseURL),
zap.String("model", cfg.Model), zap.String("model", cfg.Model),
+37 -2
View File
@@ -138,6 +138,26 @@ func NewMemoryCompressor(cfg MemoryCompressorConfig) (*MemoryCompressor, error)
}, nil }, nil
} }
// UpdateConfig 更新OpenAI配置(用于动态更新模型配置)
func (mc *MemoryCompressor) UpdateConfig(cfg *config.OpenAIConfig) {
if cfg == nil {
return
}
// 更新summaryModel字段
if cfg.Model != "" {
mc.summaryModel = cfg.Model
}
// 更新completionClient中的配置(如果是OpenAICompletionClient
if openAIClient, ok := mc.completionClient.(*OpenAICompletionClient); ok {
openAIClient.config = cfg
mc.logger.Info("MemoryCompressor配置已更新",
zap.String("model", cfg.Model),
)
}
}
// CompressHistory 根据Token限制压缩历史消息。 // CompressHistory 根据Token限制压缩历史消息。
func (mc *MemoryCompressor) CompressHistory(ctx context.Context, messages []ChatMessage) ([]ChatMessage, bool, error) { func (mc *MemoryCompressor) CompressHistory(ctx context.Context, messages []ChatMessage) ([]ChatMessage, bool, error) {
if len(messages) == 0 { if len(messages) == 0 {
@@ -238,11 +258,24 @@ func (mc *MemoryCompressor) countTotalTokens(systemMsgs, regularMsgs []ChatMessa
return total return total
} }
// getModelName 获取当前使用的模型名称(优先从completionClient获取最新配置)
func (mc *MemoryCompressor) getModelName() string {
// 如果completionClient是OpenAICompletionClient,从它获取最新的模型名称
if openAIClient, ok := mc.completionClient.(*OpenAICompletionClient); ok {
if openAIClient.config != nil && openAIClient.config.Model != "" {
return openAIClient.config.Model
}
}
// 否则使用保存的summaryModel
return mc.summaryModel
}
func (mc *MemoryCompressor) countTokens(text string) int { func (mc *MemoryCompressor) countTokens(text string) int {
if mc.tokenCounter == nil { if mc.tokenCounter == nil {
return len(text) / 4 return len(text) / 4
} }
count, err := mc.tokenCounter.Count(mc.summaryModel, text) modelName := mc.getModelName()
count, err := mc.tokenCounter.Count(modelName, text)
if err != nil { if err != nil {
return len(text) / 4 return len(text) / 4
} }
@@ -269,7 +302,9 @@ func (mc *MemoryCompressor) summarizeChunk(ctx context.Context, chunk []ChatMess
conversation := strings.Join(formatted, "\n") conversation := strings.Join(formatted, "\n")
prompt := fmt.Sprintf(summaryPromptTemplate, conversation) prompt := fmt.Sprintf(summaryPromptTemplate, conversation)
summary, err := mc.completionClient.Complete(ctx, mc.summaryModel, prompt, mc.timeout) // 使用动态获取的模型名称,而不是保存的summaryModel
modelName := mc.getModelName()
summary, err := mc.completionClient.Complete(ctx, modelName, prompt, mc.timeout)
if err != nil { if err != nil {
return ChatMessage{}, err return ChatMessage{}, err
} }
+2 -2
View File
@@ -128,9 +128,9 @@ func New(cfg *config.Config, log *logger.Logger) (*App, error) {
monitorHandler.SetExternalMCPManager(externalMCPMgr) // 设置外部MCP管理器,以便获取外部MCP执行记录 monitorHandler.SetExternalMCPManager(externalMCPMgr) // 设置外部MCP管理器,以便获取外部MCP执行记录
conversationHandler := handler.NewConversationHandler(db, log.Logger) conversationHandler := handler.NewConversationHandler(db, log.Logger)
authHandler := handler.NewAuthHandler(authManager, cfg, configPath, log.Logger) authHandler := handler.NewAuthHandler(authManager, cfg, configPath, log.Logger)
configHandler := handler.NewConfigHandler(configPath, cfg, mcpServer, executor, agent, externalMCPMgr, log.Logger)
externalMCPHandler := handler.NewExternalMCPHandler(externalMCPMgr, cfg, configPath, log.Logger)
attackChainHandler := handler.NewAttackChainHandler(db, &cfg.OpenAI, log.Logger) attackChainHandler := handler.NewAttackChainHandler(db, &cfg.OpenAI, log.Logger)
configHandler := handler.NewConfigHandler(configPath, cfg, mcpServer, executor, agent, attackChainHandler, externalMCPMgr, log.Logger)
externalMCPHandler := handler.NewExternalMCPHandler(externalMCPMgr, cfg, configPath, log.Logger)
// 设置路由 // 设置路由
setupRoutes( setupRoutes(
+14 -15
View File
@@ -74,7 +74,7 @@ func (db *DB) GetConversation(id string) (*Conversation, error) {
conv.CreatedAt, err1 = time.Parse("2006-01-02 15:04:05", createdAt) conv.CreatedAt, err1 = time.Parse("2006-01-02 15:04:05", createdAt)
} }
if err1 != nil { if err1 != nil {
conv.CreatedAt, err1 = time.Parse(time.RFC3339, createdAt) conv.CreatedAt, _ = time.Parse(time.RFC3339, createdAt)
} }
conv.UpdatedAt, err2 = time.Parse("2006-01-02 15:04:05.999999999-07:00", updatedAt) conv.UpdatedAt, err2 = time.Parse("2006-01-02 15:04:05.999999999-07:00", updatedAt)
@@ -82,7 +82,7 @@ func (db *DB) GetConversation(id string) (*Conversation, error) {
conv.UpdatedAt, err2 = time.Parse("2006-01-02 15:04:05", updatedAt) conv.UpdatedAt, err2 = time.Parse("2006-01-02 15:04:05", updatedAt)
} }
if err2 != nil { if err2 != nil {
conv.UpdatedAt, err2 = time.Parse(time.RFC3339, updatedAt) conv.UpdatedAt, _ = time.Parse(time.RFC3339, updatedAt)
} }
// 加载消息 // 加载消息
@@ -155,7 +155,7 @@ func (db *DB) ListConversations(limit, offset int) ([]*Conversation, error) {
conv.CreatedAt, err1 = time.Parse("2006-01-02 15:04:05", createdAt) conv.CreatedAt, err1 = time.Parse("2006-01-02 15:04:05", createdAt)
} }
if err1 != nil { if err1 != nil {
conv.CreatedAt, err1 = time.Parse(time.RFC3339, createdAt) conv.CreatedAt, _ = time.Parse(time.RFC3339, createdAt)
} }
conv.UpdatedAt, err2 = time.Parse("2006-01-02 15:04:05.999999999-07:00", updatedAt) conv.UpdatedAt, err2 = time.Parse("2006-01-02 15:04:05.999999999-07:00", updatedAt)
@@ -163,7 +163,7 @@ func (db *DB) ListConversations(limit, offset int) ([]*Conversation, error) {
conv.UpdatedAt, err2 = time.Parse("2006-01-02 15:04:05", updatedAt) conv.UpdatedAt, err2 = time.Parse("2006-01-02 15:04:05", updatedAt)
} }
if err2 != nil { if err2 != nil {
conv.UpdatedAt, err2 = time.Parse(time.RFC3339, updatedAt) conv.UpdatedAt, _ = time.Parse(time.RFC3339, updatedAt)
} }
conversations = append(conversations, &conv) conversations = append(conversations, &conv)
@@ -272,7 +272,7 @@ func (db *DB) GetMessages(conversationID string) ([]Message, error) {
msg.CreatedAt, err = time.Parse("2006-01-02 15:04:05", createdAt) msg.CreatedAt, err = time.Parse("2006-01-02 15:04:05", createdAt)
} }
if err != nil { if err != nil {
msg.CreatedAt, err = time.Parse(time.RFC3339, createdAt) msg.CreatedAt, _ = time.Parse(time.RFC3339, createdAt)
} }
// 解析MCP执行ID // 解析MCP执行ID
@@ -290,13 +290,13 @@ func (db *DB) GetMessages(conversationID string) ([]Message, error) {
// ProcessDetail 过程详情事件 // ProcessDetail 过程详情事件
type ProcessDetail struct { type ProcessDetail struct {
ID string `json:"id"` ID string `json:"id"`
MessageID string `json:"messageId"` MessageID string `json:"messageId"`
ConversationID string `json:"conversationId"` ConversationID string `json:"conversationId"`
EventType string `json:"eventType"` // iteration, thinking, tool_calls_detected, tool_call, tool_result, progress, error EventType string `json:"eventType"` // iteration, thinking, tool_calls_detected, tool_call, tool_result, progress, error
Message string `json:"message"` Message string `json:"message"`
Data string `json:"data"` // JSON格式的数据 Data string `json:"data"` // JSON格式的数据
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
} }
// AddProcessDetail 添加过程详情事件 // AddProcessDetail 添加过程详情事件
@@ -351,7 +351,7 @@ func (db *DB) GetProcessDetails(messageID string) ([]ProcessDetail, error) {
detail.CreatedAt, err = time.Parse("2006-01-02 15:04:05", createdAt) detail.CreatedAt, err = time.Parse("2006-01-02 15:04:05", createdAt)
} }
if err != nil { if err != nil {
detail.CreatedAt, err = time.Parse(time.RFC3339, createdAt) detail.CreatedAt, _ = time.Parse(time.RFC3339, createdAt)
} }
details = append(details, detail) details = append(details, detail)
@@ -387,7 +387,7 @@ func (db *DB) GetProcessDetailsByConversation(conversationID string) (map[string
detail.CreatedAt, err = time.Parse("2006-01-02 15:04:05", createdAt) detail.CreatedAt, err = time.Parse("2006-01-02 15:04:05", createdAt)
} }
if err != nil { if err != nil {
detail.CreatedAt, err = time.Parse(time.RFC3339, createdAt) detail.CreatedAt, _ = time.Parse(time.RFC3339, createdAt)
} }
detailsMap[detail.MessageID] = append(detailsMap[detail.MessageID], detail) detailsMap[detail.MessageID] = append(detailsMap[detail.MessageID], detail)
@@ -395,4 +395,3 @@ func (db *DB) GetProcessDetailsByConversation(conversationID string) (map[string
return detailsMap, nil return detailsMap, nil
} }
+23 -2
View File
@@ -19,6 +19,7 @@ type AttackChainHandler struct {
db *database.DB db *database.DB
logger *zap.Logger logger *zap.Logger
openAIConfig *config.OpenAIConfig openAIConfig *config.OpenAIConfig
mu sync.RWMutex // 保护 openAIConfig 的并发访问
// 用于防止同一对话的并发生成 // 用于防止同一对话的并发生成
generatingLocks sync.Map // map[string]*sync.Mutex generatingLocks sync.Map // map[string]*sync.Mutex
} }
@@ -32,6 +33,24 @@ func NewAttackChainHandler(db *database.DB, openAIConfig *config.OpenAIConfig, l
} }
} }
// UpdateConfig 更新OpenAI配置
func (h *AttackChainHandler) UpdateConfig(cfg *config.OpenAIConfig) {
h.mu.Lock()
defer h.mu.Unlock()
h.openAIConfig = cfg
h.logger.Info("AttackChainHandler配置已更新",
zap.String("base_url", cfg.BaseURL),
zap.String("model", cfg.Model),
)
}
// getOpenAIConfig 获取OpenAI配置(线程安全)
func (h *AttackChainHandler) getOpenAIConfig() *config.OpenAIConfig {
h.mu.RLock()
defer h.mu.RUnlock()
return h.openAIConfig
}
// GetAttackChain 获取攻击链(按需生成) // GetAttackChain 获取攻击链(按需生成)
// GET /api/attack-chain/:conversationId // GET /api/attack-chain/:conversationId
func (h *AttackChainHandler) GetAttackChain(c *gin.Context) { func (h *AttackChainHandler) GetAttackChain(c *gin.Context) {
@@ -50,7 +69,8 @@ func (h *AttackChainHandler) GetAttackChain(c *gin.Context) {
} }
// 先尝试从数据库加载(如果已生成过) // 先尝试从数据库加载(如果已生成过)
builder := attackchain.NewBuilder(h.db, h.openAIConfig, h.logger) openAIConfig := h.getOpenAIConfig()
builder := attackchain.NewBuilder(h.db, openAIConfig, h.logger)
chain, err := builder.LoadChainFromDatabase(conversationID) chain, err := builder.LoadChainFromDatabase(conversationID)
if err == nil && len(chain.Nodes) > 0 { if err == nil && len(chain.Nodes) > 0 {
// 如果已存在,直接返回 // 如果已存在,直接返回
@@ -139,7 +159,8 @@ func (h *AttackChainHandler) RegenerateAttackChain(c *gin.Context) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel() defer cancel()
builder := attackchain.NewBuilder(h.db, h.openAIConfig, h.logger) openAIConfig := h.getOpenAIConfig()
builder := attackchain.NewBuilder(h.db, openAIConfig, h.logger)
chain, err := builder.BuildChainFromConversation(ctx, conversationID) chain, err := builder.BuildChainFromConversation(ctx, conversationID)
if err != nil { if err != nil {
h.logger.Error("生成攻击链失败", zap.String("conversationId", conversationID), zap.Error(err)) h.logger.Error("生成攻击链失败", zap.String("conversationId", conversationID), zap.Error(err))
+29 -16
View File
@@ -22,14 +22,20 @@ import (
// ConfigHandler 配置处理器 // ConfigHandler 配置处理器
type ConfigHandler struct { type ConfigHandler struct {
configPath string configPath string
config *config.Config config *config.Config
mcpServer *mcp.Server mcpServer *mcp.Server
executor *security.Executor executor *security.Executor
agent AgentUpdater // Agent接口,用于更新Agent配置 agent AgentUpdater // Agent接口,用于更新Agent配置
externalMCPMgr *mcp.ExternalMCPManager // 外部MCP管理器 attackChainHandler AttackChainUpdater // 攻击链处理器接口,用于更新配置
logger *zap.Logger externalMCPMgr *mcp.ExternalMCPManager // 外部MCP管理器
mu sync.RWMutex logger *zap.Logger
mu sync.RWMutex
}
// AttackChainUpdater 攻击链处理器更新接口
type AttackChainUpdater interface {
UpdateConfig(cfg *config.OpenAIConfig)
} }
// AgentUpdater Agent更新接口 // AgentUpdater Agent更新接口
@@ -39,15 +45,16 @@ type AgentUpdater interface {
} }
// NewConfigHandler 创建新的配置处理器 // NewConfigHandler 创建新的配置处理器
func NewConfigHandler(configPath string, cfg *config.Config, mcpServer *mcp.Server, executor *security.Executor, agent AgentUpdater, externalMCPMgr *mcp.ExternalMCPManager, logger *zap.Logger) *ConfigHandler { func NewConfigHandler(configPath string, cfg *config.Config, mcpServer *mcp.Server, executor *security.Executor, agent AgentUpdater, attackChainHandler AttackChainUpdater, externalMCPMgr *mcp.ExternalMCPManager, logger *zap.Logger) *ConfigHandler {
return &ConfigHandler{ return &ConfigHandler{
configPath: configPath, configPath: configPath,
config: cfg, config: cfg,
mcpServer: mcpServer, mcpServer: mcpServer,
executor: executor, executor: executor,
agent: agent, agent: agent,
externalMCPMgr: externalMCPMgr, attackChainHandler: attackChainHandler,
logger: logger, externalMCPMgr: externalMCPMgr,
logger: logger,
} }
} }
@@ -522,6 +529,12 @@ func (h *ConfigHandler) ApplyConfig(c *gin.Context) {
h.logger.Info("Agent配置已更新") h.logger.Info("Agent配置已更新")
} }
// 更新AttackChainHandler的OpenAI配置
if h.attackChainHandler != nil {
h.attackChainHandler.UpdateConfig(&h.config.OpenAI)
h.logger.Info("AttackChainHandler配置已更新")
}
h.logger.Info("配置已应用", h.logger.Info("配置已应用",
zap.Int("tools_count", len(h.config.Security.Tools)), zap.Int("tools_count", len(h.config.Security.Tools)),
) )
+22 -3
View File
@@ -506,7 +506,7 @@ function initializeChatUI() {
let messageCounter = 0; let messageCounter = 0;
// 添加消息 // 添加消息
function addMessage(role, content, mcpExecutionIds = null, progressId = null) { function addMessage(role, content, mcpExecutionIds = null, progressId = null, createdAt = null) {
const messagesDiv = document.getElementById('chat-messages'); const messagesDiv = document.getElementById('chat-messages');
const messageDiv = document.createElement('div'); const messageDiv = document.createElement('div');
messageCounter++; messageCounter++;
@@ -582,7 +582,25 @@ function addMessage(role, content, mcpExecutionIds = null, progressId = null) {
// 添加时间戳 // 添加时间戳
const timeDiv = document.createElement('div'); const timeDiv = document.createElement('div');
timeDiv.className = 'message-time'; timeDiv.className = 'message-time';
timeDiv.textContent = new Date().toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }); // 如果有传入的创建时间,使用它;否则使用当前时间
let messageTime;
if (createdAt) {
// 处理字符串或Date对象
if (typeof createdAt === 'string') {
messageTime = new Date(createdAt);
} else if (createdAt instanceof Date) {
messageTime = createdAt;
} else {
messageTime = new Date(createdAt);
}
// 如果解析失败,使用当前时间
if (isNaN(messageTime.getTime())) {
messageTime = new Date();
}
} else {
messageTime = new Date();
}
timeDiv.textContent = messageTime.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
contentWrapper.appendChild(timeDiv); contentWrapper.appendChild(timeDiv);
// 如果有MCP执行ID或进度ID,添加查看详情区域(统一使用"渗透测试详情"样式) // 如果有MCP执行ID或进度ID,添加查看详情区域(统一使用"渗透测试详情"样式)
@@ -1088,7 +1106,8 @@ async function loadConversation(conversationId) {
} }
} }
const messageId = addMessage(msg.role, displayContent, msg.mcpExecutionIds || []); // 传递消息的创建时间
const messageId = addMessage(msg.role, displayContent, msg.mcpExecutionIds || [], null, msg.createdAt);
// 如果有过程详情,显示它们 // 如果有过程详情,显示它们
if (msg.processDetails && msg.processDetails.length > 0 && msg.role === 'assistant') { if (msg.processDetails && msg.processDetails.length > 0 && msg.role === 'assistant') {
// 延迟一下,确保消息已经渲染 // 延迟一下,确保消息已经渲染