Fix AI memory leaks #121
@@ -1419,10 +1419,10 @@ _ai_request_thread(gpointer data)
|
|||||||
* list modifications). We must snapshot all fields atomically.
|
* list modifications). We must snapshot all fields atomically.
|
||||||
* ===================================================================== */
|
* ===================================================================== */
|
||||||
pthread_mutex_lock(&session->lock);
|
pthread_mutex_lock(&session->lock);
|
||||||
gchar* local_provider_name = g_strdup(session->provider_name);
|
auto_gchar gchar* local_provider_name = g_strdup(session->provider_name);
|
||||||
AIProvider* local_provider = ai_provider_ref(session->provider);
|
AIProvider* local_provider = ai_provider_ref(session->provider);
|
||||||
gchar* local_model = g_strdup(session->model);
|
auto_gchar gchar* local_model = g_strdup(session->model);
|
||||||
gchar* local_api_key = g_strdup(session->api_key ? session->api_key : "");
|
auto_gchar gchar* local_api_key = g_strdup(session->api_key ? session->api_key : "");
|
||||||
pthread_mutex_unlock(&session->lock);
|
pthread_mutex_unlock(&session->lock);
|
||||||
|
|
||||||
log_debug("[AI-THREAD] Session: %s/%s", local_provider_name, local_model);
|
log_debug("[AI-THREAD] Session: %s/%s", local_provider_name, local_model);
|
||||||
@@ -1434,6 +1434,8 @@ _ai_request_thread(gpointer data)
|
|||||||
local_provider_name, local_provider_name);
|
local_provider_name, local_provider_name);
|
||||||
log_error("AI request failed for %s/%s: %s", local_provider_name, local_model, error_msg);
|
log_error("AI request failed for %s/%s: %s", local_provider_name, local_model, error_msg);
|
||||||
_aiwin_display_error(user_data, error_msg);
|
_aiwin_display_error(user_data, error_msg);
|
||||||
|
ai_provider_unref(local_provider);
|
||||||
|
ai_session_unref(session);
|
||||||
g_free(args);
|
g_free(args);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -1444,6 +1446,8 @@ _ai_request_thread(gpointer data)
|
|||||||
log_error("AI request failed for %s/%s: Failed to initialize curl",
|
log_error("AI request failed for %s/%s: Failed to initialize curl",
|
||||||
local_provider_name, local_model);
|
local_provider_name, local_model);
|
||||||
_aiwin_display_error(user_data, "Failed to initialize curl.");
|
_aiwin_display_error(user_data, "Failed to initialize curl.");
|
||||||
|
ai_provider_unref(local_provider);
|
||||||
|
ai_session_unref(session);
|
||||||
g_free(args);
|
g_free(args);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -1492,6 +1496,7 @@ _ai_request_thread(gpointer data)
|
|||||||
long http_code = 0;
|
long http_code = 0;
|
||||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
|
||||||
log_debug("[AI-THREAD] HTTP response code: %ld", http_code);
|
log_debug("[AI-THREAD] HTTP response code: %ld", http_code);
|
||||||
|
auto_gchar gchar* response_data = g_steal_pointer(&response.data);
|
||||||
|
|
||||||
if (res != CURLE_OK) {
|
if (res != CURLE_OK) {
|
||||||
auto_gchar gchar* error_msg = g_strdup(curl_easy_strerror(res));
|
auto_gchar gchar* error_msg = g_strdup(curl_easy_strerror(res));
|
||||||
@@ -1500,17 +1505,14 @@ _ai_request_thread(gpointer data)
|
|||||||
} else if (http_code >= 400) {
|
} else if (http_code >= 400) {
|
||||||
/* Handle HTTP errors */
|
/* Handle HTTP errors */
|
||||||
log_debug("[AI-THREAD] HTTP error response body (%zu bytes): %s",
|
log_debug("[AI-THREAD] HTTP error response body (%zu bytes): %s",
|
||||||
response.size, response.data ? response.data : "NULL");
|
response.size, response_data);
|
||||||
/* Try to extract the actual error message from the JSON response */
|
/* Try to extract the actual error message from the JSON response */
|
||||||
auto_gchar gchar* parsed_error = ai_parse_error_message(response.data);
|
auto_gchar gchar* parsed_error = ai_parse_error_message(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");
|
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", local_provider_name, local_model, error_msg);
|
log_error("AI request failed for %s/%s: %s", local_provider_name, local_model, error_msg);
|
||||||
_aiwin_display_error(user_data, error_msg);
|
_aiwin_display_error(user_data, error_msg);
|
||||||
} else {
|
} else {
|
||||||
/* Parse response - transfer ownership to auto_gchar for cleanup */
|
log_debug("[AI-THREAD] Raw API response (%zu bytes): %s", response.size, response_data);
|
||||||
log_debug("[AI-THREAD] Raw API response (%zu bytes): %s", response.size, response.data ? response.data : "NULL");
|
|
||||||
auto_gchar gchar* response_data = response.data;
|
|
||||||
response.data = NULL;
|
|
||||||
auto_gchar gchar* content = ai_parse_response(response_data);
|
auto_gchar gchar* content = ai_parse_response(response_data);
|
||||||
if (content) {
|
if (content) {
|
||||||
/* Add assistant response to history (under lock) */
|
/* Add assistant response to history (under lock) */
|
||||||
@@ -1531,12 +1533,9 @@ _ai_request_thread(gpointer data)
|
|||||||
curl_slist_free_all(headers);
|
curl_slist_free_all(headers);
|
||||||
curl_easy_cleanup(curl);
|
curl_easy_cleanup(curl);
|
||||||
|
|
||||||
/* Cleanup local copies */
|
|
||||||
g_free(local_provider_name);
|
|
||||||
ai_provider_unref(local_provider);
|
ai_provider_unref(local_provider);
|
||||||
g_free(local_model);
|
|
||||||
g_free(local_api_key);
|
|
||||||
|
|
||||||
|
ai_session_unref(session);
|
||||||
g_free(args);
|
g_free(args);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
@@ -8516,7 +8516,8 @@ _cmd_execute_default(ProfWin* window, const char* inp)
|
|||||||
case WIN_AI:
|
case WIN_AI:
|
||||||
{
|
{
|
||||||
ProfAiWin* aiwin = (ProfAiWin*)window;
|
ProfAiWin* aiwin = (ProfAiWin*)window;
|
||||||
cl_ev_send_ai_msg(aiwin, inp, connection_create_stanza_id());
|
auto_gchar gchar* stanza_id = connection_create_stanza_id();
|
||||||
|
cl_ev_send_ai_msg(aiwin, inp, stanza_id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -10980,6 +10981,9 @@ cmd_ai_start(ProfWin* window, const char* const command, gchar** args)
|
|||||||
}
|
}
|
||||||
log_debug("[AI-CMD] wins_new_ai() returned successfully, window type: %d", ai_win->type);
|
log_debug("[AI-CMD] wins_new_ai() returned successfully, window type: %d", ai_win->type);
|
||||||
|
|
||||||
|
// Release the reference held by cmd_ai_start, since the window now holds one
|
||||||
|
ai_session_unref(session);
|
||||||
|
|
||||||
// Add welcome message to the AI window
|
// Add welcome message to the AI window
|
||||||
log_debug("[AI-CMD] Adding welcome messages...");
|
log_debug("[AI-CMD] Adding welcome messages...");
|
||||||
win_println(ai_win, THEME_DEFAULT, "-", "AI Chat: %s/%s", provider_name, model);
|
win_println(ai_win, THEME_DEFAULT, "-", "AI Chat: %s/%s", provider_name, model);
|
||||||
|
|||||||
Reference in New Issue
Block a user