From 8e829a92c59aaaa8c6b14c3d52169ca0719209ab Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Sun, 9 Aug 2026 14:19:05 +0200 Subject: feat: namespace model ids by provider and filter model lists Cloud models are addressed as provider:model; local ones stay bare so existing sessions keep resolving. An unknown prefix is treated as part of the model name rather than a provider. Filters are case-insensitive substrings because provider ids capitalise inconsistently. Co-Authored-By: Claude Opus 5 --- test_llamachat.py | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) (limited to 'test_llamachat.py') diff --git a/test_llamachat.py b/test_llamachat.py index 09d6203..5f6bad6 100755 --- a/test_llamachat.py +++ b/test_llamachat.py @@ -978,9 +978,75 @@ def test_provider_parsing(): "filter": "qwen"}}} ) assert stringy["p"].filter == ["qwen"] + + # Any other non-list shape is wrapped too, rather than iterated: a number + # would raise, and a dict would silently degrade into its keys. + odd = providers.parse( + {"providers": {"n": {"base_url": "http://x.example.org", "filter": 5}, + "d": {"base_url": "http://y.example.org", + "filter": {"a": 1}}}} + ) + assert odd["n"].filter == ["5"] + assert odd["d"].filter == ["{'a': 1}"] print("ok provider config parsing") +def test_model_ids_and_filtering(): + """Ids are provider:model, local stays bare, filters are substrings.""" + from llamachat import providers + + table = providers.parse( + { + "providers": { + "local": {"base_url": "http://localhost:8181"}, + "together": { + "base_url": "https://api.example.org", + "filter": ["qwen", "deepseek"], + }, + "unfiltered": {"base_url": "https://api2.example.org"}, + } + } + ) + + # Local models carry no prefix, in the dropdown and in the database. + assert providers.qualify("local", "gemma4") == "gemma4" + assert providers.qualify("together", "Qwen/Qwen2.5") == "together:Qwen/Qwen2.5" + + # Splitting is the inverse, and only for providers that exist. + assert providers.split("gemma4", table) == ("local", "gemma4") + assert providers.split("together:Qwen/Qwen2.5", table) == ( + "together", + "Qwen/Qwen2.5", + ) + # An unknown prefix is part of the model name, not a provider. This is + # what keeps a local model whose name contains a colon working. + assert providers.split("weird:name", table) == ("local", "weird:name") + # Only the first colon splits. + assert providers.split("together:a:b", table) == ("together", "a:b") + + # Filtering is case-insensitive substring, any match wins. + listed = [ + "Qwen/Qwen2.5-72B-Instruct-Turbo", + "deepseek-ai/DeepSeek-V3", + "meta-llama/Llama-3.3-70B", + ] + kept = providers.apply_filter(table["together"], listed) + assert kept == [ + "Qwen/Qwen2.5-72B-Instruct-Turbo", + "deepseek-ai/DeepSeek-V3", + ] + + # No filter means everything. + assert providers.apply_filter(table["unfiltered"], listed) == listed + # The local provider is never filtered even if one is configured. + table["local"].filter = ["nothing-matches-this"] + assert providers.apply_filter(table["local"], listed) == listed + # A filter matching nothing yields nothing, it does not fall back to all. + table["together"].filter = ["zzz"] + assert providers.apply_filter(table["together"], listed) == [] + print("ok model ids and filtering") + + class _FakeResponse: """Enough of an http.client response for urlopen's context manager.""" @@ -1719,6 +1785,7 @@ if __name__ == "__main__": test_venv_discovery() test_config_defaults() test_provider_parsing() + test_model_ids_and_filtering() test_search_tool_schema() test_search_results_sanitising() test_tool_call_accumulation() -- cgit v1.2.3