pdo = $pdo; } public function find(int $id): ?TaskResult { $stmt = $this->pdo->prepare('SELECT * FROM task_results WHERE id = :id'); $stmt->execute(['id' => $id]); $row = $stmt->fetch(); return $row !== false ? TaskResult::fromArray($row) : null; } public function findByTaskId(int $taskId): array { $stmt = $this->pdo->prepare( 'SELECT * FROM task_results WHERE task_id = :task_id ORDER BY created_at DESC' ); $stmt->execute(['task_id' => $taskId]); $results = []; while ($row = $stmt->fetch()) { $results[] = TaskResult::fromArray($row); } return $results; } public function findByAssignmentId(int $assignmentId): array { $stmt = $this->pdo->prepare( 'SELECT * FROM task_results WHERE assignment_id = :assignment_id ORDER BY created_at DESC' ); $stmt->execute(['assignment_id' => $assignmentId]); $results = []; while ($row = $stmt->fetch()) { $results[] = TaskResult::fromArray($row); } return $results; } public function save(TaskResult $result): int { $sql = 'INSERT INTO task_results ( task_id, assignment_id, executor, executor_type, model_name, request, response, request_timestamp, response_timestamp, duration_ms, tokens_input, tokens_output, tokens_total, cost_usd, status, error_message, created_at ) VALUES ( :task_id, :assignment_id, :executor, :executor_type, :model_name, :request, :response, :request_timestamp, :response_timestamp, :duration_ms, :tokens_input, :tokens_output, :tokens_total, :cost_usd, :status, :error_message, :created_at )'; $stmt = $this->pdo->prepare($sql); $data = $result->toArray(); $stmt->execute([ 'task_id' => $data['task_id'], 'assignment_id' => $data['assignment_id'], 'executor' => $data['executor'], 'executor_type' => $data['executor_type'], 'model_name' => $data['model_name'], 'request' => $data['request'], 'response' => $data['response'], 'request_timestamp' => $data['request_timestamp'], 'response_timestamp' => $data['response_timestamp'], 'duration_ms' => $data['duration_ms'], 'tokens_input' => $data['tokens_input'], 'tokens_output' => $data['tokens_output'], 'tokens_total' => $data['tokens_total'], 'cost_usd' => $data['cost_usd'], 'status' => $data['status'], 'error_message' => $data['error_message'], 'created_at' => $data['created_at'], ]); return (int) $this->pdo->lastInsertId(); } public function getTokenStatistics(?int $taskId = null): array { $where = $taskId !== null ? 'WHERE task_id = :task_id' : ''; $params = $taskId !== null ? ['task_id' => $taskId] : []; $sql = "SELECT COUNT(*) as total_results, SUM(tokens_input) as total_tokens_input, SUM(tokens_output) as total_tokens_output, SUM(tokens_total) as total_tokens, SUM(cost_usd) as total_cost, AVG(duration_ms) as avg_duration_ms, SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END) as successful, SUM(CASE WHEN status = 'error' THEN 1 ELSE 0 END) as failed FROM task_results {$where}"; $stmt = $this->pdo->prepare($sql); $stmt->execute($params); return $stmt->fetch(); } public function getModelUsage(): array { $sql = 'SELECT model_name, executor_type, COUNT(*) as usage_count, SUM(tokens_total) as total_tokens, SUM(cost_usd) as total_cost, AVG(duration_ms) as avg_duration FROM task_results WHERE model_name IS NOT NULL GROUP BY model_name, executor_type ORDER BY usage_count DESC'; $stmt = $this->pdo->query($sql); return $stmt->fetchAll(); } }