llmbackend/app/Services/LlmService.php
2024-12-04 12:43:04 +08:00

84 lines
2.7 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 {
$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 提供商响应超时,请稍后重试。');
}
}
}