diff --git a/includes/core/class-publisher.php b/includes/core/class-publisher.php index 9c2fe5c..ff51a8d 100755 --- a/includes/core/class-publisher.php +++ b/includes/core/class-publisher.php @@ -85,7 +85,7 @@ class PRAutoBlogger_Publisher { ): int { $post_data = [ 'post_title' => sanitize_text_field( $idea->get_suggested_title() ), - 'post_content' => wp_kses_post( $content ), + 'post_content' => wp_kses_post( $this->sanitize_llm_content( $content ) ), 'post_status' => $post_status, 'post_type' => 'post', 'post_author' => $this->get_default_author_id(), @@ -247,6 +247,47 @@ class PRAutoBlogger_Publisher { } } + /** + * Strip common LLM output artifacts from generated content. + * + * LLMs sometimes ignore format instructions and wrap content in markdown + * code fences (```html ... ```) or full HTML document structures + * (
). This method strips those wrappers so only clean + * article-body HTML reaches WordPress. + * + * @param string $content Raw LLM output. + * + * @return string Cleaned HTML suitable for post_content. + */ + private function sanitize_llm_content( string $content ): string { + $content = trim( $content ); + + // Strip markdown code fences: ```html ... ``` or ``` ... ``` + // Handles optional language identifier after opening fence. + if ( preg_match( '/^```(?:\w+)?\s*\n(.*?)```\s*$/s', $content, $matches ) ) { + $content = trim( $matches[1] ); + } + + // Strip full HTML document wrappers: ...content... + // Extract only the inner content. + if ( preg_match( '/]*>(.*)<\/body>/is', $content, $matches ) ) { + $content = trim( $matches[1] ); + } + + // Remove stray , , ..., , tags + // in case they appear without a proper document structure. + $content = preg_replace( '/<\/?(html|head|body|meta|title|!DOCTYPE)[^>]*>/i', '', $content ); + + // Remove leftover and