diff options
| -rw-r--r-- | assistant.py | 20 |
1 files changed, 15 insertions, 5 deletions
diff --git a/assistant.py b/assistant.py index 173d767..96b9156 100644 --- a/assistant.py +++ b/assistant.py @@ -749,9 +749,11 @@ def collect_calls(calls, fragments): c["arguments"] += fn.get("arguments") or "" -# Cloud providers, named in [[cloud]]. A key is key_cmd's stdout, read once -# at startup; a provider whose key_cmd fails is left out, so its models are -# not offered and a chat still on one is refused. +# Cloud providers, named in [[cloud]]. A key is key_cmd's stdout, read at +# startup; a provider whose key_cmd fails (at login the smartcard PIN is +# often not entered within the timeout) is left out, so its models are not +# offered and a chat still on one is refused, until a models request asks +# for its key again. PROVIDERS = {} # name -> {"url", "key", "models"} @@ -796,7 +798,7 @@ def route(model): p = PROVIDERS[name] return p["url"], p["key"], rest, name if sep and any(p["name"] == name for p in CFG["cloud"]): - raise Refused(f"cloud {name}: no key (key_cmd failed at startup)") + raise Refused(f"cloud {name}: no key (key_cmd failed; open the model list to retry)") return CFG["llm"], None, model, None @@ -850,7 +852,9 @@ def cloud_models(name): def send_models(): # The canvas's model dropdown: the router's models, then every cloud - # model whose provider's key loaded. + # model whose provider's key loaded. Keys that failed are asked again + # here, off the lock: route() runs under it and must not wait on a PIN. + PROVIDERS.update(load_providers([p for p in CFG["cloud"] if p["name"] not in PROVIDERS])) try: with urllib.request.urlopen(CFG["llm"] + "/models", timeout=10) as r: local = [{"id": m["id"], "status": m["status"]["value"], "cloud": False} @@ -1619,6 +1623,12 @@ def test(): {"name": "broken", "url": base + "/v1", "key_cmd": "false", "models": ["y"]}] PROVIDERS.update(load_providers(CFG["cloud"])) assert set(PROVIDERS) == {"fake"} and PROVIDERS["fake"]["key"] == "sk-test" + # A key that failed at startup loads on the next models request. + CFG["cloud"][1]["key_cmd"] = "echo sk-late" + send_models() + assert PROVIDERS["broken"]["key"] == "sk-late" + del PROVIDERS["broken"] + CFG["cloud"][1]["key_cmd"] = "false" assert route("fake:vendor/x:free") == (base + "/cloud/v1", "sk-test", "vendor/x:free", "fake") assert route("Gemma:q4") == (CFG["llm"], None, "Gemma:q4", None) # no such provider try: |
