2024-07-24 17:16:41 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
|
|
|
|
use App\Http\Controllers\Controller;
|
2024-07-24 18:53:47 +00:00
|
|
|
use App\LLM\Qwen;
|
2024-07-24 17:16:41 +00:00
|
|
|
use App\Models\Chat;
|
2024-07-24 18:53:47 +00:00
|
|
|
use App\Repositories\LLM\ChatEnum;
|
|
|
|
use App\Repositories\LLM\History;
|
|
|
|
use App\Repositories\LLM\HumanMessage;
|
2024-07-25 07:05:06 +00:00
|
|
|
use GuzzleHttp\Exception\GuzzleException;
|
2024-07-24 17:16:41 +00:00
|
|
|
use Illuminate\Http\Request;
|
|
|
|
use Illuminate\Support\Facades\Cache;
|
2024-07-24 18:53:47 +00:00
|
|
|
use Illuminate\Support\Facades\Log;
|
2024-07-24 17:16:41 +00:00
|
|
|
use Illuminate\Support\Str;
|
|
|
|
|
|
|
|
class ChatHistoryController extends Controller
|
|
|
|
{
|
2024-07-24 18:53:47 +00:00
|
|
|
private string $stream_prefix_id = 'chat_stream_id:';
|
|
|
|
|
|
|
|
private string $stream_random_id_prefix = 'chat_stream_random_id:';
|
|
|
|
|
2024-07-24 17:16:41 +00:00
|
|
|
/**
|
|
|
|
* Display a listing of the resource.
|
|
|
|
*/
|
|
|
|
public function index(Chat $chat)
|
|
|
|
{
|
|
|
|
return $this->success(
|
2024-07-24 18:53:47 +00:00
|
|
|
$chat->histories()->latest()->get()
|
2024-07-24 17:16:41 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
2024-07-24 18:53:47 +00:00
|
|
|
if ($last_history->role == ChatEnum::Human) {
|
2024-07-24 17:16:41 +00:00
|
|
|
// 不允许发送消息
|
|
|
|
return $this->badRequest('你已经回复过了,请等待 AI 响应。');
|
|
|
|
}
|
|
|
|
|
|
|
|
// 检查缓存是否存在
|
2024-07-24 18:53:47 +00:00
|
|
|
$last_stream_key = $this->stream_prefix_id.$chat->id;
|
2024-07-24 17:16:41 +00:00
|
|
|
|
|
|
|
// 如果存在
|
|
|
|
if (Cache::has($last_stream_key)) {
|
|
|
|
return $this->conflict('上一个流信息还没有获取,请等待一分钟后重试。');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$request->validate([
|
|
|
|
'message' => 'required',
|
|
|
|
]);
|
|
|
|
|
2024-07-24 18:53:47 +00:00
|
|
|
$history = $chat->histories()->create([
|
2024-07-24 17:16:41 +00:00
|
|
|
'content' => $request->input('message'),
|
2024-07-24 18:53:47 +00:00
|
|
|
'role' => ChatEnum::Human,
|
2024-07-24 17:16:41 +00:00
|
|
|
]);
|
|
|
|
|
2024-07-24 18:53:47 +00:00
|
|
|
// 随机生成一个 ID
|
2024-07-24 17:16:41 +00:00
|
|
|
$random_id = Str::random(20);
|
|
|
|
|
2024-07-24 18:53:47 +00:00
|
|
|
// 缓存 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);
|
2024-07-24 17:16:41 +00:00
|
|
|
|
|
|
|
return $this->success([
|
|
|
|
'stream_url' => route('chat-stream', $random_id),
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2024-07-24 18:53:47 +00:00
|
|
|
/**
|
|
|
|
* @throws \Exception
|
|
|
|
*/
|
2024-07-24 17:16:41 +00:00
|
|
|
public function stream(string $stream_id)
|
|
|
|
{
|
2024-07-24 18:53:47 +00:00
|
|
|
$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);
|
2024-07-25 07:05:06 +00:00
|
|
|
$llm->setUser($chat->user);
|
2024-07-24 18:53:47 +00:00
|
|
|
|
|
|
|
$last_human_message = $histories[$histories->count() - 1];
|
|
|
|
|
|
|
|
$history->addMessage(new HumanMessage($last_human_message));
|
|
|
|
|
2024-07-25 07:05:06 +00:00
|
|
|
try {
|
|
|
|
$stream = $llm->streamResponse();
|
|
|
|
} catch (GuzzleException) {
|
|
|
|
return $this->badRequest('请求失败,请重试。');
|
|
|
|
}
|
2024-07-24 18:53:47 +00:00
|
|
|
|
|
|
|
$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;
|
2024-07-24 17:16:41 +00:00
|
|
|
}
|
|
|
|
}
|