import SwiftUI // MARK: - Agent 进度跟踪 final class AgentProgressTracker: ObservableObject { struct ToolEntry: Identifiable { let id = UUID() let tool: String let display: String var resultLines: [String] = [] } @Published var mode: String = "idle" // idle / thinking / running / done / error @Published var elapsedSeconds: Int = 0 @Published var toolEntries: [ToolEntry] = [] @Published var finalContent: String = "" @Published var errorMessage: String = "" @Published var streamingContent: String = "" // 实时流式输出 @Published var isTextInputMode: Bool = false // 打字输入模式标记(外部控制) @Published var autoNewTriggered: Bool = false // 自动新建对话已触发 @Published var voiceInputText: String? = nil // 语音识别文本(不自动发送时填入输入框) var toolsMode: String = "all" // all / hide_final / never var newConversationMode: String = "manual" // manual / auto var totalTokens: Int = 0 var manualNewRequested: Bool = false // 手动点击了新建对话 private var timer: Timer? private var startTime: Date = Date() private var toolPhaseActive = false // 工具后首段内容应替换 func start() { startTime = Date() elapsedSeconds = 0 mode = "thinking" toolEntries = [] finalContent = "" errorMessage = "" streamingContent = "" toolPhaseActive = false timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in guard let self = self, self.mode != "done", self.mode != "error" else { return } self.elapsedSeconds = Int(Date().timeIntervalSince(self.startTime)) } } /// 打字模式:清空状态但不进入 thinking func resetForInput() { mode = "idle" elapsedSeconds = 0 toolEntries = [] finalContent = "" errorMessage = "" streamingContent = "" toolPhaseActive = false timer?.invalidate() timer = nil } func appendChunk(_ chunk: String) { if toolPhaseActive { streamingContent = chunk toolPhaseActive = false } else { streamingContent += chunk } } func addTool(_ tool: String, display: String) { mode = "running" toolPhaseActive = true // 标记:下一段内容应替换 toolEntries.append(ToolEntry(tool: tool, display: display)) } func updateToolResult(_ tool: String, resultLines: [String]) { if let idx = toolEntries.lastIndex(where: { $0.tool == tool && $0.resultLines.isEmpty }) { toolEntries[idx].resultLines = resultLines } } func finish(content: String, totalTokens: Int = 0) { mode = "done" finalContent = content self.totalTokens = totalTokens if toolsMode == "hide_final" { toolEntries = [] } // 自动模式:total > 阈值时标记自动新建 if newConversationMode == "auto" && totalTokens > Config.shared.agentAutoNewTokenThreshold { autoNewTriggered = true } timer?.invalidate() timer = nil } func fail(_ message: String) { mode = "error" errorMessage = message timer?.invalidate() timer = nil } var formattedTime: String { let m = elapsedSeconds / 60 let s = elapsedSeconds % 60 return String(format: "%02d:%02d", m, s) } var summary: String { if mode == "done" { return "工作完成 · \(formattedTime)" } else if mode == "error" { return "错误: \(errorMessage)" } else if mode == "idle" { return "准备就绪" } else { return "工作中 \(formattedTime)" } } }