随着 AI 聊天的兴起,流式展现数据的需求变得更常见。前端 EventSource 的使用频率也会更高。接openai,文心一言,通义千问这些接口,并以流式的方式在页面展现出来。就得自己的接口服务端也以流式的方式返回给自己的前端,自己的服务器端接它们,让它们也要以流的方式返回。这个时候服务端不仅要做接口的对接和数据的转发,还得做数据的解析和存储。
这里前端以 vue3,后端使用 laravel 的方式,来简单介绍下怎么搞。
前端 API 选择
https://developer.mozilla.org/zh-CN/docs/Web/API/EventSource
前端选择 fetch,并没有选择 EventSource。
因为 fetch 本身就可以支持 EventSource 的方式接受数据,使用方式也会像使用 axios 类似。而单纯 EventSource 的使用会收到请求方式,不能自定义Header头,连接数目等方式的限制。fetch 就像在调用接口,EventSource 就像是 websocket。
EventSource 示例:
const sse = new EventSource("/api/v1/sse");
sse.addEventListener("notice", (e) => {
console.log(e.data);
});
sse.addEventListener("update", (e) => {
console.log(e.data);
});
sse.addEventListener("message", (e) => {
console.log(e.data);
});
fetch 示例:
const data = { username: "example" };
fetch("https://example.com/profile", {
method: "POST", // or 'PUT'
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
})
.then((response) => response.json())
.then((data) => {
console.log("Success:", data);
})
.catch((error) => {
console.error("Error:", error);
});
后端 API 选择
后端选择 GuzzleHttp\Client
, 它支持流式的方式去请求接口。
示列
use GuzzleHttp\Client;
use Psr\Http\Message\StreamInterface;
$client = new Client();
$response = $client->request(
'post',
'https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation',
[
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'xxx',
],
'json' => [
'model' => 'xxx',
],
'stream' => true
]
);
return response()->stream(function () use ($response) {
ob_start();
$stream = $response->getBody();
if ($stream instanceof StreamInterface) {
while (!$stream->eof()) { // 当没有到达流的末尾时
$chunk = $stream->read(1024); // 每次读取 1024 字节的数据块
}
ob_end_flush();
// 关闭流
$stream->close();
} else {
// 处理错误情况
echo 'Failed to get stream from response.';
}
}, 200, [
'Content-Type' => 'text/event-stream',
'Cache-Control' => 'no-cache',
'Connection' => 'keep-alive',
'X-Accel-Buffering' => 'no'
]);