CxLLM-IOS/CxLLMStudio/Services/AgentService.swift
2026-05-16 10:52:04 -05:00

182 lines
4.6 KiB
Swift

// CxLLM Studio iOS Application
// Services/AgentService.swift Agent operations service
import Foundation
import CxCode
@Observable
final class AgentService {
// MARK: - State
private(set) var isHealthy = false
private(set) var tools: [AgentToolInfo] = []
private(set) var isRunning = false
private(set) var isExecutingTool = false
private(set) var error: String?
private(set) var lastHealthCheck: Date?
private(set) var totalExecutions: Int = 0
private(set) var totalToolCalls: Int = 0
private(set) var history: [AgentExecution] = []
private let gateway: CxGateway
init(gateway: CxGateway) {
self.gateway = gateway
}
// MARK: - Health & Discovery
func checkHealth() async {
do {
let _ = try await gateway.agent.health()
isHealthy = true
error = nil
lastHealthCheck = Date()
} catch {
isHealthy = false
self.error = error.localizedDescription
lastHealthCheck = Date()
}
}
func loadTools() async {
do {
tools = try await gateway.agent.listTools()
error = nil
} catch {
self.error = error.localizedDescription
}
}
func initialize() async {
await checkHealth()
if isHealthy {
await loadTools()
}
}
// MARK: - Autopilot
func runAutopilot(task: String, maxSteps: Int = 10) async throws -> AgentExecution {
guard isHealthy else {
throw AgentServiceError.notHealthy
}
guard !isRunning else {
throw AgentServiceError.alreadyRunning
}
isRunning = true
error = nil
let startTime = Date()
do {
let result = try await gateway.agent.autoPilot(task: task, maxSteps: maxSteps)
let duration = Date().timeIntervalSince(startTime)
let execution = AgentExecution(
task: task,
steps: [],
output: result,
time: Date(),
duration: duration,
success: true
)
history.insert(execution, at: 0)
totalExecutions += 1
isRunning = false
return execution
} catch {
let duration = Date().timeIntervalSince(startTime)
let execution = AgentExecution(
task: task,
steps: [],
output: "Error: \(error.localizedDescription)",
time: Date(),
duration: duration,
success: false
)
history.insert(execution, at: 0)
self.error = error.localizedDescription
isRunning = false
throw error
}
}
// MARK: - Tool Execution
func callTool(name: String, arguments: [String: Any]) async throws -> String {
guard !isExecutingTool else {
throw AgentServiceError.alreadyRunning
}
isExecutingTool = true
error = nil
do {
let result = try await gateway.agent.callTool(name: name, arguments: arguments)
totalToolCalls += 1
isExecutingTool = false
return result
} catch {
self.error = error.localizedDescription
isExecutingTool = false
throw error
}
}
// MARK: - History Management
func clearHistory() {
history.removeAll()
}
// MARK: - Computed
var toolNames: [String] {
tools.map(\.name)
}
var successRate: Double {
guard !history.isEmpty else { return 0 }
let successes = history.filter(\.success).count
return Double(successes) / Double(history.count)
}
var averageDuration: TimeInterval {
guard !history.isEmpty else { return 0 }
return history.reduce(0.0) { $0 + $1.duration } / Double(history.count)
}
}
// MARK: - Supporting Types
struct AgentStep: Identifiable {
let id = UUID()
let index: Int
let action: String
let result: String
let timestamp: Date
}
struct AgentExecution: Identifiable {
let id = UUID()
let task: String
let steps: [AgentStep]
let output: String
let time: Date
let duration: TimeInterval
let success: Bool
}
enum AgentServiceError: LocalizedError {
case notHealthy
case alreadyRunning
var errorDescription: String? {
switch self {
case .notHealthy: return "Agent service is not healthy. Check connection."
case .alreadyRunning: return "An operation is already in progress."
}
}
}