++参考代码在文末,中间记录了代码的重点与卡点(所以文中间不包含完整代码)++
++本次实现接入Deepseek(C++) 采用SSE协议(基于HTTP协议)++
一、流式响应设计

cpp
// op1 :判断模型是否有效
// op2 :创建客户端
// op3 :构建请求 设计两个响应回调
// op4 :发送请求
// op5 :判断结果,打印全部响应信息
二、流式响应实现
1.实现时,为了方便。我们先写出json格式的请求和响应:
XML
{
"model": "deepseek-chat",
"messages": [
{
"role": "user",
"content": "你是谁?"
},
],
"temperature": 0.7,
"max_tokens": 2048,
"stream" : true
}
XML
data: {
"id": "chatcmpl-1234567890",
"object": "chat.completion.chunk",
"created": 1700000000,
"model": "deepseek-chat",
"choices": [
{
"index": 0,
"delta": {
"content": "以下"
},
"finish_reason": null
}
]
}
...
data: [DONE]
# 注释行 :以 冒号为首,注意判断
# 终止行 :data: [DONE]
# 实际上传来的响应应当是序列化的数据,且全程只有一个响应头 ,n 个响应体
2.开始实现:
cpp
req.content_receiver = [&](const char *data, size_t data_length, size_t offset, size_t total_length)->bool{
//op1:首先判断能否处理
//op2:积攒数据
//op3:解析数据 while
// 1) 判断数据类型
// 2) 相应处理
if(gotError)
{
bear::ERROR("deepseek response error!");
return false;
}
buffer.append(data,data_length);
};
// 卡点1 :不知道接下来应该做什么 ? 想通:buffer append data后,buffer每行数据一定有\n\n 而每行数据不一定都有效
其中难点
Request的参数介绍
cpp
struct Request {
std::string method;
std::string path;
std::string matched_route;
Params params;
Headers headers;
Headers trailers;
std::string body;
std::string remote_addr;
int remote_port = -1;
std::string local_addr;
int local_port = -1;
// for server
std::string version;
std::string target;
MultipartFormData form;
Ranges ranges;
Match matches;
std::unordered_map<std::string, std::string> path_params;
std::function<bool()> is_connection_closed = []() { return true; };
// for client
std::vector<std::string> accept_content_types;
ResponseHandler response_handler;
ContentReceiverWithProgress content_receiver;
DownloadProgress download_progress;
UploadProgress upload_progress;
// 一些方法...
}
本次实现的函数接口
cpp
using ResponseHandler = std::function<bool(const Response &response)>;
using ContentReceiverWithProgress = std::function<bool(
const char *data, size_t data_length, size_t offset, size_t total_length)>;
// content_receiver返回值的含义:
//return true; → 继续接收下一段数据(保持连接)
//return false; → 立即停止接收,断开连接,终止整个请求!
发送消息-流式返回测试:
cpp
///////////////////testLLM.cpp//////////////////////////
TEST(testDeepseek,sendMessage)
{
//op1:构建
auto deepseekProvider = std::make_shared<ai_model_access_tech::DeepseekProvider>();
ASSERT_TRUE(deepseekProvider.get() != nullptr);
//op2:初始化
std::map<std::string,std::string> initModelParams;
initModelParams.emplace("apiKey",std::getenv("DEEPSEEK_APIKEY"));
initModelParams.emplace("endpoint","https://api.deepseek.com");
auto ret = deepseekProvider->initModel(initModelParams);
ASSERT_TRUE(deepseekProvider->isAvailable());
//op3:发送消息
std::vector<ai_model_access_tech::Message> messages;
messages.push_back({"user","你是谁?"});
// messages.push_back({"assistant","我是deepseek请问有什么需要帮助的?"});
// messages.push_back({"user","请问SSE协议是什么?"});
std::map<std::string, std::string> messageParams;
messageParams.emplace("max_tokens","2048");
messageParams.emplace("temprature","0.7");
auto contentCallback = [&](const std::string & message, bool isLast){
bear::INFO("current info flow:{}",message);
if(isLast)
{
bear::INFO("over~");
}
};
std::string ret_s = deepseekProvider->sendMessageStream(messages,messageParams,contentCallback);
ASSERT_FALSE(ret_s.empty());
}
int main(int argc,char* argv[])
{
bear::myLog::initLogger("testLLM","stdout",spdlog::level::info);
testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}

