greentic-flow-builder 0.4.0

Greentic Flow Builder — orchestrator that powers Adaptive Card design via the adaptive-card-mcp toolkit
Documentation
//! Knowledge base browse endpoints:
//!   GET /api/examples              — list (filterable by category)
//!   GET /api/examples/:id          — fetch one entry
//!   GET /api/examples/suggest?q=   — keyword search

use crate::ui::state::AppState;
use axum::Json;
use axum::extract::{Path, Query, State};
use axum::http::StatusCode;
use axum::response::IntoResponse;
use serde::Deserialize;
use serde_json::json;
use std::sync::Arc;

#[derive(Deserialize)]
pub struct ListQuery {
    #[serde(default)]
    pub category: Option<String>,
    #[serde(default)]
    pub limit: Option<usize>,
}

pub async fn list_examples(
    State(state): State<Arc<AppState>>,
    Query(q): Query<ListQuery>,
) -> impl IntoResponse {
    let limit = q.limit.unwrap_or(20);
    let entries: Vec<_> = state
        .knowledge_base
        .all()
        .iter()
        .filter(|e| q.category.as_deref().is_none_or(|c| e.category == c))
        .take(limit)
        .map(|e| {
            json!({
                "id": e.id,
                "title": e.title,
                "description": e.description,
                "category": e.category,
                "tags": e.tags,
                "complexity": format!("{:?}", e.complexity).to_lowercase(),
            })
        })
        .collect();
    (StatusCode::OK, Json(json!({ "examples": entries })))
}

pub async fn get_example(
    State(state): State<Arc<AppState>>,
    Path(id): Path<String>,
) -> impl IntoResponse {
    match state.knowledge_base.by_id(&id) {
        Some(entry) => (StatusCode::OK, Json(json!(entry))).into_response(),
        None => (
            StatusCode::NOT_FOUND,
            Json(json!({"error": format!("knowledge entry not found: {id}")})),
        )
            .into_response(),
    }
}

#[derive(Deserialize)]
pub struct SuggestQuery {
    pub q: String,
    #[serde(default)]
    pub limit: Option<usize>,
}

pub async fn suggest(
    State(state): State<Arc<AppState>>,
    Query(q): Query<SuggestQuery>,
) -> impl IntoResponse {
    let limit = q.limit.unwrap_or(5);
    let results = state.knowledge_base.suggest(&q.q, limit);
    (StatusCode::OK, Json(json!({ "results": results })))
}