use rmcp::schemars;
#[cfg_attr(alef, alef(skip))]
#[derive(Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
pub struct ExtractParams {
#[schemars(schema_with = "extract_input_schema")]
pub input: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub config: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pdf_password: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_format: Option<String>,
}
#[cfg_attr(alef, alef(skip))]
#[derive(Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
pub struct ExtractBatchParams {
#[schemars(schema_with = "extract_inputs_schema")]
pub inputs: Vec<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub config: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pdf_password: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_format: Option<String>,
}
#[cfg_attr(alef, alef(skip))]
#[derive(Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
pub struct DetectMimeTypeParams {
pub path: String,
#[serde(default = "default_use_content")]
pub use_content: bool,
}
fn default_use_content() -> bool {
true
}
#[derive(Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
pub struct EmptyParams {}
#[cfg_attr(alef, alef(skip))]
#[derive(Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
pub struct CacheWarmParams {
#[serde(default)]
pub all_embeddings: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub embedding_model: Option<String>,
#[serde(default)]
pub ner: bool,
#[serde(default)]
pub all_ner_models: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub ner_model: Option<String>,
}
fn extract_input_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"type": "object",
"required": ["kind"],
"additionalProperties": false,
"properties": {
"kind": {
"type": "string",
"enum": ["bytes", "uri"],
"description": "Input source kind."
},
"bytes": {
"type": "array",
"items": { "type": "integer", "minimum": 0, "maximum": 255 },
"description": "Raw bytes for kind=bytes."
},
"uri": {
"type": "string",
"description": "Local path, file:// URI, or HTTP(S) URL for kind=uri."
},
"mime_type": {
"type": "string",
"description": "Optional MIME type hint."
},
"filename": {
"type": "string",
"description": "Optional filename hint for bytes inputs."
},
"config": {
"type": "object",
"description": "Optional per-input extraction overrides."
}
}
})
}
fn extract_inputs_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
let input_schema = extract_input_schema(generator);
schemars::json_schema!({
"type": "array",
"items": input_schema
})
}
#[allow(dead_code)]
#[derive(Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
pub struct DownloadGrammarsParams {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub languages: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub groups: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub all: Option<bool>,
}
#[allow(dead_code)]
#[derive(Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
pub struct ListGrammarsParams {
#[serde(default)]
pub downloaded_only: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub filter: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_extract_params_defaults() {
let json = r#"{"input": {"kind": "uri", "uri": "/test.pdf"}}"#;
let params: ExtractParams = serde_json::from_str(json).unwrap();
assert_eq!(params.input["uri"], "/test.pdf");
assert_eq!(params.config, None);
}
#[test]
fn test_extract_params_accepts_bytes_input() {
let json = r#"{"input": {"kind": "bytes", "bytes": [72, 105], "mime_type": "text/plain"}}"#;
let params: ExtractParams = serde_json::from_str(json).unwrap();
assert_eq!(params.input["kind"], "bytes");
assert_eq!(params.config, None);
}
#[test]
fn test_extract_batch_params_defaults() {
let json = r#"{"inputs": [{"kind": "uri", "uri": "/a.pdf"}, {"kind": "uri", "uri": "/b.pdf"}]}"#;
let params: ExtractBatchParams = serde_json::from_str(json).unwrap();
assert_eq!(params.inputs.len(), 2);
assert_eq!(params.config, None);
}
#[test]
fn test_detect_mime_type_params_defaults() {
let json = r#"{"path": "/test.pdf"}"#;
let params: DetectMimeTypeParams = serde_json::from_str(json).unwrap();
assert_eq!(params.path, "/test.pdf");
assert!(params.use_content);
}
#[test]
fn test_detect_mime_type_params_use_content_false() {
let json = r#"{"path": "/test.pdf", "use_content": false}"#;
let params: DetectMimeTypeParams = serde_json::from_str(json).unwrap();
assert!(!params.use_content);
}
#[test]
fn test_extract_params_with_config() {
let json = r#"{"input": {"kind": "uri", "uri": "/test.pdf"}, "config": {"use_cache": false}}"#;
let params: ExtractParams = serde_json::from_str(json).unwrap();
assert_eq!(params.input["uri"], "/test.pdf");
assert!(params.config.is_some());
}
#[test]
fn test_extract_params_serialization() {
let params = ExtractParams {
input: serde_json::json!({
"kind": "uri",
"uri": "/test.pdf",
"mime_type": "application/pdf"
}),
config: Some(serde_json::json!({"use_cache": false})),
pdf_password: None,
response_format: None,
};
let json = serde_json::to_string(¶ms).unwrap();
let deserialized: ExtractParams = serde_json::from_str(&json).unwrap();
assert_eq!(params.input, deserialized.input);
assert_eq!(params.config, deserialized.config);
}
#[test]
fn test_extract_batch_params_serialization() {
let params = ExtractBatchParams {
inputs: vec![
serde_json::json!({"kind": "uri", "uri": "/a.pdf"}),
serde_json::json!({"kind": "uri", "uri": "/b.pdf"}),
],
config: Some(serde_json::json!({"use_cache": true})),
pdf_password: None,
response_format: None,
};
let json = serde_json::to_string(¶ms).unwrap();
let deserialized: ExtractBatchParams = serde_json::from_str(&json).unwrap();
assert_eq!(params.inputs, deserialized.inputs);
assert_eq!(params.config, deserialized.config);
}
#[test]
fn test_detect_mime_type_params_serialization() {
let params = DetectMimeTypeParams {
path: "/test.pdf".to_string(),
use_content: false,
};
let json = serde_json::to_string(¶ms).unwrap();
let deserialized: DetectMimeTypeParams = serde_json::from_str(&json).unwrap();
assert_eq!(params.path, deserialized.path);
assert_eq!(params.use_content, deserialized.use_content);
}
#[test]
fn test_empty_params_schema_has_type_object() {
let schema = schemars::schema_for!(EmptyParams);
let json = serde_json::to_value(&schema).unwrap();
assert_eq!(json["type"], "object");
}
#[test]
fn test_empty_params_deserializes_from_empty_object() {
let params: EmptyParams = serde_json::from_str("{}").unwrap();
let _ = params;
}
#[test]
fn test_cache_warm_params_defaults() {
let json = r#"{}"#;
let params: CacheWarmParams = serde_json::from_str(json).unwrap();
assert!(!params.all_embeddings);
assert!(params.embedding_model.is_none());
assert!(!params.ner);
assert!(!params.all_ner_models);
assert!(params.ner_model.is_none());
}
#[test]
fn test_cache_warm_params_with_values() {
let json = r#"{
"all_embeddings": true,
"embedding_model": "balanced",
"ner": true,
"all_ner_models": true,
"ner_model": "gliner_small-v2.5"
}"#;
let params: CacheWarmParams = serde_json::from_str(json).unwrap();
assert!(params.all_embeddings);
assert_eq!(params.embedding_model.as_deref(), Some("balanced"));
assert!(params.ner);
assert!(params.all_ner_models);
assert_eq!(params.ner_model.as_deref(), Some("gliner_small-v2.5"));
}
}