//! Templates API handler. use axum::{extract::Query, http::StatusCode, response::IntoResponse, Json}; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; use crate::llm::templates; /// Query parameters for listing templates #[derive(Debug, Deserialize, ToSchema)] pub struct ListTemplatesQuery { /// Filter by contract phase (research, specify, plan, execute, review) pub phase: Option, } /// Template summary for API response #[derive(Debug, Serialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct TemplateSummary { /// Template identifier pub id: String, /// Display name pub name: String, /// Contract phase this template is designed for pub phase: String, /// Brief description pub description: String, /// Number of body elements in the template pub element_count: usize, } /// Response for listing templates #[derive(Debug, Serialize, ToSchema)] pub struct ListTemplatesResponse { pub templates: Vec, } /// List available file templates #[utoipa::path( get, path = "/api/v1/templates", params( ("phase" = Option, Query, description = "Filter by contract phase") ), responses( (status = 200, description = "Templates retrieved successfully", body = ListTemplatesResponse) ), tag = "templates" )] pub async fn list_templates( Query(query): Query, ) -> impl IntoResponse { let template_list = match query.phase.as_deref() { Some(phase) => templates::templates_for_phase(phase), None => templates::all_templates(), }; let summaries: Vec = template_list .iter() .map(|t| TemplateSummary { id: t.id.clone(), name: t.name.clone(), phase: t.phase.clone(), description: t.description.clone(), element_count: t.suggested_body.len(), }) .collect(); ( StatusCode::OK, Json(ListTemplatesResponse { templates: summaries, }), ) .into_response() } /// Get a specific template by ID #[utoipa::path( get, path = "/api/v1/templates/{id}", params( ("id" = String, Path, description = "Template ID") ), responses( (status = 200, description = "Template retrieved successfully", body = templates::FileTemplate), (status = 404, description = "Template not found") ), tag = "templates" )] pub async fn get_template( axum::extract::Path(id): axum::extract::Path, ) -> impl IntoResponse { let all = templates::all_templates(); let template = all.into_iter().find(|t| t.id == id); match template { Some(t) => (StatusCode::OK, Json(serde_json::json!(t))).into_response(), None => ( StatusCode::NOT_FOUND, Json(serde_json::json!({ "error": format!("Template '{}' not found", id) })), ) .into_response(), } }