Skip to content

Add WP AI Client#10881

Open
JasonTheAdams wants to merge 17 commits intoWordPress:trunkfrom
JasonTheAdams:add/wp-ai-client
Open

Add WP AI Client#10881
JasonTheAdams wants to merge 17 commits intoWordPress:trunkfrom
JasonTheAdams:add/wp-ai-client

Conversation

@JasonTheAdams
Copy link
Member

@JasonTheAdams JasonTheAdams commented Feb 7, 2026

Trac ticket: https://core.trac.wordpress.org/ticket/64591
Merge Proposal: https://make.wordpress.org/core/2026/02/03/proposal-for-merging-wp-ai-client-into-wordpress-7-0

Summary

Adds a provider-agnostic AI Client, enabling developers to interact with generative AI services through a single, fluent API — without needing to know which provider is configured.

This PR includes three layers:

  • PHP AI Client SDK (php-ai-client): The upstream SDK from WordPress/php-ai-client, bundled into wp-includes/php-ai-client/ with all third-party dependencies (PSR interfaces, HTTPlug) scoped to WordPress\AiClientDependencies\* to avoid conflicts with plugins shipping their own versions.

  • Import tooling (tools/php-ai-client/): An installer script and PHP-Scoper configuration that fetches, scopes, and reorganizes the SDK for bundling. Running bash tools/php-ai-client/installer.sh reproduces the bundled output deterministically.

  • WP AI Client (ai-client-utils/, ai-client.php): The WordPress integration layer. This provides minimal PSR-7/PSR-17 implementations backed by string buffers and wp_parse_url(), an HTTP client adapter that routes requests through wp_remote_request(), a discovery strategy so the SDK automatically finds these implementations, and an event dispatcher that bridges PSR-14 events to WordPress hooks.

The public API is a single function:

$summary = wp_ai_client_prompt( 'Summarize this post' )
    ->with_text( $post->post_content )
    ->generate_text();

WP_AI_Client_Prompt_Builder wraps the SDK's fluent builder with WordPress conventions — snake_case methods, WP_Error returns instead of exceptions, and using_abilities() for connecting the Abilities API to AI function calling.

The wp_ai_client_prevent_prompt filter gives site owners and plugins centralized control over AI availability. When a prompt is prevented, generating methods return WP_Error while is_supported_* methods return false — giving plugin developers a graceful way to hide AI features entirely when AI is not available.

This gives plugin and theme developers a stable, provider-neutral way to add AI features without bundling their own HTTP clients or managing provider-specific SDKs.

Testing Instructions

This is strictly the core work and doesn't include a built-in UI for setting up provider credentials. While it is possible to add a provider and set up the credentials, the easiest way to test this is using the stacked settings PR: #10904. That PR has testing instructions and links to provider plugins you can install.

Use of AI Tools

This is a compilation of work from the PHP AI Client and WP AI Client repositories, with some changes made in porting to core. Claude Code was used in both the original development of those packages as well as the porting over and creation of the tooling. All code was generated by Claude Code and reviewed by myself and @felixarntz.


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

@github-actions
Copy link

github-actions bot commented Feb 7, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props jason_the_adams, desrosj, flixos90, dkotter, jorgefilipecosta, johnbillion, swissspidy, peterwilsoncc.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions
Copy link

github-actions bot commented Feb 7, 2026

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • The Plugin and Theme Directories cannot be accessed within Playground.
  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@johnbillion
Copy link
Member

Might any of these 3p libraries potentially be used outside of the AI client in the future? ie. they might benefit from being in the higher level WordPress namespace rather than WordPress\AiClient.

Extenders might refer to that 3p code as soon as it's in, so we can't change the namespace at a later date.

@swissspidy
Copy link
Member

FYI there are 50+ Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 deprecation notices introduced by this PR.

@JasonTheAdams
Copy link
Member Author

I was fixing that as you posted the comment, @swissspidy. 😄

Copy link

