mofa_plugins/tools/
medical_knowledge.rs1use super::*;
2use serde::{Deserialize, Serialize};
3use serde_json::json;
4use std::fs;
5use std::path::PathBuf;
6use std::sync::{Arc, RwLock};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct DiseaseDiagnosis {
11 pub disease_name: String,
13 pub criteria: Vec<String>,
15 pub update_date: String,
17 pub source: String,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct TreatmentPlan {
24 pub disease_name: String,
26 pub plan: Vec<String>,
28 pub update_date: String,
30 pub source: String,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize, Default)]
36pub struct MedicalKnowledge {
37 pub diagnoses: Vec<DiseaseDiagnosis>,
39 pub treatments: Vec<TreatmentPlan>,
41}
42
43pub struct MedicalKnowledgeTool {
45 definition: ToolDefinition,
46 knowledge: Arc<RwLock<MedicalKnowledge>>,
47 knowledge_path: Arc<RwLock<PathBuf>>,
48}
49
50impl Default for MedicalKnowledgeTool {
51 fn default() -> Self {
52 Self::new()
53 }
54}
55
56impl MedicalKnowledgeTool {
57 pub fn new() -> Self {
58 let tool_def = ToolDefinition {
59 name: "medical_knowledge".to_string(),
60 description: "Medical diagnosis knowledge management: dynamically injects and updates the latest disease diagnosis standards and treatment plans at runtime.".to_string(),
61 parameters: json!({
62 "type": "object",
63 "properties": {
64 "action": {
65 "type": "string",
66 "enum": ["inject_knowledge", "query_diagnosis", "query_treatment", "refresh_knowledge"],
67 "description": "Action to perform on medical knowledge"
68 },
69 "knowledge": {
70 "type": "object",
71 "description": "Medical knowledge data to inject (required for inject_knowledge)"
72 },
73 "disease": {
74 "type": "string",
75 "description": "Disease name (required for query_diagnosis, query_treatment)"
76 },
77 "file_path": {
78 "type": "string",
79 "description": "Path to JSON file containing medical knowledge (for inject_knowledge and refresh_knowledge)"
80 }
81 },
82 "required": ["action"]
83 }),
84 requires_confirmation: false,
85 };
86
87 Self {
88 definition: tool_def,
89 knowledge: Arc::new(RwLock::new(MedicalKnowledge::default())),
90 knowledge_path: Arc::new(RwLock::new(PathBuf::from("medical_knowledge.json"))),
91 }
92 }
93
94 async fn load_knowledge_from_file(&self, file_path: &str) -> PluginResult<MedicalKnowledge> {
96 let content = fs::read_to_string(file_path)?;
97 let knowledge: MedicalKnowledge = serde_json::from_str(&content)?;
98 Ok(knowledge)
99 }
100
101 async fn save_knowledge_to_file(&self, knowledge: &MedicalKnowledge) -> PluginResult<()> {
103 let content = serde_json::to_string_pretty(knowledge)?;
104 let path = self.knowledge_path.read().unwrap();
105 fs::write(&*path, content)?; Ok(())
107 }
108}
109
110#[async_trait::async_trait]
111impl ToolExecutor for MedicalKnowledgeTool {
112 fn definition(&self) -> &ToolDefinition {
113 &self.definition
114 }
115
116 async fn execute(&self, arguments: serde_json::Value) -> PluginResult<serde_json::Value> {
117 let action = arguments["action"]
118 .as_str()
119 .ok_or_else(|| anyhow::anyhow!("Action is required"))?;
120
121 match action {
122 "inject_knowledge" => {
124 let new_knowledge = if let Some(knowledge_json) = arguments.get("knowledge") {
125 serde_json::from_value(knowledge_json.clone())?
127 } else if let Some(file_path) = arguments["file_path"].as_str() {
128 self.load_knowledge_from_file(file_path).await?
130 } else {
131 return Err(anyhow::anyhow!(
132 "Either knowledge JSON or file_path must be provided for inject_knowledge"
133 ));
134 };
135
136 let diagnoses_count = new_knowledge.diagnoses.len();
138 let treatments_count = new_knowledge.treatments.len();
139
140 self.save_knowledge_to_file(&new_knowledge).await?;
142
143 let mut knowledge = self.knowledge.write().unwrap();
145 knowledge.diagnoses = new_knowledge.diagnoses;
146 knowledge.treatments = new_knowledge.treatments;
147
148 Ok(json!({
149 "success": true,
150 "message": format!("Injected medical knowledge successfully. {} diagnoses and {} treatments loaded.", diagnoses_count, treatments_count)
151 }))
152 }
153
154 "query_diagnosis" => {
156 let disease = arguments["disease"].as_str().ok_or_else(|| {
157 anyhow::anyhow!("Disease name is required for query_diagnosis")
158 })?;
159
160 let knowledge = self.knowledge.read().unwrap();
161
162 if let Some(diagnosis) = knowledge
163 .diagnoses
164 .iter()
165 .find(|d| d.disease_name.to_lowercase() == disease.to_lowercase())
166 {
167 Ok(serde_json::to_value(diagnosis)?)
168 } else {
169 Ok(json!({
170 "success": false,
171 "message": format!("No diagnosis information found for disease: {}", disease)
172 }))
173 }
174 }
175
176 "query_treatment" => {
178 let disease = arguments["disease"].as_str().ok_or_else(|| {
179 anyhow::anyhow!("Disease name is required for query_treatment")
180 })?;
181
182 let knowledge = self.knowledge.read().unwrap();
183
184 if let Some(treatment) = knowledge
185 .treatments
186 .iter()
187 .find(|t| t.disease_name.to_lowercase() == disease.to_lowercase())
188 {
189 Ok(serde_json::to_value(treatment)?)
190 } else {
191 Ok(json!({
192 "success": false,
193 "message": format!("No treatment information found for disease: {}", disease)
194 }))
195 }
196 }
197
198 "refresh_knowledge" => {
200 let file_path = if let Some(path) = arguments["file_path"].as_str() {
202 path.to_string()
203 } else {
204 let path = self.knowledge_path.read().unwrap();
206 path.to_str()
207 .unwrap_or("medical_knowledge.json")
208 .to_string()
209 };
210
211 let new_knowledge = self.load_knowledge_from_file(&file_path).await?;
212
213 let diagnoses_count = new_knowledge.diagnoses.len();
215 let treatments_count = new_knowledge.treatments.len();
216
217 let mut knowledge = self.knowledge.write().unwrap();
219 knowledge.diagnoses = new_knowledge.diagnoses;
220 knowledge.treatments = new_knowledge.treatments;
221
222 Ok(json!({
223 "success": true,
224 "message": format!("Refreshed medical knowledge successfully from {}. {} diagnoses and {} treatments loaded.", file_path, diagnoses_count, treatments_count)
225 }))
226 }
227
228 _ => Err(anyhow::anyhow!("Unsupported action: {}", action)),
229 }
230 }
231}