Skip to content

Commit 8b12237

Browse files
committed
支持stream流式响应解决复杂问题超时报错的问题
1 parent b493f4e commit 8b12237

24 files changed

+280
-164
lines changed

app/Http/Controllers/ChatController.php

Lines changed: 68 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@
77
use Illuminate\Http\Request;
88
use Illuminate\Support\Str;
99
use Illuminate\Http\JsonResponse;
10+
use Illuminate\Support\Facades\Log;
1011
use Illuminate\Support\Facades\Storage;
1112
use Illuminate\Validation\Rules\File;
1213
use Inertia\Inertia;
14+
use PHPUnit\Event\Runtime\PHP;
1315

1416
class ChatController extends Controller
1517
{
18+
private $preset = ['role' => 'system', 'content' => 'You are GeekChat - A ChatGPT clone. Answer as concisely as possible. Using Simplified Chinese as the first language.'];
19+
1620
public function index()
1721
{
1822
return Inertia::render('Chat');
@@ -32,30 +36,68 @@ public function chat(Request $request): JsonResponse
3236
$request->validate([
3337
'prompt' => 'required|string'
3438
]);
39+
$messages = $request->session()->get('messages', [$this->preset]);
40+
$userMessage = ['role' => 'user', 'content' => $request->input('prompt')];
41+
$messages[] = $userMessage;
42+
$request->session()->put('messages', $messages); // 基于session存储当前会话信息
43+
$chatId = Str::uuid(); // 生成一个唯一聊天ID作为下次请求的凭证
44+
$request->session()->put('chat_id', $chatId);
45+
return response()->json(['chat_id' => $chatId, 'message' => $userMessage]);
46+
}
3547

36-
$messages = $request->session()->get('messages', [
37-
['role' => 'system', 'content' => 'You are GeekChat - A ChatGPT clone. Answer as concisely as possible. Using Simplified Chinese as the first language.']
38-
]);
39-
40-
$messages[] = ['role' => 'user', 'content' => $request->input('prompt')];
41-
42-
try {
43-
$response = OpenAI::chat([
44-
'model' => 'gpt-3.5-turbo',
45-
'messages' => $messages
46-
]);
47-
} catch (Exception $exception) {
48-
return $this->toJsonResponse($request, SYSTEM_ERROR, $messages);
48+
/**
49+
* Handle the stream response.
50+
*/
51+
public function stream(Request $request, string $chatId)
52+
{
53+
// 校验请求是否合法
54+
if ($request->session()->get('chat_id') != $chatId) {
55+
return abort(400);
4956
}
5057

51-
$result = json_decode($response);
52-
$respText = '';
53-
if (empty($result->choices[0]->message->content)) {
54-
$respText = '对不起,我没有理解你的意思,请重试';
55-
} else {
56-
$respText = $result->choices[0]->message->content;
58+
$messages = $request->session()->get('messages');
59+
60+
$params = [
61+
'model' => 'gpt-3.5-turbo',
62+
'messages' => $messages,
63+
'stream' => true,
64+
];
65+
66+
// 实时将流式响应数据发送到客户端
67+
$respData = '';
68+
header('Content-type: text/event-stream');
69+
header('Cache-Control: no-cache');
70+
OpenAI::chat($params, function ($ch, $data) use (&$respData) {
71+
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
72+
if ($httpCode >= 400) {
73+
echo "data: [ERROR] $httpCode";
74+
} else {
75+
$respData .= $data;
76+
echo $data;;
77+
ob_flush();
78+
flush();
79+
return strlen($data);
80+
}
81+
});
82+
83+
// 将响应数据存储到当前会话中以便刷新页面后可以看到聊天记录
84+
if (!empty($respData)) {
85+
$lines = explode("\n\n", $respData);
86+
$respText = '';
87+
foreach ($lines as $line) {
88+
$data = substr($line, 5); // 每行数据结构是 data: {...}
89+
if ($data === '[DONE]') {
90+
break;
91+
} else {
92+
$segment = json_decode($data);
93+
if (isset($segment->choices[0]->delta->content)) {
94+
$respText .= $segment->choices[0]->delta->content;
95+
}
96+
}
97+
}
98+
$messages[] = ['role' => 'assistant', 'content' => $respText];
99+
$request->session()->put('messages', $messages);
57100
}
58-
return $this->toJsonResponse($request, $respText, $messages);
59101
}
60102

61103
/**
@@ -101,25 +143,12 @@ public function audio(Request $request): JsonResponse
101143
}
102144

103145
// 接下来的流程和 ChatGPT 一样
104-
$reqMessage = ['role' => 'user', 'content' => $result->text];
105-
$messages[] = $reqMessage;
106-
try {
107-
$response = OpenAI::chat([
108-
'model' => 'gpt-3.5-turbo',
109-
'messages' => $messages
110-
]);
111-
} catch (Exception $exception) {
112-
return $this->toJsonResponse($request, SYSTEM_ERROR, $messages, true);
113-
}
114-
115-
$result = json_decode($response);
116-
$respText = '';
117-
if (empty($result->choices[0]->message->content)) {
118-
$respText = '对不起,我没有听明白你的意思,请再说一遍';
119-
} else {
120-
$respText = $result->choices[0]->message->content;
121-
}
122-
return $this->toJsonResponse($request, $respText, $messages, true);
146+
$userMessage = ['role' => 'user', 'content' => $result->text];
147+
$messages[] = $userMessage;
148+
$request->session()->put('messages', $messages);
149+
$chatId = Str::uuid();
150+
$request->session()->put('chat_id', $chatId);
151+
return response()->json(['chat_id' => $chatId, 'message' => $userMessage]); // 将语音识别结果先返回给客户端
123152
}
124153

125154
/**
@@ -131,17 +160,4 @@ public function reset(Request $request): JsonResponse
131160

132161
return response()->json(['status' => 'ok']);
133162
}
134-
135-
private function toJsonResponse($request, $message, $messages, $isAudio = false): JsonResponse
136-
{
137-
$respMessage = ['role' => 'assistant', 'content' => $message];
138-
$messages[] = $respMessage;
139-
$request->session()->put('messages', $messages);
140-
if (!$isAudio) {
141-
return response()->json($respMessage);
142-
}
143-
// 语音模式下,返回最后两条消息(第一条是语音解析出来的文本,第二条是机器人的回复)
144-
$chatMessage = array_slice($messages, -2);
145-
return response()->json($chatMessage);
146-
}
147163
}

app/Providers/RouteServiceProvider.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ protected function configureRateLimiting(): void
4545
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
4646
});
4747
RateLimiter::for('chat', function (Request $request) {
48-
return Limit::perHour(60)->by($request->user()?->id ?: $request->ip());
48+
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
4949
});
5050
RateLimiter::for('audio', function (Request $request) {
51-
return Limit::perHour(30)->by($request->user()?->id ?: $request->ip());
51+
return Limit::perMinute(30)->by($request->user()?->id ?: $request->ip());
5252
});
5353
}
5454
}

public/build/assets/ApplicationLogo-ed47ee4d.js renamed to public/build/assets/ApplicationLogo-d2561183.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)