Compare commits

..

2 Commits

Author SHA1 Message Date
e9614abb89 style(ai): rename unparseable to unparsable for codespell
Some checks failed
CI Code / Check spelling (pull_request) Successful in 14s
CI Code / Check coding style (pull_request) Failing after 23s
CI Code / Code Coverage (pull_request) Successful in 3m46s
CI Code / Linux (debian) (pull_request) Successful in 5m50s
CI Code / Linux (ubuntu) (pull_request) Successful in 6m0s
CI Code / Linux (arch) (pull_request) Successful in 7m46s
2026-07-10 12:52:44 +03:00
cac2aa856a fix(ai): bound response parsing and harden AUTO fallback errors
Some checks failed
CI Code / Check spelling (pull_request) Failing after 14s
CI Code / Check coding style (pull_request) Successful in 30s
CI Code / Linux (debian) (pull_request) Successful in 5m0s
CI Code / Linux (ubuntu) (pull_request) Successful in 5m8s
CI Code / Linux (arch) (pull_request) Successful in 6m36s
CI Code / Code Coverage (pull_request) Successful in 3m36s
- bound _parse_responses scans to the output_text part's object and to
  the content array, so a later sibling item's "text" (e.g. a reasoning
  summary) can never be returned as the assistant reply
- do not retry the other flavour on a curl timeout: the request likely
  reached the server and may still be generating, so a re-POST of the
  conversation could trigger a second billed generation
- remember an unparseable 2xx from the first AUTO attempt and surface
  stashed first-attempt errors in both error paths, instead of showing
  only the final attempt's transport or HTTP error
- recognize Ollama's model-not-found wording in _names_model so a model
  typo is not misread as a missing endpoint
- drop the dead, racy provider-lookup fallback in the generic request
  thread: a missing provider ref is a caller bug and now fails loudly
- fix ai_providers_lists_defaults to expect the header the command
  actually prints ("Configured providers:"); the test was broken since
  its introduction but CI never ran it
- add functional test group 5 (AI command surface) to FUNC_TEST_GROUPS
  so the CI parallel target runs it; proftest.c port ranges already
  account for five groups
2026-07-10 12:07:34 +03:00

View File

@@ -472,16 +472,12 @@ _names_model(const gchar* s)
{
if (!s)
return FALSE;
/* "does not exist"/"not found"/"not supported" alone also appear in
* route-missing bodies; require them to talk about a model (covers
* Ollama's 'model "x" not found, try pulling it first' and Perplexity's
* 'model "x" is not supported'). Gateways vary in casing, so compare
* case-insensitively. */
auto_gchar gchar* lower = g_ascii_strdown(s, -1);
return strstr(lower, "model_not_found") || strstr(lower, "invalid_model")
|| strstr(lower, "unknown_model")
|| (strstr(lower, "model")
&& (strstr(lower, "does not exist") || strstr(lower, "not found") || strstr(lower, "not supported")));
/* "does not exist"/"not found" alone also appear in route-missing bodies;
* require them to talk about a model (covers Ollama's 'model "x" not
* found, try pulling it first') */
return strstr(s, "model_not_found") || strstr(s, "invalid_model")
|| strstr(s, "unknown_model")
|| (strstr(s, "model") && (strstr(s, "does not exist") || strstr(s, "not found")));
}
/* A missing endpoint answers 404/405/501. A wrong model can also 404/405, but
@@ -1092,7 +1088,7 @@ _curl_exec_and_handle(CURL* curl, const gchar* url, struct curl_slist* headers,
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 : "empty response body");
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 {
@@ -2190,7 +2186,7 @@ _ai_request_thread(gpointer data)
ai_api_type_to_string(attempts[i + 1]));
if (!first_unparsable_note)
first_unparsable_note = g_strdup_printf("%s endpoint answered HTTP %ld but the body could not be parsed",
ai_api_type_to_string(used_type), http_code);
ai_api_type_to_string(used_type), http_code);
continue;
}
}
@@ -2240,7 +2236,7 @@ _ai_request_thread(gpointer data)
response.size, response_data);
/* Try to extract the actual error message from the JSON response */
auto_gchar gchar* parsed_error = ai_parse_error_message(err_body);
auto_gchar gchar* error_msg = parsed_error ? g_strdup_printf("HTTP %ld: %s", err_code, parsed_error) : g_strdup_printf("HTTP %ld: %s", err_code, err_body && strlen(err_body) > 0 ? err_body : "empty response body");
auto_gchar gchar* error_msg = parsed_error ? g_strdup_printf("HTTP %ld: %s", err_code, parsed_error) : g_strdup_printf("HTTP %ld: %s", err_code, err_body && strlen(err_body) > 0 ? err_body : "Unknown error");
if (first_unparsable_note) {
gchar* with_note = g_strdup_printf("%s (first attempt: %s)", error_msg, first_unparsable_note);
g_free(error_msg);