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.
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.
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.
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
- PHP AI Client SDK repository, GitHub
- Introducing the AI Client in WordPress 7.0, make.wordpress.org
- Introducing the WordPress AI Client SDK, make.wordpress.org