fix: 前端消息拆分+动作消息样式+DevTools自主思考状态保持+记忆表名修复
- 侧边栏底部 "昔涟 AI" 改为 "昔涟" - 暂时禁用消息朗读按钮 - 修复前端 response 处理器:支持 gateway 发送的 content+role+msg_type 字段, 使动作消息(括号内容)正确拆分为独立的 ActionMessageBubble 显示 - 修复 DevTools 自主思考面板:5秒自动刷新后展开的思考日志不再自动折叠 - 修复 memory-service 表名不一致:memory_entries → memories, 解决 DevTools 记忆管理页面查不到 admin 用户记忆的问题 - 修复 sessionStore 解析历史消息时 msgType 未定义引用 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -144,7 +144,7 @@ func (s *Store) getDB() *sql.DB {
|
||||
func (s *Store) migrate() error {
|
||||
queries := []string{
|
||||
`CREATE EXTENSION IF NOT EXISTS vector`,
|
||||
`CREATE TABLE IF NOT EXISTS memory_entries (
|
||||
`CREATE TABLE IF NOT EXISTS memories (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id VARCHAR(64) NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
@@ -163,23 +163,23 @@ func (s *Store) migrate() error {
|
||||
expires_at TIMESTAMPTZ
|
||||
)`,
|
||||
// 向后兼容:补充旧版表中可能缺失的列
|
||||
`ALTER TABLE memory_entries ADD COLUMN IF NOT EXISTS importance INT DEFAULT 5`,
|
||||
`ALTER TABLE memory_entries ADD COLUMN IF NOT EXISTS summary TEXT DEFAULT ''`,
|
||||
`ALTER TABLE memory_entries ADD COLUMN IF NOT EXISTS keywords TEXT DEFAULT '[]'`,
|
||||
`ALTER TABLE memory_entries ADD COLUMN IF NOT EXISTS session_id VARCHAR(64) DEFAULT ''`,
|
||||
`ALTER TABLE memory_entries ADD COLUMN IF NOT EXISTS source TEXT DEFAULT 'conversation'`,
|
||||
`ALTER TABLE memory_entries ADD COLUMN IF NOT EXISTS access_count INT DEFAULT 0`,
|
||||
`ALTER TABLE memory_entries ADD COLUMN IF NOT EXISTS last_access TIMESTAMPTZ DEFAULT NOW()`,
|
||||
`ALTER TABLE memory_entries ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ DEFAULT NOW()`,
|
||||
`ALTER TABLE memory_entries ADD COLUMN IF NOT EXISTS expires_at TIMESTAMPTZ`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_me_user_id ON memory_entries(user_id)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_me_category ON memory_entries(category)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_me_priority ON memory_entries(priority)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_me_importance ON memory_entries(importance)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_me_user_priority ON memory_entries(user_id, priority DESC)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_me_user_importance ON memory_entries(user_id, importance DESC)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_me_source ON memory_entries(source)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_me_category_importance ON memory_entries(category, importance DESC)`,
|
||||
`ALTER TABLE memories ADD COLUMN IF NOT EXISTS importance INT DEFAULT 5`,
|
||||
`ALTER TABLE memories ADD COLUMN IF NOT EXISTS summary TEXT DEFAULT ''`,
|
||||
`ALTER TABLE memories ADD COLUMN IF NOT EXISTS keywords TEXT DEFAULT '[]'`,
|
||||
`ALTER TABLE memories ADD COLUMN IF NOT EXISTS session_id VARCHAR(64) DEFAULT ''`,
|
||||
`ALTER TABLE memories ADD COLUMN IF NOT EXISTS source TEXT DEFAULT 'conversation'`,
|
||||
`ALTER TABLE memories ADD COLUMN IF NOT EXISTS access_count INT DEFAULT 0`,
|
||||
`ALTER TABLE memories ADD COLUMN IF NOT EXISTS last_access TIMESTAMPTZ DEFAULT NOW()`,
|
||||
`ALTER TABLE memories ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ DEFAULT NOW()`,
|
||||
`ALTER TABLE memories ADD COLUMN IF NOT EXISTS expires_at TIMESTAMPTZ`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_me_user_id ON memories(user_id)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_me_category ON memories(category)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_me_priority ON memories(priority)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_me_importance ON memories(importance)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_me_user_priority ON memories(user_id, priority DESC)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_me_user_importance ON memories(user_id, importance DESC)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_me_source ON memories(source)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_me_category_importance ON memories(category, importance DESC)`,
|
||||
`CREATE TABLE IF NOT EXISTS thinking_logs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id VARCHAR(64) NOT NULL DEFAULT 'admin',
|
||||
@@ -220,7 +220,7 @@ func (s *Store) Save(ctx context.Context, entry *model.MemoryEntry) error {
|
||||
entry.Importance = 5
|
||||
}
|
||||
|
||||
query := `INSERT INTO memory_entries (user_id, content, summary, category, priority, importance, keywords, session_id, source, embedding, expires_at)
|
||||
query := `INSERT INTO memories (user_id, content, summary, category, priority, importance, keywords, session_id, source, embedding, expires_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||
RETURNING id, created_at`
|
||||
|
||||
@@ -250,7 +250,7 @@ func (s *Store) GetByID(ctx context.Context, id string) (*model.MemoryEntry, err
|
||||
|
||||
query := `SELECT id, user_id, content, summary, category, priority, importance, keywords,
|
||||
session_id, source, access_count, last_access, created_at, updated_at, expires_at
|
||||
FROM memory_entries WHERE id = $1`
|
||||
FROM memories WHERE id = $1`
|
||||
|
||||
entry := &model.MemoryEntry{}
|
||||
var category, keywordsRaw string
|
||||
@@ -288,7 +288,7 @@ func (s *Store) Query(ctx context.Context, q model.MemoryQuery) ([]model.MemoryE
|
||||
|
||||
query := `SELECT id, user_id, content, summary, category, priority, importance, keywords,
|
||||
session_id, source, access_count, last_access, created_at, updated_at, expires_at
|
||||
FROM memory_entries WHERE user_id = $1`
|
||||
FROM memories WHERE user_id = $1`
|
||||
args := []interface{}{q.UserID}
|
||||
argIdx := 2
|
||||
|
||||
@@ -328,7 +328,7 @@ func (s *Store) Delete(ctx context.Context, id string) error {
|
||||
if db == nil {
|
||||
return errDBNotReady
|
||||
}
|
||||
_, err := db.ExecContext(ctx, `DELETE FROM memory_entries WHERE id = $1`, id)
|
||||
_, err := db.ExecContext(ctx, `DELETE FROM memories WHERE id = $1`, id)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -339,7 +339,7 @@ func (s *Store) PurgeExpired(ctx context.Context) (int64, error) {
|
||||
return 0, errDBNotReady
|
||||
}
|
||||
result, err := db.ExecContext(ctx,
|
||||
`DELETE FROM memory_entries WHERE expires_at IS NOT NULL AND expires_at < NOW()`)
|
||||
`DELETE FROM memories WHERE expires_at IS NOT NULL AND expires_at < NOW()`)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -361,7 +361,7 @@ func (s *Store) SearchByVector(ctx context.Context, userID string, embedding []f
|
||||
query := `SELECT id, user_id, content, summary, category, priority, importance, keywords,
|
||||
session_id, source, access_count, last_access, created_at, updated_at, expires_at,
|
||||
1 - (embedding <=> $1) AS similarity
|
||||
FROM memory_entries
|
||||
FROM memories
|
||||
WHERE user_id = $2 AND embedding IS NOT NULL
|
||||
ORDER BY embedding <=> $1
|
||||
LIMIT $3`
|
||||
@@ -407,7 +407,7 @@ func (s *Store) SearchByKeyword(ctx context.Context, userID, keyword string, lim
|
||||
|
||||
query := `SELECT id, user_id, content, summary, category, priority, importance, keywords,
|
||||
session_id, source, access_count, last_access, created_at, updated_at, expires_at
|
||||
FROM memory_entries
|
||||
FROM memories
|
||||
WHERE user_id = $1 AND (content ILIKE $2 OR summary ILIKE $2 OR keywords ILIKE $2)
|
||||
ORDER BY priority DESC, importance DESC
|
||||
LIMIT $3`
|
||||
@@ -429,7 +429,7 @@ func (s *Store) Update(ctx context.Context, entry *model.MemoryEntry) error {
|
||||
return errDBNotReady
|
||||
}
|
||||
|
||||
query := `UPDATE memory_entries SET content = $1, summary = $2, category = $3, priority = $4,
|
||||
query := `UPDATE memories SET content = $1, summary = $2, category = $3, priority = $4,
|
||||
importance = $5, keywords = $6, source = $7, updated_at = NOW()
|
||||
WHERE id = $8`
|
||||
|
||||
@@ -448,7 +448,7 @@ func (s *Store) GetCategories(ctx context.Context, userID string) (map[string]in
|
||||
}
|
||||
|
||||
rows, err := db.QueryContext(ctx,
|
||||
`SELECT category, COUNT(*) FROM memory_entries WHERE user_id = $1 GROUP BY category ORDER BY category`,
|
||||
`SELECT category, COUNT(*) FROM memories WHERE user_id = $1 GROUP BY category ORDER BY category`,
|
||||
userID,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -478,7 +478,7 @@ func (s *Store) Count(ctx context.Context, userID string) (int, error) {
|
||||
|
||||
var count int
|
||||
err := db.QueryRowContext(ctx,
|
||||
`SELECT COUNT(*) FROM memory_entries WHERE user_id = $1`,
|
||||
`SELECT COUNT(*) FROM memories WHERE user_id = $1`,
|
||||
userID,
|
||||
).Scan(&count)
|
||||
if err != nil {
|
||||
@@ -573,7 +573,7 @@ func (s *Store) DecayMemories(ctx context.Context, userID string) (int, int, err
|
||||
}
|
||||
|
||||
result1, err := db.ExecContext(ctx, `
|
||||
UPDATE memory_entries SET priority = GREATEST(priority - 1, 0), updated_at = NOW()
|
||||
UPDATE memories SET priority = GREATEST(priority - 1, 0), updated_at = NOW()
|
||||
WHERE user_id = $1
|
||||
AND access_count < 3
|
||||
AND last_access < NOW() - INTERVAL '30 days'
|
||||
@@ -588,7 +588,7 @@ func (s *Store) DecayMemories(ctx context.Context, userID string) (int, int, err
|
||||
decayed1, _ := result1.RowsAffected()
|
||||
|
||||
result2, err := db.ExecContext(ctx, `
|
||||
DELETE FROM memory_entries
|
||||
DELETE FROM memories
|
||||
WHERE user_id = $1
|
||||
AND priority = 0
|
||||
AND access_count = 0
|
||||
@@ -615,7 +615,7 @@ func (s *Store) incrementAccess(ctx context.Context, id string) {
|
||||
return
|
||||
}
|
||||
db.ExecContext(ctx,
|
||||
`UPDATE memory_entries SET access_count = access_count + 1, last_access = NOW() WHERE id = $1`, id)
|
||||
`UPDATE memories SET access_count = access_count + 1, last_access = NOW() WHERE id = $1`, id)
|
||||
}
|
||||
|
||||
// Close 关闭数据库连接
|
||||
@@ -754,21 +754,26 @@ func (s *Store) GetThinkingLogByID(ctx context.Context, id string) (*model.Think
|
||||
}
|
||||
|
||||
// GetThinkingStats 获取思考日志统计信息
|
||||
func (s *Store) GetThinkingStats(ctx context.Context) (*model.ThinkingStats, error) {
|
||||
func (s *Store) GetThinkingStats(ctx context.Context, userID string) (*model.ThinkingStats, error) {
|
||||
db := s.getDB()
|
||||
if db == nil {
|
||||
return nil, errDBNotReady
|
||||
}
|
||||
|
||||
var args []interface{}
|
||||
query := `SELECT
|
||||
COALESCE(COUNT(*), 0),
|
||||
COALESCE(SUM(tool_call_count), 0),
|
||||
COALESCE(AVG(content_length), 0),
|
||||
COALESCE(MAX(created_at)::TEXT, '')
|
||||
FROM thinking_logs`
|
||||
if userID != "" {
|
||||
query += ` WHERE user_id = $1`
|
||||
args = append(args, userID)
|
||||
}
|
||||
|
||||
stats := &model.ThinkingStats{}
|
||||
err := db.QueryRowContext(ctx, query).Scan(
|
||||
err := db.QueryRowContext(ctx, query, args...).Scan(
|
||||
&stats.TotalLogs, &stats.TotalToolCalls,
|
||||
&stats.AvgContentLen, &stats.LatestAt,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user