diff --git a/crates/rocm-dash-tui/src/app/mod.rs b/crates/rocm-dash-tui/src/app/mod.rs index d73af377..2201006c 100644 --- a/crates/rocm-dash-tui/src/app/mod.rs +++ b/crates/rocm-dash-tui/src/app/mod.rs @@ -995,8 +995,9 @@ impl AppState { self.chat_detect_offer = Some(cfg); } else { self.chat_detect_offer = None; - self.chat_detect_msg = - Some("no local engine found (Lemonade :13305 / vLLM :8000)".into()); + self.chat_detect_msg = Some( + "no local engine found (Lemonade :13305 / vLLM :8000 / rocm serve :11435)".into(), + ); } } diff --git a/crates/rocm-dash-tui/src/llm.rs b/crates/rocm-dash-tui/src/llm.rs index c151376b..6876a6aa 100644 --- a/crates/rocm-dash-tui/src/llm.rs +++ b/crates/rocm-dash-tui/src/llm.rs @@ -139,7 +139,9 @@ pub fn probe_endpoint(base_url: &str, timeout: Duration) -> bool { false } -/// Probe the known local serving endpoints in priority order (Lemonade, then vLLM) and return the first reachable one. +/// Probe the known local serving endpoints in priority order (Lemonade, then +/// vLLM, then `rocm serve`'s non-managed default) and return the first +/// reachable one. /// /// Used by the TUI's in-app "detect a local engine" action so the user need not run the CLI skill first. /// TCP-only (no HTTP); never blocks longer than [`PROBE_TIMEOUT`] per candidate. @@ -147,6 +149,7 @@ pub fn detect_local_endpoint() -> Option<&'static str> { [ crate::skills::LEMONADE_ENDPOINT, crate::skills::VLLM_ENDPOINT, + crate::skills::ROCM_SERVE_ENDPOINT, ] .into_iter() .find(|ep| probe_endpoint(ep, PROBE_TIMEOUT)) @@ -324,4 +327,21 @@ mod tests { )); assert!(!probe_endpoint("not a url", Duration::from_millis(100))); } + + #[test] + fn detect_local_endpoint_probes_rocm_serve_default_port() { + // A listener on rocm serve's (non-managed) default port must be picked + // up by the fallback probe, not just Lemonade/vLLM's ports. + let Ok(listener) = + std::net::TcpListener::bind(("127.0.0.1", crate::skills::ROCM_SERVE_PORT)) + else { + // Port already bound in this environment; skip rather than flake. + return; + }; + assert_eq!( + detect_local_endpoint(), + Some(crate::skills::ROCM_SERVE_ENDPOINT) + ); + drop(listener); + } } diff --git a/crates/rocm-dash-tui/src/skills.rs b/crates/rocm-dash-tui/src/skills.rs index 4a554d0e..55968c7d 100644 --- a/crates/rocm-dash-tui/src/skills.rs +++ b/crates/rocm-dash-tui/src/skills.rs @@ -21,6 +21,13 @@ pub const LEMONADE_ENDPOINT: &str = "http://localhost:13305/v1"; /// vLLM's conventional OpenAI-compatible endpoint + port. pub const VLLM_PORT: u16 = 8000; pub const VLLM_ENDPOINT: &str = "http://localhost:8000/v1"; +/// `rocm serve`'s (non-managed) default vLLM port. +/// +/// Mirrors `rocm_core::DEFAULT_LOCAL_PORT`; duplicated as a literal because +/// this crate deliberately carries no `rocm-core` dependency (see +/// `AppState::model_recipes`). +pub const ROCM_SERVE_PORT: u16 = 11_435; +pub const ROCM_SERVE_ENDPOINT: &str = "http://localhost:11435/v1"; /// One typed step in a skill manifest. Tagged by `type` in TOML. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/crates/rocm-dash-tui/src/ui/tabs/chat.rs b/crates/rocm-dash-tui/src/ui/tabs/chat.rs index 25eb1335..3b30d746 100644 --- a/crates/rocm-dash-tui/src/ui/tabs/chat.rs +++ b/crates/rocm-dash-tui/src/ui/tabs/chat.rs @@ -60,7 +60,7 @@ fn draw_consent(f: &mut Frame, area: Rect, state: &AppState, theme: &Theme) { ( " Chat — detecting… ", vec![Line::from(Span::styled( - "Probing for a local engine (Lemonade :13305 / vLLM :8000)…", + "Probing for a local engine (Lemonade :13305 / vLLM :8000 / rocm serve :11435)…", Style::default().fg(theme.fg), ))], ) @@ -209,7 +209,7 @@ fn consent_gate_lines<'a>( Style::default().fg(theme.muted), )), Line::from(Span::styled( - " • or run a local endpoint at 127.0.0.1:8000", + " • or run a local endpoint (vLLM :8000, rocm serve :11435)", Style::default().fg(theme.muted), )), ],