fix(ai): improve API error parsing and fallback
Some checks failed
CI Code / Check spelling (pull_request) Successful in 20s
CI Code / Check coding style (pull_request) Successful in 35s
CI Code / Code Coverage (pull_request) Successful in 2m32s
CI Code / Linux (debian) (pull_request) Failing after 3m37s
CI Code / Linux (ubuntu) (pull_request) Failing after 3m48s
CI Code / Linux (arch) (pull_request) Failing after 4m40s

Add early return for empty responses and fallback to console output
when the AI window is closed or invalid. Implement JSON error
parsing to extract readable messages from API failures, improving
debugging and user feedback.
This commit is contained in:
2026-05-09 21:06:09 +00:00
parent a898cb212d
commit ecb07fd00c

View File

@@ -121,18 +121,27 @@ _aiwin_display_error(gpointer user_data, const gchar* error_msg)
}
/**
* Display a response message in the AI window (thread-safe).
* Display a response message. If aiwin is provided and valid, display in AI window.
* Otherwise, display in console (for commands run from console).
* @param user_data The original user_data pointer (may be NULL)
* @param response The response message to display
*/
static void
_aiwin_display_response(gpointer user_data, const gchar* response)
{
ProfAiWin* aiwin = _aiwin_validate(user_data);
if (!aiwin) {
log_warning("[AI-THREAD] Cannot display response: aiwin is invalid or window was closed");
if (!response || strlen(response) == 0) {
return;
}
/* Try to display in AI window if available */
ProfAiWin* aiwin = _aiwin_validate(user_data);
if (!aiwin) {
pthread_mutex_lock(&lock);
cons_show("%s", response);
pthread_mutex_unlock(&lock);
return;
}
pthread_mutex_lock(&lock);
aiwin_display_response(aiwin, response);
pthread_mutex_unlock(&lock);
@@ -1270,6 +1279,64 @@ _parse_ai_response(const gchar* response_json)
return NULL;
}
/**
* Extract error message from an API error JSON response.
* Handles format: {"error":{"message":"...","type":"...","code":...}}
*/
static gchar*
_parse_error_response(const gchar* error_json)
{
if (!error_json || strlen(error_json) == 0)
return NULL;
/* Look for "message":"..." inside "error" object */
const gchar* error_obj = strstr(error_json, "\"error\":");
if (!error_obj)
return NULL;
const gchar* message_key = strstr(error_obj, "\"message\":\"");
if (!message_key)
return NULL;
message_key += strlen("\"message\":\"");
const gchar* msg_start = message_key;
const gchar* p = msg_start;
while (*p) {
if (*p == '\\' && *(p + 1) == '"') {
p += 2;
continue;
}
if (*p == '"') {
gsize len = p - msg_start;
gchar* result = g_new0(gchar, len + 1);
gchar* out = result;
const gchar* in = msg_start;
while (in < p) {
if (*in == '\\' && *(in + 1) == '"') {
*out++ = '"';
in += 2;
} else if (*in == '\\' && *(in + 1) == 'n') {
*out++ = '\n';
in += 2;
} else if (*in == '\\' && *(in + 1) == '\\') {
*out++ = '\\';
in += 2;
} else if (*in == '\\' && *(in + 1) == 't') {
*out++ = '\t';
in += 2;
} else {
*out++ = *in++;
}
}
*out = '\0';
return result;
}
p++;
}
return NULL;
}
static gpointer
_ai_request_thread(gpointer data)
{
@@ -1328,7 +1395,11 @@ _ai_request_thread(gpointer data)
log_debug("[AI-THREAD] API Request URL: %s", request_url);
log_debug("[AI-THREAD] Model: %s", session->model);
/* Execute request with POST fields */
/* Set URL and execute request */
curl_easy_setopt(curl, CURLOPT_URL, request_url);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_payload);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60L);
@@ -1348,8 +1419,9 @@ _ai_request_thread(gpointer data)
/* Handle HTTP errors */
log_debug("[AI-THREAD] HTTP error response body (%zu bytes): %s",
response.size, response.data ? response.data : "NULL");
auto_gchar gchar* error_msg = g_strdup_printf("HTTP %ld: %s", http_code,
response.data ? response.data : "Unknown error");
/* Try to extract the actual error message from the JSON response */
auto_gchar gchar* parsed_error = _parse_error_response(response.data);
auto_gchar gchar* error_msg = parsed_error ? g_strdup_printf("HTTP %ld: %s", http_code, parsed_error) : g_strdup_printf("HTTP %ld: %s", http_code, response.data && strlen(response.data) > 0 ? response.data : "Unknown error");
log_error("AI request failed for %s/%s: %s", session->provider_name, session->model, error_msg);
_aiwin_display_error(user_data, error_msg);
} else {