aboutsummaryrefslogtreecommitdiffstats
path: root/assistant.py
diff options
context:
space:
mode:
Diffstat (limited to 'assistant.py')
-rw-r--r--assistant.py20
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: