char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
c264f65ff
char/aliceai-t5-moe
1#include "cli-client.h" 2 3#include "http.h" 4 5#include <algorithm> 6#include <chrono> 7#include <thread> 8 9// generation can stall for a long time during prompt processing, so the 10// read timeout must be generous 11static constexpr time_t CLI_HTTP_READ_TIMEOUT_SEC = 3600 ; 12 13// upper bound for the accumulated response body kept for error reporting 14static constexpr size_t CLI_HTTP_MAX_ERROR_BODY = 1024 * 1024 ; 15 16// returns the path with the base url's path prefix prepended (if any) 17static std::string join_path (const common_http_url & parts ,const std::string & path ) { 18if (parts .path .empty ()|| parts .path == "/" ) { 19return path ; 20 } 21 std::string prefix = parts .path ; 22if (prefix .back ()== '/' ) { 23prefix .pop_back (); 24 } 25return prefix + path ; 26} 27 28std::string cli_client::get (const std::string & path ) { 29auto [cli ,parts ]= common_http_client (server_base ); 30cli .set_read_timeout (CLI_HTTP_READ_TIMEOUT_SEC ,0 ); 31auto path_with_model = path + (model .empty () ?"" : ("?model=" + model )); 32auto res = cli .Get (join_path (parts ,path_with_model )); 33if (!res ) { 34throw std::runtime_error ("failed to connect to " + server_base + ": " + httplib::to_string (res .error ())); 35 } 36if (res -> status < 200 || res -> status >=300 ) { 37throw std::runtime_error ("GET " + path + " failed with status " + std::to_string (res -> status )+ ": " + res -> body ); 38 } 39return res -> body ; 40} 41 42std::string cli_client::post (const std::string & path ,const std::string & body ) { 43auto [cli ,parts ]= common_http_client (server_base ); 44cli .set_read_timeout (CLI_HTTP_READ_TIMEOUT_SEC ,0 ); 45auto res = cli .Post (join_path (parts ,path ),body ,"application/json" ); 46if (!res ) { 47throw std::runtime_error ("failed to connect to " + server_base + ": " + httplib::to_string (res .error ())); 48 } 49if (res -> status < 200 || res -> status >=300 ) { 50throw std::runtime_error ("POST " + path + " failed with status " + std::to_string (res -> status )+ ": " + res -> body ); 51 } 52return res -> body ; 53} 54 55std::string cli_client::post_sse (const std::string & path , 56const std::string & body , 57const std::function < bool ()> & should_stop , 58const std::function < void (const std::string & )> & on_data ) { 59auto [cli ,parts ]= common_http_client (server_base ); 60cli .set_read_timeout (CLI_HTTP_READ_TIMEOUT_SEC ,0 ); 61 62 std::string pending ;// buffer for incomplete SSE lines 63 std::string raw_body ;// accumulated body, used only for error reporting 64 65auto receiver = [& ](const char * data ,size_t len )-> bool { 66if (should_stop ()) { 67return false;// aborts the request 68 } 69if (raw_body .size ()< CLI_HTTP_MAX_ERROR_BODY ) { 70raw_body .append (data , std::min (len ,CLI_HTTP_MAX_ERROR_BODY - raw_body .size ())); 71 } 72pending .append (data ,len ); 73size_t pos ; 74while ((pos = pending .find ('\n' ))!= std::string::npos ) { 75 std::string line = pending .substr (0 ,pos ); 76pending .erase (0 ,pos + 1 ); 77if (!line .empty ()&& line .back ()== '\r' ) { 78line .pop_back (); 79 } 80if (line .rfind ("data: " ,0 )!= 0 ) { 81continue ; 82 } 83 std::string payload = line .substr (6 ); 84if (payload == "[DONE]" ) { 85continue ; 86 } 87on_data (payload ); 88 } 89return true; 90 }; 91 92 httplib::Headers headers = {{"Accept" ,"text/event-stream" }}; 93auto res = cli .Post (join_path (parts ,path ),headers ,body ,"application/json" ,receiver ); 94 95if (!res ) { 96if (res .error ()== httplib::Error ::Canceled && should_stop ()) { 97return "" ;// cancelled by the user 98 } 99return "failed to connect to " + server_base + ": " + httplib::to_string (res .error ()); 100 } 101if (res -> status < 200 || res -> status >=300 ) { 102if (!raw_body .empty ()) { 103return raw_body ; 104 } 105return "request failed with status " + std::to_string (res -> status ); 106 } 107return "" ; 108} 109 110bool cli_client::wait_health (const std::function < bool ()> & is_aborted ) { 111int connect_attempts = 0 ; 112while (!is_aborted ()) { 113auto [cli ,parts ]= common_http_client (server_base ); 114cli .set_connection_timeout (1 ,0 ); 115auto res = cli .Get (join_path (parts ,"/health" )); 116if (res ) { 117if (res -> status == 200 ) { 118return true; 119 } 120// any other status means the server is up but not ready yet 121// (e.g. 503 while the model is still loading) 122 }else if (++ connect_attempts >=10 ) { 123last_error = "failed to connect to " + server_base + ": " + httplib::to_string (res .error ()); 124return false; 125 } 126 std::this_thread::sleep_for (std::chrono::milliseconds (300 )); 127 } 128last_error = "aborted while waiting for the server to become ready" ; 129return false; 130}