add llama provider (#37)

This commit is contained in:
Dmitry Afanasyev 2023-10-12 13:55:08 +03:00 committed by GitHub
parent 359901ade8
commit b322e3c1da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 294 additions and 142 deletions

View File

@ -54,6 +54,7 @@ class ChatGptModelsEnum(StrEnum):
gpt_3_5_turbo_stream_Cromicle = "gpt-3.5-turbo-stream-Cromicle"
gpt_4_stream_Chatgpt4Online = "gpt-4-stream-Chatgpt4Online"
gpt_3_5_turbo_stream_gptalk = "gpt-3.5-turbo-stream-gptalk"
llama2 = "llama2"
gpt_3_5_turbo_stream_ChatgptDemo = "gpt-3.5-turbo-stream-ChatgptDemo"
gpt_3_5_turbo_stream_gptforlove = "gpt-3.5-turbo-stream-gptforlove"

View File

@ -36,6 +36,7 @@ public:
boost::asio::awaitable<void> gptalk(std::shared_ptr<Channel>, nlohmann::json);
boost::asio::awaitable<void> gptForLove(std::shared_ptr<Channel>, nlohmann::json);
boost::asio::awaitable<void> chatGptDemo(std::shared_ptr<Channel>, nlohmann::json);
boost::asio::awaitable<void> llama2(std::shared_ptr<Channel>, nlohmann::json);
private:
boost::asio::awaitable<std::expected<boost::beast::ssl_stream<boost::beast::tcp_stream>, std::string>>

View File

@ -575,6 +575,90 @@ std::expected<nlohmann::json, std::string> callZeus(const std::string& host, con
return rsp;
}
struct CurlHttpRequest {
CURL* curl;
std::string url;
std::string_view http_proxy;
size_t (*cb)(void* contents, size_t size, size_t nmemb, void* userp);
void* input;
std::unordered_map<std::string, std::string>& headers;
std::string body;
std::multimap<std::string, std::string>* response_header_ptr{nullptr};
int32_t expect_response_code{200};
bool ssl_verify{false};
};
std::optional<std::string> sendHttpRequest(const CurlHttpRequest& curl_http_request) {
auto& [curl, url, http_proxy, stream_action_cb, input, http_headers, body, response_header_ptr, response_code,
ssl_verify] = curl_http_request;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
if (!http_proxy.empty())
curl_easy_setopt(curl, CURLOPT_PROXY, http_proxy.data());
if (!ssl_verify) {
curl_easy_setopt(curl, CURLOPT_CAINFO, nullptr);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
}
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 20L);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120L);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);
if (stream_action_cb != nullptr)
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, stream_action_cb);
if (input != nullptr)
curl_easy_setopt(curl, CURLOPT_WRITEDATA, input);
std::string buffer;
auto header_callback = [](char* buffer, size_t size, size_t nitems, void* userdata) {
std::string str{(char*)buffer, size * nitems};
static_cast<std::string*>(userdata)->append((char*)buffer, size * nitems);
return nitems * size;
};
size_t (*fn_header_callback)(char* buffer, size_t size, size_t nitems, void* userdata) = header_callback;
if (response_header_ptr) {
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, fn_header_callback);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &buffer);
}
struct curl_slist* headers = nullptr;
std::vector<std::string> headers_list;
for (auto& [k, v] : http_headers)
headers_list.emplace_back(std::format("{}: {}", k, v));
for (auto& header : headers_list)
headers = curl_slist_append(headers, header.c_str());
if (headers != nullptr)
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
ScopeExit auto_exit{[=] { curl_slist_free_all(headers); }};
if (!body.empty())
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
auto res = curl_easy_perform(curl);
if (res != CURLE_OK) {
auto error_info = std::format("curl_easy_perform() failed:{}", curl_easy_strerror(res));
return error_info;
}
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
if (response_code != 200) {
auto error_info = std::format("response_code :{}", response_code);
return error_info;
}
if (!buffer.empty() && response_header_ptr != nullptr) {
std::regex pattern(R"(([^:\r\n]+):([^\r\n]+))");
std::smatch matches;
auto start = buffer.cbegin();
auto end = buffer.cend();
while (std::regex_search(start, end, matches, pattern)) {
std::string field_name = matches[1].str();
std::string field_value = matches[2].str();
(*response_header_ptr).insert(std::pair{field_name, field_value});
start = matches[0].second;
}
}
return std::nullopt;
}
} // namespace
FreeGpt::FreeGpt(Config& cfg)
@ -2558,15 +2642,20 @@ boost::asio::awaitable<void> FreeGpt::chatGptDemo(std::shared_ptr<Channel> ch, n
auto prompt = json.at("meta").at("content").at("parts").at(0).at("content").get<std::string>();
CURLcode res;
int32_t response_code;
struct Input {
std::shared_ptr<Channel> ch;
std::string recv;
};
Input input;
std::unordered_map<std::string, std::string> http_headers{
{"authority", "chat.chatgptdemo.net"},
{"origin", "https://chat.chatgptdemo.net"},
{"referer", "https://chat.chatgptdemo.net/"},
};
std::multimap<std::string, std::string> response_header;
CURLcode res;
CURL* curl = curl_easy_init();
if (!curl) {
auto error_info = std::format("curl_easy_init() failed:{}", curl_easy_strerror(res));
@ -2574,48 +2663,39 @@ boost::asio::awaitable<void> FreeGpt::chatGptDemo(std::shared_ptr<Channel> ch, n
ch->try_send(err, error_info);
co_return;
}
curl_easy_setopt(curl, CURLOPT_URL, "https://chat.chatgptdemo.net/");
ScopeExit auto_exit{[=] { curl_easy_cleanup(curl); }};
if (!m_cfg.http_proxy.empty())
curl_easy_setopt(curl, CURLOPT_PROXY, m_cfg.http_proxy.c_str());
curlEasySetopt(curl);
auto action_cb = [](void* contents, size_t size, size_t nmemb, void* userp) -> size_t {
auto ret = sendHttpRequest(CurlHttpRequest{
.curl = curl,
.url = "https://chat.chatgptdemo.net/",
.http_proxy = m_cfg.http_proxy,
.cb = [](void* contents, size_t size, size_t nmemb, void* userp) mutable -> size_t {
auto input_ptr = static_cast<Input*>(userp);
std::string data{(char*)contents, size * nmemb};
auto& [ch, recv] = *input_ptr;
recv.append(data);
return size * nmemb;
};
size_t (*action_fn)(void* contents, size_t size, size_t nmemb, void* userp) = action_cb;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, action_fn);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &input);
struct curl_slist* headers = nullptr;
headers = curl_slist_append(headers, "origin: https://chat.chatgptdemo.net");
headers = curl_slist_append(headers, "referer: https://chat.chatgptdemo.net/");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
ScopeExit auto_exit{[=] {
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}};
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
},
.input = [&] -> void* {
input.recv.clear();
return &input;
}(),
.headers = http_headers,
.body = std::string{},
.response_header_ptr = &response_header,
.expect_response_code = 200,
.ssl_verify = false,
});
if (ret) {
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
auto error_info = std::format("curl_easy_perform() failed:{}", curl_easy_strerror(res));
ch->try_send(err, error_info);
ch->try_send(err, ret.value());
co_return;
}
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
if (response_code != 200) {
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
ch->try_send(err, std::format("chatGptDemo http code:{}", response_code));
co_return;
}
auto ret = findAll(R"(<div id="USERID" style="display: none">(.*)<\/div>)", input.recv);
if (ret.empty()) {
for (auto& [k, v] : response_header)
SPDLOG_INFO("{}: {}", k, v);
auto match_ret = findAll(R"(<div id="USERID" style="display: none">(.*)<\/div>)", input.recv);
if (match_ret.empty()) {
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
ch->try_send(err, std::format("not found userid"));
co_return;
@ -2626,60 +2706,55 @@ boost::asio::awaitable<void> FreeGpt::chatGptDemo(std::shared_ptr<Channel> ch, n
std::string value = str.substr(start, end - start);
return value;
};
auto user_id = extract_value(ret[0]);
auto user_id = extract_value(match_ret[0]);
SPDLOG_INFO("user_id: [{}]", user_id);
curl_easy_setopt(curl, CURLOPT_URL, "https://chat.chatgptdemo.net/new_chat");
if (!m_cfg.http_proxy.empty())
curl_easy_setopt(curl, CURLOPT_PROXY, m_cfg.http_proxy.c_str());
input.recv.clear();
auto api_action_cb = [](void* contents, size_t size, size_t nmemb, void* userp) -> size_t {
ret = sendHttpRequest(CurlHttpRequest{
.curl = curl,
.url = "https://chat.chatgptdemo.net/new_chat",
.http_proxy = m_cfg.http_proxy,
.cb = [](void* contents, size_t size, size_t nmemb, void* userp) mutable -> size_t {
auto input_ptr = static_cast<Input*>(userp);
std::string data{(char*)contents, size * nmemb};
auto& [ch, recv] = *input_ptr;
recv.append(data);
return size * nmemb;
};
size_t (*api_cb)(void* contents, size_t size, size_t nmemb, void* userp) = api_action_cb;
curlEasySetopt(curl);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, api_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &input);
},
.input = [&] -> void* {
input.recv.clear();
input.ch = ch;
return &input;
}(),
.headers = [&] -> auto& {
http_headers.emplace("Content-Type", "application/json");
return http_headers;
}(),
.body = [&] -> std::string {
constexpr std::string_view json_str = R"({"user_id":"user_id"})";
nlohmann::json request = nlohmann::json::parse(json_str, nullptr, false);
request["user_id"] = user_id;
auto request_str = request.dump();
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request_str.c_str());
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
return request.dump();
}(),
.response_header_ptr = nullptr,
.expect_response_code = 200,
.ssl_verify = false,
});
if (ret) {
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
auto error_info = std::format("curl_easy_perform() failed:{}", curl_easy_strerror(res));
ch->try_send(err, error_info);
co_return;
}
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
if (response_code != 200) {
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
ch->try_send(err, std::format("chatGptDemo http code:{}", response_code));
ch->try_send(err, ret.value());
co_return;
}
SPDLOG_INFO("input.recv: [{}]", input.recv);
nlohmann::json get_text_rsp = nlohmann::json::parse(input.recv, nullptr, false);
auto chat_id = get_text_rsp["id_"].get<std::string>();
SPDLOG_INFO("chat_id: [{}]", chat_id);
input.recv.clear();
curl_easy_setopt(curl, CURLOPT_URL, "https://chat.chatgptdemo.net/chat_api_stream");
if (!m_cfg.http_proxy.empty())
curl_easy_setopt(curl, CURLOPT_PROXY, m_cfg.http_proxy.c_str());
curlEasySetopt(curl);
auto stream_action_cb = [](void* contents, size_t size, size_t nmemb, void* userp) mutable -> size_t {
ret = sendHttpRequest(CurlHttpRequest{
.curl = curl,
.url = "https://chat.chatgptdemo.net/chat_api_stream",
.http_proxy = m_cfg.http_proxy,
.cb = [](void* contents, size_t size, size_t nmemb, void* userp) mutable -> size_t {
auto input_ptr = static_cast<Input*>(userp);
std::string data{(char*)contents, size * nmemb};
auto& [ch, recv] = *input_ptr;
@ -2709,36 +2784,110 @@ boost::asio::awaitable<void> FreeGpt::chatGptDemo(std::shared_ptr<Channel> ch, n
boost::asio::post(ch->get_executor(), [=] { ch->try_send(err, str); });
}
return size * nmemb;
};
size_t (*stream_action_cb_fn)(void* contents, size_t size, size_t nmemb, void* userp) = stream_action_cb;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, stream_action_cb_fn);
},
.input = [&] -> void* {
input.recv.clear();
input.ch = ch;
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &input);
headers = curl_slist_append(headers, "authority: chat.chatgptdemo.net");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
return &input;
}(),
.headers = http_headers,
.body = [&] -> std::string {
constexpr std::string_view ask_json_str =
R"({"question": "hello", "chat_id": "6524f3640d0d824902f598c1", "timestamp": 1696920420510})";
nlohmann::json ask_request = nlohmann::json::parse(ask_json_str, nullptr, false);
ask_request["question"] = prompt;
ask_request["chat_id"] = chat_id;
uint64_t timestamp = getTimestamp();
request["timestamp"] = timestamp;
ask_request["timestamp"] = timestamp;
std::string ask_request_str = ask_request.dump();
SPDLOG_INFO("ask_request_str: [{}]", ask_request_str);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ask_request_str.c_str());
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
return ask_request_str;
}(),
.response_header_ptr = nullptr,
.expect_response_code = 200,
.ssl_verify = false,
});
if (ret) {
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
ch->try_send(err, ret.value());
co_return;
}
co_return;
}
boost::asio::awaitable<void> FreeGpt::llama2(std::shared_ptr<Channel> ch, nlohmann::json json) {
co_await boost::asio::post(boost::asio::bind_executor(*m_thread_pool_ptr, boost::asio::use_awaitable));
ScopeExit _exit{[=] { boost::asio::post(ch->get_executor(), [=] { ch->close(); }); }};
boost::system::error_code err{};
auto prompt = json.at("meta").at("content").at("parts").at(0).at("content").get<std::string>();
struct Input {
std::shared_ptr<Channel> ch;
std::string recv;
};
Input input;
CURLcode res;
CURL* curl = curl_easy_init();
if (!curl) {
auto error_info = std::format("curl_easy_init() failed:{}", curl_easy_strerror(res));
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
auto error_info = std::format("curl_easy_perform() failed:{}", curl_easy_strerror(res));
ch->try_send(err, error_info);
co_return;
}
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
if (response_code != 200) {
ScopeExit auto_exit{[=] { curl_easy_cleanup(curl); }};
auto ret = sendHttpRequest(CurlHttpRequest{
.curl = curl,
.url = "https://www.llama2.ai/api",
.http_proxy = m_cfg.http_proxy,
.cb = [](void* contents, size_t size, size_t nmemb, void* userp) mutable -> size_t {
auto input_ptr = static_cast<Input*>(userp);
std::string data{(char*)contents, size * nmemb};
auto& [ch, recv] = *input_ptr;
boost::asio::post(ch->get_executor(), [=] {
boost::system::error_code err{};
ch->try_send(err, data);
});
return size * nmemb;
},
.input = [&] -> void* {
input.recv.clear();
input.ch = ch;
return &input;
}(),
.headers = [&] -> auto& {
static std::unordered_map<std::string, std::string> headers{
{"Accept", "*/*"},
{"origin", "https://www.llama2.ai"},
{"referer", "https://www.llama2.ai/"},
{"Content-Type", "text/plain;charset=UTF-8"},
};
return headers;
}(),
.body = [&] -> std::string {
constexpr std::string_view ask_json_str = R"({
"prompt": "[INST] hello [/INST]\n",
"version": "2796ee9483c3fd7aa2e171d38f4ca12251a30609463dcfd4cd76703f22e96cdf",
"systemPrompt": "You are a helpful assistant.",
"temperature": 0.75,
"topP": 0.9,
"maxTokens": 800
})";
nlohmann::json ask_request = nlohmann::json::parse(ask_json_str, nullptr, false);
ask_request["prompt"] = std::format("[INST] {} [/INST]\n", prompt);
std::string ask_request_str = ask_request.dump();
SPDLOG_INFO("ask_request_str: [{}]", ask_request_str);
return ask_request_str;
}(),
.response_header_ptr = nullptr,
.expect_response_code = 200,
.ssl_verify = false,
});
if (ret) {
co_await boost::asio::post(boost::asio::bind_executor(ch->get_executor(), boost::asio::use_awaitable));
ch->try_send(err, std::format("gptalk http code:{}", response_code));
ch->try_send(err, ret.value());
co_return;
}
co_return;

View File

@ -351,6 +351,7 @@ int main(int argc, char** argv) {
ADD_METHOD("gpt-3.5-turbo-stream-ChatForAi", FreeGpt::chatForAi);
ADD_METHOD("gpt-3.5-turbo-stream-gptforlove", FreeGpt::gptForLove);
ADD_METHOD("gpt-3.5-turbo-stream-ChatgptDemo", FreeGpt::chatGptDemo);
ADD_METHOD("llama2", FreeGpt::llama2);
SPDLOG_INFO("active provider:");
for (auto& [provider, _] : gpt_function)