mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-06-11 00:27:53 +02:00
Add files via upload
This commit is contained in:
@@ -565,19 +565,53 @@ func (db *DB) DeleteConversation(id string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("删除对话失败: %w", err)
|
return fmt.Errorf("删除对话失败: %w", err)
|
||||||
}
|
}
|
||||||
// Best-effort cleanup for conversation-scoped filesystem artifacts
|
db.removeConversationScopedDirs(id)
|
||||||
// (e.g., summarization transcript, reduction/checkpoint files under conversation_artifacts/<id>).
|
|
||||||
if base := strings.TrimSpace(db.conversationArtifactsDir); base != "" {
|
|
||||||
artDir := filepath.Join(base, id)
|
|
||||||
if rmErr := os.RemoveAll(artDir); rmErr != nil {
|
|
||||||
db.logger.Warn("删除会话 artifacts 目录失败", zap.String("conversationId", id), zap.String("dir", artDir), zap.Error(rmErr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
db.logger.Info("对话及其所有相关数据已删除", zap.String("conversationId", id))
|
db.logger.Info("对话及其所有相关数据已删除", zap.String("conversationId", id))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sanitizeConversationPathSegment(s string) string {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
if s == "" {
|
||||||
|
return "default"
|
||||||
|
}
|
||||||
|
s = strings.ReplaceAll(s, string(filepath.Separator), "-")
|
||||||
|
s = strings.ReplaceAll(s, "/", "-")
|
||||||
|
s = strings.ReplaceAll(s, "\\", "-")
|
||||||
|
s = strings.ReplaceAll(s, "..", "__")
|
||||||
|
if len(s) > 180 {
|
||||||
|
s = s[:180]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) removeConversationScopedDir(base, conversationID, label string) {
|
||||||
|
base = strings.TrimSpace(base)
|
||||||
|
if base == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dir := filepath.Join(base, sanitizeConversationPathSegment(conversationID))
|
||||||
|
if rmErr := os.RemoveAll(dir); rmErr != nil {
|
||||||
|
if db.logger != nil {
|
||||||
|
db.logger.Warn("删除会话目录失败",
|
||||||
|
zap.String("conversationId", conversationID),
|
||||||
|
zap.String("kind", label),
|
||||||
|
zap.String("dir", dir),
|
||||||
|
zap.Error(rmErr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) removeConversationScopedDirs(conversationID string) {
|
||||||
|
// summarization transcript, reduction files, etc.
|
||||||
|
db.removeConversationScopedDir(db.conversationArtifactsDir, conversationID, "conversation_artifacts")
|
||||||
|
// Eino plantask JSON boards (skills_dir/.eino/plantask/<id>/).
|
||||||
|
db.removeConversationScopedDir(db.einoPlantaskBaseDir, conversationID, "plantask")
|
||||||
|
// Eino ADK runner checkpoints (checkpoint_dir/<id>/).
|
||||||
|
db.removeConversationScopedDir(db.einoCheckpointBaseDir, conversationID, "eino_checkpoint")
|
||||||
|
}
|
||||||
|
|
||||||
// SaveAgentTrace 保存最后一轮代理消息轨迹与助手输出摘要。
|
// SaveAgentTrace 保存最后一轮代理消息轨迹与助手输出摘要。
|
||||||
// SQLite 列名仍为 last_react_input / last_react_output,与历史库表兼容;语义上为「全模式代理轨迹」,非仅 ReAct。
|
// SQLite 列名仍为 last_react_input / last_react_output,与历史库表兼容;语义上为「全模式代理轨迹」,非仅 ReAct。
|
||||||
func (db *DB) SaveAgentTrace(conversationID, traceInputJSON, assistantOutput string) error {
|
func (db *DB) SaveAgentTrace(conversationID, traceInputJSON, assistantOutput string) error {
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDeleteConversationRemovesEinoScopedDirs(t *testing.T) {
|
||||||
|
tmp := t.TempDir()
|
||||||
|
dbPath := filepath.Join(tmp, "conversations.db")
|
||||||
|
db, err := NewDB(dbPath, zap.NewNop())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewDB: %v", err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
plantaskBase := filepath.Join(tmp, "skills", ".eino", "plantask")
|
||||||
|
checkpointBase := filepath.Join(tmp, "eino-checkpoints")
|
||||||
|
db.SetEinoConversationDirs(plantaskBase, checkpointBase)
|
||||||
|
|
||||||
|
conv, err := db.CreateConversation("cleanup test", ConversationCreateMeta{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("CreateConversation: %v", err)
|
||||||
|
}
|
||||||
|
convID := conv.ID
|
||||||
|
seg := sanitizeConversationPathSegment(convID)
|
||||||
|
for _, base := range []struct {
|
||||||
|
root string
|
||||||
|
file string
|
||||||
|
}{
|
||||||
|
{db.conversationArtifactsDir, "transcript.txt"},
|
||||||
|
{plantaskBase, "task-1.json"},
|
||||||
|
{checkpointBase, "runner-deep.ckpt"},
|
||||||
|
} {
|
||||||
|
dir := filepath.Join(base.root, seg)
|
||||||
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||||
|
t.Fatalf("mkdir %s: %v", dir, err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, base.file), []byte("x"), 0o644); err != nil {
|
||||||
|
t.Fatalf("write %s: %v", base.file, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.DeleteConversation(convID); err != nil {
|
||||||
|
t.Fatalf("DeleteConversation: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, base := range []string{db.conversationArtifactsDir, plantaskBase, checkpointBase} {
|
||||||
|
dir := filepath.Join(base, seg)
|
||||||
|
if _, statErr := os.Stat(dir); !os.IsNotExist(statErr) {
|
||||||
|
t.Fatalf("expected removed dir %s, stat err=%v", dir, statErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,6 +49,8 @@ type DB struct {
|
|||||||
*sql.DB
|
*sql.DB
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
conversationArtifactsDir string
|
conversationArtifactsDir string
|
||||||
|
einoPlantaskBaseDir string // skills_dir + plantask_rel_dir (per-conversation subdirs)
|
||||||
|
einoCheckpointBaseDir string // checkpoint_dir root (per-conversation subdirs)
|
||||||
checkpointLoopName string
|
checkpointLoopName string
|
||||||
checkpointStop chan struct{}
|
checkpointStop chan struct{}
|
||||||
checkpointDone chan struct{}
|
checkpointDone chan struct{}
|
||||||
@@ -155,6 +157,16 @@ func NewDB(dbPath string, logger *zap.Logger) (*DB, error) {
|
|||||||
return database, nil
|
return database, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetEinoConversationDirs configures best-effort filesystem cleanup on DeleteConversation.
|
||||||
|
// plantaskBase is skills_root/plantask_rel (no conversation id); checkpointBase is checkpoint_dir root.
|
||||||
|
func (db *DB) SetEinoConversationDirs(plantaskBase, checkpointBase string) {
|
||||||
|
if db == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
db.einoPlantaskBaseDir = strings.TrimSpace(plantaskBase)
|
||||||
|
db.einoCheckpointBaseDir = strings.TrimSpace(checkpointBase)
|
||||||
|
}
|
||||||
|
|
||||||
// initTables 初始化数据库表
|
// initTables 初始化数据库表
|
||||||
func (db *DB) initTables() error {
|
func (db *DB) initTables() error {
|
||||||
// 创建对话表(last_react_input / last_react_output 存「代理消息轨迹」JSON 与助手摘要,列名保留以兼容已有库)
|
// 创建对话表(last_react_input / last_react_output 存「代理消息轨迹」JSON 与助手摘要,列名保留以兼容已有库)
|
||||||
|
|||||||
Reference in New Issue
Block a user