'gpt-4.1', 'engine' => 'gpt-4.1', 'family' => 'gpt41', 'aliases' => ['gpt-4.1','gpt-4-1','gpt_4_1','gpt41','gpt4.1'], ]; // PREMIUM (4o ve 4-turbo aynı aileye map) case 'gpt-4o': case 'gpt4o': case 'o4': case '4o': case 'gpt-4-turbo': case 'gpt4-turbo': return [ 'canonical' => 'gpt-4o', 'engine' => 'gpt-4o', 'family' => 'gpt4o', 'aliases' => ['gpt-4o','gpt4o','o4','4o','gpt-4-turbo','gpt4-turbo'], ]; // ECONOMY (default) default: return [ 'canonical' => 'gpt-4o-mini', 'engine' => 'gpt-4o-mini', 'family' => 'gpt4omini', 'aliases' => ['gpt-4o-mini','gpt4o-mini','4o-mini','mini'], ]; } } } /* Safe fallbacks (skipped if defined in the main file) */ if ( ! function_exists('aatb_model_cost') ) { function aatb_model_cost( string $model ): int { return match ( strtolower(trim($model)) ) { 'gpt-4o' => 5, 'gpt-4.1' => 3, 'gpt-4o-mini' => 1, default => 1, }; } } if ( ! function_exists('aatb_model_tier') ) { function aatb_model_tier( string $model ): string { return match ( strtolower(trim($model)) ) { 'gpt-4o' => 'premium', 'gpt-4.1' => 'balanced', default => 'economical', }; } } /*─────────────────────────────────────────────────────────────── * RankPilotAI universal HTTP wrapper * (set token_cost_hint once) *───────────────────────────────────────────────────────────────*/ function aatb_call_rankpilot_api( string $token, array $payload, string $route, string $method = 'POST' ){ $opts = get_option( 'aatb_settings', [] ); // If model missing, read from settings (mini by default) if ( empty( $payload['model'] ) ) { $payload['model'] = strtolower( $opts['model_choice'] ?? 'gpt-4o-mini' ); } // Normalize model name $pack = aatb_model_alias_pack( (string) $payload['model'] ); $canonical = $pack['canonical'] ?? 'gpt-4o-mini'; $payload['model'] = $canonical; $payload['engine'] = $pack['engine'] ?? $canonical; $payload['family'] = $pack['family'] ?? ''; $payload['aliases'] = $pack['aliases'] ?? []; // Billing / token cost (ensure via backward-compatible fields) $tier = function_exists('aatb_model_tier') ? aatb_model_tier($canonical) : 'economy'; $cost = function_exists('aatb_model_cost') ? aatb_model_cost($canonical) : 1; $payload['tier'] = $tier; // Set all names so either old or new backends can read them $payload['token_cost'] = $cost; // legacy name $payload['token_cost_hint'] = $cost; // new name $payload['model_cost'] = $cost; // legacy $payload['price_hint'] = $cost; // some stacks use this $payload['force_cost'] = $cost; // force if supported // Definitive charge hint for the backend $payload['charge'] = [ 'per' => 'alt-text', 'mode' => 'model_tier', 'tier' => $tier, 'tokens' => $cost, // economy=1, balanced=3, premium=5 'confirm' => 1, ]; // Language mirror (legacy backend compatibility) if ( isset($payload['language']) && ! isset($payload['lang']) ) { $payload['lang'] = $payload['language']; } // If no custom prompt provided, add from settings if ( empty($payload['custom_prompt']) && ! empty($opts['custom_prompt']) ) { $payload['custom_prompt'] = $opts['custom_prompt']; } // Base context $base = [ 'plugin' => 'ai-alt-text-builder', 'site_token' => trim( (string) $token ), 'site_url' => home_url(), 'domain' => parse_url( home_url(), PHP_URL_HOST ), ]; $url = 'https://rankpilotai.com/wp-json/rankpilotai/v1' . $route; $data = array_merge( $base, $payload ); $extra = [ 'headers' => [ 'User-Agent' => 'AI Alt Text Builder/' . ( defined('AATB_VERSION') ? AATB_VERSION : '1.x' ), 'Cache-Control' => 'no-cache', ], 'timeout' => 30, ]; $res = aatb_http_json( $url, $data, $method, $extra ); // WP_Error ise, sadece hata mesajını kısa şekilde logla if ( is_wp_error( $res ) ) { if ( defined('WP_DEBUG') && WP_DEBUG ) { error_log( '[AATB ERROR] HTTP error on route ' . $route . ': ' . implode( '; ', $res->get_error_messages() ) ); } throw new Exception( $res->get_error_message() ); } $code = (int) wp_remote_retrieve_response_code( $res ); $body = wp_remote_retrieve_body( $res ); $json = json_decode( $body, true ); if ( $code !== 200 ) { $msg = null; if ( is_array($json) ) { foreach ( ['error_detail','message','detail','error','msg','reason'] as $k ) { if ( ! empty($json[$k]) ) { $msg = $json[$k]; break; } } } if ( defined('WP_DEBUG') && WP_DEBUG ) { $dbg = $msg ?: 'HTTP ' . $code; error_log('[AATB ERROR] Non-200 response on route ' . $route . ': ' . $dbg); } throw new Exception( $msg ?: "HTTP {$code}" ); } return $json; } /** * Fetch token status from RankPilotAI */ function aatb_check_token_status( string $token ): array { try { return aatb_call_rankpilot_api( $token, [ 'cache_bust' => time(), 'include' => 'balances,usage,limits' ], '/token-info', 'POST' ); } catch ( Exception $e ) { return [ 'error' => $e->getMessage(), 'allowed_models' => [ 'gpt-4o-mini','gpt-4o','gpt-4.1' ] ]; } } /*─────────────────────────────────────────────────────────────── * token-info → Normalize GLOBAL “remaining” tokens *───────────────────────────────────────────────────────────────*/ function aatb_status_remaining( array $st ): int { if ( isset($st['remaining_total']) ) return (int)$st['remaining_total']; if ( isset($st['remaining_all']) ) return (int)$st['remaining_all']; if ( isset($st['global_remaining']) ) return (int)$st['global_remaining']; $fb = (int)($st['free_balance'] ?? 0); $pr = (int)($st['plan_remaining'] ?? 0); $pb = (int)($st['pack_balance'] ?? 0); $sum_bal = $fb + $pr + $pb; if ( $sum_bal > 0 ) return $sum_bal; $free = (int)($st['free'] ?? 0); $monthly = (int)($st['monthly_remaining'] ?? 0); $pack = (int)($st['token_pack'] ?? 0); $sum_legacy = $free + $monthly + $pack; if ( $sum_legacy > 0 ) return $sum_legacy; if ( isset($st['remaining']) ) return max(0, (int)$st['remaining']); $limit = (int)($st['plan_limit_total'] ?? $st['plan_limit'] ?? $st['token_limit'] ?? 0); $usage = null; foreach ( ['usage_total','all_usage','total_usage','token_usage','usage'] as $k ) { if ( isset($st[$k]) ) { $usage = (int)$st[$k]; break; } } if ( $usage === null ) $usage = (int)($st['site_usage'] ?? 0); return max(0, $limit - (int)$usage); } /*─────────────────────────────────────────────────────────────── * Optional: pick a random OpenAI key from constant/option *───────────────────────────────────────────────────────────────*/ function aatb_get_openai_key(): string { if ( defined( 'RP_OPENAI_KEYS' ) && RP_OPENAI_KEYS ) { $keys = preg_split( '/[\s,|]+/', RP_OPENAI_KEYS ); $keys = array_filter( array_map( 'trim', $keys ) ); if ( $keys ) return $keys[ array_rand( $keys ) ]; } $opt = get_option( 'aatb_settings', [] ); return $opt['openai_key'] ?? ''; } /*─────────────────────────────────────────────────────────────── * Alt-text score (length heuristic) *───────────────────────────────────────────────────────────────*/ function aatb_calc_score( string $alt = '' ): int { $l = strlen( trim( $alt ) ); return match ( true ) { $l === 0 => 0, $l <= 125 => 100, $l <= 150 => 80, $l <= 175 => 60, $l <= 200 => 20, default => 0, }; } /*-------------------------------------------------------------- * Quick helper: detect placeholder-like alt text *------------------------------------------------------------*/ function aatb_is_placeholder_alt( string $alt ): bool { return (bool) preg_match( '/(placeholder|cannot\sbe\sdisplayed|no specific content)/i', $alt ); }