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