-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Add WP AI Client #10881
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add WP AI Client #10881
Changes from all commits
1479dc5
2c842f1
99efae9
1c07c3e
23f1af0
42197b5
8a9d2c6
56c6873
a5bd792
242f9f9
7caa159
0edbfef
ebbdc54
278f753
85b1916
0e78c62
1e2d52c
9626b32
62b33aa
52d4963
472f69f
b4f9bd7
9c3b25e
d708bd2
97f8598
9e0ebcc
76bc7ba
9364aca
87c0400
d97a86a
582d0d0
9098a6e
5f3c5be
00ef1b8
0e8a5dc
e8a2a2f
0774f6f
c58351d
7f94810
76af7a8
b5baa00
6fa12cb
c6880e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| <?php | ||
| /** | ||
| * WordPress AI Client API. | ||
| * | ||
| * @package WordPress | ||
| * @subpackage AI | ||
| * @since 7.0.0 | ||
| */ | ||
|
|
||
| use WordPress\AiClient\AiClient; | ||
|
|
||
| /** | ||
| * Creates a new AI prompt builder using the default provider registry. | ||
| * | ||
| * This is the main entry point for generating AI content in WordPress. It returns | ||
| * a fluent builder that can be used to configure and execute AI prompts. | ||
| * | ||
| * The prompt can be provided as a simple string for basic text prompts, or as more | ||
| * complex types for advanced use cases like multi-modal content or conversation history. | ||
| * | ||
| * @since 7.0.0 | ||
| * | ||
| * @param string|MessagePart|Message|array|list<string|MessagePart|array>|list<Message>|null $prompt Optional. Initial prompt content. | ||
| * A string for simple text prompts, | ||
| * a MessagePart or Message object for | ||
| * structured content, an array for a | ||
| * message array shape, or a list of | ||
| * parts or messages for multi-turn | ||
| * conversations. Default null. | ||
| * @return WP_AI_Client_Prompt_Builder The prompt builder instance. | ||
| */ | ||
| function wp_ai_client_prompt( $prompt = null ) { | ||
| return new WP_AI_Client_Prompt_Builder( AiClient::defaultRegistry(), $prompt ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,216 @@ | ||
| <?php | ||
| /** | ||
| * WP AI Client: WP_AI_Client_Cache class | ||
| * | ||
| * @package WordPress | ||
| * @subpackage AI | ||
| * @since 7.0.0 | ||
| */ | ||
|
|
||
| use WordPress\AiClientDependencies\Psr\SimpleCache\CacheInterface; | ||
|
|
||
| /** | ||
| * WordPress-specific PSR-16 cache adapter for the AI Client. | ||
| * | ||
| * Bridges PSR-16 cache operations to WordPress object cache functions, | ||
| * enabling the AI client to leverage WordPress caching infrastructure. | ||
| * | ||
| * @since 7.0.0 | ||
| * @internal Intended only to wire up the PHP AI Client SDK to WordPress's caching system. | ||
| * @access private | ||
| */ | ||
| class WP_AI_Client_Cache implements CacheInterface { | ||
felixarntz marked this conversation as resolved.
Show resolved
Hide resolved
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thinking out loud, would it be a bit better to call that directory
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I've not loved the name. Abilities API and other simply use
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tentatively resolved in d708bd2 |
||
|
|
||
| /** | ||
| * Cache group used for all cache operations. | ||
| * | ||
| * @since 7.0.0 | ||
| * @var string | ||
| */ | ||
| private const CACHE_GROUP = 'wp_ai_client'; | ||
|
|
||
| /** | ||
| * Fetches a value from the cache. | ||
| * | ||
| * @since 7.0.0 | ||
| * | ||
| * @param string $key The unique key of this item in the cache. | ||
| * @param mixed $default_value Default value to return if the key does not exist. | ||
| * @return mixed The value of the item from the cache, or $default_value in case of cache miss. | ||
| */ | ||
| public function get( $key, $default_value = null ) { | ||
| $found = false; | ||
| $value = wp_cache_get( $key, self::CACHE_GROUP, false, $found ); | ||
|
|
||
| if ( ! $found ) { | ||
| return $default_value; | ||
| } | ||
|
|
||
| return $value; | ||
| } | ||
|
|
||
| /** | ||
| * Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time. | ||
| * | ||
| * @since 7.0.0 | ||
| * | ||
| * @param string $key The key of the item to store. | ||
| * @param mixed $value The value of the item to store, must be serializable. | ||
| * @param null|int|DateInterval $ttl Optional. The TTL value of this item. | ||
| * @return bool True on success and false on failure. | ||
| */ | ||
| public function set( $key, $value, $ttl = null ): bool { | ||
| $expire = $this->ttl_to_seconds( $ttl ); | ||
|
|
||
| return wp_cache_set( $key, $value, self::CACHE_GROUP, $expire ); | ||
| } | ||
|
|
||
| /** | ||
| * Delete an item from the cache by its unique key. | ||
| * | ||
| * @since 7.0.0 | ||
| * | ||
| * @param string $key The unique cache key of the item to delete. | ||
| * @return bool True if the item was successfully removed. False if there was an error. | ||
| */ | ||
| public function delete( $key ): bool { | ||
| return wp_cache_delete( $key, self::CACHE_GROUP ); | ||
| } | ||
|
|
||
| /** | ||
| * Wipes clean the entire cache's keys. | ||
| * | ||
| * This method only clears the cache group used by this adapter. If the underlying | ||
| * cache implementation does not support group flushing, this method returns false. | ||
| * | ||
| * @since 7.0.0 | ||
| * | ||
| * @return bool True on success and false on failure. | ||
| */ | ||
| public function clear(): bool { | ||
| if ( ! function_exists( 'wp_cache_supports' ) || ! wp_cache_supports( 'flush_group' ) ) { | ||
| return false; | ||
| } | ||
|
|
||
| return wp_cache_flush_group( self::CACHE_GROUP ); | ||
| } | ||
|
|
||
| /** | ||
| * Obtains multiple cache items by their unique keys. | ||
| * | ||
| * @since 7.0.0 | ||
| * | ||
| * @param iterable<string> $keys A list of keys that can be obtained in a single operation. | ||
| * @param mixed $default_value Default value to return for keys that do not exist. | ||
| * @return array<string, mixed> A list of key => value pairs. | ||
| */ | ||
| public function getMultiple( $keys, $default_value = null ) { | ||
| /** | ||
| * Keys array. | ||
| * | ||
| * @var array<string> $keys_array | ||
| */ | ||
| $keys_array = $this->iterable_to_array( $keys ); | ||
| $values = wp_cache_get_multiple( $keys_array, self::CACHE_GROUP ); | ||
| $result = array(); | ||
|
|
||
| foreach ( $keys_array as $key ) { | ||
| if ( false === $values[ $key ] ) { | ||
| // Could be a stored false or a cache miss — disambiguate via get(). | ||
| $result[ $key ] = $this->get( $key, $default_value ); | ||
| } else { | ||
| $result[ $key ] = $values[ $key ]; | ||
| } | ||
| } | ||
|
|
||
| return $result; | ||
| } | ||
|
|
||
| /** | ||
| * Persists a set of key => value pairs in the cache, with an optional TTL. | ||
| * | ||
| * @since 7.0.0 | ||
| * | ||
| * @param iterable<string, mixed> $values A list of key => value pairs for a multiple-set operation. | ||
| * @param null|int|DateInterval $ttl Optional. The TTL value of this item. | ||
| * @return bool True on success and false on failure. | ||
| */ | ||
| public function setMultiple( $values, $ttl = null ): bool { | ||
| $values_array = $this->iterable_to_array( $values ); | ||
| $expire = $this->ttl_to_seconds( $ttl ); | ||
| $results = wp_cache_set_multiple( $values_array, self::CACHE_GROUP, $expire ); | ||
|
|
||
| // Return true only if all operations succeeded. | ||
| return ! in_array( false, $results, true ); | ||
| } | ||
|
|
||
| /** | ||
| * Deletes multiple cache items in a single operation. | ||
| * | ||
| * @since 7.0.0 | ||
| * | ||
| * @param iterable<string> $keys A list of string-based keys to be deleted. | ||
| * @return bool True if the items were successfully removed. False if there was an error. | ||
| */ | ||
| public function deleteMultiple( $keys ): bool { | ||
| $keys_array = $this->iterable_to_array( $keys ); | ||
| $results = wp_cache_delete_multiple( $keys_array, self::CACHE_GROUP ); | ||
|
|
||
| // Return true only if all operations succeeded. | ||
| return ! in_array( false, $results, true ); | ||
| } | ||
|
|
||
| /** | ||
| * Determines whether an item is present in the cache. | ||
| * | ||
| * @since 7.0.0 | ||
| * | ||
| * @param string $key The cache item key. | ||
| * @return bool True if the item exists in the cache, false otherwise. | ||
| */ | ||
| public function has( $key ): bool { | ||
| $found = false; | ||
| wp_cache_get( $key, self::CACHE_GROUP, false, $found ); | ||
|
|
||
| return (bool) $found; | ||
| } | ||
|
|
||
| /** | ||
| * Converts a PSR-16 TTL value to seconds for WordPress cache functions. | ||
| * | ||
| * @since 7.0.0 | ||
| * | ||
| * @param null|int|DateInterval $ttl The TTL value. | ||
| * @return int The TTL in seconds, or 0 for no expiration. | ||
| */ | ||
| private function ttl_to_seconds( $ttl ): int { | ||
| if ( null === $ttl ) { | ||
| return 0; | ||
| } | ||
|
|
||
| if ( $ttl instanceof DateInterval ) { | ||
| $now = new DateTime(); | ||
| $end = ( clone $now )->add( $ttl ); | ||
|
|
||
| return $end->getTimestamp() - $now->getTimestamp(); | ||
| } | ||
|
|
||
| return max( 0, (int) $ttl ); | ||
| } | ||
|
|
||
| /** | ||
| * Converts an iterable to an array. | ||
| * | ||
| * @since 7.0.0 | ||
| * | ||
| * @param iterable<mixed> $items The iterable to convert. | ||
| * @return array<mixed> The array. | ||
| */ | ||
| private function iterable_to_array( $items ): array { | ||
| if ( is_array( $items ) ) { | ||
| return $items; | ||
| } | ||
|
|
||
| return iterator_to_array( $items ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| <?php | ||
| /** | ||
| * WP AI Client: WP_AI_Client_Discovery_Strategy class | ||
| * | ||
| * @package WordPress | ||
| * @subpackage AI | ||
| * @since 7.0.0 | ||
| */ | ||
|
|
||
| use WordPress\AiClient\Providers\Http\Abstracts\AbstractClientDiscoveryStrategy; | ||
| use WordPress\AiClientDependencies\Nyholm\Psr7\Factory\Psr17Factory; | ||
| use WordPress\AiClientDependencies\Psr\Http\Client\ClientInterface; | ||
|
|
||
| /** | ||
| * Discovery strategy for WordPress HTTP client. | ||
| * | ||
| * Registers the WordPress HTTP client adapter with the HTTPlug discovery system | ||
| * so the AI Client SDK can find and use it automatically. | ||
| * | ||
| * @since 7.0.0 | ||
| * @internal Intended only to register WordPress's HTTP client so that the PHP AI Client SDK can use it. | ||
| * @access private | ||
| */ | ||
| class WP_AI_Client_Discovery_Strategy extends AbstractClientDiscoveryStrategy { | ||
|
|
||
| /** | ||
| * Creates an instance of the WordPress HTTP client. | ||
| * | ||
| * @since 7.0.0 | ||
| * | ||
| * @param Psr17Factory $psr17_factory The PSR-17 factory for creating HTTP messages. | ||
| * @return ClientInterface The PSR-18 HTTP client. | ||
| */ | ||
| protected static function createClient( Psr17Factory $psr17_factory ): ClientInterface { | ||
| return new WP_AI_Client_HTTP_Client( $psr17_factory, $psr17_factory ); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| <?php | ||
| /** | ||
| * WP AI Client: WP_AI_Client_Event_Dispatcher class | ||
| * | ||
| * @package WordPress | ||
| * @subpackage AI | ||
| * @since 7.0.0 | ||
| */ | ||
|
|
||
| use WordPress\AiClientDependencies\Psr\EventDispatcher\EventDispatcherInterface; | ||
|
|
||
| /** | ||
| * WordPress-specific PSR-14 event dispatcher for the AI Client. | ||
| * | ||
| * Bridges PSR-14 events to WordPress action hooks, enabling plugins to hook | ||
| * into AI client lifecycle events. | ||
| * | ||
| * @since 7.0.0 | ||
| * @internal Intended only to wire up the PHP AI Client SDK to WordPress's hook system. | ||
| * @access private | ||
| */ | ||
| class WP_AI_Client_Event_Dispatcher implements EventDispatcherInterface { | ||
|
|
||
| /** | ||
| * Dispatches an event to WordPress action hooks. | ||
| * | ||
| * Converts the event class name to a WordPress action hook name and fires it. | ||
| * For example, BeforeGenerateResultEvent becomes wp_ai_client_before_generate_result. | ||
| * | ||
| * @since 7.0.0 | ||
| * | ||
| * @param object $event The event object to dispatch. | ||
| * @return object The same event object, potentially modified by listeners. | ||
| */ | ||
| public function dispatch( object $event ): object { | ||
| $event_name = $this->get_hook_name_portion_for_event( $event ); | ||
|
|
||
| /** | ||
| * Fires when an AI client event is dispatched. | ||
| * | ||
| * The dynamic portion of the hook name, `$event_name`, refers to the | ||
| * snake_case version of the event class name, without the `_event` suffix. | ||
| * | ||
| * For example, an event class named `BeforeGenerateResultEvent` will fire the | ||
| * `wp_ai_client_before_generate_result` action hook. | ||
| * | ||
| * In practice, the available action hook names are: | ||
| * | ||
| * - wp_ai_client_before_generate_result | ||
| * - wp_ai_client_after_generate_result | ||
| * | ||
| * @since 7.0.0 | ||
| * | ||
| * @param object $event The event object. | ||
| */ | ||
| do_action( "wp_ai_client_{$event_name}", $event ); | ||
|
|
||
| return $event; | ||
| } | ||
|
|
||
| /** | ||
| * Converts an event object class name to a WordPress action hook name portion. | ||
| * | ||
| * @since 7.0.0 | ||
| * | ||
| * @param object $event The event object. | ||
| * @return string The hook name portion derived from the event class name. | ||
| */ | ||
| private function get_hook_name_portion_for_event( object $event ): string { | ||
| $class_name = get_class( $event ); | ||
| $pos = strrpos( $class_name, '\\' ); | ||
| $short_name = false !== $pos ? substr( $class_name, $pos + 1 ) : $class_name; | ||
|
|
||
| // Convert PascalCase to snake_case. | ||
| $snake_case = strtolower( (string) preg_replace( '/([a-z])([A-Z])/', '$1_$2', $short_name ) ); | ||
|
|
||
| // Strip '_event' suffix if present. | ||
| if ( str_ends_with( $snake_case, '_event' ) ) { | ||
| $snake_case = (string) substr( $snake_case, 0, -6 ); | ||
| } | ||
|
|
||
| return $snake_case; | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.