Make model serving collision-safe and simplify the Dash serve wizard
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 40
- Forks
- 9
- Avg merge
- 4d 20h
- Merged PRs (30d)
- 59
Description
Problem Statement
The Dash serve wizard exposes engine, device policy, host, port, and mode alongside the model selector even though ordinary users only need to choose a model recipe. The controls do not communicate their interaction model: Left/Right changes some rows but silently does nothing on text rows, and typing into the populated port appends to the default rather than replacing it.
Every ordinary serve launch also defaults to port 11435. A second model can therefore be launched against an occupied port and only fail later in engine startup. The delayed failure is difficult to understand and can leave a misleading service attempt while the user only wanted to run another model.
Solution
Make the ordinary Dash flow model-first. The collapsed wizard shows the model recipe picker, one inline Advanced settings disclosure row, and the review/launch action. Engine, GPU-required device selection, loopback host, collision-safe automatic port selection, and managed mode remain automatic unless the user expands Advanced settings.
When expanded, Advanced settings remains part of the same wizard rather than opening another modal. Choice fields visibly use cycling controls. Editable fields visibly use text controls and enter an explicit edit state where typing replaces the selected default, Left/Right moves the cursor, Enter accepts, and Escape cancels.
Move port policy into the central rocm serve path so Dash, direct CLI use, assistant tool calls, foreground launches, and managed launches share one rule. Omitting --port selects an available port beginning with the established local default. Supplying --port preserves that exact request and fails early with an actionable collision message when it is occupied. Automatic launches may choose another available port; explicit launches must never be silently moved.
User Stories
- As a first-time user, I want the serve wizard to initially ask only for a model recipe, so that I can launch a model without understanding network or engine settings.
- As a basic user, I want engine selection to be automatic, so that the chosen recipe runs on a supported engine without manual configuration.
- As a basic user, I want device selection to remain GPU-required and automatic, so that serving never silently falls back to CPU.
- As a basic user, I want the host to default to loopback, so that my server is local and safe by default.
- As a basic user, I want the port to be chosen automatically, so that I can launch multiple models without manually coordinating ports.
- As a basic user, I want managed mode to remain the default, so that launched services appear in service management and dashboard telemetry.
- As a user, I want Advanced settings to expand inline, so that I retain context and do not have to navigate another modal.
- As a user, I want Advanced settings to collapse inline, so that the common path remains compact after I inspect or change an option.
- As a user, I want collapsed automatic settings to be labelled
Automatic, so that I know defaults will be resolved for me. - As a user, I want collapsed overridden settings to be labelled
Customized, so that hidden overrides are not mistaken for defaults. - As a user, I want my advanced values preserved when the section is collapsed, so that inspecting the simple view does not discard my choices.
- As a keyboard user, I want choice rows to display chevrons, so that I know Left/Right will cycle them.
- As a keyboard user, I want editable rows to display brackets, so that I know they accept text rather than Left/Right cycling.
- As a keyboard user, I want Enter on an editable value to start editing, so that navigation and text entry are distinct modes.
- As a keyboard user, I want the existing editable value selected when editing begins, so that typing replaces the default instead of appending to it.
- As a keyboard user, I want Left/Right to move the cursor while editing, so that I can correct a value without erasing the entire field.
- As a keyboard user, I want Enter to accept an edit and Escape to cancel it, so that editing has predictable commit and rollback behavior.
- As a keyboard user, I want the footer to describe the focused control, so that instructions never advertise keys that do nothing on that row.
- As a user, I want Port to begin as
Auto, so that the normal path avoids collisions without exposing a number. - As an advanced user, I want to select
Customfor Port and edit the number inline, so that I can integrate with tools that require a fixed endpoint. - As an advanced user, I want an invalid custom port rejected in the form, so that I can correct it before approval.
- As an advanced user, I want an occupied custom port rejected with a plain explanation, so that I know what to change.
- As an advanced user, I want the collision message to recommend Auto or another port, so that recovery is obvious.
- As a CLI user, I want
rocm serve MODELto choose an available port, so that CLI and Dash behavior remain consistent. - As a CLI user, I want
rocm serve MODEL --port Nto request exactly N, so that scripts retain deterministic explicit-port behavior. - As a CLI user, I want an occupied explicit port to fail before an engine is launched, so that no misleading service record or long readiness wait is created.
- As a user launching a second model, I want automatic selection to skip ports already owned by live managed services, so that both models can run concurrently.
- As a user launching beside an unrelated local application, I want automatic selection to skip ports occupied outside ROCm CLI, so that that application is not disrupted.
- As a user repeating an equivalent managed launch, I want the existing live engine-and-model service returned instead of a duplicate process, so that idempotent behavior is preserved regardless of its selected port.
- As a user, I want stale or stopped service history not to reserve a port, so that ports become reusable after a service ends.
- As a user, I want the approval card to say that the port is automatic and the launch output to show the actual resolved endpoint, so that the review is honest and I can connect clients after allocation.
- As a user, I want automatic selection to skip ports already occupied when the launch transaction runs, so that ordinary multi-model and local-application collisions are prevented before engine startup.
- As a user who explicitly selected a port, I want a bind race to fail rather than move my service silently, so that the endpoint contract is honored.
- As a user on Windows, Linux, or WSL, I want the same collision policy and plain error semantics, so that serving behaves consistently on supported platforms.
- As a user of local chat and telemetry, I want dynamically selected endpoints to continue coming from the managed service registry, so that chat discovery and metrics follow the actual service rather than a hardcoded default.
- As a user launching on a public host, I want existing public-bind approval and authentication protections preserved, so that automatic port selection does not weaken endpoint security.
- As an operator, I want service restart and recovery to reuse the recorded resolved port, so that an existing service's endpoint does not change unexpectedly.
- As an operator, I want logs and audit records to name the resolved endpoint and collision reason, so that failures can be diagnosed without guessing.
Implementation Decisions
rocm serveis the single owner of automatic versus explicit port policy. Dash omits--portfor Auto and includes it only for Custom. Auto is available only with the canonical default host127.0.0.1; custom, IPv6, hostname, wildcard, and public hosts require an explicit port so address-family semantics remain unambiguous.- The established local model-server port remains the first automatic candidate. Automatic selection advances through a bounded local candidate range and returns a clear exhaustion error when no candidate is available.
- Explicit
--portremains a valid stable CLI contract and is never rewritten. Proactive OS collision preflight applies to explicit ports on canonical127.0.0.1; advanced custom-host endpoints retain the existing engine-owned bind/error behavior. - Canonical-loopback port availability considers both live managed-service records and the operating-system bind result. Stopped or stale records do not block reuse.
- Equivalent managed engine-and-canonical-model launches remain idempotent and return the existing live service before creating another process.
- A shared, bounded cross-process allocation lock serializes new CLI launches and daemon/manual recovery while they refresh liveness, detect equivalent services, reserve a canonical-loopback candidate, confirm immediate child startup, and publish a live record. Recovery leases its recorded canonical-loopback port before publishing
recovering; an occupied port leaves the prior status unchanged and spawns nothing. - Automatic selection uses a temporary IPv4 loopback socket lease to reject ports already bound when the transaction runs. Other bind failures stop immediately with the operating-system error. Because third-party engines do not accept inherited listeners, an unrelated process can still race the final release/spawn handoff; that rare engine failure is reported honestly and never silently changes an explicit or already-reported endpoint.
- A failed collision check or exhausted automatic range must not create a service manifest, key file, supervisor process, or readiness wait.
- The resolved port is persisted in the existing managed service record. Restart, recovery, chat discovery, telemetry collection, summaries, logs, and endpoint authentication continue consuming that record rather than recalculating a port. The approval card says
Port: automatic; endpoint shown after launch; only post-allocation launch output reports the concrete endpoint. - The default wizard field order is Model, Advanced settings, Review and launch.
- Expanding Advanced settings inserts Engine, Device, Host, Port, and Mode into the same field list. It does not create another modal.
- Automatic Engine means the CLI resolves from explicit configuration, host GPU preference, recipe preference, and platform default using the existing precedence.
- Automatic Device preserves the existing GPU-required serving invariant. This feature does not add or encourage CPU fallback.
- Automatic Host is exact
127.0.0.1. Automatic Mode is managed. - Port has two choice states: Auto and Custom. Custom exposes the numeric editor on the same row. A noncanonical loopback Host with Port Auto is invalid: review/launch remains blocked and the inline fix says to set Port to Custom. Dash supports loopback hosts only; public/wildcard/other-address Hosts are rejected before approval with guidance to use the CLI's explicit
--allow-public-bindflow, preserving endpoint-authentication review. - Choice rows render visible cycling affordances; text rows render visible edit affordances.
- Text editing is an explicit state. Enter begins editing with the current value selected, printable input replaces the selected value, Left/Right moves the cursor, Backspace edits, Enter accepts, and Escape restores the pre-edit value.
- The footer is derived from the focused field and edit state rather than showing one generic instruction string.
- Collapsing Advanced settings preserves values. Its summary reports
Automaticonly when every advanced field remains at its automatic default; otherwise it reportsCustomizedand may include the custom port. - The existing model recipe picker, local-path browser, approval gate, job bridge, live job console, duplicate-job guard, service registry, authentication policy, and engine protocol remain the integration surfaces.
- Public hosts continue to require the existing explicit public-bind permission and endpoint authentication. Dash does not expose that security-sensitive override; it directs advanced public binds to the CLI. Port automation does not bypass those checks.
- Existing direct engine adapter commands keep concrete port arguments; only the user-facing
rocm servecommand gains omitted-port automatic selection. - User-facing copy uses
model,server,automatic,local, andport, avoiding internal runtime or adapter jargon on the collapsed screen.
Testing Decisions
- Tests defend observable behavior rather than struct layout, private helper names, or source text.
- The highest port-policy seam is the user-facing serve argument and canonical-loopback launch-policy boundary: omitted port selects an available endpoint, explicit occupied loopback port is rejected, explicit free loopback port is retained, stopped records do not block reuse, and equivalent live services remain idempotent.
- Port-policy tests use real IPv4 loopback listeners, injected ordered candidates/errors, bounded separate-process lock contention, and deterministic barriers to cover occupied/free conditions and concurrent CLI/recovery allocations without requiring a GPU or live inference engine.
- Automatic-selection tests cover the established default being free, the default being occupied, multiple consecutive collisions, exact-range exhaustion, non-address-in-use failure classification, concurrent equivalent launches converging, concurrent different-model launches receiving distinct ports, lock timeout/process-death release, and recovery preserving its recorded port.
- Collision tests assert that preflight failure occurs before manifest creation or process spawn at the launch-policy seam; publication-failure tests assert any spawned child is terminated and the live reservation is cleaned.
- Dash tests use the existing wizard event-handler, argument-building, approval, job-bridge, and rendered-buffer seams.
- Dash behavior tests cover collapsed default rows, inline expansion/collapse, focus order in each state, preservation of overrides, Automatic/Customized summary text, visible field affordances, and context-sensitive footer hints.
- Editing tests cover selected-on-entry replacement, cursor movement, insertion, backspace, accept, cancel, host trimming, digits-only custom port entry, and invalid port validation.
- Argument tests assert that Auto omits
--hostand--port, Custom emits the chosen explicit--port, loopback custom Host emits--host, noncanonical loopback Host with Auto is rejected, and public/wildcard/other-address Hosts never reach approval in Dash. - Approval tests continue to prove that no mutating job runs until the user approves, invalid host/port and public-host combinations never reach approval, and the exact generated argv is shown for review.
- Registry-consumer tests confirm chat discovery and telemetry continue to use each service record's endpoint and port rather than the established default.
- CLI parsing tests confirm omitted
--portis distinguishable from an explicit port and that existing explicit-port scripts remain accepted. - Supported-platform compilation and workspace quality gates remain required. Live GPU serving is not required for deterministic collision and wizard tests, but existing GPU-required smoke behavior must remain unchanged.
Out of Scope
- Changing model recipes or engine preference rules.
- Adding CPU fallback or changing GPU selection policy.
- Allowing one model to span multiple GPUs.
- Changing direct engine adapter CLI defaults.
- Adding a second modal or a separate advanced-settings screen.
- Persisting serve-wizard overrides as global defaults.
- Automatically exposing servers publicly.
- Changing endpoint authentication, API-key storage, service restart semantics, or service-record schema beyond using the already-recorded resolved port.
- Redesigning the services manager, chat UI, telemetry dashboard, or approval component.
- Adding mouse-specific behavior beyond preserving existing TUI behavior.
Further Notes
The project UX guidance identifies non-technical Windows users as the primary audience, requires plain language and arrow-key navigation, and explicitly recommends hiding advanced choices behind a selectable Advanced options row. The change should preserve the current approval-before-mutation rule and strict GPU-only behavior.
The managed service registry is already the authority for dynamic endpoints used by chat and telemetry. The established port remains useful as the first automatic candidate and as a legacy discovery fallback, but new managed services must not depend on that port being unique.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the rocm serve entry point and the Dash serve wizard, then trace how launches interact with the managed service registry. The implementation is done when automatic and explicit port behavior, inline wizard editing, collision handling, and resolved-endpoint persistence work across the launch and recovery paths described in the issue.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend, cli
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100