Repository navigation
Connectors: Add has_ai_credentials() helper function to core #14869
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
base: trunk
Are you sure you want to change the base?
Changes from all commits
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 |
|---|---|---|
|
|
@@ -160,6 +160,76 @@ function wp_get_connectors(): array { | |
| return $registry->get_all_registered(); | ||
| } | ||
|
|
||
| /** | ||
| * Checks whether AI credentials are configured. | ||
| * | ||
| * Checks all registered connectors of type 'ai_provider' to determine if | ||
| * valid API key credentials have been configured via an environment variable, | ||
| * PHP constant, or database option. | ||
| * | ||
| * @since 7.2.0 | ||
| * | ||
| * @return bool True if AI credentials are configured, false otherwise. | ||
| */ | ||
| function has_ai_credentials(): bool { | ||
| $has_credentials = false; | ||
| $connectors = array(); | ||
|
|
||
| if ( wp_supports_ai() ) { | ||
|
Contributor
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. Are there any pre-flight filters when getting connectors that need to be accounted for here? |
||
| $connectors = wp_get_connectors(); | ||
|
|
||
| foreach ( $connectors as $connector_data ) { | ||
| if ( ! isset( $connector_data['type'] ) || 'ai_provider' !== $connector_data['type'] ) { | ||
| continue; | ||
| } | ||
|
|
||
| $auth = $connector_data['authentication'] ?? null; | ||
| if ( ! is_array( $auth ) || ( $auth['method'] ?? '' ) !== 'api_key' ) { | ||
| continue; | ||
| } | ||
|
|
||
| $setting_name = $auth['setting_name'] ?? ''; | ||
| if ( ! is_string( $setting_name ) || '' === $setting_name ) { | ||
| continue; | ||
| } | ||
|
|
||
| if ( 'none' !== _wp_connectors_get_api_key_source( | ||
| $setting_name, | ||
| $auth['env_var_name'] ?? '', | ||
| $auth['constant_name'] ?? '' | ||
| ) ) { | ||
| $has_credentials = true; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Filters whether AI credentials are available. | ||
| * | ||
| * Allows third-party plugins to declare credential availability for | ||
| * connectors that do not rely on API key settings or for custom logic. | ||
| * | ||
| * @since 7.2.0 | ||
| * | ||
| * @param bool $has_credentials Whether AI credentials are available. | ||
| * @param array<string, array<string, mixed>> $connectors The registered connectors. | ||
| */ | ||
| $has_credentials = (bool) apply_filters( 'wp_has_ai_credentials', $has_credentials, $connectors ); | ||
|
|
||
| /** | ||
| * Filters whether AI credentials are available. | ||
| * | ||
| * Legacy filter for compatibility with the WordPress AI plugin. | ||
| * | ||
| * @since 7.2.0 | ||
| * | ||
| * @param bool $has_credentials Whether AI credentials are available. | ||
| * @param array<string, array<string, mixed>> $connectors The registered connectors. | ||
| */ | ||
| return (bool) apply_filters( 'wpai_has_ai_credentials', $has_credentials, $connectors ); | ||
|
Comment on lines
+219
to
+230
Contributor
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. This can removed be handled by the plugin: // Plugin code:
add_filter( 'wp_has_ai_credentials', function ( $has_credentials, $connectors ) {
return (bool) apply_filters( 'wpai_has_ai_credentials', $has_credentials, $connectors );
}, 10, 2 );As with Gutenberg, plugin focused code should not end up in WP Core. |
||
| } | ||
|
|
||
| /** | ||
| * Resolves an AI provider logo file path to a URL. | ||
| * | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,184 @@ | ||||||||
| <?php | ||||||||
| /** | ||||||||
| * Tests for has_ai_credentials(). | ||||||||
| * | ||||||||
| * @covers ::has_ai_credentials | ||||||||
| * | ||||||||
| * @group connectors | ||||||||
| * @group ai | ||||||||
| */ | ||||||||
| class Tests_Connectors_HasAiCredentials extends WP_UnitTestCase { | ||||||||
|
|
||||||||
| /** | ||||||||
| * Cleans up options, environment variables, and filters after each test. | ||||||||
| */ | ||||||||
| public function tear_down() { | ||||||||
| delete_option( 'connectors_ai_openai_api_key' ); | ||||||||
| delete_option( 'connectors_ai_google_api_key' ); | ||||||||
| delete_option( 'connectors_ai_anthropic_api_key' ); | ||||||||
|
Comment on lines
+16
to
+18
Contributor
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. Options are reset by the database transaction in the test suite.
Suggested change
|
||||||||
| putenv( 'OPENAI_API_KEY' ); | ||||||||
| putenv( 'GOOGLE_API_KEY' ); | ||||||||
| putenv( 'GEMINI_API_KEY' ); | ||||||||
| putenv( 'ANTHROPIC_API_KEY' ); | ||||||||
|
|
||||||||
| parent::tear_down(); | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * Tests that has_ai_credentials returns false when no AI credentials are set. | ||||||||
| * | ||||||||
| * @ticket 64924 | ||||||||
| */ | ||||||||
| public function test_returns_false_when_no_credentials_set() { | ||||||||
| $this->assertFalse( has_ai_credentials() ); | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * Tests that has_ai_credentials returns true when an AI provider option is set in the database. | ||||||||
| * | ||||||||
| * @ticket 64924 | ||||||||
| */ | ||||||||
| public function test_returns_true_when_database_option_is_set() { | ||||||||
| update_option( 'connectors_ai_openai_api_key', 'sk-test-database-key' ); | ||||||||
|
|
||||||||
| $this->assertTrue( has_ai_credentials() ); | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * Tests that has_ai_credentials returns true when an AI provider key is set via environment variable. | ||||||||
| * | ||||||||
| * @ticket 64924 | ||||||||
| */ | ||||||||
| public function test_returns_true_when_env_var_is_set() { | ||||||||
| putenv( 'OPENAI_API_KEY=sk-test-env-key' ); | ||||||||
|
|
||||||||
| $this->assertTrue( has_ai_credentials() ); | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * Tests that has_ai_credentials returns true for other default providers when configured via env var. | ||||||||
| * | ||||||||
| * @ticket 64924 | ||||||||
| */ | ||||||||
| public function test_returns_true_when_google_or_anthropic_env_var_is_set() { | ||||||||
| putenv( 'ANTHROPIC_API_KEY=sk-ant-test-key' ); | ||||||||
| $this->assertTrue( has_ai_credentials() ); | ||||||||
| putenv( 'ANTHROPIC_API_KEY' ); | ||||||||
|
|
||||||||
| putenv( 'GOOGLE_API_KEY=test-gemini-key' ); | ||||||||
| $this->assertTrue( has_ai_credentials() ); | ||||||||
| putenv( 'GOOGLE_API_KEY' ); | ||||||||
| } | ||||||||
|
|
||||||||
|
Comment on lines
+63
to
+72
Contributor
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. I'd split this in to two tests. The environment variables are reset in this test suite's tear_down so don't need to be reset here. |
||||||||
| /** | ||||||||
| * Tests that empty string options or empty env vars do not count as valid credentials. | ||||||||
| * | ||||||||
| * @ticket 64924 | ||||||||
| */ | ||||||||
| public function test_returns_false_when_empty_string_set() { | ||||||||
| update_option( 'connectors_ai_openai_api_key', '' ); | ||||||||
| putenv( 'OPENAI_API_KEY=' ); | ||||||||
|
|
||||||||
| $this->assertFalse( has_ai_credentials() ); | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * Tests that non-AI connectors do not cause has_ai_credentials to return true. | ||||||||
| * | ||||||||
| * @ticket 64924 | ||||||||
| */ | ||||||||
| public function test_ignores_non_ai_provider_connectors() { | ||||||||
| $registry = WP_Connector_Registry::get_instance(); | ||||||||
| $this->assertNotNull( $registry ); | ||||||||
|
Contributor
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. Tests with multiple assertions will require a unique message parameter for each to aid debugging at a later date. |
||||||||
|
|
||||||||
| $registry->register( | ||||||||
| 'test_spam_filter', | ||||||||
| array( | ||||||||
| 'name' => 'Spam Filter', | ||||||||
| 'type' => 'spam_filter', | ||||||||
| 'description' => 'Test non-AI connector.', | ||||||||
| 'authentication' => array( | ||||||||
| 'method' => 'api_key', | ||||||||
| 'setting_name' => 'connectors_spam_filter_api_key', | ||||||||
| ), | ||||||||
| ) | ||||||||
| ); | ||||||||
|
|
||||||||
| update_option( 'connectors_spam_filter_api_key', 'sk-spam-filter-key' ); | ||||||||
|
|
||||||||
| $has_ai = has_ai_credentials(); | ||||||||
|
|
||||||||
| delete_option( 'connectors_spam_filter_api_key' ); | ||||||||
|
Contributor
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. Not needed, as above. |
||||||||
| $registry->unregister( 'test_spam_filter' ); | ||||||||
|
|
||||||||
| $this->assertFalse( $has_ai ); | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * Tests that has_ai_credentials returns false when wp_supports_ai() returns false. | ||||||||
| * | ||||||||
| * @ticket 64924 | ||||||||
| */ | ||||||||
| public function test_returns_false_when_wp_supports_ai_is_false() { | ||||||||
| update_option( 'connectors_ai_openai_api_key', 'sk-test-key' ); | ||||||||
|
|
||||||||
| add_filter( 'wp_supports_ai', '__return_false' ); | ||||||||
| $has_ai = has_ai_credentials(); | ||||||||
| remove_filter( 'wp_supports_ai', '__return_false' ); | ||||||||
|
Contributor
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.
Suggested change
The WP Test Suite resets filters between tests. 🔢 This applies to all tests with |
||||||||
|
|
||||||||
| $this->assertFalse( $has_ai ); | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * Tests that the wp_has_ai_credentials filter can override false to true. | ||||||||
| * | ||||||||
| * @ticket 64924 | ||||||||
| */ | ||||||||
| public function test_wp_has_ai_credentials_filter_can_override_to_true() { | ||||||||
| $filter = static function ( $has_credentials, $connectors ) { | ||||||||
| return true; | ||||||||
| }; | ||||||||
|
|
||||||||
| add_filter( 'wp_has_ai_credentials', $filter, 10, 2 ); | ||||||||
| $result = has_ai_credentials(); | ||||||||
| remove_filter( 'wp_has_ai_credentials', $filter ); | ||||||||
|
|
||||||||
| $this->assertTrue( $result ); | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * Tests that the wp_has_ai_credentials filter can override true to false. | ||||||||
| * | ||||||||
| * @ticket 64924 | ||||||||
| */ | ||||||||
| public function test_wp_has_ai_credentials_filter_can_override_to_false() { | ||||||||
| update_option( 'connectors_ai_openai_api_key', 'sk-test-key' ); | ||||||||
|
|
||||||||
| $filter = static function ( $has_credentials, $connectors ) { | ||||||||
| return false; | ||||||||
| }; | ||||||||
|
|
||||||||
| add_filter( 'wp_has_ai_credentials', $filter, 10, 2 ); | ||||||||
| $result = has_ai_credentials(); | ||||||||
| remove_filter( 'wp_has_ai_credentials', $filter ); | ||||||||
|
|
||||||||
| $this->assertFalse( $result ); | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * Tests that the legacy wpai_has_ai_credentials filter works for backward compatibility. | ||||||||
| * | ||||||||
| * @ticket 64924 | ||||||||
| */ | ||||||||
| public function test_legacy_wpai_has_ai_credentials_filter() { | ||||||||
| $filter = static function ( $has_credentials, $connectors ) { | ||||||||
| return true; | ||||||||
| }; | ||||||||
|
|
||||||||
| add_filter( 'wpai_has_ai_credentials', $filter, 10, 2 ); | ||||||||
| $result = has_ai_credentials(); | ||||||||
| remove_filter( 'wpai_has_ai_credentials', $filter ); | ||||||||
|
|
||||||||
| $this->assertTrue( $result ); | ||||||||
| } | ||||||||
| } | ||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefix all the things.