openai_client = $openai_client ?: new AIGH_OpenAI_Client(); } /** * Get default configuration * * @return array */ protected function get_default_config() { return array( 'use_research' => true, // Use research_brief if available in context 'temperature' => 0.7, 'max_tokens' => 4000 ); } /** * Execute the step * * @param AIGH_Pipeline_Context $context * @return AIGH_Pipeline_Context|WP_Error */ public function execute($context) { $this->log_start($context); // Get required inputs $topic = $this->get_from_context($context, 'topic'); $length = $this->get_from_context($context, 'length', 'medium'); $tone = $this->get_from_context($context, 'tone', 'professional'); $length_guide = $this->get_from_context($context, 'length_guide', '800-1000 words'); // Get persona if provided $persona = $this->get_from_context($context, 'persona', ''); // Get custom style if provided (overrides tone) $custom_style = $this->get_from_context($context, 'custom_style', ''); // Get SEO keywords - check both 'seo_keywords' and 'keywords' keys $seo_keywords = $this->get_from_context($context, 'seo_keywords', ''); if (empty($seo_keywords)) { $seo_keywords = $this->get_from_context($context, 'keywords', ''); } // Debug logging if (defined('WP_DEBUG') && WP_DEBUG) { // phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging wrapped in WP_DEBUG check error_log('[AIGH] generate_draft - seo_keywords from context: ' . ($seo_keywords ?: 'EMPTY')); error_log('[AIGH] generate_draft - custom_style: ' . (!empty($custom_style) ? 'PROVIDED' : 'EMPTY')); // phpcs:enable WordPress.PHP.DevelopmentFunctions.error_log_error_log } // Check for research brief $research_brief = $this->get_from_context($context, 'research_brief', ''); $use_research = $this->get_config_value('use_research', true) && !empty($research_brief); // Generate content if ($use_research) { $content = $this->generate_with_research($topic, $length_guide, $tone, $research_brief, $seo_keywords, $custom_style, $persona); } else { $content = $this->generate_basic($topic, $length_guide, $tone, $seo_keywords, $custom_style, $persona); } if (is_wp_error($content)) { $this->log_end($context, false); return $content; } // Store results in context $this->set_in_context($context, 'content', $content); $this->set_in_context($context, 'generated_at', current_time('mysql')); $this->set_in_context($context, 'research_used', $use_research); $this->log_end($context, true); return $context; } /** * Generate basic content without research * * @param string $topic * @param string $length_guide * @param string $tone * @param string $seo_keywords * @param string $custom_style * @return string|WP_Error */ private function generate_basic($topic, $length_guide, $tone, $seo_keywords = '', $custom_style = '', $persona = '') { // Build style instruction $style_instruction = ''; if (!empty($custom_style)) { $style_instruction = "Follow this writing style guide:\n{$custom_style}"; } else { $style_instruction = "Use a {$tone} tone."; } // Build persona instruction $persona_instruction = ''; if (!empty($persona)) { $persona_instruction = "You are writing as: {$persona}. Bring this expertise and perspective to the content."; } $system_prompt = "You are a professional content writer and SEO specialist. Create high-quality, engaging, SEO-optimized content. {$style_instruction}"; if (!empty($persona_instruction)) { $system_prompt .= "\n\n{$persona_instruction}"; } $user_prompt = "Write a blog article about: {$topic}\n\n"; // Add SEO keywords instruction if provided if (!empty($seo_keywords)) { // Parse keywords - first one is primary, rest are secondary $keywords_array = array_map('trim', explode(',', $seo_keywords)); $primary_keyword = $keywords_array[0] ?? ''; $secondary_keywords = array_slice($keywords_array, 1); $user_prompt .= "=== SEO KEYWORDS (MUST USE) ===\n"; $user_prompt .= "PRIMARY KEYWORD: {$primary_keyword}\n"; if (!empty($secondary_keywords)) { $user_prompt .= "SECONDARY KEYWORDS: " . implode(', ', $secondary_keywords) . "\n"; } $user_prompt .= "\nCRITICAL REQUIREMENTS:\n"; $user_prompt .= "- Use the PRIMARY keyword \"{$primary_keyword}\" in:\n"; $user_prompt .= " * The first paragraph (within first 100 words)\n"; $user_prompt .= " * At least 2 subheadings (
tags\n"; $user_prompt .= "- Include an engaging introduction and a strong closing section\n"; $user_prompt .= "- NEVER use 'Conclusion' as a heading - use a creative, topic-relevant heading instead\n"; $user_prompt .= "- Use subheadings to break up content\n"; if (!empty($seo_keywords)) { $user_prompt .= "- Optimize heavily for the specified SEO keywords\n"; } $user_prompt .= "\nWrite the complete article content:"; return $this->openai_client->generate_text( $system_prompt, $user_prompt, $this->get_config_value('temperature', 0.7), $this->get_config_value('max_tokens', 4000) ); } /** * Generate content with research context * * @param string $topic * @param string $length_guide * @param string $tone * @param string $research_brief * @param string $seo_keywords * @param string $custom_style * @return string|WP_Error */ private function generate_with_research($topic, $length_guide, $tone, $research_brief, $seo_keywords = '', $custom_style = '', $persona = '') { // Build style instruction $style_instruction = ''; if (!empty($custom_style)) { $style_instruction = "Follow this writing style guide:\n{$custom_style}"; } else { $style_instruction = "Use a {$tone} tone."; } // Build persona instruction $persona_instruction = ''; if (!empty($persona)) { $persona_instruction = "You are writing as: {$persona}. Bring this expertise and perspective to the content."; } $system_prompt = "You are a professional content writer and SEO specialist. Create high-quality, well-researched, SEO-optimized content based on verified sources. {$style_instruction} You MUST incorporate the specific information provided in the research - do not write generic content."; if (!empty($persona_instruction)) { $system_prompt .= "\n\n{$persona_instruction}"; } $user_prompt = "Write a blog article about: {$topic}\n\n"; // Add SEO keywords instruction if provided if (!empty($seo_keywords)) { // Parse keywords - first one is primary, rest are secondary $keywords_array = array_map('trim', explode(',', $seo_keywords)); $primary_keyword = $keywords_array[0] ?? ''; $secondary_keywords = array_slice($keywords_array, 1); $user_prompt .= "=== SEO KEYWORDS (MUST USE) ===\n"; $user_prompt .= "PRIMARY KEYWORD: {$primary_keyword}\n"; if (!empty($secondary_keywords)) { $user_prompt .= "SECONDARY KEYWORDS: " . implode(', ', $secondary_keywords) . "\n"; } $user_prompt .= "\nCRITICAL REQUIREMENTS:\n"; $user_prompt .= "- Use the PRIMARY keyword \"{$primary_keyword}\" in:\n"; $user_prompt .= " * The first paragraph (within first 100 words)\n"; $user_prompt .= " * At least 2 subheadings (
tags\n"; $user_prompt .= "- Engaging introduction with strong hook based on research\n"; $user_prompt .= "- Descriptive subheadings"; if (!empty($seo_keywords)) { $user_prompt .= " (include SEO keywords in subheadings)"; } $user_prompt .= "\n- Strong closing section - NEVER use 'Conclusion' as a heading, use a creative topic-relevant heading instead\n\n"; $user_prompt .= "Write the complete article using the research above (HTML formatted):"; return $this->openai_client->generate_text( $system_prompt, $user_prompt, $this->get_config_value('temperature', 0.7), $this->get_config_value('max_tokens', 4000) ); } /** * Set the OpenAI client (for dependency injection/testing) * * @param AIGH_OpenAI_Client $client */ public function set_openai_client($client) { $this->openai_client = $client; } /** * Get configuration schema for UI rendering * * @return array */ public function get_config_schema() { return array( 'use_research' => array( 'type' => 'checkbox', 'label' => 'Use Research', 'description' => 'Use research brief if available in the pipeline', 'default' => true ), 'temperature' => array( 'type' => 'range', 'label' => 'Creativity', 'description' => 'Higher values = more creative, lower = more focused', 'min' => 0.1, 'max' => 1.0, 'step' => 0.1, 'default' => 0.7 ) ); } }