summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAldrik Ramaekers <aldrikboy@gmail.com>2025-10-04 13:57:52 +0200
committerAldrik Ramaekers <aldrikboy@gmail.com>2025-10-04 13:57:52 +0200
commitb8e049f02a16dc8398b2a954b561c84beda423c7 (patch)
tree9f7a49e7e087f5e2cdcaddfacb7eef5b5f8650c0 /src
parent0546522f56ddd6dc4abdf09cdedd1e59598462c6 (diff)
deepseek ai backend
Diffstat (limited to 'src')
-rw-r--r--src/administration.cpp1
-rw-r--r--src/administration_reader.cpp1
-rw-r--r--src/administration_writer.cpp1
-rw-r--r--src/ai_providers/DeepSeek.cpp118
-rw-r--r--src/ai_providers/openAI.cpp1
-rw-r--r--src/import_service.cpp1
-rw-r--r--src/ui/ui_settings.cpp11
7 files changed, 126 insertions, 8 deletions
diff --git a/src/administration.cpp b/src/administration.cpp
index 3be34da..a07e79a 100644
--- a/src/administration.cpp
+++ b/src/administration.cpp
@@ -528,6 +528,7 @@ void administration_create_income_statement(income_statement* statement)
quarter.uncategorized_taxes = 0.0f;
quarter.report_count = 0;
quarter.is_empty = 1;
+ quarter.profit = 0.0f;
snprintf(quarter.quarter_str, MAX_LEN_SHORT_DESC, "%dQ%d", quarter.quarter+1, quarter.year);
project_count = administration_project_count();
diff --git a/src/administration_reader.cpp b/src/administration_reader.cpp
index a72ba8a..e845e7c 100644
--- a/src/administration_reader.cpp
+++ b/src/administration_reader.cpp
@@ -410,7 +410,6 @@ bool administration_reader_import_administration_info(char* buffer, size_t buffe
ai_service ai_service;
ai_service.provider = (ai_provider)xml_get_s32_x(root, "AIService", "Provider", 0);
xml_get_str_x(root, ai_service.api_key_public, MAX_LEN_API_KEY, "AIService", "PublicKey", 0);
- xml_get_str_x(root, ai_service.api_key_private, MAX_LEN_API_KEY, "AIService", "PrivateKey", 0);
administration_set_ai_service(ai_service);
log_info("Loaded administration info in %.3fms. next_id=%d next_sequence_number=%d",
diff --git a/src/administration_writer.cpp b/src/administration_writer.cpp
index 8bcf741..0ac0b40 100644
--- a/src/administration_writer.cpp
+++ b/src/administration_writer.cpp
@@ -788,7 +788,6 @@ bool administration_writer_save_all_administration_info_blocking()
ai_service ai_service = administration_get_ai_service();
strops_replace_int32(file_content, buf_length, "{{AI_SERVICE_PROVIDER}}", (s32)ai_service.provider);
strops_replace(file_content, buf_length, "{{AI_SERVICE_PUBLIC_KEY}}", ai_service.api_key_public);
- strops_replace(file_content, buf_length, "{{AI_SERVICE_PRIVATE_KEY}}", ai_service.api_key_private);
//// Write to Disk.
int final_length = (int)strlen(file_content);
diff --git a/src/ai_providers/DeepSeek.cpp b/src/ai_providers/DeepSeek.cpp
new file mode 100644
index 0000000..67d17c2
--- /dev/null
+++ b/src/ai_providers/DeepSeek.cpp
@@ -0,0 +1,118 @@
+/*
+* Copyright (c) 2025 Aldrik Ramaekers <aldrik.ramaekers@gmail.com>
+*
+* 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 <fstream>
+#include <iostream>
+#include <string>
+
+#define CPPHTTPLIB_OPENSSL_SUPPORT
+#include "httplib.h"
+#include "strops.hpp"
+#include "log.hpp"
+#include "import_service.hpp"
+
+#define QUERY_BUFFER_SIZE 1000000
+
+char* query_buffer = 0;
+static bool _DeepSeek_query_with_file(char* query, size_t query_length, char* file_id, char** response)
+{
+ assert(query_buffer);
+
+ const char *api_key = administration_get_ai_service().api_key_public;
+
+ httplib::SSLClient cli("api.deepseek.com");
+ //cli.enable_server_certificate_verification(false);
+
+ //char* query_escaped = strops_prep_str_for_json(query, query_length);
+ //free(query); // TODO why??
+
+ size_t file_size = strlen(query_buffer);
+ sprintf(query_buffer + file_size, "%s", query);
+
+ char* query_escaped = strops_prep_str_for_json(query_buffer, strlen(query_buffer));
+
+ size_t body_size = file_size + QUERY_BUFFER_SIZE;
+ char* body = (char*)malloc(body_size);
+ snprintf(body, body_size,
+ "{\"model\":\"deepseek-reasoner\", \"messages\": [ { \"role\": \"user\", \"content\": \"%s\" } ] }", query_escaped);
+
+ httplib::Headers headers;
+ headers.insert(std::make_pair("Authorization", std::string("Bearer ") + api_key));
+ headers.insert(std::make_pair("Content-Type", "application/json"));
+ headers.insert(std::make_pair("Accept", "application/json"));
+
+ httplib::Result res = cli.Post("/chat/completions", headers, body, "application/json");
+ free(body);
+
+ if (!res || res->status != 200) {
+ log_error("ERROR Failed to query API.");
+ log_error(res->body.c_str());
+ 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, "content", *response, 100000);
+ *response = strops_unprep_str_from_json(*response);
+
+ return 1;
+}
+
+static bool _DeepSeek_upload_file(char* file_path, char* file_id, size_t file_id_len)
+{
+ const char *filename = strops_get_filename(file_path);
+
+ FILE* orig_file = fopen(file_path, "r");
+ 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);
+
+ size_t buffer_size = sz + QUERY_BUFFER_SIZE;
+ char* file_content_buffer = (char*)malloc(buffer_size);
+ memset(file_content_buffer, 0, buffer_size);
+
+ query_buffer = file_content_buffer;
+
+ file_content_buffer += sprintf(file_content_buffer, "[file name]: %s\n", filename);
+ file_content_buffer += sprintf(file_content_buffer, "[file content begin]\n");
+
+ fread(file_content_buffer, sz, 1, orig_file);
+ file_content_buffer += sz;
+
+ for (int i = 0; i < file_content_buffer-query_buffer; i++) if (query_buffer[i] <= 0x1f) query_buffer[i] = ' ';
+
+ file_content_buffer += sprintf(file_content_buffer, "\n[file content end]\n");
+ file_content_buffer[0] = 0;
+
+ fclose(orig_file);
+
+ return 1;
+}
+
+ai_provider_impl _deepseek_api_provider = {
+ _DeepSeek_upload_file,
+ _DeepSeek_query_with_file,
+}; \ No newline at end of file
diff --git a/src/ai_providers/openAI.cpp b/src/ai_providers/openAI.cpp
index 7ebd680..20c1fa4 100644
--- a/src/ai_providers/openAI.cpp
+++ b/src/ai_providers/openAI.cpp
@@ -51,6 +51,7 @@ static bool _openAI_query_with_file(char* query, size_t query_length, char* file
if (!res || res->status != 200) {
log_error("ERROR Failed to query API.");
+ log_error(res->body.c_str());
return 0;
}
diff --git a/src/import_service.cpp b/src/import_service.cpp
index 0aaa839..cff7fdb 100644
--- a/src/import_service.cpp
+++ b/src/import_service.cpp
@@ -36,6 +36,7 @@ ai_provider_impl _ai_get_impl()
switch(provider)
{
case AI_PROVIDER_OPENAI: return _chatgpt_api_provider;
+ case AI_PROVIDER_DEEPSEEK: return _deepseek_api_provider;
default: assert(0); break;
}
diff --git a/src/ui/ui_settings.cpp b/src/ui/ui_settings.cpp
index 1f9fe5a..d7dfd4d 100644
--- a/src/ui/ui_settings.cpp
+++ b/src/ui/ui_settings.cpp
@@ -338,13 +338,15 @@ static void ui_draw_services()
// AI service
if (ImGui::CollapsingHeader(localize("settings.services.ai_service")))
{
- char* services[1] = {
- "OpenAI"
+ // TODO: get this from iterator over ai_get_impl
+ char* services[2] = {
+ "OpenAI",
+ "DeepSeek",
};
if (ImGui::BeginCombo(localize("settings.services.ai_service.provider"), services[new_service.provider]))
{
- for (u32 n = 0; n < 1; n++)
+ for (u32 n = 0; n < 2; n++)
{
bool is_selected = n == (uint32_t)new_service.provider;
if (ImGui::Selectable(services[n], is_selected)) {
@@ -357,9 +359,6 @@ static void ui_draw_services()
ImGui::InputTextWithHint(localize("settings.services.ai_service.pubkey"), localize("settings.services.ai_service.pubkey"),
new_service.api_key_public, sizeof(new_service.api_key_public));
- ImGui::InputTextWithHint(localize("settings.services.ai_service.privkey"), localize("settings.services.ai_service.privkey"),
- new_service.api_key_private, sizeof(new_service.api_key_private));
-
if (ImGui::Button(localize("form.save"))) {
administration_set_ai_service(new_service);
}