From 39570e45866f85f42f5d1ddc6d2647656e01ff8a Mon Sep 17 00:00:00 2001 From: peptiderepo Date: Sun, 19 Apr 2026 23:54:26 +0700 Subject: [PATCH] feat: add OpenRouter reasoning mode support (v0.4.9) Enables reasoning-capable models (Grok 4.1 Fast, DeepSeek-R1, etc.) via new admin settings: Enable Reasoning toggle and Reasoning Effort selector. Provider auto-injects reasoning params when enabled; callers can override per-call via options array. Response parser captures reasoning_tokens and reasoning_content for visibility. Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.md | 14 +++++++++++ .../admin/class-settings-fields-extended.php | 25 +++++++++++++++++++ .../providers/class-open-router-provider.php | 13 +++++++++- .../class-open-router-response-parser.php | 7 ++++++ prautoblogger.php | 4 +-- 5 files changed, 60 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9fbb04..5c93f1d 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,20 @@ All notable changes to PRAutoBlogger will be documented in this file. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project uses [Semantic Versioning](https://semver.org/). +## [0.4.9] — 2026-04-19 + +### Added +- **OpenRouter reasoning mode support.** New "Enable Reasoning" toggle and + "Reasoning Effort" selector in AI Models settings. When enabled, sends + `reasoning: {enabled: true, effort: ""}` to models that support it + (Grok 4.1 Fast, DeepSeek-R1, etc.). Effort levels: Extra High, High, + Medium, Low, Minimal. Reasoning tokens are billed as output tokens — cost + is automatically tracked. Models that don't support reasoning ignore the + parameter. Per-call override available via the `reasoning` key in the + provider options array. +- Response parser now captures `reasoning_tokens` and `reasoning_content` + from the OpenRouter response for downstream logging and debugging. + ## [0.4.8] — 2026-04-19 ### Fixed diff --git a/includes/admin/class-settings-fields-extended.php b/includes/admin/class-settings-fields-extended.php index b18d818..b21dc84 100644 --- a/includes/admin/class-settings-fields-extended.php +++ b/includes/admin/class-settings-fields-extended.php @@ -80,6 +80,31 @@ class PRAutoBlogger_Settings_Fields_Extended { */ public static function get_fields(): array { return [ + // ── AI Models (extended) ──────────────────────────────────── + [ + 'id' => 'prautoblogger_reasoning_enabled', + 'label' => __( 'Enable Reasoning', 'prautoblogger' ), + 'type' => 'toggle', + 'section' => 'prautoblogger_models', + 'default' => '0', + 'description' => __( 'Send reasoning instructions to models that support it (e.g. Grok, DeepSeek-R1). Reasoning tokens are billed as output tokens — expect higher costs per call. Models that don\'t support reasoning will ignore this.', 'prautoblogger' ), + ], + [ + 'id' => 'prautoblogger_reasoning_effort', + 'label' => __( 'Reasoning Effort', 'prautoblogger' ), + 'type' => 'select', + 'section' => 'prautoblogger_models', + 'default' => 'medium', + 'options' => [ + 'xhigh' => __( 'Extra High — maximum depth, highest cost', 'prautoblogger' ), + 'high' => __( 'High — thorough reasoning', 'prautoblogger' ), + 'medium' => __( 'Medium — balanced (recommended)', 'prautoblogger' ), + 'low' => __( 'Low — light reasoning, lower cost', 'prautoblogger' ), + 'minimal' => __( 'Minimal — barely any reasoning', 'prautoblogger' ), + ], + 'description' => __( 'How much effort the model spends reasoning before answering. Higher effort = more reasoning tokens = higher cost. Only applies when reasoning is enabled above.', 'prautoblogger' ), + ], + // ── Schedule & Budget ─────────────────────────────────────── [ 'id' => 'prautoblogger_daily_article_target', diff --git a/includes/providers/class-open-router-provider.php b/includes/providers/class-open-router-provider.php index f16ed1a..4b4354a 100644 --- a/includes/providers/class-open-router-provider.php +++ b/includes/providers/class-open-router-provider.php @@ -36,7 +36,8 @@ class PRAutoBlogger_OpenRouter_Provider implements PRAutoBlogger_LLM_Provider_In * temperature?: float, * max_tokens?: int, * response_format?: array{type: string}, - * } $options Optional parameters. + * reasoning?: array{enabled: bool, effort?: string}, + * } $options Optional parameters. Pass 'reasoning' to override the global setting. * * @return array{ * content: string, @@ -88,6 +89,16 @@ class PRAutoBlogger_OpenRouter_Provider implements PRAutoBlogger_LLM_Provider_In $body['response_format'] = $options['response_format']; } + // Reasoning mode: explicit caller override takes priority, then global setting. + if ( isset( $options['reasoning'] ) ) { + $body['reasoning'] = $options['reasoning']; + } elseif ( '1' === get_option( 'prautoblogger_reasoning_enabled', '0' ) ) { + $body['reasoning'] = [ + 'enabled' => true, + 'effort' => get_option( 'prautoblogger_reasoning_effort', 'medium' ), + ]; + } + $last_error = ''; $config = new PRAutoBlogger_OpenRouter_Config(); diff --git a/includes/providers/class-open-router-response-parser.php b/includes/providers/class-open-router-response-parser.php index 5e94cd3..9915cbb 100644 --- a/includes/providers/class-open-router-response-parser.php +++ b/includes/providers/class-open-router-response-parser.php @@ -43,8 +43,10 @@ class PRAutoBlogger_OpenRouter_Response_Parser { * model: string, * prompt_tokens: int, * completion_tokens: int, + * reasoning_tokens: int, * total_tokens: int, * finish_reason: string, + * reasoning_content: string, * } * * @throws \RuntimeException If response structure is invalid. @@ -94,13 +96,18 @@ class PRAutoBlogger_OpenRouter_Response_Parser { $usage = $data['usage'] ?? []; + // Reasoning content lives alongside 'content' in the message object. + $reasoning_content = $data['choices'][0]['message']['reasoning'] ?? ''; + return [ 'content' => (string) $content, 'model' => $data['model'] ?? $model, 'prompt_tokens' => (int) ( $usage['prompt_tokens'] ?? 0 ), 'completion_tokens' => (int) ( $usage['completion_tokens'] ?? 0 ), + 'reasoning_tokens' => (int) ( $usage['reasoning_tokens'] ?? 0 ), 'total_tokens' => (int) ( $usage['total_tokens'] ?? 0 ), 'finish_reason' => $data['choices'][0]['finish_reason'] ?? 'unknown', + 'reasoning_content' => (string) $reasoning_content, ]; } diff --git a/prautoblogger.php b/prautoblogger.php index 2ee55ed..d2e5859 100644 --- a/prautoblogger.php +++ b/prautoblogger.php @@ -8,7 +8,7 @@ declare(strict_types=1); * Plugin Name: PRAutoBlogger * Plugin URI: https://peptiderepo.com/prautoblogger * Description: Monitors social media for trending topics, generates SEO-friendly blog posts using AI, and publishes them on a daily schedule with full cost tracking and self-improvement metrics. - * Version: 0.4.8 + * Version: 0.4.9 * Requires at least: 6.0 * Requires PHP: 7.4 * Author: PeptideRepo @@ -35,7 +35,7 @@ if ( ! defined( 'ABSPATH' ) ) { | and limits without magic strings. */ -define( 'PRAUTOBLOGGER_VERSION', '0.4.8' ); +define( 'PRAUTOBLOGGER_VERSION', '0.4.9' ); define( 'PRAUTOBLOGGER_DB_VERSION', '1.1.0' ); define( 'PRAUTOBLOGGER_PLUGIN_FILE', __FILE__ ); define( 'PRAUTOBLOGGER_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );