188 lines
4.6 KiB
PHP
188 lines
4.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services\AI\Providers;
|
|
|
|
use App\Services\AI\Contracts\ProvidesAIServices;
|
|
use Exception;
|
|
use Illuminate\Http\Client\ConnectionException;
|
|
use Illuminate\Http\Client\PendingRequest;
|
|
use Illuminate\Http\Client\Pool;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Str;
|
|
|
|
abstract class BaseProvider implements ProvidesAIServices
|
|
{
|
|
protected array $defaultOptions;
|
|
|
|
protected string $baseUrl;
|
|
|
|
protected string $endpoint;
|
|
|
|
protected array $headers;
|
|
|
|
protected array $options;
|
|
|
|
protected string $prompt;
|
|
|
|
protected array $models;
|
|
|
|
public function __construct(array $options)
|
|
{
|
|
$this->defaultOptions = $options;
|
|
|
|
$this->initializeOptions($options);
|
|
}
|
|
|
|
/**
|
|
* Set provider's base URL, which is the root of its API
|
|
*/
|
|
public function baseUrl(string $url): self
|
|
{
|
|
$this->baseUrl = Str::unwrap($url, null, '/');
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set API endpoint used for next request
|
|
*/
|
|
public function endpoint(string $endpoint): self
|
|
{
|
|
$this->endpoint = Str::unwrap($endpoint, '/');
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add some headers to the request
|
|
*/
|
|
public function withHeaders(array $headers): self
|
|
{
|
|
$this->headers = array_replace_recursive($this->headers ?? [], $headers);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Provides low-level options to the provider. They will be integrated into
|
|
* request's body.
|
|
*/
|
|
public function withOptions(array $options): self
|
|
{
|
|
$this->options = array_replace_recursive($this->options ?? [], $options);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Define the service used for next request
|
|
*/
|
|
public function forService(string $service): self
|
|
{
|
|
$serviceConfig = data_get($this->defaultOptions, sprintf('services.%s', $service));
|
|
|
|
if (empty($serviceConfig)) {
|
|
throw new Exception(sprintf('Service %s has no configuration for provider %s', $service, get_class($this)));
|
|
}
|
|
|
|
$this->initializeOptions($serviceConfig);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Sets the prompt used in next request
|
|
*/
|
|
public function withPrompt(string $prompt): self
|
|
{
|
|
$this->prompt = $prompt;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Sets the model(s) to use during this request life-cycle
|
|
*/
|
|
public function withModels(array $models): self
|
|
{
|
|
foreach ($models as $key => $val) {
|
|
if (is_string($val)) {
|
|
// If $val is a string, we did not provide additionnal settings
|
|
$models[$val] = [];
|
|
|
|
unset($models[$key]);
|
|
}
|
|
}
|
|
|
|
$this->models = $models;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function prepareRequest(PendingRequest|Pool $pool)
|
|
{
|
|
$pendingRequests = [];
|
|
|
|
foreach ($this->models as $model => $overrides) {
|
|
$options = array_replace_recursive($this->options, $overrides);
|
|
|
|
$options['model'] = $model;
|
|
|
|
try {
|
|
$key = sprintf('%s.%s', get_class($this), $model);
|
|
|
|
$request = $pool;
|
|
|
|
if ($request instanceof Pool) {
|
|
$request = $pool->as($key);
|
|
}
|
|
|
|
$pendingRequests[$key] = $request->timeout(30)
|
|
->withHeaders($this->headers)
|
|
->post(sprintf('%s/%s', $this->baseUrl, $this->endpoint), $options);
|
|
} catch (ConnectionException $ex) {
|
|
$pendingRequests[$key] = [
|
|
'response' => $ex->getMessage(),
|
|
];
|
|
}
|
|
}
|
|
|
|
return $pendingRequests;
|
|
}
|
|
|
|
public function sendRequest(): array
|
|
{
|
|
return $this->prepareRequest(Http::baseUrl($this->baseUrl));
|
|
}
|
|
|
|
/**
|
|
* Set provider's default configuration
|
|
*/
|
|
private function initializeOptions(array $options)
|
|
{
|
|
if (array_key_exists('headers', $options)) {
|
|
$this->withHeaders($options['headers']);
|
|
}
|
|
|
|
if (array_key_exists('options', $options)) {
|
|
$this->withOptions($options['options']);
|
|
}
|
|
|
|
if (array_key_exists('baseUrl', $options)) {
|
|
$this->baseUrl($options['baseUrl']);
|
|
}
|
|
|
|
if (array_key_exists('endpoint', $options)) {
|
|
$this->endpoint($options['endpoint']);
|
|
}
|
|
|
|
if (array_key_exists('prompt', $options)) {
|
|
$this->withPrompt($options['prompt']);
|
|
}
|
|
|
|
if (array_key_exists('models', $options)) {
|
|
$this->withModels($options['models']);
|
|
}
|
|
}
|
|
}
|