/* * Copyright (c) 2025 Aldrik Ramaekers * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define _CRT_SECURE_NO_WARNINGS #include #include #include #define CPPHTTPLIB_OPENSSL_SUPPORT #include "httplib.h" #include "strops.hpp" #include "log.hpp" #include "import_service.hpp" static bool _openAI_query_with_file(char* query, size_t query_length, char* file_id, char** response) { #if 0 const char *api_key = administration_get_ai_service().api_key_public; httplib::SSLClient cli("api.openai.com", 443); //cli.enable_server_certificate_verification(false); char* query_escaped = strops_prep_str_for_json(query, query_length); free(query); size_t body_size = query_length + 200; char* body = (char*)malloc(body_size); snprintf(body, body_size, "{\"model\":\"gpt-5-nano\", \"input\": [ { \"role\": \"user\", \"content\": [ { \"type\": \"input_file\", \"file_id\": \"%s\" }, " "{ \"type\": \"input_text\", \"text\": \"%s\" } ] } ] }", file_id, query_escaped); httplib::Headers headers; headers.insert(std::make_pair("Authorization", std::string("Bearer ") + api_key)); httplib::Result res = cli.Post("/v1/responses", headers, body, "application/json"); free(body); if (!res || res->status != 200) { log_error("ERROR Failed to query API."); return 0; } char* response_body = (char*)res->body.c_str(); *response = (char*)malloc(100000); memset(*response, 0, 100000); strncpy(*response, response_body, 100000); strops_get_json_value(*response, "text", *response, 100000); *response = strops_unprep_str_from_json(*response); #else *response = (char*)malloc(100000); memset(*response, 0, 100000); strops_copy(*response, " urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0 urn:fdc:peppol.eu:2017:poacc:billing:01:1.0 492043632 2024-09-01 2024-09-01 380 USD Final invoice do:team:67840ecb-44e2-472e-bc45-801bd4e1f1fe DigitalOcean LLC 101 Avenue of the Americas 2nd Floor New York 10013 NY US EU528002224 VAT DigitalOcean LLC My Team Keerderstraat 81 Maastricht 6226 XW LI NL VAT aldrikboy@gmail.com 492043632 3.49 15.60 3.28 VAT 1.00 0.21 VAT 16.60 16.60 20.09 20.09 1 16.60 false Discount Product Usage Charges Internal Tax Rate ID VAT ", 100000); #endif return 1; } static bool _openAI_upload_file(char* file_path, char* file_id, size_t file_id_len) { const char *api_key = administration_get_ai_service().api_key_public; const char *filename = strops_get_filename(file_path); FILE* orig_file = fopen(file_path, "rb"); if (orig_file == NULL) { log_error("ERROR: file to upload could not be opened."); return 0; } fseek(orig_file, 0L, SEEK_END); long sz = ftell(orig_file); fseek(orig_file, 0, SEEK_SET); httplib::SSLClient cli("api.openai.com", 443); //cli.enable_server_certificate_verification(false); char body[512]; snprintf(body, sizeof(body), "{\"filename\":\"%s\",\"purpose\":\"user_data\", \"bytes\": %d, \"mime_type\": \"application/pdf\", \"expires_after\": { \"anchor\": \"created_at\", \"seconds\": 3600 } }", filename, sz); httplib::Headers headers; headers.insert(std::make_pair("Authorization", std::string("Bearer ") + api_key)); httplib::Result res = cli.Post("/v1/uploads", headers, body, "application/json"); if (!res || res->status != 200) { log_error("ERROR Failed to create upload."); log_error(res->body.c_str()); fclose(orig_file); return 0; } char upload_id[128]; strops_get_json_value(res->body.c_str(), "id", upload_id, sizeof(upload_id)); size_t part_size = 64000000; // 64mb log_info("Created upload %s with part size %zu.", upload_id, part_size); char *buffer = (char*)malloc(part_size); char completion_body[1048]; snprintf(completion_body, sizeof(completion_body), "{\"part_ids\": ["); int part_number = 0; while (1) { size_t read_bytes = fread(buffer, 1, part_size, orig_file); if (read_bytes == 0) break; httplib::Headers part_headers; part_headers.insert(std::make_pair("Authorization", std::string("Bearer ") + api_key)); std::string chunk(buffer, read_bytes); httplib::UploadFormDataItems items = { {"data", chunk, filename, "application/octet-stream"} }; char path[256]; snprintf(path, sizeof(path), "/v1/uploads/%s/parts?part_number=%d", upload_id, part_number); httplib::Result part_res = cli.Post(path, part_headers, items); if (!part_res || part_res->status != 200) { log_error("Failed to upload part %d.", part_number); log_error(part_res->body.c_str()); free(buffer); fclose(orig_file); return 0; } else { char part_id[128]; strops_get_json_value(part_res->body.c_str(), "id", part_id, sizeof(part_id)); if (part_number == 0) snprintf(completion_body+strlen(completion_body), sizeof(completion_body)-strlen(completion_body), "\"%s\"", part_id); if (part_number != 0) snprintf(completion_body+strlen(completion_body), sizeof(completion_body)-strlen(completion_body), ", \"%s\"", part_id); } log_info("Uploaded part %d\n", part_number); part_number++; } snprintf(completion_body+strlen(completion_body), sizeof(completion_body)-strlen(completion_body), "]}"); free(buffer); fclose(orig_file); // ---------- Step 3: Complete upload ---------- httplib::Result complete_res = cli.Post((std::string("/v1/uploads/") + upload_id + "/complete").c_str(), headers, completion_body, "application/json"); if (!complete_res || complete_res->status != 200) { log_error("ERROR Failed to complete upload."); log_error(complete_res->body.c_str()); return 0; } char* completion_body_response = (char*)complete_res->body.c_str(); strops_get_json_value(completion_body_response, "id", file_id, file_id_len, 1); return 1; } ai_provider_impl _chatgpt_api_provider = { _openAI_upload_file, _openAI_query_with_file, };