1use std::env;
8
9use axum::extract::State;
10use axum::http::StatusCode;
11use axum::response::IntoResponse;
12use axum::Json;
13use embacle::discovery::resolve_binary;
14use tracing::debug;
15
16use crate::openai_types::{ModelObject, ModelsResponse};
17use crate::runner::ALL_PROVIDERS;
18use crate::state::SharedState;
19
20pub async fn handle(State(state): State<SharedState>) -> impl IntoResponse {
26 let mut data = Vec::new();
27
28 for &provider in ALL_PROVIDERS {
29 let binary_name = provider.binary_name();
30 let env_key = provider.env_override_key();
31 let env_override = env::var(env_key).ok();
32
33 if resolve_binary(binary_name, env_override.as_deref()).is_err() {
34 debug!(provider = %provider, "Binary not found, skipping");
35 continue;
36 }
37
38 match state.get_runner(provider).await {
39 Ok(runner) => {
40 let provider_name = runner.name();
41 let models = runner.available_models();
42
43 if models.is_empty() {
44 data.push(ModelObject {
46 id: provider_name.to_owned(),
47 object: "model",
48 owned_by: provider_name.to_owned(),
49 });
50 } else {
51 for model in models {
52 data.push(ModelObject {
53 id: format!("{provider_name}:{model}"),
54 object: "model",
55 owned_by: provider_name.to_owned(),
56 });
57 }
58 }
59 }
60 Err(e) => {
61 debug!(provider = %provider, error = %e, "Failed to create runner");
62 }
63 }
64 }
65
66 let resp = ModelsResponse {
67 object: "list",
68 data,
69 };
70
71 (StatusCode::OK, Json(resp))
72}