@@ -6,6 +6,9 @@ pub enum OllamaError {
|
||||
#[error("prompt is required and cannot be empty")]
|
||||
MissingPrompt,
|
||||
|
||||
#[error("model is required and cannot be empty")]
|
||||
MissingModel,
|
||||
|
||||
#[error("messages must be a non-empty array containing at least one user message")]
|
||||
MissingMessages,
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
mod auth;
|
||||
mod errors;
|
||||
mod openapi;
|
||||
mod providers;
|
||||
mod routes;
|
||||
mod state;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
use utoipa::OpenApi;
|
||||
|
||||
#[derive(OpenApi)]
|
||||
#[openapi(
|
||||
paths(
|
||||
crate::routes::v1::chat::completions
|
||||
),
|
||||
components(
|
||||
schemas(
|
||||
// add your request/response structs here later
|
||||
)
|
||||
),
|
||||
tags(
|
||||
(name = "chat", description = "Chat endpoints"),
|
||||
(name = "models", description = "Model management")
|
||||
)
|
||||
)]
|
||||
pub struct V1ApiDoc;
|
||||
@@ -82,7 +82,7 @@ impl OllamaProvider {
|
||||
let model = body
|
||||
.get("model")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("llama3");
|
||||
.ok_or(OllamaError::MissingModel)?;
|
||||
|
||||
Ok((prompt, model))
|
||||
}
|
||||
|
||||
@@ -11,6 +11,13 @@ use serde_json::Value;
|
||||
use crate::errors::OllamaError;
|
||||
use crate::state::app_state::AppState;
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/chat/completions",
|
||||
responses(
|
||||
(status = 200, description = "Chat completion", body = Value),
|
||||
)
|
||||
)]
|
||||
pub async fn completions(
|
||||
State(state): State<AppState>,
|
||||
Json(body): Json<Value>,
|
||||
@@ -73,6 +80,10 @@ fn ollama_err(e: OllamaError) -> (axum::http::StatusCode, String) {
|
||||
axum::http::StatusCode::BAD_REQUEST,
|
||||
"prompt is required and cannot be empty".to_string(),
|
||||
),
|
||||
OllamaError::MissingModel => (
|
||||
axum::http::StatusCode::BAD_REQUEST,
|
||||
"model is required and cannot be empty".to_string(),
|
||||
),
|
||||
OllamaError::ModelNotFound(m) => (
|
||||
axum::http::StatusCode::UNPROCESSABLE_ENTITY,
|
||||
format!("model '{m}' is not available — run `ollama pull {m}` first"),
|
||||
|
||||
+12
-2
@@ -1,16 +1,26 @@
|
||||
pub mod chat;
|
||||
pub mod models;
|
||||
mod openapi;
|
||||
|
||||
use crate::auth::middleware::auth_middleware;
|
||||
use crate::state::app_state::AppState;
|
||||
use axum::{Router, middleware, routing::get, routing::post};
|
||||
|
||||
pub fn router() -> Router<AppState> {
|
||||
fn public_router() -> Router<AppState> {
|
||||
Router::new().route("/openapi.json", get(openapi::openapi_json))
|
||||
}
|
||||
|
||||
pub fn protected_router() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/models", get(models::list_models))
|
||||
.route("/completions", post(chat::completions))
|
||||
.route("/chat/completions", post(chat::chat_completions))
|
||||
.route("/models/{model}/load", post(models::load_model))
|
||||
.route("/models/{model}/unload", post(models::unload_model))
|
||||
.layer(middleware::from_fn(auth_middleware))
|
||||
}
|
||||
|
||||
pub fn router() -> Router<AppState> {
|
||||
Router::new()
|
||||
.merge(public_router())
|
||||
.merge(protected_router().layer(middleware::from_fn(auth_middleware)))
|
||||
}
|
||||
|
||||
@@ -53,6 +53,10 @@ fn ollama_err(e: OllamaError) -> (axum::http::StatusCode, String) {
|
||||
axum::http::StatusCode::NOT_FOUND,
|
||||
format!("model '{m}' not found — run `ollama pull {m}`"),
|
||||
),
|
||||
OllamaError::MissingModel => (
|
||||
axum::http::StatusCode::BAD_REQUEST,
|
||||
"model is required and cannot be empty".to_string(),
|
||||
),
|
||||
OllamaError::MissingPrompt => (
|
||||
axum::http::StatusCode::BAD_REQUEST,
|
||||
"prompt is required".to_string(),
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
use axum::Json;
|
||||
use utoipa::OpenApi;
|
||||
|
||||
use crate::openapi::V1ApiDoc;
|
||||
|
||||
pub async fn openapi_json() -> Json<utoipa::openapi::OpenApi> {
|
||||
Json(V1ApiDoc::openapi())
|
||||
}
|
||||
Reference in New Issue
Block a user