use futures_util::{StreamExt, stream};
use replysieve::{Checker, Comment, Level, Verdict};
use rmcp::handler::server::wrapper::{Json, Parameters};
use rmcp::{ErrorData, ServiceExt, tool, tool_router};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
const CONCURRENCY: usize = 8;
#[derive(Deserialize, JsonSchema)]
struct CheckComment {
text: String,
title: Option<String>,
description: Option<String>,
parent: Option<String>,
level: Option<Level>,
#[serde(default)]
recent: Vec<String>,
}
#[derive(Deserialize, JsonSchema)]
struct CheckBatch {
comments: Vec<Comment>,
level: Option<Level>,
#[serde(default)]
recent: Vec<String>,
}
#[derive(Serialize, JsonSchema)]
struct Verdicts {
verdicts: Vec<Verdict>,
}
struct Server {
checker: Checker,
}
impl Server {
fn at(&self, level: Option<Level>) -> Checker {
match level {
Some(l) => self.checker.clone().at_level(l),
None => self.checker.clone(),
}
}
}
fn failed(e: replysieve::Error) -> ErrorData {
ErrorData::invalid_request(e.to_string(), None)
}
#[tool_router(server_handler)]
impl Server {
#[tool(
name = "check_comment",
description = "Judge one comment: approve, reject or needs_review, with a reason, confidence, level and model."
)]
async fn check_comment(
&self,
Parameters(p): Parameters<CheckComment>,
) -> Result<Json<Verdict>, ErrorData> {
let comment = Comment {
text: p.text,
title: p.title,
description: p.description,
parent: p.parent,
};
let v = self
.at(p.level)
.check(&comment, &p.recent)
.await
.map_err(failed)?;
Ok(Json(v))
}
#[tool(
name = "check_batch",
description = "Judge many comments at once. Returns one verdict per comment, in input order."
)]
async fn check_batch(
&self,
Parameters(p): Parameters<CheckBatch>,
) -> Result<Json<Verdicts>, ErrorData> {
let checker = self.at(p.level);
let recent = std::sync::Arc::new(p.recent);
let results: Vec<_> = stream::iter(p.comments.into_iter().map(|c| {
let (checker, recent) = (checker.clone(), recent.clone());
async move { checker.check(&c, &recent).await }
}))
.buffered(CONCURRENCY)
.collect()
.await;
let verdicts = results
.into_iter()
.collect::<Result<_, _>>()
.map_err(failed)?;
Ok(Json(Verdicts { verdicts }))
}
}
pub async fn serve(checker: Checker) -> anyhow::Result<()> {
let running = Server { checker }.serve(rmcp::transport::stdio()).await?;
running.waiting().await?;
Ok(())
}