85 lines
2.8 KiB
PHP
85 lines
2.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\Client;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\RateLimiter;
|
|
|
|
class LlmService
|
|
{
|
|
private const REQUEST_TIMEOUT = 30; // 请求超时时间(秒)
|
|
|
|
/**
|
|
* 发送 LLM 请求
|
|
*
|
|
* @param Client $client 客户
|
|
* @param string $prompt 提示词
|
|
* @param array $options 可选参数
|
|
* @return array{response: string, usage: array}
|
|
* @throws \RuntimeException
|
|
*/
|
|
public function sendRequest(Client $client, string $prompt, array $options = []): array
|
|
{
|
|
// 检查请求频率限制
|
|
$key = 'llm_request_' . $client->id;
|
|
if (RateLimiter::tooManyAttempts($key, $client->rate_limit)) {
|
|
$seconds = RateLimiter::availableIn($key);
|
|
throw new \RuntimeException("请求过于频繁,请在 {$seconds} 秒后重试。");
|
|
}
|
|
|
|
try {
|
|
/** @var \App\Models\LlmProvider $provider */
|
|
$provider = $client->llmProvider;
|
|
|
|
if ($provider->status !== 'active') {
|
|
throw new \RuntimeException('LLM 提供商当前不可用。');
|
|
}
|
|
|
|
// 发送请求到 LLM 提供商
|
|
$response = Http::timeout($client->timeout)
|
|
->withToken($provider->api_token)
|
|
->withHeaders([
|
|
'Content-Type' => 'application/json',
|
|
'Accept' => 'application/json',
|
|
])
|
|
->post($provider->api_url, array_merge([
|
|
'prompt' => $prompt,
|
|
], $options));
|
|
|
|
if (!$response->successful()) {
|
|
Log::error('LLM provider request failed', [
|
|
'status' => $response->status(),
|
|
'body' => $response->body(),
|
|
'provider' => $provider->name,
|
|
'client_id' => $client->id,
|
|
]);
|
|
|
|
throw new \RuntimeException('LLM 提供商服务异常,请稍后重试。');
|
|
}
|
|
|
|
// 记录成功的请求
|
|
RateLimiter::hit($key, 60);
|
|
|
|
$result = $response->json();
|
|
|
|
return [
|
|
'response' => $result['choices'][0]['text'] ?? $result['response'] ?? '',
|
|
'usage' => $result['usage'] ?? [],
|
|
];
|
|
|
|
} catch (\Illuminate\Http\Client\ConnectionException $e) {
|
|
Log::error('LLM provider connection timeout', [
|
|
'error' => $e->getMessage(),
|
|
'trace' => $e->getTraceAsString(),
|
|
'provider' => $provider->name ?? null,
|
|
'client_id' => $client->id,
|
|
]);
|
|
|
|
throw new \RuntimeException('LLM 提供商响应超时,请稍后重试。');
|
|
}
|
|
}
|
|
}
|