m***@163.com
m***@163.com
  • 发布:2023-06-21 18:30
  • 更新:2023-06-25 09:30
  • 阅读:232

SSEChannel的问题

分类:uniCloud

响应流数据,在本地云函数,测试没有问题,但在测试环境,线上去链接云端云函数,使用SSEChannel推送数据,当响应超过1分钟后,之后再响应大约三四秒种左右的时间,响应数据就会中断

2023-06-21 18:30 负责人:无 分享
已邀请:
DCloud_uniCloud_WYQ

DCloud_uniCloud_WYQ

云函数在执行超出1分钟后会被终止,如果有生成非常长的内容的需求可以考虑使用uni-ai计费网关。

流式响应需要云端持续从服务商接收数据并发送给客户端,这需要云函数一直保持运行。如果使用uni-ai计费网关则无需云函数保持运行,在请求发送给DCloud服务器后DCloud服务器会使用推送通道将结果通知给客户端,而云函数可以再继续处理下一个请求或者直接休眠,从而节省大量云函数资源(GBs)。

  • m***@163.com (作者)

    有采用uniapp官方的uni-ai-chat,也是存在这个问题,于是手写了下直接请求openai的方式,通过打印,发现问题出现在推送通道上,云函数超过一分钟,推送通道是可以保持继续执行的,但继续推送几秒钟后,直接就休止了(采用的model是“gpt-3.5-turbo-16k”),即不执行err的监听,也不执行end的监听。

    2023-06-25 09:42

  • m***@163.com (作者)

    客户端代码,是这样的, sseChannel.on('end', message => {

    // 监听end事件,如果云端执行end时传了message,会在客户端end事件内收到传递的消息

    console.log('on end', message)

    this.status = 'no-more'

    //执行SSE关闭事件

    this.close()

    })

    2023-06-25 09:57

m***@163.com

m***@163.com (作者)

您好,您答复的内容中有“无需云函数保持运行,在请求发送给DCloud服务器后DCloud服务器会使用推送通道将结果通知给客户端”,
这是我的云对象代码,我有使用推送通道将结果通知客户端的,
目前的问题是,大约63秒,64秒时,推送自己就停止了,
当我不使用推送通道的时候,打印是正常的。

let reply = ""  
            return new Promise(async (resolve, reject) => {  
                // 反序列化sseChannel  
                const channel = uniCloud.deserializeSSEChannel(sseChannel)  

                const res = await openai.createChatCompletion({  
                    model: "gpt-3.5-turbo-16k",  
                    messages,  
                    presence_penalty: 0,  
                    stream: true,  
                    temperature: 0.5  
                }, {  
                    responseType: "stream"  
                });  
                res.data.on("data", async (data) => {  

                    try {  
                        // 对每次推送的数据进行格式化, 得到的是 JSON 字符串、或者 [DONE] 表示流结束  
                        let message = data.toString().trim()  

                        message = message.replace('data: [DONE]', '');  
                        // 将所有"data:"替换为",data:",确保每个"data:"前面都有逗号  
                        message = message.replace(/data:/g, ",data:");  

                        const occurrences = countOccurrences(message, 'data:');  

                        if (occurrences == 1) {  
                            //说明数据正常  
                            message = message.replace(/^,data:/, '');  
                            const parsed = JSON.parse(message);  
                            if (parsed.choices[0].finish_reason == "stop") {  
                                // 结束了  
                                await channel.end()  
                                // 返回处理结果  
                                resolve({  
                                    errCode: 0,  
                                    "content": reply,  
                                    "role": "assistant"  
                                })  
                            } else {  
                                const token = parsed.choices[0].delta.content  
                                reply += token  
                                console.log(token);  
                                await channel.write(token)  
                            }  

                        } else {  

                            //异常结构数据  
                            let parts = message.split(",data:");  
                            // 使用 map() 方法对每个部分进行处理并创建新的对象  
                            let result = parts.map(part => {  
                                if (part.trim() !== "") {  
                                    return JSON.parse(part.trim());  
                                }  
                            }).filter(Boolean)  
                          for (let item of result) {  
                            if (item.choices[0].finish_reason == "stop") {  
                              // 结束了  
                              await channel.end();  
                              // 返回处理结果  
                              resolve({  
                                errCode: 0,  
                                content: reply,  
                                role: "assistant"  
                              });  
                            } else {  
                              const token = item.choices[0].delta.content;  
                              reply += token;  
                              console.log(token);  
                              await channel.write(token);  
                            }  
                          }  
                        }  
                    } catch (e) {  
                        //TODO handle the exception  
                        reject(e)  
                    }  

                })  

                // 返回错误  
                res.data.on('error', async (error) => {  
                    console.error('---error----', error)  
                    reject(error)  
                })  
            })
  • DCloud_uniCloud_WYQ

    问题是云函数一次只能执行60秒,所以没有执行到end方法

    2023-06-27 11:23

要回复问题请先登录注册