lh 3 月之前
父節點
當前提交
f0c0df911e
共有 2 個文件被更改,包括 419 次插入289 次删除
  1. 1 1
      app/Http/Controllers/Anime/AnimeController.php
  2. 418 288
      app/Services/DeepSeek/DeepSeekService.php

+ 1 - 1
app/Http/Controllers/Anime/AnimeController.php

@@ -50,7 +50,7 @@ class AnimeController extends BaseController
     }
 
 
-    public function artStyleList(): array
+    public function artStyleList()
     {
         $result = [
             [

+ 418 - 288
app/Services/DeepSeek/DeepSeekService.php

@@ -25,6 +25,7 @@ class DeepSeekService
     private $api_key;
     private $sys_message;
     private $headers;
+    private $valid_text_models;
     protected $aiImageGenerationService;
 
     public function __construct(AIImageGenerationService $aiImageGenerationService) {
@@ -35,6 +36,7 @@ class DeepSeekService
             'Authorization' => 'Bearer '.$this->api_key,
             'Content-Type'  => 'application/json; charset=UTF-8'
         ];
+        $this->valid_text_models = ['doubao-seed-2-0-pro-260215', 'doubao-seed-2-0-lite-260215', 'deepseek-v3-2-251201'];
         $this->sys_message = [
                 'role'    => 'system',
                 'content' => "你是一个专业的文档分析助手及资深编剧,请根据用户提供的文档内容完成示例格式的内容输出(需通过以下几个板块进行回复: <故事梗概><剧本亮点><人物关系><核心矛盾><主体列表><美术风格><场景列表><分集剧本>,每个板块之间用###分隔;同时板块之间需满足以下要求:\n
@@ -141,7 +143,7 @@ class DeepSeekService
     {
         return [
             '日系动漫风格' => [
-                'aliases' => ['日系动漫风格', '日系动漫', '日系', '动漫'],
+                'aliases' => ['日系动漫风格', '日系动漫', '日系', '动漫', '日漫'],
                 'prompt' => "日系动漫风格,线条干净流畅,色彩明亮柔和,人物精致,光影细腻,画面统一稳定"
             ],
             '国漫风格' => [
@@ -244,6 +246,116 @@ class DeepSeekService
         return preg_replace($pattern, "###美术风格\n" . trim($artStyle), $content, 1);
     }
 
+    /**
+     * DeepSeek流式输出处理方法
+     * 
+     * @param array $post_data DeepSeek API请求参数
+     * @return \Generator 返回生成器,用于流式输出
+     */
+    private function deepSeekStreamResponse($post_data) {
+        $client = new Client(['timeout' => 1200, 'verify' => false]);
+        $response = $client->post($this->url, [
+            'json' => $post_data, 
+            'headers' => $this->headers,
+            'stream' => true  // 启用流式响应
+        ]);
+        
+        $body = $response->getBody();
+        $fullContent = '';
+        $fullReasoningContent = '';
+        $usage = [];
+        $buffer = '';
+
+        dLog('deepseek')->info('开始读取 DeepSeek 流式响应');
+
+        // 逐行读取流式响应
+        while (!$body->eof()) {
+            $chunk = $body->read(1024); // 每次读取 1KB
+            $buffer .= $chunk;
+            
+            // 按行分割
+            $lines = explode("\n", $buffer);
+            // 保留最后一个不完整的行
+            $buffer = array_pop($lines);
+            
+            foreach ($lines as $line) {
+                $line = trim($line);
+                
+                // 跳过空行
+                if (empty($line)) {
+                    continue;
+                }
+                
+                // 检查是否是 SSE 数据行
+                if (strpos($line, 'data: ') !== 0) {
+                    dLog('deepseek')->warning('非 SSE 数据行', ['line' => $line]);
+                    continue;
+                }
+
+                $data = substr($line, 6); // 移除 "data: " 前缀
+                
+                if ($data === '[DONE]') {
+                    dLog('deepseek')->info('收到结束标记');
+                    break 2; // 跳出两层循环
+                }
+
+                try {
+                    $json = json_decode($data, true);
+                    
+                    if (json_last_error() !== JSON_ERROR_NONE) {
+                        dLog('deepseek')->warning('JSON 解析错误: ' . json_last_error_msg(), ['data' => $data]);
+                        continue;
+                    }
+                    
+                    if (isset($json['choices'][0]['delta'])) {
+                        $delta = $json['choices'][0]['delta'];
+                        
+                        // 处理思维链内容(仅 deepseek-reasoner 模型)
+                        if (isset($delta['reasoning_content'])) {
+                            $fullReasoningContent .= $delta['reasoning_content'];
+                            yield [
+                                'type' => 'reasoning',
+                                'content' => $delta['reasoning_content'],
+                                'full_reasoning' => $fullReasoningContent
+                            ];
+                        }
+                        
+                        // 处理最终回答内容
+                        if (isset($delta['content'])) {
+                            $fullContent .= $delta['content'];
+                            yield [
+                                'type' => 'content',
+                                'content' => $delta['content'],
+                            ];
+                        }
+                    }
+                    
+                    // 获取使用统计信息
+                    if (isset($json['usage'])) {
+                        $usage = $json['usage'];
+                        dLog('deepseek')->info('收到使用统计', ['usage' => $usage]);
+                    }
+                    
+                } catch (\Exception $e) {
+                    dLog('deepseek')->error('解析流式响应失败: ' . $e->getMessage(), ['line' => $line]);
+                    continue;
+                }
+            }
+        }
+
+        dLog('deepseek')->info('流式读取完成', [
+            'content_length' => strlen($fullContent),
+            'reasoning_length' => strlen($fullReasoningContent)
+        ]);
+
+        yield [
+            'type' => 'done',
+            'full_content' => $fullContent,
+            'full_reasoning' => $fullReasoningContent,
+            'usage' => $usage
+        ];
+    }
+
     // 与推理模型对话
     public function chatWithReasoner($data) {
         $content = getProp($data, 'content');
@@ -1919,108 +2031,34 @@ class DeepSeekService
             'stream'            => true  // 启用流式输出
         ];
 
-        $client = new Client(['timeout' => 1200, 'verify' => false]);
-        $response = $client->post($this->url, [
-            'json' => $post_data, 
-            'headers' => $this->headers,
-            'stream' => true  // 启用流式响应
-        ]);
-        
-        $body = $response->getBody();
+        // 根据模型类型选择调用方法
         $fullContent = '';
         $fullReasoningContent = '';
         $usage = [];
-        $buffer = '';
-
-        dLog('deepseek')->info('开始读取 DeepSeek 流式响应');
-
-        // 逐行读取流式响应
-        while (!$body->eof()) {
-            $chunk = $body->read(1024); // 每次读取 1KB
-            $buffer .= $chunk;
-            
-            // 按行分割
-            $lines = explode("\n", $buffer);
-            // 保留最后一个不完整的行
-            $buffer = array_pop($lines);
-            
-            foreach ($lines as $line) {
-                $line = trim($line);
-                
-                // 跳过空行
-                if (empty($line)) {
-                    continue;
-                }
-                
-                // dLog('deepseek')->info('收到原始行数据', ['line' => $line]);
-                
-                // 检查是否是 SSE 数据行
-                if (strpos($line, 'data: ') !== 0) {
-                    dLog('deepseek')->warning('非 SSE 数据行', ['line' => $line]);
-                    continue;
-                }
-
-                $data = substr($line, 6); // 移除 "data: " 前缀
-                
-                if ($data === '[DONE]') {
-                    dLog('deepseek')->info('收到结束标记');
-                    break 2; // 跳出两层循环
-                }
-
-                try {
-                    $json = json_decode($data, true);
-                    
-                    if (json_last_error() !== JSON_ERROR_NONE) {
-                        dLog('deepseek')->warning('JSON 解析错误: ' . json_last_error_msg(), ['data' => $data]);
-                        continue;
-                    }
-                    
-                    // dLog('deepseek')->info('成功解析 JSON', ['has_choices' => isset($json['choices'])]);
-                    
-                    if (isset($json['choices'][0]['delta'])) {
-                        $delta = $json['choices'][0]['delta'];
-                        
-                        // 处理思维链内容(仅 deepseek-reasoner 模型)
-                        // 注释掉以下内容则不返回d思维链内容
-                        if (isset($delta['reasoning_content'])) {
-                            $fullReasoningContent .= $delta['reasoning_content'];
-                            // dLog('deepseek')->info('输出思维链内容', ['length' => strlen($delta['reasoning_content'])]);
-                            yield [
-                                'type' => 'reasoning',
-                                'content' => $delta['reasoning_content'],
-                                'full_reasoning' => $fullReasoningContent
-                            ];
-                        }
-                        
-                        // 处理最终回答内容
-                        if (isset($delta['content'])) {
-                            $fullContent .= $delta['content'];
-                            // dLog('deepseek')->info('输出回答内容', ['length' => strlen($delta['content'])]);
-                            yield [
-                                'type' => 'content',
-                                'content' => $delta['content'],
-                                // 'full_content' => $fullContent
-                            ];
-                        }
-                    }
-                    
-                    // 获取使用统计信息
-                    if (isset($json['usage'])) {
-                        $usage = $json['usage'];
-                        dLog('deepseek')->info('收到使用统计', ['usage' => $usage]);
-                    }
-                    
-                } catch (\Exception $e) {
-                    dLog('deepseek')->error('解析流式响应失败: ' . $e->getMessage(), ['line' => $line]);
-                    continue;
+        
+        if (in_array($model, ['deepseek-reasoner', 'deepseek-chat'])) {
+            // R1模型使用DeepSeek API
+            $streamGenerator = $this->deepSeekStreamResponse($post_data);
+        } else {
+            // V3模型使用火山引擎API
+            $streamGenerator = $this->volcEngineChatCompletion($post_data);
+        }
+
+        // 处理流式输出
+        foreach ($streamGenerator as $chunk) {
+            if (isset($chunk['type'])) {
+                if ($chunk['type'] === 'done') {
+                    // 最终结果
+                    $fullContent = $chunk['full_content'];
+                    $fullReasoningContent = $chunk['full_reasoning'];
+                    $usage = isset($chunk['usage']) ? $chunk['usage'] : [];
+                } else {
+                    // 逐块yield数据
+                    yield $chunk;
                 }
             }
         }
 
-        dLog('deepseek')->info('流式读取完成', [
-            'content_length' => strlen($fullContent),
-            'reasoning_length' => strlen($fullReasoningContent)
-        ]);
         dLog('deepseek')->info('完整内容: '.$fullContent);
 
         // 处理完整内容并返回最终结果
@@ -2344,108 +2382,34 @@ class DeepSeekService
             'stream'            => true  // 启用流式输出
         ];
 
-        $client = new Client(['timeout' => 1200, 'verify' => false]);
-        $response = $client->post($this->url, [
-            'json' => $post_data, 
-            'headers' => $this->headers,
-            'stream' => true  // 启用流式响应
-        ]);
-        
-        $body = $response->getBody();
+        // 根据模型类型选择调用方法
         $fullContent = '';
         $fullReasoningContent = '';
         $usage = [];
-        $buffer = '';
-
-        dLog('deepseek')->info('开始读取 DeepSeek 流式响应');
-
-        // 逐行读取流式响应
-        while (!$body->eof()) {
-            $chunk = $body->read(1024); // 每次读取 1KB
-            $buffer .= $chunk;
-            
-            // 按行分割
-            $lines = explode("\n", $buffer);
-            // 保留最后一个不完整的行
-            $buffer = array_pop($lines);
-            
-            foreach ($lines as $line) {
-                $line = trim($line);
-                
-                // 跳过空行
-                if (empty($line)) {
-                    continue;
-                }
-                
-                // dLog('deepseek')->info('收到原始行数据', ['line' => $line]);
-                
-                // 检查是否是 SSE 数据行
-                if (strpos($line, 'data: ') !== 0) {
-                    dLog('deepseek')->warning('非 SSE 数据行', ['line' => $line]);
-                    continue;
-                }
-
-                $data = substr($line, 6); // 移除 "data: " 前缀
-                
-                if ($data === '[DONE]') {
-                    dLog('deepseek')->info('收到结束标记');
-                    break 2; // 跳出两层循环
-                }
-
-                try {
-                    $json = json_decode($data, true);
-                    
-                    if (json_last_error() !== JSON_ERROR_NONE) {
-                        dLog('deepseek')->warning('JSON 解析错误: ' . json_last_error_msg(), ['data' => $data]);
-                        continue;
-                    }
-                    
-                    // dLog('deepseek')->info('成功解析 JSON', ['has_choices' => isset($json['choices'])]);
-                    
-                    if (isset($json['choices'][0]['delta'])) {
-                        $delta = $json['choices'][0]['delta'];
-                        
-                        // 处理思维链内容(仅 deepseek-reasoner 模型)
-                        // 注释掉以下内容则不返回d思维链内容
-                        if (isset($delta['reasoning_content'])) {
-                            $fullReasoningContent .= $delta['reasoning_content'];
-                            // dLog('deepseek')->info('输出思维链内容', ['length' => strlen($delta['reasoning_content'])]);
-                            yield [
-                                'type' => 'reasoning',
-                                'content' => $delta['reasoning_content'],
-                                'full_reasoning' => $fullReasoningContent
-                            ];
-                        }
-                        
-                        // 处理最终回答内容
-                        if (isset($delta['content'])) {
-                            $fullContent .= $delta['content'];
-                            // dLog('deepseek')->info('输出回答内容', ['length' => strlen($delta['content'])]);
-                            yield [
-                                'type' => 'content',
-                                'content' => $delta['content'],
-                                // 'full_content' => $fullContent
-                            ];
-                        }
-                    }
-                    
-                    // 获取使用统计信息
-                    if (isset($json['usage'])) {
-                        $usage = $json['usage'];
-                        dLog('deepseek')->info('收到使用统计', ['usage' => $usage]);
-                    }
-                    
-                } catch (\Exception $e) {
-                    dLog('deepseek')->error('解析流式响应失败: ' . $e->getMessage(), ['line' => $line]);
-                    continue;
+        
+        if (in_array($model, ['deepseek-reasoner', 'deepseek-chat'])) {
+            // R1模型使用DeepSeek API
+            $streamGenerator = $this->deepSeekStreamResponse($post_data);
+        } else {
+            // V3模型使用火山引擎API
+            $streamGenerator = $this->volcEngineChatCompletion($post_data);
+        }
+
+        // 处理流式输出
+        foreach ($streamGenerator as $chunk) {
+            if (isset($chunk['type'])) {
+                if ($chunk['type'] === 'done') {
+                    // 最终结果
+                    $fullContent = $chunk['full_content'];
+                    $fullReasoningContent = $chunk['full_reasoning'];
+                    $usage = isset($chunk['usage']) ? $chunk['usage'] : [];
+                } else {
+                    // 逐块yield数据
+                    yield $chunk;
                 }
             }
         }
 
-        dLog('deepseek')->info('流式读取完成', [
-            'content_length' => strlen($fullContent),
-            'reasoning_length' => strlen($fullReasoningContent)
-        ]);
         dLog('deepseek')->info('完整内容: '.$fullContent);
 
         // 处理完整内容并返回最终结果
@@ -2776,109 +2740,34 @@ class DeepSeekService
             'stream'            => true  // 启用流式输出
         ];
 
-        $client = new Client(['timeout' => 1200, 'verify' => false]);
-        $response = $client->post($this->url, [
-            'json' => $post_data, 
-            'headers' => $this->headers,
-            'stream' => true  // 启用流式响应
-        ]);
-        
-        $body = $response->getBody();
+        // 根据模型类型选择调用方法
         $fullContent = '';
         $fullReasoningContent = '';
         $usage = [];
-        $buffer = '';
-
-        dLog('deepseek')->info('开始读取 DeepSeek 流式响应');
-
-        // 逐行读取流式响应
-        while (!$body->eof()) {
-            $chunk = $body->read(1024); // 每次读取 1KB
-            $buffer .= $chunk;
-            
-            // 按行分割
-            $lines = explode("\n", $buffer);
-            // 保留最后一个不完整的行
-            $buffer = array_pop($lines);
-            
-            foreach ($lines as $line) {
-                $line = trim($line);
-                
-                // 跳过空行
-                if (empty($line)) {
-                    continue;
-                }
-                
-                // dLog('deepseek')->info('收到原始行数据', ['line' => $line]);
-                
-                // 检查是否是 SSE 数据行
-                if (strpos($line, 'data: ') !== 0) {
-                    dLog('deepseek')->warning('非 SSE 数据行', ['line' => $line]);
-                    continue;
-                }
-
-                $data = substr($line, 6); // 移除 "data: " 前缀
-                
-                if ($data === '[DONE]') {
-                    dLog('deepseek')->info('收到结束标记');
-                    break 2; // 跳出两层循环
-                }
-
-                try {
-                    $json = json_decode($data, true);
-                    
-                    if (json_last_error() !== JSON_ERROR_NONE) {
-                        dLog('deepseek')->warning('JSON 解析错误: ' . json_last_error_msg(), ['data' => $data]);
-                        continue;
-                    }
-                    
-                    // dLog('deepseek')->info('成功解析 JSON', ['has_choices' => isset($json['choices'])]);
-                    
-                    if (isset($json['choices'][0]['delta'])) {
-                        $delta = $json['choices'][0]['delta'];
-                        
-                        // 处理思维链内容(仅 deepseek-reasoner 模型)
-                        // 注释掉以下内容则不返回d思维链内容
-                        if (isset($delta['reasoning_content'])) {
-                            $fullReasoningContent .= $delta['reasoning_content'];
-                            // dLog('deepseek')->info('输出思维链内容', ['length' => strlen($delta['reasoning_content'])]);
-                            yield [
-                                'type' => 'reasoning',
-                                'content' => $delta['reasoning_content'],
-                                'full_reasoning' => $fullReasoningContent
-                            ];
-                        }
-                        
-                        // 处理最终回答内容
-                        if (isset($delta['content'])) {
-                            $fullContent .= $delta['content'];
-                            // dLog('deepseek')->info('输出回答内容', ['length' => strlen($delta['content'])]);
-                            yield [
-                                'type' => 'content',
-                                'content' => $delta['content'],
-                                // 'full_content' => $fullContent
-                            ];
-                        }
-                    }
-                    
-                    // 获取使用统计信息
-                    if (isset($json['usage'])) {
-                        $usage = $json['usage'];
-                        dLog('deepseek')->info('收到使用统计', ['usage' => $usage]);
-                    }
-                    
-                } catch (\Exception $e) {
-                    dLog('deepseek')->error('解析流式响应失败: ' . $e->getMessage(), ['line' => $line]);
-                    continue;
+        
+        if (in_array($model, ['deepseek-reasoner', 'deepseek-chat'])) {
+            // R1模型使用DeepSeek API
+            $streamGenerator = $this->deepSeekStreamResponse($post_data);
+        } else {
+            // V3模型使用火山引擎API
+            $streamGenerator = $this->volcEngineChatCompletion($post_data);
+        }
+
+        // 处理流式输出
+        foreach ($streamGenerator as $chunk) {
+            if (isset($chunk['type'])) {
+                if ($chunk['type'] === 'done') {
+                    // 最终结果
+                    $fullContent = $chunk['full_content'];
+                    $fullReasoningContent = $chunk['full_reasoning'];
+                    $usage = isset($chunk['usage']) ? $chunk['usage'] : [];
+                } else {
+                    // 逐块yield数据
+                    yield $chunk;
                 }
             }
         }
 
-        dLog('deepseek')->info('流式读取完成', [
-            'content_length' => strlen($fullContent),
-            'reasoning_length' => strlen($fullReasoningContent)
-        ]);
-
         dLog('deepseek')->info('完整内容: '.$fullContent);
         // 处理完整内容并返回最终结果
         $episode_arr = handleEpisodeContent($fullContent);
@@ -3865,5 +3754,246 @@ class DeepSeekService
         
         return null;
     }
-    
+
+    /**
+     * 调用火山引擎对话(Chat) API
+     * 
+     * @param array $params 包含以下参数:
+     *   - model: 模型ID(必填)
+     *   - messages: 消息列表(必填)
+     *   - stream: 是否流式输出(可选,默认false)
+     *   - max_tokens: 最大输出token数(可选)
+     *   - temperature: 采样温度(可选,默认1)
+     *   - top_p: 核采样概率(可选,默认0.7)
+     *   - frequency_penalty: 频率惩罚系数(可选,默认0)
+     *   - presence_penalty: 存在惩罚系数(可选,默认0)
+     *   - stop: 停止词(可选)
+     *   - tools: 工具列表(可选)
+     * @return array|Generator 非流式返回数组,流式返回生成器
+     */
+    public function volcEngineChatCompletion(array $params)
+    {
+        $apiKey = env('VOLC_AI_API_KEY');
+        
+        if (empty($apiKey)) {
+            Utils::throwError('20003:火山引擎API Key未配置,请检查环境变量VOLC_AI_API_KEY');
+        }
+
+        // 构建请求参数
+        $requestData = [
+            'model' => $params['model'] ?? 'deepseek-v3-2-251201',
+            'messages' => $params['messages'] ?? [],
+        ];
+
+        if (!in_array($requestData['model'], $this->valid_text_models)) {
+            Utils::throwError('20003:该模型不支持!');
+        }
+
+        // 添加可选参数
+        if (isset($params['stream'])) {
+            $requestData['stream'] = $params['stream'];
+        }
+        
+        if (isset($params['stream_options'])) {
+            $requestData['stream_options'] = $params['stream_options'];
+        }
+
+        if (isset($params['max_tokens'])) {
+            $requestData['max_tokens'] = $params['max_tokens'];
+        }
+
+        if (isset($params['max_completion_tokens'])) {
+            $requestData['max_completion_tokens'] = $params['max_completion_tokens'];
+        }
+
+        if (isset($params['temperature'])) {
+            $requestData['temperature'] = $params['temperature'];
+        }
+
+        if (isset($params['top_p'])) {
+            $requestData['top_p'] = $params['top_p'];
+        }
+
+        if (isset($params['frequency_penalty'])) {
+            $requestData['frequency_penalty'] = $params['frequency_penalty'];
+        }
+
+        if (isset($params['presence_penalty'])) {
+            $requestData['presence_penalty'] = $params['presence_penalty'];
+        }
+
+        if (isset($params['stop'])) {
+            $requestData['stop'] = $params['stop'];
+        }
+
+        if (isset($params['tools'])) {
+            $requestData['tools'] = $params['tools'];
+        }
+
+        if (isset($params['tool_choice'])) {
+            $requestData['tool_choice'] = $params['tool_choice'];
+        }
+
+        if (isset($params['response_format'])) {
+            $requestData['response_format'] = $params['response_format'];
+        }
+
+        if (isset($params['thinking'])) {
+            $requestData['thinking'] = $params['thinking'];
+        }
+
+        if (isset($params['reasoning_effort'])) {
+            $requestData['reasoning_effort'] = $params['reasoning_effort'];
+        }
+
+        dLog('deepseek')->info('火山引擎对话API请求参数: ', $requestData);
+
+        $client = new Client(['verify' => false, 'timeout' => 1200]);
+
+        try {
+            // 判断是否为流式输出
+            $isStream = isset($params['stream']) && $params['stream'] === true;
+
+            if ($isStream) {
+                // 流式输出
+                return $this->volcEngineChatCompletionStream($client, $apiKey, $requestData);
+            } else {
+                // 非流式输出
+                $response = $client->post('https://ark.cn-beijing.volces.com/api/v3/chat/completions', [
+                    'headers' => [
+                        'Authorization' => 'Bearer ' . $apiKey,
+                        'Content-Type' => 'application/json',
+                    ],
+                    'json' => $requestData
+                ]);
+
+                $responseData = json_decode($response->getBody(), true);
+                dLog('deepseek')->info('火山引擎对话API响应: ', $responseData);
+
+                return $responseData;
+            }
+        } catch (\Exception $e) {
+            dLog('deepseek')->error('火山引擎对话API请求异常: ' . $e->getMessage());
+            Utils::throwError('20003:火山引擎对话API请求失败: ' . $e->getMessage());
+        }
+    }
+
+    /**
+     * 火山引擎对话API流式输出
+     * 
+     * @param Client $client HTTP客户端
+     * @param string $apiKey API密钥
+     * @param array $requestData 请求数据
+     * @return Generator
+     */
+    private function volcEngineChatCompletionStream($client, $apiKey, $requestData)
+    {
+        try {
+            $response = $client->post('https://ark.cn-beijing.volces.com/api/v3/chat/completions', [
+                'headers' => [
+                    'Authorization' => 'Bearer ' . $apiKey,
+                    'Content-Type' => 'application/json',
+                ],
+                'json' => $requestData,
+                'stream' => true
+            ]);
+
+            $body = $response->getBody();
+            $buffer = '';
+            $fullContent = '';
+            $fullReasoningContent = '';
+            $usage = [];
+
+            dLog('deepseek')->info('开始读取火山引擎对话API流式响应');
+
+            // 逐行读取流式响应
+            while (!$body->eof()) {
+                $chunk = $body->read(1024);
+                $buffer .= $chunk;
+                
+                // 按行分割
+                $lines = explode("\n", $buffer);
+                $buffer = array_pop($lines);
+                
+                foreach ($lines as $line) {
+                    $line = trim($line);
+                    
+                    if (empty($line)) {
+                        continue;
+                    }
+                    
+                    // 检查是否是SSE数据行
+                    if (strpos($line, 'data: ') !== 0) {
+                        continue;
+                    }
+
+                    $data = substr($line, 6);
+                    
+                    if ($data === '[DONE]') {
+                        dLog('deepseek')->info('收到结束标记');
+                        break 2;
+                    }
+
+                    try {
+                        $json = json_decode($data, true);
+                        
+                        if (json_last_error() !== JSON_ERROR_NONE) {
+                            dLog('deepseek')->warning('JSON解析错误: ' . json_last_error_msg());
+                            continue;
+                        }
+                        
+                        if (isset($json['choices'][0]['delta'])) {
+                            $delta = $json['choices'][0]['delta'];
+                            
+                            // 处理思维链内容
+                            if (isset($delta['reasoning_content'])) {
+                                $fullReasoningContent .= $delta['reasoning_content'];
+                                yield [
+                                    'type' => 'reasoning',
+                                    'content' => $delta['reasoning_content'],
+                                    'full_reasoning' => $fullReasoningContent
+                                ];
+                            }
+                            
+                            // 处理回答内容
+                            if (isset($delta['content'])) {
+                                $fullContent .= $delta['content'];
+                                yield [
+                                    'type' => 'content',
+                                    'content' => $delta['content'],
+                                ];
+                            }
+                        }
+                        
+                        // 获取使用统计信息
+                        if (isset($json['usage'])) {
+                            $usage = $json['usage'];
+                            dLog('deepseek')->info('收到使用统计', ['usage' => $usage]);
+                        }
+                        
+                    } catch (\Exception $e) {
+                        dLog('deepseek')->error('解析流式响应失败: ' . $e->getMessage());
+                        continue;
+                    }
+                }
+            }
+
+            dLog('deepseek')->info('流式读取完成', [
+                'content_length' => strlen($fullContent),
+                'reasoning_length' => strlen($fullReasoningContent)
+            ]);
+
+            yield [
+                'type' => 'done',
+                'full_content' => $fullContent,
+                'full_reasoning' => $fullReasoningContent,
+                'usage' => $usage
+            ];
+
+        } catch (\Exception $e) {
+            dLog('deepseek')->error('火山引擎对话API流式请求异常: ' . $e->getMessage());
+            throw $e;
+        }
+    }
+
 }