12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485 |
- <?php
- namespace App\Services\LLM;
- use Illuminate\Support\Facades\Http;
- use Illuminate\Support\Facades\Log;
- class DeepSeekService
- {
- public static function streamedResponseChat($message): void
- {
- $llmConfig = config('llm.deepseek');
- $headers = [
- 'Authorization' => 'Bearer ' . $llmConfig['key'],
- 'Content-Type' => 'application/json',
- ];
- $body = [
- 'model' => $llmConfig['model'],
- 'messages' => $message,
- 'stream' => true,
- 'max_tokens' => 1024,
- 'temperature' => 0.7,
- 'top_p' => 1,
- "top_k"=> 50,
- "frequency_penalty" => 0.5,
- "n" => 1
- ];
- // 3. 调用DeepSeek流式API
- $response = Http::withHeaders($headers)->timeout(300)->send('POST', $llmConfig['base_url'] , [
- 'json' => $body
- ]);
- // 4. 流式转发数据
- $body = $response->toPsrResponse()->getBody();
- while (true) {
- if ($body->eof() || !$body->isReadable()) {
- break;
- }
- $chunk = $body->getContents();
- // 分割完整的JSON行
- $lines = explode("\n\n", $chunk);
- foreach ($lines as $line) {
- $line = trim($line);
- if ($line === '') {
- continue; // 跳过空行
- }
- // 处理单条事件
- if (str_starts_with($line, 'data: ')) {
- // 去除 "data: " 前缀
- $jsonStr = substr($line, 6);
- self::processEvent($jsonStr);
- }
- }
- // 检测连接是否中断
- if (connection_aborted()) {
- break;
- }
- }
- }
- // 处理单个事件的方法
- protected static function processEvent(string $jsonStr): void
- {
- if ($jsonStr === '[DONE]') {
- echo "event: end\n\n";
- ob_flush();
- flush();
- return;
- }
- try {
- $data = json_decode($jsonStr, true, 512, JSON_THROW_ON_ERROR);
- $content = $data['choices'][0]['delta']['content'] ?? '';
- echo "data: " . json_encode(['content' => $content], JSON_UNESCAPED_UNICODE) . "\n\n";
- ob_flush();
- flush();
- } catch (\JsonException $e) {
- Log::error('JSON 解析失败: ' . $e->getMessage());
- }
- }
- }
|