-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Add: Connectors screen and API #75833
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
Changes from 60 commits
2112fa8
6604d0d
eea90e2
2045880
2621e63
07694cb
fb4ba8d
8214ea7
1ead5a0
2e536aa
e4dc487
2392c0f
9e8398b
6a543af
1812aa7
56e05da
160e249
9f6d184
df48889
22c72e2
84bd4ee
b630db4
cc18956
9ada092
069a71c
d42370a
6a5806b
aaa082d
a3a491e
b3426bc
0b32e53
92ad29f
58d2b46
e6ec507
40ca346
eacfe66
727d144
d4c0d47
733dceb
ab5de8d
045d624
d4285b4
0349c34
9ba27a5
b94264c
d219ba3
6102089
3bec2be
9985d04
023afbd
dd111ba
82890c7
19757e7
8cdc3c5
579e63a
b72cea2
43ce449
93ae861
7c867b8
1bf97fb
5d9b28f
897aaab
007c9d4
67c84de
001c038
ad8ca56
f66b513
1e17207
ed83d19
c9b2ead
f369988
c4db60a
10d0205
e07d6a5
98388d5
a16e665
10f0dd4
7fd1ca7
5e47da7
550567d
a6670b9
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,316 @@ | ||||||||||||||||||||||
| <?php | ||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Default connectors backend logic. | ||||||||||||||||||||||
| * | ||||||||||||||||||||||
| * @package gutenberg | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Masks an API key, showing only the last 4 characters. | ||||||||||||||||||||||
| * | ||||||||||||||||||||||
| * @access private | ||||||||||||||||||||||
| * | ||||||||||||||||||||||
| * @param string $key The API key to mask. | ||||||||||||||||||||||
| * @return string The masked key, e.g. "••••••••••••fj39". | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| function _gutenberg_mask_api_key( $key ) { | ||||||||||||||||||||||
|
||||||||||||||||||||||
| function _gutenberg_mask_api_key( $key ) { | |
| function _gutenberg_mask_api_key( string $key ): string { |
Outdated
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.
| function _gutenberg_is_api_key_valid( $key, $provider_id ) { | |
| function _gutenberg_is_api_key_valid( string $key, string $provider_id ): ?bool { |
Outdated
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.
| function _gutenberg_set_provider_api_key( $key, $provider_id ) { | |
| function _gutenberg_set_provider_api_key( string $key, string $provider_id ): void { |
Outdated
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.
| * @param string $option_name The option name for the API key. | |
| * @param string $mask_callback The mask filter function name. | |
| * @return string The real API key value. | |
| */ | |
| function _gutenberg_get_real_api_key( $option_name, $mask_callback ) { | |
| * @param string $option_name The option name for the API key. | |
| * @param callable $mask_callback The mask filter function name. | |
| * @return string The real API key value. | |
| */ | |
| function _gutenberg_get_real_api_key( string $option_name, callable $mask_callback ): string { |
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.
Masking is nice, but should we also encrypt out API keys before we store them in them the database? Seems like a bigger problem than before when giving AI access to your database is one of the bigger practical use cases for agentic site work.
Also confirming - add_filter( 'option_{$option_name}', static fn () => MY_PRIVATE_ENV_CONST, 9 ); is the only way to programmatically bypass db storage for enterprise and other security or deployment minded folks who care about these things, and that too might break in 7.1?
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.
Seems straight up deceptive to state the key is 'securely stored' if there is no encryption before it saved to the database.
Outdated
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.
| function _gutenberg_mask_gemini_api_key( $value ) { | |
| function _gutenberg_mask_gemini_api_key( string $value ): string { |
jorgefilipecosta marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Outdated
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.
| function _gutenberg_validate_gemini_api_key_on_save( $value, $old_value ) { | |
| if ( empty( $value ) ) { | |
| function _gutenberg_validate_gemini_api_key_on_save( string $value, string $old_value ) { | |
| if ( '' === $value ) { |
jorgefilipecosta marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
jorgefilipecosta marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Outdated
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.
| function _gutenberg_mask_openai_api_key( $value ) { | |
| if ( empty( $value ) ) { | |
| function _gutenberg_mask_openai_api_key( string $value ): string { | |
| if ( '' === $value ) { |
jorgefilipecosta marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Outdated
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.
| function _gutenberg_validate_openai_api_key_on_save( $value, $old_value ) { | |
| if ( empty( $value ) ) { | |
| function _gutenberg_validate_openai_api_key_on_save( string $value, string $old_value ) { | |
| if ( '' === $value ) { |
jorgefilipecosta marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Outdated
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.
| function _gutenberg_mask_anthropic_api_key( $value ) { | |
| if ( empty( $value ) ) { | |
| function _gutenberg_mask_anthropic_api_key( string $value ) { | |
| if ( '' === $value ) { |
jorgefilipecosta marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Outdated
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.
| function _gutenberg_validate_anthropic_api_key_on_save( $value, $old_value ) { | |
| if ( empty( $value ) ) { | |
| function _gutenberg_validate_anthropic_api_key_on_save( string $value, string $old_value ) { | |
| if ( '' === $value ) { |
jorgefilipecosta marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Outdated
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.
| function _gutenberg_validate_connector_keys_in_rest( $response, $server, $request ) { | |
| function _gutenberg_validate_connector_keys_in_rest( WP_REST_Response $response, WP_REST_Server $server, WP_REST_Request $request ): WP_REST_Response { |
Outdated
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.
cf. _gutenberg_register_default_connector_settings() and _gutenberg_pass_default_connector_keys_to_ai_client() where in _gutenberg_register_default_connector_settings() an associative array is used for the array values but not in the latter.
Also, there are three sets of these arrays. Shouldn't there be just one array that has all of the information shared by these three functions? For example, there can be one single shared private function that does:
/**
* Gets the provider conncetors.
*
* @access private
*
* @return array<string, array{ provider: string, mask: callable, validate: callable }> Connectors.
*/
function _gutenberg_get_connectors(): array {
return array(
'connectors_gemini_api_key' => array(
'provider' => 'google',
'mask' => '_gutenberg_mask_gemini_api_key',
'validate' => '_gutenberg_validate_gemini_api_key_on_save',
),
'connectors_openai_api_key' => array(
'provider' => 'openai',
'mask' => '_gutenberg_mask_openai_api_key',
'validate' => '_gutenberg_validate_openai_api_key_on_save',
),
'connectors_anthropic_api_key' => array(
'provider' => 'anthropic',
'mask' => '_gutenberg_mask_anthropic_api_key',
'validate' => '_gutenberg_validate_anthropic_api_key_on_save',
),
);
}
jorgefilipecosta marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
jorgefilipecosta marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Outdated
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.
| function _gutenberg_register_default_connector_settings() { | |
| function _gutenberg_register_default_connector_settings(): void { |
Outdated
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.
See note above about _gutenberg_get_connectors().
Outdated
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.
Instead of putting the validate function in a pre_update_option_* filter, why not use it as the sanitize_callback?
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.
Very good idea, it is done 👍
Outdated
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.
| function _gutenberg_pass_default_connector_keys_to_ai_client() { | |
| function _gutenberg_pass_default_connector_keys_to_ai_client(): void { |
Outdated
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.
See note above about _gutenberg_get_connectors().
jorgefilipecosta marked this conversation as resolved.
Show resolved
Hide resolved
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| <?php | ||
| /** | ||
| * Bootstraps the Connectors page in wp-admin. | ||
| * | ||
| * @package gutenberg | ||
| */ | ||
|
|
||
| add_action( 'admin_menu', '_gutenberg_connectors_add_settings_menu_item' ); | ||
|
|
||
| /** | ||
| * Registers the Connectors menu item under Settings. | ||
| * | ||
| * @access private | ||
| */ | ||
| function _gutenberg_connectors_add_settings_menu_item() { | ||
jorgefilipecosta marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| add_submenu_page( | ||
| 'options-general.php', | ||
| __( 'Connectors', 'gutenberg' ), | ||
| __( 'Connectors', 'gutenberg' ), | ||
| 'manage_options', | ||
| 'connectors-wp-admin', | ||
| 'gutenberg_connectors_wp_admin_render_page', | ||
| 1 | ||
| ); | ||
| } | ||
|
|
||
| require __DIR__ . '/default-connectors.php'; | ||
Uh oh!
There was an error while loading. Please reload this page.