|  | 
|  | 1 | +<?php | 
|  | 2 | +/** | 
|  | 3 | + * CLI script for interacting with the AI client. | 
|  | 4 | + * | 
|  | 5 | + * This script allows users to send prompts to the AI and receive responses. | 
|  | 6 | + * It supports named arguments for provider and model selection. | 
|  | 7 | + * | 
|  | 8 | + * Usage: | 
|  | 9 | + *   GOOGLE_API_KEY=123456 php cli.php 'Your prompt here' --providerId=google --modelId=gemini-2.5-flash | 
|  | 10 | + *   OPENAI_API_KEY=123456 php cli.php 'Your prompt here' --providerId=openai | 
|  | 11 | + *   GOOGLE_API_KEY=123456 OPENAI_API_KEY=123456 php cli.php 'Your prompt here' | 
|  | 12 | + */ | 
|  | 13 | + | 
|  | 14 | +declare(strict_types=1); | 
|  | 15 | + | 
|  | 16 | +use WordPress\AiClient\AiClient; | 
|  | 17 | +use WordPress\AiClient\Providers\Http\Exception\ResponseException; | 
|  | 18 | +use WordPress\AiClient\Providers\Models\DTO\ModelConfig; | 
|  | 19 | + | 
|  | 20 | +require_once __DIR__ . '/vendor/autoload.php'; | 
|  | 21 | + | 
|  | 22 | +/** | 
|  | 23 | + * Prints the output to stdout. | 
|  | 24 | + * | 
|  | 25 | + * @param string $output The output to print. | 
|  | 26 | + */ | 
|  | 27 | +function printOutput(string $output): void | 
|  | 28 | +{ | 
|  | 29 | +    echo $output . PHP_EOL; | 
|  | 30 | +} | 
|  | 31 | + | 
|  | 32 | +/** | 
|  | 33 | + * Logs an informational message to stderr. | 
|  | 34 | + * | 
|  | 35 | + * @param string $message The message to log. | 
|  | 36 | + */ | 
|  | 37 | +function logInfo(string $message): void | 
|  | 38 | +{ | 
|  | 39 | +    fwrite(STDERR, '[INFO] ' . $message . PHP_EOL); | 
|  | 40 | +} | 
|  | 41 | + | 
|  | 42 | +/** | 
|  | 43 | + * Logs a warning message to stderr. | 
|  | 44 | + * | 
|  | 45 | + * @param string $message The message to log. | 
|  | 46 | + */ | 
|  | 47 | +function logWarning(string $message): void | 
|  | 48 | +{ | 
|  | 49 | +    fwrite(STDERR, '[WARNING] ' . $message . PHP_EOL); | 
|  | 50 | +} | 
|  | 51 | + | 
|  | 52 | +/** | 
|  | 53 | + * Logs an error message to stderr and terminates the script. | 
|  | 54 | + * | 
|  | 55 | + * @param string $message The message to log. | 
|  | 56 | + * @param int    $exit_code The exit code to use. | 
|  | 57 | + */ | 
|  | 58 | +function logError(string $message, int $exit_code = 1): void | 
|  | 59 | +{ | 
|  | 60 | +    fwrite(STDERR, '[ERROR] ' . $message . PHP_EOL); | 
|  | 61 | +    exit($exit_code); | 
|  | 62 | +} | 
|  | 63 | + | 
|  | 64 | +// --- Argument parsing --- | 
|  | 65 | + | 
|  | 66 | +$positional_args = []; | 
|  | 67 | +$named_args      = []; | 
|  | 68 | + | 
|  | 69 | +for ($i = 1; $i < $argc; $i++) { | 
|  | 70 | +    $arg = $argv[$i]; | 
|  | 71 | +    if (str_starts_with($arg, '--')) { | 
|  | 72 | +        $parts = explode('=', substr($arg, 2), 2); | 
|  | 73 | +        $key   = $parts[0]; | 
|  | 74 | +        $value = $parts[1] ?? true; | 
|  | 75 | +        if (empty($key)) { | 
|  | 76 | +            logWarning("Ignoring invalid named argument: {$arg}"); | 
|  | 77 | +            continue; | 
|  | 78 | +        } | 
|  | 79 | +        $named_args[$key] = $value; | 
|  | 80 | +    } else { | 
|  | 81 | +        $positional_args[] = $arg; | 
|  | 82 | +    } | 
|  | 83 | +} | 
|  | 84 | + | 
|  | 85 | +// --- Input validation --- | 
|  | 86 | + | 
|  | 87 | +if (empty($positional_args[0])) { | 
|  | 88 | +    logError('Missing required positional argument "prompt input".'); | 
|  | 89 | +} | 
|  | 90 | + | 
|  | 91 | +// Prompt input. Allow complex input as a JSON string. | 
|  | 92 | +$promptInput = $positional_args[0]; | 
|  | 93 | +if (strpos($promptInput, '{') === 0 || strpos($promptInput, '[') === 0) { | 
|  | 94 | +    $decodedInput = json_decode($promptInput, true); | 
|  | 95 | +    if ($decodedInput) { | 
|  | 96 | +        $promptInput = $decodedInput; | 
|  | 97 | +    } | 
|  | 98 | +} | 
|  | 99 | + | 
|  | 100 | +// Provider ID, model ID, and output format. | 
|  | 101 | +$providerId = $named_args['providerId'] ?? null; | 
|  | 102 | +$modelId = $named_args['modelId'] ?? null; | 
|  | 103 | +$outputFormat = $named_args['outputFormat'] ?? 'message-text'; | 
|  | 104 | + | 
|  | 105 | +// Any model configuration options. | 
|  | 106 | +$schema = ModelConfig::getJsonSchema()['properties']; | 
|  | 107 | +$model_config_data = []; | 
|  | 108 | +foreach ($named_args as $key => $value) { | 
|  | 109 | +    if (!isset($schema[$key])) { | 
|  | 110 | +        continue; | 
|  | 111 | +    } | 
|  | 112 | + | 
|  | 113 | +    $property_schema = $schema[$key]; | 
|  | 114 | +    $type = $property_schema['type'] ?? null; | 
|  | 115 | + | 
|  | 116 | +    $processed_value = $value; | 
|  | 117 | +    if ($type === 'array' || $type === 'object') { | 
|  | 118 | +        $decoded = json_decode((string) $value, true); | 
|  | 119 | +        if (json_last_error() !== JSON_ERROR_NONE) { | 
|  | 120 | +            logWarning("Invalid JSON for argument --{$key}: " . json_last_error_msg()); | 
|  | 121 | +            continue; | 
|  | 122 | +        } | 
|  | 123 | +        $processed_value = $decoded; | 
|  | 124 | +    } elseif ($type === 'integer') { | 
|  | 125 | +        $processed_value = (int) $value; | 
|  | 126 | +    } elseif ($type === 'number') { | 
|  | 127 | +        $processed_value = (float) $value; | 
|  | 128 | +    } elseif ($type === 'boolean') { | 
|  | 129 | +        $processed_value = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); | 
|  | 130 | +        if (null === $processed_value) { | 
|  | 131 | +            logWarning("Invalid boolean for argument --{$key}: {$value}"); | 
|  | 132 | +            continue; | 
|  | 133 | +        } | 
|  | 134 | +    } | 
|  | 135 | + | 
|  | 136 | +    $model_config_data[$key] = $processed_value; | 
|  | 137 | +} | 
|  | 138 | + | 
|  | 139 | +// --- Main logic --- | 
|  | 140 | + | 
|  | 141 | +try { | 
|  | 142 | +    $modelConfig = ModelConfig::fromArray($model_config_data); | 
|  | 143 | + | 
|  | 144 | +    $promptBuilder = AiClient::prompt($promptInput); | 
|  | 145 | +    $promptBuilder = $promptBuilder->usingModelConfig($modelConfig); | 
|  | 146 | +    if ($providerId && $modelId) { | 
|  | 147 | +        $providerClassName = AiClient::defaultRegistry()->getProviderClassName($providerId); | 
|  | 148 | +        $promptBuilder = $promptBuilder->usingModel($providerClassName::model($modelId)); | 
|  | 149 | +    } elseif ($providerId) { | 
|  | 150 | +        $promptBuilder = $promptBuilder->usingProvider($providerId); | 
|  | 151 | +    } | 
|  | 152 | +} catch (InvalidArgumentException $e) { | 
|  | 153 | +    logError('Invalid arguments while trying to set up prompt builder: ' . $e->getMessage()); | 
|  | 154 | +} catch (ResponseException $e) { | 
|  | 155 | +    logError('Request failed while trying to set up prompt builder: ' . $e->getMessage()); | 
|  | 156 | +} | 
|  | 157 | + | 
|  | 158 | +try { | 
|  | 159 | +    $result = $promptBuilder->generateTextResult(); | 
|  | 160 | +} catch (InvalidArgumentException $e) { | 
|  | 161 | +    logError('Invalid arguments while trying to generate text result: ' . $e->getMessage()); | 
|  | 162 | +} catch (ResponseException $e) { | 
|  | 163 | +    logError('Request failed while trying to generate text result: ' . $e->getMessage()); | 
|  | 164 | +} | 
|  | 165 | + | 
|  | 166 | +logInfo("Using provider ID: \"{$result->getProviderMetadata()->getId()}\""); | 
|  | 167 | +logInfo("Using model ID: \"{$result->getModelMetadata()->getId()}\""); | 
|  | 168 | + | 
|  | 169 | +switch ($outputFormat) { | 
|  | 170 | +    case 'result-json': | 
|  | 171 | +        $output = json_encode($result, JSON_PRETTY_PRINT); | 
|  | 172 | +        break; | 
|  | 173 | +    case 'candidates-json': | 
|  | 174 | +        $output = json_encode($result->getCandidates(), JSON_PRETTY_PRINT); | 
|  | 175 | +        break; | 
|  | 176 | +    case 'message-text': | 
|  | 177 | +    default: | 
|  | 178 | +        $output = $result->toText(); | 
|  | 179 | +} | 
|  | 180 | + | 
|  | 181 | +printOutput($output); | 
0 commit comments