diff options
| author | Aldrik Ramaekers <aldrikboy@gmail.com> | 2025-09-28 20:28:40 +0200 |
|---|---|---|
| committer | Aldrik Ramaekers <aldrikboy@gmail.com> | 2025-09-28 20:28:40 +0200 |
| commit | 485e5ebf340857db07b1c8ecb5c63dcf3a908377 (patch) | |
| tree | ceca7fd0e20e20b4452c2ecc67563546a1e23730 | |
| parent | a2299b0bae21c8f05f091732a78fc250cbd5e016 (diff) | |
move string function out of openAI.cpp into strops.cpp
| -rw-r--r-- | docs/CHANGES.rst | 3 | ||||
| -rw-r--r-- | include/strops.hpp | 6 | ||||
| -rw-r--r-- | src/ai_providers/openAI.cpp | 98 | ||||
| -rw-r--r-- | src/ai_service.cpp | 2 | ||||
| -rw-r--r-- | src/strops.cpp | 84 |
5 files changed, 103 insertions, 90 deletions
diff --git a/docs/CHANGES.rst b/docs/CHANGES.rst index 1a46011..b93080c 100644 --- a/docs/CHANGES.rst +++ b/docs/CHANGES.rst @@ -13,9 +13,8 @@ for invoice importing using AI: - let user choose the model to use in settings/services/ai - real error logging for OpenAI and importing in general - when importing an invoice: do not accept invoices for unsupported countries (yet) - +- write tests for strops.hpp - log_set_depth function so data can be grouped -- move string function out of openAI.cpp into strops.cpp - log elapsed time for ai requests - refactor _add functions to use _import functions - _import functions should not check for validity and should never fail because of invalid data diff --git a/include/strops.hpp b/include/strops.hpp index 68a927e..d11f0c2 100644 --- a/include/strops.hpp +++ b/include/strops.hpp @@ -24,4 +24,8 @@ void strops_replace(char *buf, size_t buf_size, const char *search, const cha void strops_replace_int32(char *buf, size_t buf_size, const char *search, int32_t number); void strops_replace_int64(char *buf, size_t buf_size, const char *search, int64_t number); void strops_replace_float(char *buf, size_t buf_size, const char *search, float number, int decimals); -bool strops_prefix(const char *pre, const char *str);
\ No newline at end of file +bool strops_prefix(const char *pre, const char *str); +char* strops_get_json_value(const char *json, const char *key, char *out, size_t out_size, int nth = 0); +char* strops_get_filename(const char* path); +char* strops_prep_str_for_json(const char *input, size_t buffer_size); +char* strops_unprep_str_from_json(char *input);
\ No newline at end of file diff --git a/src/ai_providers/openAI.cpp b/src/ai_providers/openAI.cpp index 5dd2c50..e005a80 100644 --- a/src/ai_providers/openAI.cpp +++ b/src/ai_providers/openAI.cpp @@ -14,97 +14,21 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define _CRT_SECURE_NO_WARNINGS + #include <fstream> #include <iostream> #include <string> #define CPPHTTPLIB_OPENSSL_SUPPORT #include "httplib.h" +#include "strops.hpp" #include "log.hpp" #include "ai_service.hpp" -static char *extract_json_value(const char *json, const char *key, char *out, size_t out_size, int skip = 0) { - char pattern[128]; - snprintf(pattern, sizeof(pattern), "\"%s\"", key); - const char *pos = strstr(json, pattern); - while(skip > 0) { - pos = strstr(pos+1, pattern); - skip--; - } - 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-1) != '\\') && i < out_size - 1) { - out[i++] = *pos++; - } - out[i] = '\0'; - return out; -} - -static 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 -} - -static char *escape_quotes(const char *input, size_t buffer_size) { - if (!input) return NULL; - - char *result = (char*)malloc(buffer_size + 100); // Ballpark - if (!result) return NULL; - - const char *src = input; - char *dst = result; - - while (*src) { - if (*src == '"') { - *dst++ = '\\'; - *dst++ = '"'; - } - else if (*src == '\n') { - // empty - } - else { - *dst++ = *src; - } - src++; - } - *dst = '\0'; - - return result; -} - -static char *unescape_quotes(char *input) { - if (!input) return NULL; - - char *src = input; - char *dst = input; - - while (*src) { - if (*src == '\\' && *(src+1) == '"') { - src++; - } - else if (*src == '\\' && *(src+1) == 'n') { - src++;src++; - } - *dst++ = *src++; - } - *dst = '\0'; - - return input; -} - static bool _openAI_query_with_file(char* query, size_t query_length, char* file_id, char** response) { - #define TESTING_IMPORT + //#define TESTING_IMPORT #ifndef TESTING_IMPORT const char *api_key = administration_get_ai_service().api_key_public; @@ -112,7 +36,7 @@ static bool _openAI_query_with_file(char* query, size_t query_length, char* file httplib::SSLClient cli("api.openai.com", 443); //cli.enable_server_certificate_verification(false); - char* query_escaped = escape_quotes(query, query_length); + char* query_escaped = strops_prep_str_for_json(query, query_length); free(query); size_t body_size = query_length + 200; @@ -141,8 +65,8 @@ static bool _openAI_query_with_file(char* query, size_t query_length, char* file memset(*response, 0, 100000); strncpy(*response, response_body, 100000); - extract_json_value(*response, "text", *response, 100000); - *response = unescape_quotes(*response); + strops_get_json_value(*response, "text", *response, 100000); + *response = strops_unprep_str_from_json(*response); return 1; } @@ -150,7 +74,7 @@ static bool _openAI_query_with_file(char* query, size_t query_length, char* file 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 = get_filename(file_path); + const char *filename = strops_get_filename(file_path); FILE* orig_file = fopen(file_path, "rb"); if (orig_file == NULL) { @@ -179,7 +103,7 @@ static bool _openAI_upload_file(char* file_path, char* file_id, size_t file_id_l } char upload_id[128]; - extract_json_value(res->body.c_str(), "id", upload_id, sizeof(upload_id)); + 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); @@ -215,7 +139,7 @@ static bool _openAI_upload_file(char* file_path, char* file_id, size_t file_id_l } else { char part_id[128]; - extract_json_value(part_res->body.c_str(), "id", part_id, sizeof(part_id)); + 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); } @@ -241,7 +165,7 @@ static bool _openAI_upload_file(char* file_path, char* file_id, size_t file_id_l } char* completion_body_response = (char*)complete_res->body.c_str(); - extract_json_value(completion_body_response, "id", file_id, file_id_len, 1); + strops_get_json_value(completion_body_response, "id", file_id, file_id_len, 1); return 1; } diff --git a/src/ai_service.cpp b/src/ai_service.cpp index e271fcb..82d81d6 100644 --- a/src/ai_service.cpp +++ b/src/ai_service.cpp @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define _CRT_SECURE_NO_WARNINGS + #include <fstream> #include <iostream> #include <string> diff --git a/src/strops.cpp b/src/strops.cpp index 2c5f0b9..28f9bcc 100644 --- a/src/strops.cpp +++ b/src/strops.cpp @@ -14,6 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <stdlib.h> #include <string.h> #include <ctype.h> #include <stdio.h> @@ -107,4 +108,87 @@ void strops_replace_float(char *buf, size_t buf_size, const char *search, float char num_buf[200]; snprintf(num_buf, 200, "%.*f", decimals, number); strops_replace(buf, buf_size, search, num_buf); +} + +char* strops_get_json_value(const char *json, const char *key, char *out, size_t out_size, int skip) +{ + char pattern[128]; + snprintf(pattern, sizeof(pattern), "\"%s\"", key); + const char *pos = strstr(json, pattern); + while(skip > 0) { + pos = strstr(pos+1, pattern); + skip--; + } + 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-1) != '\\') && i < out_size - 1) { + out[i++] = *pos++; + } + out[i] = '\0'; + return out; +} + +char* strops_get_filename(const char* path) +{ + char* filename = (char*)strrchr(path, '/'); // for Unix-style paths + if (filename) return filename + 1; // skip the '/' + filename = (char*)strrchr(path, '\\'); // for Windows-style paths + if (filename) return filename + 1; + return (char*)path; // no slashes found, path itself is filename +} + +char* strops_prep_str_for_json(const char *input, size_t buffer_size) +{ + if (!input) return NULL; + + char *result = (char*)malloc(buffer_size * 2 + 1); + if (!result) return NULL; + + const char *src = input; + char *dst = result; + + while (*src) { + if (*src == '"') { + *dst++ = '\\'; + *dst++ = '"'; + } + else if (*src == '\n') { + // empty + } + else { + *dst++ = *src; + } + src++; + } + *dst = '\0'; + + return result; +} + +char* strops_unprep_str_from_json(char *input) +{ + if (!input) return NULL; + + char *src = input; + char *dst = input; + + while (*src) { + if (*src == '\\' && *(src+1) == '"') { + src++; + } + else if (*src == '\\' && *(src+1) == 'n') { + src++;src++; + } + *dst++ = *src++; + } + *dst = '\0'; + + return input; }
\ No newline at end of file |
