WordPress 7.0 ships with a built-in AI Client. You configure a provider, send a prompt, and get a response back through a consistent PHP API. Sounds simple enough. But if you’re connecting it to a local Ollama instance, there are a few things the docs won’t tell you.
Last Friday I did a stream where I tried an Ollama connector for the new connector screen (In Spanish). While I initially struggled to get it working, I eventually succeeded, so I wanted to share what I learned about using local models with WordPress.
This post walks through getting it working: the code, the wp_ai_client_prompt() API, and the gotchas that cost me time so they don’t cost you yours.
For the full API reference, check the dev note on make/core.
- The working code
- The
wp_ai_client_prompt()API - Silent failures and how to debug them
- Hook priority matters
- Building your own local provider plugin
- A defensive pattern for local providers
The working code
Prerequisites
Two things before you touch WordPress:
Ollama installed and running. After installation, verify with ollama list in your terminal.
At least one model pulled. If you haven’t pulled any model yet, run ollama pull llama3.2 (or whatever model you prefer). The AI Client needs at least one model available to report the provider as configured.
Install the plugin
Install and activate AI Provider for Ollama from your WordPress admin (Plugins > Add New) or via WP-CLI:
wp plugin install ai-provider-for-ollama --activateThe plugin registers Ollama as an AI provider in the WordPress Connectors system. It handles all the plumbing for you: localhost HTTP allowlisting, port registration, and fallback authentication for local instances.
Verify on the Connectors screen
Go to Settings > Connectors in your WordPress admin. You should see Ollama listed as a provider. No API key is needed for local instances, the plugin handles that automatically.
Then go to Settings > Ollama. The “Available Models” section should list the models you’ve pulled. If the host URL field is empty, the plugin defaults to http://localhost:11434.
If you’re using Ollama Cloud instead of a local instance, enter your Ollama Cloud host URL in the settings and add your API key on the Connectors screen.
Run your first prompt
The fastest way to test is a small throwaway plugin. Create wp-content/plugins/test-connectors/index.php:
<?php
/**
* Plugin Name: Test Connectors
* Description: A test plugin for connectors.
* Version: 1.0
*/
// Priority 25: runs after provider registration (5), fallback auth (15),
// and core credential wiring via _wp_connectors_pass_default_keys_to_ai_client (20).
add_action(
'init',
function () {
$result = wp_ai_client_prompt( 'Hello, how are you?' )
->using_provider( 'ollama' )
->using_system_instruction( 'You are a helpful assistant.' )
->generate_text();
if ( is_wp_error( $result ) ) {
error_log( 'Connectors Test Error: ' . $result->get_error_message() );
return;
}
error_log( 'Generated Text: ' . $result );
},
25
);Activate the plugin, load any page on your site, and check wp-content/debug.log. You should see something like:
Generated Text: Hello! I'm doing well, thank you for asking. How can I assist you today?⚠️ Notice the priority 25 on that add_action call. The default is 10, and at that point the provider’s authentication isn’t wired up yet. Your prompt will silently fail with “No models found.” More on this in the Hook priority matters section.
If it works, deactivate the test plugin. You don’t want it running on every page load.
Three things to notice in this code, each explained in detail below:
- The methods are snake_case:
using_provider(), notusingProvider() - The result is checked with
is_wp_error()before use - The hook runs at priority 25, not the default 10
The wp_ai_client_prompt() API
Every interaction with the AI Client starts with wp_ai_client_prompt(). It returns a WP_AI_Client_Prompt_Builder, a fluent builder where you chain configuration before calling a generation method:
$text = wp_ai_client_prompt( 'Summarize the benefits of caching in WordPress.' )
->using_temperature( 0.7 )
->generate_text();The builder supports text generation, image generation, text-to-speech, and more. Here are the key methods:
| Configuration | Method |
|---|---|
| System instruction | using_system_instruction() |
| Provider preference | using_provider() |
| Model preference | using_model_preference() |
| Temperature | using_temperature() |
| Max tokens | using_max_tokens() |
| JSON response | as_json_response( $schema ) |
| Feature check | is_supported_for_text_generation() |
For richer metadata (token usage, provider/model info), use generate_text_result() instead of generate_text(). The result object is serializable and works with rest_ensure_response().
⚠️ Some third-party documentation, including the ai-provider-for-ollama README, still references WordPress\AI_Client\Prompt_Builder. That was the entry point from the standalone wp-ai-client package that predated WordPress 7.0. That class no longer exists.
I’ve opened an issue to get the README updated.
Here are the key differences from the old API:
| Old API (pre-7.0) | WordPress 7.0 |
|---|---|
Prompt_Builder::create() | wp_ai_client_prompt( 'prompt text' ) |
->set_system_instruction() | ->using_system_instruction() |
->add_text_message( 'text' ) | Prompt text passed as function argument |
| camelCase SDK methods | snake_case wrapper methods |
For the full API reference, including structured JSON responses, image generation, multimodal output, and more, see Introducing the AI Client in WordPress 7.0.
Silent failures and how to debug them
The AI Client is designed to fail gracefully. That sounds great until you’re debugging, because “graceful failure” really means errors hide behind several layers of silence.
generate_text() returns WP_Error, not a string
Without is_wp_error(), concatenating the result with a string throws a fatal. The error message won’t point you to the real problem. It just tells you the object couldn’t be converted to a string.
isConfigured() swallows all exceptions
Before generating, the SDK calls isProviderConfigured(), which tries to list models from the provider. If that request fails for any reason (network error, no auth set, server down), the exception is caught and isConfigured() returns false. You see:
No models found for provider "ollama" that support text_generation for this prompt.That message tells you the provider returned zero usable models. Not why.
fetchModelDetails() also swallows exceptions
After fetching the model list from /api/tags, the provider calls /api/show for each model to check its capabilities. If those calls fail, it returns null and falls back to text-only generation. Quietly.
Debugging approach
Let’s walk through this step by step. Work outward from the HTTP layer:
// Step 1: can PHP reach Ollama at all?
$test = wp_safe_remote_get( 'http://localhost:11434/api/tags' );
if ( is_wp_error( $test ) ) {
error_log( 'Ollama unreachable: ' . $test->get_error_message() );
} else {
error_log( 'Ollama HTTP status: ' . wp_remote_retrieve_response_code( $test ) );
}
// Step 2: does generate_text() return a WP_Error?
$result = wp_ai_client_prompt( 'Hello' )
->using_provider( 'ollama' )
->generate_text();
if ( is_wp_error( $result ) ) {
error_log( $result->get_error_message() );
}💡 Start with Step 1 every time. If wp_safe_remote_get() returns a 200 but you still get “No models found,” the problem is in the registry setup (auth, priorities), not connectivity. That narrows things down fast.
A 200 from Ollama but “No models found” means the problem is in the registry setup, not connectivity. Keep reading.
Hook priority matters
The registry needs a specific setup sequence before wp_ai_client_prompt() works reliably. Run your code too early in init and the provider’s authentication won’t be wired up yet. The availability check fails silently.
Here’s the actual sequence in WordPress 7.0:
| Priority | What happens |
|---|---|
| 5 | Ollama provider plugin registers the provider in the registry – source |
| 15 | Ollama plugin registers fallback auth (empty API key for local instances) – source |
| 20 | WordPress core passes Connectors screen credentials to the AI Client – source |
| 25+ | Safe to call wp_ai_client_prompt() |
The default init priority is 10. Hook there and the provider is registered but has no authentication set. The availability check fails silently. You see “No models found” despite Ollama running fine.
// Default priority 10 — runs before auth is wired up.
add_action( 'init', function () {
$result = wp_ai_client_prompt( 'Hello' )
->using_provider( 'ollama' )
->generate_text();
} );
// Priority 25 — runs after the full setup sequence.
add_action( 'init', function () {
$result = wp_ai_client_prompt( 'Hello' )
->using_provider( 'ollama' )
->generate_text();
}, 25 );This isn’t documented anywhere. You can trace it by reading connectors.php and default-filters.php in core, but you’d only go there after ruling out everything else. Priority 25 is the safe minimum for any code calling the AI Client on init.
Building your own local provider plugin
If you’re building a plugin for another local model runner (LM Studio, Jan, llama.cpp, or anything else listening on localhost), the Ollama plugin handles a few things you’ll need to replicate yourself.
Allow localhost requests
wp_safe_remote_request(), the function the AI Client’s HTTP adapter uses, blocks requests to private IPs and localhost by default. It’s SSRF protection built into WordPress. Your provider plugin needs to explicitly allow requests to its local host:
add_filter(
'http_request_host_is_external',
function ( $external, $host, $url ) {
if ( strpos( $url, 'http://localhost:YOUR_PORT' ) !== false ) {
return true;
}
return $external;
},
10,
3
);Without this filter, every HTTP request from the SDK to your local server fails silently. You can see how the Ollama plugin does this in Plugin.php (lines 177-183). It also hooks http_allowed_safe_ports to allowlist port 11434, which you’ll need too if your runner uses a non-standard port.
Register fallback authentication
The SDK assumes all providers require authentication. Local providers usually don’t need an API key, but the registry still needs an auth instance. Register an empty string as a placeholder at init priority 15, after provider registration at 5, before core credentials at 20:
add_action(
'init',
function () use ( $registry ) {
// Only set fallback if no real auth was configured.
if ( null !== $registry->getProviderRequestAuthentication( 'my-provider' ) ) {
return;
}
$registry->setProviderRequestAuthentication(
'my-provider',
new ApiKeyRequestAuthentication( '' )
);
},
15
);Without this, isProviderConfigured() returns false and “No models found” is all you’ll see. See the Ollama plugin’s register_fallback_auth() for a production example. It also guards against overwriting real credentials if a user enters an API key on the Connectors screen.
A defensive pattern for local providers
Cloud providers are either available or not. You have a valid API key or you don’t.
Local providers are different. A server like Ollama can be stopped, mid-startup, or running without the model your plugin needs. Unlike a cloud API, its availability can change between page loads — or
even mid-request.
That’s where is_supported_for_text_generation() comes in. It’s a pre-flight check: before committing to the actual AI call, you ask
“can this provider handle text generation right now?” The check is deterministic, makes no API calls, and costs nothing — so it’s safe to use in rendering paths.
add_action(
'init',
function () {
$prompt = wp_ai_client_prompt( 'Summarize this post.' )
->using_provider( 'ollama' )
->using_system_instruction( 'You are a helpful assistant.' );
if ( ! $prompt->is_supported_for_text_generation() ) {
// Ollama isn't running, has no compatible models,
// or the registry isn't fully set up.
return;
}
$result = $prompt->generate_text();
if ( is_wp_error( $result ) ) {
error_log( 'AI error: ' . $result->get_error_message() );
return;
}
error_log( 'Generated: ' . $result );
},
25
);Without this check, your plugin would attempt the call anyway — potentially waiting for a connection timeout against a server that isn’t there, then logging an unhelpful error. With it, the feature silently skips when the
provider isn’t ready.
If your plugin ships local AI features, this is the difference between a graceful “feature unavailable” and a slow page load with cryptic errors in the log.
For more details on feature detection and sibling methods like is_supported_for_image_generation(), see the Introducing the AI Client in WordPress 7.0 dev note. The WordPress/wp-ai-client GitHub repository is also a good reference for exploring the implementation.
Useful links
- Introducing the AI Client in WordPress 7.0 — the official dev note
- WP AI Client repository — PHP API reference and examples
- PHP AI Client repository — the underlying SDK
- Fueled/ai-provider-for-ollama repo
- Trac #64591 — the merge ticket for WP AI Client into WordPress 7.0
- WordPress Connectors merge proposal
If you’ve connected WordPress to another local provider and ran into something different, I’d love to hear about it.

Leave a Reply