126 lines
4.4 KiB
PHP
126 lines
4.4 KiB
PHP
<?php
|
|
|
|
namespace app\service\Embedding;
|
|
|
|
use app\service\LLM\LLMRequestException;
|
|
use GuzzleHttp\Client;
|
|
use GuzzleHttp\Exception\RequestException;
|
|
use RuntimeException;
|
|
use Throwable;
|
|
|
|
class BigModelEmbeddingClient
|
|
{
|
|
private Client $client;
|
|
private array $config;
|
|
|
|
public function __construct(?array $config = null)
|
|
{
|
|
$this->config = $config ?? config('LLMapi.embedding', []);
|
|
$baseUrl = rtrim((string) ($this->config['base_url'] ?? ''), '/');
|
|
|
|
$this->client = new Client([
|
|
'base_uri' => $baseUrl . '/',
|
|
'timeout' => (int) ($this->config['timeout'] ?? 60),
|
|
'connect_timeout' => (int) ($this->config['connect_timeout'] ?? 10),
|
|
]);
|
|
}
|
|
|
|
public function isConfigured(): bool
|
|
{
|
|
return trim((string) ($this->config['api_key'] ?? '')) !== ''
|
|
&& trim((string) ($this->config['base_url'] ?? '')) !== '';
|
|
}
|
|
|
|
public function embed(array $texts, array $options = []): array
|
|
{
|
|
if (!$this->isConfigured()) {
|
|
throw new RuntimeException('BigModel embedding API is not configured.');
|
|
}
|
|
|
|
$texts = array_values($texts);
|
|
if ($texts === []) {
|
|
return [
|
|
'model' => (string) ($options['model'] ?? $this->config['model'] ?? 'embedding-3'),
|
|
'data' => [],
|
|
'usage' => [],
|
|
];
|
|
}
|
|
|
|
$batchSize = (int) ($this->config['batch_size'] ?? 64);
|
|
if (count($texts) > $batchSize) {
|
|
throw new RuntimeException("Embedding batch exceeds configured batch size {$batchSize}.");
|
|
}
|
|
|
|
$body = [
|
|
'model' => $options['model'] ?? $this->config['model'] ?? 'embedding-3',
|
|
'input' => $texts,
|
|
];
|
|
|
|
$dimensions = $options['dimensions'] ?? $this->config['dimensions'] ?? null;
|
|
if ($dimensions !== null) {
|
|
$dimensions = (int) $dimensions;
|
|
if (!in_array($dimensions, [256, 512, 1024, 2048], true)) {
|
|
throw new RuntimeException('Embedding dimensions must be one of 256, 512, 1024, or 2048.');
|
|
}
|
|
$body['dimensions'] = $dimensions;
|
|
}
|
|
|
|
try {
|
|
$response = $this->client->post('embeddings', [
|
|
'headers' => [
|
|
'Authorization' => 'Bearer ' . $this->config['api_key'],
|
|
'Content-Type' => 'application/json',
|
|
],
|
|
'json' => $body,
|
|
]);
|
|
} catch (RequestException $exception) {
|
|
throw $this->requestException($exception);
|
|
} catch (Throwable $exception) {
|
|
throw new RuntimeException('BigModel embedding request failed: ' . $exception->getMessage(), 0, $exception);
|
|
}
|
|
|
|
$payload = json_decode((string) $response->getBody(), true);
|
|
if (!is_array($payload) || !isset($payload['data']) || !is_array($payload['data'])) {
|
|
throw new RuntimeException('BigModel embedding response is invalid.');
|
|
}
|
|
|
|
return $payload;
|
|
}
|
|
|
|
private function requestException(RequestException $exception): LLMRequestException
|
|
{
|
|
$statusCode = $exception->getResponse()?->getStatusCode();
|
|
$body = $exception->getResponse() ? (string) $exception->getResponse()->getBody() : '';
|
|
$payload = json_decode($body, true);
|
|
$providerCode = null;
|
|
$providerMessage = null;
|
|
|
|
if (is_array($payload)) {
|
|
$providerCode = isset($payload['error']['code']) ? (string) $payload['error']['code'] : null;
|
|
$providerMessage = isset($payload['error']['message']) ? (string) $payload['error']['message'] : null;
|
|
|
|
if ($providerCode === null && isset($payload['code'])) {
|
|
$providerCode = (string) $payload['code'];
|
|
}
|
|
if ($providerMessage === null && isset($payload['message'])) {
|
|
$providerMessage = (string) $payload['message'];
|
|
}
|
|
}
|
|
|
|
$message = 'BigModel embedding request failed';
|
|
if ($statusCode !== null) {
|
|
$message .= " with HTTP {$statusCode}";
|
|
}
|
|
if ($providerCode !== null) {
|
|
$message .= " and provider code {$providerCode}";
|
|
}
|
|
if ($providerMessage !== null) {
|
|
$message .= ": {$providerMessage}";
|
|
} else {
|
|
$message .= ': ' . $exception->getMessage();
|
|
}
|
|
|
|
return new LLMRequestException($message, $statusCode, $providerCode, is_array($payload) ? $payload : null);
|
|
}
|
|
}
|