From 6a44d27fd391030cd70aa528e0b0e381b12a233d Mon Sep 17 00:00:00 2001 From: ZF sun <34314687@qq.com> Date: Sat, 6 Dec 2025 16:54:34 +0800 Subject: [PATCH] =?UTF-8?q?test(addon/aikefu):=20=E8=BF=98=E5=8E=9F?= =?UTF-8?q?=E5=8E=9F=E5=85=88=E7=9A=84kefu.app=20=E6=96=B9=E4=BE=BF?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/api/controller/Kefu.php | 170 ++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) diff --git a/src/app/api/controller/Kefu.php b/src/app/api/controller/Kefu.php index 434834e21..08f464202 100644 --- a/src/app/api/controller/Kefu.php +++ b/src/app/api/controller/Kefu.php @@ -131,6 +131,7 @@ class Kefu extends BaseApi ], 500); } } + /** * 获得AI服务的配置信息发送给客户端 * @return \think\response\Json @@ -325,6 +326,175 @@ class Kefu extends BaseApi return $this->response($this->error('请求失败:' . $e->getMessage())); } } + /** + * 智能客服聊天流式接口 + */ + public function chatStream() + { + // 获取请求参数 + $message = $this->params['message'] ?? ''; + $user_id = $this->params['user_id'] ?? $this->member_id; + $conversation_id = $this->params['conversation_id'] ?? ''; + $stream = true; + + // (可选)获取站点ID和会员ID,可以通过事件数据传递 + $site_id = $this->params['uniacid'] ?? $this->site_id; // 使用 uniacid, 方便以后迁移,而且uniacid 是唯一的, site_id 不是,同时被params给过滤了 + $member_id = $this->params['member_id'] ?? $this->member_id; + $token = $this->params['token'] ?? $this->token; + + // 验证参数 + if (empty($message)) { + $this->sendStreamError('请输入消息内容'); + exit; + } + + // 准备事件数据 + $event_data = [ + 'message' => $message, + 'user_id' => $user_id, + 'conversation_id' => $conversation_id, + 'stream' => $stream, + 'site_id' =>$site_id, + 'member_id' => $member_id, + 'token' => $token, + 'request_id' => uniqid('stream_', true), + 'timestamp' => time(), + ]; + + try { + // 设置流式响应头 + $this->setStreamHeaders(); + + // 发送开始事件 + $this->sendStreamEvent('start', [ + 'request_id' => $event_data['request_id'], + 'timestamp' => $event_data['timestamp'], + 'message' => '开始处理请求' + ]); + + // 触发流式聊天事件 + $result = Event::trigger('KefuChatStream', $event_data); + + // 处理事件结果并流式输出 + $this->processStreamResults($result, $event_data); + + } catch (\Exception $e) { + $this->sendStreamError('请求失败:' . $e->getMessage()); + } + } + + /** + * 设置流式响应头 + */ + private function setStreamHeaders() + { + header('Content-Type: text/event-stream'); + header('Cache-Control: no-cache'); + header('Connection: keep-alive'); + header('Access-Control-Allow-Origin: *'); + header('Access-Control-Allow-Headers: Cache-Control, Content-Type'); + header('X-Accel-Buffering: no'); // 禁用nginx缓冲 + if (function_exists('apache_setenv')) { + apache_setenv('no-gzip', '1'); + } + } + + /** + * 发送流式事件 + */ + private function sendStreamEvent($event_type, $data) + { + $payload = [ + 'id' => uniqid(), + 'event' => $event_type, + 'timestamp' => time(), + 'data' => $data + ]; + + echo "event: {$event_type}\n"; + echo "data: " . json_encode($payload, JSON_UNESCAPED_UNICODE) . "\n\n"; + + if (ob_get_level()) { + ob_flush(); + } + flush(); + } + + /** + * 发送流式错误 + */ + private function sendStreamError($message) + { + if (!headers_sent()) { + $this->setStreamHeaders(); + } + + $this->sendStreamEvent('error', [ + 'error' => $message, + 'finished' => true + ]); + + exit; + } + + /** + * 处理流式结果 + */ + private function processStreamResults($results, $event_data) + { + try { + if (is_array($results) && !empty($results)) { + foreach ($results as $result) { + if (isset($result['type'])) { + switch ($result['type']) { + case 'chunk': + // 发送内容块 + $this->sendStreamEvent('message', [ + 'content' => $result['content'] ?? '', + 'conversation_id' => $result['conversation_id'] ?? $event_data['conversation_id'], + 'message_id' => $result['message_id'] ?? '', + 'finished' => $result['finished'] ?? false + ]); + break; + + case 'error': + // 发送错误 + $this->sendStreamEvent('error', [ + 'error' => $result['message'] ?? '未知错误', + 'conversation_id' => $result['conversation_id'] ?? $event_data['conversation_id'] + ]); + break; + + case 'complete': + // 发送完成事件 + $this->sendStreamEvent('complete', [ + 'conversation_id' => $result['conversation_id'] ?? $event_data['conversation_id'], + 'message_id' => $result['message_id'] ?? '', + 'usage' => $result['usage'] ?? [], + 'finish_reason' => $result['finish_reason'] ?? '' + ]); + break; + } + } + } + } else { + // 如果没有事件处理器或结果为空,发送完成事件 + $this->sendStreamEvent('complete', [ + 'conversation_id' => $event_data['conversation_id'], + 'message' => '暂无可用响应' + ]); + } + + // 发送结束事件 + $this->sendStreamEvent('end', [ + 'request_id' => $event_data['request_id'], + 'status' => 'completed' + ]); + + } catch (\Exception $e) { + $this->sendStreamError('处理流式结果失败:' . $e->getMessage()); + } + } /** * 创建新会话