loadmaster_probe/
detect.rs1use serde::Serialize;
4use serde_json::Value;
5
6use crate::client::Client;
7
8#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
9pub struct Detected {
10 pub runtime: String,
12 pub version: Option<String>,
13 #[serde(skip)]
15 pub chat_template: Option<String>,
16 pub models: Vec<String>,
17}
18
19fn str_of(v: &Value, k: &str) -> Option<String> {
20 v.get(k).and_then(Value::as_str).map(str::to_owned)
21}
22
23pub async fn detect(c: &Client) -> Detected {
27 let models_body = c.get("/v1/models").await;
28 let models: Vec<String> = models_body
29 .as_ref()
30 .and_then(|(_, _, b)| b["data"].as_array().cloned())
31 .unwrap_or_default()
32 .iter()
33 .filter_map(|m| str_of(m, "id"))
34 .collect();
35 let found = |runtime: &str, version: Option<String>, chat_template: Option<String>| Detected {
36 runtime: runtime.into(),
37 version,
38 chat_template,
39 models: models.clone(),
40 };
41 let ok = |r: &Option<(u16, reqwest::header::HeaderMap, Value)>| -> Option<Value> {
42 r.as_ref()
43 .filter(|(s, _, b)| *s == 200 && b.is_object())
44 .map(|(_, _, b)| b.clone())
45 };
46
47 if let Some(b) = ok(&c.get("/props").await)
48 && (b.get("chat_template").is_some()
49 || b.get("build_info").is_some()
50 || b.get("default_generation_settings").is_some())
51 {
52 return found(
53 "llama.cpp",
54 str_of(&b, "build_info"),
55 str_of(&b, "chat_template"),
56 );
57 }
58 if let Some(b) = ok(&c.get("/get_model_info").await)
59 && b.get("model_path").is_some()
60 {
61 return found("sglang", str_of(&b, "version"), None);
62 }
63 if let Some(b) = ok(&c.get("/api/v0/models").await)
64 && b.get("data").is_some()
65 {
66 return found("lmstudio", None, None);
67 }
68 if let Some(b) = ok(&c.get("/api/version").await)
69 && b.get("version").is_some()
70 {
71 return found("ollama", str_of(&b, "version"), None);
72 }
73 if let Some(b) = ok(&c.get("/v1/model").await)
74 && (b["owned_by"] == "tabbyAPI" || b.get("parameters").is_some())
75 {
76 return found("tabbyapi", None, None);
77 }
78 if let Some(b) = ok(&c.get("/version").await)
79 && let Some(v) = str_of(&b, "version")
80 {
81 return found("vllm", Some(v), None);
82 }
83 let owned = models_body
84 .as_ref()
85 .and_then(|(_, _, b)| b["data"][0]["owned_by"].as_str().map(str::to_owned))
86 .unwrap_or_default();
87 let runtime = match owned.as_str() {
88 "llamacpp" => "llama.cpp",
89 "vllm" => "vllm",
90 "library" => "ollama",
91 "sglang" => "sglang",
92 "tabbyAPI" => "tabbyapi",
93 _ => "unknown",
94 };
95 found(runtime, None, None)
96}