success( $chat->histories()->latest()->get() ); } /** * Store a newly created resource in storage. */ public function store(Request $request, Chat $chat) { // 获取上一条记录 $last_history = $chat->histories()->orderBy('id', 'desc')->first(); // 如果存在 if ($last_history) { // 如果上一条是 user if ($last_history->role == ChatEnum::Human) { // 不允许发送消息 return $this->badRequest('你已经回复过了,请等待 AI 响应。'); } // 检查缓存是否存在 $last_stream_key = $this->stream_prefix_id.$chat->id; // 如果存在 if (Cache::has($last_stream_key)) { return $this->conflict('上一个流信息还没有获取,请等待一分钟后重试。'); } } $request->validate([ 'message' => 'required', ]); $history = $chat->histories()->create([ 'content' => $request->input('message'), 'role' => ChatEnum::Human, ]); // 随机生成一个 ID $random_id = Str::random(20); // 缓存 key $history_stream_key = $this->stream_prefix_id.$chat->id; Cache::put($history_stream_key, $random_id, 60); Cache::put($this->stream_random_id_prefix.$random_id, $chat->id, 60); return $this->success([ 'stream_url' => route('chat-stream', $random_id), ]); } /** * @throws \Exception */ public function stream(string $stream_id) { $cached = Cache::get($this->stream_random_id_prefix.$stream_id); if (! $cached) { return $this->badRequest('无效的流 ID。'); } $chat = Chat::with('assistant.tools', 'assistant')->findOrFail($cached); $histories = $chat->histories()->get(); // 如果为 0 if ($histories->count() == 0) { return $this->badRequest('请先发送消息。'); } $llm_histories = new History(); $llm_histories->setHistory($histories->toArray()); $llm = new Qwen(); $history = new History(); $tools = $chat->assistant->tools()->get(); $llm->setTools($tools); $llm->setHistory($history); $llm->setUser($chat->user); $last_human_message = $histories[$histories->count() - 1]; $history->addMessage(new HumanMessage($last_human_message)); try { $stream = $llm->streamResponse(); } catch (GuzzleException) { return $this->badRequest('请求失败,请重试。'); } $response = response()->stream(function () use (&$stream, &$chat) { // 循环输出 foreach ($stream as $item) { $data = []; if ($item->role == ChatEnum::Tool) { if ($item->processing) { $data = [ 'message' => '正在执行: '.$item->content, ]; } else { $data = [ 'message' => '结果: '.$item->content, ]; } } elseif ($item->role == ChatEnum::AssistantChunk) { $data = [ 'message' => $item->getLastAppend(), ]; } elseif ($item->role == ChatEnum::Assistant) { $chat->histories()->create([ 'content' => $item->content, 'role' => ChatEnum::Assistant, ]); Log::info('AI: '.$item->content); } echo 'data: '.json_encode($data, JSON_UNESCAPED_UNICODE)."\n\n"; ob_flush(); flush(); } }); Cache::forget($this->stream_random_id_prefix.$stream_id); Cache::forget($this->stream_prefix_id.$chat->id); $response->headers->set('Content-Type', 'text/event-stream'); $response->headers->set('Cache-Control', 'no-cache'); $response->headers->set('Connection', 'keep-alive'); return $response; } }