@dkotter dkotter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did a fairly quick review here (mostly ignoring everything in the php-ai-client directory as I know that's already been reviewed and tested upstream) and have left a few minor comments.

I was mostly curious to see what, if any, changes would need to be made for those that have already been building on top of the WP AI Client (like we're doing in the AI Experiments plugin). Or if there's anything here that's missing that we'd need to ensure those integrations work (beyond the obvious exclusions in this PR like the provider credentials settings screen).

Not surprisingly, seems there's only super minimal changes we'd make, namely:

  1. Removing the inclusion of the WP AI Client
  2. Remove the loading and initialization of the client
  3. Changing from AI_Client::prompt_with_wp_error to wp_ai_client_prompt

But looks like everything else we're doing will continue to work, which is great.

We've built many features on top of the (WP) AI Client now in AI Experiments and it's all been working great, so would love to see this merged into core to make it even easier to build on.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming this is probably due to the scoping tool we run here but noticing all the extra line breaks are removed from the php-ai-client files. This may be expected/desired, just makes it a bit harder to read through the code

$function_id,
$function_name,
array(
'error' => 'Not an ability function call',
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know there's some disagreement around translating error or debug messages but wondering if all the strings in this file should be passed through translation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I think because this is meant for the AI model we should keep this in English, as that seems to be the best supported language. Open to others' thoughts, though!

$args = $this->prepare_wp_args( $request );
$url = (string) $request->getUri();

$response = wp_remote_request( $url, $args );
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know there are a number of filters in the WP_Http class that wp_remote_request will make use of, curious if there's any desire around adding more specific filters here, for those that may want to filter arguments (headers, body, etc)?

Again, you can do that right now with the more generic filters upstream and that may be good enough but could make an argument for having wp_ai_client_ filter options here as well to make it easier to only filter when making an AI request

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally tend to hold on adding filters until there's a clear use case, but I'm open to adding more filters if others feel there should be more to start.

@JasonTheAdams
Copy link
Member Author

Might any of these 3p libraries potentially be used outside of the AI client in the future? ie. they might benefit from being in the higher level WordPress namespace rather than WordPress\AiClient.

Extenders might refer to that 3p code as soon as it's in, so we can't change the namespace at a later date.

I'm certainly open to this! What libraries are you thinking of, specifically?

@JasonTheAdams
Copy link
Member Author

Thanks for looking over this, @dkotter! I've resolved most of what you raised. I'm temporarily shifting to get the settings screen PR in place so we can start testing!

@JasonTheAdams JasonTheAdams mentioned this pull request Feb 11, 2026
5 tasks
This adjusts the test coverage workflow to run for any change to a PHP file in a pull request.

This is not meant to be included in the final merge, but aims to help gauge the test coverage changes this merge would result in.
@desrosj
Copy link
Member

desrosj commented Feb 12, 2026

So I updated the test coverage workflow to run every time this PR is updated (please revert this before a final commit to SVN. In the status checks, you'll find two codecov lines.

Surprisingly, it seems that 96%+ of this PR is actually covered by tests (which excludes the bundled library parts of this PR based on the PHPUnit configuration file changes), and this PR actually increases overall test coverages by roughly 2-tenths of a percent. For easier reference, you can view the coverage report for the PR here.

Chatting it through with @aaronjorbin, @jeffpaul, and @felixarntz just now, this seems to make sense. The wp-ai-client is essentially a pass through, so it's not difficult to have "coverage". But it's unlikely that a high percentage of the underlying PHP SDK is being tested through the wp-ai-client tests here.

I do feel a bit better about the test coverage part of this. I think this will need to be a blended approach. There should be a high level of coverage for the non-bundled library code here combined with the test coverage within the PHP SDK library. @aaronjorbin has created an issue to follow up with adding test coverage reporting to the php-ai-client repository to track and confirm this.

@peterwilsoncc
Copy link
Contributor

@JasonTheAdams Are you able to add some testing notes to the PR description. I'm having trouble finguring out what needs to be done to add providers to the settings screen with the AI Experiments plugin installed. The menu page is available but without any form fields.

Screenshot 2026-02-13 at 8 03 56 am

@JasonTheAdams
Copy link
Member Author

@peterwilsoncc Done!

* @param string $mode Mode with which to open the underlying filename/stream.
* @return StreamInterface
*/
public function createStreamFromFile( string $filename, string $mode = 'r' ): StreamInterface {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The interface says:
The file MUST be opened using the given mode, which may be any mode supported by the fopen function.

But on this implementation we ignore the mode and probably just open in read mode all the time (behavior of file_get_contents).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing in the codebase is using createStreamFromFile, could we remove this implementation?

break;
}

if ( $this->offset < 0 ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we check the upper bound too e.g: against $length.

* @param string|UriInterface $uri Request URI.
*/
public function __construct( string $method, $uri ) {
$this->method = $method;
Copy link
Member

@jorgefilipecosta jorgefilipecosta Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we validate if method is non empty string and matches one of the valid HTTP request methods?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is validated elsewhere? But correct me if I'm wrong @JasonTheAdams

/**
* Test createStreamFromFile reads file content.
*
* @ticket TBD
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could already use ticket 64591.

*
* @since 7.0.0
*
* @param mixed $prompt Optional. Initial prompt content. Default null.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A promp is either string or null correct? Could we replace, mixed with string|null?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, there are several formats you can specify a prompt - can be a string, a Message object, multiple Message objects, a list of MessagePart - it has to be flexible to support the simple case (a string), but also the complex case (e.g. next response in a multimodal multi-turn conversation with tool calls).

But great catch, @JasonTheAdams can you add a more comprehensive description here on the possible types?

declare (strict_types=1);
namespace WordPress\AiClient\Providers\Models\DTO;

use InvalidArgumentException;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess here we should use WordPress\AiClient\Common\Exception\InvalidArgumentException?

}

return $result;
} catch ( Exception $e ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are catching only exception so TypeError/Error can still fatal.
Can we catch Throwable here instead of only Exception so invalid argument types also become WP_Error and don’t fatal?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that's what we want to achieve here. Fatal errors are still fatal errors, also elsewhere in Core.

This is only supposed to catch any "regular", i.e. manually thrown exceptions in the php-ai-client and any underlying third-party libraries that rely on throwing exceptions.

Catching Throwable here would obfuscate fatal errors, and I don't think that's what we want. Especially because Core already has its fatal error detection and recovery mechanism.


$host = $this->uri->getHost();
if ( '' !== $host && ! $this->hasHeader( 'Host' ) ) {
$this->set_header_internal( 'Host', $host );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UriInterface does not includes port on host, so we are dropping the port from the host header, when is_standard_port is false.

Should we include the port in case the a non standard post is used?

* @return int Number of bytes written.
*/
public function write( string $string ): int {
$this->content .= $string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Write is always appending, regardless of the tell() seek() positions is this expected? In that case should we just remove tell and seek given that it is ignored?

public function createStreamFromFile( string $filename, string $mode = 'r' ): StreamInterface {
$content = file_get_contents( $filename ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents

if ( false === $content ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is failure e.g: file does not exist, etc, we just return as if the file existed and was empty? Should we throw an error instead?

$headers = array();

foreach ( $request->getHeaders() as $name => $values ) {
if ( strpos( $name, 'X-Stream' ) === 0 ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HTTP headers are case insensitive, we are not matching x-stream, should we use stripos instead?

*
* @since 7.0.0
*/
class WP_AI_Client_Cache implements CacheInterface {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we expect this class to be useful and be consumed by WordPress plugins? If not and it is something internal for WP/ PHP AI SDK it may make sense to add @internal and @access private anotations. The same should be decided for some other classes we are adding.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point!

I would suggest that we apply these annotations to all classes in `ai-client-utils. They're all entirely internal and only exist to wire up the PHP AI Client with WordPress-specific implementations.

* @since 0.2.0
*
* @param string|ModelInterface|array{0:string,1:string} ...$preferredModels The preferred models as model IDs,
* model instances, or [model ID, provider ID] tuples.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order of the tupple is provider ID, model ID I think in this comment we should match what happens in the code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch, though this needs to be fixed upstream. I opened WordPress/php-ai-client#204

*
* @throws InvalidArgumentException If the array is not a list.
*/
public function setFunctionDeclarations(array $function_declarations): void
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everywhere else on this file we name parameters in camelCase should we use camelCase here to instead of snake_case?

*
* Not all OpenAI compatible APIs include a 'type' key, so we only check its value if it is set.
*/
if (isset($toolCallData['type']) && 'function' !== $toolCallData['type'] || !isset($toolCallData['function']) || !is_array($toolCallData['function'])) {
Copy link
Member

@jorgefilipecosta jorgefilipecosta Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we have complex bollean I guess we could inlcude "("s to make precedence between and/or clear and make code easier to read.

*/
protected function validateMediaOrientationAspectRatioCompatibility(MediaOrientationEnum $orientation, string $aspectRatio): void
{
if ($orientation->isSquare() && $aspectRatio !== '1:1') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: Should we check $aspectRatioParts[0] == $aspectRatioParts[1] instead of "1:1" e.g: "16:16" would also be a square.

Copy link
Member

@felixarntz felixarntz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JasonTheAdams This looks overall quite solid to me. Left a few points of feedback how we can further improve.

- 'docker-compose.yml'
- 'phpunit.xml.dist'
- 'tests/phpunit/multisite.xml'
- '**.php'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: Revert before commit (just leaving this here as a reminder).

Comment on lines +116 to +125
foreach ( $message->getParts() as $part ) {
if ( $part->getType()->isFunctionCall() ) {
$function_call = $part->getFunctionCall();
if ( $function_call instanceof FunctionCall && self::is_ability_call( $function_call ) ) {
return true;
}
}
}

return false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit-pick: You can simplify this by using array_find.

This is a PHP 8.4+ function, but it's available via polyfill in WordPress Core regardless.

* @param string $ability_name The ability name to convert.
* @return string The function name.
*/
public static function ability_name_to_function_name( string $ability_name ): string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this public? Where is it used? This is a strange API design, given that the reverse function_name_to_ability_name is not public.

*
* @since 7.0.0
*/
class WP_AI_Client_Cache implements CacheInterface {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point!

I would suggest that we apply these annotations to all classes in `ai-client-utils. They're all entirely internal and only exist to wire up the PHP AI Client with WordPress-specific implementations.

*
* @since 7.0.0
*/
class WP_AI_Client_Ability_Function_Resolver {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to https://github.com/WordPress/wordpress-develop/pull/10881/changes#r2807854150, isn't this class actually one that external devs are encouraged to use (to resolve ability function calls)?

If so, I think it actually shouldn't be here, but directly in wp-includes, next to class-wp-ai-client-prompt-builder.php. It makes sense IMO to leave ai-client-utils to internal wiring for the PHP AI Client.

*
* @since 7.0.0
*
* @param mixed $prompt Optional. Initial prompt content. Default null.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, there are several formats you can specify a prompt - can be a string, a Message object, multiple Message objects, a list of MessagePart - it has to be flexible to support the simple case (a string), but also the complex case (e.g. next response in a multimodal multi-turn conversation with tool calls).

But great catch, @JasonTheAdams can you add a more comprehensive description here on the possible types?

Comment on lines +43 to +90
* @method self with_text(string $text) Adds text to the current message.
* @method self with_file($file, ?string $mimeType = null) Adds a file to the current message.
* @method self with_function_response(FunctionResponse $functionResponse) Adds a function response to the current message.
* @method self with_message_parts(MessagePart ...$parts) Adds message parts to the current message.
* @method self with_history(Message ...$messages) Adds conversation history messages.
* @method self using_model(ModelInterface $model) Sets the model to use for generation.
* @method self using_model_preference(...$preferredModels) Sets preferred models to evaluate in order.
* @method self using_model_config(ModelConfig $config) Sets the model configuration.
* @method self using_provider(string $providerIdOrClassName) Sets the provider to use for generation.
* @method self using_system_instruction(string $systemInstruction) Sets the system instruction.
* @method self using_max_tokens(int $maxTokens) Sets the maximum number of tokens to generate.
* @method self using_temperature(float $temperature) Sets the temperature for generation.
* @method self using_top_p(float $topP) Sets the top-p value for generation.
* @method self using_top_k(int $topK) Sets the top-k value for generation.
* @method self using_stop_sequences(string ...$stopSequences) Sets stop sequences for generation.
* @method self using_candidate_count(int $candidateCount) Sets the number of candidates to generate.
* @method self using_function_declarations(FunctionDeclaration ...$functionDeclarations) Sets the function declarations available to the model.
* @method self using_presence_penalty(float $presencePenalty) Sets the presence penalty for generation.
* @method self using_frequency_penalty(float $frequencyPenalty) Sets the frequency penalty for generation.
* @method self using_web_search(WebSearch $webSearch) Sets the web search configuration.
* @method self using_request_options(RequestOptions $options) Sets the request options for HTTP transport.
* @method self using_top_logprobs(?int $topLogprobs = null) Sets the top log probabilities configuration.
* @method self as_output_mime_type(string $mimeType) Sets the output MIME type.
* @method self as_output_schema(array<string, mixed> $schema) Sets the output schema.
* @method self as_output_modalities(ModalityEnum ...$modalities) Sets the output modalities.
* @method self as_output_file_type(FileTypeEnum $fileType) Sets the output file type.
* @method self as_json_response(?array<string, mixed> $schema = null) Configures the prompt for JSON response output.
* @method bool|WP_Error is_supported(?CapabilityEnum $capability = null) Checks if the prompt is supported for the given capability.
* @method bool is_supported_for_text_generation() Checks if the prompt is supported for text generation.
* @method bool is_supported_for_image_generation() Checks if the prompt is supported for image generation.
* @method bool is_supported_for_text_to_speech_conversion() Checks if the prompt is supported for text to speech conversion.
* @method bool is_supported_for_video_generation() Checks if the prompt is supported for video generation.
* @method bool is_supported_for_speech_generation() Checks if the prompt is supported for speech generation.
* @method bool is_supported_for_music_generation() Checks if the prompt is supported for music generation.
* @method bool is_supported_for_embedding_generation() Checks if the prompt is supported for embedding generation.
* @method GenerativeAiResult|WP_Error generate_result(?CapabilityEnum $capability = null) Generates a result from the prompt.
* @method GenerativeAiResult|WP_Error generate_text_result() Generates a text result from the prompt.
* @method GenerativeAiResult|WP_Error generate_image_result() Generates an image result from the prompt.
* @method GenerativeAiResult|WP_Error generate_speech_result() Generates a speech result from the prompt.
* @method GenerativeAiResult|WP_Error convert_text_to_speech_result() Converts text to speech and returns the result.
* @method string|WP_Error generate_text() Generates text from the prompt.
* @method list<string>|WP_Error generate_texts(?int $candidateCount = null) Generates multiple text candidates from the prompt.
* @method File|WP_Error generate_image() Generates an image from the prompt.
* @method list<File>|WP_Error generate_images(?int $candidateCount = null) Generates multiple images from the prompt.
* @method File|WP_Error convert_text_to_speech() Converts text to speech.
* @method list<File>|WP_Error convert_text_to_speeches(?int $candidateCount = null) Converts text to multiple speech outputs.
* @method File|WP_Error generate_speech() Generates speech from the prompt.
* @method list<File>|WP_Error generate_speeches(?int $candidateCount = null) Generates multiple speech outputs from the prompt.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be awesome to add a CI check for this, to ensure it always remains 100% aligned with the camelCase methods that the php-ai-client PromptBuilder offers.

Maybe something to ask Claude to come up with? :)

}

return $result;
} catch ( Exception $e ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that's what we want to achieve here. Fatal errors are still fatal errors, also elsewhere in Core.

This is only supposed to catch any "regular", i.e. manually thrown exceptions in the php-ai-client and any underlying third-party libraries that rely on throwing exceptions.

Catching Throwable here would obfuscate fatal errors, and I don't think that's what we want. Especially because Core already has its fatal error detection and recovery mechanism.


require_once dirname( __DIR__, 2 ) . '/includes/wp-ai-client-mock-model-creation-trait.php';

class Tests_AI_Client_PromptBuilder extends WP_UnitTestCase {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The coverage here is already quite good I believe; but I think it would be worth checking what else we can cover, e.g. certain edge cases or other combinations of using* methods that maybe are related, etc. Just to make sure Core itself has sufficient test coverage to reliably catch any future problems that could be caused by updates to the third-party code (including php-ai-client itself).

@@ -0,0 +1,1343 @@
<?php
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking that it would be useful to have a check that these files (everything in php-ai-client/src and php-ai-client/third-party is not edited in a PR. It may not be obvious to a contributor that this is an external dependency, so that's why this would be useful.

I'm pretty sure we already have something like this when editing Gutenberg block code in Core, but I don't recall where that is controlled. Do you know @desrosj?

In any case, this is not a blocker for completing this PR, but something to think about. If we can find a simple solution, let's go for it. One thing to consider though is that there has to be a way to explicitly opt out of it for a specific PR, if the PR is for the actual update of these dpeendencies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants