1use crate::llm::actions::protocol_trait::Protocol;
10
11pub fn render_all_protocol_docs() -> String {
13 let mut out = String::new();
14 out.push_str("# NetGet protocol documentation\n\n");
15 out.push_str(
16 "Every protocol compiled into this binary, as the model sees it. Names in \
17 parentheses are keywords you can use in a prompt.\n\n",
18 );
19
20 let server_registry = crate::protocol::server_registry::registry();
22 let mut servers = server_registry.all_protocols();
23 servers.sort_by(|a, b| a.0.cmp(&b.0));
24 out.push_str(&format!("## Server protocols ({})\n\n", servers.len()));
25 for (name, p) in &servers {
26 out.push_str(&render_protocol_doc(name, p.as_ref()));
27 }
28
29 let mut client_names = crate::protocol::CLIENT_REGISTRY.list_protocols();
31 client_names.sort();
32 out.push_str(&format!("## Client protocols ({})\n\n", client_names.len()));
33 for name in &client_names {
34 if let Some(p) = crate::protocol::CLIENT_REGISTRY.get(name) {
35 out.push_str(&render_protocol_doc(name, p.as_ref()));
36 }
37 }
38
39 out
40}
41
42fn render_protocol_doc<P: Protocol + ?Sized>(name: &str, p: &P) -> String {
43 let mut s = String::new();
44 let meta = p.metadata();
45
46 s.push_str(&format!("### {}\n", name));
47 s.push_str(&format!("- Stack: `{}`\n", p.stack_name()));
48 s.push_str(&format!("- Maturity: {:?}\n", meta.state));
49 s.push_str(&format!("- Privilege: {:?}\n", meta.privilege_requirement));
50 let desc = p.description();
51 if !desc.is_empty() {
52 s.push_str(&format!("- Description: {}\n", desc));
53 }
54 let keywords = p.keywords();
55 if !keywords.is_empty() {
56 s.push_str(&format!("- Keywords: {}\n", keywords.join(", ")));
57 }
58 if let Some(notes) = meta.notes {
59 s.push_str(&format!("- Notes: {}\n", notes));
60 }
61
62 let params = p.get_startup_parameters();
64 if !params.is_empty() {
65 s.push_str("\n**Startup parameters:**\n");
66 for param in ¶ms {
67 let req = if param.required {
68 "required"
69 } else {
70 "optional"
71 };
72 s.push_str(&format!(
73 "- `{}` ({}, {}): {}\n",
74 param.name, param.type_hint, req, param.description
75 ));
76 }
77 }
78
79 let events = p.get_event_types();
81 if !events.is_empty() {
82 s.push_str("\n**Events:**\n");
83 for ev in &events {
84 s.push_str(&format!("- `{}` — {}\n", ev.id, ev.description));
85 for action in &ev.actions {
86 s.push_str(&format!(
87 " - action `{}`: {}\n",
88 action.name, action.description
89 ));
90 }
91 }
92 }
93
94 let sync_actions = p.get_sync_actions();
96 if !sync_actions.is_empty() {
97 s.push_str("\n**Actions:**\n");
98 for action in &sync_actions {
99 s.push_str(&format!("- `{}`: {}\n", action.name, action.description));
100 for param in &action.parameters {
101 let req = if param.required {
102 "required"
103 } else {
104 "optional"
105 };
106 s.push_str(&format!(
107 " - `{}` ({}, {}): {}\n",
108 param.name, param.type_hint, req, param.description
109 ));
110 }
111 }
112 }
113
114 let examples = p.get_startup_examples().to_prompt_text();
116 if !examples.trim().is_empty() {
117 s.push_str("\n**Examples:**\n");
118 s.push_str(&examples);
119 s.push('\n');
120 }
121
122 s.push('\n');
123 s
124}