Reliability

Provider-Switching for Cost and Quality

⏱ 15 min

Course 1 introduced using_model_preference() as a way to avoid hardcoding a single vendor. This lesson treats it as a real architectural decision rather than a one-line trick: how the fallback actually resolves, why a preference list should differ by feature rather than being one global constant, and how to think about cheaper, faster models versus stronger, slower ones for a given task, since “always use the strongest model” is rarely the right default once a feature runs at real traffic volume.

What you'll learn in this lesson
How the preference list actually resolves
First configured, available provider wins, not first in some ranked-quality order.
Per-feature preference lists
Why a single global model choice for your whole plugin is usually the wrong shape.
Cheaper/faster vs stronger/slower
What actually differs between model tiers and which of your features need which.
Combining preference lists with the error handling from Lesson 5
What happens when none of the preferred models are available.
Prerequisites

Lessons 1 through 5, especially the error handling patterns, since this lesson builds directly on what happens when a preferred provider isn’t available.

Step 1: How using_model_preference() actually resolves

using_model_preference() takes an ordered list of model identifiers and the SDK walks it in order, using the first one that has a configured, working provider behind it:

// File: my-plugin/class-summary-feature.php
$result = wp_ai_client_prompt( $prompt_text )
	->using_model_preference( 'claude-sonnet-4-5', 'gemini-3-pro-preview', 'gpt-5.1' )
	->generate_text();

The order you list matters, it’s a preference, not a random pick from a pool. If a site has both Anthropic and OpenAI configured, this example always tries Claude first and only falls through to GPT if Claude’s provider isn’t available for some reason, missing key, that provider plugin deactivated, or (from Lesson 5) a wp_ai_client_prevent_prompt filter blocking it. It does not mean “load balance across whichever is configured,” it means “prefer this one, then this one, then this one.”

Step 2: One global preference list is usually the wrong shape

It’s tempting to define a single constant somewhere and use it everywhere:

// File: my-plugin.php - a shape to avoid
define( 'MY_PLUGIN_MODEL_PREFERENCE', array( 'claude-sonnet-4-5', 'gemini-3-pro-preview', 'gpt-5.1' ) );

The problem is that different features in the same plugin have different actual requirements. A background job tagging thousands of posts with categories has a very different cost and latency budget than a one-off admin tool generating a single page of marketing copy for a human to review before publishing. Scope the preference list to the feature, not the plugin:

// File: my-plugin/class-bulk-tagging.php
const MODEL_PREFERENCE = array( 'claude-sonnet-4-5', 'gemini-3-pro-preview', 'gpt-5.1' );
// File: my-plugin/class-editorial-assistant.php
const MODEL_PREFERENCE = array( 'gemini-3-pro-preview', 'claude-sonnet-4-5', 'gpt-5.1' );

Two features in the same plugin, two independently reasoned-about preference lists. That’s not duplication for its own sake, it’s the same reason you wouldn’t use one global timeout value for every HTTP call a plugin makes regardless of what’s on the other end.

Step 3: Choosing a tier for the task, not by default

Every provider ships a range of models, some optimized for depth and reasoning quality, others optimized for speed and lower per-request cost. Which one belongs in a given feature’s preference list is a real judgment call, weighed against three questions:

  • How much does correctness matter here? A feature that summarizes an internal post for an editor to skim can tolerate an occasional rough summary. A feature generating customer-facing legal or medical text cannot, and should lean toward a stronger model even at higher cost and latency.
  • How often does this run? A feature that fires once when an admin clicks a button can afford a slower, stronger model. A feature that fires on every front-end page load for every visitor needs to be fast and cheap enough that its cost scales sensibly with traffic, and should usually be caching results rather than calling an AI model per request at all.
  • How reversible is a bad output? An AI-drafted comment reply that a human reviews before it’s ever sent is a low-stakes place to try a faster, cheaper model. An automated action taken directly on a mistaken output is not.
Check each provider's own current model list before choosing

Providers add and retire specific model versions more often than any course text can track. Rather than treating any single model identifier as permanent, check Anthropic’s, OpenAI’s, and Google’s own current model documentation when you’re choosing a preference list, and revisit it periodically, the fastest or cheapest option in a given provider’s lineup today may not be the same one in a year.

Step 4: What happens when nothing in the list is available

A preference list is still just a fallback chain, and every entry in it can fail to resolve, no provider configured at all, or every configured provider rejecting the request for one of Lesson 5’s reasons. Combine the preference list with the same is_wp_error() check from that lesson rather than assuming a preference list makes failure impossible:

// File: my-plugin/class-bulk-tagging.php
$result = wp_ai_client_prompt( $prompt_text )
	->using_model_preference( ...self::MODEL_PREFERENCE )
	->generate_text();

if ( is_wp_error( $result ) ) {
	// None of the preferred models resolved to a working provider.
	error_log( 'No AI provider available for bulk tagging: ' . $result->get_error_message() );
	return null;
}

A longer preference list reduces how often this branch runs, it doesn’t eliminate the need to handle it.

A preference list isn't a quality guarantee

Falling through to a second or third model in the list means your feature’s output quality can vary depending on which provider a given site happens to have configured, not just on the prompt. If consistent output quality matters more than availability for a specific feature, consider failing outright with a clear message rather than silently falling through to a noticeably weaker model.

Test it: confirm fallthrough behavior

With only one provider configured, list a model from a provider you don’t have configured first, and confirm the SDK still falls through correctly:

wp-env run cli wp eval '
$result = wp_ai_client_prompt( "Reply with exactly the word: fallback-worked" )
	->using_model_preference( "gpt-5.1", "claude-sonnet-4-5", "gemini-3-pro-preview" )
	->generate_text();
echo $result;
'

If you only have Claude configured, you should still see fallback-worked, confirming the SDK skipped past the unavailable GPT preference to the working Claude one.

Recap

using_model_preference() resolves to the first model in your ordered list that has a working, configured provider, it’s a fallback chain, not a load balancer. Scope preference lists to the feature rather than the whole plugin, choose a stronger or weaker model tier based on how much correctness, frequency, and reversibility actually matter for that specific feature, and keep the error handling from Lesson 5 in place even with a long preference list, since every entry in it can still fail to resolve.

Resources & further reading

← Handling Errors and Building Fallbacks Adding AI Features to Your Own Plugin →