-
Notifications
You must be signed in to change notification settings - Fork 34
Provider base and implementation for Anthropic, Google, and OpenAI via common abstractions #39
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 2 commits
9d1c48c
3962f5b
9bf93ad
fa8774d
170fce4
7201212
c2a7c3e
d9b32fc
e7cb666
cd53963
30e63ba
5cc0e03
48e8365
f9f147d
50c3c0e
c73e812
90f187c
8bd9ee9
83f3268
3c53649
faec411
eb1192e
01a470b
78bf887
7ca7bc8
b4e2bcc
03cf0f7
ff8ce6b
34f279a
f75289a
dc0b26d
b04c637
62fd97c
ffdccd3
6fa22e8
97ef2cd
5992015
2bf6951
9d307f6
6c53d89
56c9178
073a767
7757d33
8788280
0e0ac80
e6848b6
45a8d50
20858ba
ecc3a53
2169172
9b4f09b
f5fbe89
4c58f96
6322f96
2a0e5f0
f192b63
853bb55
657d754
e4bc2d7
4499d45
bbffb4a
0f03598
39edb57
c6575f5
7fb9267
1c53f39
a8dfd8a
aa89253
5044796
e87d4f3
50850b6
ccd62f1
36df670
005fed1
f00d34a
a23d42d
11ccd68
3051148
702f4fb
e92b8ee
0845206
f802aeb
61ac6d3
31f3e05
d6c3ab2
ef3b0ef
296040a
3186fbc
eea2c67
3815355
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,44 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace WordPress\AiClient\ProviderImplementations\OpenAi; | ||
|
|
||
| use WordPress\AiClient\Providers\AbstractOpenAiCompatibleModelMetadataDirectory; | ||
| use WordPress\AiClient\Providers\Models\DTO\ModelMetadata; | ||
|
|
||
| /** | ||
| * Class for the OpenAI model metadata directory. | ||
| * | ||
| * @since n.e.x.t | ||
| */ | ||
| class OpenAiModelMetadataDirectory extends AbstractOpenAiCompatibleModelMetadataDirectory | ||
| { | ||
| /** | ||
| * @inheritDoc | ||
| */ | ||
| protected function createRequest(string $path): RequestInterface | ||
|
Check failure on line 20 in src/ProviderImplementations/OpenAi/OpenAiModelMetadataDirectory.php
|
||
| { | ||
| // Something like this. | ||
| return new OpenAiCompatibleRequest('https://api.openai.com/v1', $path); | ||
|
Check failure on line 23 in src/ProviderImplementations/OpenAi/OpenAiModelMetadataDirectory.php
|
||
| } | ||
|
|
||
| /** | ||
| * @inheritDoc | ||
| */ | ||
| protected function parseResponseToModelMetadataList(ResponseInterface $response): array | ||
|
Check failure on line 29 in src/ProviderImplementations/OpenAi/OpenAiModelMetadataDirectory.php
|
||
| { | ||
| $responseData = $response->getData(); | ||
| if (!isset($responseData['data']) || !$responseData['data']) { | ||
| throw new RuntimeException( | ||
| 'Unexpected API response: Missing the data key.' | ||
| ); | ||
| } | ||
| return array_map( | ||
| static function (array $modelData): ModelMetadata { | ||
| // TODO: Create ModelMetadata object from API data. | ||
| }, | ||
| $responseData['data'] | ||
| ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace WordPress\AiClient\ProviderImplementations\OpenAi; | ||
|
|
||
| use WordPress\AiClient\Providers\AbstractProvider; | ||
| use WordPress\AiClient\Providers\Contracts\ModelMetadataDirectoryInterface; | ||
| use WordPress\AiClient\Providers\Contracts\ProviderAvailabilityInterface; | ||
| use WordPress\AiClient\Providers\Contracts\ProviderInterface; | ||
| use WordPress\AiClient\Providers\DTO\ProviderMetadata; | ||
| use WordPress\AiClient\Providers\Enums\ProviderTypeEnum; | ||
| use WordPress\AiClient\Providers\ListModelsApiBasedProviderAvailability; | ||
| use WordPress\AiClient\Providers\Models\Contracts\ModelInterface; | ||
| use WordPress\AiClient\Providers\Models\DTO\ModelConfig; | ||
| use WordPress\AiClient\Providers\Models\DTO\ModelMetadata; | ||
|
|
||
| /** | ||
| * Class for the OpenAI provider. | ||
| * | ||
| * @since n.e.x.t | ||
| */ | ||
| class OpenAiProvider extends AbstractProvider | ||
| { | ||
| /** | ||
| * @inheritDoc | ||
| */ | ||
| protected static function createModel( | ||
| ModelMetadata $modelMetadata, | ||
| ProviderMetadata $providerMetadata | ||
| ): ModelInterface { | ||
| $capabilities = $modelMetadata->getCapabilities(); | ||
| foreach ($capabilities as $capability) { | ||
| if ($capability->isTextGeneration()) { | ||
| return new OpenAiTextGenerationModel($modelMetadata, $providerMetadata); | ||
| } | ||
| if ($capability->isImageGeneration()) { | ||
| // TODO: Implement OpenAiImageGenerationModel. | ||
| return new OpenAiImageGenerationModel($modelMetadata, $providerMetadata); | ||
| } | ||
| if ($capability->isTextToSpeechConversion()) { | ||
| // TODO: Implement OpenAiTextToSpeechConversionModel. | ||
| return new OpenAiTextToSpeechConversionModel($modelMetadata, $providerMetadata); | ||
| } | ||
| } | ||
|
|
||
| throw new RuntimeException( | ||
| 'Unsupported model capabilities: ' . implode(', ', $capabilities) | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * @inheritDoc | ||
| */ | ||
| protected static function createProviderMetadata(): ProviderMetadata | ||
| { | ||
| return new ProviderMetadata( | ||
| 'openai', | ||
| 'OpenAI', | ||
| ProviderTypeEnum::cloud() | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * @inheritDoc | ||
| */ | ||
| protected static function createProviderAvailability(): ProviderAvailabilityInterface | ||
| { | ||
| // Check valid API access by attempting to list models. | ||
| return new ListModelsApiBasedProviderAvailability( | ||
| static::modelMetadataDirectory() | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * @inheritDoc | ||
| */ | ||
| protected static function createModelMetadataDirectory(): ModelMetadataDirectoryInterface | ||
| { | ||
| return new OpenAiModelMetadataDirectory(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace WordPress\AiClient\ProviderImplementations\OpenAi; | ||
|
|
||
| use WordPress\AiClient\Providers\AbstractOpenAiCompatibleTextGenerationModel; | ||
| use WordPress\AiClient\Providers\Models\DTO\ModelMetadata; | ||
|
|
||
| /** | ||
| * Class for an OpenAI text generation model. | ||
| * | ||
| * @since n.e.x.t | ||
| */ | ||
| class OpenAiTextGenerationModel extends AbstractOpenAiCompatibleTextGenerationModel | ||
| { | ||
| /** | ||
| * @inheritDoc | ||
| */ | ||
| protected function createRequest(string $path, array $params): RequestInterface | ||
| { | ||
| // Something like this. | ||
| return new OpenAiCompatibleRequest('https://api.openai.com/v1', $path); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace WordPress\AiClient\Providers; | ||
|
|
||
| use InvalidArgumentException; | ||
| use WordPress\AiClient\Providers\DTO\ProviderMetadata; | ||
| use WordPress\AiClient\Providers\Models\Contracts\ModelInterface; | ||
| use WordPress\AiClient\Providers\Models\Contracts\WithHttpTransporterInterface; | ||
| use WordPress\AiClient\Providers\Models\DTO\ModelConfig; | ||
| use WordPress\AiClient\Providers\Models\DTO\ModelMetadata; | ||
| use WordPress\AiClient\Providers\Models\Traits\WithHttpTransporterTrait; | ||
|
|
||
| /** | ||
| * Base class for an API-based model for a provider. | ||
| * | ||
| * @since n.e.x.t | ||
| */ | ||
| abstract class AbstractApiBasedModel implements | ||
| ModelInterface, | ||
| WithHttpTransporterInterface | ||
| { | ||
| use WithHttpTransporterTrait; | ||
|
|
||
| /** | ||
| * @var ModelMetadata The metadata for the model. | ||
| */ | ||
| private ModelMetadata $metadata; | ||
|
|
||
| /** | ||
| * @var ProviderMetadata The metadata for the model's provider. | ||
| */ | ||
| private ProviderMetadata $providerMetadata; | ||
|
|
||
| /** | ||
| * @var ModelConfig The configuration for the model. | ||
| */ | ||
| private ModelConfig $config; | ||
|
|
||
| /** | ||
| * Constructor. | ||
| * | ||
| * @since n.e.x.t | ||
| * | ||
| * @param ModelMetadata $metadata The metadata for the model. | ||
| * @param ProviderMetadata $providerMetadata The metadata for the model's provider. | ||
| */ | ||
| public function __construct(ModelMetadata $metadata, ProviderMetadata $providerMetadata) | ||
| { | ||
| $this->metadata = $metadata; | ||
| $this->providerMetadata = $providerMetadata; | ||
| $this->config = ModelConfig::fromArray([]); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the metadata for the model. | ||
| * | ||
| * @since n.e.x.t | ||
| * | ||
| * @return ModelMetadata The model metadata. | ||
| */ | ||
| public function metadata(): ModelMetadata | ||
| { | ||
| return $this->metadata; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the metadata for the model's provider. | ||
| * | ||
| * @since n.e.x.t | ||
| * | ||
| * @return ProviderMetadata The provider metadata. | ||
| */ | ||
| public function providerMetadata(): ProviderMetadata | ||
| { | ||
| return $this->providerMetadata; | ||
| } | ||
|
|
||
| /** | ||
| * Sets the configuration for the model. | ||
| * | ||
| * @since n.e.x.t | ||
| * | ||
| * @param ModelConfig $config The configuration for the model. | ||
| */ | ||
| public function setConfig(ModelConfig $config): void | ||
| { | ||
| $this->config = $config; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the configuration for the model. | ||
| * | ||
| * @since n.e.x.t | ||
| * | ||
| * @return ModelConfig The model configuration. | ||
| */ | ||
| public function getConfig(): ModelConfig | ||
| { | ||
| return $this->config; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace WordPress\AiClient\Providers; | ||
|
|
||
| use InvalidArgumentException; | ||
| use WordPress\AiClient\Providers\Contracts\ModelMetadataDirectoryInterface; | ||
| use WordPress\AiClient\Providers\Models\Contracts\WithHttpTransporterInterface; | ||
| use WordPress\AiClient\Providers\Models\DTO\ModelMetadata; | ||
| use WordPress\AiClient\Providers\Models\Traits\WithHttpTransporterTrait; | ||
|
|
||
| /** | ||
| * Base class for an API-based model metadata directory for a provider. | ||
| * | ||
| * @since n.e.x.t | ||
| */ | ||
| abstract class AbstractApiBasedModelMetadataDirectory implements | ||
| ModelMetadataDirectoryInterface, | ||
| WithHttpTransporterInterface | ||
| { | ||
| use WithHttpTransporterTrait; | ||
|
|
||
| /** | ||
| * @var ?array<string, ModelMetadata> Map of model ID to model metadata, effectively for caching. | ||
| */ | ||
| private ?array $modelMetadataMap = null; | ||
|
|
||
| /** | ||
| * Lists the metadata for all models from the provider. | ||
| * | ||
| * @since n.e.x.t | ||
| * | ||
| * @return list<ModelMetadata> List of model metadata objects. | ||
| */ | ||
| final public function listModelMetadata(): array | ||
| { | ||
| $modelsMetadata = $this->getModelMetadataMap(); | ||
| return array_values($modelsMetadata); | ||
| } | ||
|
|
||
| /** | ||
| * Checks whether model metadata for the given model ID exists. | ||
| * | ||
| * This is effectively a check for whether the given model ID is for a valid model from the provider. | ||
| * | ||
| * @since n.e.x.t | ||
| * | ||
| * @param string $modelId The model ID. | ||
| * @return bool True if there is metadata for the model. | ||
| */ | ||
| final public function hasModelMetadata(string $modelId): bool | ||
| { | ||
| try { | ||
| $this->getModelMetadata(); | ||
| } catch (InvalidArgumentException $e) { | ||
| return false; | ||
| } | ||
| return true; | ||
felixarntz marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| /** | ||
| * Gets the model metadata for the given model ID. | ||
| * | ||
| * @since n.e.x.t | ||
| * | ||
| * @param string $modelId The model ID. | ||
| * @return ModelMetadata The model metadata. | ||
| * @throws InvalidArgumentException If the model for the given ID does not exist. | ||
| */ | ||
| final public function getModelMetadata(string $modelId): ModelMetadata | ||
| { | ||
| $modelsMetadata = $this->getModelMetadataMap(); | ||
| if (!isset($modelsMetadata[$modelId])) { | ||
| throw new InvalidArgumentException( | ||
| sprintf('No model with ID %s was found in the provider', $modelId) | ||
| ); | ||
| } | ||
| return $modelsMetadata[$modelId]; | ||
|
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. If we simplify the has function, per my last comment, then we can simplify this, too: if (!$this->has($modelId)) {
throw new InvalidArgumentException(
sprintf('No model with ID %s was found in the provider', $modelId)
);
}
return $this->getModelMetadataMap()[$modelId];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 would push back on this: If you argue above that calling 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. My issue wasn't with methods referencing one another; I think that's just reducing redundancy. I more view That said, it's really not a big deal. It's such a simple check. 😄 |
||
| } | ||
|
|
||
| /** | ||
| * Returns the map of model ID to model metadata for all models from the provider. | ||
| * | ||
| * @since n.e.x.t | ||
| * | ||
| * @return array<string, ModelMetadata> Map of model ID to model metadata. | ||
| */ | ||
| private function getModelMetadataMap(): array | ||
| { | ||
| if ($this->modelMetadataMap === null) { | ||
| $this->modelMetadataMap = $this->sendListModelsRequest(); | ||
| } | ||
| return $this->modelMetadataMap; | ||
| } | ||
|
|
||
| /** | ||
| * Sends the API request to list models from the provider and returns the map of model ID to model metadata. | ||
| * | ||
| * @since n.e.x.t | ||
| * | ||
| * @return array<string, ModelMetadata> Map of model ID to model metadata. | ||
| */ | ||
| abstract protected function sendListModelsRequest(): array; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.