llm = $llm; $this->cost_tracker = $cost_tracker; } /** * Generate a blog post from an article idea. * * Reads pipeline mode from settings and dispatches to the appropriate method. * Side effects: LLM API calls (1-3 depending on mode), cost logging. * * @param PRAutoBlogger_Article_Idea $idea The scored idea to generate content for. * @return string Generated HTML content. * @throws \RuntimeException On generation failure. */ public function generate( PRAutoBlogger_Article_Idea $idea ): string { $mode = get_option( 'prautoblogger_writing_pipeline', 'multi_step' ); $request = new PRAutoBlogger_Content_Request( $idea, $mode, get_option( 'prautoblogger_tone', 'informational' ), absint( get_option( 'prautoblogger_min_word_count', 800 ) ), absint( get_option( 'prautoblogger_max_word_count', 2000 ) ), get_option( 'prautoblogger_niche_description', '' ), json_decode( get_option( 'prautoblogger_topic_exclusions', '[]' ), true ) ?: [], get_option( 'prautoblogger_writing_instructions', '' ) ); if ( 'single_pass' === $mode ) { return $this->generate_single_pass( $request ); } return $this->generate_multi_step( $request ); } /** * Single-pass generation: one LLM call produces the complete article. * * @param PRAutoBlogger_Content_Request $request Content request with settings. * @return string Generated HTML content. */ private function generate_single_pass( PRAutoBlogger_Content_Request $request ): string { $model = get_option( 'prautoblogger_writing_model', PRAUTOBLOGGER_DEFAULT_WRITING_MODEL ); $response = $this->llm->send_chat_completion( [ [ 'role' => 'system', 'content' => PRAutoBlogger_Content_Prompts::build_system( $request ) ], [ 'role' => 'user', 'content' => PRAutoBlogger_Content_Prompts::build_single_pass( $request ) ], ], $model, [ 'temperature' => 0.7, 'max_tokens' => 4000 ] ); $this->cost_tracker->log_api_call( null, 'draft', $this->llm->get_provider_name(), $response['model'], $response['prompt_tokens'], $response['completion_tokens'] ); return $response['content']; } /** Multi-step generation: Outline → Draft → Polish. */ private function generate_multi_step( PRAutoBlogger_Content_Request $request ): string { $model = get_option( 'prautoblogger_writing_model', PRAUTOBLOGGER_DEFAULT_WRITING_MODEL ); $outline = $this->stage_outline( $request, $model ); $draft = $this->stage_draft( $request, $outline, $model ); $polished = $this->stage_polish( $request, $draft, $model ); return $polished; } /** Multi-step stage 1: Generate an article outline. */ private function stage_outline( PRAutoBlogger_Content_Request $request, string $model ): string { $system = PRAutoBlogger_Content_Prompts::build_system( $request ); $response = $this->llm->send_chat_completion( [ [ 'role' => 'system', 'content' => $system ], [ 'role' => 'user', 'content' => PRAutoBlogger_Content_Prompts::build_outline( $request ) ], ], $model, [ 'temperature' => 0.5, 'max_tokens' => 1500 ] ); $this->cost_tracker->log_api_call( null, 'outline', $this->llm->get_provider_name(), $response['model'], $response['prompt_tokens'], $response['completion_tokens'] ); return $response['content']; } /** Multi-step stage 2: Write the full draft from the outline. */ private function stage_draft( PRAutoBlogger_Content_Request $request, string $outline, string $model ): string { $system = PRAutoBlogger_Content_Prompts::build_system( $request ); $response = $this->llm->send_chat_completion( [ [ 'role' => 'system', 'content' => $system ], [ 'role' => 'user', 'content' => PRAutoBlogger_Content_Prompts::build_draft( $request, $outline ) ], ], $model, [ 'temperature' => 0.7, 'max_tokens' => 4000 ] ); $this->cost_tracker->log_api_call( null, 'draft', $this->llm->get_provider_name(), $response['model'], $response['prompt_tokens'], $response['completion_tokens'] ); return $response['content']; } /** Multi-step stage 3: Polish and refine the draft. */ private function stage_polish( PRAutoBlogger_Content_Request $request, string $draft, string $model ): string { $system = PRAutoBlogger_Content_Prompts::build_system( $request ); $response = $this->llm->send_chat_completion( [ [ 'role' => 'system', 'content' => $system ], [ 'role' => 'user', 'content' => PRAutoBlogger_Content_Prompts::build_polish( $draft ) ], ], $model, [ 'temperature' => 0.4, 'max_tokens' => 4000 ] ); $this->cost_tracker->log_api_call( null, 'polish', $this->llm->get_provider_name(), $response['model'], $response['prompt_tokens'], $response['completion_tokens'] ); return $response['content']; } }