1use serde_json::Value;
5use yapi_types::message::{ContentBlock, ImageContent};
6
7fn image(data: &str, mime_type: &str) -> ContentBlock {
9 ContentBlock::Image(ImageContent {
10 data: data.to_owned(),
11 mime_type: mime_type.to_owned(),
12 })
13}
14
15fn field<'a>(value: &'a Value, key: &str) -> &'a str {
16 value.get(key).and_then(Value::as_str).unwrap_or_default()
17}
18
19pub fn block_to_llm(block: &Value) -> ContentBlock {
23 match field(block, "type") {
24 "text" => ContentBlock::text(field(block, "text")),
25 "image" => image(field(block, "data"), field(block, "mimeType")),
26 "audio" => ContentBlock::text(format!("[audio {} omitted]", field(block, "mimeType"))),
27 "resource_link" => {
28 ContentBlock::text(format!("{}: {}", field(block, "name"), field(block, "uri")))
29 }
30 "resource" => {
31 let resource = &block["resource"];
32 if let Some(content) = resource.get("text").and_then(Value::as_str) {
33 return ContentBlock::text(content);
34 }
35 let mime_type = resource.get("mimeType").and_then(Value::as_str);
36 match mime_type {
37 Some(mime) if mime.starts_with("image/") => image(field(resource, "blob"), mime),
38 _ => ContentBlock::text(format!(
39 "[binary resource {} ({}) omitted]",
40 field(resource, "uri"),
41 mime_type.unwrap_or("unknown type")
42 )),
43 }
44 }
45 other => ContentBlock::text(format!("[unsupported MCP content {other}]")),
46 }
47}
48
49pub fn to_llm_content(result: &Value) -> Vec<ContentBlock> {
52 let mut content: Vec<ContentBlock> = result
53 .get("content")
54 .and_then(Value::as_array)
55 .map(|blocks| blocks.iter().map(block_to_llm).collect())
56 .unwrap_or_default();
57 if content.is_empty()
58 && let Some(structured) = result.get("structuredContent")
59 && let Ok(json) = yapi_types::json::to_string_pretty(structured, " ")
60 {
61 content.push(ContentBlock::text(json));
62 }
63 content
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use serde_json::json;
70
71 #[test]
72 fn converts_like_pi() {
73 let result = json!({"content": [
74 {"type": "text", "text": "a"},
75 {"type": "audio", "data": "x", "mimeType": "audio/wav"},
76 {"type": "resource_link", "uri": "u://x", "name": "x"},
77 {"type": "resource", "resource": {"uri": "u://b", "blob": "AA==", "mimeType": "application/zip"}},
78 {"type": "resource", "resource": {"uri": "u://p", "blob": "AA==", "mimeType": "image/png"}},
79 {"type": "widget"},
80 ]});
81 assert_eq!(
82 to_llm_content(&result),
83 vec![
84 ContentBlock::text("a"),
85 ContentBlock::text("[audio audio/wav omitted]"),
86 ContentBlock::text("x: u://x"),
87 ContentBlock::text("[binary resource u://b (application/zip) omitted]"),
88 image("AA==", "image/png"),
89 ContentBlock::text("[unsupported MCP content widget]"),
90 ]
91 );
92 assert_eq!(
93 to_llm_content(&json!({"content": [], "structuredContent": {"sum": 3}})),
94 vec![ContentBlock::text("{\n \"sum\": 3\n}")]
95 );
96 }
97}