openai.hpp (37475B)
1 // The MIT License (MIT) 2 // 3 // Copyright (c) 2023 Olrea, Florian Dang 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 // 12 // The above copyright notice and this permission notice shall be included in all 13 // copies or substantial portions of the Software. 14 // 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 // SOFTWARE. 22 23 #ifndef OPENAI_HPP_ 24 #define OPENAI_HPP_ 25 26 27 #if OPENAI_VERBOSE_OUTPUT 28 #pragma message ("OPENAI_VERBOSE_OUTPUT is ON") 29 #endif 30 31 #include <iostream> 32 #include <stdexcept> 33 #include <string> 34 #include <vector> 35 #include <sstream> 36 #include <mutex> 37 #include <cstdlib> 38 #include <map> 39 40 #ifndef CURL_STATICLIB 41 #include <curl/curl.h> 42 #else 43 #include "curl/curl.h" 44 #endif 45 46 #include <nlohmann/json.hpp> // nlohmann/json 47 48 namespace openai { 49 50 namespace _detail { 51 52 // Json alias 53 using Json = nlohmann::json; 54 55 struct Response { 56 std::string text; 57 bool is_error; 58 std::string error_message; 59 }; 60 61 // Simple curl Session inspired by CPR 62 class Session { 63 public: 64 Session(bool throw_exception) : throw_exception_{throw_exception} { 65 initCurl(); 66 ignoreSSL(); 67 } 68 69 Session(bool throw_exception, std::string proxy_url) : throw_exception_{ throw_exception } { 70 initCurl(); 71 ignoreSSL(); 72 setProxyUrl(proxy_url); 73 } 74 75 ~Session() { 76 curl_easy_cleanup(curl_); 77 curl_global_cleanup(); 78 if (mime_form_ != nullptr) { 79 curl_mime_free(mime_form_); 80 } 81 } 82 83 void initCurl() { 84 curl_global_init(CURL_GLOBAL_ALL); 85 curl_ = curl_easy_init(); 86 if (curl_ == nullptr) { 87 throw std::runtime_error("curl cannot initialize"); // here we throw it shouldn't happen 88 } 89 curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1); 90 } 91 92 void ignoreSSL() { 93 curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 0L); 94 } 95 96 void setUrl(const std::string& url) { url_ = url; } 97 98 void setToken(const std::string& token, const std::string& organization) { 99 token_ = token; 100 organization_ = organization; 101 } 102 void setProxyUrl(const std::string& url) { 103 proxy_url_ = url; 104 curl_easy_setopt(curl_, CURLOPT_PROXY, proxy_url_.c_str()); 105 106 } 107 108 void setBeta(const std::string& beta) { beta_ = beta; } 109 110 void setBody(const std::string& data); 111 void setMultiformPart(const std::pair<std::string, std::string>& filefield_and_filepath, const std::map<std::string, std::string>& fields); 112 113 Response getPrepare(); 114 Response postPrepare(const std::string& contentType = ""); 115 Response deletePrepare(); 116 Response makeRequest(const std::string& contentType = ""); 117 std::string easyEscape(const std::string& text); 118 119 private: 120 static size_t writeFunction(void* ptr, size_t size, size_t nmemb, std::string* data) { 121 data->append((char*) ptr, size * nmemb); 122 return size * nmemb; 123 } 124 125 private: 126 CURL* curl_; 127 CURLcode res_; 128 curl_mime *mime_form_ = nullptr; 129 std::string url_; 130 std::string proxy_url_; 131 std::string token_; 132 std::string organization_; 133 std::string beta_; 134 135 bool throw_exception_; 136 std::mutex mutex_request_; 137 }; 138 139 inline void Session::setBody(const std::string& data) { 140 if (curl_) { 141 curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE, data.length()); 142 curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, data.data()); 143 } 144 } 145 146 inline void Session::setMultiformPart(const std::pair<std::string, std::string>& fieldfield_and_filepath, const std::map<std::string, std::string>& fields) { 147 // https://curl.se/libcurl/c/curl_mime_init.html 148 if (curl_) { 149 if (mime_form_ != nullptr) { 150 curl_mime_free(mime_form_); 151 mime_form_ = nullptr; 152 } 153 curl_mimepart *field = nullptr; 154 155 mime_form_ = curl_mime_init(curl_); 156 157 field = curl_mime_addpart(mime_form_); 158 curl_mime_name(field, fieldfield_and_filepath.first.c_str()); 159 curl_mime_filedata(field, fieldfield_and_filepath.second.c_str()); 160 161 for (const auto &field_pair : fields) { 162 field = curl_mime_addpart(mime_form_); 163 curl_mime_name(field, field_pair.first.c_str()); 164 curl_mime_data(field, field_pair.second.c_str(), CURL_ZERO_TERMINATED); 165 } 166 167 curl_easy_setopt(curl_, CURLOPT_MIMEPOST, mime_form_); 168 } 169 } 170 171 inline Response Session::getPrepare() { 172 if (curl_) { 173 curl_easy_setopt(curl_, CURLOPT_HTTPGET, 1L); 174 curl_easy_setopt(curl_, CURLOPT_POST, 0L); 175 curl_easy_setopt(curl_, CURLOPT_NOBODY, 0L); 176 } 177 return makeRequest(); 178 } 179 180 inline Response Session::postPrepare(const std::string& contentType) { 181 return makeRequest(contentType); 182 } 183 184 inline Response Session::deletePrepare() { 185 if (curl_) { 186 curl_easy_setopt(curl_, CURLOPT_HTTPGET, 0L); 187 curl_easy_setopt(curl_, CURLOPT_NOBODY, 0L); 188 curl_easy_setopt(curl_, CURLOPT_CUSTOMREQUEST, "DELETE"); 189 } 190 return makeRequest(); 191 } 192 193 inline Response Session::makeRequest(const std::string& contentType) { 194 std::lock_guard<std::mutex> lock(mutex_request_); 195 196 struct curl_slist* headers = NULL; 197 if (!contentType.empty()) { 198 headers = curl_slist_append(headers, std::string{"Content-Type: " + contentType}.c_str()); 199 if (contentType == "multipart/form-data") { 200 headers = curl_slist_append(headers, "Expect:"); 201 } 202 } 203 headers = curl_slist_append(headers, std::string{"Authorization: Bearer " + token_}.c_str()); 204 if (!organization_.empty()) { 205 headers = curl_slist_append(headers, std::string{"OpenAI-Organization: " + organization_}.c_str()); 206 } 207 if (!beta_.empty()) { 208 headers = curl_slist_append(headers, std::string{"OpenAI-Beta: " + beta_}.c_str()); 209 } 210 curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers); 211 curl_easy_setopt(curl_, CURLOPT_URL, url_.c_str()); 212 213 std::string response_string; 214 std::string header_string; 215 curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, writeFunction); 216 curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &response_string); 217 curl_easy_setopt(curl_, CURLOPT_HEADERDATA, &header_string); 218 219 res_ = curl_easy_perform(curl_); 220 221 bool is_error = false; 222 std::string error_msg{}; 223 if(res_ != CURLE_OK) { 224 is_error = true; 225 error_msg = "OpenAI curl_easy_perform() failed: " + std::string{curl_easy_strerror(res_)}; 226 if (throw_exception_) { 227 throw std::runtime_error(error_msg); 228 } 229 else { 230 std::cerr << error_msg << '\n'; 231 } 232 } 233 234 return { response_string, is_error, error_msg }; 235 } 236 237 inline std::string Session::easyEscape(const std::string& text) { 238 char *encoded_output = curl_easy_escape(curl_, text.c_str(), static_cast<int>(text.length())); 239 const auto str = std::string{ encoded_output }; 240 curl_free(encoded_output); 241 return str; 242 } 243 244 // forward declaration for category structures 245 class OpenAI; 246 247 // https://platform.openai.com/docs/api-reference/models 248 // List and describe the various models available in the API. You can refer to the Models documentation to understand what models are available and the differences between them. 249 struct CategoryModel { 250 Json list(); 251 Json retrieve(const std::string& model); 252 253 CategoryModel(OpenAI& openai) : openai_{openai} {} 254 private: 255 OpenAI& openai_; 256 }; 257 258 // https://platform.openai.com/docs/api-reference/assistants 259 // Build assistants that can call models and use tools to perform tasks. 260 struct CategoryAssistants { 261 Json create(Json input); 262 Json retrieve(const std::string& assistants); 263 Json modify(const std::string& assistants, Json input); 264 Json del(const std::string& assistants); 265 Json list(); 266 Json createFile(const std::string& assistants, Json input); 267 Json retrieveFile(const std::string& assistants, const std::string& files); 268 Json delFile(const std::string& assistants, const std::string& files); 269 Json listFile(const std::string& assistants); 270 271 CategoryAssistants(OpenAI& openai) : openai_{openai} {} 272 private: 273 OpenAI& openai_; 274 }; 275 276 // https://platform.openai.com/docs/api-reference/threads 277 // Create threads that assistants can interact with. 278 struct CategoryThreads { 279 Json create(); 280 Json retrieve(const std::string& threads); 281 Json modify(const std::string& threads, Json input); 282 Json del(const std::string& threads); 283 Json list(); 284 285 // https://platform.openai.com/docs/api-reference/messages 286 // Create messages within threads 287 Json createMessage(const std::string& threads, Json input); 288 Json retrieveMessage(const std::string& threads, const std::string& messages); 289 Json modifyMessage(const std::string& threads, const std::string& messages, Json input); 290 Json listMessage(const std::string& threads); 291 Json retrieveMessageFile(const std::string& threads, const std::string& messages, const std::string& files); 292 Json listMessageFile(const std::string& threads, const std::string& messages); 293 294 // https://platform.openai.com/docs/api-reference/runs 295 // Represents an execution run on a thread. 296 Json createRun(const std::string& threads, Json input); 297 Json retrieveRun(const std::string& threads, const std::string& runs); 298 Json modifyRun(const std::string& threads, const std::string& runs, Json input); 299 Json listRun(const std::string& threads); 300 Json submitToolOutputsToRun(const std::string& threads, const std::string& runs, Json input); 301 Json cancelRun(const std::string& threads, const std::string& runs); 302 Json createThreadAndRun(Json input); 303 Json retrieveRunStep(const std::string& threads, const std::string& runs, const std::string& steps); 304 Json listRunStep(const std::string& threads, const std::string& runs); 305 306 CategoryThreads(OpenAI& openai) : openai_{openai} {} 307 private: 308 OpenAI& openai_; 309 }; 310 311 // https://platform.openai.com/docs/api-reference/completions 312 // Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position. 313 struct CategoryCompletion { 314 Json create(Json input); 315 316 CategoryCompletion(OpenAI& openai) : openai_{openai} {} 317 318 private: 319 OpenAI& openai_; 320 }; 321 322 // https://platform.openai.com/docs/api-reference/chat 323 // Given a prompt, the model will return one or more predicted chat completions. 324 struct CategoryChat { 325 Json create(Json input); 326 327 CategoryChat(OpenAI& openai) : openai_{openai} {} 328 329 private: 330 OpenAI& openai_; 331 }; 332 333 // https://platform.openai.com/docs/api-reference/audio 334 // Learn how to turn audio into text. 335 struct CategoryAudio { 336 Json transcribe(Json input); 337 Json translate(Json input); 338 339 CategoryAudio(OpenAI& openai) : openai_{openai} {} 340 341 private: 342 OpenAI& openai_; 343 }; 344 345 // https://platform.openai.com/docs/api-reference/edits 346 // Given a prompt and an instruction, the model will return an edited version of the prompt. 347 struct CategoryEdit { 348 Json create(Json input); 349 350 CategoryEdit(OpenAI& openai) : openai_{openai} {} 351 352 private: 353 OpenAI& openai_; 354 }; 355 356 357 // https://platform.openai.com/docs/api-reference/images 358 // Given a prompt and/or an input image, the model will generate a new image. 359 struct CategoryImage { 360 Json create(Json input); 361 Json edit(Json input); 362 Json variation(Json input); 363 364 CategoryImage(OpenAI& openai) : openai_{openai} {} 365 366 private: 367 OpenAI& openai_; 368 }; 369 370 // https://platform.openai.com/docs/api-reference/embeddings 371 // Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. 372 struct CategoryEmbedding { 373 Json create(Json input); 374 CategoryEmbedding(OpenAI& openai) : openai_{openai} {} 375 376 private: 377 OpenAI& openai_; 378 }; 379 380 struct FileRequest { 381 std::string file; 382 std::string purpose; 383 }; 384 385 // https://platform.openai.com/docs/api-reference/files 386 // Files are used to upload documents that can be used with features like Fine-tuning. 387 struct CategoryFile { 388 Json list(); 389 Json upload(Json input); 390 Json del(const std::string& file); // TODO 391 Json retrieve(const std::string& file_id); 392 Json content(const std::string& file_id); 393 394 CategoryFile(OpenAI& openai) : openai_{openai} {} 395 396 private: 397 OpenAI& openai_; 398 }; 399 400 // https://platform.openai.com/docs/api-reference/fine-tunes 401 // Manage fine-tuning jobs to tailor a model to your specific training data. 402 struct CategoryFineTune { 403 Json create(Json input); 404 Json list(); 405 Json retrieve(const std::string& fine_tune_id); 406 Json content(const std::string& fine_tune_id); 407 Json cancel(const std::string& fine_tune_id); 408 Json events(const std::string& fine_tune_id); 409 Json del(const std::string& model); 410 411 CategoryFineTune(OpenAI& openai) : openai_{openai} {} 412 413 private: 414 OpenAI& openai_; 415 }; 416 417 // https://platform.openai.com/docs/api-reference/moderations 418 // Given a input text, outputs if the model classifies it as violating OpenAI's content policy. 419 struct CategoryModeration { 420 Json create(Json input); 421 422 CategoryModeration(OpenAI& openai) : openai_{openai} {} 423 424 private: 425 OpenAI& openai_; 426 }; 427 428 429 // OpenAI 430 class OpenAI { 431 public: 432 OpenAI(const std::string& token = "", const std::string& organization = "", bool throw_exception = true, const std::string& api_base_url = "", const std::string& beta = "") 433 : session_{throw_exception}, token_{token}, organization_{organization}, throw_exception_{throw_exception} { 434 if (token.empty()) { 435 if(const char* env_p = std::getenv("OPENAI_API_KEY")) { 436 token_ = std::string{env_p}; 437 } 438 } 439 if (api_base_url.empty()) { 440 if(const char* env_p = std::getenv("OPENAI_API_BASE")) { 441 base_url = std::string{env_p} + "/"; 442 } 443 else { 444 base_url = "https://api.openai.com/v1/"; 445 } 446 } 447 else { 448 base_url = api_base_url; 449 } 450 session_.setUrl(base_url); 451 session_.setToken(token_, organization_); 452 session_.setBeta(beta); 453 } 454 455 OpenAI(const OpenAI&) = delete; 456 OpenAI& operator=(const OpenAI&) = delete; 457 OpenAI(OpenAI&&) = delete; 458 OpenAI& operator=(OpenAI&&) = delete; 459 460 void setToken(const std::string& token = "", const std::string& organization = "") { session_.setToken(token, organization); } 461 462 void setProxy(const std::string& url) { session_.setProxyUrl(url); } 463 464 void setBeta(const std::string& beta) { session_.setBeta(beta); } 465 466 // void change_token(const std::string& token) { token_ = token; }; 467 void setThrowException(bool throw_exception) { throw_exception_ = throw_exception; } 468 469 void setMultiformPart(const std::pair<std::string, std::string>& filefield_and_filepath, const std::map<std::string, std::string>& fields) { session_.setMultiformPart(filefield_and_filepath, fields); } 470 471 Json post(const std::string& suffix, const std::string& data, const std::string& contentType) { 472 setParameters(suffix, data, contentType); 473 auto response = session_.postPrepare(contentType); 474 if (response.is_error){ 475 trigger_error(response.error_message); 476 } 477 478 Json json{}; 479 if (isJson(response.text)){ 480 481 json = Json::parse(response.text); 482 checkResponse(json); 483 } 484 else{ 485 #if OPENAI_VERBOSE_OUTPUT 486 std::cerr << "Response is not a valid JSON"; 487 std::cout << "<< " << response.text << "\n"; 488 #endif 489 } 490 491 return json; 492 } 493 494 Json get(const std::string& suffix, const std::string& data = "") { 495 setParameters(suffix, data); 496 auto response = session_.getPrepare(); 497 if (response.is_error) { trigger_error(response.error_message); } 498 499 Json json{}; 500 if (isJson(response.text)) { 501 json = Json::parse(response.text); 502 checkResponse(json); 503 } 504 else { 505 #if OPENAI_VERBOSE_OUTPUT 506 std::cerr << "Response is not a valid JSON\n"; 507 std::cout << "<< " << response.text<< "\n"; 508 #endif 509 json = Json{{"Result", response.text}}; 510 } 511 return json; 512 } 513 514 Json post(const std::string& suffix, const Json& json, const std::string& contentType="application/json") { 515 return post(suffix, json.dump(), contentType); 516 } 517 518 Json del(const std::string& suffix) { 519 setParameters(suffix, ""); 520 auto response = session_.deletePrepare(); 521 if (response.is_error) { trigger_error(response.error_message); } 522 523 Json json{}; 524 if (isJson(response.text)) { 525 json = Json::parse(response.text); 526 checkResponse(json); 527 } 528 else { 529 #if OPENAI_VERBOSE_OUTPUT 530 std::cerr << "Response is not a valid JSON\n"; 531 std::cout << "<< " << response.text<< "\n"; 532 #endif 533 } 534 return json; 535 } 536 537 std::string easyEscape(const std::string& text) { return session_.easyEscape(text); } 538 539 void debug() const { std::cout << token_ << '\n'; } 540 541 void setBaseUrl(const std::string &url) { 542 base_url = url; 543 } 544 545 std::string getBaseUrl() const { 546 return base_url; 547 } 548 549 private: 550 std::string base_url; 551 552 void setParameters(const std::string& suffix, const std::string& data, const std::string& contentType = "") { 553 auto complete_url = base_url+ suffix; 554 session_.setUrl(complete_url); 555 556 if (contentType != "multipart/form-data") { 557 session_.setBody(data); 558 } 559 560 #if OPENAI_VERBOSE_OUTPUT 561 std::cout << "<< request: "<< complete_url << " " << data << '\n'; 562 #endif 563 } 564 565 void checkResponse(const Json& json) { 566 if (json.count("error")) { 567 auto reason = json["error"].dump(); 568 trigger_error(reason); 569 570 #if OPENAI_VERBOSE_OUTPUT 571 std::cerr << ">> response error :\n" << json.dump(2) << "\n"; 572 #endif 573 } 574 } 575 576 // as of now the only way 577 bool isJson(const std::string &data){ 578 bool rc = true; 579 try { 580 auto json = Json::parse(data); // throws if no json 581 } 582 catch (std::exception &){ 583 rc = false; 584 } 585 return(rc); 586 } 587 588 void trigger_error(const std::string& msg) { 589 if (throw_exception_) { 590 throw std::runtime_error(msg); 591 } 592 else { 593 std::cerr << "[OpenAI] error. Reason: " << msg << '\n'; 594 } 595 } 596 597 public: 598 CategoryModel model {*this}; 599 CategoryAssistants assistant {*this}; 600 CategoryThreads thread {*this}; 601 CategoryCompletion completion{*this}; 602 CategoryEdit edit {*this}; 603 CategoryImage image {*this}; 604 CategoryEmbedding embedding {*this}; 605 CategoryFile file {*this}; 606 CategoryFineTune fine_tune {*this}; 607 CategoryModeration moderation{*this}; 608 CategoryChat chat {*this}; 609 CategoryAudio audio {*this}; 610 // CategoryEngine engine{*this}; // Not handled since deprecated (use Model instead) 611 612 private: 613 Session session_; 614 std::string token_; 615 std::string organization_; 616 bool throw_exception_; 617 }; 618 619 inline std::string bool_to_string(const bool b) { 620 std::ostringstream ss; 621 ss << std::boolalpha << b; 622 return ss.str(); 623 } 624 625 inline OpenAI& start(const std::string& token = "", const std::string& organization = "", bool throw_exception = true, const std::string& api_base_url = "") { 626 static OpenAI instance{token, organization, throw_exception, api_base_url}; 627 return instance; 628 } 629 630 inline OpenAI& instance() { 631 return start(); 632 } 633 634 inline Json post(const std::string& suffix, const Json& json) { 635 return instance().post(suffix, json); 636 } 637 638 inline Json get(const std::string& suffix/*, const Json& json*/) { 639 return instance().get(suffix); 640 } 641 642 // Helper functions to get category structures instance() 643 644 inline CategoryModel& model() { 645 return instance().model; 646 } 647 648 inline CategoryAssistants& assistant() { 649 return instance().assistant; 650 } 651 652 inline CategoryThreads& thread() { 653 return instance().thread; 654 } 655 656 inline CategoryCompletion& completion() { 657 return instance().completion; 658 } 659 660 inline CategoryChat& chat() { 661 return instance().chat; 662 } 663 664 inline CategoryAudio& audio() { 665 return instance().audio; 666 } 667 668 inline CategoryEdit& edit() { 669 return instance().edit; 670 } 671 672 inline CategoryImage& image() { 673 return instance().image; 674 } 675 676 inline CategoryEmbedding& embedding() { 677 return instance().embedding; 678 } 679 680 inline CategoryFile& file() { 681 return instance().file; 682 } 683 684 inline CategoryFineTune& fineTune() { 685 return instance().fine_tune; 686 } 687 688 inline CategoryModeration& moderation() { 689 return instance().moderation; 690 } 691 692 // Definitions of category methods 693 694 // GET https://api.openai.com/v1/models 695 // Lists the currently available models, and provides basic information about each one such as the owner and availability. 696 inline Json CategoryModel::list() { 697 return openai_.get("models"); 698 } 699 700 // GET https://api.openai.com/v1/models/{model} 701 // Retrieves a model instance, providing basic information about the model such as the owner and permissioning. 702 inline Json CategoryModel::retrieve(const std::string& model) { 703 return openai_.get("models/" + model); 704 } 705 706 // POST https://api.openai.com/v1/assistants 707 // Create an assistant with a model and instructions. 708 inline Json CategoryAssistants::create(Json input) { 709 return openai_.post("assistants", input); 710 } 711 712 // GET https://api.openai.com/v1/assistants/{assistant_id} 713 // Retrieves an assistant. 714 inline Json CategoryAssistants::retrieve(const std::string& assistants) { 715 return openai_.get("assistants/" + assistants); 716 } 717 718 // POST https://api.openai.com/v1/assistants/{assistant_id} 719 // Modifies an assistant. 720 inline Json CategoryAssistants::modify(const std::string& assistants, Json input) { 721 return openai_.post("assistants/" + assistants, input); 722 } 723 724 // DELETE https://api.openai.com/v1/assistants/{assistant_id} 725 // Delete an assistant. 726 inline Json CategoryAssistants::del(const std::string& assistants) { 727 return openai_.del("assistants/" + assistants); 728 } 729 730 // GET https://api.openai.com/v1/assistants 731 // Returns a list of assistants. 732 inline Json CategoryAssistants::list() { 733 return openai_.get("assistants"); 734 } 735 736 // POST https://api.openai.com/v1/assistants/{assistant_id}/files 737 // Create an assistant file by attaching a File to an assistant. 738 inline Json CategoryAssistants::createFile(const std::string& assistants, Json input) { 739 return openai_.post("assistants/" + assistants + "/files", input); 740 } 741 742 // GET https://api.openai.com/v1/assistants/{assistant_id}/files/{file_id} 743 // Retrieves an AssistantFile. 744 inline Json CategoryAssistants::retrieveFile(const std::string& assistants, const std::string& files) { 745 return openai_.get("assistants/" + assistants + "/files/" + files); 746 } 747 748 // DELETE https://api.openai.com/v1/assistants/{assistant_id}/files/{file_id} 749 // Delete an assistant file. 750 inline Json CategoryAssistants::delFile(const std::string& assistants, const std::string& files) { 751 return openai_.del("assistants/" + assistants + "/files/" + files); 752 } 753 754 // GET https://api.openai.com/v1/assistants/{assistant_id}/files 755 // Returns a list of assistant files. 756 inline Json CategoryAssistants::listFile(const std::string& assistants) { 757 return openai_.get("assistants/" + assistants + "/files"); 758 } 759 760 // POST https://api.openai.com/v1/threads 761 // Create a thread. 762 inline Json CategoryThreads::create() { 763 Json input; 764 return openai_.post("threads", input); 765 } 766 767 // GET https://api.openai.com/v1/threads/{thread_id} 768 // Retrieves a thread. 769 inline Json CategoryThreads::retrieve(const std::string& threads) { 770 return openai_.get("threads/" + threads); 771 } 772 773 // POST https://api.openai.com/v1/threads/{thread_id} 774 // Modifies a thread. 775 inline Json CategoryThreads::modify(const std::string& threads, Json input) { 776 return openai_.post("threads/" + threads, input); 777 } 778 779 // DELETE https://api.openai.com/v1/threads/{thread_id} 780 // Delete a thread. 781 inline Json CategoryThreads::del(const std::string& threads) { 782 return openai_.del("threads/" + threads); 783 } 784 785 // POST https://api.openai.com/v1/threads/{thread_id}/messages 786 // Create a message. 787 inline Json CategoryThreads::createMessage(const std::string& threads, Json input) { 788 return openai_.post("threads/" + threads + "/messages", input); 789 } 790 791 // GET https://api.openai.com/v1/threads/{thread_id}/messages/{message_id} 792 // Retrieve a message. 793 inline Json CategoryThreads::retrieveMessage(const std::string& threads, const std::string& messages) { 794 return openai_.get("threads/" + threads + "/messages/" + messages); 795 } 796 797 // POST https://api.openai.com/v1/threads/{thread_id}/messages/{message_id} 798 // Modifies a message. 799 inline Json CategoryThreads::modifyMessage(const std::string& threads, const std::string& messages, Json input) { 800 return openai_.post("threads/" + threads + "/messages/" + messages, input); 801 } 802 803 // GET https://api.openai.com/v1/threads/{thread_id}/messages 804 // Returns a list of messages for a given thread. 805 inline Json CategoryThreads::listMessage(const std::string& threads) { 806 return openai_.get("threads/" + threads + "/messages"); 807 } 808 809 // GET https://api.openai.com/v1/threads/{thread_id}/messages/{message_id}/files/{file_id} 810 // Retrieves a message file. 811 inline Json CategoryThreads::retrieveMessageFile(const std::string& threads, const std::string& messages, const std::string& files) { 812 return openai_.get("threads/" + threads + "/messages/" + messages + "/files/" + files); 813 } 814 815 // GET https://api.openai.com/v1/threads/{thread_id}/messages/{message_id}/files 816 // Returns a list of message files. 817 inline Json CategoryThreads::listMessageFile(const std::string& threads, const std::string& messages) { 818 return openai_.get("threads/" + threads + "/messages/" + messages + "/files"); 819 } 820 821 // POST https://api.openai.com/v1/threads/{thread_id}/runs 822 // Create a run. 823 inline Json CategoryThreads::createRun(const std::string& threads, Json input) { 824 return openai_.post("threads/" + threads + "/runs", input); 825 } 826 827 // GET https://api.openai.com/v1/threads/{thread_id}/runs/{run_id} 828 // Retrieves a run. 829 inline Json CategoryThreads::retrieveRun(const std::string& threads, const std::string& runs) { 830 return openai_.get("threads/" + threads + "/runs/" + runs); 831 } 832 833 // POST https://api.openai.com/v1/threads/{thread_id}/runs/{run_id} 834 // Modifies a run. 835 inline Json CategoryThreads::modifyRun(const std::string& threads, const std::string& runs, Json input) { 836 return openai_.post("threads/" + threads + "/runs/" + runs, input); 837 } 838 839 // GET https://api.openai.com/v1/threads/{thread_id}/runs 840 // Returns a list of runs belonging to a thread. 841 inline Json CategoryThreads::listRun(const std::string& threads) { 842 return openai_.get("threads/" + threads + "/runs"); 843 } 844 845 // POST https://api.openai.com/v1/threads/{thread_id}/runs/{run_id}/submit_tool_outputs 846 // When a run has the status: "requires_action" and required_action.type is submit_tool_outputs, this endpoint can be used to submit the outputs from the tool calls once they're all completed. All outputs must be submitted in a single request. 847 inline Json CategoryThreads::submitToolOutputsToRun(const std::string& threads, const std::string& runs, Json input) { 848 return openai_.post("threads/" + threads + "/runs/" + runs + "submit_tool_outputs", input); 849 } 850 851 // POST https://api.openai.com/v1/threads/{thread_id}/runs/{run_id}/cancel 852 // Cancels a run that is in_progress. 853 inline Json CategoryThreads::cancelRun(const std::string& threads, const std::string& runs) { 854 Json input; 855 return openai_.post("threads/" + threads + "/runs/" + runs + "/cancel", input); 856 } 857 858 // POST https://api.openai.com/v1/threads/runs 859 // Create a thread and run it in one request. 860 inline Json CategoryThreads::createThreadAndRun(Json input) { 861 return openai_.post("threads/runs", input); 862 } 863 864 // GET https://api.openai.com/v1/threads/{thread_id}/runs/{run_id}/steps/{step_id} 865 // Retrieves a run step. 866 inline Json CategoryThreads::retrieveRunStep(const std::string& threads, const std::string& runs, const std::string& steps) { 867 return openai_.get("threads/" + threads + "/runs/" + runs + "/steps/" + steps); 868 } 869 870 // GET https://api.openai.com/v1/threads/{thread_id}/runs/{run_id}/steps 871 // Returns a list of run steps belonging to a run. 872 inline Json CategoryThreads::listRunStep(const std::string& threads, const std::string& runs) { 873 return openai_.get("threads/" + threads + "/runs/" + runs + "/steps"); 874 } 875 876 // POST https://api.openai.com/v1/completions 877 // Creates a completion for the provided prompt and parameters 878 inline Json CategoryCompletion::create(Json input) { 879 return openai_.post("completions", input); 880 } 881 882 // POST https://api.openai.com/v1/chat/completions 883 // Creates a chat completion for the provided prompt and parameters 884 inline Json CategoryChat::create(Json input) { 885 return openai_.post("chat/completions", input); 886 } 887 888 // POST https://api.openai.com/v1/audio/transcriptions 889 // Transcribes audio into the input language. 890 inline Json CategoryAudio::transcribe(Json input) { 891 auto lambda = [input]() -> std::map<std::string, std::string> { 892 std::map<std::string, std::string> temp; 893 temp.insert({"model", input["model"].get<std::string>()}); 894 if (input.contains("language")) { 895 temp.insert({"language", input["language"].get<std::string>()}); 896 } 897 if (input.contains("prompt")) { 898 temp.insert({"prompt", input["prompt"].get<std::string>()}); 899 } 900 if (input.contains("response_format")) { 901 temp.insert({"response_format", input["response_format"].get<std::string>()}); 902 } 903 if (input.contains("temperature")) { 904 temp.insert({"temperature", std::to_string(input["temperature"].get<float>())}); 905 } 906 return temp; 907 }; 908 openai_.setMultiformPart({"file", input["file"].get<std::string>()}, 909 lambda() 910 ); 911 912 return openai_.post("audio/transcriptions", std::string{""}, "multipart/form-data"); 913 } 914 915 // POST https://api.openai.com/v1/audio/translations 916 // Translates audio into into English.. 917 inline Json CategoryAudio::translate(Json input) { 918 auto lambda = [input]() -> std::map<std::string, std::string> { 919 std::map<std::string, std::string> temp; 920 temp.insert({"model", input["model"].get<std::string>()}); 921 if (input.contains("language")) { 922 temp.insert({"language", input["language"].get<std::string>()}); 923 } 924 if (input.contains("prompt")) { 925 temp.insert({"prompt", input["prompt"].get<std::string>()}); 926 } 927 if (input.contains("response_format")) { 928 temp.insert({"response_format", input["response_format"].get<std::string>()}); 929 } 930 if (input.contains("temperature")) { 931 temp.insert({"temperature", std::to_string(input["temperature"].get<float>())}); 932 } 933 return temp; 934 }; 935 openai_.setMultiformPart({"file", input["file"].get<std::string>()}, 936 lambda() 937 ); 938 939 return openai_.post("audio/translations", std::string{""}, "multipart/form-data"); 940 } 941 942 // POST https://api.openai.com/v1/translations 943 // Creates a new edit for the provided input, instruction, and parameters 944 inline Json CategoryEdit::create(Json input) { 945 return openai_.post("edits", input); 946 } 947 948 // POST https://api.openai.com/v1/images/generations 949 // Given a prompt and/or an input image, the model will generate a new image. 950 inline Json CategoryImage::create(Json input) { 951 return openai_.post("images/generations", input); 952 } 953 954 // POST https://api.openai.com/v1/images/edits 955 // Creates an edited or extended image given an original image and a prompt. 956 inline Json CategoryImage::edit(Json input) { 957 std::string prompt = input["prompt"].get<std::string>(); // required 958 // Default values 959 std::string mask = ""; 960 int n = 1; 961 std::string size = "1024x1024"; 962 std::string response_format = "url"; 963 std::string user = ""; 964 965 if (input.contains("mask")) { 966 mask = input["mask"].get<std::string>(); 967 } 968 if (input.contains("n")) { 969 n = input["n"].get<int>(); 970 } 971 if (input.contains("size")) { 972 size = input["size"].get<std::string>(); 973 } 974 if (input.contains("response_format")) { 975 response_format = input["response_format"].get<std::string>(); 976 } 977 if (input.contains("user")) { 978 user = input["user"].get<std::string>(); 979 } 980 openai_.setMultiformPart({"image",input["image"].get<std::string>()}, 981 std::map<std::string, std::string>{ 982 {"prompt", prompt}, 983 {"mask", mask}, 984 {"n", std::to_string(n)}, 985 {"size", size}, 986 {"response_format", response_format}, 987 {"user", user} 988 } 989 ); 990 991 return openai_.post("images/edits", std::string{""}, "multipart/form-data"); 992 } 993 994 // POST https://api.openai.com/v1/images/variations 995 // Creates a variation of a given image. 996 inline Json CategoryImage::variation(Json input) { 997 // Default values 998 int n = 1; 999 std::string size = "1024x1024"; 1000 std::string response_format = "url"; 1001 std::string user = ""; 1002 1003 if (input.contains("n")) { 1004 n = input["n"].get<int>(); 1005 } 1006 if (input.contains("size")) { 1007 size = input["size"].get<std::string>(); 1008 } 1009 if (input.contains("response_format")) { 1010 response_format = input["response_format"].get<std::string>(); 1011 } 1012 if (input.contains("user")) { 1013 user = input["user"].get<std::string>(); 1014 } 1015 openai_.setMultiformPart({"image",input["image"].get<std::string>()}, 1016 std::map<std::string, std::string>{ 1017 {"n", std::to_string(n)}, 1018 {"size", size}, 1019 {"response_format", response_format}, 1020 {"user", user} 1021 } 1022 ); 1023 1024 return openai_.post("images/variations", std::string{""}, "multipart/form-data"); 1025 } 1026 1027 inline Json CategoryEmbedding::create(Json input) { 1028 return openai_.post("embeddings", input); 1029 } 1030 1031 inline Json CategoryFile::list() { 1032 return openai_.get("files"); 1033 } 1034 1035 inline Json CategoryFile::upload(Json input) { 1036 openai_.setMultiformPart({"file", input["file"].get<std::string>()}, 1037 std::map<std::string, std::string>{{"purpose", input["purpose"].get<std::string>()}} 1038 ); 1039 1040 return openai_.post("files", std::string{""}, "multipart/form-data"); 1041 } 1042 1043 inline Json CategoryFile::del(const std::string& file_id) { 1044 return openai_.del("files/" + file_id); 1045 } 1046 1047 inline Json CategoryFile::retrieve(const std::string& file_id) { 1048 return openai_.get("files/" + file_id); 1049 } 1050 1051 inline Json CategoryFile::content(const std::string& file_id) { 1052 return openai_.get("files/" + file_id + "/content"); 1053 } 1054 1055 inline Json CategoryFineTune::create(Json input) { 1056 return openai_.post("fine-tunes", input); 1057 } 1058 1059 inline Json CategoryFineTune::list() { 1060 return openai_.get("fine-tunes"); 1061 } 1062 1063 inline Json CategoryFineTune::retrieve(const std::string& fine_tune_id) { 1064 return openai_.get("fine-tunes/" + fine_tune_id); 1065 } 1066 1067 inline Json CategoryFineTune::content(const std::string& fine_tune_id) { 1068 return openai_.get("fine-tunes/" + fine_tune_id + "/content"); 1069 } 1070 1071 inline Json CategoryFineTune::cancel(const std::string& fine_tune_id) { 1072 return openai_.post("fine-tunes/" + fine_tune_id + "/cancel", Json{}); 1073 } 1074 1075 inline Json CategoryFineTune::events(const std::string& fine_tune_id) { 1076 return openai_.get("fine-tunes/" + fine_tune_id + "/events"); 1077 } 1078 1079 inline Json CategoryFineTune::del(const std::string& model) { 1080 return openai_.del("models/" + model); 1081 } 1082 1083 inline Json CategoryModeration::create(Json input) { 1084 return openai_.post("moderations", input); 1085 } 1086 1087 } // namespace _detail 1088 1089 // Public interface 1090 1091 using _detail::OpenAI; 1092 1093 // instance 1094 using _detail::start; 1095 using _detail::instance; 1096 1097 // Generic methods 1098 using _detail::post; 1099 using _detail::get; 1100 1101 // Helper categories access 1102 using _detail::model; 1103 using _detail::assistant; 1104 using _detail::thread; 1105 using _detail::completion; 1106 using _detail::edit; 1107 using _detail::image; 1108 using _detail::embedding; 1109 using _detail::file; 1110 using _detail::fineTune; 1111 using _detail::moderation; 1112 using _detail::chat; 1113 using _detail::audio; 1114 1115 using _detail::Json; 1116 1117 } // namespace openai 1118 1119 #endif // OPENAI_HPP_