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 let state_guard = state.read().await;
28
29 for &provider in ALL_PROVIDERS {
30 let binary_name = provider.binary_name();
31 let env_key = provider.env_override_key();
32 let env_override = env::var(env_key).ok();
33
34 if resolve_binary(binary_name, env_override.as_deref()).is_err() {
35 debug!(provider = %provider, "Binary not found, skipping");
36 continue;
37 }
38
39 match state_guard.get_runner(provider).await {
40 Ok(runner) => {
41 let provider_name = runner.name();
42 let models = runner.available_models();
43
44 if models.is_empty() {
45 data.push(ModelObject {
47 id: provider_name.to_owned(),
48 object: "model",
49 owned_by: provider_name.to_owned(),
50 });
51 } else {
52 for model in models {
53 data.push(ModelObject {
54 id: format!("{provider_name}:{model}"),
55 object: "model",
56 owned_by: provider_name.to_owned(),
57 });
58 }
59 }
60 }
61 Err(e) => {
62 debug!(provider = %provider, error = %e, "Failed to create runner");
63 }
64 }
65 }
66
67 let resp = ModelsResponse {
68 object: "list",
69 data,
70 };
71
72 (StatusCode::OK, Json(resp))
73}