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/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/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/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; 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 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