style(phpcs): autofix WordPress-Core violations via phpcbf
Run phpcbf --standard=WordPress-Core across includes/, prautoblogger.php, and uninstall.php. Fixed 1,362 formatting violations in 75 files: - Short array syntax: [] → array() - Line breaking for long function calls - Consistent indentation - Function call parameter alignment No logic changes; purely mechanical formatting.
This commit is contained in:
@@ -45,7 +45,7 @@ class PRAutoBlogger_LLM_Research_Provider implements PRAutoBlogger_Source_Provid
|
||||
|
||||
if ( '' === trim( $prompt ) ) {
|
||||
PRAutoBlogger_Logger::instance()->warning( 'LLM Research prompt is empty. Skipping.', 'llm-research' );
|
||||
return [];
|
||||
return array();
|
||||
}
|
||||
|
||||
$system_prompt = $this->build_system_prompt();
|
||||
@@ -53,16 +53,22 @@ class PRAutoBlogger_LLM_Research_Provider implements PRAutoBlogger_Source_Provid
|
||||
|
||||
$llm = new PRAutoBlogger_OpenRouter_Provider();
|
||||
$response = $llm->send_chat_completion(
|
||||
[
|
||||
[ 'role' => 'system', 'content' => $system_prompt ],
|
||||
[ 'role' => 'user', 'content' => $user_prompt ],
|
||||
],
|
||||
array(
|
||||
array(
|
||||
'role' => 'system',
|
||||
'content' => $system_prompt,
|
||||
),
|
||||
array(
|
||||
'role' => 'user',
|
||||
'content' => $user_prompt,
|
||||
),
|
||||
),
|
||||
$model,
|
||||
[
|
||||
array(
|
||||
'temperature' => 0.7,
|
||||
'max_tokens' => 8000,
|
||||
'response_format' => [ 'type' => 'json_object' ],
|
||||
]
|
||||
'response_format' => array( 'type' => 'json_object' ),
|
||||
)
|
||||
);
|
||||
|
||||
// Use the pipeline's cost tracker if provided (for run_id tagging),
|
||||
@@ -102,11 +108,11 @@ class PRAutoBlogger_LLM_Research_Provider implements PRAutoBlogger_Source_Provid
|
||||
* @return array{remaining: int, limit: int, resets_at: string}
|
||||
*/
|
||||
public function get_rate_limit_status(): array {
|
||||
return [
|
||||
return array(
|
||||
'remaining' => 999,
|
||||
'limit' => 999,
|
||||
'resets_at' => '',
|
||||
];
|
||||
'resets_at' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,7 +139,7 @@ class PRAutoBlogger_LLM_Research_Provider implements PRAutoBlogger_Source_Provid
|
||||
. "Respond with valid JSON only (no preamble, no markdown fences):\n"
|
||||
. '{"findings": [{"title": "string", "content": "string (2-3 paragraphs)", '
|
||||
. '"relevance_score": 0-100, "category": "question|trend|comparison|guide|misconception|safety"}]}' . "\n\n"
|
||||
. "Generate 8-12 findings. Prioritize depth over breadth. Avoid generic or surface-level topics.";
|
||||
. 'Generate 8-12 findings. Prioritize depth over breadth. Avoid generic or surface-level topics.';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,16 +170,16 @@ class PRAutoBlogger_LLM_Research_Provider implements PRAutoBlogger_Source_Provid
|
||||
. mb_substr( $content, 0, 500 ),
|
||||
'llm-research'
|
||||
);
|
||||
return [];
|
||||
return array();
|
||||
}
|
||||
|
||||
$items = [];
|
||||
$items = array();
|
||||
$run_date = gmdate( 'Y-m-d' );
|
||||
|
||||
foreach ( $data['findings'] as $index => $finding ) {
|
||||
$title = sanitize_text_field( $finding['title'] ?? '' );
|
||||
$body = sanitize_textarea_field( $finding['content'] ?? '' );
|
||||
$score = min( 100, max( 0, (int) ( $finding['relevance_score'] ?? 50 ) ) );
|
||||
$title = sanitize_text_field( $finding['title'] ?? '' );
|
||||
$body = sanitize_textarea_field( $finding['content'] ?? '' );
|
||||
$score = min( 100, max( 0, (int) ( $finding['relevance_score'] ?? 50 ) ) );
|
||||
$category = sanitize_text_field( $finding['category'] ?? 'guide' );
|
||||
|
||||
if ( '' === $title || '' === $body ) {
|
||||
@@ -184,23 +190,25 @@ class PRAutoBlogger_LLM_Research_Provider implements PRAutoBlogger_Source_Provid
|
||||
// but a new day produces fresh entries.
|
||||
$source_id = 'llm_' . $run_date . '_' . $index;
|
||||
|
||||
$items[] = new PRAutoBlogger_Source_Data( [
|
||||
'source_type' => self::SOURCE_TYPE,
|
||||
'source_id' => $source_id,
|
||||
'subreddit' => null,
|
||||
'title' => $title,
|
||||
'content' => $body,
|
||||
'author' => $model,
|
||||
'score' => $score,
|
||||
'comment_count' => 0,
|
||||
'permalink' => null,
|
||||
'collected_at' => current_time( 'mysql' ),
|
||||
'metadata' => [
|
||||
'category' => $category,
|
||||
'data_source' => 'llm_research',
|
||||
'model' => $model,
|
||||
],
|
||||
] );
|
||||
$items[] = new PRAutoBlogger_Source_Data(
|
||||
array(
|
||||
'source_type' => self::SOURCE_TYPE,
|
||||
'source_id' => $source_id,
|
||||
'subreddit' => null,
|
||||
'title' => $title,
|
||||
'content' => $body,
|
||||
'author' => $model,
|
||||
'score' => $score,
|
||||
'comment_count' => 0,
|
||||
'permalink' => null,
|
||||
'collected_at' => current_time( 'mysql' ),
|
||||
'metadata' => array(
|
||||
'category' => $category,
|
||||
'data_source' => 'llm_research',
|
||||
'model' => $model,
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
PRAutoBlogger_Logger::instance()->info(
|
||||
|
||||
@@ -41,7 +41,7 @@ class PRAutoBlogger_OpenRouter_Embedding_Provider {
|
||||
*/
|
||||
public function get_embeddings( array $texts, string $model = '' ): array {
|
||||
if ( empty( $texts ) ) {
|
||||
return [];
|
||||
return array();
|
||||
}
|
||||
|
||||
$api_key = $this->get_api_key();
|
||||
@@ -64,14 +64,16 @@ class PRAutoBlogger_OpenRouter_Embedding_Provider {
|
||||
try {
|
||||
$response = wp_remote_post(
|
||||
$base_url . '/embeddings',
|
||||
[
|
||||
array(
|
||||
'timeout' => 30,
|
||||
'headers' => $request_headers,
|
||||
'body' => wp_json_encode( [
|
||||
'model' => $model,
|
||||
'input' => array_values( $texts ),
|
||||
] ),
|
||||
]
|
||||
'body' => wp_json_encode(
|
||||
array(
|
||||
'model' => $model,
|
||||
'input' => array_values( $texts ),
|
||||
)
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
@@ -149,11 +151,14 @@ class PRAutoBlogger_OpenRouter_Embedding_Provider {
|
||||
}
|
||||
|
||||
// Sort by index to match input order (API may return out of order).
|
||||
usort( $body['data'], static function ( array $a, array $b ): int {
|
||||
return ( $a['index'] ?? 0 ) <=> ( $b['index'] ?? 0 );
|
||||
} );
|
||||
usort(
|
||||
$body['data'],
|
||||
static function ( array $a, array $b ): int {
|
||||
return ( $a['index'] ?? 0 ) <=> ( $b['index'] ?? 0 );
|
||||
}
|
||||
);
|
||||
|
||||
$embeddings = [];
|
||||
$embeddings = array();
|
||||
foreach ( $body['data'] as $item ) {
|
||||
if ( ! isset( $item['embedding'] ) || ! is_array( $item['embedding'] ) ) {
|
||||
throw new \RuntimeException( 'Embedding item missing "embedding" array.' );
|
||||
|
||||
@@ -73,7 +73,7 @@ class PRAutoBlogger_OpenRouter_Image_Batch {
|
||||
*/
|
||||
public function execute( array $requests ): array {
|
||||
if ( empty( $requests ) ) {
|
||||
return [];
|
||||
return array();
|
||||
}
|
||||
|
||||
$api_key = $this->support->get_api_key();
|
||||
@@ -86,15 +86,15 @@ class PRAutoBlogger_OpenRouter_Image_Batch {
|
||||
);
|
||||
|
||||
// Convert associative headers to curl-style indexed array.
|
||||
$curl_headers = [];
|
||||
$curl_headers = array();
|
||||
foreach ( $headers as $name => $value ) {
|
||||
$curl_headers[] = $name . ': ' . $value;
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_multi_init
|
||||
$multi_handle = curl_multi_init();
|
||||
$handles = [];
|
||||
$start_times = [];
|
||||
$handles = array();
|
||||
$start_times = array();
|
||||
|
||||
foreach ( $requests as $key => $req ) {
|
||||
$model = $this->pricing->resolve_model( (string) ( $req['options']['model'] ?? '' ) );
|
||||
@@ -119,7 +119,11 @@ class PRAutoBlogger_OpenRouter_Image_Batch {
|
||||
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_multi_add_handle
|
||||
curl_multi_add_handle( $multi_handle, $ch );
|
||||
$handles[ $key ] = [ 'handle' => $ch, 'model' => $model, 'req' => $req ];
|
||||
$handles[ $key ] = array(
|
||||
'handle' => $ch,
|
||||
'model' => $model,
|
||||
'req' => $req,
|
||||
);
|
||||
$start_times[ $key ] = microtime( true );
|
||||
}
|
||||
|
||||
@@ -127,7 +131,7 @@ class PRAutoBlogger_OpenRouter_Image_Batch {
|
||||
$this->run_multi( $multi_handle );
|
||||
|
||||
// Collect results.
|
||||
$results = [];
|
||||
$results = array();
|
||||
foreach ( $handles as $key => $entry ) {
|
||||
$results[ $key ] = $this->collect_result(
|
||||
(string) $key,
|
||||
@@ -200,7 +204,7 @@ class PRAutoBlogger_OpenRouter_Image_Batch {
|
||||
$detail
|
||||
);
|
||||
PRAutoBlogger_Logger::instance()->error( $msg, 'openrouter-image-batch' );
|
||||
return [ 'error' => $msg ];
|
||||
return array( 'error' => $msg );
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_getinfo
|
||||
@@ -216,7 +220,7 @@ class PRAutoBlogger_OpenRouter_Image_Batch {
|
||||
substr( $raw, 0, 300 )
|
||||
);
|
||||
PRAutoBlogger_Logger::instance()->error( $msg, 'openrouter-image-batch' );
|
||||
return [ 'error' => $msg ];
|
||||
return array( 'error' => $msg );
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -228,10 +232,10 @@ class PRAutoBlogger_OpenRouter_Image_Batch {
|
||||
$e->getMessage()
|
||||
);
|
||||
PRAutoBlogger_Logger::instance()->error( $msg, 'openrouter-image-batch' );
|
||||
return [ 'error' => $msg ];
|
||||
return array( 'error' => $msg );
|
||||
}
|
||||
|
||||
return [
|
||||
return array(
|
||||
'bytes' => $image_bytes,
|
||||
'mime_type' => 'image/png',
|
||||
'width' => $req['width'],
|
||||
@@ -240,7 +244,7 @@ class PRAutoBlogger_OpenRouter_Image_Batch {
|
||||
'seed' => null,
|
||||
'cost_usd' => $this->pricing->estimate_cost( $req['width'], $req['height'], $model ),
|
||||
'latency_ms' => $latency_ms,
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,17 +263,45 @@ class PRAutoBlogger_OpenRouter_Image_Batch {
|
||||
$target = $height > 0 ? (float) $width / (float) $height : 1.0;
|
||||
|
||||
// Snap to nearest standard aspect ratio.
|
||||
$aspects = [
|
||||
[ 'w' => 1, 'h' => 1, 'r' => 1.0 ],
|
||||
[ 'w' => 3, 'h' => 2, 'r' => 1.5 ],
|
||||
[ 'w' => 2, 'h' => 3, 'r' => 0.6667 ],
|
||||
[ 'w' => 4, 'h' => 3, 'r' => 1.3333 ],
|
||||
[ 'w' => 3, 'h' => 4, 'r' => 0.75 ],
|
||||
[ 'w' => 16, 'h' => 9, 'r' => 1.7778 ],
|
||||
[ 'w' => 9, 'h' => 16, 'r' => 0.5625 ],
|
||||
];
|
||||
$best = $aspects[0];
|
||||
$best_d = abs( $target - $best['r'] );
|
||||
$aspects = array(
|
||||
array(
|
||||
'w' => 1,
|
||||
'h' => 1,
|
||||
'r' => 1.0,
|
||||
),
|
||||
array(
|
||||
'w' => 3,
|
||||
'h' => 2,
|
||||
'r' => 1.5,
|
||||
),
|
||||
array(
|
||||
'w' => 2,
|
||||
'h' => 3,
|
||||
'r' => 0.6667,
|
||||
),
|
||||
array(
|
||||
'w' => 4,
|
||||
'h' => 3,
|
||||
'r' => 1.3333,
|
||||
),
|
||||
array(
|
||||
'w' => 3,
|
||||
'h' => 4,
|
||||
'r' => 0.75,
|
||||
),
|
||||
array(
|
||||
'w' => 16,
|
||||
'h' => 9,
|
||||
'r' => 1.7778,
|
||||
),
|
||||
array(
|
||||
'w' => 9,
|
||||
'h' => 16,
|
||||
'r' => 0.5625,
|
||||
),
|
||||
);
|
||||
$best = $aspects[0];
|
||||
$best_d = abs( $target - $best['r'] );
|
||||
foreach ( $aspects as $c ) {
|
||||
$d = abs( $target - $c['r'] );
|
||||
if ( $d < $best_d ) {
|
||||
@@ -279,16 +311,16 @@ class PRAutoBlogger_OpenRouter_Image_Batch {
|
||||
}
|
||||
$aspect = $best['w'] . ':' . $best['h'];
|
||||
|
||||
return [
|
||||
return array(
|
||||
'model' => $model,
|
||||
'messages' => [
|
||||
[
|
||||
'messages' => array(
|
||||
array(
|
||||
'role' => 'user',
|
||||
'content' => 'Generate an image: ' . $prompt,
|
||||
],
|
||||
],
|
||||
'modalities' => [ 'image', 'text' ],
|
||||
'image_config' => [ 'aspect_ratio' => $aspect ],
|
||||
];
|
||||
),
|
||||
),
|
||||
'modalities' => array( 'image', 'text' ),
|
||||
'image_config' => array( 'aspect_ratio' => $aspect ),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,13 +22,13 @@ class PRAutoBlogger_OpenRouter_Image_Pricing {
|
||||
*
|
||||
* @var array<string, float>
|
||||
*/
|
||||
private const COST_PER_IMAGE = [
|
||||
'google/gemini-2.5-flash-image' => 0.005,
|
||||
'google/gemini-3.1-flash-image-preview' => 0.008,
|
||||
'google/gemini-3-pro-image-preview' => 0.03,
|
||||
'openai/gpt-5-image-mini' => 0.02,
|
||||
'openai/gpt-5-image' => 0.08,
|
||||
];
|
||||
private const COST_PER_IMAGE = array(
|
||||
'google/gemini-2.5-flash-image' => 0.005,
|
||||
'google/gemini-3.1-flash-image-preview' => 0.008,
|
||||
'google/gemini-3-pro-image-preview' => 0.03,
|
||||
'openai/gpt-5-image-mini' => 0.02,
|
||||
'openai/gpt-5-image' => 0.08,
|
||||
);
|
||||
|
||||
/**
|
||||
* Resolve the model identifier from a hint, falling back to site option,
|
||||
|
||||
@@ -43,7 +43,7 @@ class PRAutoBlogger_OpenRouter_Image_Provider implements PRAutoBlogger_Image_Pro
|
||||
*
|
||||
* Retries 5xx/429 with exponential backoff; 4xx (except 429) fail immediately.
|
||||
*/
|
||||
public function generate_image( string $prompt, int $width, int $height, array $options = [] ): array {
|
||||
public function generate_image( string $prompt, int $width, int $height, array $options = array() ): array {
|
||||
$this->assert_valid_dimensions( $width, $height );
|
||||
$prompt = trim( $prompt );
|
||||
if ( '' === $prompt ) {
|
||||
@@ -74,11 +74,14 @@ class PRAutoBlogger_OpenRouter_Image_Provider implements PRAutoBlogger_Image_Pro
|
||||
|
||||
try {
|
||||
for ( $attempt = 1; $attempt <= PRAUTOBLOGGER_MAX_RETRIES; $attempt++ ) {
|
||||
$response = wp_remote_post( $url, [
|
||||
'timeout' => PRAUTOBLOGGER_API_TIMEOUT_SECONDS,
|
||||
'headers' => $headers,
|
||||
'body' => wp_json_encode( $body ),
|
||||
] );
|
||||
$response = wp_remote_post(
|
||||
$url,
|
||||
array(
|
||||
'timeout' => PRAUTOBLOGGER_API_TIMEOUT_SECONDS,
|
||||
'headers' => $headers,
|
||||
'body' => wp_json_encode( $body ),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
$last_error = $response->get_error_message();
|
||||
@@ -116,7 +119,7 @@ class PRAutoBlogger_OpenRouter_Image_Provider implements PRAutoBlogger_Image_Pro
|
||||
|
||||
$image_bytes = $this->support->extract_image_bytes( $raw );
|
||||
|
||||
return [
|
||||
return array(
|
||||
'bytes' => $image_bytes,
|
||||
'mime_type' => 'image/png',
|
||||
'width' => $width,
|
||||
@@ -125,7 +128,7 @@ class PRAutoBlogger_OpenRouter_Image_Provider implements PRAutoBlogger_Image_Pro
|
||||
'seed' => null,
|
||||
'cost_usd' => $this->pricing->estimate_cost( $width, $height, $model ),
|
||||
'latency_ms' => (int) ( ( microtime( true ) - $started_at ) * 1000 ),
|
||||
];
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
remove_action( 'http_api_curl', $curl_filter, 99 );
|
||||
@@ -176,7 +179,7 @@ class PRAutoBlogger_OpenRouter_Image_Provider implements PRAutoBlogger_Image_Pro
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function estimate_cost( int $width, int $height, array $options = [] ): float {
|
||||
public function estimate_cost( int $width, int $height, array $options = array() ): float {
|
||||
$model = $this->pricing->resolve_model( (string) ( $options['model'] ?? '' ) );
|
||||
return $this->pricing->estimate_cost( $width, $height, $model );
|
||||
}
|
||||
@@ -191,32 +194,41 @@ class PRAutoBlogger_OpenRouter_Image_Provider implements PRAutoBlogger_Image_Pro
|
||||
try {
|
||||
$api_key = $this->support->get_api_key();
|
||||
} catch ( \RuntimeException $e ) {
|
||||
return [ 'status' => 'error', 'message' => $e->getMessage() ];
|
||||
return array(
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage(),
|
||||
);
|
||||
}
|
||||
|
||||
$response = wp_remote_get( 'https://openrouter.ai/api/v1/auth/key', [
|
||||
'timeout' => 15,
|
||||
'headers' => [ 'Authorization' => 'Bearer ' . $api_key ],
|
||||
] );
|
||||
$response = wp_remote_get(
|
||||
'https://openrouter.ai/api/v1/auth/key',
|
||||
array(
|
||||
'timeout' => 15,
|
||||
'headers' => array( 'Authorization' => 'Bearer ' . $api_key ),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return [
|
||||
return array(
|
||||
'status' => 'error',
|
||||
'message' => __( 'Could not reach OpenRouter API.', 'prautoblogger' ),
|
||||
'debug' => $response->get_error_message(),
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
$status = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 200 === $status ) {
|
||||
return [ 'status' => 'ok', 'message' => __( 'OpenRouter API key valid.', 'prautoblogger' ) ];
|
||||
return array(
|
||||
'status' => 'ok',
|
||||
'message' => __( 'OpenRouter API key valid.', 'prautoblogger' ),
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
return array(
|
||||
'status' => 'error',
|
||||
'message' => sprintf( __( 'OpenRouter returned HTTP %d.', 'prautoblogger' ), $status ),
|
||||
'debug' => substr( (string) wp_remote_retrieve_body( $response ), 0, 200 ),
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,34 +236,62 @@ class PRAutoBlogger_OpenRouter_Image_Provider implements PRAutoBlogger_Image_Pro
|
||||
*
|
||||
* @var array<int, array{w: int, h: int, ratio: float}>
|
||||
*/
|
||||
private const STANDARD_ASPECTS = [
|
||||
[ 'w' => 1, 'h' => 1, 'ratio' => 1.0 ],
|
||||
[ 'w' => 3, 'h' => 2, 'ratio' => 1.5 ],
|
||||
[ 'w' => 2, 'h' => 3, 'ratio' => 0.6667 ],
|
||||
[ 'w' => 4, 'h' => 3, 'ratio' => 1.3333 ],
|
||||
[ 'w' => 3, 'h' => 4, 'ratio' => 0.75 ],
|
||||
[ 'w' => 16, 'h' => 9, 'ratio' => 1.7778 ],
|
||||
[ 'w' => 9, 'h' => 16, 'ratio' => 0.5625 ],
|
||||
];
|
||||
private const STANDARD_ASPECTS = array(
|
||||
array(
|
||||
'w' => 1,
|
||||
'h' => 1,
|
||||
'ratio' => 1.0,
|
||||
),
|
||||
array(
|
||||
'w' => 3,
|
||||
'h' => 2,
|
||||
'ratio' => 1.5,
|
||||
),
|
||||
array(
|
||||
'w' => 2,
|
||||
'h' => 3,
|
||||
'ratio' => 0.6667,
|
||||
),
|
||||
array(
|
||||
'w' => 4,
|
||||
'h' => 3,
|
||||
'ratio' => 1.3333,
|
||||
),
|
||||
array(
|
||||
'w' => 3,
|
||||
'h' => 4,
|
||||
'ratio' => 0.75,
|
||||
),
|
||||
array(
|
||||
'w' => 16,
|
||||
'h' => 9,
|
||||
'ratio' => 1.7778,
|
||||
),
|
||||
array(
|
||||
'w' => 9,
|
||||
'h' => 16,
|
||||
'ratio' => 0.5625,
|
||||
),
|
||||
);
|
||||
|
||||
private function build_request_body( string $prompt, int $width, int $height, string $model ): array {
|
||||
$aspect = $this->snap_aspect_ratio( $width, $height );
|
||||
|
||||
return [
|
||||
'model' => $model,
|
||||
'messages' => [
|
||||
[
|
||||
return array(
|
||||
'model' => $model,
|
||||
'messages' => array(
|
||||
array(
|
||||
'role' => 'user',
|
||||
'content' => 'Generate an image: ' . $prompt,
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
// Use ["image", "text"] — some providers (Gemini) require both
|
||||
// modalities even when we only want the image output.
|
||||
'modalities' => [ 'image', 'text' ],
|
||||
'image_config' => [
|
||||
'modalities' => array( 'image', 'text' ),
|
||||
'image_config' => array(
|
||||
'aspect_ratio' => $aspect,
|
||||
],
|
||||
];
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -262,9 +302,9 @@ class PRAutoBlogger_OpenRouter_Image_Provider implements PRAutoBlogger_Image_Pro
|
||||
* @return string Aspect ratio string like "16:9".
|
||||
*/
|
||||
private function snap_aspect_ratio( int $width, int $height ): string {
|
||||
$target = $height > 0 ? (float) $width / (float) $height : 1.0;
|
||||
$best = self::STANDARD_ASPECTS[0];
|
||||
$best_d = abs( $target - $best['ratio'] );
|
||||
$target = $height > 0 ? (float) $width / (float) $height : 1.0;
|
||||
$best = self::STANDARD_ASPECTS[0];
|
||||
$best_d = abs( $target - $best['ratio'] );
|
||||
|
||||
foreach ( self::STANDARD_ASPECTS as $candidate ) {
|
||||
$d = abs( $target - $candidate['ratio'] );
|
||||
@@ -292,7 +332,10 @@ class PRAutoBlogger_OpenRouter_Image_Provider implements PRAutoBlogger_Image_Pro
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf(
|
||||
esc_html__( 'Image dimensions %1$dx%2$d outside supported range (%3$d–%4$d px).', 'prautoblogger' ),
|
||||
$width, $height, self::MIN_DIMENSION_PX, self::MAX_DIMENSION_PX
|
||||
$width,
|
||||
$height,
|
||||
self::MIN_DIMENSION_PX,
|
||||
self::MAX_DIMENSION_PX
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ class PRAutoBlogger_OpenRouter_Image_Support {
|
||||
);
|
||||
}
|
||||
|
||||
$images = $decoded['choices'][0]['message']['images'] ?? [];
|
||||
$images = $decoded['choices'][0]['message']['images'] ?? array();
|
||||
if ( empty( $images ) ) {
|
||||
throw new \RuntimeException(
|
||||
esc_html__( 'OpenRouter response contained no images.', 'prautoblogger' )
|
||||
|
||||
@@ -21,15 +21,36 @@ class PRAutoBlogger_OpenRouter_Pricing {
|
||||
*
|
||||
* @var array<string, array{prompt: float, completion: float}>
|
||||
*/
|
||||
private const MODEL_PRICING = [
|
||||
'anthropic/claude-3.5-haiku' => [ 'prompt' => 0.80, 'completion' => 4.00 ],
|
||||
'anthropic/claude-sonnet-4' => [ 'prompt' => 3.00, 'completion' => 15.00 ],
|
||||
'anthropic/claude-opus-4' => [ 'prompt' => 15.00, 'completion' => 75.00 ],
|
||||
'openai/gpt-4o' => [ 'prompt' => 2.50, 'completion' => 10.00 ],
|
||||
'openai/gpt-4o-mini' => [ 'prompt' => 0.15, 'completion' => 0.60 ],
|
||||
'google/gemini-2.0-flash' => [ 'prompt' => 0.10, 'completion' => 0.40 ],
|
||||
'meta-llama/llama-3.3-70b' => [ 'prompt' => 0.40, 'completion' => 0.40 ],
|
||||
];
|
||||
private const MODEL_PRICING = array(
|
||||
'anthropic/claude-3.5-haiku' => array(
|
||||
'prompt' => 0.80,
|
||||
'completion' => 4.00,
|
||||
),
|
||||
'anthropic/claude-sonnet-4' => array(
|
||||
'prompt' => 3.00,
|
||||
'completion' => 15.00,
|
||||
),
|
||||
'anthropic/claude-opus-4' => array(
|
||||
'prompt' => 15.00,
|
||||
'completion' => 75.00,
|
||||
),
|
||||
'openai/gpt-4o' => array(
|
||||
'prompt' => 2.50,
|
||||
'completion' => 10.00,
|
||||
),
|
||||
'openai/gpt-4o-mini' => array(
|
||||
'prompt' => 0.15,
|
||||
'completion' => 0.60,
|
||||
),
|
||||
'google/gemini-2.0-flash' => array(
|
||||
'prompt' => 0.10,
|
||||
'completion' => 0.40,
|
||||
),
|
||||
'meta-llama/llama-3.3-70b' => array(
|
||||
'prompt' => 0.40,
|
||||
'completion' => 0.40,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Get available models from OpenRouter's /models endpoint.
|
||||
@@ -48,7 +69,7 @@ class PRAutoBlogger_OpenRouter_Pricing {
|
||||
|
||||
$api_key = $this->get_api_key();
|
||||
if ( '' === $api_key ) {
|
||||
return [];
|
||||
return array();
|
||||
}
|
||||
|
||||
// Belt-and-suspenders: inject Authorization at cURL level (see OpenRouter_Provider).
|
||||
@@ -58,43 +79,47 @@ class PRAutoBlogger_OpenRouter_Pricing {
|
||||
return;
|
||||
}
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_setopt
|
||||
curl_setopt( $handle, CURLOPT_HTTPHEADER, [
|
||||
'Authorization: ' . $auth_header_value,
|
||||
] );
|
||||
curl_setopt(
|
||||
$handle,
|
||||
CURLOPT_HTTPHEADER,
|
||||
array(
|
||||
'Authorization: ' . $auth_header_value,
|
||||
)
|
||||
);
|
||||
};
|
||||
add_action( 'http_api_curl', $curl_auth_filter_mod, 99, 3 );
|
||||
|
||||
$response = wp_remote_get(
|
||||
'https://openrouter.ai/api/v1/models',
|
||||
[
|
||||
array(
|
||||
'timeout' => 30,
|
||||
'headers' => [
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $api_key,
|
||||
],
|
||||
]
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
remove_action( 'http_api_curl', $curl_auth_filter_mod, 99 );
|
||||
|
||||
if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
|
||||
PRAutoBlogger_Logger::instance()->warning( 'Failed to fetch OpenRouter models list.', 'openrouter' );
|
||||
return [];
|
||||
return array();
|
||||
}
|
||||
|
||||
$data = json_decode( wp_remote_retrieve_body( $response ), true );
|
||||
$models = [];
|
||||
$models = array();
|
||||
|
||||
if ( isset( $data['data'] ) && is_array( $data['data'] ) ) {
|
||||
foreach ( $data['data'] as $model ) {
|
||||
$models[] = [
|
||||
$models[] = array(
|
||||
'id' => $model['id'] ?? '',
|
||||
'name' => $model['name'] ?? $model['id'] ?? '',
|
||||
'context_length' => (int) ( $model['context_length'] ?? 0 ),
|
||||
'pricing' => [
|
||||
'pricing' => array(
|
||||
'prompt' => (float) ( $model['pricing']['prompt'] ?? 0 ) * 1000000,
|
||||
'completion' => (float) ( $model['pricing']['completion'] ?? 0 ) * 1000000,
|
||||
],
|
||||
];
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +160,10 @@ class PRAutoBlogger_OpenRouter_Pricing {
|
||||
if ( null === $pricing ) {
|
||||
// Unknown model — use a conservative estimate.
|
||||
PRAutoBlogger_Logger::instance()->warning( "Unknown model pricing for '{$model}'. Using conservative estimate.", 'openrouter' );
|
||||
$pricing = [ 'prompt' => 10.0, 'completion' => 30.0 ];
|
||||
$pricing = array(
|
||||
'prompt' => 10.0,
|
||||
'completion' => 30.0,
|
||||
);
|
||||
}
|
||||
|
||||
return ( $prompt_tokens * $pricing['prompt'] / 1000000 )
|
||||
|
||||
@@ -50,7 +50,7 @@ class PRAutoBlogger_OpenRouter_Provider implements PRAutoBlogger_LLM_Provider_In
|
||||
*
|
||||
* @throws \RuntimeException On API error after retries exhausted.
|
||||
*/
|
||||
public function send_chat_completion( array $messages, string $model, array $options = [] ): array {
|
||||
public function send_chat_completion( array $messages, string $model, array $options = array() ): array {
|
||||
$api_key = $this->get_api_key();
|
||||
if ( '' === $api_key ) {
|
||||
throw new \RuntimeException(
|
||||
@@ -74,10 +74,10 @@ class PRAutoBlogger_OpenRouter_Provider implements PRAutoBlogger_LLM_Provider_In
|
||||
);
|
||||
}
|
||||
|
||||
$body = [
|
||||
$body = array(
|
||||
'model' => $model,
|
||||
'messages' => $messages,
|
||||
];
|
||||
);
|
||||
|
||||
if ( isset( $options['temperature'] ) ) {
|
||||
$body['temperature'] = $options['temperature'];
|
||||
@@ -93,10 +93,10 @@ class PRAutoBlogger_OpenRouter_Provider implements PRAutoBlogger_LLM_Provider_In
|
||||
if ( isset( $options['reasoning'] ) ) {
|
||||
$body['reasoning'] = $options['reasoning'];
|
||||
} elseif ( '1' === get_option( 'prautoblogger_reasoning_enabled', '0' ) ) {
|
||||
$body['reasoning'] = [
|
||||
$body['reasoning'] = array(
|
||||
'enabled' => true,
|
||||
'effort' => get_option( 'prautoblogger_reasoning_effort', 'medium' ),
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
$last_error = '';
|
||||
@@ -107,8 +107,8 @@ class PRAutoBlogger_OpenRouter_Provider implements PRAutoBlogger_LLM_Provider_In
|
||||
$cache_ttl = $config->get_cache_ttl_seconds();
|
||||
$via_gateway = $config->is_via_gateway();
|
||||
|
||||
$builder = new PRAutoBlogger_OpenRouter_Request_Builder();
|
||||
$request_headers = $builder->build_headers( $api_key, $via_gateway, $cache_ttl );
|
||||
$builder = new PRAutoBlogger_OpenRouter_Request_Builder();
|
||||
$request_headers = $builder->build_headers( $api_key, $via_gateway, $cache_ttl );
|
||||
$curl_auth_filter = $builder->register_curl_auth_filter( $request_headers, $base_host );
|
||||
|
||||
try {
|
||||
@@ -117,11 +117,11 @@ class PRAutoBlogger_OpenRouter_Provider implements PRAutoBlogger_LLM_Provider_In
|
||||
for ( $attempt = 1; $attempt <= PRAUTOBLOGGER_MAX_RETRIES; $attempt++ ) {
|
||||
$response = wp_remote_post(
|
||||
$base_url . '/chat/completions',
|
||||
[
|
||||
array(
|
||||
'timeout' => PRAUTOBLOGGER_API_TIMEOUT_SECONDS,
|
||||
'headers' => $request_headers,
|
||||
'body' => wp_json_encode( $body ),
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
|
||||
@@ -29,12 +29,12 @@ class PRAutoBlogger_OpenRouter_Request_Builder {
|
||||
* @return array<string, string> HTTP headers ready for wp_remote_post().
|
||||
*/
|
||||
public function build_headers( string $api_key, bool $via_gateway, int $cache_ttl ): array {
|
||||
$headers = [
|
||||
$headers = array(
|
||||
'Authorization' => 'Bearer ' . $api_key,
|
||||
'Content-Type' => 'application/json',
|
||||
'HTTP-Referer' => home_url(),
|
||||
'X-Title' => 'PRAutoBlogger WordPress Plugin',
|
||||
];
|
||||
);
|
||||
|
||||
if ( $via_gateway && $cache_ttl > 0 ) {
|
||||
$headers['cf-aig-cache-ttl'] = (string) $cache_ttl;
|
||||
@@ -66,7 +66,7 @@ class PRAutoBlogger_OpenRouter_Request_Builder {
|
||||
if ( '' === $base_host || false === strpos( (string) $url, $base_host ) ) {
|
||||
return;
|
||||
}
|
||||
$curl_headers = [];
|
||||
$curl_headers = array();
|
||||
foreach ( $request_headers as $name => $value ) {
|
||||
$curl_headers[] = $name . ': ' . $value;
|
||||
}
|
||||
|
||||
@@ -94,12 +94,12 @@ class PRAutoBlogger_OpenRouter_Response_Parser {
|
||||
$content = '';
|
||||
}
|
||||
|
||||
$usage = $data['usage'] ?? [];
|
||||
$usage = $data['usage'] ?? array();
|
||||
|
||||
// Reasoning content lives alongside 'content' in the message object.
|
||||
$reasoning_content = $data['choices'][0]['message']['reasoning'] ?? '';
|
||||
|
||||
return [
|
||||
return array(
|
||||
'content' => (string) $content,
|
||||
'model' => $data['model'] ?? $model,
|
||||
'prompt_tokens' => (int) ( $usage['prompt_tokens'] ?? 0 ),
|
||||
@@ -108,7 +108,7 @@ class PRAutoBlogger_OpenRouter_Response_Parser {
|
||||
'total_tokens' => (int) ( $usage['total_tokens'] ?? 0 ),
|
||||
'finish_reason' => $data['choices'][0]['finish_reason'] ?? 'unknown',
|
||||
'reasoning_content' => (string) $reasoning_content,
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -79,20 +79,24 @@ class PRAutoBlogger_OpenRouter_Validator {
|
||||
return;
|
||||
}
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_setopt
|
||||
curl_setopt( $handle, CURLOPT_HTTPHEADER, [
|
||||
'Authorization: ' . $auth_header_value,
|
||||
] );
|
||||
curl_setopt(
|
||||
$handle,
|
||||
CURLOPT_HTTPHEADER,
|
||||
array(
|
||||
'Authorization: ' . $auth_header_value,
|
||||
)
|
||||
);
|
||||
};
|
||||
add_action( 'http_api_curl', $curl_auth_filter_cred, 99, 3 );
|
||||
|
||||
$response = wp_remote_get(
|
||||
$base_url . '/auth/key',
|
||||
[
|
||||
array(
|
||||
'timeout' => self::TIMEOUT_SECONDS,
|
||||
'headers' => [
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $api_key,
|
||||
],
|
||||
]
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
remove_action( 'http_api_curl', $curl_auth_filter_cred, 99 );
|
||||
@@ -115,10 +119,10 @@ class PRAutoBlogger_OpenRouter_Validator {
|
||||
|
||||
$status_code = wp_remote_retrieve_response_code( $response );
|
||||
if ( 200 === $status_code ) {
|
||||
return [
|
||||
return array(
|
||||
'status' => 'ok',
|
||||
'message' => __( 'OpenRouter connected.', 'prautoblogger' ),
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
$body_raw = wp_remote_retrieve_body( $response );
|
||||
@@ -143,10 +147,10 @@ class PRAutoBlogger_OpenRouter_Validator {
|
||||
* @return array{status: string, message: string, debug: string}
|
||||
*/
|
||||
private function err( string $debug, string $message ): array {
|
||||
return [
|
||||
return array(
|
||||
'status' => 'error',
|
||||
'message' => $message,
|
||||
'debug' => $debug,
|
||||
];
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,12 +63,12 @@ class PRAutoBlogger_Reddit_Availability {
|
||||
$url = self::BASE_URL . '/r/all/hot.rss?limit=1';
|
||||
$response = wp_remote_get(
|
||||
$url,
|
||||
[
|
||||
array(
|
||||
'timeout' => 10,
|
||||
'headers' => [
|
||||
'headers' => array(
|
||||
'User-Agent' => self::USER_AGENT,
|
||||
],
|
||||
]
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
@@ -91,12 +91,12 @@ class PRAutoBlogger_Reddit_Availability {
|
||||
$url = self::BASE_URL . '/r/all/hot.json?limit=1';
|
||||
$response = wp_remote_get(
|
||||
$url,
|
||||
[
|
||||
array(
|
||||
'timeout' => 10,
|
||||
'headers' => [
|
||||
'headers' => array(
|
||||
'User-Agent' => self::USER_AGENT,
|
||||
],
|
||||
]
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
@@ -121,11 +121,11 @@ class PRAutoBlogger_Reddit_Availability {
|
||||
return $status;
|
||||
}
|
||||
|
||||
return [
|
||||
return array(
|
||||
'remaining' => 10,
|
||||
'limit' => 10,
|
||||
'resets_at' => '',
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,13 +143,13 @@ class PRAutoBlogger_Reddit_Availability {
|
||||
public function api_get( string $url ): ?array {
|
||||
$response = wp_remote_get(
|
||||
$url,
|
||||
[
|
||||
array(
|
||||
'timeout' => 30,
|
||||
'headers' => [
|
||||
'headers' => array(
|
||||
'User-Agent' => self::USER_AGENT,
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
]
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
@@ -168,11 +168,11 @@ class PRAutoBlogger_Reddit_Availability {
|
||||
if ( '' !== $rate_remaining ) {
|
||||
set_transient(
|
||||
self::RATE_LIMIT_TRANSIENT,
|
||||
[
|
||||
array(
|
||||
'remaining' => (int) $rate_remaining,
|
||||
'limit' => (int) $rate_used + (int) $rate_remaining,
|
||||
'resets_at' => gmdate( 'Y-m-d H:i:s', time() + (int) $rate_reset ),
|
||||
],
|
||||
),
|
||||
120
|
||||
);
|
||||
}
|
||||
|
||||
@@ -97,13 +97,13 @@ class PRAutoBlogger_Reddit_JSON_Client {
|
||||
|
||||
$response = wp_remote_get(
|
||||
$url,
|
||||
[
|
||||
array(
|
||||
'timeout' => self::TIMEOUT_SECONDS,
|
||||
'headers' => [
|
||||
'headers' => array(
|
||||
'User-Agent' => self::USER_AGENT,
|
||||
'Accept' => 'application/atom+xml, application/rss+xml',
|
||||
],
|
||||
]
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
@@ -111,7 +111,7 @@ class PRAutoBlogger_Reddit_JSON_Client {
|
||||
'Reddit RSS GET failed: ' . $response->get_error_message(),
|
||||
'reddit'
|
||||
);
|
||||
return [];
|
||||
return array();
|
||||
}
|
||||
|
||||
$status_code = wp_remote_retrieve_response_code( $response );
|
||||
@@ -120,10 +120,10 @@ class PRAutoBlogger_Reddit_JSON_Client {
|
||||
sprintf( 'Reddit RSS HTTP %d for r/%s', $status_code, $subreddit ),
|
||||
'reddit'
|
||||
);
|
||||
return [];
|
||||
return array();
|
||||
}
|
||||
|
||||
$body = wp_remote_retrieve_body( $response );
|
||||
$body = wp_remote_retrieve_body( $response );
|
||||
$parser = new PRAutoBlogger_Reddit_RSS_Parser();
|
||||
return $parser->parse( $body, $subreddit );
|
||||
}
|
||||
@@ -154,15 +154,15 @@ class PRAutoBlogger_Reddit_JSON_Client {
|
||||
$response = $this->api_get( $url );
|
||||
|
||||
if ( null === $response || ! isset( $response['data']['children'] ) ) {
|
||||
return [];
|
||||
return array();
|
||||
}
|
||||
|
||||
$posts = [];
|
||||
$posts = array();
|
||||
foreach ( $response['data']['children'] as $child ) {
|
||||
if ( isset( $child['data'] ) ) {
|
||||
$post = $child['data'];
|
||||
$post = $child['data'];
|
||||
$post['data_source'] = 'reddit_json';
|
||||
$posts[] = $post;
|
||||
$posts[] = $post;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,10 +197,10 @@ class PRAutoBlogger_Reddit_JSON_Client {
|
||||
|
||||
// Comments endpoint returns [post_listing, comments_listing].
|
||||
if ( ! is_array( $response ) || ! isset( $response[1]['data']['children'] ) ) {
|
||||
return [];
|
||||
return array();
|
||||
}
|
||||
|
||||
$comments = [];
|
||||
$comments = array();
|
||||
foreach ( $response[1]['data']['children'] as $child ) {
|
||||
// Filter out "more" type entries, keep only actual comments (kind=t1).
|
||||
if ( isset( $child['data'] ) && 't1' === ( $child['kind'] ?? '' ) ) {
|
||||
|
||||
@@ -47,7 +47,7 @@ class PRAutoBlogger_Reddit_Provider implements PRAutoBlogger_Source_Provider_Int
|
||||
$comments_limit = $config['comments_per_post'] ?? 5;
|
||||
|
||||
$client = new PRAutoBlogger_Reddit_JSON_Client();
|
||||
$collected = [];
|
||||
$collected = array();
|
||||
|
||||
PRAutoBlogger_Logger::instance()->info(
|
||||
sprintf(
|
||||
@@ -72,26 +72,28 @@ class PRAutoBlogger_Reddit_Provider implements PRAutoBlogger_Source_Provider_Int
|
||||
foreach ( $posts as $post ) {
|
||||
$data_source = $post['data_source'] ?? 'reddit';
|
||||
|
||||
$collected[] = new PRAutoBlogger_Source_Data( [
|
||||
'source_type' => 'reddit',
|
||||
'source_id' => 't3_' . ( $post['id'] ?? '' ),
|
||||
'subreddit' => $subreddit,
|
||||
'title' => $post['title'] ?? '',
|
||||
'content' => $post['selftext'] ?? '',
|
||||
'author' => $post['author'] ?? '[deleted]',
|
||||
'score' => (int) ( $post['score'] ?? 0 ),
|
||||
'comment_count' => (int) ( $post['num_comments'] ?? 0 ),
|
||||
'permalink' => 'https://reddit.com' . ( $post['permalink'] ?? '' ),
|
||||
'collected_at' => current_time( 'mysql' ),
|
||||
'metadata' => [
|
||||
'post_type' => ( $post['is_self'] ?? false ) ? 'self' : 'link',
|
||||
'flair' => $post['link_flair_text'] ?? null,
|
||||
'created_utc' => $post['created_utc'] ?? null,
|
||||
'upvote_ratio' => $post['upvote_ratio'] ?? null,
|
||||
'is_original' => $post['is_original_content'] ?? false,
|
||||
'data_source' => $data_source,
|
||||
],
|
||||
] );
|
||||
$collected[] = new PRAutoBlogger_Source_Data(
|
||||
array(
|
||||
'source_type' => 'reddit',
|
||||
'source_id' => 't3_' . ( $post['id'] ?? '' ),
|
||||
'subreddit' => $subreddit,
|
||||
'title' => $post['title'] ?? '',
|
||||
'content' => $post['selftext'] ?? '',
|
||||
'author' => $post['author'] ?? '[deleted]',
|
||||
'score' => (int) ( $post['score'] ?? 0 ),
|
||||
'comment_count' => (int) ( $post['num_comments'] ?? 0 ),
|
||||
'permalink' => 'https://reddit.com' . ( $post['permalink'] ?? '' ),
|
||||
'collected_at' => current_time( 'mysql' ),
|
||||
'metadata' => array(
|
||||
'post_type' => ( $post['is_self'] ?? false ) ? 'self' : 'link',
|
||||
'flair' => $post['link_flair_text'] ?? null,
|
||||
'created_utc' => $post['created_utc'] ?? null,
|
||||
'upvote_ratio' => $post['upvote_ratio'] ?? null,
|
||||
'is_original' => $post['is_original_content'] ?? false,
|
||||
'data_source' => $data_source,
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
// Fetch comments for high-engagement posts via .json
|
||||
// (comments are not available in RSS feeds).
|
||||
@@ -100,25 +102,27 @@ class PRAutoBlogger_Reddit_Provider implements PRAutoBlogger_Source_Provider_Int
|
||||
$comments = $client->fetch_comments( $subreddit, $post_id, $comments_limit );
|
||||
|
||||
foreach ( $comments as $comment ) {
|
||||
$collected[] = new PRAutoBlogger_Source_Data( [
|
||||
'source_type' => 'reddit',
|
||||
'source_id' => 't1_' . ( $comment['id'] ?? '' ),
|
||||
'subreddit' => $subreddit,
|
||||
'title' => null,
|
||||
'content' => $comment['body'] ?? '',
|
||||
'author' => $comment['author'] ?? '[deleted]',
|
||||
'score' => (int) ( $comment['score'] ?? 0 ),
|
||||
'comment_count' => 0,
|
||||
'permalink' => 'https://reddit.com' . ( $comment['permalink'] ?? '' ),
|
||||
'collected_at' => current_time( 'mysql' ),
|
||||
'metadata' => [
|
||||
'parent_post_id' => 't3_' . $post_id,
|
||||
'parent_title' => $post['title'] ?? '',
|
||||
'is_comment' => true,
|
||||
'created_utc' => $comment['created_utc'] ?? null,
|
||||
'data_source' => 'reddit_json',
|
||||
],
|
||||
] );
|
||||
$collected[] = new PRAutoBlogger_Source_Data(
|
||||
array(
|
||||
'source_type' => 'reddit',
|
||||
'source_id' => 't1_' . ( $comment['id'] ?? '' ),
|
||||
'subreddit' => $subreddit,
|
||||
'title' => null,
|
||||
'content' => $comment['body'] ?? '',
|
||||
'author' => $comment['author'] ?? '[deleted]',
|
||||
'score' => (int) ( $comment['score'] ?? 0 ),
|
||||
'comment_count' => 0,
|
||||
'permalink' => 'https://reddit.com' . ( $comment['permalink'] ?? '' ),
|
||||
'collected_at' => current_time( 'mysql' ),
|
||||
'metadata' => array(
|
||||
'parent_post_id' => 't3_' . $post_id,
|
||||
'parent_title' => $post['title'] ?? '',
|
||||
'is_comment' => true,
|
||||
'created_utc' => $comment['created_utc'] ?? null,
|
||||
'data_source' => 'reddit_json',
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,6 +188,6 @@ class PRAutoBlogger_Reddit_Provider implements PRAutoBlogger_Source_Provider_Int
|
||||
private function get_configured_subreddits(): array {
|
||||
$json = get_option( 'prautoblogger_target_subreddits', '' );
|
||||
$list = json_decode( $json, true );
|
||||
return is_array( $list ) ? $list : [];
|
||||
return is_array( $list ) ? $list : array();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ class PRAutoBlogger_Reddit_RSS_Parser {
|
||||
|
||||
if ( false === $feed ) {
|
||||
PRAutoBlogger_Logger::instance()->error( 'Failed to parse Reddit RSS XML.', 'reddit' );
|
||||
return [];
|
||||
return array();
|
||||
}
|
||||
|
||||
// Register the Atom namespace.
|
||||
@@ -49,10 +49,10 @@ class PRAutoBlogger_Reddit_RSS_Parser {
|
||||
$entries = $feed->xpath( '//atom:entry' );
|
||||
|
||||
if ( empty( $entries ) ) {
|
||||
return [];
|
||||
return array();
|
||||
}
|
||||
|
||||
$posts = [];
|
||||
$posts = array();
|
||||
foreach ( $entries as $entry ) {
|
||||
$post = $this->parse_entry( $entry, $subreddit );
|
||||
if ( null !== $post ) {
|
||||
@@ -118,7 +118,7 @@ class PRAutoBlogger_Reddit_RSS_Parser {
|
||||
$permalink = $parsed['path'] ?? '';
|
||||
}
|
||||
|
||||
return [
|
||||
return array(
|
||||
'id' => $post_id,
|
||||
'title' => $title,
|
||||
'selftext' => $content,
|
||||
@@ -132,6 +132,6 @@ class PRAutoBlogger_Reddit_RSS_Parser {
|
||||
'upvote_ratio' => null,
|
||||
'is_original_content' => false,
|
||||
'data_source' => 'reddit_rss',
|
||||
];
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,17 +59,17 @@ class PRAutoBlogger_Runware_Image_Batch {
|
||||
*/
|
||||
public function execute( array $requests ): array {
|
||||
if ( empty( $requests ) ) {
|
||||
return [];
|
||||
return array();
|
||||
}
|
||||
|
||||
$api_key = $this->support->get_api_key();
|
||||
$round1 = $this->round_one_inference( $api_key, $requests );
|
||||
$results = [];
|
||||
$to_download = [];
|
||||
$results = array();
|
||||
$to_download = array();
|
||||
|
||||
foreach ( $round1 as $key => $entry ) {
|
||||
if ( isset( $entry['error'] ) ) {
|
||||
$results[ $key ] = [ 'error' => $entry['error'] ];
|
||||
$results[ $key ] = array( 'error' => $entry['error'] );
|
||||
continue;
|
||||
}
|
||||
$to_download[ $key ] = $entry;
|
||||
@@ -82,10 +82,10 @@ class PRAutoBlogger_Runware_Image_Batch {
|
||||
$bytes_map = $this->round_two_download( $to_download );
|
||||
foreach ( $to_download as $key => $entry ) {
|
||||
if ( isset( $bytes_map[ $key ]['error'] ) ) {
|
||||
$results[ $key ] = [ 'error' => $bytes_map[ $key ]['error'] ];
|
||||
$results[ $key ] = array( 'error' => $bytes_map[ $key ]['error'] );
|
||||
continue;
|
||||
}
|
||||
$results[ $key ] = [
|
||||
$results[ $key ] = array(
|
||||
'bytes' => $bytes_map[ $key ]['bytes'],
|
||||
'mime_type' => 'image/png',
|
||||
'width' => $entry['width'],
|
||||
@@ -94,7 +94,7 @@ class PRAutoBlogger_Runware_Image_Batch {
|
||||
'seed' => $entry['seed'],
|
||||
'cost_usd' => $this->pricing->estimate_cost( $entry['width'], $entry['height'], $entry['model'] ),
|
||||
'latency_ms' => (int) ( ( microtime( true ) - $entry['started_at'] ) * 1000 ),
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
return $results;
|
||||
@@ -108,10 +108,10 @@ class PRAutoBlogger_Runware_Image_Batch {
|
||||
* @return array<string, array> Map of key => {error} | {image_url, width, height, model, seed, started_at}.
|
||||
*/
|
||||
private function round_one_inference( string $api_key, array $requests ): array {
|
||||
$headers = [ 'Content-Type: application/json' ];
|
||||
$headers = array( 'Content-Type: application/json' );
|
||||
$mh = curl_multi_init();
|
||||
$handles = [];
|
||||
$start_times = [];
|
||||
$handles = array();
|
||||
$start_times = array();
|
||||
|
||||
foreach ( $requests as $key => $req ) {
|
||||
$model = $this->pricing->resolve_model( (string) ( $req['options']['model'] ?? '' ) );
|
||||
@@ -123,28 +123,32 @@ class PRAutoBlogger_Runware_Image_Batch {
|
||||
$ch = curl_init();
|
||||
curl_setopt( $ch, CURLOPT_URL, PRAutoBlogger_Runware_Image_Support::API_URL );
|
||||
curl_setopt( $ch, CURLOPT_POST, true );
|
||||
curl_setopt( $ch, CURLOPT_POSTFIELDS, wp_json_encode(
|
||||
$this->build_payload( $api_key, (string) $req['prompt'], $snap_w, $snap_h, $model, $seed, $steps )
|
||||
) );
|
||||
curl_setopt(
|
||||
$ch,
|
||||
CURLOPT_POSTFIELDS,
|
||||
wp_json_encode(
|
||||
$this->build_payload( $api_key, (string) $req['prompt'], $snap_w, $snap_h, $model, $seed, $steps )
|
||||
)
|
||||
);
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_TIMEOUT, PRAUTOBLOGGER_API_TIMEOUT_SECONDS );
|
||||
curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 10 );
|
||||
|
||||
curl_multi_add_handle( $mh, $ch );
|
||||
$handles[ $key ] = [
|
||||
$handles[ $key ] = array(
|
||||
'handle' => $ch,
|
||||
'model' => $model,
|
||||
'width' => $snap_w,
|
||||
'height' => $snap_h,
|
||||
'seed' => $seed,
|
||||
];
|
||||
);
|
||||
$start_times[ $key ] = microtime( true );
|
||||
}
|
||||
|
||||
$this->run_multi( $mh );
|
||||
|
||||
$out = [];
|
||||
$out = array();
|
||||
foreach ( $handles as $key => $entry ) {
|
||||
$out[ $key ] = $this->collect_inference_result( (string) $key, $entry, $start_times[ $key ] );
|
||||
curl_multi_remove_handle( $mh, $entry['handle'] );
|
||||
@@ -163,7 +167,7 @@ class PRAutoBlogger_Runware_Image_Batch {
|
||||
*/
|
||||
private function round_two_download( array $inferences ): array {
|
||||
$mh = curl_multi_init();
|
||||
$handles = [];
|
||||
$handles = array();
|
||||
|
||||
foreach ( $inferences as $key => $entry ) {
|
||||
$ch = curl_init();
|
||||
@@ -177,22 +181,22 @@ class PRAutoBlogger_Runware_Image_Batch {
|
||||
|
||||
$this->run_multi( $mh );
|
||||
|
||||
$out = [];
|
||||
$out = array();
|
||||
foreach ( $handles as $key => $ch ) {
|
||||
$errno = curl_errno( $ch );
|
||||
if ( 0 !== $errno ) {
|
||||
$msg = sprintf( 'Runware image download cURL error %d for "%s": %s', $errno, $key, curl_error( $ch ) );
|
||||
PRAutoBlogger_Logger::instance()->error( $msg, 'runware-image-batch' );
|
||||
$out[ $key ] = [ 'error' => $msg ];
|
||||
$out[ $key ] = array( 'error' => $msg );
|
||||
} else {
|
||||
$code = (int) curl_getinfo( $ch, CURLINFO_HTTP_CODE );
|
||||
$body = (string) curl_multi_getcontent( $ch );
|
||||
if ( 200 !== $code || '' === $body ) {
|
||||
$msg = sprintf( 'Runware image download HTTP %d for "%s" (%d bytes)', $code, $key, strlen( $body ) );
|
||||
PRAutoBlogger_Logger::instance()->error( $msg, 'runware-image-batch' );
|
||||
$out[ $key ] = [ 'error' => $msg ];
|
||||
$out[ $key ] = array( 'error' => $msg );
|
||||
} else {
|
||||
$out[ $key ] = [ 'bytes' => $body ];
|
||||
$out[ $key ] = array( 'bytes' => $body );
|
||||
}
|
||||
}
|
||||
curl_multi_remove_handle( $mh, $ch );
|
||||
@@ -231,9 +235,12 @@ class PRAutoBlogger_Runware_Image_Batch {
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function build_payload( string $api_key, string $prompt, int $width, int $height, string $model, int $seed, int $steps ): array {
|
||||
return [
|
||||
[ 'taskType' => 'authentication', 'apiKey' => $api_key ],
|
||||
[
|
||||
return array(
|
||||
array(
|
||||
'taskType' => 'authentication',
|
||||
'apiKey' => $api_key,
|
||||
),
|
||||
array(
|
||||
'taskType' => 'imageInference',
|
||||
'taskUUID' => wp_generate_uuid4(),
|
||||
'positivePrompt' => $prompt,
|
||||
@@ -245,8 +252,8 @@ class PRAutoBlogger_Runware_Image_Batch {
|
||||
'outputFormat' => 'PNG',
|
||||
'outputType' => 'URL',
|
||||
'seed' => $seed,
|
||||
],
|
||||
];
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -263,7 +270,7 @@ class PRAutoBlogger_Runware_Image_Batch {
|
||||
if ( 0 !== $errno ) {
|
||||
$msg = sprintf( 'Runware batch inference cURL error %d for "%s": %s', $errno, $key, curl_error( $ch ) );
|
||||
PRAutoBlogger_Logger::instance()->error( $msg, 'runware-image-batch' );
|
||||
return [ 'error' => $msg ];
|
||||
return array( 'error' => $msg );
|
||||
}
|
||||
|
||||
$http_code = (int) curl_getinfo( $ch, CURLINFO_HTTP_CODE );
|
||||
@@ -271,7 +278,7 @@ class PRAutoBlogger_Runware_Image_Batch {
|
||||
if ( $http_code >= 400 ) {
|
||||
$msg = sprintf( 'Runware batch inference HTTP %d for "%s": %s', $http_code, $key, substr( $raw, 0, 300 ) );
|
||||
PRAutoBlogger_Logger::instance()->error( $msg, 'runware-image-batch' );
|
||||
return [ 'error' => $msg ];
|
||||
return array( 'error' => $msg );
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -279,16 +286,16 @@ class PRAutoBlogger_Runware_Image_Batch {
|
||||
} catch ( \Throwable $e ) {
|
||||
$msg = sprintf( 'Runware batch inference parse error for "%s": %s', $key, $e->getMessage() );
|
||||
PRAutoBlogger_Logger::instance()->error( $msg, 'runware-image-batch' );
|
||||
return [ 'error' => $msg ];
|
||||
return array( 'error' => $msg );
|
||||
}
|
||||
|
||||
return [
|
||||
return array(
|
||||
'image_url' => $image_url,
|
||||
'width' => (int) $entry['width'],
|
||||
'height' => (int) $entry['height'],
|
||||
'model' => (string) $entry['model'],
|
||||
'seed' => (int) $entry['seed'],
|
||||
'started_at' => $start,
|
||||
];
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,12 +26,12 @@ class PRAutoBlogger_Runware_Image_Pricing {
|
||||
*
|
||||
* @var array<string, float>
|
||||
*/
|
||||
private const COST_PER_IMAGE = [
|
||||
private const COST_PER_IMAGE = array(
|
||||
// FLUX.1 schnell — 4-step distilled. Our new default (2026-04-21).
|
||||
'runware:100@1' => 0.0006,
|
||||
// FLUX.1 dev — 28-step full model. Higher fidelity, ~30x cost.
|
||||
'runware:101@1' => 0.02,
|
||||
];
|
||||
);
|
||||
|
||||
/**
|
||||
* Default number of inference steps per model. Runware bills per image
|
||||
@@ -39,10 +39,10 @@ class PRAutoBlogger_Runware_Image_Pricing {
|
||||
*
|
||||
* @var array<string, int>
|
||||
*/
|
||||
private const DEFAULT_STEPS = [
|
||||
private const DEFAULT_STEPS = array(
|
||||
'runware:100@1' => 4,
|
||||
'runware:101@1' => 28,
|
||||
];
|
||||
);
|
||||
|
||||
/**
|
||||
* Resolve the model identifier from a hint, falling back to site option,
|
||||
|
||||
@@ -45,7 +45,7 @@ class PRAutoBlogger_Runware_Image_Provider implements PRAutoBlogger_Image_Provid
|
||||
* backoff; 4xx (except 429) fail immediately. The image download GET
|
||||
* is not retried — signed URLs are short-lived.
|
||||
*/
|
||||
public function generate_image( string $prompt, int $width, int $height, array $options = [] ): array {
|
||||
public function generate_image( string $prompt, int $width, int $height, array $options = array() ): array {
|
||||
$this->assert_valid_dimensions( $width, $height );
|
||||
$prompt = trim( $prompt );
|
||||
if ( '' === $prompt ) {
|
||||
@@ -61,16 +61,19 @@ class PRAutoBlogger_Runware_Image_Provider implements PRAutoBlogger_Image_Provid
|
||||
$seed = $this->support->normalize_seed( isset( $options['seed'] ) ? (int) $options['seed'] : null );
|
||||
$steps = isset( $options['steps'] ) ? (int) $options['steps'] : $this->pricing->default_steps( $model );
|
||||
$body = $this->build_request_body( $api_key, $prompt, $snap_w, $snap_h, $model, $seed, $steps );
|
||||
$headers = [ 'Content-Type' => 'application/json' ];
|
||||
$headers = array( 'Content-Type' => 'application/json' );
|
||||
$started_at = microtime( true );
|
||||
$last_error = '';
|
||||
|
||||
for ( $attempt = 1; $attempt <= PRAUTOBLOGGER_MAX_RETRIES; $attempt++ ) {
|
||||
$response = wp_remote_post( PRAutoBlogger_Runware_Image_Support::API_URL, [
|
||||
'timeout' => PRAUTOBLOGGER_API_TIMEOUT_SECONDS,
|
||||
'headers' => $headers,
|
||||
'body' => wp_json_encode( $body ),
|
||||
] );
|
||||
$response = wp_remote_post(
|
||||
PRAutoBlogger_Runware_Image_Support::API_URL,
|
||||
array(
|
||||
'timeout' => PRAUTOBLOGGER_API_TIMEOUT_SECONDS,
|
||||
'headers' => $headers,
|
||||
'body' => wp_json_encode( $body ),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
$last_error = $response->get_error_message();
|
||||
@@ -109,7 +112,7 @@ class PRAutoBlogger_Runware_Image_Provider implements PRAutoBlogger_Image_Provid
|
||||
$image_url = $this->support->extract_image_url( $raw );
|
||||
$image_bytes = $this->support->download_image_bytes( $image_url );
|
||||
|
||||
return [
|
||||
return array(
|
||||
'bytes' => $image_bytes,
|
||||
'mime_type' => 'image/png',
|
||||
'width' => $snap_w,
|
||||
@@ -118,7 +121,7 @@ class PRAutoBlogger_Runware_Image_Provider implements PRAutoBlogger_Image_Provid
|
||||
'seed' => $seed,
|
||||
'cost_usd' => $this->pricing->estimate_cost( $snap_w, $snap_h, $model ),
|
||||
'latency_ms' => (int) ( ( microtime( true ) - $started_at ) * 1000 ),
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
throw new \RuntimeException(
|
||||
@@ -160,7 +163,7 @@ class PRAutoBlogger_Runware_Image_Provider implements PRAutoBlogger_Image_Provid
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function estimate_cost( int $width, int $height, array $options = [] ): float {
|
||||
public function estimate_cost( int $width, int $height, array $options = array() ): float {
|
||||
$model = $this->pricing->resolve_model( (string) ( $options['model'] ?? '' ) );
|
||||
return $this->pricing->estimate_cost( $width, $height, $model );
|
||||
}
|
||||
@@ -181,50 +184,62 @@ class PRAutoBlogger_Runware_Image_Provider implements PRAutoBlogger_Image_Provid
|
||||
try {
|
||||
$api_key = $this->support->get_api_key();
|
||||
} catch ( \RuntimeException $e ) {
|
||||
return [ 'status' => 'error', 'message' => $e->getMessage() ];
|
||||
return array(
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage(),
|
||||
);
|
||||
}
|
||||
|
||||
$body = [
|
||||
[ 'taskType' => 'authentication', 'apiKey' => $api_key ],
|
||||
];
|
||||
$body = array(
|
||||
array(
|
||||
'taskType' => 'authentication',
|
||||
'apiKey' => $api_key,
|
||||
),
|
||||
);
|
||||
|
||||
$response = wp_remote_post( PRAutoBlogger_Runware_Image_Support::API_URL, [
|
||||
'timeout' => 15,
|
||||
'headers' => [ 'Content-Type' => 'application/json' ],
|
||||
'body' => wp_json_encode( $body ),
|
||||
] );
|
||||
$response = wp_remote_post(
|
||||
PRAutoBlogger_Runware_Image_Support::API_URL,
|
||||
array(
|
||||
'timeout' => 15,
|
||||
'headers' => array( 'Content-Type' => 'application/json' ),
|
||||
'body' => wp_json_encode( $body ),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return [
|
||||
return array(
|
||||
'status' => 'error',
|
||||
'message' => __( 'Could not reach Runware API.', 'prautoblogger' ),
|
||||
'debug' => $response->get_error_message(),
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
$status = (int) wp_remote_retrieve_response_code( $response );
|
||||
$raw = (string) wp_remote_retrieve_body( $response );
|
||||
|
||||
if ( 200 !== $status ) {
|
||||
return [
|
||||
return array(
|
||||
'status' => 'error',
|
||||
'message' => sprintf( __( 'Runware returned HTTP %d.', 'prautoblogger' ), $status ),
|
||||
'debug' => substr( $raw, 0, 200 ),
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
$decoded = json_decode( $raw, true );
|
||||
if ( is_array( $decoded ) && isset( $decoded['errors'] ) && ! empty( $decoded['errors'] ) ) {
|
||||
$first = $decoded['errors'][0] ?? [];
|
||||
$first = $decoded['errors'][0] ?? array();
|
||||
$msg = is_array( $first ) ? (string) ( $first['message'] ?? '' ) : (string) $first;
|
||||
return [
|
||||
return array(
|
||||
'status' => 'error',
|
||||
'message' => __( 'Runware rejected the API key.', 'prautoblogger' ),
|
||||
'debug' => substr( $msg, 0, 200 ),
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
return [ 'status' => 'ok', 'message' => __( 'Runware API key valid.', 'prautoblogger' ) ];
|
||||
return array(
|
||||
'status' => 'ok',
|
||||
'message' => __( 'Runware API key valid.', 'prautoblogger' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,12 +265,12 @@ class PRAutoBlogger_Runware_Image_Provider implements PRAutoBlogger_Image_Provid
|
||||
int $seed,
|
||||
int $steps
|
||||
): array {
|
||||
return [
|
||||
[
|
||||
return array(
|
||||
array(
|
||||
'taskType' => 'authentication',
|
||||
'apiKey' => $api_key,
|
||||
],
|
||||
[
|
||||
),
|
||||
array(
|
||||
'taskType' => 'imageInference',
|
||||
'taskUUID' => wp_generate_uuid4(),
|
||||
'positivePrompt' => $prompt,
|
||||
@@ -267,8 +282,8 @@ class PRAutoBlogger_Runware_Image_Provider implements PRAutoBlogger_Image_Provid
|
||||
'outputFormat' => 'PNG',
|
||||
'outputType' => 'URL',
|
||||
'seed' => $seed,
|
||||
],
|
||||
];
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -286,7 +301,10 @@ class PRAutoBlogger_Runware_Image_Provider implements PRAutoBlogger_Image_Provid
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf(
|
||||
esc_html__( 'Image dimensions %1$dx%2$d outside supported range (%3$d–%4$d px).', 'prautoblogger' ),
|
||||
$width, $height, self::MIN_DIMENSION_PX, self::MAX_DIMENSION_PX
|
||||
$width,
|
||||
$height,
|
||||
self::MIN_DIMENSION_PX,
|
||||
self::MAX_DIMENSION_PX
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ class PRAutoBlogger_Runware_Image_Support {
|
||||
|
||||
// Runware wraps errors in an `errors` array.
|
||||
if ( isset( $decoded['errors'] ) && is_array( $decoded['errors'] ) && ! empty( $decoded['errors'] ) ) {
|
||||
$first = $decoded['errors'][0] ?? [];
|
||||
$first = $decoded['errors'][0] ?? array();
|
||||
$msg = is_array( $first )
|
||||
? (string) ( $first['message'] ?? wp_json_encode( $first ) )
|
||||
: (string) $first;
|
||||
@@ -80,7 +80,7 @@ class PRAutoBlogger_Runware_Image_Support {
|
||||
);
|
||||
}
|
||||
|
||||
$data = $decoded['data'] ?? [];
|
||||
$data = $decoded['data'] ?? array();
|
||||
if ( ! is_array( $data ) || empty( $data ) ) {
|
||||
throw new \RuntimeException(
|
||||
esc_html__( 'Runware response contained no data.', 'prautoblogger' )
|
||||
@@ -115,9 +115,12 @@ class PRAutoBlogger_Runware_Image_Support {
|
||||
* @throws \RuntimeException On HTTP error or empty body.
|
||||
*/
|
||||
public function download_image_bytes( string $image_url ): string {
|
||||
$response = wp_remote_get( $image_url, [
|
||||
'timeout' => 30,
|
||||
] );
|
||||
$response = wp_remote_get(
|
||||
$image_url,
|
||||
array(
|
||||
'timeout' => 30,
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
throw new \RuntimeException(
|
||||
|
||||
@@ -61,7 +61,7 @@ interface PRAutoBlogger_Image_Provider_Interface {
|
||||
* an invalid response shape.
|
||||
* @throws \InvalidArgumentException If dimensions are out of range.
|
||||
*/
|
||||
public function generate_image( string $prompt, int $width, int $height, array $options = [] ): array;
|
||||
public function generate_image( string $prompt, int $width, int $height, array $options = array() ): array;
|
||||
|
||||
/**
|
||||
* Estimate the USD cost of a single generation call before making it.
|
||||
@@ -75,7 +75,7 @@ interface PRAutoBlogger_Image_Provider_Interface {
|
||||
*
|
||||
* @return float Estimated cost in USD for a single image at the given dimensions.
|
||||
*/
|
||||
public function estimate_cost( int $width, int $height, array $options = [] ): float;
|
||||
public function estimate_cost( int $width, int $height, array $options = array() ): float;
|
||||
|
||||
/**
|
||||
* Machine-readable provider identifier used in logs and settings.
|
||||
|
||||
@@ -35,7 +35,7 @@ interface PRAutoBlogger_LLM_Provider_Interface {
|
||||
*
|
||||
* @throws \RuntimeException On API error after retries exhausted.
|
||||
*/
|
||||
public function send_chat_completion( array $messages, string $model, array $options = [] ): array;
|
||||
public function send_chat_completion( array $messages, string $model, array $options = array() ): array;
|
||||
|
||||
/**
|
||||
* Get the list of available models from the provider.
|
||||
|
||||
Reference in New Issue
Block a user