1
0
cms11/app/Services/AI/Providers/BaseProvider.php

188 lines
4.6 KiB
PHP
Raw Normal View History

2024-05-16 22:20:21 +02:00
<?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']);
}
}
}