feat(onboarding): verify Test connection without a model id (#792) - #1529
feat(onboarding): verify Test connection without a model id (#792)#1529lifelmy wants to merge 13 commits into
Conversation
Test connection required a model id: probe_llm_provider raises when the model is empty and the Web UI disabled the button until a model was entered. That is a chicken-and-egg bind — a user cannot list a provider's models until the connection is verified, but cannot verify it without already knowing a model. onboarding.provider.probe now falls back to the model-list endpoint (discover_provider_models) when the model field is empty, mapping the listing outcome onto the frozen probe envelope (chat-only timings stay at their never-reached sentinels). The Web UI no longer treats an empty model as a missing required field for draft primary-provider probes; stored/draft profile probes still resolve a concrete model. Fixes TokenRhythm#792
# Conflicts: # src/opensquilla/gateway/rpc_onboarding.py
|
Current follow-up on head Verification:
The remaining failed GitHub check is unrelated to this PR's onboarding diff: Windows high-risk (core) completed 6,947 tests successfully and then A maintainer/admin rerun of failed jobs for run 33724721382 is still needed to clear the transient Windows gate. |
|
Refreshed this PR onto the latest Verification on the refreshed head:
The push superseded the prior CI run containing the unrelated Windows core timeout. GitHub reports the refreshed branch as mergeable; its new CI jobs are running. |
|
Refreshed this PR onto the latest upstream/main (4535029); new head is 33de521. The branch was 1 commit behind and merged cleanly without manual conflict edits. Verification: backend onboarding/RPC/issue-link suites passed (1073 passed, 4 skipped); WebUI privacy suite passed (175 passed); focused ruff and git diff --check passed. |
|
Task A CI follow-up (2026-09-05): the completed Windows recovery-migration shard had one failure in |
# Conflicts: # src/opensquilla/gateway/rpc_onboarding.py
|
Task A refresh (2026-09-05): fetched latest Verification on the refreshed head:
No new comments/reviews or unresolved review threads. |
|
Refreshed this branch onto latest upstream/main (1663b1a); new head is e3c1716. The one-commit base update merged without conflicts, preserving the onboarding Test connection behavior. Verification: backend onboarding suite 72 passed; WebUI privacy Vitest suite 175 passed; focused Ruff and git diff --check passed. |
|
Task A refresh: merged latest |
|
Task A refresh: merged latest |
Problem
Fixes #792.
In Settings → Model services, the Test connection button calls the
onboarding.provider.probeRPC, whose underlyingprobe_llm_provider()hard-requires a model id (an empty model raisesValueError). That is a chicken-and-egg bind for a new user: you cannot know which models a provider serves until you can list them, but the model list is only fetched after the connection verifies — and the connection cannot be verified without already supplying a valid model id.Change
When the model field is empty, connectivity is now verified through the model-list endpoint (
GET /v1/modelsvia the existingdiscover_provider_models()) instead of a one-token chat completion — exactly the behavior the issue proposes.gateway/rpc_onboarding.py):onboarding.provider.probebranches on the model field. With a model it runs the usual usage-accounted chat probe; with an empty model it calls a new_connectivity_probe_via_discovery()helper that runsdiscover_provider_models()and maps the listing outcome (ok/failureKind/detail) back onto the frozen probe envelope. The chat-only timing fields (latencyMs,firstResponseMs,totalMs) stay at their never-reached-the-network sentinels since no chat round-trip happened. Anoklisting that simply returns nothing still counts as reachable (the credentials verified). The wire contract is unchanged.useSetupCatalog.ts): for a draft primary-provider config, an empty model is no longer treated as a missing required field, so the Test connection button is enabled and probes with nomodelparam. Stored/draft profile probes go throughonboarding.llmProfile[.draft].probe, which still resolves a concrete deployment model, so those keep requiring a model.Testing (all run locally, real execution)
uv run ruff check src tests→ all checks passeduv run pytest -q tests/test_gateway/test_rpc_onboarding.py tests/test_contracts tests/test_onboarding→ 1196 passed, 4 skippedtest_provider_probe_without_model_verifies_via_model_list,test_provider_probe_without_model_reports_auth_failureuv run pytest -q tests/test_cli/test_models_probe.py→ 12 passed;uv run pytest -q tests/test_onboarding/test_provider_probe.py→ passeduv run mypy src/opensquilla/gateway/rpc_onboarding.py→ no issuesnpx vitest runonuseSetupCatalog.privacy.test.ts+useSetupProviderForm.test.ts(232 passed) andSetupProviderPanel.test.ts+SetupProviderCredentialCard.test.ts(123 passed); updated the privacy suite's model-less probe expectationsnpm run build(Web UI artifact verified + staged),npm run typecheck(all guards passed),uv build --wheel→ builtopensquilla-0.5.4-py3-none-any.whlNotes
I use a coding assistant to help implement, and I review and take responsibility for the final change. Opened as a draft for maintainer review; happy to adjust the surface (e.g. keep the button enabled but relabel it, or gate the fallback behind a capability flag) if you prefer a different shape.