fix(ai): repair custom setting name autocompletion

The setting-name branch for /ai set custom never fired: it passed the
prefix "/ai set custom " with a trailing space to
autocomplete_param_with_ac(), which appends a space itself, so the
matched prefix contained a double space no real input ever has. Even
without that, the primitive completes the first token after the command
while the setting name is the second, and the num_args guard only held
before the name was started. The suggestion list (tools/search/memory/
plugins) predated the /ai set custom backend and matched no real
payload parameter.

Replace the branch with token-position completion (arg 5 via
autocomplete_param_no_with_func) fed by a list rebuilt on demand from
common payload parameters plus the keys already set on the provider,
exposed through the new ai_get_provider_setting_keys() (snapshot taken
under settings_lock). Drop the now-unused parse_args() call whose NULL
result was fed to g_strv_length() unguarded, spamming a glib CRITICAL
on TAB at /ai with zero or five-plus arguments.
This commit is contained in:
2026-07-06 11:08:58 +03:00
parent 14f76ab1db
commit b3a269342d
6 changed files with 112 additions and 22 deletions

View File

@@ -637,6 +637,34 @@ test_ai_set_provider_setting_reserved_key(void** state)
assert_string_equal("0.7", temp);
}
void
test_ai_get_provider_setting_keys(void** state)
{
ai_add_provider("p_setting_keys", "https://example.test/skeys/");
/* No settings yet, unknown provider, NULL — all yield NULL */
assert_null(ai_get_provider_setting_keys("p_setting_keys"));
assert_null(ai_get_provider_setting_keys("nonexistent"));
assert_null(ai_get_provider_setting_keys(NULL));
ai_set_provider_setting("p_setting_keys", "temperature", "0.7");
ai_set_provider_setting("p_setting_keys", "reasoning_effort", "high");
/* Both keys listed (order is hash-defined, assert membership) */
GList* keys = ai_get_provider_setting_keys("p_setting_keys");
assert_int_equal(2, g_list_length(keys));
assert_non_null(g_list_find_custom(keys, "temperature", (GCompareFunc)g_strcmp0));
assert_non_null(g_list_find_custom(keys, "reasoning_effort", (GCompareFunc)g_strcmp0));
g_list_free_full(keys, g_free);
/* Removing a setting removes its key */
ai_set_provider_setting("p_setting_keys", "temperature", NULL);
keys = ai_get_provider_setting_keys("p_setting_keys");
assert_int_equal(1, g_list_length(keys));
assert_string_equal("reasoning_effort", keys->data);
g_list_free_full(keys, g_free);
}
void
test_ai_set_provider_api_type(void** state)
{

View File

@@ -44,6 +44,7 @@ void test_ai_set_provider_default_model(void** state);
void test_ai_get_provider_default_model(void** state);
void test_ai_set_provider_setting(void** state);
void test_ai_set_provider_setting_reserved_key(void** state);
void test_ai_get_provider_setting_keys(void** state);
void test_ai_set_provider_api_type(void** state);
void test_ai_api_type_string_round_trip(void** state);
void test_ai_get_provider_setting(void** state);

View File

@@ -753,6 +753,7 @@ main(int argc, char* argv[])
cmocka_unit_test_setup_teardown(test_ai_get_provider_default_model, ai_client_setup, ai_client_teardown),
cmocka_unit_test_setup_teardown(test_ai_set_provider_setting, ai_client_setup, ai_client_teardown),
cmocka_unit_test_setup_teardown(test_ai_set_provider_setting_reserved_key, ai_client_setup, ai_client_teardown),
cmocka_unit_test_setup_teardown(test_ai_get_provider_setting_keys, ai_client_setup, ai_client_teardown),
cmocka_unit_test_setup_teardown(test_ai_set_provider_api_type, ai_client_setup, ai_client_teardown),
cmocka_unit_test(test_ai_api_type_string_round_trip),
cmocka_unit_test_setup_teardown(test_ai_get_provider_setting, ai_client_setup, ai_client_teardown),