A comprehensive guide to the WP AI Client and Abilities API for plugin and theme developers.
- Introduction and Architecture Overview
- Setting Up Connectors (API Key Management)
- Your First AI Prompt (Quick Start)
- The Prompt Builder In Depth
- The Abilities API (Since 6.9)
- Connecting Abilities to AI (Function Calling)
- REST API Endpoints
- Hooks and Filters Reference
- Architecture Deep Dive
- Complete Plugin Example
- Best Practices and Common Patterns
- Troubleshooting
- API Quick Reference
WordPress 7.0 introduces the WP AI Client, a provider-agnostic AI integration framework. It builds on the Abilities API introduced in WordPress 6.9 to let plugins expose structured functionality that AI models can invoke as tools.
┌─────────────────────────────────────────────┐
│ WordPress Developer API │
│ wp_ai_client_prompt() / Abilities API │
├─────────────────────────────────────────────┤
│ Adapter Layer │
│ PSR-18 HTTP · PSR-16 Cache · PSR-14 Events │
├─────────────────────────────────────────────┤
│ PHP AI Client SDK │
│ Provider Registry · Models · DTOs │
└─────────────────────────────────────────────┘
- WordPress Developer API — the public functions you call:
wp_ai_client_prompt(),wp_register_ability(), and friends. These use WordPress conventions (snake_case,WP_Error, hooks). - Adapter Layer — four small classes that bridge WordPress internals to PSR interfaces so the SDK can use
wp_safe_remote_request()for HTTP,wp_cache_*for caching, anddo_action()for lifecycle events. - PHP AI Client SDK — the vendor library under
wp-includes/php-ai-client/. It handles provider negotiation, model selection, request formatting, and response parsing.
The framework ships with support for Google (Gemini), OpenAI, and Anthropic. Your code never talks to a specific provider directly. You configure API keys, and the SDK picks a suitable model automatically—or you can request one explicitly.
$text = wp_ai_client_prompt( 'Summarize WordPress in one sentence.' )
->generate_text();
if ( is_wp_error( $text ) ) {
error_log( $text->get_error_message() );
} else {
echo $text; // "WordPress is an open-source content management system..."
}Before any AI call can succeed, the site needs at least one configured provider API key.
Navigate to Settings > Connectors in the WordPress admin. You will see fields for each supported provider where you can enter your API key.
| Provider | Option Name |
|---|---|
connectors_ai_google_api_key |
|
| OpenAI | connectors_ai_openai_api_key |
| Anthropic | connectors_ai_anthropic_api_key |
- Storage: Keys are saved as regular WordPress options via
update_option(). - Sanitization: Each key passes through
sanitize_text_field()and is validated against the provider before being stored. If validation fails, an empty string is saved. - Masking: A filter on
option_{$setting_name}replaces all but the last 4 characters with bullet characters. This meansget_option( 'connectors_ai_google_api_key' )returns a masked value in normal usage. - Validation: On save and when requested via the REST Settings endpoint, the key is tested using the provider's own availability check (
$registry->isProviderConfigured()). - Passing to SDK: On
init, WordPress reads the real (unmasked) keys and passes them to the SDK'sProviderRegistryviaApiKeyRequestAuthentication.
If you need to set a key programmatically (e.g., from a constant):
// In wp-config.php or a mu-plugin:
define( 'MY_OPENAI_KEY', 'sk-...' );
// In a plugin:
add_action( 'init', function () {
if ( defined( 'MY_OPENAI_KEY' ) && ! get_option( 'connectors_ai_openai_api_key' ) ) {
update_option( 'connectors_ai_openai_api_key', MY_OPENAI_KEY );
}
}, 5 );Note: The
update_option()call triggers the sanitization and validation callbacks registered by the Connectors API.
Everything starts with wp_ai_client_prompt():
function wp_ai_client_prompt( $prompt = null ): WP_AI_Client_Prompt_BuilderIt accepts an optional initial prompt (a string, MessagePart, Message, or an array/list of these) and returns a fluent builder.
$text = wp_ai_client_prompt( 'Write a haiku about PHP.' )
->generate_text();
if ( is_wp_error( $text ) ) {
wp_die( $text->get_error_message() );
}
echo esc_html( $text );$text = wp_ai_client_prompt( 'Explain caching in WordPress.' )
->using_temperature( 0.3 )
->using_max_tokens( 500 )
->generate_text();You don't have to provide the prompt in the constructor. Use with_text() to add content incrementally:
$builder = wp_ai_client_prompt()
->using_system_instruction( 'You are a WordPress documentation assistant.' )
->with_text( 'What is the difference between posts and pages?' )
->using_temperature( 0.2 )
->using_max_tokens( 1000 );
$text = $builder->generate_text();Every generation method returns either a result or a WP_Error:
$text = wp_ai_client_prompt( 'Hello' )->generate_text();
if ( is_wp_error( $text ) ) {
// Common error codes:
// 'prompt_builder_error' - SDK or provider exception
// 'prompt_prevented' - Blocked by the wp_ai_client_prevent_prompt filter
$code = $text->get_error_code();
$message = $text->get_error_message();
$data = $text->get_error_data(); // Contains 'exception_class' key
error_log( "AI error [{$code}]: {$message}" );
}WP_AI_Client_Prompt_Builder wraps the SDK's PromptBuilder and provides a fluent, snake_case API. All configuration methods return $this for chaining. Generation methods return a result or WP_Error.
These methods add content to the current message being built.
Adds a text segment to the current message.
wp_ai_client_prompt()
->with_text( 'Translate the following to French:' )
->with_text( 'Hello, how are you?' )
->generate_text();Adds a file (image, document, etc.) to the prompt for multi-modal input.
wp_ai_client_prompt()
->with_text( 'Describe this image.' )
->with_file( '/path/to/photo.jpg', 'image/jpeg' )
->generate_text();Adds a function response to the message. Used in tool-use loops (see Section 6).
use WordPress\AiClient\Tools\DTO\FunctionResponse;
$response = new FunctionResponse( 'call-id-123', 'wpab__my_plugin__search', $data );
wp_ai_client_prompt()
->with_function_response( $response )
->generate_text();Adds one or more MessagePart objects to the current message for advanced multi-part content.
use WordPress\AiClient\Messages\DTO\MessagePart;
$part = new MessagePart( 'Some text content' );
wp_ai_client_prompt()
->with_message_parts( $part )
->generate_text();Adds previous conversation messages for multi-turn conversations.
use WordPress\AiClient\Messages\DTO\UserMessage;
use WordPress\AiClient\Messages\DTO\ModelMessage;
use WordPress\AiClient\Messages\DTO\MessagePart;
$history = [
new UserMessage( [ new MessagePart( 'What is WordPress?' ) ] ),
new ModelMessage( [ new MessagePart( 'WordPress is an open-source CMS...' ) ] ),
];
$text = wp_ai_client_prompt()
->with_history( ...$history )
->with_text( 'How do I install plugins?' )
->generate_text();Sets a specific model instance for generation.
use WordPress\AiClient\AiClient;
$registry = AiClient::defaultRegistry();
$model = $registry->getProvider( 'google' )::model( 'gemini-2.0-flash' );
wp_ai_client_prompt( 'Hello' )
->using_model( $model )
->generate_text();Provides a list of preferred models. The builder evaluates them in order and selects the first available one.
wp_ai_client_prompt( 'Hello' )
->using_model_preference( $model_a, $model_b, $model_c )
->generate_text();Restricts generation to a specific provider by ID or class name.
wp_ai_client_prompt( 'Hello' )
->using_provider( 'openai' )
->generate_text();Sets a complete ModelConfig object at once (combines temperature, max tokens, etc.).
use WordPress\AiClient\Providers\Models\DTO\ModelConfig;
$config = ModelConfig::fromArray( [
'maxTokens' => 1000,
'temperature' => 0.5,
] );
wp_ai_client_prompt( 'Hello' )
->using_model_config( $config )
->generate_text();All configuration methods return $this for chaining.
| Method | Parameter | Description |
|---|---|---|
using_system_instruction() |
string $systemInstruction |
Sets the system instruction / persona |
using_max_tokens() |
int $maxTokens |
Maximum tokens to generate |
using_temperature() |
float $temperature |
Randomness (0.0 = deterministic, higher = more creative) |
using_top_p() |
float $topP |
Nucleus sampling threshold |
using_top_k() |
int $topK |
Top-k sampling |
using_stop_sequences() |
string ...$stopSequences |
Sequences that stop generation |
using_candidate_count() |
int $candidateCount |
Number of candidates to generate |
using_presence_penalty() |
float $presencePenalty |
Penalize repeated topics |
using_frequency_penalty() |
float $frequencyPenalty |
Penalize repeated tokens |
using_top_logprobs() |
?int $topLogprobs = null |
Number of top log probabilities to return |
using_request_options() |
RequestOptions $options |
HTTP transport options (timeout, redirects) |
Example combining several options:
$text = wp_ai_client_prompt( 'Write a product description for a blue widget.' )
->using_system_instruction( 'You are a concise marketing copywriter.' )
->using_temperature( 0.7 )
->using_max_tokens( 200 )
->using_stop_sequences( '---', '###' )
->using_presence_penalty( 0.3 )
->generate_text();Registers raw function declarations that the AI model can call. This is the low-level approach.
use WordPress\AiClient\Tools\DTO\FunctionDeclaration;
$get_weather = new FunctionDeclaration(
'get_weather',
'Returns the current weather for a city',
[
'type' => 'object',
'properties' => [
'city' => [
'type' => 'string',
'description' => 'The city name',
],
],
'required' => [ 'city' ],
]
);
$result = wp_ai_client_prompt( 'What is the weather in Paris?' )
->using_function_declarations( $get_weather )
->generate_text_result();Registers WordPress abilities as tool declarations. This is the recommended approach when working with the Abilities API. Accepts WP_Ability objects or ability name strings.
// By name (looked up from the registry):
wp_ai_client_prompt( 'Search for recent posts about AI' )
->using_abilities( 'my-plugin/search-posts', 'my-plugin/create-draft' )
->generate_text_result();
// By object:
$ability = wp_get_ability( 'my-plugin/search-posts' );
wp_ai_client_prompt( 'Find posts about caching' )
->using_abilities( $ability )
->generate_text_result();Enables the model to search the web during generation (provider support varies).
use WordPress\AiClient\Tools\DTO\WebSearch;
$web_search = WebSearch::fromArray( [
'allowedDomains' => [ 'developer.wordpress.org' ],
] );
wp_ai_client_prompt( 'What are the latest WordPress coding standards?' )
->using_web_search( $web_search )
->generate_text();Configures the prompt to return JSON. Optionally pass a JSON Schema to enforce structure.
$json = wp_ai_client_prompt( 'List 3 colors with hex codes.' )
->as_json_response( [
'type' => 'object',
'properties' => [
'colors' => [
'type' => 'array',
'items' => [
'type' => 'object',
'properties' => [
'name' => [ 'type' => 'string' ],
'hex' => [ 'type' => 'string' ],
],
],
],
],
] )
->generate_text();
$data = json_decode( $json, true );Sets the output schema without switching to JSON mode. Useful when you want schema validation but not necessarily JSON output.
Sets which output types the model should produce.
use WordPress\AiClient\Messages\Enums\ModalityEnum;
wp_ai_client_prompt( 'Describe and draw a sunset.' )
->as_output_modalities( ModalityEnum::TEXT, ModalityEnum::IMAGE )
->generate_result();Sets the expected output MIME type.
Sets the output file type (inline base64 or remote URL).
use WordPress\AiClient\Files\Enums\FileTypeEnum;
wp_ai_client_prompt( 'Generate a logo' )
->as_output_file_type( FileTypeEnum::INLINE )
->generate_image();Before attempting generation, you can check whether the configured provider and model support a given capability. Support check methods return bool (or bool|WP_Error for is_supported()).
| Method | Checks support for |
|---|---|
is_supported( ?CapabilityEnum $capability = null ) |
A specific capability (returns bool|WP_Error) |
is_supported_for_text_generation() |
Text generation |
is_supported_for_image_generation() |
Image generation |
is_supported_for_text_to_speech_conversion() |
Text-to-speech |
is_supported_for_speech_generation() |
Speech generation |
is_supported_for_music_generation() |
Music generation |
is_supported_for_video_generation() |
Video generation |
is_supported_for_embedding_generation() |
Embedding generation |
$builder = wp_ai_client_prompt( 'Generate a landscape image.' );
if ( $builder->is_supported_for_image_generation() ) {
$image = $builder->generate_image();
} else {
// Fall back or show an error.
wp_die( 'Image generation is not available with the current provider.' );
}Note: If the builder is already in an error state or the prompt is prevented by a filter, support check methods return
false.
Generation methods execute the prompt and return a result or WP_Error.
| Method | Return Type | Description |
|---|---|---|
generate_text() |
string|WP_Error |
Returns the generated text directly |
generate_texts( ?int $candidateCount = null ) |
list<string>|WP_Error |
Returns multiple text candidates |
generate_text_result() |
GenerativeAiResult|WP_Error |
Returns the full result object for text |
// Single text:
$text = wp_ai_client_prompt( 'Hello' )->generate_text();
// Multiple candidates:
$texts = wp_ai_client_prompt( 'Suggest a tagline.' )
->generate_texts( 3 );
// $texts = ['Tagline A', 'Tagline B', 'Tagline C']
// Full result object:
$result = wp_ai_client_prompt( 'Hello' )->generate_text_result();
if ( ! is_wp_error( $result ) ) {
$text = $result->toText();
$token_info = $result->getTokenUsage();
}| Method | Return Type | Description |
|---|---|---|
generate_image() |
File|WP_Error |
Returns a single generated image |
generate_images( ?int $candidateCount = null ) |
list<File>|WP_Error |
Returns multiple generated images |
generate_image_result() |
GenerativeAiResult|WP_Error |
Returns the full result object for images |
$image = wp_ai_client_prompt( 'A cat wearing a WordPress t-shirt' )
->generate_image();
if ( ! is_wp_error( $image ) ) {
// $image is a File DTO with getData(), getMimeType(), etc.
}| Method | Return Type | Description |
|---|---|---|
generate_speech() |
File|WP_Error |
Generates speech from audio input |
generate_speeches( ?int $candidateCount = null ) |
list<File>|WP_Error |
Multiple speech outputs |
generate_speech_result() |
GenerativeAiResult|WP_Error |
Full result for speech |
convert_text_to_speech() |
File|WP_Error |
Converts text to speech audio |
convert_text_to_speeches( ?int $candidateCount = null ) |
list<File>|WP_Error |
Multiple text-to-speech outputs |
convert_text_to_speech_result() |
GenerativeAiResult|WP_Error |
Full result for text-to-speech |
$audio = wp_ai_client_prompt( 'Welcome to my WordPress site.' )
->convert_text_to_speech();| Method | Return Type | Description |
|---|---|---|
generate_result( ?CapabilityEnum $capability = null ) |
GenerativeAiResult|WP_Error |
Generates a result for any capability |
When you use *_result() methods, you get a GenerativeAiResult with these key methods:
$result = wp_ai_client_prompt( 'Hello' )->generate_text_result();
if ( ! is_wp_error( $result ) ) {
// Text extraction:
$text = $result->toText(); // First candidate's text
$texts = $result->toTexts(); // All candidates' texts
// File extraction:
$image = $result->toImageFile(); // First image file
$audio = $result->toAudioFile(); // First audio file
$video = $result->toVideoFile(); // First video file
// Multiple files:
$images = $result->toImageFiles();
$audios = $result->toAudioFiles();
$videos = $result->toVideoFiles();
// Message access (for tool-use loops):
$message = $result->toMessage(); // First candidate's Message object
$messages = $result->toMessages();
// Metadata:
$candidates = $result->getCandidates();
$token_usage = $result->getTokenUsage();
$provider_meta = $result->getProviderMetadata();
$model_meta = $result->getModelMetadata();
$has_multiple = $result->hasMultipleCandidates();
}The builder uses an "error state" pattern:
- If any method in the chain throws an exception, the builder catches it and enters an error state.
- Once in an error state, all subsequent configuration methods are no-ops (they return
$thissilently). - All subsequent support check methods return
false. - All subsequent generation methods return the stored
WP_Error.
This means you can build a long chain safely without try/catch:
// Even if using_provider() fails, generate_text() returns a WP_Error
// rather than throwing an exception.
$text = wp_ai_client_prompt( 'Hello' )
->using_provider( 'nonexistent-provider' )
->using_temperature( 0.5 )
->generate_text();
if ( is_wp_error( $text ) ) {
// Handle the error from using_provider()
}| Code | Cause |
|---|---|
prompt_builder_error |
An exception was thrown by the SDK (provider error, invalid config, network failure, etc.) |
prompt_prevented |
The wp_ai_client_prevent_prompt filter returned true |
The WP_Error data array includes an exception_class key:
if ( is_wp_error( $text ) ) {
$exception_class = $text->get_error_data()['exception_class'] ?? '';
// e.g. 'NetworkException', 'InvalidArgumentException', etc.
}This filter fires before any support check or generation method. Return true to block the prompt:
add_filter( 'wp_ai_client_prevent_prompt', function ( bool $prevent, WP_AI_Client_Prompt_Builder $builder ): bool {
// Example: block AI calls for subscribers.
if ( ! current_user_can( 'edit_posts' ) ) {
return true;
}
return $prevent;
}, 10, 2 );When prevented:
- Support check methods return
false. - Generation methods return a
WP_Errorwith codeprompt_prevented.
The Abilities API lets plugins register self-contained units of functionality with defined inputs, outputs, and permissions. These abilities can be executed directly, exposed via the REST API, and provided to AI models as callable tools.
An ability is a named operation with:
- A namespace and name (e.g.,
my-plugin/search-posts) - A human-readable label and description
- A category for grouping
- An input schema (JSON Schema) defining expected parameters
- An output schema (JSON Schema) defining the return structure
- An execute callback that performs the actual work
- A permission callback that gates access
- Metadata including annotations (
readonly,destructive,idempotent) andshow_in_rest
Categories must be registered before the abilities that belong to them. Use the wp_abilities_api_categories_init action:
add_action( 'wp_abilities_api_categories_init', function ( WP_Ability_Categories_Registry $registry ) {
wp_register_ability_category( 'content-management', [
'label' => __( 'Content Management', 'my-plugin' ),
'description' => __( 'Abilities for managing site content.', 'my-plugin' ),
] );
} );Category registration arguments:
| Key | Type | Required | Description |
|---|---|---|---|
label |
string |
Yes | Human-readable label |
description |
string |
Yes | Category description |
meta |
array |
No | Additional metadata |
Register abilities on the wp_abilities_api_init action:
add_action( 'wp_abilities_api_init', function ( WP_Abilities_Registry $registry ) {
wp_register_ability( 'my-plugin/search-posts', [
'label' => __( 'Search Posts', 'my-plugin' ),
'description' => __( 'Searches published posts by keyword and returns matching titles and excerpts.', 'my-plugin' ),
'category' => 'content-management',
'input_schema' => [
'type' => 'object',
'properties' => [
'query' => [
'type' => 'string',
'description' => 'The search keyword',
],
'count' => [
'type' => 'integer',
'description' => 'Max results to return',
'default' => 5,
],
],
'required' => [ 'query' ],
],
'output_schema' => [
'type' => 'array',
'items' => [
'type' => 'object',
'properties' => [
'title' => [ 'type' => 'string' ],
'excerpt' => [ 'type' => 'string' ],
'url' => [ 'type' => 'string' ],
],
],
],
'execute_callback' => function ( array $input ) {
$posts = get_posts( [
's' => $input['query'],
'posts_per_page' => $input['count'] ?? 5,
'post_status' => 'publish',
] );
return array_map( function ( $post ) {
return [
'title' => get_the_title( $post ),
'excerpt' => get_the_excerpt( $post ),
'url' => get_permalink( $post ),
];
}, $posts );
},
'permission_callback' => function () {
return current_user_can( 'read' );
},
'meta' => [
'annotations' => [
'readonly' => true,
'destructive' => false,
'idempotent' => true,
],
'show_in_rest' => true,
],
] );
} );| Key | Type | Required | Description |
|---|---|---|---|
label |
string |
Yes | Human-readable label |
description |
string |
Yes | Description (also used as the AI tool description) |
category |
string |
Yes | Category slug (must already be registered) |
execute_callback |
callable |
Yes | function( $input ): mixed|WP_Error |
permission_callback |
callable |
Yes | function( $input ): bool|WP_Error |
input_schema |
array |
No | JSON Schema for input validation |
output_schema |
array |
No | JSON Schema for output validation |
ability_class |
string |
No | Custom class extending WP_Ability |
meta |
array |
No | Metadata (see below) |
'meta' => [
'annotations' => [
'readonly' => true|false|null, // Does not modify data
'destructive' => true|false|null, // Performs destructive updates
'idempotent' => true|false|null, // Repeated calls have no additional effect
],
'show_in_rest' => true|false, // Expose via REST API (default: false)
],- Must contain 2 to 4 segments separated by
/ - Only lowercase alphanumeric characters and dashes allowed within segments
- Pattern:
/^[a-z0-9-]+(?:\/[a-z0-9-]+){1,3}$/ - Examples:
my-plugin/search,my-plugin/posts/create,my-plugin/posts/comments/list
if ( wp_has_ability( 'my-plugin/search-posts' ) ) {
// Ability is registered.
}$ability = wp_get_ability( 'my-plugin/search-posts' );
if ( $ability ) {
echo $ability->get_name(); // 'my-plugin/search-posts'
echo $ability->get_label(); // 'Search Posts'
echo $ability->get_description();
echo $ability->get_category(); // 'content-management'
$schema = $ability->get_input_schema();
$meta = $ability->get_meta();
}$abilities = wp_get_abilities();
// Returns WP_Ability[]$ability = wp_get_ability( 'my-plugin/search-posts' );
if ( $ability ) {
$result = $ability->execute( [ 'query' => 'WordPress', 'count' => 3 ] );
if ( is_wp_error( $result ) ) {
// Handle error. Common codes:
// 'ability_invalid_input' - Input failed schema validation
// 'ability_invalid_permissions' - Permission callback denied access
// 'ability_invalid_output' - Output failed schema validation
error_log( $result->get_error_message() );
} else {
// $result is the value returned by execute_callback.
}
}The execute() method performs these steps in order:
- Normalizes input (applies defaults from schema)
- Validates input against
input_schema - Checks permissions via
permission_callback - Fires
wp_before_execute_abilityaction - Calls
execute_callback - Validates output against
output_schema - Fires
wp_after_execute_abilityaction - Returns the result
| Function | Description |
|---|---|
wp_register_ability_category( string $slug, array $args ): ?WP_Ability_Category |
Registers a category |
wp_unregister_ability_category( string $slug ): ?WP_Ability_Category |
Unregisters a category |
wp_has_ability_category( string $slug ): bool |
Checks if a category exists |
wp_get_ability_category( string $slug ): ?WP_Ability_Category |
Gets a category |
wp_get_ability_categories(): array |
Gets all categories |
The bridge between the Abilities API and the AI Client is the using_abilities() method and the WP_AI_Client_Ability_Function_Resolver class.
When you call using_abilities(), the builder:
- Resolves each ability (by name or object).
- Converts the ability name to a function name using the
wpab__prefix (e.g.,my-plugin/search-postsbecomeswpab__my-plugin__search-posts). - Creates a
FunctionDeclarationusing the ability's description and input schema. - Registers these declarations with the underlying SDK builder.
This class handles the conversion between ability names and function names, and executes abilities from AI function calls.
Ability name: my-plugin/search-posts
Function name: wpab__my-plugin__search-posts
The conversion replaces / with __ and prepends the wpab__ prefix.
// Ability name -> Function name:
$fn = WP_AI_Client_Ability_Function_Resolver::ability_name_to_function_name( 'my-plugin/search-posts' );
// 'wpab__my-plugin__search-posts'
// Function name -> Ability name:
$name = WP_AI_Client_Ability_Function_Resolver::function_name_to_ability_name( 'wpab__my-plugin__search-posts' );
// 'my-plugin/search-posts'| Method | Description |
|---|---|
is_ability_call( FunctionCall $call ): bool |
Checks if a function call uses the wpab__ prefix |
execute_ability( FunctionCall $call ): FunctionResponse |
Executes a single ability from a function call |
has_ability_calls( Message $message ): bool |
Checks if a message contains any ability calls |
execute_abilities( Message $message ): Message |
Executes all ability calls in a message, returns a new message with responses |
When an AI model calls abilities as tools, you need a loop: generate, check for tool calls, execute them, feed results back, and generate again.
use WordPress\AiClient\Results\DTO\GenerativeAiResult;
// Step 1: Set up the prompt with abilities as tools.
$builder = wp_ai_client_prompt( 'Find recent posts about caching and summarize them.' )
->using_system_instruction( 'Use the available tools to search for posts, then summarize.' )
->using_abilities( 'my-plugin/search-posts' );
// Step 2: Generate the initial result.
$result = $builder->generate_text_result();
if ( is_wp_error( $result ) ) {
wp_die( $result->get_error_message() );
}
// Step 3: Check if the model wants to call abilities.
$message = $result->toMessage();
if ( WP_AI_Client_Ability_Function_Resolver::has_ability_calls( $message ) ) {
// Step 4: Execute all ability calls and get a response message.
$response_message = WP_AI_Client_Ability_Function_Resolver::execute_abilities( $message );
// Step 5: Continue the conversation with the function responses.
$final_result = wp_ai_client_prompt()
->using_system_instruction( 'Use the available tools to search for posts, then summarize.' )
->using_abilities( 'my-plugin/search-posts' )
->with_history( $message, $response_message )
->generate_text();
if ( is_wp_error( $final_result ) ) {
wp_die( $final_result->get_error_message() );
}
echo esc_html( $final_result );
} else {
// The model answered directly without tool calls.
echo esc_html( $result->toText() );
}For complex tasks, the model may need multiple rounds of tool calls:
$system = 'You are a content assistant. Use tools as needed.';
$abilities = [ 'my-plugin/search-posts', 'my-plugin/create-draft' ];
$history = [];
$max_iterations = 5;
$builder = wp_ai_client_prompt( 'Find posts about performance and draft a summary post.' )
->using_system_instruction( $system )
->using_abilities( ...$abilities );
for ( $i = 0; $i < $max_iterations; $i++ ) {
$result = $builder->generate_text_result();
if ( is_wp_error( $result ) ) {
error_log( 'AI error: ' . $result->get_error_message() );
break;
}
$message = $result->toMessage();
if ( ! WP_AI_Client_Ability_Function_Resolver::has_ability_calls( $message ) ) {
// No more tool calls, output the final text.
echo esc_html( $result->toText() );
break;
}
// Execute abilities and build next prompt with history.
$response_message = WP_AI_Client_Ability_Function_Resolver::execute_abilities( $message );
$history[] = $message;
$history[] = $response_message;
$builder = wp_ai_client_prompt()
->using_system_instruction( $system )
->using_abilities( ...$abilities )
->with_history( ...$history );
}The Abilities API registers REST endpoints under the wp-abilities/v1 namespace. All endpoints require the user to have the read capability.
GET /wp-json/wp-abilities/v1/abilities
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
context |
string |
view |
view, edit, or embed |
page |
integer |
1 |
Page number (min: 1) |
per_page |
integer |
50 |
Items per page (1-100) |
category |
string |
Filter by category slug |
Response: Array of ability objects. Only abilities with show_in_rest: true are returned.
Response Headers:
X-WP-Total— total number of abilitiesX-WP-TotalPages— total pages
// JavaScript example:
const response = await fetch( '/wp-json/wp-abilities/v1/abilities?category=content-management', {
headers: { 'X-WP-Nonce': wpApiSettings.nonce },
} );
const abilities = await response.json();GET /wp-json/wp-abilities/v1/abilities/{name}
Response Schema:
{
"name": "my-plugin/search-posts",
"label": "Search Posts",
"description": "Searches published posts by keyword.",
"category": "content-management",
"input_schema": { "type": "object", "..." },
"output_schema": { "type": "array", "..." },
"meta": {
"annotations": {
"readonly": true,
"destructive": false,
"idempotent": true
}
}
}const response = await fetch( '/wp-json/wp-abilities/v1/abilities/my-plugin/search-posts', {
headers: { 'X-WP-Nonce': wpApiSettings.nonce },
} );
const ability = await response.json();POST /wp-json/wp-abilities/v1/abilities/{name}/run
The HTTP method is validated against the ability's annotations:
| Annotations | Allowed Method |
|---|---|
readonly: true |
GET |
destructive: true + idempotent: true |
DELETE |
| Default (none of the above) | POST |
Request Body:
{
"input": {
"query": "WordPress performance",
"count": 3
}
}Response:
{
"result": [
{
"title": "Speeding Up WordPress",
"excerpt": "...",
"url": "https://example.com/speeding-up-wordpress"
}
]
}Error Responses:
| Code | Status | Cause |
|---|---|---|
rest_ability_not_found |
404 | Ability doesn't exist or show_in_rest is false |
rest_ability_invalid_method |
405 | HTTP method doesn't match annotations |
rest_ability_cannot_execute |
403 | Permission callback denied access |
// Execute a read-only ability with GET:
const response = await fetch(
'/wp-json/wp-abilities/v1/abilities/my-plugin/search-posts/run?input[query]=WordPress',
{ headers: { 'X-WP-Nonce': wpApiSettings.nonce } }
);
// Execute a write ability with POST:
const response = await fetch(
'/wp-json/wp-abilities/v1/abilities/my-plugin/create-draft/run',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': wpApiSettings.nonce,
},
body: JSON.stringify( {
input: { title: 'My Draft', content: 'Draft content...' },
} ),
}
);
const { result } = await response.json();GET /wp-json/wp-abilities/v1/categories
Query Parameters: context, page, per_page (same defaults as abilities).
GET /wp-json/wp-abilities/v1/categories/{slug}
Response Schema:
{
"slug": "content-management",
"label": "Content Management",
"description": "Abilities for managing site content.",
"meta": {}
}Response Links: Each category includes an abilities link pointing to /wp-abilities/v1/abilities?category={slug}.
const response = await fetch( '/wp-json/wp-abilities/v1/categories', {
headers: { 'X-WP-Nonce': wpApiSettings.nonce },
} );
const categories = await response.json();Fires when the ability categories registry is initialized. Register your categories here.
add_action( 'wp_abilities_api_categories_init', function ( WP_Ability_Categories_Registry $registry ) {
wp_register_ability_category( 'my-category', [
'label' => 'My Category',
'description' => 'Custom abilities.',
] );
} );Parameters:
$registry(WP_Ability_Categories_Registry) — the categories registry instance
Fires when the abilities registry is initialized. Register your abilities here.
add_action( 'wp_abilities_api_init', function ( WP_Abilities_Registry $registry ) {
wp_register_ability( 'my-plugin/my-ability', [ /* ... */ ] );
} );Parameters:
$registry(WP_Abilities_Registry) — the abilities registry instance
Filters ability arguments before validation. Useful for modifying abilities registered by other plugins.
add_filter( 'wp_register_ability_args', function ( array $args, string $name ): array {
// Force all abilities to require edit_posts permission.
if ( str_starts_with( $name, 'third-party/' ) ) {
$args['permission_callback'] = function () {
return current_user_can( 'edit_posts' );
};
}
return $args;
}, 10, 2 );Parameters:
$args(array) — the ability registration arguments$name(string) — the ability name
Fires before an ability is executed, after input validation and permission checks pass.
add_action( 'wp_before_execute_ability', function ( string $ability_name, $input ) {
error_log( "Executing ability: {$ability_name}" );
}, 10, 2 );Parameters:
$ability_name(string) — the ability name$input(mixed) — the validated input
Fires immediately after an ability finishes executing.
add_action( 'wp_after_execute_ability', function ( string $ability_name, $input, $result ) {
if ( is_wp_error( $result ) ) {
error_log( "Ability {$ability_name} failed: {$result->get_error_message()}" );
}
}, 10, 3 );Parameters:
$ability_name(string) — the ability name$input(mixed) — the input that was provided$result(mixed) — the execution result
Filters the default HTTP request timeout (in seconds) for AI Client requests. Default: 30.
add_filter( 'wp_ai_client_default_request_timeout', function ( int $timeout ): int {
return 60; // Allow up to 60 seconds for AI requests.
} );Parameters:
$timeout(int) — timeout in seconds (default: 30)
Filters whether to prevent a prompt from being executed. Fires before any support check or generation method.
add_filter( 'wp_ai_client_prevent_prompt', function ( bool $prevent, WP_AI_Client_Prompt_Builder $builder ): bool {
// Rate limiting example:
$user_id = get_current_user_id();
$count = (int) get_transient( "ai_calls_{$user_id}" );
if ( $count >= 50 ) {
return true; // Block the prompt.
}
set_transient( "ai_calls_{$user_id}", $count + 1, HOUR_IN_SECONDS );
return $prevent;
}, 10, 2 );Parameters:
$prevent(bool) — whether to prevent the prompt (default:false)$builder(WP_AI_Client_Prompt_Builder) — a read-only clone of the builder
Fires before a prompt is sent to the AI model. Dispatched via the PSR-14 event system.
use WordPress\AiClient\Events\BeforeGenerateResultEvent;
add_action( 'wp_ai_client_before_generate_result', function ( BeforeGenerateResultEvent $event ) {
$messages = $event->getMessages();
$model = $event->getModel();
$capability = $event->getCapability();
error_log( 'Sending prompt to model: ' . $model->metadata()->getId() );
} );Parameters:
$event(BeforeGenerateResultEvent) — contains messages, model, and capability
Fires after a response is received from the AI model.
use WordPress\AiClient\Events\AfterGenerateResultEvent;
add_action( 'wp_ai_client_after_generate_result', function ( AfterGenerateResultEvent $event ) {
$result = $event->getResult();
$token_usage = $result->getTokenUsage();
error_log( sprintf(
'AI response received. Tokens used: %d',
$token_usage->getTotalTokens()
) );
} );Parameters:
$event(AfterGenerateResultEvent) — contains messages, model, capability, and result
The AI Client uses four adapter classes to bridge WordPress internals to PSR interfaces expected by the SDK.
Implements ClientInterface (PSR-18) and ClientWithOptionsInterface.
- Translates PSR-7
RequestInterfaceobjects intowp_safe_remote_request()calls. - Converts WordPress HTTP responses back to PSR-7
ResponseInterfaceobjects. - Supports timeout and redirect options via
RequestOptions. - Throws
NetworkExceptiononWP_Errorfrom the WordPress HTTP API.
Implements CacheInterface (PSR-16).
- Maps
get(),set(),delete(), etc. to WordPresswp_cache_*functions. - Uses the
wp_ai_clientcache group. - Supports TTL via both
int(seconds) andDateInterval. - Uses
wp_cache_flush_group()forclear()when available.
Implements EventDispatcherInterface (PSR-14).
- Converts SDK event class names to WordPress action hooks.
- Naming convention:
BeforeGenerateResultEvent→wp_ai_client_before_generate_result. - Conversion steps: extract short class name → PascalCase to snake_case → strip
_eventsuffix → prependwp_ai_client_.
Extends AbstractClientDiscoveryStrategy from the SDK.
- Registers
WP_AI_Client_HTTP_Clientwith the HTTPlug discovery system. - Uses
Nyholm\Psr7\Factory\Psr17Factoryfor PSR-17 message factories. - Allows the SDK to automatically discover and use WordPress's HTTP transport.
The SDK lives under wp-includes/php-ai-client/src/ with these key namespaces:
| Namespace | Purpose |
|---|---|
WordPress\AiClient |
Root: AiClient class, top-level entry |
WordPress\AiClient\Builders |
PromptBuilder, MessageBuilder |
WordPress\AiClient\Messages\DTO |
Message, MessagePart, UserMessage, ModelMessage |
WordPress\AiClient\Messages\Enums |
ModalityEnum, MessageRoleEnum, MessagePartTypeEnum |
WordPress\AiClient\Tools\DTO |
FunctionDeclaration, FunctionCall, FunctionResponse, WebSearch |
WordPress\AiClient\Providers |
ProviderRegistry, AbstractProvider, provider contracts |
WordPress\AiClient\Providers\Models\DTO |
ModelConfig, ModelMetadata |
WordPress\AiClient\Providers\Models\Enums |
CapabilityEnum, OptionEnum |
WordPress\AiClient\Providers\Http\DTO |
RequestOptions, ApiKeyRequestAuthentication |
WordPress\AiClient\Results\DTO |
GenerativeAiResult, Candidate, TokenUsage |
WordPress\AiClient\Files\DTO |
File |
WordPress\AiClient\Events |
BeforeGenerateResultEvent, AfterGenerateResultEvent |
CapabilityEnum — capabilities a model can support:
TEXT_GENERATION,IMAGE_GENERATION,TEXT_TO_SPEECH_CONVERSIONSPEECH_GENERATION,MUSIC_GENERATION,VIDEO_GENERATIONEMBEDDING_GENERATION,CHAT_HISTORY
ModalityEnum — input/output modalities:
TEXT,DOCUMENT,IMAGE,AUDIO,VIDEO
FileTypeEnum — how file data is delivered:
INLINE(base64-encoded),REMOTE(URL reference)
Providers implement ProviderInterface:
interface ProviderInterface {
public static function metadata(): ProviderMetadata;
public static function model( string $modelId, ?ModelConfig $modelConfig = null ): ModelInterface;
public static function availability(): ProviderAvailabilityInterface;
public static function modelMetadataDirectory(): ModelMetadataDirectoryInterface;
}Models implement ModelInterface and capability-specific interfaces like TextGenerationModelInterface or ImageGenerationModelInterface.
The ProviderRegistry manages all registered providers, handles authentication, discovers available models, and selects the best model for a given request.
While the SDK ships with Google, OpenAI, and Anthropic providers, you could conceptually register a custom provider:
// This is a simplified conceptual example.
// A real implementation requires implementing ProviderInterface
// and the associated model interfaces.
use WordPress\AiClient\AiClient;
use WordPress\AiClient\Providers\Http\DTO\ApiKeyRequestAuthentication;
// Get the default registry.
$registry = AiClient::defaultRegistry();
// Register your custom provider class.
$registry->registerProvider( MyCustomProvider::class );
// Set authentication.
$registry->setProviderRequestAuthentication(
MyCustomProvider::metadata()->getId(),
new ApiKeyRequestAuthentication( 'my-api-key' )
);The custom provider class would need to:
- Extend
AbstractProvideror implementProviderInterface. - Implement model classes for each capability (text generation, image generation, etc.).
- Provide a model metadata directory for model discovery.
Below is a full working plugin that registers abilities and uses the AI Client with tool calling.
<?php
/**
* Plugin Name: AI Content Assistant
* Description: Uses WordPress AI Client with abilities to search and create content.
* Version: 1.0.0
* Requires at least: 7.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Register the ability category.
*/
add_action( 'wp_abilities_api_categories_init', function () {
wp_register_ability_category( 'ai-content-assistant', [
'label' => __( 'AI Content Assistant', 'ai-content-assistant' ),
'description' => __( 'Abilities for the AI Content Assistant plugin.', 'ai-content-assistant' ),
] );
} );
/**
* Register abilities.
*/
add_action( 'wp_abilities_api_init', function () {
// Ability 1: Search posts.
wp_register_ability( 'ai-content-assistant/search-posts', [
'label' => __( 'Search Posts', 'ai-content-assistant' ),
'description' => __( 'Searches published posts by keyword and returns titles, excerpts, and URLs.', 'ai-content-assistant' ),
'category' => 'ai-content-assistant',
'input_schema' => [
'type' => 'object',
'properties' => [
'query' => [
'type' => 'string',
'description' => 'Search keyword',
],
'count' => [
'type' => 'integer',
'description' => 'Maximum number of results',
'default' => 5,
],
],
'required' => [ 'query' ],
],
'output_schema' => [
'type' => 'array',
'items' => [
'type' => 'object',
'properties' => [
'id' => [ 'type' => 'integer' ],
'title' => [ 'type' => 'string' ],
'excerpt' => [ 'type' => 'string' ],
'url' => [ 'type' => 'string' ],
],
],
],
'execute_callback' => function ( array $input ) {
$posts = get_posts( [
's' => sanitize_text_field( $input['query'] ),
'posts_per_page' => min( (int) ( $input['count'] ?? 5 ), 20 ),
'post_status' => 'publish',
] );
return array_map( function ( $post ) {
return [
'id' => $post->ID,
'title' => get_the_title( $post ),
'excerpt' => wp_trim_words( get_the_excerpt( $post ), 30 ),
'url' => get_permalink( $post ),
];
}, $posts );
},
'permission_callback' => function () {
return current_user_can( 'read' );
},
'meta' => [
'annotations' => [
'readonly' => true,
'destructive' => false,
'idempotent' => true,
],
'show_in_rest' => true,
],
] );
// Ability 2: Create a draft post.
wp_register_ability( 'ai-content-assistant/create-draft', [
'label' => __( 'Create Draft Post', 'ai-content-assistant' ),
'description' => __( 'Creates a new draft post with the given title and content.', 'ai-content-assistant' ),
'category' => 'ai-content-assistant',
'input_schema' => [
'type' => 'object',
'properties' => [
'title' => [
'type' => 'string',
'description' => 'Post title',
],
'content' => [
'type' => 'string',
'description' => 'Post content in HTML',
],
],
'required' => [ 'title', 'content' ],
],
'output_schema' => [
'type' => 'object',
'properties' => [
'post_id' => [ 'type' => 'integer' ],
'edit_url' => [ 'type' => 'string' ],
],
],
'execute_callback' => function ( array $input ) {
$post_id = wp_insert_post( [
'post_title' => sanitize_text_field( $input['title'] ),
'post_content' => wp_kses_post( $input['content'] ),
'post_status' => 'draft',
'post_author' => get_current_user_id(),
], true );
if ( is_wp_error( $post_id ) ) {
return $post_id;
}
return [
'post_id' => $post_id,
'edit_url' => get_edit_post_link( $post_id, 'raw' ),
];
},
'permission_callback' => function () {
return current_user_can( 'edit_posts' );
},
'meta' => [
'annotations' => [
'readonly' => false,
'destructive' => false,
'idempotent' => false,
],
'show_in_rest' => true,
],
] );
} );
/**
* Handles an AI content assistant request with a tool-use loop.
*
* @param string $user_prompt The user's request.
* @return string|WP_Error The final AI response text or an error.
*/
function ai_content_assistant_run( string $user_prompt ) {
$system_instruction = 'You are a WordPress content assistant. '
. 'Use the search-posts tool to find existing content and the create-draft tool to create new posts. '
. 'Always search before creating to avoid duplicates.';
$abilities = [
'ai-content-assistant/search-posts',
'ai-content-assistant/create-draft',
];
$history = [];
$max_iterations = 5;
$builder = wp_ai_client_prompt( $user_prompt )
->using_system_instruction( $system_instruction )
->using_abilities( ...$abilities )
->using_temperature( 0.3 )
->using_max_tokens( 2000 );
for ( $i = 0; $i < $max_iterations; $i++ ) {
$result = $builder->generate_text_result();
if ( is_wp_error( $result ) ) {
return $result;
}
$message = $result->toMessage();
// If no tool calls, we have the final answer.
if ( ! WP_AI_Client_Ability_Function_Resolver::has_ability_calls( $message ) ) {
return $result->toText();
}
// Execute all ability calls.
$response_message = WP_AI_Client_Ability_Function_Resolver::execute_abilities( $message );
// Add to history and rebuild the prompt.
$history[] = $message;
$history[] = $response_message;
$builder = wp_ai_client_prompt()
->using_system_instruction( $system_instruction )
->using_abilities( ...$abilities )
->using_temperature( 0.3 )
->using_max_tokens( 2000 )
->with_history( ...$history );
}
return new WP_Error(
'max_iterations_reached',
__( 'The AI assistant reached the maximum number of tool-call iterations.', 'ai-content-assistant' )
);
}
// Example usage (e.g., in an admin page handler or AJAX callback):
// $response = ai_content_assistant_run( 'Find posts about caching and create a summary draft.' );Always check for WP_Error from generation methods:
$text = wp_ai_client_prompt( $prompt )->generate_text();
if ( is_wp_error( $text ) ) {
// Log the error internally.
error_log( sprintf( 'AI error [%s]: %s', $text->get_error_code(), $text->get_error_message() ) );
// Show a user-friendly message.
wp_admin_notice( __( 'AI generation failed. Please try again later.' ), [ 'type' => 'error' ] );
return;
}Before attempting image or speech generation, verify the provider supports it:
$builder = wp_ai_client_prompt( 'A sunset over mountains' );
if ( ! $builder->is_supported_for_image_generation() ) {
// Offer a text description instead.
$builder = wp_ai_client_prompt( 'Describe a sunset over mountains in vivid detail.' );
$text = $builder->generate_text();
} else {
$image = $builder->generate_image();
}- Use your plugin slug as the first segment:
my-plugin/action - Use descriptive verb-noun names:
my-plugin/search-posts,my-plugin/create-draft - For resource-oriented abilities, use the resource as a middle segment:
my-plugin/posts/search
Set annotations accurately. They affect REST API method routing:
readonly: true→ ability is available via GETdestructive: true+idempotent: true→ ability is available via DELETE- Default → ability is available via POST
Always implement permission callbacks. Never return true unconditionally in production:
'permission_callback' => function ( $input ) {
// Check specific capabilities.
if ( ! current_user_can( 'edit_posts' ) ) {
return new WP_Error(
'rest_forbidden',
__( 'You do not have permission to use this ability.' ),
[ 'status' => 403 ]
);
}
return true;
},AI requests can be slow. Increase the timeout for complex prompts:
// Global default:
add_filter( 'wp_ai_client_default_request_timeout', function () {
return 60;
} );
// Per-request:
use WordPress\AiClient\Providers\Http\DTO\RequestOptions;
wp_ai_client_prompt( $long_prompt )
->using_request_options( RequestOptions::fromArray( [
RequestOptions::KEY_TIMEOUT => 120,
] ) )
->generate_text();Provide focused system instructions that guide the model's behavior:
wp_ai_client_prompt( $user_input )
->using_system_instruction(
'You are a WordPress support assistant. '
. 'Answer questions about WordPress features, plugins, and best practices. '
. 'Keep responses concise and include code examples when helpful. '
. 'If you are unsure, say so rather than guessing.'
)
->generate_text();| Error Code / Message | Cause | Solution |
|---|---|---|
prompt_builder_error with NetworkException |
HTTP request to the AI provider failed | Check API key, network connectivity, and timeout settings |
prompt_builder_error with InvalidArgumentException |
Invalid parameter passed to a builder method | Check parameter types against the API reference |
prompt_prevented |
The wp_ai_client_prevent_prompt filter returned true |
Check if a plugin is blocking prompts; verify user capabilities |
ability_invalid_input |
Input failed JSON Schema validation | Verify the input matches the ability's input_schema |
ability_invalid_permissions |
Permission callback returned false or WP_Error |
Check the current user's capabilities |
ability_invalid_output |
Output failed JSON Schema validation | Verify the execute callback returns data matching output_schema |
rest_ability_not_found (404) |
REST request for an ability that doesn't exist or isn't exposed | Check that the ability is registered and show_in_rest is true |
rest_ability_invalid_method (405) |
Wrong HTTP method for the ability's annotations | Use GET for readonly, DELETE for destructive+idempotent, POST otherwise |
1. Hook into lifecycle events:
use WordPress\AiClient\Events\BeforeGenerateResultEvent;
use WordPress\AiClient\Events\AfterGenerateResultEvent;
add_action( 'wp_ai_client_before_generate_result', function ( BeforeGenerateResultEvent $event ) {
error_log( 'AI Request - Model: ' . $event->getModel()->metadata()->getId() );
error_log( 'AI Request - Messages: ' . wp_json_encode( $event->getMessages() ) );
} );
add_action( 'wp_ai_client_after_generate_result', function ( AfterGenerateResultEvent $event ) {
$usage = $event->getResult()->getTokenUsage();
error_log( 'AI Response - Tokens: ' . wp_json_encode( $usage->toArray() ) );
} );2. Log ability execution:
add_action( 'wp_before_execute_ability', function ( string $name, $input ) {
error_log( "Ability executing: {$name} with input: " . wp_json_encode( $input ) );
}, 10, 2 );
add_action( 'wp_after_execute_ability', function ( string $name, $input, $result ) {
if ( is_wp_error( $result ) ) {
error_log( "Ability failed: {$name} - {$result->get_error_message()}" );
} else {
error_log( "Ability succeeded: {$name}" );
}
}, 10, 3 );3. Check provider configuration:
Verify that at least one API key is configured under Settings > Connectors and that the key is valid.
4. Inspect the WP_Error data:
if ( is_wp_error( $result ) ) {
$data = $result->get_error_data();
error_log( 'Exception class: ' . ( $data['exception_class'] ?? 'unknown' ) );
}| Method | Category | Returns |
|---|---|---|
with_text( string ) |
Content | self |
with_file( $file, ?string ) |
Content | self |
with_function_response( FunctionResponse ) |
Content | self |
with_message_parts( MessagePart... ) |
Content | self |
with_history( Message... ) |
Content | self |
using_model( ModelInterface ) |
Model | self |
using_model_preference( ... ) |
Model | self |
using_provider( string ) |
Model | self |
using_model_config( ModelConfig ) |
Config | self |
using_system_instruction( string ) |
Config | self |
using_max_tokens( int ) |
Config | self |
using_temperature( float ) |
Config | self |
using_top_p( float ) |
Config | self |
using_top_k( int ) |
Config | self |
using_stop_sequences( string... ) |
Config | self |
using_candidate_count( int ) |
Config | self |
using_presence_penalty( float ) |
Config | self |
using_frequency_penalty( float ) |
Config | self |
using_top_logprobs( ?int ) |
Config | self |
using_request_options( RequestOptions ) |
Config | self |
using_function_declarations( FunctionDeclaration... ) |
Tools | self |
using_abilities( WP_Ability|string... ) |
Tools | self |
using_web_search( WebSearch ) |
Tools | self |
as_json_response( ?array ) |
Output | self |
as_output_schema( array ) |
Output | self |
as_output_modalities( ModalityEnum... ) |
Output | self |
as_output_mime_type( string ) |
Output | self |
as_output_file_type( FileTypeEnum ) |
Output | self |
is_supported( ?CapabilityEnum ) |
Check | bool|WP_Error |
is_supported_for_text_generation() |
Check | bool |
is_supported_for_image_generation() |
Check | bool |
is_supported_for_text_to_speech_conversion() |
Check | bool |
is_supported_for_speech_generation() |
Check | bool |
is_supported_for_music_generation() |
Check | bool |
is_supported_for_video_generation() |
Check | bool |
is_supported_for_embedding_generation() |
Check | bool |
generate_text() |
Generate | string|WP_Error |
generate_texts( ?int ) |
Generate | list<string>|WP_Error |
generate_text_result() |
Generate | GenerativeAiResult|WP_Error |
generate_image() |
Generate | File|WP_Error |
generate_images( ?int ) |
Generate | list<File>|WP_Error |
generate_image_result() |
Generate | GenerativeAiResult|WP_Error |
generate_speech() |
Generate | File|WP_Error |
generate_speeches( ?int ) |
Generate | list<File>|WP_Error |
generate_speech_result() |
Generate | GenerativeAiResult|WP_Error |
convert_text_to_speech() |
Generate | File|WP_Error |
convert_text_to_speeches( ?int ) |
Generate | list<File>|WP_Error |
convert_text_to_speech_result() |
Generate | GenerativeAiResult|WP_Error |
generate_result( ?CapabilityEnum ) |
Generate | GenerativeAiResult|WP_Error |
| Key | Type | Required |
|---|---|---|
label |
string |
Yes |
description |
string |
Yes |
category |
string |
Yes |
execute_callback |
callable |
Yes |
permission_callback |
callable |
Yes |
input_schema |
array |
No |
output_schema |
array |
No |
ability_class |
string |
No |
meta |
array |
No |
| Method | Route | Description |
|---|---|---|
| GET | /wp-abilities/v1/abilities |
List abilities |
| GET | /wp-abilities/v1/abilities/{name} |
Get ability details |
| GET/POST/DELETE | /wp-abilities/v1/abilities/{name}/run |
Execute ability |
| GET | /wp-abilities/v1/categories |
List categories |
| GET | /wp-abilities/v1/categories/{slug} |
Get category details |
| Hook | Type | Parameters |
|---|---|---|
wp_abilities_api_categories_init |
Action | $registry |
wp_abilities_api_init |
Action | $registry |
wp_register_ability_args |
Filter | $args, $name |
wp_before_execute_ability |
Action | $ability_name, $input |
wp_after_execute_ability |
Action | $ability_name, $input, $result |
wp_ai_client_default_request_timeout |
Filter | $timeout |
wp_ai_client_prevent_prompt |
Filter | $prevent, $builder |
wp_ai_client_before_generate_result |
Action | $event |
wp_ai_client_after_generate_result |
Action | $event |
| Code | Source | Description |
|---|---|---|
prompt_builder_error |
AI Client | SDK exception (network, invalid args, provider error) |
prompt_prevented |
AI Client | Blocked by wp_ai_client_prevent_prompt filter |
ability_invalid_input |
Abilities API | Input failed schema validation |
ability_invalid_output |
Abilities API | Output failed schema validation |
ability_invalid_permissions |
Abilities API | Permission callback denied access |
ability_invalid_execute_callback |
Abilities API | Invalid execute callback |
ability_invalid_permission_callback |
Abilities API | Invalid permission callback |
rest_ability_not_found |
REST API | Ability not found or not exposed in REST |
rest_ability_invalid_method |
REST API | Wrong HTTP method for ability annotations |
rest_ability_cannot_execute |
REST API | REST permission check failed |