参考代码:
cpp
std::string DeepseekProvider::sendMessageStream(const std::vector<Message> &messages,
const std::map<std::string, std::string> ¶ms,
std::function<void(const std::string &, bool)>
callback)
{
bear::DEBUG("deepseekProvider senMessageStream......");
//op1:判断模型是否有效
if(!_isAvailable)
{
bear::ERROR("DeepseekProvider sendMessage:model not available");
return "";
}
//op2:创建客户端
httplib::Client client(_endpoint.c_str());
client.set_connection_timeout(10,0);
client.set_read_timeout(300,0);
//op3:构建请求
//头
httplib::Headers headers = {
{"Content-Type", "application/json"},
{"Authorization", "Bearer " + _apiKey},
{"Accept","text/event-stream"}
};
//体
int max_tokens = 2048;
double temprature = 0.7;
if (params.find("max_tokens") != params.end())
{
max_tokens = std::stoi(params.at("max_tokens"));
}
if (params.find("temprature") != params.end())
{
temprature = std::stod(params.at("temprature"));
}
Json::Value messagesArray(Json::arrayValue);
for(auto &message:messages)
{
Json::Value msg;
msg["role"] = message._role;
msg["content"] = message._content;
messagesArray.append(msg);
}
Json::Value requestBody;
requestBody["model"] = getModelName();
requestBody["messages"] = messagesArray;
requestBody["max_tokens"] = max_tokens;
requestBody["temprature"] = temprature;
requestBody["stream"] = true;
Json::StreamWriterBuilder swb;
swb["indentation"] = "";
auto body = Json::writeString(swb,requestBody);
//真正构建req
httplib::Request req;
req.method = "POST";
req.path = "/chat/completions";
req.headers = headers;
req.body = body;
//回调:响应处理器
std::string buffer; //积攒的序列化响应体
int statusCode; //响应的响应码
bool gotError = false; //
std::string errorMsg; //
bool streamFinish = false; //流式响应是否处理完毕
std::string fullMsg = ""; //解析完毕所有的响应内容
req.response_handler = [&](const httplib::Response &response)->bool{
statusCode = response.status;
if(response.status != 200)
{
errorMsg = "HTTP ERROR: " + std::to_string(statusCode);
bear::ERROR("deepseek response error! {}",errorMsg);
gotError = true;
return false;
}
return true;
};
//回调:响应内容处理器
req.content_receiver = [&](const char *data, size_t data_length, size_t offset, size_t total_length)->bool{
//op1:首先判断能否处理
//op2:积攒数据
//op3:解析数据 while
// 1) 判断数据类型
// 2) 相应处理
if(gotError)
{
bear::ERROR("deepseek response error!");
return false;
}
buffer.append(data,data_length);
int pos = 0;
while((pos = buffer.find("\n\n")) != std::string::npos)
{
std::string dataStr = buffer.substr(0,pos);
buffer.erase(0,pos+2);
if(dataStr.empty() || dataStr[0] == ':')
{
// 是否为注释行
bear::WARN("deepseekProvider streamResponse:This line is empty or is a comment.");
continue;
}
if(dataStr.compare(0,6,"data: ") == 0)
{
// 是否为终止行
if(dataStr == "data: [DONE]")
{
streamFinish = true;
callback("",true);
return true;
}
else
{
std::string chunk = dataStr.substr(6);
//op1:反序列化
Json::CharReaderBuilder crb;
std::istringstream chunk_stream(chunk);
Json::Value responseBody;
std::string errorResponse;
//op2:解析字段
auto ret = Json::parseFromStream(crb,chunk_stream,&responseBody,&errorResponse);
if(ret == false)
{
bear::WARN("deepseekProvider parseFromStream error !");
continue;
}
//op3:处理 + 提取
if(responseBody.isMember("choices") && responseBody["choices"].isArray() && !responseBody["choices"].empty())
{
auto choices = responseBody["choices"][0];
if(choices.isMember("delta") && choices["delta"].isObject() && choices["delta"].isMember("content"))
{
auto content = choices["delta"]["content"].asString();
if(!content.empty())
{
fullMsg.append(content);
}
if(callback)
callback(content,false);
}
}
}
}
}
return true;
};
//op4:发送请求
auto ret = client.send(req);
if(!ret)
{
auto errs = ret.error();
bear::ERROR("deepseekProvider req:Network issues (server connection timeout), DNS lookup failure,{}",to_string(errs));
return "";
}
//op5:完整流程
if(!streamFinish)
{
bear::ERROR("deepseekProvider streamResponse FAILED");
callback("",false);
return "";
}
bear::INFO("the full content is '{}'",fullMsg);
return fullMsg;
}