#include "coze.h"
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QTextEdit>
#include <QPushButton>
#include <QComboBox>
#include <QProgressBar>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QUrl>
#include <QMessageBox>
#include <QDateTime>
#include <QDebug>
#include <QSettings>
#include <QScrollBar>
#include <QShortcut>
#include <QFont>
#include <QFile>
#include <QDir>
#include <QStandardPaths>
#include <QLabel>
#include <QRegularExpression>
#include <QListWidget>
#include <QDialog>
#include <QListWidgetItem>
#include <QTimer>
// ------------------------- 构造函数 -------------------------
coze::coze(QWidget *parent)
: QWidget(parent)
, networkManager(new QNetworkAccessManager(this))
, apiToken("")
, botId("")
, userId("123456789")
, conversationId("")
, chatId("")
, isStreaming(false) // 默认关闭流式输出
{
setupUI();
loadSettings();
// 初始化进度条计时器
progressTimer = new QTimer(this);
progressValue = 0;
connect(progressTimer, &QTimer::timeout, [this]() {
progressValue = (progressValue + 5) % 100;
progressBar->setValue(progressValue);
});
connect(networkManager, &QNetworkAccessManager::finished,
this, &coze::handleCozeResponse);
}
// ------------------------- 析构函数 -------------------------
coze::~coze()
{
saveSettings();
}
// ------------------------- 设置API凭证 -------------------------
void coze::setApiCredentials(const QString& token, const QString& id)
{
apiToken = token;
botId = id;
saveSettings();
}
// ------------------------- 创建UI界面 -------------------------
void coze::setupUI()
{
// 主布局
QVBoxLayout *mainLayout = new QVBoxLayout(this);
// 标题
titleLabel = new QLabel("Coze AI 助手", this);
titleLabel->setAlignment(Qt::AlignCenter);
titleLabel->setStyleSheet("font-size: 18px; font-weight: bold; margin: 10px;");
mainLayout->addWidget(titleLabel);
// 模型选择
QHBoxLayout *modelLayout = new QHBoxLayout();
QLabel *modelLabel = new QLabel("选择模型:", this);
modelComboBox = new QComboBox(this);
modelComboBox->addItem("默认模型", "");
modelComboBox->addItem("GPT-4", "gpt-4");
modelComboBox->addItem("GPT-3.5", "gpt-3.5-turbo");
modelComboBox->addItem("DeepSeek-R1", "deepseek-r1");
modelLayout->addWidget(modelLabel);
modelLayout->addWidget(modelComboBox);
mainLayout->addLayout(modelLayout);
// 对话区域
conversationArea = new QTextEdit(this);
conversationArea->setReadOnly(true);
conversationArea->setFont(QFont("Arial", 11));
conversationArea->setStyleSheet("background-color: white; border: 1px solid #ddd;");
mainLayout->addWidget(conversationArea, 1);
// 输入区域
QHBoxLayout *inputLayout = new QHBoxLayout();
inputBox = new QLineEdit(this);
inputBox->setPlaceholderText("输入您的问题...");
sendButton = new QPushButton("发送", this);
clearButton = new QPushButton("清除", this);
historyButton = new QPushButton("历史", this);
// 设置快捷键
QShortcut *enterShortcut = new QShortcut(QKeySequence(Qt::Key_Return), inputBox);
connect(enterShortcut, &QShortcut::activated, this, &coze::onInputFinished);
inputLayout->addWidget(inputBox, 1);
inputLayout->addWidget(sendButton);
inputLayout->addWidget(clearButton);
inputLayout->addWidget(historyButton);
mainLayout->addLayout(inputLayout);
// 进度条
progressBar = new QProgressBar(this);
progressBar->setVisible(false);
progressBar->setRange(0, 0); // 不确定进度
progressBar->setTextVisible(false);
mainLayout->addWidget(progressBar);
// 连接信号槽
connect(sendButton, &QPushButton::clicked, this, &coze::onSendClicked);
connect(clearButton, &QPushButton::clicked, this, &coze::onClearClicked);
connect(historyButton, &QPushButton::clicked, this, &coze::onHistoryClicked);
connect(modelComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &coze::onModelChanged);
// 初始化对话
appendMessage("系统", "欢迎使用 Coze AI 助手!请输入您的问题开始对话。");
}
// ------------------------- 发送消息 -------------------------
void coze::onSendClicked()
{
QString query = inputBox->text().trimmed();
if (query.isEmpty()) {
showError("请输入问题内容");
return;
}
// 添加到对话历史(用户消息)
QJsonObject userMessage;
userMessage["role"] = "user";
userMessage["content"] = query;
userMessage["content_type"] = "text";
conversationHistory.append(userMessage);
// 更新UI
appendMessage("用户", query);
inputBox->clear();
appendMessage("AI", "正在思考,请稍候...");
// 禁用输入
inputBox->setEnabled(false);
sendButton->setEnabled(false);
progressBar->setVisible(true);
// 重置流式响应状态
currentAssistantMessageId = "";
currentAssistantContent = "";
// 发送请求
sendQueryToCoze(query);
}
// ------------------------- 输入完成(回车键) -------------------------
void coze::onInputFinished()
{
onSendClicked();
}
// ------------------------- 清除对话 -------------------------
void coze::onClearClicked()
{
conversationArea->clear();
conversationHistory = QJsonArray();
conversationId = ""; // 清除会话ID
chatId = ""; // 清除对话ID
appendMessage("系统", "对话已清除。请输入新问题开始对话。");
saveConversationHistory();
}
// ------------------------- 显示历史记录 -------------------------
void coze::onHistoryClicked()
{
QDialog historyDialog(this);
historyDialog.setWindowTitle("对话历史");
historyDialog.resize(600, 400);
QVBoxLayout *dialogLayout = new QVBoxLayout(&historyDialog);
QListWidget *historyList = new QListWidget(&historyDialog);
dialogLayout->addWidget(historyList);
// 加载历史记录
QString historyPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/history";
QDir historyDir(historyPath);
if (historyDir.exists()) {
QStringList files = historyDir.entryList(QStringList() << "*.json", QDir::Files, QDir::Time);
for (const QString &file : files) {
historyList->addItem(file);
}
}
// 连接选择事件
connect(historyList, &QListWidget::itemDoubleClicked, [&](QListWidgetItem *item) {
loadConversationHistory(historyPath + "/" + item->text());
historyDialog.accept();
});
historyDialog.exec();
}
// ------------------------- 模型改变 -------------------------
void coze::onModelChanged(int index)
{
Q_UNUSED(index);
saveSettings();
}
// ------------------------- 转换对话历史为additional_messages格式 -------------------------
QJsonArray coze::convertToAdditionalMessages()
{
QJsonArray additionalMessages;
for (const QJsonValue &value : conversationHistory) {
QJsonObject message = value.toObject();
QJsonObject additionalMessage;
additionalMessage["role"] = message["role"].toString();
additionalMessage["content"] = message["content"].toString();
additionalMessage["content_type"] = "text";
// 设置消息类型
if (message["role"].toString() == "user") {
additionalMessage["type"] = "question";
} else if (message["role"].toString() == "assistant") {
additionalMessage["type"] = "answer";
}
additionalMessages.append(additionalMessage);
}
return additionalMessages;
}
// ------------------------- 发送请求到Coze API -------------------------
void coze::sendQueryToCoze(const QString& query)
{
if (apiToken.isEmpty() || botId.isEmpty()) {
showError("API凭证未设置,请先配置API密钥");
resetInputState();
return;
}
QNetworkRequest request;
QUrl url("https://api.coze.cn/v3/chat");
// 添加会话ID查询参数
if (!conversationId.isEmpty()) {
url.setQuery("conversation_id=" + conversationId);
}
request.setUrl(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Authorization", ("Bearer " + apiToken).toUtf8());
// 构建请求体
QJsonObject requestBody;
requestBody["bot_id"] = botId;
requestBody["user_id"] = userId;
requestBody["stream"] = isStreaming; // 使用成员变量控制流式输出
requestBody["auto_save_history"] = true;
// 转换对话历史
QJsonArray additionalMessages = convertToAdditionalMessages();
requestBody["additional_messages"] = additionalMessages;
// 添加模型参数
QString model = modelComboBox->currentData().toString();
if (!model.isEmpty()) {
requestBody["model"] = model;
}
// 启动进度条动画
progressTimer->start(100); // 每100ms更新一次
progressBar->setRange(0, 0); // 不确定模式
progressBar->setVisible(true);
// 调试输出请求信息
qDebug() << "Authorization Header:" << ("Bearer " + apiToken);
qDebug() << "Request URL:" << request.url().toString();
QJsonDocument reqDoc(requestBody);
qDebug() << "Request Body:" << reqDoc.toJson(QJsonDocument::Indented);
// 发送POST请求
networkManager->post(request, reqDoc.toJson());
}
// ------------------------- 处理API响应 -------------------------
void coze::handleCozeResponse(QNetworkReply* reply)
{
// 停止进度条动画
progressTimer->stop();
progressBar->setVisible(false);
if (reply->error() != QNetworkReply::NoError) {
showError("网络错误: " + reply->errorString());
resetInputState();
reply->deleteLater();
return;
}
QByteArray responseData = reply->readAll();
reply->deleteLater();
// 调试输出原始响应
qDebug() << "Raw API Response:" << QString(responseData);
// 移除"正在思考"消息
QString lastHtml = conversationArea->toHtml();
if (lastHtml.contains("正在思考")) {
conversationArea->setHtml(lastHtml.replace(
QRegularExpression("<b>AI:</b> 正在思考,请稍候\\.\\.\\.<br>$"), ""
));
}
// 处理非流式响应
QJsonParseError parseError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData, &parseError);
if (parseError.error != QJsonParseError::NoError) {
showError("JSON解析错误: " + parseError.errorString());
return;
}
if (!jsonDoc.isObject()) {
showError("API返回格式错误");
return;
}
QJsonObject responseObj = jsonDoc.object();
// 检查是否有错误
if (responseObj.contains("error")) {
QJsonObject errorObj = responseObj["error"].toObject();
showError("API错误: " + errorObj["message"].toString());
return;
}
// 提取智能体回复
QString aiResponse;
if (responseObj.contains("messages")) {
QJsonArray messages = responseObj["messages"].toArray();
for (const QJsonValue &message : messages) {
QJsonObject msgObj = message.toObject();
if (msgObj["role"].toString() == "assistant") {
aiResponse = msgObj["content"].toString();
break;
}
}
}
if (aiResponse.isEmpty()) {
showError("未收到有效回复");
return;
}
// 更新会话ID(如果提供)
if (responseObj.contains("conversation_id")) {
conversationId = responseObj["conversation_id"].toString();
qDebug() << "更新会话ID:" << conversationId;
}
// 更新对话历史
QJsonObject aiMessage;
aiMessage["role"] = "assistant";
aiMessage["content"] = aiResponse;
conversationHistory.append(aiMessage);
// 添加到UI
appendMessage("AI", aiResponse);
// 保存对话历史
saveConversationHistory();
resetInputState();
}
// ------------------------- 处理流式事件 -------------------------
void coze::processStreamEvent(const QString& eventName, const QJsonObject& data)
{
qDebug() << "Processing event:" << eventName;
if (eventName == "conversation.chat.created") {
// 保存对话ID
if (data.contains("id")) {
chatId = data["id"].toString();
qDebug() << "Chat created, ID:" << chatId;
}
// 保存会话ID
if (data.contains("conversation_id") && conversationId.isEmpty()) {
conversationId = data["conversation_id"].toString();
qDebug() << "Conversation ID:" << conversationId;
}
handleChatEvent(data);
}
else if (eventName == "conversation.chat.in_progress") {
handleChatEvent(data);
}
else if (eventName == "conversation.message.delta") {
handleDeltaMessage(data);
}
else if (eventName == "conversation.message.completed") {
handleCompletedMessage(data);
}
else if (eventName == "conversation.chat.completed") {
handleChatEvent(data);
}
else if (eventName == "conversation.chat.failed") {
showError("对话处理失败: " + data["last_error"].toObject()["msg"].toString());
}
else if (eventName == "error") {
showError("API错误: " + data["msg"].toString());
}
else {
qDebug() << "Unhandled event:" << eventName << "Data:" << data;
}
}
// ------------------------- 处理增量消息 -------------------------
void coze::handleDeltaMessage(const QJsonObject& messageObj)
{
// 检查是否为AI消息
if (messageObj["role"].toString() != "assistant" ||
messageObj["type"].toString() != "answer") {
return;
}
// 获取消息ID
QString messageId = messageObj["id"].toString();
// 如果是新消息,重置内容
if (messageId != currentAssistantMessageId) {
currentAssistantMessageId = messageId;
currentAssistantContent = "";
// 移除"正在思考"消息
QString lastHtml = conversationArea->toHtml();
if (lastHtml.contains("正在思考")) {
conversationArea->setHtml(lastHtml.replace(
QRegularExpression("<b>AI:</b> 正在思考,请稍候\\.\\.\\.<br>$"), ""
));
}
}
// 添加增量内容
QString deltaContent = messageObj["content"].toString();
currentAssistantContent += deltaContent;
// 更新UI
QString lastHtml = conversationArea->toHtml();
if (lastHtml.contains("<b>AI:</b> " + currentAssistantContent)) {
// 如果内容已显示,只更新最后一行
QRegularExpression re("<b>AI:</b> (.*?)<br>$");
QRegularExpressionMatch match = re.match(lastHtml);
if (match.hasMatch()) {
QString oldContent = match.captured(1);
conversationArea->setHtml(lastHtml.replace(
oldContent, currentAssistantContent
));
}
} else {
// 否则添加新消息
appendMessage("AI", currentAssistantContent);
}
}
// ------------------------- 处理完成的消息 -------------------------
void coze::handleCompletedMessage(const QJsonObject& messageObj)
{
// 检查是否为AI消息
if (messageObj["role"].toString() != "assistant" ||
messageObj["type"].toString() != "answer") {
return;
}
// 获取完整消息内容
QString fullContent = messageObj["content"].toString();
// 更新对话历史
if (!currentAssistantContent.isEmpty()) {
// 更新UI中的消息
QString lastHtml = conversationArea->toHtml();
QRegularExpression re("<b>AI:</b> (.*?)<br>$");
QRegularExpressionMatch match = re.match(lastHtml);
if (match.hasMatch()) {
QString oldContent = match.captured(1);
conversationArea->setHtml(lastHtml.replace(
oldContent, fullContent
));
}
// 添加到对话历史
QJsonObject aiMessage;
aiMessage["role"] = "assistant";
aiMessage["content"] = fullContent;
conversationHistory.append(aiMessage);
// 保存对话历史
saveConversationHistory();
}
}
// ------------------------- 处理对话事件 -------------------------
void coze::handleChatEvent(const QJsonObject& chatObj)
{
// 更新状态信息
if (chatObj.contains("status")) {
QString status = chatObj["status"].toString();
QString statusText;
if (status == "created") statusText = "对话已创建";
else if (status == "in_progress") statusText = "处理中";
else if (status == "completed") statusText = "已完成";
else if (status == "failed") statusText = "失败";
else if (status == "requires_action") statusText = "需要操作";
qDebug() << "对话状态:" << statusText;
}
// 更新会话ID(如果为空)
if (conversationId.isEmpty() && chatObj.contains("conversation_id")) {
conversationId = chatObj["conversation_id"].toString();
qDebug() << "更新会话ID:" << conversationId;
}
// 更新对话ID
if (chatObj.contains("id")) {
chatId = chatObj["id"].toString();
}
}
// ------------------------- 添加消息到对话区域 -------------------------
void coze::appendMessage(const QString& sender, const QString& message, bool isError)
{
QString formattedMessage;
if (isError) {
// 错误消息使用红色
formattedMessage = QString("<b style='color:red;'>%1:</b> <span style='color:black;'>%2</span><br>")
.arg(sender, message);
} else {
// 普通消息使用黑色
formattedMessage = QString("<b>%1:</b> <span style='color:black;'>%2</span><br>")
.arg(sender, message);
}
conversationArea->append(formattedMessage);
// 自动滚动到底部
QScrollBar *scrollbar = conversationArea->verticalScrollBar();
scrollbar->setValue(scrollbar->maximum());
}
// ------------------------- 显示错误消息 -------------------------
void coze::showError(const QString& message)
{
appendMessage("错误", message, true);
qDebug() << "错误:" << message;
// 恢复UI状态
resetInputState();
}
// ------------------------- 重置输入状态 -------------------------
void coze::resetInputState()
{
inputBox->setEnabled(true);
sendButton->setEnabled(true);
progressBar->setVisible(false);
inputBox->setFocus();
}
// ------------------------- 保存设置 -------------------------
void coze::saveSettings()
{
QSettings settings("MyCompany", "CozeAI");
settings.setValue("apiToken", apiToken);
settings.setValue("botId", botId);
settings.setValue("conversationId", conversationId);
settings.setValue("modelIndex", modelComboBox->currentIndex());
}
// ------------------------- 加载设置 -------------------------
void coze::loadSettings()
{
QSettings settings("MyCompany", "CozeAI");
apiToken = settings.value("apiToken", "").toString();
botId = settings.value("botId", "").toString();
conversationId = settings.value("conversationId", "").toString();
int modelIndex = settings.value("modelIndex", 0).toInt();
if (modelIndex >= 0 && modelIndex < modelComboBox->count()) {
modelComboBox->setCurrentIndex(modelIndex);
}
loadConversationHistory();
}
// ------------------------- 保存对话历史 -------------------------
void coze::saveConversationHistory()
{
if (conversationHistory.isEmpty()) return;
// 创建历史目录
QString historyPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/history";
QDir().mkpath(historyPath);
// 生成文件名
QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss");
QString fileName = historyPath + "/conversation_" + timestamp + ".json";
// 保存为JSON文件
QFile file(fileName);
if (file.open(QIODevice::WriteOnly)) {
QJsonDocument doc(conversationHistory);
file.write(doc.toJson());
file.close();
}
}
// ------------------------- 加载对话历史 -------------------------
void coze::loadConversationHistory(const QString& filePath)
{
QString loadPath = filePath;
if (loadPath.isEmpty()) {
// 自动加载最新历史
QString historyPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/history";
QDir historyDir(historyPath);
if (historyDir.exists()) {
QStringList files = historyDir.entryList(QStringList() << "*.json", QDir::Files, QDir::Time);
if (!files.isEmpty()) {
loadPath = historyPath + "/" + files.first();
}
}
}
if (loadPath.isEmpty()) return;
// 加载文件
QFile file(loadPath);
if (file.open(QIODevice::ReadOnly)) {
QByteArray data = file.readAll();
QJsonParseError parseError;
QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
if (parseError.error == QJsonParseError::NoError && doc.isArray()) {
conversationHistory = doc.array();
// 清空对话区域
conversationArea->clear();
// 重新添加所有消息
for (const QJsonValue &value : conversationHistory) {
QJsonObject message = value.toObject();
QString role = message["role"].toString();
QString content = message["content"].toString();
if (role == "user") {
appendMessage("用户", content);
} else if (role == "assistant") {
appendMessage("AI", content);
}
}
// 添加系统消息
appendMessage("系统", "历史对话已加载。");
}
}
}
coze.cpp文件点击发送后显示未收到有效文件,分析原因并修改
最新发布