information-retrieval

Exploration of information retrieval topics
git clone git://git.laack.co/information-retrieval.git
Log | Files | Refs

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_