test(addon/aikefu): 还原原先的kefu.app 方便测试
This commit is contained in:
@@ -16,7 +16,7 @@ CREATE TABLE IF NOT EXISTS `lucky_aikefu_conversation` (
|
|||||||
KEY `site_id` (`site_id`),
|
KEY `site_id` (`site_id`),
|
||||||
KEY `user_id` (`user_id`),
|
KEY `user_id` (`user_id`),
|
||||||
KEY `conversation_id` (`conversation_id`)
|
KEY `conversation_id` (`conversation_id`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='智能客服会话表';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4_unicode_ci COMMENT='智能客服会话表';
|
||||||
|
|
||||||
-- 创建智能客服消息表
|
-- 创建智能客服消息表
|
||||||
CREATE TABLE IF NOT EXISTS `lucky_aikefu_message` (
|
CREATE TABLE IF NOT EXISTS `lucky_aikefu_message` (
|
||||||
@@ -33,4 +33,10 @@ CREATE TABLE IF NOT EXISTS `lucky_aikefu_message` (
|
|||||||
KEY `user_id` (`user_id`),
|
KEY `user_id` (`user_id`),
|
||||||
KEY `conversation_id` (`conversation_id`),
|
KEY `conversation_id` (`conversation_id`),
|
||||||
KEY `message_id` (`message_id`)
|
KEY `message_id` (`message_id`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='智能客服消息表';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4_unicode_ci COMMENT='智能客服消息表';
|
||||||
|
|
||||||
|
-- 修改表字符集,utf8mb4_unicode_ci, 兼容emoji表情
|
||||||
|
|
||||||
|
ALTER TABLE lucky_aikefu_message MODIFY content TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
ALTER TABLE lucky_aikefu_conversation CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
ALTER TABLE lucky_aikefu_message CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
@@ -328,176 +328,6 @@ class AI extends BaseApi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 智能客服聊天流式接口
|
|
||||||
*/
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user