diff --git a/src/main.cpp b/src/main.cpp index 8cf73af..2f5e88e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,7 +27,9 @@ const SecretSchema kSecretSchema = { struct Config { std::string hosted_url; + std::string hosted_model; std::string local_url = "http://localhost:8080"; + std::string local_model; std::string prompt; bool hosted_first = true; bool clipboard = false; @@ -52,8 +54,10 @@ struct App { struct SettingsFields { GtkWidget* hosted_url; + GtkWidget* hosted_model; GtkWidget* hosted_key; GtkWidget* local_url; + GtkWidget* local_model; GtkWidget* local_key; GtkWidget* prompt; GtkWidget* hosted_first; @@ -126,8 +130,12 @@ void load_config(App* app) { gchar* value = nullptr; value = g_key_file_get_string(file, kConfigGroup, "hosted_url", nullptr); if (value) { app->config.hosted_url = value; g_free(value); } + value = g_key_file_get_string(file, kConfigGroup, "hosted_model", nullptr); + if (value) { app->config.hosted_model = value; g_free(value); } value = g_key_file_get_string(file, kConfigGroup, "local_url", nullptr); if (value) { app->config.local_url = value; g_free(value); } + value = g_key_file_get_string(file, kConfigGroup, "local_model", nullptr); + if (value) { app->config.local_model = value; g_free(value); } value = g_key_file_get_string(file, kConfigGroup, "prompt", nullptr); if (value) { app->config.prompt = value; g_free(value); } if (g_key_file_has_key(file, kConfigGroup, "hosted_first", nullptr)) @@ -145,7 +153,9 @@ bool save_config(App* app, const Config& config, const std::string& hosted_key, const std::string& local_key, std::string* err) { GKeyFile* file = g_key_file_new(); g_key_file_set_string(file, kConfigGroup, "hosted_url", config.hosted_url.c_str()); + g_key_file_set_string(file, kConfigGroup, "hosted_model", config.hosted_model.c_str()); g_key_file_set_string(file, kConfigGroup, "local_url", config.local_url.c_str()); + g_key_file_set_string(file, kConfigGroup, "local_model", config.local_model.c_str()); g_key_file_set_string(file, kConfigGroup, "prompt", config.prompt.c_str()); g_key_file_set_boolean(file, kConfigGroup, "hosted_first", config.hosted_first); g_key_file_set_boolean(file, kConfigGroup, "clipboard", config.clipboard); @@ -190,7 +200,9 @@ void save_clicked(GtkButton*, gpointer data) { App* app = static_cast(g_object_get_data(G_OBJECT(fields->hosted_url), "app")); Config config; config.hosted_url = gtk_entry_get_text(GTK_ENTRY(fields->hosted_url)); + config.hosted_model = gtk_entry_get_text(GTK_ENTRY(fields->hosted_model)); config.local_url = gtk_entry_get_text(GTK_ENTRY(fields->local_url)); + config.local_model = gtk_entry_get_text(GTK_ENTRY(fields->local_model)); config.prompt = gtk_entry_get_text(GTK_ENTRY(fields->prompt)); config.hosted_first = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(fields->hosted_first)); config.clipboard = gtk_combo_box_get_active(GTK_COMBO_BOX(fields->destination)) == 1; @@ -234,21 +246,25 @@ void open_settings(App* app) { auto* fields = new SettingsFields(); fields->hosted_url = labelled(grid, "Hosted endpoint", gtk_entry_new(), 0); - fields->hosted_key = labelled(grid, "Hosted API key", gtk_entry_new(), 1); - fields->local_url = labelled(grid, "Local endpoint", gtk_entry_new(), 2); - fields->local_key = labelled(grid, "Local API key", gtk_entry_new(), 3); - fields->prompt = labelled(grid, "Prompt", gtk_entry_new(), 4); + fields->hosted_model = labelled(grid, "Hosted model", gtk_entry_new(), 1); + fields->hosted_key = labelled(grid, "Hosted API key", gtk_entry_new(), 2); + fields->local_url = labelled(grid, "Local endpoint", gtk_entry_new(), 3); + fields->local_model = labelled(grid, "Local model", gtk_entry_new(), 4); + fields->local_key = labelled(grid, "Local API key", gtk_entry_new(), 5); + fields->prompt = labelled(grid, "Prompt", gtk_entry_new(), 6); fields->hosted_first = gtk_check_button_new_with_label("Hosted first, local fallback"); - gtk_grid_attach(GTK_GRID(grid), fields->hosted_first, 1, 5, 1, 1); + gtk_grid_attach(GTK_GRID(grid), fields->hosted_first, 1, 7, 1, 1); fields->destination = gtk_combo_box_text_new(); gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(fields->destination), "Type at cursor"); gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(fields->destination), "Clipboard"); - labelled(grid, "Output", fields->destination, 6); + labelled(grid, "Output", fields->destination, 8); gtk_entry_set_visibility(GTK_ENTRY(fields->hosted_key), false); gtk_entry_set_visibility(GTK_ENTRY(fields->local_key), false); gtk_entry_set_text(GTK_ENTRY(fields->hosted_url), app->config.hosted_url.c_str()); + gtk_entry_set_text(GTK_ENTRY(fields->hosted_model), app->config.hosted_model.c_str()); gtk_entry_set_text(GTK_ENTRY(fields->hosted_key), app->hosted_key.c_str()); gtk_entry_set_text(GTK_ENTRY(fields->local_url), app->config.local_url.c_str()); + gtk_entry_set_text(GTK_ENTRY(fields->local_model), app->config.local_model.c_str()); gtk_entry_set_text(GTK_ENTRY(fields->local_key), app->local_key.c_str()); gtk_entry_set_text(GTK_ENTRY(fields->prompt), app->config.prompt.c_str()); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fields->hosted_first), app->config.hosted_first); @@ -318,7 +334,8 @@ size_t curl_write(char* buffer, size_t size, size_t nmemb, void* data) { } bool transcription_request(const std::string& url, const std::string& key, - const std::string& path, const std::string& prompt, + const std::string& model, const std::string& path, + const std::string& prompt, std::string* transcript, std::string* error) { CURL* curl = curl_easy_init(); if (!curl) { *error = "Could not initialize HTTP client"; return false; } @@ -327,6 +344,11 @@ bool transcription_request(const std::string& url, const std::string& key, curl_mimepart* part = curl_mime_addpart(mime); curl_mime_name(part, "response_format"); curl_mime_data(part, "json", CURL_ZERO_TERMINATED); + if (!model.empty()) { + part = curl_mime_addpart(mime); + curl_mime_name(part, "model"); + curl_mime_data(part, model.c_str(), CURL_ZERO_TERMINATED); + } part = curl_mime_addpart(mime); curl_mime_name(part, "file"); curl_mime_filedata(part, path.c_str()); @@ -452,15 +474,17 @@ gpointer job_thread(gpointer data) { auto* job = static_cast(data); std::string hosted_error; if (job->config.hosted_first && !job->config.hosted_url.empty()) { - job->ok = transcription_request(job->config.hosted_url, job->hosted_key, job->file, - job->config.prompt, &job->text, &hosted_error); + job->ok = transcription_request(job->config.hosted_url, job->hosted_key, + job->config.hosted_model, job->file, job->config.prompt, + &job->text, &hosted_error); if (job->ok) { g_main_context_invoke(nullptr, finish_job, job); return nullptr; } } - job->ok = transcription_request(job->config.local_url, job->local_key, job->file, - job->config.prompt, &job->text, &job->error); + job->ok = transcription_request(job->config.local_url, job->local_key, + job->config.local_model, job->file, job->config.prompt, + &job->text, &job->error); if (!job->ok && !hosted_error.empty()) job->error = "Hosted: " + hosted_error + "\nLocal: " + job->error; g_main_context_invoke(nullptr, finish_job, job); return nullptr;