Fix bridge compression history handling (#726)

* feat(bridge): refactor compression to use DB history and add structured logging

- Extract buildDbHistory() to share message loading between buildCompressedHistory and forceCompressBridgeHistory
- forceCompressBridgeHistory now reads from local DB instead of using Python-provided messages, ensuring consistency with api_server path
- Pass sessionId to compressor for snapshot-aware compression
- Add force_compress flag to bridge chat requests
- Add bridgeLogger structured logging for compression lifecycle
- Simplify schemas, session-sync, and providers

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix bridge compression history handling

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
ekko
2026-05-14 21:02:59 +08:00
committed by GitHub
parent 7420f7aad5
commit d0f1e7d1f2
19 changed files with 576 additions and 638 deletions
+25 -1
View File
@@ -5,6 +5,7 @@ import { homedir } from 'os'
import * as hermesCli from '../../services/hermes/hermes-cli'
const WEBUI_LOG_FILE = join(homedir(), '.hermes-web-ui', 'logs', 'server.log')
const BRIDGE_LOG_FILE = join(homedir(), '.hermes-web-ui', 'logs', 'bridge.log')
interface LogEntry {
timestamp: string; level: string; logger: string; message: string; raw: string
@@ -15,7 +16,7 @@ function parseLine(line: string): LogEntry {
const obj = JSON.parse(line)
if (obj.level && obj.time) {
const ts = new Date(obj.time).toLocaleString('zh-CN', { hour12: false }).replace(/\//g, '-')
const levelMap: Record<number, string> = { 10: 'DEBUG', 20: 'INFO', 30: 'WARN', 40: 'ERROR', 50: 'FATAL' }
const levelMap: Record<number, string> = { 10: 'TRACE', 20: 'DEBUG', 30: 'INFO', 40: 'WARN', 50: 'ERROR', 60: 'FATAL' }
// Pino 日志格式: { level, time, msg, name (logger name), hostname, pid, ... }
const loggerName = obj.name || obj.logger || 'app'
const message = obj.msg || (obj.err ? obj.err.message : '')
@@ -39,6 +40,14 @@ export async function list(ctx: any) {
files.push({ name: 'webui', size, modified })
} catch { }
}
if (existsSync(BRIDGE_LOG_FILE)) {
try {
const stat = statSync(BRIDGE_LOG_FILE)
const size = stat.size > 1024 * 1024 ? `${(stat.size / 1024 / 1024).toFixed(1)}MB` : `${(stat.size / 1024).toFixed(1)}KB`
const modified = stat.mtime.toLocaleString()
files.push({ name: 'bridge', size, modified })
} catch { }
}
ctx.body = { files }
}
@@ -64,6 +73,21 @@ export async function read(ctx: any) {
return
}
if (logName === 'bridge') {
try {
if (!existsSync(BRIDGE_LOG_FILE)) { ctx.body = { entries: [] }; return }
const content = await readFile(BRIDGE_LOG_FILE, 'utf-8')
const rawLines = content.split('\n')
const sliced = rawLines.length > lines ? rawLines.slice(-lines) : rawLines
const entries: LogEntry[] = []
for (const line of sliced) { if (!line.trim()) continue; entries.push(parseLine(line)) }
ctx.body = { entries: entries.reverse() }
} catch (err: any) {
ctx.status = 500; ctx.body = { error: err.message }
}
return
}
try {
const content = await hermesCli.readLogs(logName, lines, level, session, since)
const rawLines = content.split('\n')