aboutsummaryrefslogtreecommitdiffstats
path: root/llamachat/providers.py
diff options
context:
space:
mode:
Diffstat (limited to 'llamachat/providers.py')
-rw-r--r--llamachat/providers.py24
1 files changed, 22 insertions, 2 deletions
diff --git a/llamachat/providers.py b/llamachat/providers.py
index 5874b32..0d2d9f7 100644
--- a/llamachat/providers.py
+++ b/llamachat/providers.py
@@ -76,6 +76,13 @@ def parse(values: dict) -> dict[str, Provider]:
out: dict[str, Provider] = {}
for name, entry in table.items():
entry = entry or {}
+ name = str(name)
+ if not name or ":" in name:
+ # The id scheme splits on the first colon, so a name containing
+ # one builds ids that split back to something else entirely, and
+ # an empty name builds ":model". Both route to local under a
+ # nonsense name, silently. Skipping is the only honest option.
+ continue
base_url = str(entry.get("base_url") or "").rstrip("/")
if not base_url:
# ponytail: a provider with no URL is misconfigured, not a
@@ -91,8 +98,8 @@ def parse(values: dict) -> dict[str, Provider]:
if not isinstance(needles, (list, tuple)):
needles = [needles]
vision = entry.get("vision")
- out[str(name)] = Provider(
- name=str(name),
+ out[name] = Provider(
+ name=name,
base_url=base_url,
api_key=str(entry.get("api_key") or ""),
filter=[str(f) for f in needles],
@@ -117,6 +124,14 @@ def split(model_id: str, table: dict[str, Provider]) -> tuple[str, str]:
A prefix that is not a configured provider is treated as part of the
model name, which keeps a bare local model containing a colon working.
"""
+ # ponytail: this is not injective, and cannot be while local ids stay
+ # bare. A local model actually named "together:x" is indistinguishable
+ # from together's model "x", so it resolves to local only until the user
+ # configures a provider called "together", at which point the stored
+ # session id rebinds to the cloud model and starts billing without a
+ # word. Unlikely, since local names are usually filenames, and the bare
+ # local id is the feature's premise. The upgrade path is to qualify local
+ # as "local:<model>" too and migrate the sessions.model column.
prefix, sep, rest = model_id.partition(":")
if sep and prefix in table and prefix != LOCAL:
return prefix, rest
@@ -129,8 +144,13 @@ def apply_filter(provider: Provider, listed: list[str]) -> list[str]:
Case-insensitive, because provider ids capitalise inconsistently:
"qwen" has to match "Qwen/Qwen2.5-72B-Instruct-Turbo".
"""
+ # Every path returns a fresh list on purpose: callers hold onto the
+ # result, and handing back "listed" itself would let them mutate the
+ # caller's own list. Not a redundant copy, do not simplify it away.
if provider.is_local or not provider.filter:
return list(listed)
+ # An empty needle is a typo, not a request to hide every model, so a
+ # filter of only empty strings means no filter.
needles = [f.lower() for f in provider.filter if f]
if not needles:
return list(listed)