cf-removal: patch registry, pipeline, settings, admin-page, interface
Wires out the Cloudflare Workers AI image provider across the plugin: - class-image-pipeline.php: drop 'cloudflare' dispatch branch; Runware becomes the unrecognised-provider fallback - class-image-model-registry.php: remove flux-1-schnell/cloudflare row; update provider-enum docblocks (runware|openrouter only) - class-settings-fields-extended.php: remove three CF fields (cloudflare_ai_token, cloudflare_account_id, cf_image_via_gateway); drop Cloudflare mention from model-picker + NSFW retry descriptions - class-admin-page.php: remove prautoblogger_cloudflare_ai_token from the encrypted-options list - interface-image-provider.php: retarget preamble @see at Runware + OpenRouter; update get_provider_name() example - class-image-nsfw-blocked.php: generalise preamble (Runware + OpenRouter can raise this) Also cleans stale CF Workers AI mentions in: class-ajax-handlers.php, interface-model-registry.php, class-image-nsfw-retry.php, class-open-router-image-pricing.php, prautoblogger.php. The Cloudflare AI Gateway path (in class-open-router-config.php, class-settings-fields.php 'prautoblogger_ai_gateway_base_url', class-open-router-request-builder.php cache headers) is intentionally untouched — it fronts OpenRouter and is unrelated to the CF Workers AI image provider.
This commit is contained in:
@@ -231,7 +231,7 @@ class PRAutoBlogger_Admin_Page {
|
|||||||
$option_name = substr( $filter, strlen( 'sanitize_option_' ) );
|
$option_name = substr( $filter, strlen( 'sanitize_option_' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
$encrypted = [ 'prautoblogger_openrouter_api_key', 'prautoblogger_ga4_credentials_json', 'prautoblogger_cloudflare_ai_token', 'prautoblogger_runware_api_key' ];
|
$encrypted = [ 'prautoblogger_openrouter_api_key', 'prautoblogger_ga4_credentials_json', 'prautoblogger_runware_api_key' ];
|
||||||
if ( in_array( $option_name, $encrypted, true ) ) {
|
if ( in_array( $option_name, $encrypted, true ) ) {
|
||||||
// Empty value means password field wasn't touched — keep existing.
|
// Empty value means password field wasn't touched — keep existing.
|
||||||
if ( '' === $value ) {
|
if ( '' === $value ) {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class PRAutoBlogger_Image_Model_Registry {
|
|||||||
* Each entry contains:
|
* Each entry contains:
|
||||||
* - id: Model identifier used in API calls.
|
* - id: Model identifier used in API calls.
|
||||||
* - name: Human-readable display name.
|
* - name: Human-readable display name.
|
||||||
* - provider: 'runware' | 'openrouter' | 'cloudflare'.
|
* - provider: 'runware' | 'openrouter'.
|
||||||
* - cost_per_image: Estimated USD cost per generation.
|
* - cost_per_image: Estimated USD cost per generation.
|
||||||
* - capabilities: Array of capability tags.
|
* - capabilities: Array of capability tags.
|
||||||
* - description: Short description for the admin UI.
|
* - description: Short description for the admin UI.
|
||||||
@@ -90,14 +90,6 @@ class PRAutoBlogger_Image_Model_Registry {
|
|||||||
'capabilities' => [ 'image_generation' ],
|
'capabilities' => [ 'image_generation' ],
|
||||||
'description' => __( 'OpenAI premium. Photorealistic.', 'prautoblogger' ),
|
'description' => __( 'OpenAI premium. Photorealistic.', 'prautoblogger' ),
|
||||||
],
|
],
|
||||||
[
|
|
||||||
'id' => 'flux-1-schnell',
|
|
||||||
'name' => 'FLUX.1 schnell (Cloudflare)',
|
|
||||||
'provider' => 'cloudflare',
|
|
||||||
'cost_per_image' => 0.0007,
|
|
||||||
'capabilities' => [ 'image_generation' ],
|
|
||||||
'description' => __( 'Legacy Cloudflare fallback. Use Runware FLUX instead.', 'prautoblogger' ),
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +98,7 @@ class PRAutoBlogger_Image_Model_Registry {
|
|||||||
* model id is not in the registry.
|
* model id is not in the registry.
|
||||||
*
|
*
|
||||||
* @param string $model_id Model slug from the admin UI.
|
* @param string $model_id Model slug from the admin UI.
|
||||||
* @return string Provider id ('runware' | 'openrouter' | 'cloudflare' | '').
|
* @return string Provider id ('runware' | 'openrouter' | '').
|
||||||
*/
|
*/
|
||||||
public static function provider_for( string $model_id ): string {
|
public static function provider_for( string $model_id ): string {
|
||||||
foreach ( self::get_models() as $model ) {
|
foreach ( self::get_models() as $model ) {
|
||||||
|
|||||||
@@ -221,25 +221,9 @@ class PRAutoBlogger_Settings_Fields_Extended {
|
|||||||
'section' => 'prautoblogger_images',
|
'section' => 'prautoblogger_images',
|
||||||
'default' => PRAUTOBLOGGER_DEFAULT_IMAGE_MODEL,
|
'default' => PRAUTOBLOGGER_DEFAULT_IMAGE_MODEL,
|
||||||
'capability' => 'image_generation',
|
'capability' => 'image_generation',
|
||||||
'description' => __( 'Pick an image model. The provider (OpenRouter or Cloudflare Workers AI) is derived from the model registry on save, so mismatched pairs are no longer possible.', 'prautoblogger' ),
|
'description' => __( 'Pick an image model. The provider (Runware or OpenRouter) is derived from the model registry on save, so mismatched pairs are no longer possible.', 'prautoblogger' ),
|
||||||
'badge' => __( 'Quality', 'prautoblogger' ),
|
'badge' => __( 'Quality', 'prautoblogger' ),
|
||||||
],
|
],
|
||||||
[
|
|
||||||
'id' => 'prautoblogger_cloudflare_ai_token',
|
|
||||||
'label' => __( 'Cloudflare API Token', 'prautoblogger' ),
|
|
||||||
'type' => 'password',
|
|
||||||
'section' => 'prautoblogger_images',
|
|
||||||
'description' => __( 'Only needed if using Cloudflare provider. Workers AI token with Read + Edit scope.', 'prautoblogger' ),
|
|
||||||
'icon' => '🔑',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'id' => 'prautoblogger_cloudflare_account_id',
|
|
||||||
'label' => __( 'Cloudflare Account ID', 'prautoblogger' ),
|
|
||||||
'type' => 'text',
|
|
||||||
'section' => 'prautoblogger_images',
|
|
||||||
'default' => '',
|
|
||||||
'description' => __( 'Only needed if using Cloudflare provider.', 'prautoblogger' ),
|
|
||||||
],
|
|
||||||
[
|
[
|
||||||
'id' => 'prautoblogger_runware_api_key',
|
'id' => 'prautoblogger_runware_api_key',
|
||||||
'label' => __( 'Runware API Key', 'prautoblogger' ),
|
'label' => __( 'Runware API Key', 'prautoblogger' ),
|
||||||
@@ -270,15 +254,7 @@ class PRAutoBlogger_Settings_Fields_Extended {
|
|||||||
'type' => 'toggle',
|
'type' => 'toggle',
|
||||||
'section' => 'prautoblogger_images',
|
'section' => 'prautoblogger_images',
|
||||||
'default' => '1',
|
'default' => '1',
|
||||||
'description' => __( 'When the provider rejects an image prompt as NSFW (e.g. Cloudflare code 3030), retry once with a generic fallback scene built from the article title. Disable to fail fast if the filter gets trigger-happy.', 'prautoblogger' ),
|
'description' => __( 'When the provider rejects an image prompt as NSFW, retry once with a generic fallback scene built from the article title. Disable to fail fast if the filter gets trigger-happy.', 'prautoblogger' ),
|
||||||
],
|
|
||||||
[
|
|
||||||
'id' => 'prautoblogger_cf_image_via_gateway',
|
|
||||||
'label' => __( 'Route Cloudflare Images via AI Gateway', 'prautoblogger' ),
|
|
||||||
'type' => 'toggle',
|
|
||||||
'section' => 'prautoblogger_images',
|
|
||||||
'default' => '1',
|
|
||||||
'description' => __( 'Route Cloudflare Workers AI image calls through the Cloudflare AI Gateway (requires the AI Gateway URL configured under API Keys). Adds response caching, cost logging, and rate-limiting. Turn off to hit the direct Workers AI API — only needed if the gateway route regresses.', 'prautoblogger' ),
|
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class PRAutoBlogger_Ajax_Handlers {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Image generation can take 20-30s on Cloudflare Workers AI.
|
// Image generation can take 20-30s (Runware FLUX + OpenRouter image models).
|
||||||
// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
|
// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
|
||||||
@set_time_limit( 120 );
|
@set_time_limit( 120 );
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
|||||||
/**
|
/**
|
||||||
* Single-shot retry of NSFW-blocked image slots with a sanitized prompt.
|
* Single-shot retry of NSFW-blocked image slots with a sanitized prompt.
|
||||||
*
|
*
|
||||||
* When an image provider (currently only Cloudflare Workers AI) rejects a
|
* When an image provider rejects a prompt via its content filter, its
|
||||||
* prompt via its content filter, its batch result carries
|
* batch result carries
|
||||||
* `['error_type' => 'nsfw_blocked']`. This helper reruns just the blocked
|
* `['error_type' => 'nsfw_blocked']`. This helper reruns just the blocked
|
||||||
* slot once with the rule-based fallback prompt (article title + style
|
* slot once with the rule-based fallback prompt (article title + style
|
||||||
* suffix, no LLM rewrite) and merges the new result back into the batch.
|
* suffix, no LLM rewrite) and merges the new result back into the batch.
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class PRAutoBlogger_Image_Pipeline {
|
|||||||
*
|
*
|
||||||
* When no provider is injected, the constructor reads the
|
* When no provider is injected, the constructor reads the
|
||||||
* `prautoblogger_image_provider` setting and instantiates the
|
* `prautoblogger_image_provider` setting and instantiates the
|
||||||
* matching concrete class ('runware', 'openrouter', or 'cloudflare').
|
* matching concrete class ('runware' or 'openrouter').
|
||||||
*
|
*
|
||||||
* @param PRAutoBlogger_Image_Provider_Interface|null $provider Optional provider override.
|
* @param PRAutoBlogger_Image_Provider_Interface|null $provider Optional provider override.
|
||||||
* @param PRAutoBlogger_Cost_Tracker|null $cost_tracker Optional cost tracker.
|
* @param PRAutoBlogger_Cost_Tracker|null $cost_tracker Optional cost tracker.
|
||||||
@@ -56,17 +56,19 @@ class PRAutoBlogger_Image_Pipeline {
|
|||||||
/**
|
/**
|
||||||
* Instantiate the image provider based on the admin setting.
|
* Instantiate the image provider based on the admin setting.
|
||||||
*
|
*
|
||||||
|
* Returns Runware by default when the setting is unrecognised —
|
||||||
|
* Cloudflare Workers AI was removed as a provider in v0.10.0; legacy
|
||||||
|
* 'cloudflare' values are migrated at admin_init, but an unmigrated
|
||||||
|
* install should still get a working fallback rather than a fatal.
|
||||||
|
*
|
||||||
* @return PRAutoBlogger_Image_Provider_Interface
|
* @return PRAutoBlogger_Image_Provider_Interface
|
||||||
*/
|
*/
|
||||||
private static function create_default_provider(): PRAutoBlogger_Image_Provider_Interface {
|
private static function create_default_provider(): PRAutoBlogger_Image_Provider_Interface {
|
||||||
$provider_id = (string) get_option( 'prautoblogger_image_provider', PRAUTOBLOGGER_DEFAULT_IMAGE_PROVIDER );
|
$provider_id = (string) get_option( 'prautoblogger_image_provider', PRAUTOBLOGGER_DEFAULT_IMAGE_PROVIDER );
|
||||||
if ( 'runware' === $provider_id ) {
|
|
||||||
return new PRAutoBlogger_Runware_Image_Provider();
|
|
||||||
}
|
|
||||||
if ( 'openrouter' === $provider_id ) {
|
if ( 'openrouter' === $provider_id ) {
|
||||||
return new PRAutoBlogger_OpenRouter_Image_Provider();
|
return new PRAutoBlogger_OpenRouter_Image_Provider();
|
||||||
}
|
}
|
||||||
return new PRAutoBlogger_Cloudflare_Image_Provider();
|
return new PRAutoBlogger_Runware_Image_Provider();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,18 +4,18 @@ declare(strict_types=1);
|
|||||||
/**
|
/**
|
||||||
* Exception raised when an image provider rejects a prompt as NSFW.
|
* Exception raised when an image provider rejects a prompt as NSFW.
|
||||||
*
|
*
|
||||||
* Cloudflare Workers AI returns HTTP 400 with an `errors[].code === 3030`
|
* Image providers (Runware, OpenRouter) return provider-specific error
|
||||||
* body when its content filter blocks a prompt. We catch that shape in
|
* shapes when their content filters block a prompt. We catch those shapes
|
||||||
* the provider and raise this typed exception so the image pipeline can
|
* in each provider and raise this typed exception so the image pipeline
|
||||||
* distinguish a content-filter rejection from a generic 4xx and retry
|
* can distinguish a content-filter rejection from a generic 4xx and
|
||||||
* once with a sanitized fallback prompt.
|
* retry once with a sanitized fallback prompt.
|
||||||
*
|
*
|
||||||
* Triggered by: PRAutoBlogger_Cloudflare_Image_Provider on NSFW 4xx.
|
* Triggered by: image providers on NSFW-filter rejection.
|
||||||
* Dependencies: None.
|
* Dependencies: None.
|
||||||
*
|
*
|
||||||
* @see providers/class-cloudflare-image-provider.php — Raises this.
|
* @see providers/class-runware-image-provider.php — May raise this.
|
||||||
|
* @see providers/class-open-router-image-provider.php — May raise this.
|
||||||
* @see core/class-image-nsfw-retry.php — Catches + rebuilds prompt.
|
* @see core/class-image-nsfw-retry.php — Catches + rebuilds prompt.
|
||||||
* @see https://developers.cloudflare.com/workers-ai/models/flux-1-schnell/ — Upstream error codes.
|
|
||||||
*/
|
*/
|
||||||
class PRAutoBlogger_Image_NSFW_Blocked extends \RuntimeException {
|
class PRAutoBlogger_Image_NSFW_Blocked extends \RuntimeException {
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
|||||||
/**
|
/**
|
||||||
* Pricing + model-id resolution helpers for the OpenRouter image provider.
|
* Pricing + model-id resolution helpers for the OpenRouter image provider.
|
||||||
*
|
*
|
||||||
* Mirrors the Cloudflare pricing helper pattern. Each model's cost per image
|
* Mirrors the Runware pricing helper pattern. Each model's cost per image
|
||||||
* is a flat rate sourced from OpenRouter's pricing page (April 2026).
|
* is a flat rate sourced from OpenRouter's pricing page (April 2026).
|
||||||
*
|
*
|
||||||
* Triggered by: PRAutoBlogger_OpenRouter_Image_Provider (both generate_image()
|
* Triggered by: PRAutoBlogger_OpenRouter_Image_Provider (both generate_image()
|
||||||
@@ -12,7 +12,7 @@ declare(strict_types=1);
|
|||||||
* Dependencies: None — pure calculation + option reads.
|
* Dependencies: None — pure calculation + option reads.
|
||||||
*
|
*
|
||||||
* @see class-openrouter-image-provider.php — Caller.
|
* @see class-openrouter-image-provider.php — Caller.
|
||||||
* @see class-cloudflare-image-pricing.php — Sibling pattern for Cloudflare.
|
* @see class-runware-image-pricing.php — Sibling pattern for Runware.
|
||||||
*/
|
*/
|
||||||
class PRAutoBlogger_OpenRouter_Image_Pricing {
|
class PRAutoBlogger_OpenRouter_Image_Pricing {
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contract for any image generation provider (Cloudflare Workers AI, DALL-E, etc.).
|
* Contract for any image generation provider (Runware FLUX, OpenRouter Gemini, etc.).
|
||||||
*
|
*
|
||||||
* Consumers depend on this interface, never on a concrete provider — swapping
|
* Consumers depend on this interface, never on a concrete provider — swapping
|
||||||
* providers means writing one new class, not refactoring the pipeline. This
|
* providers means writing one new class, not refactoring the pipeline. This
|
||||||
@@ -13,7 +13,8 @@ declare(strict_types=1);
|
|||||||
* content generation run, after editorial review, before publish.
|
* content generation run, after editorial review, before publish.
|
||||||
* Dependencies: None at interface level — implementations wrap a specific HTTP API.
|
* Dependencies: None at interface level — implementations wrap a specific HTTP API.
|
||||||
*
|
*
|
||||||
* @see class-cloudflare-image-provider.php — Cloudflare Workers AI implementation.
|
* @see class-runware-image-provider.php — Runware FLUX implementation (default).
|
||||||
|
* @see class-open-router-image-provider.php — OpenRouter image implementation.
|
||||||
* @see interface-llm-provider.php — Parallel pattern for LLM providers.
|
* @see interface-llm-provider.php — Parallel pattern for LLM providers.
|
||||||
* @see ARCHITECTURE.md — External API Integrations table.
|
* @see ARCHITECTURE.md — External API Integrations table.
|
||||||
*/
|
*/
|
||||||
@@ -79,7 +80,7 @@ interface PRAutoBlogger_Image_Provider_Interface {
|
|||||||
/**
|
/**
|
||||||
* Machine-readable provider identifier used in logs and settings.
|
* Machine-readable provider identifier used in logs and settings.
|
||||||
*
|
*
|
||||||
* @return string Lowercase identifier, e.g. 'cloudflare_workers_ai'.
|
* @return string Lowercase identifier, e.g. 'runware' or 'openrouter'.
|
||||||
*/
|
*/
|
||||||
public function get_provider_name(): string;
|
public function get_provider_name(): string;
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ declare(strict_types=1);
|
|||||||
* Contract for a provider-specific AI model registry.
|
* Contract for a provider-specific AI model registry.
|
||||||
*
|
*
|
||||||
* Phase 1 (v1): single implementation for OpenRouter (`class-open-router-model-registry.php`).
|
* Phase 1 (v1): single implementation for OpenRouter (`class-open-router-model-registry.php`).
|
||||||
* Phase 3: a second implementation wraps Cloudflare Workers AI model data. A
|
* Phase 3: a second implementation wraps Runware model data. A higher-level
|
||||||
* higher-level dispatcher iterates registered registries, calls each with the same
|
* dispatcher iterates registered registries, calls each with the same capability,
|
||||||
* capability, and unions the results — the per-registry interface stays unchanged.
|
* and unions the results — the per-registry interface stays unchanged.
|
||||||
*
|
*
|
||||||
* Implementations MUST:
|
* Implementations MUST:
|
||||||
* - Store the normalized registry as a WP option + transient cache layer.
|
* - Store the normalized registry as a WP option + transient cache layer.
|
||||||
@@ -86,7 +86,7 @@ interface PRAutoBlogger_Model_Registry_Interface {
|
|||||||
public function get_fetched_at(): int;
|
public function get_fetched_at(): int;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Machine-readable provider identifier (e.g. 'openrouter', 'cloudflare_workers_ai').
|
* Machine-readable provider identifier (e.g. 'openrouter', 'runware').
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ define( 'PRAUTOBLOGGER_DEFAULT_WRITING_MODEL', 'google/gemini-2.5-flash-lite' );
|
|||||||
define( 'PRAUTOBLOGGER_DEFAULT_EDITOR_MODEL', 'google/gemini-2.5-flash-lite' );
|
define( 'PRAUTOBLOGGER_DEFAULT_EDITOR_MODEL', 'google/gemini-2.5-flash-lite' );
|
||||||
|
|
||||||
// Image generation defaults.
|
// Image generation defaults.
|
||||||
// Default provider is Runware; OpenRouter (Gemini) + Cloudflare remain as alternates.
|
// Default provider is Runware; OpenRouter (Gemini) remains as the alternate.
|
||||||
// Default model is FLUX.1 schnell via Runware (~$0.0006/image, ~2s). CEO
|
// Default model is FLUX.1 schnell via Runware (~$0.0006/image, ~2s). CEO
|
||||||
// decision 2026-04-21 after comic A/B: schnell's looser fidelity reads as
|
// decision 2026-04-21 after comic A/B: schnell's looser fidelity reads as
|
||||||
// editorial-cartoon style and is 65× cheaper than Nano Banana. Users can
|
// editorial-cartoon style and is 65× cheaper than Nano Banana. Users can
|
||||||
|
|||||||
Reference in New Issue
Block a user