summaryrefslogtreecommitdiff
path: root/makima/src/server/handlers/templates.rs
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-11 05:52:14 +0000
committersoryu <soryu@soryu.co>2026-01-15 00:21:16 +0000
commit87044a747b47bd83249d61a45842c7f7b2eae56d (patch)
treeef2000ce79ffcc2723ef841acef5aa1deb1d5378 /makima/src/server/handlers/templates.rs
parent077820c4167c168072d217a1b01df840463a12a8 (diff)
downloadsoryu-87044a747b47bd83249d61a45842c7f7b2eae56d.tar.gz
soryu-87044a747b47bd83249d61a45842c7f7b2eae56d.zip
Contract system
Diffstat (limited to 'makima/src/server/handlers/templates.rs')
-rw-r--r--makima/src/server/handlers/templates.rs107
1 files changed, 107 insertions, 0 deletions
diff --git a/makima/src/server/handlers/templates.rs b/makima/src/server/handlers/templates.rs
new file mode 100644
index 0000000..868d5b4
--- /dev/null
+++ b/makima/src/server/handlers/templates.rs
@@ -0,0 +1,107 @@
+//! 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<String>,
+}
+
+/// 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<TemplateSummary>,
+}
+
+/// List available file templates
+#[utoipa::path(
+ get,
+ path = "/api/v1/templates",
+ params(
+ ("phase" = Option<String>, 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<ListTemplatesQuery>,
+) -> impl IntoResponse {
+ let template_list = match query.phase.as_deref() {
+ Some(phase) => templates::templates_for_phase(phase),
+ None => templates::all_templates(),
+ };
+
+ let summaries: Vec<TemplateSummary> = 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<String>,
+) -> 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(),
+ }
+}