6 #define CPPHTTPLIB_READ_TIMEOUT_SECOND 60*60
26 virtual std::vector<std::vector<double>>
Evaluate(
const std::vector<std::vector<double>>& inputs,
28 (void)inputs; (void)config_json;
29 throw std::runtime_error(
"Evaluate was called, but not implemented by model!");
32 virtual std::vector<double>
Gradient(
unsigned int outWrt,
34 const std::vector<std::vector<double>>& inputs,
35 const std::vector<double>& sens,
37 (void)outWrt; (void)inWrt; (void)inputs; (void)sens; (void)config_json;
38 throw std::runtime_error(
"Gradient was called, but not implemented by model!");
43 const std::vector<std::vector<double>>& inputs,
44 const std::vector<double>& vec,
46 (void)outWrt; (void)inWrt; (void)inputs; (void)vec; (void)config_json;
47 throw std::runtime_error(
"ApplyJacobian was called, but not implemented by model!");
53 const std::vector<std::vector<double>>& inputs,
54 const std::vector<double>& sens,
55 const std::vector<double>& vec,
57 (void)outWrt; (void)inWrt1; (void)inWrt2; (void)inputs; (void)sens; (void)vec; (void)config_json;
58 throw std::runtime_error(
"ApplyHessian was called, but not implemented by model!");
82 if (response.
value<
double>(
"protocolVersion",0) != 1.0)
83 throw std::runtime_error(
"Model protocol version not supported!");
86 std::vector<std::string> models = response[
"models"];
87 if (std::find(models.begin(), models.end(),
name) == models.end()) {
88 std::string model_names =
"";
89 for (
auto& m : models) {
90 model_names +=
"'" + m +
"' ";
92 throw std::runtime_error(
"Model " +
name +
" not found on server! Available models: " + model_names +
".");
96 throw std::runtime_error(
"GET Info failed with error type '" +
to_string(res.error()) +
"'");
100 request_body[
"name"] =
name;
102 if (
auto res =
cli.Post(
"/ModelInfo",
headers, request_body.
dump(),
"application/json")) {
105 json supported_features = response.
at(
"support");
111 throw std::runtime_error(
"POST ModelInfo failed with error type '" +
to_string(res.error()) +
"'");
118 request_body[
"name"] =
name;
119 if (!config_json.empty())
120 request_body[
"config"] = config_json;
122 if (
auto res =
cli.Post(
"/InputSizes",
headers, request_body.
dump(),
"application/json")) {
124 std::vector<std::size_t> outputvec = response_body[
"inputSizes"].
get<std::vector<std::size_t>>();
127 throw std::runtime_error(
"POST InputSizes failed with error type '" +
to_string(res.error()) +
"'");
128 return std::vector<std::size_t>(0);
135 request_body[
"name"] =
name;
136 if (!config_json.empty())
137 request_body[
"config"] = config_json;
139 if (
auto res =
cli.Post(
"/OutputSizes",
headers, request_body.
dump(),
"application/json")) {
141 std::vector<std::size_t> outputvec = response_body[
"outputSizes"].
get<std::vector<std::size_t>>();
144 throw std::runtime_error(
"POST OutputSizes failed with error type '" +
to_string(res.error()) +
"'");
145 return std::vector<std::size_t>(0);
149 std::vector<std::vector<double>>
Evaluate(
const std::vector<std::vector<double>>& inputs,
json config_json =
json())
override {
152 request_body[
"name"] =
name;
154 for (std::size_t i = 0; i < inputs.size(); i++) {
155 request_body[
"input"][i] = inputs[i];
157 if (!config_json.empty())
158 request_body[
"config"] = config_json;
160 if (
auto res =
cli.Post(
"/Evaluate",
headers, request_body.
dump(),
"application/json")) {
164 std::vector<std::vector<double>> outputs(response_body[
"output"].size());
165 for (std::size_t i = 0; i < response_body[
"output"].
size(); i++) {
166 outputs[i] = response_body[
"output"][i].
get<std::vector<double>>();
170 throw std::runtime_error(
"POST Evaluate failed with error type '" +
to_string(res.error()) +
"'");
176 const std::vector<std::vector<double>>& inputs,
177 const std::vector<double>& sens,
182 request_body[
"name"] =
name;
183 request_body[
"outWrt"] = outWrt;
184 request_body[
"inWrt"] = inWrt;
185 for (std::size_t i = 0; i < inputs.size(); i++) {
186 request_body[
"input"][i] = inputs[i];
188 request_body[
"sens"] = sens;
189 if (!config_json.empty())
190 request_body[
"config"] = config_json;
192 if (
auto res =
cli.Post(
"/Gradient",
headers, request_body.
dump(),
"application/json")) {
196 return response_body[
"output"].
get<std::vector<double>>();
198 throw std::runtime_error(
"POST Gradient failed with error type '" +
to_string(res.error()) +
"'");
204 const std::vector<std::vector<double>>& inputs,
205 const std::vector<double>& vec,
206 json config_json =
json())
override {
209 request_body[
"name"] =
name;
210 request_body[
"outWrt"] = outWrt;
211 request_body[
"inWrt"] = inWrt;
212 for (std::size_t i = 0; i < inputs.size(); i++) {
213 request_body[
"input"][i] = inputs[i];
215 request_body[
"vec"] = vec;
216 if (!config_json.empty())
217 request_body[
"config"] = config_json;
219 if (
auto res =
cli.Post(
"/ApplyJacobian",
headers, request_body.
dump(),
"application/json")) {
223 return response_body[
"output"].
get<std::vector<double>>();
225 throw std::runtime_error(
"POST ApplyJacobian failed with error type '" +
to_string(res.error()) +
"'");
232 const std::vector<std::vector<double>>& inputs,
233 const std::vector<double>& sens,
234 const std::vector<double>& vec,
235 json config_json =
json())
override {
238 request_body[
"name"] =
name;
239 request_body[
"outWrt"] = outWrt;
240 request_body[
"inWrt1"] = inWrt1;
241 request_body[
"inWrt2"] = inWrt2;
242 for (std::size_t i = 0; i < inputs.size(); i++) {
243 request_body[
"input"][i] = inputs[i];
245 request_body[
"sens"] = sens;
246 request_body[
"vec"] = vec;
247 if (!config_json.empty())
248 request_body[
"config"] = config_json;
250 if (
auto res =
cli.Post(
"/ApplyHessian",
headers, request_body.
dump(),
"application/json")) {
254 return response_body[
"output"].
get<std::vector<double>>();
256 throw std::runtime_error(
"POST ApplyHessian failed with error type '" +
to_string(res.error()) +
"'");
275 mutable httplib::Client
cli;
285 if (response_body.
find(
"error") != response_body.
end()) {
286 throw std::runtime_error(
"Model server returned error of type " + response_body[
"error"][
"type"].get<std::string>() +
", message: " + response_body[
"error"][
"message"].get<std::string>());
293 bool check_input_sizes(
const std::vector<std::vector<double>>& inputs,
const json& config_json,
const Model& model, httplib::Response& res) {
296 response_body[
"error"][
"type"] =
"InvalidInput";
297 response_body[
"error"][
"message"] =
"Number of inputs does not match number of model inputs. Expected " +
std::to_string(model.
GetInputSizes(config_json).size()) +
" but got " +
std::to_string(inputs.size());
298 res.set_content(response_body.
dump(),
"application/json");
302 for (std::size_t i = 0; i < inputs.size(); i++) {
303 if (inputs[i].size() != model.
GetInputSizes(config_json)[i]) {
305 response_body[
"error"][
"type"] =
"InvalidInput";
307 res.set_content(response_body.
dump(),
"application/json");
319 response_body[
"error"][
"type"] =
"InvalidInput";
321 res.set_content(response_body.
dump(),
"application/json");
332 response_body[
"error"][
"type"] =
"InvalidInput";
334 res.set_content(response_body.
dump(),
"application/json");
345 response_body[
"error"][
"type"] =
"InvalidOutput";
346 response_body[
"error"][
"message"] =
"Number of outputs declared by model does not match number of outputs returned by model. Model declared " +
std::to_string(model.
GetOutputSizes(config_json).size()) +
" but returned " +
std::to_string(outputs.size());
347 res.set_content(response_body.
dump(),
"application/json");
351 for (std::size_t i = 0; i < outputs.size(); i++) {
354 response_body[
"error"][
"type"] =
"InvalidOutput";
356 res.set_content(response_body.
dump(),
"application/json");
366 if (inWrt < 0 || inWrt >= (
int)model.
GetInputSizes(config_json).size()) {
368 response_body[
"error"][
"type"] =
"InvalidInput";
370 res.set_content(response_body.
dump(),
"application/json");
379 if (outWrt < 0 || outWrt >= (
int)model.
GetOutputSizes(config_json).size()) {
381 response_body[
"error"][
"type"] =
"InvalidInput";
383 res.set_content(response_body.
dump(),
"application/json");
393 response_body[
"error"][
"type"] =
"UnsupportedFeature";
394 response_body[
"error"][
"message"] =
"Feature '" + feature +
"' is not supported by this model";
395 res.set_content(response_body.
dump(),
"application/json");
401 for (
auto& model : models) {
402 if (model->GetName() == name) {
406 throw std::runtime_error(
"Model not found");
413 }
catch (std::runtime_error&
e) {
415 response_body[
"error"][
"type"] =
"ModelNotFound";
416 response_body[
"error"][
"message"] =
"Model '" + name +
"' not supported by this server!";
417 res.set_content(response_body.
dump(),
"application/json");
425 void serveModels(std::vector<Model*> models, std::string host,
int port) {
428 std::mutex model_mutex;
430 svr.Post(
"/Evaluate", [&](
const httplib::Request &req, httplib::Response &res) {
441 std::vector<std::vector<double>> inputs(request_body[
"input"].size());
442 for (std::size_t i = 0; i < inputs.size(); i++) {
443 inputs[i] = request_body[
"input"][i].
get<std::vector<double>>();
446 json empty_default_config;
447 json config_json = request_body.
value(
"config", empty_default_config);
452 const std::lock_guard<std::mutex> model_lock(model_mutex);
453 std::vector<std::vector<double>> outputs = model.
Evaluate(inputs, config_json);
459 for (std::size_t i = 0; i < outputs.size(); i++) {
460 response_body[
"output"][i] = outputs[i];
463 res.set_content(response_body.
dump(),
"application/json");
466 svr.Post(
"/Gradient", [&](
const httplib::Request &req, httplib::Response &res) {
477 unsigned int inWrt = request_body.
at(
"inWrt");
478 unsigned int outWrt = request_body.
at(
"outWrt");
480 std::vector<std::vector<double>> inputs(request_body[
"input"].size());
481 for (std::size_t i = 0; i < inputs.size(); i++) {
482 inputs[i] = request_body[
"input"][i].
get<std::vector<double>>();
485 std::vector<double> sens = request_body.
at(
"sens");
487 json empty_default_config;
488 json config_json = request_body.
value(
"config", empty_default_config);
499 const std::lock_guard<std::mutex> model_lock(model_mutex);
500 std::vector<double> gradient = model.
Gradient(outWrt, inWrt, inputs, sens, config_json);
503 response_body[
"output"] = gradient;
505 res.set_content(response_body.
dump(),
"application/json");
508 svr.Post(
"/ApplyJacobian", [&](
const httplib::Request &req, httplib::Response &res) {
519 unsigned int inWrt = request_body.
at(
"inWrt");
520 unsigned int outWrt = request_body.
at(
"outWrt");
522 std::vector<std::vector<double>> inputs(request_body[
"input"].size());
523 for (std::size_t i = 0; i < inputs.size(); i++) {
524 inputs[i] = request_body[
"input"][i].
get<std::vector<double>>();
527 std::vector<double> vec = request_body.
at(
"vec");
529 json empty_default_config;
530 json config_json = request_body.
value(
"config", empty_default_config);
541 const std::lock_guard<std::mutex> model_lock(model_mutex);
542 std::vector<double> jacobian_action = model.
ApplyJacobian(outWrt, inWrt, inputs, vec, config_json);
545 response_body[
"output"] = jacobian_action;
547 res.set_content(response_body.
dump(),
"application/json");
550 svr.Post(
"/ApplyHessian", [&](
const httplib::Request &req, httplib::Response &res) {
561 unsigned int outWrt = request_body.
at(
"outWrt");
562 unsigned int inWrt1 = request_body.
at(
"inWrt1");
563 unsigned int inWrt2 = request_body.
at(
"inWrt2");
565 std::vector<std::vector<double>> inputs(request_body[
"input"].size());
566 for (std::size_t i = 0; i < inputs.size(); i++) {
567 inputs[i] = request_body[
"input"][i].
get<std::vector<double>>();
570 std::vector<double> sens = request_body.
at(
"sens");
571 std::vector<double> vec = request_body.
at(
"vec");
573 json empty_default_config;
574 json config_json = request_body.
value(
"config", empty_default_config);
587 const std::lock_guard<std::mutex> model_lock(model_mutex);
588 std::vector<double> hessian_action = model.
ApplyHessian(outWrt, inWrt1, inWrt2, inputs, sens, vec, config_json);
591 response_body[
"output"] = hessian_action;
593 res.set_content(response_body.
dump(),
"application/json");
596 svr.Get(
"/Info", [&](
const httplib::Request &, httplib::Response &res) {
598 response_body[
"protocolVersion"] = 1.0;
599 std::vector<std::string> model_names;
600 for (
auto& model : models) {
603 response_body[
"models"] = model_names;
605 res.set_content(response_body.
dump(),
"application/json");
608 svr.Post(
"/ModelInfo", [&](
const httplib::Request &req, httplib::Response &res) {
615 response_body[
"support"] = {};
621 res.set_content(response_body.
dump(),
"application/json");
624 svr.Post(
"/InputSizes", [&](
const httplib::Request &req, httplib::Response &res) {
630 json empty_default_config;
631 json config_json = request_body.
value(
"config", empty_default_config);
634 response_body[
"inputSizes"] = model.
GetInputSizes(config_json);
636 res.set_content(response_body.
dump(),
"application/json");
639 svr.Post(
"/OutputSizes", [&](
const httplib::Request &req, httplib::Response &res) {
645 json empty_default_config;
646 json config_json = request_body.
value(
"config", empty_default_config);
651 res.set_content(response_body.
dump(),
"application/json");
654 std::cout <<
"Listening on port " << port <<
"..." << std::endl;
655 svr.listen(host.c_str(), port);
656 std::cout <<
"Quit" << std::endl;
a class to store JSON values
basic_json get() const
get special-case overload
ValueType value(const typename object_t::key_type &key, const ValueType &default_value) const
access specified object element with default value
static JSON_HEDLEY_WARN_UNUSED_RESULT basic_json parse(InputType &&i, const parser_callback_t cb=nullptr, const bool allow_exceptions=true, const bool ignore_comments=false)
deserialize from a compatible input
size_type size() const noexcept
returns the number of elements
string_t dump(const int indent=-1, const char indent_char=' ', const bool ensure_ascii=false, const error_handler_t error_handler=error_handler_t::strict) const
serialization
reference at(size_type idx)
access specified array element with bounds checking
iterator end() noexcept
returns an iterator to one past the last element
void push_back(basic_json &&val)
add an object to an array
iterator find(KeyT &&key)
find an element in a JSON object
bool supportsApplyHessian
bool SupportsApplyHessian() override
bool SupportsEvaluate() override
bool SupportsGradient() override
bool SupportsApplyJacobian() override
std::vector< std::vector< double > > Evaluate(const std::vector< std::vector< double >> &inputs, json config_json=json()) override
std::vector< std::size_t > GetOutputSizes(const json &config_json=json()) const override
std::vector< double > Gradient(unsigned int outWrt, unsigned int inWrt, const std::vector< std::vector< double >> &inputs, const std::vector< double > &sens, json config_json=json()) override
void throw_if_error_in_response(const json &response_body)
std::vector< double > ApplyHessian(unsigned int outWrt, unsigned int inWrt1, unsigned int inWrt2, const std::vector< std::vector< double >> &inputs, const std::vector< double > &sens, const std::vector< double > &vec, json config_json=json()) override
bool supportsApplyJacobian
std::vector< double > ApplyJacobian(unsigned int outWrt, unsigned int inWrt, const std::vector< std::vector< double >> &inputs, const std::vector< double > &vec, json config_json=json()) override
HTTPModel(std::string host, std::string name, httplib::Headers headers=httplib::Headers())
std::vector< std::size_t > GetInputSizes(const json &config_json=json()) const override
virtual bool SupportsGradient()
virtual std::vector< double > ApplyHessian(unsigned int outWrt, unsigned int inWrt1, unsigned int inWrt2, const std::vector< std::vector< double >> &inputs, const std::vector< double > &sens, const std::vector< double > &vec, json config_json=json())
virtual bool SupportsApplyHessian()
virtual std::vector< std::size_t > GetInputSizes(const json &config_json=json()) const =0
virtual std::vector< double > Gradient(unsigned int outWrt, unsigned int inWrt, const std::vector< std::vector< double >> &inputs, const std::vector< double > &sens, json config_json=json())
virtual std::vector< std::vector< double > > Evaluate(const std::vector< std::vector< double >> &inputs, json config_json=json())
virtual std::vector< double > ApplyJacobian(unsigned int outWrt, unsigned int inWrt, const std::vector< std::vector< double >> &inputs, const std::vector< double > &vec, json config_json=json())
virtual std::vector< std::size_t > GetOutputSizes(const json &config_json=json()) const =0
std::string GetName() const
virtual bool SupportsApplyJacobian()
virtual bool SupportsEvaluate()
basic_json<> json
default JSON class
NLOHMANN_BASIC_JSON_TPL_DECLARATION std::string to_string(const NLOHMANN_BASIC_JSON_TPL &j)
user-defined to_string function for JSON values
void write_unsupported_feature_response(httplib::Response &res, std::string feature)
bool check_output_sizes(const std::vector< std::vector< double >> &outputs, const json &config_json, const Model &model, httplib::Response &res)
bool check_sensitivity_size(const std::vector< double > &sens, int outWrt, const json &config_json, const Model &model, httplib::Response &res)
bool check_input_sizes(const std::vector< std::vector< double >> &inputs, const json &config_json, const Model &model, httplib::Response &res)
bool check_output_wrt(int outWrt, const json &config_json, const Model &model, httplib::Response &res)
void serveModels(std::vector< Model * > models, std::string host, int port)
bool check_vector_size(const std::vector< double > &vec, int inWrt, const json &config_json, const Model &model, httplib::Response &res)
bool check_input_wrt(int inWrt, const json &config_json, const Model &model, httplib::Response &res)
Model & get_model_from_name(std::vector< Model * > &models, std::string name)
bool check_model_exists(std::vector< Model * > &models, std::string name, httplib::Response &res)