From e93e112be31798275950cdaeb5038b28b7e092be Mon Sep 17 00:00:00 2001 From: "xiaoqiang.zhou" Date: Sun, 29 Sep 2024 20:04:01 +0800 Subject: [PATCH 1/4] support stream for hunyuan AI Change-Id: I0fc56a1437dad475303784eeca9a2f65ac30ca5b --- core/include/tencentcloud/core/Config.h | 2 +- .../tencentcloud/core/http/HttpClient.h | 7 ++ .../tencentcloud/core/profile/HttpProfile.h | 11 ++- core/src/AbstractClient.cpp | 1 + core/src/http/HttpClient.cpp | 34 ++++++- core/src/profile/HttpProfile.cpp | 15 ++- .../v20230901/model/ChatCompletionsResponse.h | 4 + hunyuan/src/v20230901/HunyuanClient.cpp | 9 +- .../model/ChatCompletionsResponse.cpp | 92 +++++++++++++++++++ 9 files changed, 167 insertions(+), 8 deletions(-) diff --git a/core/include/tencentcloud/core/Config.h b/core/include/tencentcloud/core/Config.h index cd0b7e04c1..8d7f14542c 100644 --- a/core/include/tencentcloud/core/Config.h +++ b/core/include/tencentcloud/core/Config.h @@ -18,7 +18,7 @@ #define TENCENTCLOUD_CORE_CONFIG_H_ // version = (major << 16) + (minor << 8) + patch -#define TENCENTCLOUD_VERSION ((3 << 16) + (0 << 8) + 8) +#define TENCENTCLOUD_VERSION ((3 << 16) + (0 << 8) + 1069) #define TENCENTCLOUD_VERSION_STR "3.0.1069" diff --git a/core/include/tencentcloud/core/http/HttpClient.h b/core/include/tencentcloud/core/http/HttpClient.h index 65b3956578..21b8100fa1 100644 --- a/core/include/tencentcloud/core/http/HttpClient.h +++ b/core/include/tencentcloud/core/http/HttpClient.h @@ -17,6 +17,7 @@ #ifndef TENCENTCLOUD_CORE_HTTP_HTTPCLIENT_H_ #define TENCENTCLOUD_CORE_HTTP_HTTPCLIENT_H_ +#include #include #include #include @@ -29,6 +30,8 @@ namespace TencentCloud { public: typedef Outcome HttpResponseOutcome; + typedef std::function HttpStreamCallback; + HttpClient(); ~HttpClient(); @@ -40,6 +43,9 @@ namespace TencentCloud void SetProxy(const NetworkProxy &proxy); + void SetStreamCallback(HttpStreamCallback callback); + bool WriteStreamFunction(std::string user_data); + static void InitGlobalState(); static void CleanupGlobalState(); @@ -48,6 +54,7 @@ namespace TencentCloud int64_t m_reqTimeout; int64_t m_connectTimeout; NetworkProxy m_proxy; + HttpStreamCallback m_streamCallback; #ifdef ENABLE_COMPRESS_MODULE int GzipDecompress(const char *src, int srcLen, const char *dst, int* dstLen); bool TryDecompress(const char *src, int srcLen, std::string &decompressData); diff --git a/core/include/tencentcloud/core/profile/HttpProfile.h b/core/include/tencentcloud/core/profile/HttpProfile.h index 61f421439c..94b6344d64 100644 --- a/core/include/tencentcloud/core/profile/HttpProfile.h +++ b/core/include/tencentcloud/core/profile/HttpProfile.h @@ -18,10 +18,13 @@ #define TENCENTCLOUD_CORE_HTTPPROFILE_H_ #include +#include namespace TencentCloud { static const int64_t TM_MINUTE_SECOND = 60; + typedef std::function HttpStreamCallback; + class HttpProfile { @@ -48,6 +51,9 @@ namespace TencentCloud void SetKeepAlive(bool flag=false); bool IsKeepAlive() const; HttpProfile::Scheme GetProtocol() const; + void SetHTTPStreamCallback(HttpStreamCallback callback); + HttpStreamCallback GetHTTPStreamCallback(); + HttpProfile(const HttpProfile &o) : m_reqMethod(o.m_reqMethod), @@ -55,7 +61,8 @@ namespace TencentCloud m_protocol(o.m_protocol), m_reqTimeout(o.m_reqTimeout), m_connectTimeout(o.m_connectTimeout), - m_keepAlive(o.m_keepAlive) + m_keepAlive(o.m_keepAlive), + m_streamCallback(o.m_streamCallback) { } @@ -69,6 +76,7 @@ namespace TencentCloud m_reqTimeout = o.m_reqTimeout; m_connectTimeout = o.m_connectTimeout; m_keepAlive = o.m_keepAlive; + m_streamCallback = o.m_streamCallback; } return *this; } @@ -80,6 +88,7 @@ namespace TencentCloud int64_t m_reqTimeout; int64_t m_connectTimeout; bool m_keepAlive; + HttpStreamCallback m_streamCallback; }; } diff --git a/core/src/AbstractClient.cpp b/core/src/AbstractClient.cpp index f730d8227f..0620ebbb21 100644 --- a/core/src/AbstractClient.cpp +++ b/core/src/AbstractClient.cpp @@ -165,6 +165,7 @@ HttpClient::HttpResponseOutcome AbstractClient::DoRequest(const std::string &act GenerateSignature(httpRequest); m_httpClient->SetReqTimeout(httpProfile.GetReqTimeout()*1000); m_httpClient->SetConnectTimeout(httpProfile.GetConnectTimeout()*1000); + m_httpClient->SetStreamCallback(httpProfile.GetHTTPStreamCallback()); return m_httpClient->SendRequest(httpRequest); } diff --git a/core/src/http/HttpClient.cpp b/core/src/http/HttpClient.cpp index 438c554d2d..14a708de9e 100644 --- a/core/src/http/HttpClient.cpp +++ b/core/src/http/HttpClient.cpp @@ -32,11 +32,19 @@ using namespace TencentCloud; namespace { + struct HttpWriteUserData { + std::ostringstream *out; + HttpClient* client; + }; + size_t recvBody(char *ptr, size_t size, size_t nmemb, void *userdata) { - std::ostringstream &out = *static_cast(userdata); - out << std::string(ptr, nmemb*size); - return nmemb * size; + struct HttpWriteUserData *data = static_cast(userdata); + auto recv_size = nmemb * size; + *data->out << std::string(ptr,recv_size); + bool result = data->client->WriteStreamFunction(ptr); + + return result? nmemb * size : 0; } size_t recvHeaders(char *buffer, size_t size, size_t nitems, void *userdata) @@ -124,6 +132,19 @@ void HttpClient::SetProxy(const NetworkProxy &proxy) m_proxy = proxy; } +void HttpClient::SetStreamCallback(HttpStreamCallback callback){ + m_streamCallback = callback; +} + +bool HttpClient::WriteStreamFunction(std::string user_data){ + if (m_streamCallback){ + return m_streamCallback(user_data); + } + + return true; +} + + HttpClient::HttpResponseOutcome HttpClient::SendRequest(const HttpRequest &request) { curl_easy_reset(m_curlHandle); @@ -177,8 +198,13 @@ HttpClient::HttpResponseOutcome HttpClient::SendRequest(const HttpRequest &reque } curl_easy_setopt(m_curlHandle, CURLOPT_HTTPHEADER, header_list); std::ostringstream out; - curl_easy_setopt(m_curlHandle, CURLOPT_WRITEDATA, &out); + struct HttpWriteUserData data; + data.out = &out; + data.client = this; + curl_easy_setopt(m_curlHandle, CURLOPT_WRITEDATA, &data); curl_easy_setopt(m_curlHandle, CURLOPT_WRITEFUNCTION, recvBody); + + setCUrlProxy(m_curlHandle, m_proxy); char errbuf[CURL_ERROR_SIZE]; diff --git a/core/src/profile/HttpProfile.cpp b/core/src/profile/HttpProfile.cpp index 5923a0f107..e0a8dac8c7 100644 --- a/core/src/profile/HttpProfile.cpp +++ b/core/src/profile/HttpProfile.cpp @@ -25,7 +25,8 @@ HttpProfile::HttpProfile() : m_protocol(HttpProfile::Scheme::HTTPS), m_reqTimeout(TM_MINUTE_SECOND), m_connectTimeout(TM_MINUTE_SECOND), - m_keepAlive(false) + m_keepAlive(false), + m_streamCallback(nullptr) { } @@ -82,3 +83,15 @@ bool HttpProfile::IsKeepAlive() const { return m_keepAlive; } + + +void HttpProfile::SetHTTPStreamCallback(HttpStreamCallback callback){ + m_streamCallback = std::move(callback); +} + +HttpStreamCallback HttpProfile::GetHTTPStreamCallback(){ + return m_streamCallback; +} + + + diff --git a/hunyuan/include/tencentcloud/hunyuan/v20230901/model/ChatCompletionsResponse.h b/hunyuan/include/tencentcloud/hunyuan/v20230901/model/ChatCompletionsResponse.h index fd9996b019..507db04ddb 100644 --- a/hunyuan/include/tencentcloud/hunyuan/v20230901/model/ChatCompletionsResponse.h +++ b/hunyuan/include/tencentcloud/hunyuan/v20230901/model/ChatCompletionsResponse.h @@ -165,6 +165,10 @@ namespace TencentCloud */ bool SearchInfoHasBeenSet() const; + + std::vector SplitFullStreamedData(const std::string& data); + CoreInternalOutcome ParseFinshStreamData(const std::string &data); + void RemoveStrings(std::string& s, std::string p); private: /** diff --git a/hunyuan/src/v20230901/HunyuanClient.cpp b/hunyuan/src/v20230901/HunyuanClient.cpp index 4758d4f386..9db747f33a 100644 --- a/hunyuan/src/v20230901/HunyuanClient.cpp +++ b/hunyuan/src/v20230901/HunyuanClient.cpp @@ -17,6 +17,7 @@ #include #include #include +#include using namespace TencentCloud; using namespace TencentCloud::Hunyuan::V20230901; @@ -91,7 +92,13 @@ HunyuanClient::ChatCompletionsOutcome HunyuanClient::ChatCompletions(const ChatC auto r = outcome.GetResult(); string payload = string(r.Body(), r.BodySize()); ChatCompletionsResponse rsp = ChatCompletionsResponse(); - auto o = rsp.Deserialize(payload); + TencentCloud::CoreInternalOutcome o; + if (request.GetStream()) { + o = rsp.ParseFinshStreamData(payload); + } else { + o = rsp.Deserialize(payload); + } + if (o.IsSuccess()) return ChatCompletionsOutcome(rsp); else diff --git a/hunyuan/src/v20230901/model/ChatCompletionsResponse.cpp b/hunyuan/src/v20230901/model/ChatCompletionsResponse.cpp index 7204f4856c..9a7ac0bb8c 100644 --- a/hunyuan/src/v20230901/model/ChatCompletionsResponse.cpp +++ b/hunyuan/src/v20230901/model/ChatCompletionsResponse.cpp @@ -19,6 +19,9 @@ #include #include +#include +#include + using TencentCloud::CoreInternalOutcome; using namespace TencentCloud::Hunyuan::V20230901::Model; using namespace std; @@ -35,6 +38,95 @@ ChatCompletionsResponse::ChatCompletionsResponse() : { } + +std::vector ChatCompletionsResponse::SplitFullStreamedData(const std::string& data) { + if (data.empty()) { + return {}; + } + + std::vector split_data; + std::string temp; + std::istringstream iss(data); + while (std::getline(iss, temp)) { + if (temp.empty()) { + split_data.push_back(temp); + } else { + split_data.push_back(temp); + } + } + + // remove empty strings from the vector + split_data.erase(std::remove_if(split_data.begin(), split_data.end(), [](const std::string& s) { return s.empty(); }), split_data.end()); + + return split_data; +} + +void ChatCompletionsResponse::RemoveStrings(std::string& s, std::string p) { + std::string::size_type i = s.find(p); + while (i != std::string::npos) { + s.erase(i, p.length()); + i = s.find(p, i); + } +} + +CoreInternalOutcome ChatCompletionsResponse::ParseFinshStreamData(const std::string &data){ + std::vector data_lines = SplitFullStreamedData(data); + + if (data_lines.empty()) { + return CoreInternalOutcome(Core::Error("response data is empty")); + } + + std::vector choices_list; + Message full_msg; + full_msg.SetRole("assistant"); + for (auto& line : data_lines){ + RemoveStrings(line, "data: "); + + rapidjson::Document d; + d.Parse(line.c_str()); + if (d.HasParseError() || !d.IsObject()){ + return CoreInternalOutcome(Core::Error("response not json format")); + } + + if (d.HasMember("Choices") && !d["Choices"].IsNull()) + { + if (!d["Choices"].IsArray()) + return CoreInternalOutcome(Core::Error("response `Choices` is not array type")); + + const rapidjson::Value &tmpValue = d["Choices"]; + + for (rapidjson::Value::ConstValueIterator itr = tmpValue.Begin(); itr != tmpValue.End(); ++itr) + { + const rapidjson::Value &value = *itr; + string requestId = ""; + if (value.HasMember("Delta") && !value["Delta"].IsNull()) + { + if (!value["Delta"].IsObject()) + { + return CoreInternalOutcome(Core::Error("response `Choice.Delta` is not object type").SetRequestId(requestId)); + } + + Delta current_delta; + CoreInternalOutcome outcome = current_delta.Deserialize(value["Delta"]); + if (!outcome.IsSuccess()) + { + outcome.GetError().SetRequestId(requestId); + return outcome; + } + full_msg.SetContent(full_msg.GetContent() + current_delta.GetContent()); + } + } + } + } + + Choice item; + item.SetMessage(full_msg); + m_choices.push_back(item); + m_choicesHasBeenSet = true; + + return CoreInternalOutcome(true); +} + CoreInternalOutcome ChatCompletionsResponse::Deserialize(const string &payload) { rapidjson::Document d; From 0204b8b89f4812dd61e77666d5c3b1dd32660ef3 Mon Sep 17 00:00:00 2001 From: seanchann Date: Mon, 9 Dec 2024 11:27:27 +0800 Subject: [PATCH 2/4] fix missing cstdint with new gcc --- core/include/tencentcloud/core/Config.h | 4 ++-- core/include/tencentcloud/core/NetworkProxy.h | 1 + .../hunyuan/v20230901/model/ActivateServiceRequest.h | 1 + .../tencentcloud/tts/v20190823/model/CreateTtsTaskRequest.h | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/core/include/tencentcloud/core/Config.h b/core/include/tencentcloud/core/Config.h index 8d7f14542c..02e737b687 100644 --- a/core/include/tencentcloud/core/Config.h +++ b/core/include/tencentcloud/core/Config.h @@ -18,8 +18,8 @@ #define TENCENTCLOUD_CORE_CONFIG_H_ // version = (major << 16) + (minor << 8) + patch -#define TENCENTCLOUD_VERSION ((3 << 16) + (0 << 8) + 1069) +#define TENCENTCLOUD_VERSION ((3 << 16) + (0 << 8) + 8) #define TENCENTCLOUD_VERSION_STR "3.0.1069" -#endif // !TENCENTCLOUD_CORE_CONFIG_H_ +#endif // !TENCENTCLOUD_CORE_CONFIG_H_ diff --git a/core/include/tencentcloud/core/NetworkProxy.h b/core/include/tencentcloud/core/NetworkProxy.h index 7d136ae077..fe3fbf99d2 100644 --- a/core/include/tencentcloud/core/NetworkProxy.h +++ b/core/include/tencentcloud/core/NetworkProxy.h @@ -18,6 +18,7 @@ #define TENCENTCLOUD_CORE_NETWORKPROXY_H_ #include +#include namespace TencentCloud { diff --git a/hunyuan/include/tencentcloud/hunyuan/v20230901/model/ActivateServiceRequest.h b/hunyuan/include/tencentcloud/hunyuan/v20230901/model/ActivateServiceRequest.h index 575355d5e9..f9cd0cfbe7 100644 --- a/hunyuan/include/tencentcloud/hunyuan/v20230901/model/ActivateServiceRequest.h +++ b/hunyuan/include/tencentcloud/hunyuan/v20230901/model/ActivateServiceRequest.h @@ -20,6 +20,7 @@ #include #include #include +#include #include diff --git a/tts/include/tencentcloud/tts/v20190823/model/CreateTtsTaskRequest.h b/tts/include/tencentcloud/tts/v20190823/model/CreateTtsTaskRequest.h index d1699a6ff4..f1dee979af 100644 --- a/tts/include/tencentcloud/tts/v20190823/model/CreateTtsTaskRequest.h +++ b/tts/include/tencentcloud/tts/v20190823/model/CreateTtsTaskRequest.h @@ -20,6 +20,7 @@ #include #include #include +#include #include From 5c5d8429ff5da92d67eed72e4e97508e30e9e377 Mon Sep 17 00:00:00 2001 From: seanchann Date: Mon, 9 Dec 2024 11:28:38 +0800 Subject: [PATCH 3/4] unset unchanged file --- core/include/tencentcloud/core/Config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/include/tencentcloud/core/Config.h b/core/include/tencentcloud/core/Config.h index 02e737b687..cd0b7e04c1 100644 --- a/core/include/tencentcloud/core/Config.h +++ b/core/include/tencentcloud/core/Config.h @@ -22,4 +22,4 @@ #define TENCENTCLOUD_VERSION_STR "3.0.1069" -#endif // !TENCENTCLOUD_CORE_CONFIG_H_ +#endif // !TENCENTCLOUD_CORE_CONFIG_H_ From a7c3bf6bf104e20c8a01e75414b8dcac5b0097cb Mon Sep 17 00:00:00 2001 From: seanchann Date: Mon, 9 Dec 2024 11:42:14 +0800 Subject: [PATCH 4/4] fix missing cstdint header file --- .../tencentcloud/tts/v20190823/model/TextToVoiceRequest.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tts/include/tencentcloud/tts/v20190823/model/TextToVoiceRequest.h b/tts/include/tencentcloud/tts/v20190823/model/TextToVoiceRequest.h index 751c08cb26..c5a6a93aa6 100644 --- a/tts/include/tencentcloud/tts/v20190823/model/TextToVoiceRequest.h +++ b/tts/include/tencentcloud/tts/v20190823/model/TextToVoiceRequest.h @@ -20,6 +20,7 @@ #include #include #include +#include #include