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