fix(ai): keep in-memory defaults seeded when prefs is unavailable
Some checks failed
CI Code / Check spelling (pull_request) Successful in 17s
CI Code / Check coding style (pull_request) Successful in 32s
CI Code / Code Coverage (pull_request) Successful in 2m34s
CI Code / Linux (debian) (pull_request) Failing after 3m35s
CI Code / Linux (ubuntu) (pull_request) Failing after 3m45s
CI Code / Linux (arch) (pull_request) Failing after 4m27s

The defaults_initialized sentinel only makes sense when prefs is loaded.
Three unit tests use ai_client_setup, which calls ai_client_init without
loading prefs — prefs_ai_get_providers was bailing on the seeding branch
because of the prefs NULL check, leaving ai_client with zero providers
and breaking tests that expect at least one default.

Decouple the two concerns:
- Always seed openai/perplexity into the in-memory result list when the
  configured set is empty and we haven't already seeded.
- Only persist the seeded providers and write the defaults_initialized
  flag when prefs is actually available.

In production this is identical to the previous behaviour; in tests
without prefs the old "always-return-defaults" semantics is preserved
without a persistence side effect.

Also reflow ai_client.c JSON escape switch and HTTP-error ternary to
satisfy clang-format 18 (CI checks).
This commit is contained in:
2026-05-14 17:34:54 +03:00
parent 0847394d34
commit 43d557f2dc
2 changed files with 55 additions and 22 deletions

View File

@@ -197,15 +197,41 @@ _json_unescape_substring(const gchar* start, const gchar* end)
for (const gchar* in = start; in < end;) {
if (*in == '\\' && in + 1 < end) {
switch (in[1]) {
case '"': *w++ = '"'; in += 2; break;
case '\\': *w++ = '\\'; in += 2; break;
case '/': *w++ = '/'; in += 2; break;
case 'n': *w++ = '\n'; in += 2; break;
case 't': *w++ = '\t'; in += 2; break;
case 'r': *w++ = '\r'; in += 2; break;
case 'b': *w++ = '\b'; in += 2; break;
case 'f': *w++ = '\f'; in += 2; break;
default: *w++ = *in++; break;
case '"':
*w++ = '"';
in += 2;
break;
case '\\':
*w++ = '\\';
in += 2;
break;
case '/':
*w++ = '/';
in += 2;
break;
case 'n':
*w++ = '\n';
in += 2;
break;
case 't':
*w++ = '\t';
in += 2;
break;
case 'r':
*w++ = '\r';
in += 2;
break;
case 'b':
*w++ = '\b';
in += 2;
break;
case 'f':
*w++ = '\f';
in += 2;
break;
default:
*w++ = *in++;
break;
}
} else {
*w++ = *in++;
@@ -688,9 +714,9 @@ _curl_exec_and_handle(CURL* curl, const gchar* url, struct curl_slist* headers,
} else if (http_code >= 400) {
auto_gchar gchar* parsed = ai_parse_error_message(response->data);
auto_gchar gchar* error_msg = parsed
? g_strdup_printf("HTTP %ld: %s", http_code, parsed)
: g_strdup_printf("HTTP %ld: %s", http_code,
response->data && strlen(response->data) > 0 ? response->data : "Unknown error");
? g_strdup_printf("HTTP %ld: %s", http_code, parsed)
: g_strdup_printf("HTTP %ld: %s", http_code,
response->data && strlen(response->data) > 0 ? response->data : "Unknown error");
log_error("Request HTTP error for '%s': %s", provider->name, error_msg);
_aiwin_display_error(user_data, error_msg);
} else {

View File

@@ -67,7 +67,7 @@
#define PREF_GROUP_PLUGINS "plugins"
#define PREF_GROUP_EXECUTABLES "executables"
#define PREF_GROUP_AI "ai"
#define PREF_GROUP_AI_PREFIX "ai/" /* per-provider subsection prefix: [ai/<provider>] */
#define PREF_GROUP_AI_PREFIX "ai/" /* per-provider subsection prefix: [ai/<provider>] */
#define PREF_AI_SETTING_PREFIX "setting." /* per-provider custom setting key prefix */
#define INPBLOCK_DEFAULT 1000
@@ -2045,18 +2045,25 @@ prefs_ai_get_providers(void)
}
prefs_free_ai_providers(configured);
/* Seed openai/perplexity once on very first init only — driven by the
* "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) {
/* Seed openai/perplexity into the in-memory result on very first init
* only. In production the "defaults_initialized" sentinel in [ai] is what
* tells us we've already seeded — a user who removes every provider must
* not get them silently re-added on the next start. When prefs is NULL
* (e.g. unit tests that bring up ai_client without prefs_load) we still
* seed the in-memory list so behaviour matches a fresh first run, but
* skip the persistence write. */
gboolean already_seeded = prefs && g_key_file_get_boolean(prefs, PREF_GROUP_AI, "defaults_initialized", NULL);
if (!already_seeded && !result) {
if (prefs) {
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/"));
}
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/"));
}
if (prefs && !already_seeded) {
g_key_file_set_boolean(prefs, PREF_GROUP_AI, "defaults_initialized", TRUE);
_save_prefs();
}