Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 48 additions & 27 deletions presshub-ai-editor/includes/class-api-client.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,30 @@ class PressHub_AI_API_Client {
* provider default).
*/
public static function current_request_meta(): string {
$provider = (string) get_option( 'presshub_ai_provider', 'openai' );
$model = (string) get_option( 'presshub_ai_model_' . $provider, '' );
$max_tokens = (int) get_option( 'presshub_ai_max_tokens_' . $provider, PressHub_AI_Provider_Defaults::default_max_tokens() );
$provider = '';
$model = '';
$max_tokens = 0;

if ( class_exists( 'PressHub_AI_Provider_Store' ) ) {
$enabled = PressHub_AI_Provider_Store::get_all( true );
if ( ! empty( $enabled ) && is_array( $enabled ) ) {
$prov = $enabled[0];
$provider = (string) ( $prov['type'] ?? 'openai' );
$model = (string) ( $prov['default_model'] ?? '' );
$max_tokens = (int) ( $prov['max_tokens'] ?? PressHub_AI_Provider_Defaults::default_max_tokens() );
}
}

if ( '' === $provider ) {
$provider = (string) get_option( 'presshub_ai_provider', 'openai' );
}
if ( '' === $model ) {
$model = (string) get_option( 'presshub_ai_model_' . $provider, 'default' );
}
if ( $max_tokens <= 0 ) {
$max_tokens = (int) get_option( 'presshub_ai_max_tokens_' . $provider, PressHub_AI_Provider_Defaults::default_max_tokens() );
}

return 'provider=' . $provider
. ' model=' . ( '' !== $model ? $model : 'default' )
. ' max_tokens=' . $max_tokens;
Expand Down Expand Up @@ -296,18 +317,11 @@ public static function resolve_module_config( string $module ): array {
);
}

// 6. Temperature resolution
// 6. Temperature resolution: Module override -> Provider Store record -> Default
$opt_temp = ( 'tts' === $module )
? get_option( 'presshub_ai_briefing_tts_temperature', null )
: get_option( "presshub_ai_{$module}_temperature", null );

if ( null === $opt_temp || '' === $opt_temp ) {
$opt_temp = get_option( 'presshub_ai_temperature_' . $provider_id, null );
if ( ( null === $opt_temp || '' === $opt_temp ) && $provider_id !== $type ) {
$opt_temp = get_option( 'presshub_ai_temperature_' . $type, null );
}
}

