From 39902f1888f202aa77ac471a2655d0b34d5bfe88 Mon Sep 17 00:00:00 2001 From: HexLab98 Date: Sun, 19 Jul 2026 16:55:51 +0700 Subject: [PATCH] test(models): cover kimi search alias for Kimi Coding k3 Assert the picker haystack keeps ordinary ids unchanged, surfaces wire id k3 for "kimi"/"k3" queries, and accept search_labels in curses mocks. --- tests/hermes_cli/test_model_search.py | 28 +++++++++++++ .../test_setup_menu_curses_migration.py | 26 +++++++++++- ui-tui/src/lib/model-search-text.test.ts | 40 +++++++++++++++++++ 3 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 tests/hermes_cli/test_model_search.py create mode 100644 ui-tui/src/lib/model-search-text.test.ts diff --git a/tests/hermes_cli/test_model_search.py b/tests/hermes_cli/test_model_search.py new file mode 100644 index 0000000000000..1b7728a14a1e1 --- /dev/null +++ b/tests/hermes_cli/test_model_search.py @@ -0,0 +1,28 @@ +"""Picker search aliases for brand-less wire model ids.""" + +from hermes_cli.curses_ui import _filter_indices +from hermes_cli.model_search import model_search_text + + +def test_model_search_text_keeps_ordinary_ids(): + assert model_search_text("kimi-k2.6") == "kimi-k2.6" + assert model_search_text("glm-5.2") == "glm-5.2" + + +def test_model_search_text_adds_kimi_aliases_for_k3(): + assert model_search_text("k3") == "k3 kimi-k3 kimi" + assert model_search_text("K3") == "K3 kimi-k3 kimi" + + +def test_filter_indices_surfaces_k3_for_kimi_query(): + models = ["kimi-k2.6", "kimi-k2.5", "k3", "kimi-for-coding"] + haystacks = [model_search_text(m) for m in models] + ranked = [models[i] for i in _filter_indices(haystacks, "kimi")] + assert "k3" in ranked + + +def test_filter_indices_still_finds_k3_by_wire_id(): + models = ["kimi-k2.6", "k3", "kimi-for-coding"] + haystacks = [model_search_text(m) for m in models] + ranked = [models[i] for i in _filter_indices(haystacks, "k3")] + assert ranked == ["k3"] diff --git a/tests/hermes_cli/test_setup_menu_curses_migration.py b/tests/hermes_cli/test_setup_menu_curses_migration.py index eaf382272c52c..fb38aa131a966 100644 --- a/tests/hermes_cli/test_setup_menu_curses_migration.py +++ b/tests/hermes_cli/test_setup_menu_curses_migration.py @@ -14,9 +14,19 @@ def test_prompt_model_selection_uses_curses_radiolist(): seen = {} - def _fake(title, items, *, selected=0, cancel_returns=None, description=None, searchable=False): + def _fake( + title, + items, + *, + selected=0, + cancel_returns=None, + description=None, + searchable=False, + search_labels=None, + ): seen["title"] = title seen["items"] = items + seen["search_labels"] = search_labels return 1 # pick second model with patch("hermes_cli.curses_ui.curses_radiolist", side_effect=_fake), \ @@ -30,6 +40,8 @@ def test_prompt_model_selection_uses_curses_radiolist(): plain = [radio_item_plain(item) for item in seen["items"]] assert plain[:2] == ["model-a", "model-b"] assert "Skip (keep current)" in plain + assert seen["search_labels"] is not None + assert len(seen["search_labels"]) == len(seen["items"]) def test_prompt_model_selection_esc_cancels(): @@ -70,8 +82,18 @@ def test_model_selection_with_pricing_passes_description(): seen = {} - def _fake(title, items, *, selected=0, cancel_returns=None, description=None, searchable=False): + def _fake( + title, + items, + *, + selected=0, + cancel_returns=None, + description=None, + searchable=False, + search_labels=None, + ): seen["description"] = description + seen["search_labels"] = search_labels return len(items) - 1 # Skip pricing = { diff --git a/ui-tui/src/lib/model-search-text.test.ts b/ui-tui/src/lib/model-search-text.test.ts new file mode 100644 index 0000000000000..9f8d3a0c5b36d --- /dev/null +++ b/ui-tui/src/lib/model-search-text.test.ts @@ -0,0 +1,40 @@ +import { describe, expect, it } from 'vitest' + +import { fuzzyRank } from './fuzzy.js' +import { modelSearchText } from './model-search-text.js' + +describe('modelSearchText', () => { + it('keeps ordinary model ids unchanged', () => { + expect(modelSearchText('kimi-k2.6')).toBe('kimi-k2.6') + expect(modelSearchText('glm-5.2')).toBe('glm-5.2') + }) + + it('adds kimi aliases for the bare Kimi Coding k3 wire id', () => { + expect(modelSearchText('k3')).toBe('k3 kimi-k3 kimi') + expect(modelSearchText('K3')).toBe('K3 kimi-k3 kimi') + }) +}) + +describe('model picker search with aliases', () => { + const models = [ + 'kimi-k2.6', + 'kimi-k2.5', + 'k3', + 'kimi-for-coding', + ] + + it('surfaces k3 when the user searches kimi', () => { + const ranked = fuzzyRank(models, 'kimi', modelSearchText).map(r => r.item) + expect(ranked).toContain('k3') + }) + + it('still finds k3 by its wire id', () => { + const ranked = fuzzyRank(models, 'k3', modelSearchText).map(r => r.item) + expect(ranked).toEqual(['k3']) + }) + + it('does not invent k3 for unrelated queries', () => { + const ranked = fuzzyRank(models, 'glm', modelSearchText).map(r => r.item) + expect(ranked).toEqual([]) + }) +})