| 64 | } |
| 65 | |
| 66 | pub(crate) async fn start<M: Model>(master: Master<M>) -> anyhow::Result<()> { |
| 67 | let address = master.ctx.args.api.as_ref().unwrap().to_string(); |
| 68 | |
| 69 | log::info!("starting api on http://{} ...", &address); |
| 70 | |
| 71 | let state = Arc::new(RwLock::new(master)); |
| 72 | |
| 73 | HttpServer::new( |
| 74 | move || { |
| 75 | App::new() |
| 76 | .app_data(web::Data::new(state.clone())) |
| 77 | // Text / Chat |
| 78 | .route( |
| 79 | "/v1/chat/completions", |
| 80 | web::post().to(generate_text::<M>), |
| 81 | ) |
| 82 | .route( |
| 83 | "/api/v1/chat/completions", |
| 84 | web::post().to(generate_text::<M>), |
| 85 | ) |
| 86 | // Models |
| 87 | .route("/v1/models", web::get().to(list_models::<M>)) |
| 88 | // Audio / TTS |
| 89 | .route( |
| 90 | "/v1/audio/speech", |
| 91 | web::post().to(generate_speech::<M>), |
| 92 | ) |
| 93 | // Image (OpenAI-compatible) |
| 94 | .route( |
| 95 | "/v1/images/generations", |
| 96 | web::post().to(generate_image_openai::<M>), |
| 97 | ) |
| 98 | // Image (legacy) |
| 99 | .route( |
| 100 | "/api/v1/image", |
| 101 | web::post().to(generate_image::<M>), |
| 102 | ) |
| 103 | // Topology & UI |
| 104 | .route( |
| 105 | "/api/v1/topology", |
| 106 | web::get().to(ui::topology::<M>), |
| 107 | ) |
| 108 | .route("/", web::get().to(ui::index::<M>)) |
| 109 | .default_service(web::route().to(not_found)) |
| 110 | }, //.wrap(actix_web::middleware::Logger::default())) |
| 111 | ) |
| 112 | .bind(&address) |
| 113 | .map_err(|e| anyhow!(e))? |
| 114 | .run() |
| 115 | .await |
| 116 | .map_err(|e| anyhow!(e)) |
| 117 | } |
| 118 | |
| 119 | #[cfg(test)] |
| 120 | mod tests { |