started) { return; } // Prevent any output buffering while (ob_get_level()) { ob_end_clean(); } // Set headers for SSE header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); header('Connection: keep-alive'); header('X-Accel-Buffering: no'); // Disable nginx buffering // Disable PHP output buffering if (function_exists('apache_setenv')) { @apache_setenv('no-gzip', '1'); } @ini_set('zlib.output_compression', '0'); @ini_set('implicit_flush', '1'); $this->started = true; // Send initial connection message $this->send('connected', array( 'message' => 'Connected to AI Article Writer', 'timestamp' => current_time('mysql') )); } /** * Set message ID for this stream * * @param string $message_id Message ID */ public function set_message_id($message_id) { $this->message_id = $message_id; } /** * Get current message ID * * @return string|null Message ID */ public function get_message_id() { return $this->message_id; } /** * Send SSE event * * @param string $event_type Event type * @param mixed $data Event data */ public function send($event_type, $data) { if (!$this->started) { $this->start(); } // Add message_id to data if available and not already set if ($this->message_id && !isset($data['message_id'])) { $data['message_id'] = $this->message_id; } // Format data $json_data = json_encode($data, JSON_UNESCAPED_UNICODE); // Send event echo "event: {$event_type}\n"; echo "data: {$json_data}\n\n"; // Flush output if (ob_get_level()) { ob_flush(); } flush(); } /** * Send status update * * @param string $message Status message */ public function send_status($message) { $this->send('status', array( 'message' => $message, 'timestamp' => current_time('mysql') )); } /** * Send step start event * * @param int $step_number Step number * @param string $step_name Step name * @param string $description Step description */ public function send_step_start($step_number, $step_name, $description) { $this->send('step_start', array( 'step' => $step_number, 'name' => $step_name, 'description' => $description, 'timestamp' => current_time('mysql') )); } /** * Send step output (analysis content) * * @param int $step_number Step number * @param string $output Step output/analysis content */ public function send_step_output($step_number, $output) { $this->send('step_output', array( 'step' => $step_number, 'output' => $output, 'timestamp' => current_time('mysql') )); } /** * Send step completion * * @param int $step_number Step number * @param string $status Status (success/error) * @param mixed $result Step result */ public function send_step_complete($step_number, $status, $result = null) { $this->send('step_complete', array( 'step' => $step_number, 'status' => $status, 'result' => $result, 'timestamp' => current_time('mysql') )); } /** * Send progress update * * @param int $current Current progress * @param int $total Total steps * @param string $message Progress message */ public function send_progress($current, $total, $message = '') { $percentage = $total > 0 ? round(($current / $total) * 100) : 0; $this->send('progress', array( 'current' => $current, 'total' => $total, 'percentage' => $percentage, 'message' => $message, 'timestamp' => current_time('mysql') )); } /** * Send error event * * @param string $message Error message * @param mixed $details Error details * @param string|null $message_id Message ID (optional, will use stored if not provided) */ public function send_error($message, $details = null, $message_id = null) { $data = array( 'message' => $message, 'details' => $details, 'timestamp' => current_time('mysql') ); // Use provided message_id or stored one if ($message_id !== null) { $data['message_id'] = $message_id; } elseif ($this->message_id) { $data['message_id'] = $this->message_id; } $this->send('error', $data); } /** * Send completion event with final article content * * @param string $content Final article content * @param mixed $result Final result data * @param string|null $message_id Message ID (optional, will use stored if not provided) */ public function send_complete($content, $result = null, $message_id = null) { $data = array( 'content' => $content, 'result' => $result, 'timestamp' => current_time('mysql') ); // Use provided message_id or stored one if ($message_id !== null) { $data['message_id'] = $message_id; } elseif ($this->message_id) { $data['message_id'] = $this->message_id; } $this->send('complete', $data); } /** * Close stream */ public function close() { $this->send('done', array( 'message' => 'Stream closed', 'timestamp' => current_time('mysql') )); exit; } }