Skip to content

Commit 415f4eb

Browse files
lzwjavaclaude
andcommitted
feat: filter unsupported models and show test progress
- Add UnsupportedModelError for models not accessible via /chat/completions - Test all models in parallel before displaying in /model command - Show real-time progress (e.g., "25% (10/40)") during model testing - Remove user message from history when unsupported model error occurs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 38b4fe1 commit 415f4eb

4 files changed

Lines changed: 59 additions & 3 deletions

File tree

iclaw/commands/model.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import sys
2+
import concurrent.futures
23
from iclaw.github_api import get_models, get_copilot_token
34
from iclaw.commands.auth import handle_login_command
5+
from iclaw.commands.test_models import test_model
46

57

68
def handle_model_provider_command(config_path, current_provider):
@@ -46,12 +48,29 @@ def handle_model_command(copilot_token, current_model):
4648
print(f"Error fetching models: {e}\n", file=sys.stderr)
4749
return current_model
4850

51+
total = len(model_data)
52+
print(f"Testing {total} models...")
53+
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
54+
futures = {
55+
executor.submit(test_model, copilot_token, m["id"]): m for m in model_data
56+
}
57+
working_models = []
58+
completed = 0
59+
for future in concurrent.futures.as_completed(futures):
60+
model_id, works = future.result()
61+
if works:
62+
working_models.append(futures[future])
63+
completed += 1
64+
pct = int(completed * 100 / total)
65+
print(f"\r{pct}% ({completed}/{total})", end="", flush=True)
66+
print()
67+
4968
groups = {}
50-
for m in model_data:
69+
for m in working_models:
5170
owner = m.get("owned_by", "unknown")
5271
groups.setdefault(owner, []).append(m["id"])
5372

54-
flat_models = [m["id"] for m in model_data]
73+
flat_models = [m["id"] for m in working_models]
5574
print(f"\nCurrent model: {current_model}")
5675
print("Available models:")
5776

iclaw/commands/test_models.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from iclaw import http
2+
from iclaw.github_api import COPILOT_API_BASE, COPILOT_HEADERS
3+
4+
5+
def test_model(copilot_token, model_id):
6+
"""Test if a model works with /chat/completions"""
7+
payload = {
8+
"model": model_id,
9+
"messages": [{"role": "user", "content": "hi"}],
10+
"stream": False,
11+
}
12+
try:
13+
resp = http.get_session().post(
14+
f"{COPILOT_API_BASE}/chat/completions",
15+
headers={"Authorization": f"Bearer {copilot_token}", **COPILOT_HEADERS},
16+
json=payload,
17+
timeout=10,
18+
)
19+
return model_id, resp.status_code == 200
20+
except Exception:
21+
return model_id, False

iclaw/github_api.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ def get_models(copilot_token):
4343
return resp.json().get("data", [])
4444

4545

46+
class UnsupportedModelError(Exception):
47+
pass
48+
49+
4650
def chat(messages, copilot_token, model="gpt-4o", tools=None):
4751
payload = {"model": model, "messages": messages, "stream": False}
4852
if tools:
@@ -54,6 +58,10 @@ def chat(messages, copilot_token, model="gpt-4o", tools=None):
5458
json=payload,
5559
)
5660
if not resp.ok:
61+
if resp.status_code == 400 and "unsupported_api_for_model" in resp.text:
62+
raise UnsupportedModelError(
63+
f'Model "{model}" is not accessible via /chat/completions'
64+
)
5765
raise RuntimeError(
5866
f"Chat API error: {resp.status_code} {resp.reason}\n{resp.text}"
5967
)

iclaw/main.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
save_session_settings,
2525
)
2626
from iclaw.exec_tool import exec_command as exec
27-
from iclaw.github_api import chat, get_copilot_token
27+
from iclaw.github_api import UnsupportedModelError, chat, get_copilot_token
2828
from iclaw.tools.defs import TOOLS
2929
from iclaw.tools.edit_tool import EditTool
3030
from iclaw.web_search import web_search
@@ -152,6 +152,10 @@ def main():
152152
messages.append({"role": "assistant", "content": reply})
153153
last_reply = reply
154154
log.log_info(f"\n{reply}\n")
155+
except UnsupportedModelError as e:
156+
print(f"Error: {e}", file=sys.stderr)
157+
print("Please select a different model with /model", file=sys.stderr)
158+
messages.pop()
155159
except Exception as e:
156160
print(f"Error: {e}", file=sys.stderr)
157161
continue
@@ -308,6 +312,10 @@ def main():
308312
messages.append({"role": "assistant", "content": reply})
309313
last_reply = reply
310314
log.log_info(f"\n{reply}\n")
315+
except UnsupportedModelError as e:
316+
print(f"Error: {e}", file=sys.stderr)
317+
print("Please select a different model with /model", file=sys.stderr)
318+
messages.pop()
311319
except Exception as e:
312320
print(f"Error: {e}", file=sys.stderr)
313321

0 commit comments

Comments
 (0)