fix(ai): persistence and command-output fixes
- Persist per-provider default model in [ai] prefs (set/get/remove helpers, load on init, clear with provider removal). Previously /ai set default-model only updated runtime state. - Stop re-seeding openai/perplexity into [ai] every time the configured provider list is empty. The defaults are now seeded once, guarded by a "defaults_initialized" sentinel, so removing every provider does not silently restore them. - /ai providers (no arg) now lists the configured providers with key status, instead of the hardcoded openai/perplexity blurb. - /ai models <provider> (fresh fetch) prints the model names it cached, not just a count. Previously the user had to follow up with --cached to actually see them. - /ai remove provider now also clears the persisted default model, token, and cached model list, leaving no orphan keys in [ai]. - Drop dead ai_models_find declaration from ai_client.h (no implementation in this tree).
This commit is contained in:
@@ -296,13 +296,16 @@ ai_client_init(void)
|
|||||||
/* Load saved API keys from config */
|
/* Load saved API keys from config */
|
||||||
ai_load_keys();
|
ai_load_keys();
|
||||||
|
|
||||||
/* Load cached models for all providers */
|
/* Load cached models and persisted default-model for all providers */
|
||||||
GList* provider_list = ai_list_providers();
|
GList* provider_list = ai_list_providers();
|
||||||
GList* models_curr = provider_list;
|
for (GList* curr = provider_list; curr; curr = g_list_next(curr)) {
|
||||||
while (models_curr) {
|
AIProvider* provider = (AIProvider*)curr->data;
|
||||||
AIProvider* provider = (AIProvider*)models_curr->data;
|
|
||||||
_ai_load_models_for_provider(provider);
|
_ai_load_models_for_provider(provider);
|
||||||
models_curr = g_list_next(models_curr);
|
auto_gchar gchar* dm = prefs_ai_get_default_model(provider->name);
|
||||||
|
if (dm && dm[0] != '\0') {
|
||||||
|
g_free(provider->default_model);
|
||||||
|
provider->default_model = g_strdup(dm);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
g_list_free(provider_list);
|
g_list_free(provider_list);
|
||||||
|
|
||||||
@@ -400,6 +403,9 @@ ai_remove_provider(const gchar* name)
|
|||||||
|
|
||||||
/* Remove from config before removing from hash table */
|
/* Remove from config before removing from hash table */
|
||||||
prefs_ai_remove_provider(name);
|
prefs_ai_remove_provider(name);
|
||||||
|
prefs_ai_remove_default_model(name);
|
||||||
|
prefs_ai_remove_models(name);
|
||||||
|
prefs_ai_remove_token(name);
|
||||||
|
|
||||||
/* Sync autocomplete before removing */
|
/* Sync autocomplete before removing */
|
||||||
autocomplete_remove(providers_ac, name);
|
autocomplete_remove(providers_ac, name);
|
||||||
@@ -501,6 +507,7 @@ ai_set_provider_default_model(const gchar* provider_name, const gchar* model)
|
|||||||
|
|
||||||
g_free(provider->default_model);
|
g_free(provider->default_model);
|
||||||
provider->default_model = g_strdup(model);
|
provider->default_model = g_strdup(model);
|
||||||
|
prefs_ai_set_default_model(provider_name, model);
|
||||||
log_info("Default model for provider '%s' set to: %s", provider_name, model);
|
log_info("Default model for provider '%s' set to: %s", provider_name, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -979,9 +986,18 @@ _models_response_handler(AIProvider* provider, const gchar* response, gpointer u
|
|||||||
{
|
{
|
||||||
_parse_and_cache_models(provider, response);
|
_parse_and_cache_models(provider, response);
|
||||||
|
|
||||||
auto_gchar gchar* msg = g_strdup_printf("Cached %d models for provider '%s'. Use '/ai start <provider> <model>' to start a chat.",
|
gint count = g_list_length(provider->models);
|
||||||
g_list_length(provider->models), provider->name);
|
GString* msg = g_string_new(NULL);
|
||||||
_aiwin_display_response(user_data, msg);
|
g_string_append_printf(msg, "Cached %d models for provider '%s':", count, provider->name);
|
||||||
|
for (GList* curr = provider->models; curr; curr = g_list_next(curr)) {
|
||||||
|
g_string_append_printf(msg, "\n %s", (const gchar*)curr->data);
|
||||||
|
}
|
||||||
|
if (count > 0) {
|
||||||
|
g_string_append_printf(msg, "\nUse '/ai start %s <model>' to start a chat.", provider->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto_gchar gchar* out = g_string_free(msg, FALSE);
|
||||||
|
_aiwin_display_response(user_data, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gpointer
|
static gpointer
|
||||||
|
|||||||
@@ -106,15 +106,6 @@ GList* ai_list_providers(void);
|
|||||||
*/
|
*/
|
||||||
gchar* ai_providers_find(const char* const search_str, gboolean previous, void* context);
|
gchar* ai_providers_find(const char* const search_str, gboolean previous, void* context);
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a model name for autocomplete.
|
|
||||||
* @param search_str The search string
|
|
||||||
* @param previous Whether to go to previous match
|
|
||||||
* @param context ProfAiWin* for the current session
|
|
||||||
* @return Model name, or NULL if not found
|
|
||||||
*/
|
|
||||||
gchar* ai_models_find(const char* const search_str, gboolean previous, void* context);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the provider autocomplete state.
|
* Reset the provider autocomplete state.
|
||||||
* Called from cmd_ac_reset() to clear autocomplete state.
|
* Called from cmd_ac_reset() to clear autocomplete state.
|
||||||
|
|||||||
@@ -11174,30 +11174,24 @@ cmd_ai_clear(ProfWin* window, const char* const command, gchar** args)
|
|||||||
gboolean
|
gboolean
|
||||||
cmd_ai_providers(ProfWin* window, const char* const command, gchar** args)
|
cmd_ai_providers(ProfWin* window, const char* const command, gchar** args)
|
||||||
{
|
{
|
||||||
if (args[1] == NULL || g_strcmp0(args[1], "list") != 0) {
|
|
||||||
// List all available providers
|
|
||||||
cons_show("Available AI providers:");
|
|
||||||
cons_show("");
|
|
||||||
cons_show(" openai - OpenAI API (https://api.openai.com)");
|
|
||||||
cons_show(" perplexity - Perplexity API (https://api.perplexity.ai)");
|
|
||||||
cons_show("");
|
|
||||||
cons_show("Add custom providers with: /ai set provider <name> <url>");
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// List configured providers with key status
|
|
||||||
cons_show("Configured providers:");
|
cons_show("Configured providers:");
|
||||||
cons_show("");
|
cons_show("");
|
||||||
|
|
||||||
GList* providers = ai_list_providers();
|
GList* providers = ai_list_providers();
|
||||||
|
if (!providers) {
|
||||||
|
cons_show(" (none configured)");
|
||||||
|
cons_show("");
|
||||||
|
cons_show("Add a provider with: /ai set provider <name> <url>");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
for (GList* curr = providers; curr; curr = g_list_next(curr)) {
|
for (GList* curr = providers; curr; curr = g_list_next(curr)) {
|
||||||
AIProvider* provider = curr->data;
|
AIProvider* provider = curr->data;
|
||||||
gchar* key = ai_get_provider_key(provider->name);
|
auto_gchar gchar* key = ai_get_provider_key(provider->name);
|
||||||
cons_show(" %s", provider->name);
|
cons_show(" %s", provider->name);
|
||||||
cons_show(" URL: %s", provider->api_url);
|
cons_show(" URL: %s", provider->api_url);
|
||||||
cons_show(" Key: %s", key ? "configured" : "NOT configured");
|
cons_show(" Key: %s", key ? "configured" : "NOT configured");
|
||||||
cons_show("");
|
cons_show("");
|
||||||
g_free(key);
|
|
||||||
}
|
}
|
||||||
g_list_free(providers);
|
g_list_free(providers);
|
||||||
|
|
||||||
|
|||||||
@@ -1863,6 +1863,53 @@ prefs_free_ai_models(GList* models)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
* AI Default Model (per provider)
|
||||||
|
* ======================================================================== */
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
prefs_ai_set_default_model(const char* const provider, const char* const model)
|
||||||
|
{
|
||||||
|
if (!provider || !model || !prefs)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (model[0] == '\0')
|
||||||
|
return prefs_ai_remove_default_model(provider);
|
||||||
|
|
||||||
|
auto_gchar gchar* key = g_strconcat(provider, "_default_model", NULL);
|
||||||
|
g_key_file_set_string(prefs, PREF_GROUP_AI, key, model);
|
||||||
|
_save_prefs();
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
prefs_ai_remove_default_model(const char* const provider)
|
||||||
|
{
|
||||||
|
if (!provider || !prefs)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
auto_gchar gchar* key = g_strconcat(provider, "_default_model", NULL);
|
||||||
|
if (!g_key_file_has_key(prefs, PREF_GROUP_AI, key, NULL))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
g_key_file_remove_key(prefs, PREF_GROUP_AI, key, NULL);
|
||||||
|
_save_prefs();
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
char*
|
||||||
|
prefs_ai_get_default_model(const char* const provider)
|
||||||
|
{
|
||||||
|
if (!provider || !prefs)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
auto_gchar gchar* key = g_strconcat(provider, "_default_model", NULL);
|
||||||
|
if (!g_key_file_has_key(prefs, PREF_GROUP_AI, key, NULL))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return g_key_file_get_string(prefs, PREF_GROUP_AI, key, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================================================================
|
/* ========================================================================
|
||||||
* AI Provider Management
|
* AI Provider Management
|
||||||
* ======================================================================== */
|
* ======================================================================== */
|
||||||
@@ -1953,35 +2000,35 @@ prefs_free_ai_providers(GList* providers)
|
|||||||
GList*
|
GList*
|
||||||
prefs_ai_get_providers(void)
|
prefs_ai_get_providers(void)
|
||||||
{
|
{
|
||||||
/* If user has configured any providers, return those */
|
|
||||||
GList* configured = prefs_ai_list_providers();
|
GList* configured = prefs_ai_list_providers();
|
||||||
if (configured && g_list_length(configured) > 0) {
|
GList* result = NULL;
|
||||||
/* Build result list with name, url pairs */
|
|
||||||
GList* result = NULL;
|
for (GList* curr = configured; curr; curr = g_list_next(curr)) {
|
||||||
GList* curr = configured;
|
gchar* name = (gchar*)curr->data;
|
||||||
while (curr) {
|
auto_gchar gchar* url = prefs_ai_get_provider_url(name);
|
||||||
gchar* name = (gchar*)curr->data;
|
if (url && strlen(url) > 0) {
|
||||||
gchar* url = prefs_ai_get_provider_url(name);
|
result = g_list_append(result, g_strdup(name));
|
||||||
if (url && strlen(url) > 0) {
|
result = g_list_append(result, g_strdup(url));
|
||||||
result = g_list_append(result, g_strdup(name));
|
|
||||||
result = g_list_append(result, g_strdup(url));
|
|
||||||
}
|
|
||||||
g_free(url);
|
|
||||||
curr = g_list_next(curr);
|
|
||||||
}
|
}
|
||||||
prefs_free_ai_providers(configured);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
prefs_free_ai_providers(configured);
|
prefs_free_ai_providers(configured);
|
||||||
|
|
||||||
prefs_ai_set_provider("openai", "https://api.openai.com/");
|
/* Seed openai/perplexity once on very first init only — driven by the
|
||||||
prefs_ai_set_provider("perplexity", "https://api.perplexity.ai/");
|
* "defaults_initialized" sentinel so a user who removes every provider
|
||||||
|
* does not get them silently re-added. */
|
||||||
|
if (prefs && !g_key_file_get_boolean(prefs, PREF_GROUP_AI, "defaults_initialized", NULL)) {
|
||||||
|
if (!result) {
|
||||||
|
prefs_ai_set_provider("openai", "https://api.openai.com/");
|
||||||
|
prefs_ai_set_provider("perplexity", "https://api.perplexity.ai/");
|
||||||
|
result = g_list_append(result, g_strdup("openai"));
|
||||||
|
result = g_list_append(result, g_strdup("https://api.openai.com/"));
|
||||||
|
result = g_list_append(result, g_strdup("perplexity"));
|
||||||
|
result = g_list_append(result, g_strdup("https://api.perplexity.ai/"));
|
||||||
|
}
|
||||||
|
g_key_file_set_boolean(prefs, PREF_GROUP_AI, "defaults_initialized", TRUE);
|
||||||
|
_save_prefs();
|
||||||
|
}
|
||||||
|
|
||||||
GList* result = NULL;
|
|
||||||
result = g_list_append(result, g_strdup("openai"));
|
|
||||||
result = g_list_append(result, g_strdup("https://api.openai.com/"));
|
|
||||||
result = g_list_append(result, g_strdup("perplexity"));
|
|
||||||
result = g_list_append(result, g_strdup("https://api.perplexity.ai/"));
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -380,4 +380,9 @@ gboolean prefs_ai_remove_models(const char* const provider);
|
|||||||
GList* prefs_ai_get_models(const char* const provider);
|
GList* prefs_ai_get_models(const char* const provider);
|
||||||
void prefs_free_ai_models(GList* models);
|
void prefs_free_ai_models(GList* models);
|
||||||
|
|
||||||
|
/* AI default model per provider */
|
||||||
|
gboolean prefs_ai_set_default_model(const char* const provider, const char* const model);
|
||||||
|
gboolean prefs_ai_remove_default_model(const char* const provider);
|
||||||
|
char* prefs_ai_get_default_model(const char* const provider);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user