feat(ai): implement robust JSON model parsing
Some checks failed
CI Code / Check coding style (pull_request) Successful in 46s
CI Code / Linux (arch) (pull_request) Failing after 9m46s
CI Code / Code Coverage (pull_request) Failing after 14m8s
CI Code / Check spelling (pull_request) Failing after 14m16s
CI Code / Linux (ubuntu) (pull_request) Failing after 14m24s
CI Code / Linux (debian) (pull_request) Failing after 14m33s

Rewrote internal JSON parsing to correctly handle whitespace, escaped
quotes, and strict field context extraction. This prevents incorrect
field names or provider names from being added to the model list.

Added `ai_parse_models_from_json` public API to facilitate testing.

Changed `/ai models` default behavior to always fetch fresh models.
Replaced `--refresh` flag with `--cached` to display local cache.

Added comprehensive unit tests covering OpenAI and Perplexity formats,
array format, empty/null JSON, escaped quotes, and whitespace handling.
This commit is contained in:
2026-05-09 13:56:59 +00:00
parent 07d267b5bc
commit a898cb212d
6 changed files with 396 additions and 39 deletions

View File

@@ -11134,14 +11134,16 @@ cmd_ai_switch(ProfWin* window, const char* const command, gchar** args)
gboolean
cmd_ai_models(ProfWin* window, const char* const command, gchar** args)
{
// /ai models <provider> [--refresh]
// /ai models <provider> [--cached]
// Default: fetch fresh models from API
// --cached: display cached models (if available)
if (args[1] == NULL) {
cons_bad_cmd_usage(command);
return TRUE;
}
const gchar* provider_name = args[1];
gboolean refresh = (g_strv_length(args) >= 3) && (g_strcmp0(args[2], "--refresh") == 0);
gboolean use_cached = (g_strv_length(args) >= 3) && (g_strcmp0(args[2], "--cached") == 0);
AIProvider* provider = ai_get_provider(provider_name);
if (!provider) {
@@ -11149,12 +11151,12 @@ cmd_ai_models(ProfWin* window, const char* const command, gchar** args)
return TRUE;
}
if (refresh || !ai_models_are_fresh(provider_name)) {
// Fetch models from API
ProfAiWin* aiwin = (window && window->type == WIN_AI) ? (ProfAiWin*)window : wins_get_ai();
ai_fetch_models(provider_name, aiwin);
} else {
// Display cached models
if (use_cached) {
// Display cached models (if available)
if (!provider->models) {
cons_show("No cached models for provider '%s'. Use '/ai models %s' to fetch from API.", provider_name, provider_name);
return TRUE;
}
cons_show("Cached models for provider '%s':", provider_name);
GList* curr = provider->models;
while (curr) {
@@ -11162,7 +11164,11 @@ cmd_ai_models(ProfWin* window, const char* const command, gchar** args)
curr = g_list_next(curr);
}
cons_show("");
cons_show("Use '/ai models %s --refresh' to fetch fresh models from the API.", provider_name);
cons_show("Use '/ai models %s' to fetch fresh models from the API.", provider_name);
} else {
// Default: fetch fresh models from API
ProfAiWin* aiwin = (window && window->type == WIN_AI) ? (ProfAiWin*)window : wins_get_ai();
ai_fetch_models(provider_name, aiwin);
}
return TRUE;