>> Rows keyed by stage. */ public function get_by_run( string $run_id ): array { global $wpdb; if ( null === $wpdb || '' === $run_id ) { return array(); } $table = $wpdb->prefix . 'prautoblogger_generation_log'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching $rows = $wpdb->get_results( $wpdb->prepare( "SELECT id, stage, model, agent_role, prompt_version, prompt_tokens, completion_tokens, estimated_cost, request_json, response_status, error_message, created_at FROM {$table} WHERE run_id = %s ORDER BY id ASC", $run_id ), ARRAY_A ); $index = array(); foreach ( ( $rows ?? array() ) as $row ) { if ( ! is_array( $row ) ) { continue; } $stage = (string) ( $row['stage'] ?? 'unknown' ); if ( ! isset( $index[ $stage ] ) ) { $index[ $stage ] = array(); } $index[ $stage ][] = $row; } return $index; } /** * Aggregate cost + token totals per stage for the sidebar receipt. * * @param array>> $log_index Output of get_by_run(). * @return array */ public function aggregate_per_stage( array $log_index ): array { $totals = array(); foreach ( $log_index as $stage => $rows ) { $cost = 0.0; $prompt_tokens = 0; $completion_tokens = 0; foreach ( $rows as $row ) { $cost += (float) ( $row['estimated_cost'] ?? 0 ); $prompt_tokens += (int) ( $row['prompt_tokens'] ?? 0 ); $completion_tokens += (int) ( $row['completion_tokens'] ?? 0 ); } $totals[ $stage ] = array( 'cost' => $cost, 'prompt_tokens' => $prompt_tokens, 'completion_tokens' => $completion_tokens, 'calls' => count( $rows ), ); } return $totals; } }