Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions ai-command.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace WP_CLI\AI;

use WP_CLI;
use WP_CLI\Utils;

if ( ! class_exists( '\WP_CLI' ) ) {
return;
Expand All @@ -14,5 +15,11 @@
require_once $wpcli_ai_autoloader;
}

WP_CLI::add_command( 'ai', AI_Command::class );
WP_CLI::add_command( 'ai credentials', Credentials_Command::class );
$wpcli_ai_before_invoke = static function () {
if ( Utils\wp_version_compare( '7.0-beta1', '<' ) ) {
WP_CLI::error( 'Requires WordPress 7.0 or greater.' );
}
};

WP_CLI::add_command( 'ai', AI_Command::class, [ 'before_invoke' => $wpcli_ai_before_invoke ] );
WP_CLI::add_command( 'ai credentials', Credentials_Command::class, [ 'before_invoke' => $wpcli_ai_before_invoke ] );
3 changes: 1 addition & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
"license": "MIT",
"authors": [],
"require": {
"wordpress/wp-ai-client": "^0.3.0",
"wp-cli/wp-cli": "^2.12"
"wp-cli/wp-cli": "^2.13"
},
"require-dev": {
"wp-cli/wp-cli-tests": "^5"
Expand Down
10 changes: 0 additions & 10 deletions features/ai.feature

This file was deleted.

19 changes: 19 additions & 0 deletions features/credentials.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,26 @@ Feature: Manage AI provider credentials
Background:
Given a WP install

@less-than-wp-7.0
Scenario: Command not available on WP < 7.0
Given a WP install

When I try `wp ai credentials list`
Then STDERR should contain:
"""
Requires WordPress 7.0 or greater.
"""
And the return code should be 1

@require-wp-7.0
Scenario: List credentials when none exist
When I run `wp ai credentials list`
Then STDOUT should contain:
"""
No credentials found.
"""

@require-wp-7.0
Scenario: Set and list credentials
When I run `wp ai credentials set openai --api-key=sk-test123456789`
Then STDOUT should contain:
Expand All @@ -23,6 +36,7 @@ Feature: Manage AI provider credentials
[{"provider":"openai","api_key":"sk-*********6789"}]
"""

@require-wp-7.0
Scenario: Get specific provider credentials
When I run `wp ai credentials set anthropic --api-key=sk-ant-api-key-123`
Then STDOUT should contain:
Expand All @@ -40,6 +54,7 @@ Feature: Manage AI provider credentials
"api_key":"sk-**********-123"
"""

@require-wp-7.0
Scenario: Delete provider credentials
When I run `wp ai credentials set google --api-key=test-google-key`
Then STDOUT should contain:
Expand All @@ -60,6 +75,7 @@ Feature: Manage AI provider credentials
"""
And the return code should be 1

@require-wp-7.0
Scenario: Error when getting non-existent credentials
When I try `wp ai credentials get nonexistent`
Then STDERR should contain:
Expand All @@ -68,6 +84,7 @@ Feature: Manage AI provider credentials
"""
And the return code should be 1

@require-wp-7.0
Scenario: Error when setting credentials without api-key
When I try `wp ai credentials set openai`
Then STDERR should contain:
Expand All @@ -76,6 +93,7 @@ Feature: Manage AI provider credentials
"""
And the return code should be 1

@require-wp-7.0
Scenario: List multiple credentials in table format
When I run `wp ai credentials set openai --api-key=sk-openai123`
And I run `wp ai credentials set anthropic --api-key=sk-ant-api-456`
Expand All @@ -85,6 +103,7 @@ Feature: Manage AI provider credentials
| openai | sk-*****i123 |
| anthropic | sk-*******-456 |

@require-wp-7.0
Scenario: Update existing credentials
When I run `wp ai credentials set openai --api-key=old-key-123`
Then STDOUT should contain:
Expand Down
19 changes: 19 additions & 0 deletions features/generate.feature
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ Feature: Generate AI content
use WordPress\AiClient\Results\Enums\FinishReasonEnum;
use WordPress\AI_Client\API_Credentials\API_Credentials_Manager;

if ( ! interface_exists( 'WordPress\AiClient\Providers\Models\Contracts\ModelInterface' ) ) {
return;
}

class WP_CLI_Mock_Model implements ModelInterface, TextGenerationModelInterface {
private $id;
private $config;
Expand Down Expand Up @@ -150,10 +154,23 @@ Feature: Generate AI content
);
"""

@less-than-wp-7.0
Scenario: Command not available on WP < 7.0
Given a WP install

When I try `wp ai generate text "Test prompt" --model=invalidformat`
Then STDERR should contain:
"""
Requires WordPress 7.0 or greater.
"""
And the return code should be 1

@require-wp-7.0
Scenario: Generate command validates model format
When I try `wp ai generate text "Test prompt" --model=invalidformat`
Then the return code should be 1

@require-wp-7.0
Scenario: Generate command validates max-tokens
When I try `wp ai generate text "Test prompt" --max-tokens=-5`
Then the return code should be 1
Expand All @@ -162,6 +179,7 @@ Feature: Generate AI content
Max tokens must be a positive integer
"""

@require-wp-7.0
Scenario: Generate command validates top-p range
When I try `wp ai generate text "Test prompt" --top-p=1.5`
Then the return code should be 1
Expand All @@ -170,6 +188,7 @@ Feature: Generate AI content
Top-p must be between 0.0 and 1.0
"""

@require-wp-7.0
Scenario: Generate command validates top-k positive
When I try `wp ai generate text "Test prompt" --top-k=-10`
Then the return code should be 1
Expand Down
51 changes: 18 additions & 33 deletions src/AI_Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,11 @@ class AI_Command extends WP_CLI_Command {
* @return void
*/
public function generate( $args, $assoc_args ) {
$this->initialize_ai_client();

list( $type, $prompt ) = $args;

try {
$builder = AI_Client::prompt( $prompt );
// @phpstan-ignore function.notFound
$builder = wp_ai_client_prompt( $prompt );

if ( isset( $assoc_args['provider'] ) ) {
$builder = $builder->using_provider( $assoc_args['provider'] );
Expand Down Expand Up @@ -216,13 +215,12 @@ public function generate( $args, $assoc_args ) {
* @return void
*/
public function check( $args, $assoc_args ) {
$this->initialize_ai_client();

list( $prompt ) = $args;
$type = $assoc_args['type'] ?? 'text';

try {
$builder = AI_Client::prompt( $prompt );
// @phpstan-ignore function.notFound
$builder = wp_ai_client_prompt( $prompt );

if ( 'text' === $type ) {
$supported = $builder->is_supported_for_text_generation();
Expand Down Expand Up @@ -281,11 +279,9 @@ public function check( $args, $assoc_args ) {
* @return void
*/
public function status( $args, $assoc_args ) {
$this->initialize_ai_client();

try {
// Create a builder to check capabilities (using constant for consistency)
$builder = AI_Client::prompt();
// @phpstan-ignore function.notFound
$builder = wp_ai_client_prompt();

// Check each capability
$capabilities = array(
Expand All @@ -312,15 +308,18 @@ public function status( $args, $assoc_args ) {
* @param \WordPress\AI_Client\Builders\Prompt_Builder $builder The prompt builder.
* @param array{format: string} $assoc_args Associative arguments.
* @return void
*
* @phpstan-ignore class.notFound
*/
private function generate_text( $builder, $assoc_args ) {
$format = $assoc_args['format'] ?? 'text';

// Check if supported
// @phpstan-ignore class.notFound
if ( ! $builder->is_supported_for_text_generation() ) {
WP_CLI::error( 'Text generation is not supported. Make sure AI provider credentials are configured.' );
}

// @phpstan-ignore class.notFound
$text = $builder->generate_text_result();

if ( 'json' === $format ) {
Expand All @@ -340,8 +339,11 @@ private function generate_text( $builder, $assoc_args ) {
"Summary:\nModel used: %s (%s)\nToken usage:\nInput tokens: %s\nOutput tokens: %s\nTotal: %s\n",
$text->getModelMetadata()->getName(),
$text->getProviderMetadata()->getName(),
// @phpstan-ignore class.notFound
$token_usage[ TokenUsage::KEY_PROMPT_TOKENS ],
// @phpstan-ignore class.notFound
$token_usage[ TokenUsage::KEY_COMPLETION_TOKENS ],
// @phpstan-ignore class.notFound
$token_usage[ TokenUsage::KEY_TOTAL_TOKENS ],
),
'ai'
Expand All @@ -351,12 +353,14 @@ private function generate_text( $builder, $assoc_args ) {
/**
* Generates an image from the prompt builder.
*
* @param \WordPress\AI_Client\Builders\Prompt_Builder $builder The prompt builder.
* @param \WordPress\AI_Client\Builders\Prompt_Builder $builder The prompt builder.
* @param array{'destination-file': string, stdout: bool} $assoc_args Associative arguments.
* @return void
*
* @phpstan-ignore class.notFound
*/
private function generate_image( $builder, $assoc_args ) {
// Check if supported
// @phpstan-ignore class.notFound
if ( ! $builder->is_supported_for_image_generation() ) {
WP_CLI::error( 'Image generation is not supported. Make sure AI provider credentials are configured.' );
}
Expand All @@ -374,6 +378,7 @@ private function generate_image( $builder, $assoc_args ) {
}
}

// @phpstan-ignore class.notFound
$image_file = $builder->generate_image();

if ( isset( $assoc_args['destination-file'] ) ) {
Expand Down Expand Up @@ -424,24 +429,4 @@ private function generate_image( $builder, $assoc_args ) {
WP_CLI::line( (string) $image_file->getDataUri() );
}
}

/**
* Ensures WordPress AI Client is available.
*
* @return void
*/
private function initialize_ai_client() {
\WordPress\AI_Client\AI_Client::init();

add_filter(
'user_has_cap',
static function ( array $allcaps ) {
$allcaps[ \WordPress\AI_Client\Capabilities\Capabilities_Manager::PROMPT_AI_CAPABILITY ] = true;

return $allcaps;
}
);

WP_CLI::do_hook( 'ai_client_init' );
}
}
4 changes: 4 additions & 0 deletions src/Credentials_Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ private function get_all_credentials() {
return array();
}

/**
* @var array<string, string> $credentials
*/

return $credentials;
}

Expand Down