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:
peptiderepo
2026-04-21 16:48:04 +00:00
parent 1c3d9b50c6
commit 21430ce3bf
11 changed files with 35 additions and 64 deletions

View File

@@ -231,7 +231,7 @@ class PRAutoBlogger_Admin_Page {
$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 ) ) {
// Empty value means password field wasn't touched — keep existing.
if ( '' === $value ) {

View File

@@ -22,7 +22,7 @@ class PRAutoBlogger_Image_Model_Registry {
* Each entry contains:
* - id: Model identifier used in API calls.
* - name: Human-readable display name.
* - provider: 'runware' | 'openrouter' | 'cloudflare'.
* - provider: 'runware' | 'openrouter'.
* - cost_per_image: Estimated USD cost per generation.
* - capabilities: Array of capability tags.
* - description: Short description for the admin UI.
@@ -90,14 +90,6 @@ class PRAutoBlogger_Image_Model_Registry {
'capabilities' => [ 'image_generation' ],
'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.
*
* @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 {
foreach ( self::get_models() as $model ) {

View File

@@ -221,25 +221,9 @@ class PRAutoBlogger_Settings_Fields_Extended {
'section' => 'prautoblogger_images',
'default' => PRAUTOBLOGGER_DEFAULT_IMAGE_MODEL,
'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' ),
],
[
'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',
'label' => __( 'Runware API Key', 'prautoblogger' ),
@@ -270,15 +254,7 @@ class PRAutoBlogger_Settings_Fields_Extended {
'type' => 'toggle',
'section' => 'prautoblogger_images',
'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' ),
],
[
'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' ),
'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' ),
],
];
}

View File

@@ -42,7 +42,7 @@ class PRAutoBlogger_Ajax_Handlers {
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
@set_time_limit( 120 );

View File

@@ -4,8 +4,8 @@ declare(strict_types=1);
/**
* Single-shot retry of NSFW-blocked image slots with a sanitized prompt.
*
* When an image provider (currently only Cloudflare Workers AI) rejects a
* prompt via its content filter, its batch result carries
* When an image provider rejects a prompt via its content filter, its
* batch result carries
* `['error_type' => 'nsfw_blocked']`. This helper reruns just the blocked
* slot once with the rule-based fallback prompt (article title + style
* suffix, no LLM rewrite) and merges the new result back into the batch.

View File

@@ -38,7 +38,7 @@ class PRAutoBlogger_Image_Pipeline {
*
* When no provider is injected, the constructor reads 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_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.
*
* 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
*/
private static function create_default_provider(): PRAutoBlogger_Image_Provider_Interface {
$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 ) {
return new PRAutoBlogger_OpenRouter_Image_Provider();
}
return new PRAutoBlogger_Cloudflare_Image_Provider();
return new PRAutoBlogger_Runware_Image_Provider();
}
/**

View File

@@ -4,18 +4,18 @@ declare(strict_types=1);
/**
* Exception raised when an image provider rejects a prompt as NSFW.
*
* Cloudflare Workers AI returns HTTP 400 with an `errors[].code === 3030`
* body when its content filter blocks a prompt. We catch that shape in
* the provider and raise this typed exception so the image pipeline can
* distinguish a content-filter rejection from a generic 4xx and retry
* once with a sanitized fallback prompt.
* Image providers (Runware, OpenRouter) return provider-specific error
* shapes when their content filters block a prompt. We catch those shapes
* in each provider and raise this typed exception so the image pipeline
* can distinguish a content-filter rejection from a generic 4xx and
* 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.
*
* @see providers/class-cloudflare-image-provider.php — Raises this.
* @see core/class-image-nsfw-retry.php — Catches + rebuilds prompt.
* @see https://developers.cloudflare.com/workers-ai/models/flux-1-schnell/ — Upstream error codes.
* @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.
*/
class PRAutoBlogger_Image_NSFW_Blocked extends \RuntimeException {

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
/**
* 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).
*
* Triggered by: PRAutoBlogger_OpenRouter_Image_Provider (both generate_image()
@@ -12,7 +12,7 @@ declare(strict_types=1);
* Dependencies: None — pure calculation + option reads.
*
* @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 {

View File

@@ -2,7 +2,7 @@
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
* 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.
* 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 ARCHITECTURE.md — External API Integrations table.
*/
@@ -79,7 +80,7 @@ interface PRAutoBlogger_Image_Provider_Interface {
/**
* 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;

View File

@@ -5,9 +5,9 @@ declare(strict_types=1);
* Contract for a provider-specific AI model registry.
*
* 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
* higher-level dispatcher iterates registered registries, calls each with the same
* capability, and unions the results — the per-registry interface stays unchanged.
* Phase 3: a second implementation wraps Runware model data. A higher-level
* dispatcher iterates registered registries, calls each with the same capability,
* and unions the results — the per-registry interface stays unchanged.
*
* Implementations MUST:
* - 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;
/**
* Machine-readable provider identifier (e.g. 'openrouter', 'cloudflare_workers_ai').
* Machine-readable provider identifier (e.g. 'openrouter', 'runware').
*
* @return string
*/

View File

@@ -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' );
// 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
// 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