get_niche_description(); $prompt = PRAutoBlogger_Prompt_Registry::render( 'content.system', array( 'niche_clause' => '' !== $niche ? " specializing in {$niche}" : '', 'tone' => $request->get_tone(), ) ); // Append user-defined writing instructions as a mandatory style guide. $instructions = trim( $request->get_writing_instructions() ); if ( '' !== $instructions ) { $prompt .= "\n\n--- MANDATORY STYLE GUIDE ---\n"; $prompt .= "You MUST follow every requirement below. These override any conflicting defaults:\n\n"; $prompt .= $instructions; $prompt .= "\n--- END STYLE GUIDE ---"; } $prompt .= "\n\n" . self::build_linking_rules(); return $prompt; } /** * Build the user prompt for single-pass generation. * * @param PRAutoBlogger_Content_Request $request Content request with settings. * @return string User prompt. */ public static function build_single_pass( PRAutoBlogger_Content_Request $request ): string { $idea = $request->get_idea(); return PRAutoBlogger_Prompt_Registry::render( 'content.single_pass', array( 'title' => $idea->get_suggested_title(), 'topic' => $idea->get_topic(), 'article_type' => $idea->get_article_type(), 'key_points' => implode( "\n- ", $idea->get_key_points() ), 'keywords' => implode( ', ', $idea->get_target_keywords() ), 'min_words' => (string) $request->get_min_word_count(), 'max_words' => (string) $request->get_max_word_count(), ) ); } /** * Build the outline stage prompt (multi-step stage 1). * * @param PRAutoBlogger_Content_Request $request Content request with settings. * @return string Outline prompt. */ public static function build_outline( PRAutoBlogger_Content_Request $request ): string { $idea = $request->get_idea(); return PRAutoBlogger_Prompt_Registry::render( 'content.outline', array( 'title' => $idea->get_suggested_title(), 'topic' => $idea->get_topic(), 'article_type' => $idea->get_article_type(), 'key_points' => implode( "\n- ", $idea->get_key_points() ), 'keywords' => implode( ', ', $idea->get_target_keywords() ), 'min_words' => (string) $request->get_min_word_count(), 'max_words' => (string) $request->get_max_word_count(), ) ); } /** * Build the draft stage prompt (multi-step stage 2). * * @param PRAutoBlogger_Content_Request $request Content request with settings. * @param string $outline The outline from stage 1. * @return string Draft prompt. */ public static function build_draft( PRAutoBlogger_Content_Request $request, string $outline ): string { return PRAutoBlogger_Prompt_Registry::render( 'content.draft', array( 'outline' => $outline, 'tone' => $request->get_tone(), 'min_words' => (string) $request->get_min_word_count(), 'max_words' => (string) $request->get_max_word_count(), 'keywords' => implode( ', ', $request->get_idea()->get_target_keywords() ), ) ); } /** * Build the polish stage prompt (multi-step stage 3). * * @param string $draft The draft from stage 2. * @return string Polish prompt. */ public static function build_polish( string $draft ): string { return PRAutoBlogger_Prompt_Registry::render( 'content.polish', array( 'draft' => $draft ) ); } /** * Build the system prompt for an Authority-tier editorial revision call. * * Used by Editorial_Revision_Caller when asking the writer to revise a * draft given the chief editor's critique. Keeps the revision focused on * applying specific feedback rather than a full re-draft. * * @param PRAutoBlogger_Article_Idea $idea The article idea under revision. * @return string System prompt for the revision call. */ public static function build_revision_system( PRAutoBlogger_Article_Idea $idea ): string { $niche = (string) get_option( 'prautoblogger_niche_description', '' ); $niche_clause = '' !== $niche ? " specializing in {$niche}" : ''; return sprintf( "You are an expert writer%s. You have written a draft article and received feedback from your chief editor. Your task is to revise the article to address EVERY point raised in the editor's critique. Do NOT rewrite sections that were not criticised. Return ONLY the revised HTML article with no commentary, preamble, or metadata. Topic: %s. Title: %s.", $niche_clause, $idea->get_topic(), $idea->get_suggested_title() ); } /** * Build the user prompt for an Authority-tier editorial revision call. * * @param string $current_draft Current HTML draft to be revised. * @param string $editor_notes Critique notes from the chief editor. * @return string User prompt for the revision call. */ public static function build_revision_user( string $current_draft, string $editor_notes ): string { return sprintf( "EDITOR CRITIQUE:\n%s\n\n---\nCURRENT DRAFT:\n%s\n\n---\nPlease revise the draft above to address every point in the editor critique. Return the complete revised article as HTML only.", $editor_notes, $current_draft ); } /** * Build the linking rules section (internal articles + peptide database). * * Fetches published blog posts and peptide pages, formats them as a * reference list so the model uses real URLs instead of fabricating them. * * @return string Complete linking rules block. */ private static function build_linking_rules(): string { $rules = "--- LINKING RULES ---\n"; $rules .= "NEVER fabricate or invent URLs. You do NOT have access to external sources.\n"; $rules .= "Only use the internal links listed below when linking within the article.\n"; $rules .= "If no listed article is relevant to a section, do not insert a link.\n"; $rules .= "Do NOT add links for peptide names -- those are injected automatically after generation.\n\n"; $rules .= self::build_article_links(); $rules .= '--- END LINKING RULES ---'; return $rules; } /** * Build a reference list of published blog posts for internal linking. * * @return string Formatted list, or a fallback note. */ private static function build_article_links(): string { $posts = get_posts( array( 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 30, 'orderby' => 'date', 'order' => 'DESC', 'fields' => 'ids', ) ); if ( empty( $posts ) ) { return "No published articles available for internal linking yet.\n"; } $lines = array( "Available article links (use where topically relevant):\n" ); foreach ( $posts as $post_id ) { $title = get_the_title( $post_id ); $url = get_permalink( $post_id ); if ( $title && $url ) { $lines[] = "- {$title}: {$url}"; } } return implode( "\n", $lines ) . "\n"; } }