diff options
Diffstat (limited to 'src/ai_service.cpp')
| -rw-r--r-- | src/ai_service.cpp | 135 |
1 files changed, 43 insertions, 92 deletions
diff --git a/src/ai_service.cpp b/src/ai_service.cpp index 2553f61..e271fcb 100644 --- a/src/ai_service.cpp +++ b/src/ai_service.cpp @@ -22,121 +22,72 @@ #include "httplib.h" #include "log.hpp" #include "ai_service.hpp" +#include "strops.hpp" +#include "administration_reader.hpp" +ai_provider_impl _ai_get_impl() +{ + ai_provider provider = administration_get_ai_service().provider; -// ---- Utility: simple JSON value extractor (very naive) ---- -char *extract_json_value(const char *json, const char *key, char *out, size_t out_size) { - char pattern[128]; - snprintf(pattern, sizeof(pattern), "\"%s\"", key); - const char *pos = strstr(json, pattern); - if (!pos) return NULL; - pos = strchr(pos, ':'); - if (!pos) return NULL; - pos++; - - // Skip whitespace and quotes - while (*pos == ' ' || *pos == '\"') pos++; - - size_t i = 0; - while (*pos && *pos != '\"' && *pos != ',' && *pos != '}' && i < out_size - 1) { - out[i++] = *pos++; + switch(provider) + { + case AI_PROVIDER_OPENAI: return _chatgpt_api_provider; + default: assert(0); break; } - out[i] = '\0'; - return out; -} -// ---- Read file chunk ---- -size_t read_chunk(FILE *fp, char *buffer, size_t chunk_size) { - return fread(buffer, 1, chunk_size, fp); + return ai_provider_impl {0}; } -const char* get_filename(const char* path) { - const char* filename = strrchr(path, '/'); // for Unix-style paths - if (filename) return filename + 1; // skip the '/' - filename = strrchr(path, '\\'); // for Windows-style paths - if (filename) return filename + 1; - return path; // no slashes found, path itself is filename -} +extern const char* peppol_invoice_template; +extern const char* peppol_invoice_line_template; ai_request* ai_document_to_invoice(char* file_path) { - const char *api_key = administration_get_ai_service().api_key_public; - const char *filename = 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; - } + ai_provider_impl impl = _ai_get_impl(); - 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."); - fclose(orig_file); + char file_id[100]; + if (!impl.upload_file(file_path, file_id, 100)) { return 0; } - char upload_id[128]; - extract_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); + size_t query_buffer_len = 50000; + char* template_buffer = (char*)malloc(query_buffer_len); + memset(template_buffer, 0, query_buffer_len); - char *buffer = (char*)malloc(part_size); + strncpy(template_buffer, peppol_invoice_template, query_buffer_len); + strops_replace(template_buffer, 50000, "{{INVOICE_LINE_LIST}}", peppol_invoice_line_template); - int part_number = 0; - while (1) { - size_t read_bytes = read_chunk(orig_file, buffer, part_size); - if (read_bytes == 0) break; + char* ai_query = + "\n\nI have provided a file containing an invoice. Fill in the above Peppol 3.0 template with the information from the invoice." + "Do not add any fields to the template. If you can't find data for a given field, leave it empty. Do not make up any information." + "Only return the filled out template in valid XML format. Nothing else.\n"; - httplib::Headers part_headers; - part_headers.insert(std::make_pair("Authorization", std::string("Bearer ") + api_key)); - part_headers.insert(std::make_pair("Content-Type", "multipart/form-data")); + size_t query_len = strlen(template_buffer); + strncpy(template_buffer + query_len, ai_query, query_buffer_len - query_len); - std::string chunk(buffer, read_bytes); + char* response; + if (!impl.query_with_file(template_buffer, query_buffer_len, file_id, &response)) { + return 0; + } - httplib::UploadFormDataItems items = { - {"data", chunk, filename, "application/pdf"} - }; + invoice inv; + if (!administration_reader_read_invoice_from_xml(&inv, response, strlen(response))) { + return false; + } - char path[256]; - snprintf(path, sizeof(path), "/v1/uploads/%s/parts?part_number=%d", upload_id, part_number); + invoice tmp = administration_invoice_create_empty(); - httplib::Result part_res = cli.Post(path, part_headers, items); + inv.status = invoice_status::INVOICE_RECEIVED; + strops_copy(inv.id, tmp.id, MAX_LEN_ID); // TODO next_id is not being incremented + strops_copy(inv.customer.id, MY_COMPANY_ID, MAX_LEN_ID); // TODO param for incomming/exporting necessary - if (!part_res || part_res->status != 200) { - log_error("Failed to upload part %d.", part_number); - free(buffer); - fclose(orig_file); - return 0; - } + strops_copy(inv.document.original_path, file_path, MAX_LEN_PATH); + strops_copy(inv.document.copy_path, "", MAX_LEN_PATH); - log_info("Uploaded part %d\n", part_number); - part_number++; - } + a_err result = administration_invoice_import(&inv); - 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, "", "application/json"); - if (!complete_res || complete_res->status != 200) { - log_error("ERROR Failed to complete upload."); - return 0; - } + free(template_buffer); + free(response); return 0; }
\ No newline at end of file |