if ( null !== $opt_temp && '' !== $opt_temp ) {
$temperature = max( 0.0, min( 2.0, (float) $opt_temp ) );
} elseif ( isset( $provider_record['temperature'] ) ) {
Expand All @@ -316,18 +330,11 @@ public static function resolve_module_config( string $module ): array {
$temperature = PressHub_AI_Provider_Defaults::default_temperature();
}

// 7. Max Tokens resolution
// 7. Max Tokens resolution: Module override -> Provider Store record -> Default
$opt_tokens = ( 'tts' === $module )
? get_option( 'presshub_ai_briefing_tts_max_tokens', null )
: get_option( "presshub_ai_{$module}_max_tokens", null );

if ( null === $opt_tokens || '' === $opt_tokens ) {
$opt_tokens = get_option( 'presshub_ai_max_tokens_' . $provider_id, null );
if ( ( null === $opt_tokens || '' === $opt_tokens ) && $provider_id !== $type ) {
$opt_tokens = get_option( 'presshub_ai_max_tokens_' . $type, null );
}
}

if ( null !== $opt_tokens && '' !== $opt_tokens ) {
$max_tokens = max( 1, (int) $opt_tokens );
} elseif ( isset( $provider_record['max_tokens'] ) ) {
Expand All @@ -336,18 +343,11 @@ public static function resolve_module_config( string $module ): array {
$max_tokens = PressHub_AI_Provider_Defaults::default_max_tokens();
}

// 8. Timeout resolution
// 8. Timeout resolution: Module override -> Provider Store record -> Default
$opt_timeout = ( 'tts' === $module )
? get_option( 'presshub_ai_briefing_tts_timeout', null )
: get_option( "presshub_ai_{$module}_timeout", null );

if ( null === $opt_timeout || '' === $opt_timeout ) {
$opt_timeout = get_option( 'presshub_ai_timeout_' . $provider_id, null );
if ( ( null === $opt_timeout || '' === $opt_timeout ) && $provider_id !== $type ) {
$opt_timeout = get_option( 'presshub_ai_timeout_' . $type, null );
}
}

if ( null !== $opt_timeout && '' !== $opt_timeout ) {
$timeout = max( 1, (int) $opt_timeout );
} elseif ( isset( $provider_record['timeout'] ) ) {
Expand Down Expand Up @@ -524,6 +524,18 @@ public function __construct( $module_or_provider = null ) {
if ( ! empty( $configured['api_key'] ) ) {
$this->api_key = $configured['api_key'];
}
if ( isset( $configured['temperature'] ) ) {
$this->temperature = (float) $configured['temperature'];
}
if ( isset( $configured['max_tokens'] ) ) {
$this->max_tokens = (int) $configured['max_tokens'];
}
if ( isset( $configured['timeout'] ) ) {
$this->timeout = (int) $configured['timeout'];
}
if ( ! empty( $configured['default_model'] ) ) {
$this->model = $configured['default_model'];
}
}
}

Expand Down Expand Up @@ -861,6 +873,15 @@ public function test_connection( $provider = null ) {
$this->current_action = 'custom_test';
if ( ! empty( $provider ) ) {
$prov_record = PressHub_AI_Provider_Store::get( $provider );
if ( null === $prov_record ) {
$all_provs = PressHub_AI_Provider_Store::get_all( false );
foreach ( $all_provs as $p ) {
if ( ( $p['type'] ?? '' ) === $provider || ( $p['id'] ?? '' ) === $provider ) {
$prov_record = $p;
break;
}
}
}
if ( $prov_record ) {
$this->set_provider_config( $prov_record );
} else {
Expand Down
40 changes: 38 additions & 2 deletions presshub-ai-editor/presshub-ai-editor.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* Plugin Name: PressHub AI Co-Pilot
* Description: AI Co-Authoring and Editorial Workflow for PressHub.
* Version: 2.3.4
* Version: 2.3.5
* Requires at least: 6.0
* Requires PHP: 7.4
* Tested up to: 6.7
Expand Down Expand Up @@ -42,7 +42,43 @@ function presshub_ai_migrate_max_tokens_defaults() {
}
add_action( 'admin_init', 'presshub_ai_migrate_max_tokens_defaults' );

define( 'PRESSHUB_AI_VERSION', '2.3.4' );
/**
* Purge legacy flat provider tuning options (max_tokens, temperature, timeout)
* from wp_options so they no longer shadow Provider Store records.
* Also migrate any uncustomized 16384 provider records in Provider Store to 20000.
*/
if ( ! function_exists( 'presshub_ai_migrate_purge_legacy_provider_tuning' ) ) {
function presshub_ai_migrate_purge_legacy_provider_tuning() {
if ( '1' === get_option( 'presshub_ai_migrated_provider_store_precedence', '0' ) ) {
return;
}

foreach ( array( 'openai', 'anthropic', 'gemini' ) as $provider ) {
delete_option( 'presshub_ai_max_tokens_' . $provider );
delete_option( 'presshub_ai_temperature_' . $provider );
delete_option( 'presshub_ai_timeout_' . $provider );
}

if ( class_exists( 'PressHub_AI_Provider_Store' ) ) {
$providers = PressHub_AI_Provider_Store::get_all( false );
$mutated = false;
foreach ( $providers as $i => $prov ) {
if ( isset( $prov['max_tokens'] ) && 16384 === (int) $prov['max_tokens'] ) {
$providers[ $i ]['max_tokens'] = 20000;
$mutated = true;
}
}
if ( $mutated ) {
update_option( PressHub_AI_Provider_Store::OPTION_CONFIGURED_PROVIDERS, $providers, false );
}
}

update_option( 'presshub_ai_migrated_provider_store_precedence', '1', false );
}
}
add_action( 'admin_init', 'presshub_ai_migrate_purge_legacy_provider_tuning' );

define( 'PRESSHUB_AI_VERSION', '2.3.5' );
define( 'PRESSHUB_AI_DIR', plugin_dir_path( __FILE__ ) );
define( 'PRESSHUB_AI_URL', plugin_dir_url( __FILE__ ) );

Expand Down
35 changes: 35 additions & 0 deletions presshub-ai-editor/tests/APIClientModularTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,41 @@ public static function run(): void {
$failures[] = "tts resolution failed. Got: " . var_export( $tts_cfg, true );
}

// ==================================================================
// 2b. Provider Store tuning precedence: $provider_record['max_tokens']
// beats legacy flat options in wp_options.
// ==================================================================
PressHub_AI_Provider_Store::save_provider( [
'id' => 'tuning-precedence-prov',
'type' => 'gemini',
'name' => 'Tuning Precedence Gemini',
'max_tokens' => 28000,
'temperature' => 0.35,
'timeout' => 150,
'enabled' => true,
] );
$GLOBALS['OPTIONS_STORE']['presshub_ai_briefing_text_provider'] = 'tuning-precedence-prov';
// Simulate stale legacy options from an upgraded install
$GLOBALS['OPTIONS_STORE']['presshub_ai_max_tokens_gemini'] = '10000';
$GLOBALS['OPTIONS_STORE']['presshub_ai_temperature_gemini'] = '0.9';
$GLOBALS['OPTIONS_STORE']['presshub_ai_timeout_gemini'] = '45';

$precedence_cfg = PressHub_AI_API_Client::resolve_module_config( 'briefing_text' );
if ( $precedence_cfg['max_tokens'] !== 28000 ) {
$failures[] = "Provider Store max_tokens (28000) not honored; got: " . var_export( $precedence_cfg['max_tokens'], true );
}
if ( $precedence_cfg['temperature'] !== 0.35 ) {
$failures[] = "Provider Store temperature (0.35) not honored; got: " . var_export( $precedence_cfg['temperature'], true );
}
if ( $precedence_cfg['timeout'] !== 150 ) {
$failures[] = "Provider Store timeout (150) not honored; got: " . var_export( $precedence_cfg['timeout'], true );
}

// Clean up legacy options
unset( $GLOBALS['OPTIONS_STORE']['presshub_ai_max_tokens_gemini'] );
unset( $GLOBALS['OPTIONS_STORE']['presshub_ai_temperature_gemini'] );
unset( $GLOBALS['OPTIONS_STORE']['presshub_ai_timeout_gemini'] );

// ==================================================================
// 3. Fallback when module options are empty: first enabled provider in store
// ==================================================================
Expand Down
41 changes: 35 additions & 6 deletions presshub-ai-editor/tests/MaxTokensMigrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,45 @@ function mtm_check( $label, $condition ) {
presshub_ai_migrate_max_tokens_defaults();
mtm_check( 'second run is a no-op', $before === $GLOBALS['OPTIONS_STORE'] );

// --- Case 3: migration flag already set → untouched stale values ------------
// --- Case 4: purge legacy tuning options & migrate 16384 store records ------
$GLOBALS['OPTIONS_STORE'] = [
'presshub_ai_migrated_max_tokens' => '1',
'presshub_ai_max_tokens_openai' => '2000',
'presshub_ai_max_tokens_openai' => '10000',
'presshub_ai_max_tokens_anthropic' => '4000',
'presshub_ai_max_tokens_gemini' => '16384',
'presshub_ai_temperature_openai' => '0.7',
'presshub_ai_timeout_gemini' => '120',
'presshub_ai_configured_providers' => [
[
'id' => 'prov-1',
'type' => 'gemini',
'max_tokens' => 16384, // uncustomized old default
],
[
'id' => 'prov-2',
'type' => 'openai',
'max_tokens' => 32000, // customized value
],
],
];
presshub_ai_migrate_max_tokens_defaults();
mtm_check( 'flag-set run leaves values alone', isset( $GLOBALS['OPTIONS_STORE']['presshub_ai_max_tokens_openai'] ) );

presshub_ai_migrate_purge_legacy_provider_tuning();

mtm_check( 'legacy max_tokens_openai purged', ! isset( $GLOBALS['OPTIONS_STORE']['presshub_ai_max_tokens_openai'] ) );
mtm_check( 'legacy max_tokens_anthropic purged', ! isset( $GLOBALS['OPTIONS_STORE']['presshub_ai_max_tokens_anthropic'] ) );
mtm_check( 'legacy max_tokens_gemini purged', ! isset( $GLOBALS['OPTIONS_STORE']['presshub_ai_max_tokens_gemini'] ) );
mtm_check( 'legacy temperature_openai purged', ! isset( $GLOBALS['OPTIONS_STORE']['presshub_ai_temperature_openai'] ) );
mtm_check( 'legacy timeout_gemini purged', ! isset( $GLOBALS['OPTIONS_STORE']['presshub_ai_timeout_gemini'] ) );
mtm_check( 'uncustomized 16384 migrated to 20000', 20000 === ( $GLOBALS['OPTIONS_STORE']['presshub_ai_configured_providers'][0]['max_tokens'] ?? 0 ) );
mtm_check( 'customized 32000 left unchanged', 32000 === ( $GLOBALS['OPTIONS_STORE']['presshub_ai_configured_providers'][1]['max_tokens'] ?? 0 ) );
mtm_check( 'purge migration flag set', ( $GLOBALS['OPTIONS_STORE']['presshub_ai_migrated_provider_store_precedence'] ?? '' ) === '1' );

// Case 4b: Idempotent
$before_purge = $GLOBALS['OPTIONS_STORE'];
presshub_ai_migrate_purge_legacy_provider_tuning();
mtm_check( 'purge migration is idempotent', $before_purge === $GLOBALS['OPTIONS_STORE'] );

if ( $failures > 0 ) {
fwrite( STDERR, "MaxTokensMigrationTest: {$failures} failure(s)\n" );
exit( 1 );
}
echo "MaxTokensMigrationTest: OK (8 checks)\n";
echo "MaxTokensMigrationTest: OK (17 checks)\n";
Loading