Skip to main content

netget/client/adc_peer/
actions.rs

1use crate::llm::actions::{
2    client_trait::{Client, ClientActionResult},
3    protocol_trait::Protocol,
4    ActionDefinition,
5};
6use crate::protocol::{ConnectContext, EventType};
7use crate::server::p2p_support::{action, parameter};
8use crate::state::AppState;
9use anyhow::Result;
10use serde_json::{json, Value};
11use std::{future::Future, net::SocketAddr, pin::Pin, sync::LazyLock};
12
13#[derive(Default)]
14pub struct AdcPeerClientProtocol;
15impl AdcPeerClientProtocol {
16    pub fn new() -> Self {
17        Self
18    }
19}
20pub fn requests() -> Vec<ActionDefinition> {
21    vec![
22        action(
23            "adc_peer_get",
24            "Download a file or file list",
25            vec![
26                parameter(
27                    "identifier",
28                    "string",
29                    "TTH/hash, files.xml.bz2 or relative file identifier",
30                    true,
31                ),
32                parameter(
33                    "offset",
34                    "number",
35                    "Starting byte offset within the requested file (zero based)",
36                    true,
37                ),
38                parameter(
39                    "length",
40                    "number",
41                    "Bytes to transfer (0..1048576) or -1 for remainder",
42                    true,
43                ),
44                parameter(
45                    "expected_tth",
46                    "string",
47                    "Optional whole-payload Tiger tree hash",
48                    false,
49                ),
50            ],
51            json!({"type":"adc_peer_get","identifier":"files.xml.bz2","offset":0,"length":-1}),
52        ),
53        action(
54            "disconnect",
55            "Close this peer",
56            vec![],
57            json!({"type":"disconnect"}),
58        ),
59    ]
60}
61impl Protocol for AdcPeerClientProtocol {
62    fn protocol_name(&self) -> &'static str {
63        "ADC Peer"
64    }
65    fn stack_name(&self) -> &'static str {
66        "ETH>IP>TCP>ADC Peer"
67    }
68    fn keywords(&self) -> Vec<&'static str> {
69        vec!["adc_peer", "adc peer"]
70    }
71    fn description(&self) -> &'static str {
72        "Selected ADC Peer file transfers connecting role"
73    }
74    fn example_prompt(&self) -> &'static str {
75        "Connect to ADC Peer on 127.0.0.1:1512"
76    }
77    fn group_name(&self) -> &'static str {
78        "Peer-to-peer"
79    }
80    fn get_startup_parameters(&self) -> Vec<crate::llm::actions::ParameterDefinition> {
81        {
82            let mut p = crate::server::p2p_support::tls_parameters(false);
83            for (name, description) in [
84                (
85                    "cid",
86                    "Own ADC CID matching the identity announced on the hub",
87                ),
88                ("token", "Rendezvous token from DCTM/DRCM"),
89            ] {
90                p.push(crate::llm::actions::ParameterDefinition {
91                    name: name.into(),
92                    description: description.into(),
93                    type_hint: "string".into(),
94                    required: false,
95                    example: json!(""),
96                    default: None,
97                });
98            }
99            p
100        }
101    }
102    fn get_startup_examples(&self) -> crate::llm::actions::StartupExamples {
103        let base = json!({"type":"open_client","protocol":"adc_peer","remote_addr":"127.0.0.1:1512","instruction":"Use selected ADC Peer operations"});
104        let mut fixed = base.clone();
105        fixed["event_handlers"] = json!([{"event_pattern":"adc_peer_connected","handler":{"type":"static","actions":[requests()[0].example]}}]);
106        let mut script = base.clone();
107        script["event_handlers"] = json!([{"event_pattern":"adc_peer_connected","handler":{"type":"script","language":"python","code":"import json\nprint(json.dumps({'actions':[{'type':'disconnect'}]}))"}}]);
108        crate::llm::actions::StartupExamples::new(base, script, fixed)
109    }
110    fn get_async_actions(&self, _: &AppState) -> Vec<ActionDefinition> {
111        requests()
112    }
113    fn get_sync_actions(&self) -> Vec<ActionDefinition> {
114        vec![]
115    }
116    fn get_event_types(&self) -> Vec<EventType> {
117        EVENTS.clone()
118    }
119    fn metadata(&self) -> crate::protocol::ProtocolMetadataV2 {
120        crate::protocol::ProtocolMetadataV2::builder().state(crate::protocol::metadata::DevelopmentState::Experimental).implementation("Native bounded ADC Peer selected codec; TCP and optional implicit TLS").llm_control("Handler-supplied file and file-list downloads, ranges and refusals").e2e_testing("Independent peer exchanges, malformed input, owner shutdown and standalone feature builds").notes("ADC BASE/TIGR CINF negotiation and selected CGET/CSND file ranges; ADCS uses verified implicit TLS. XML/BZip2 file lists and optional full-payload Tiger tree hash verification. 1 MiB per transfer. Direct connections only; no hub token binding, multi-source scheduler, NAT traversal or filesystem access. 256 connections, 30 s first frame, 600 s idle, 10 s handshake/exchange. No persistent protocol data; handlers supply content and decisions.").max_inbound_bytes(crate::server::adc_peer::codec::MAX_COMMAND).well_known_port(1512).build()
121    }
122}
123impl Client for AdcPeerClientProtocol {
124    fn connect(
125        &self,
126        ctx: ConnectContext,
127    ) -> Pin<Box<dyn Future<Output = Result<SocketAddr>> + Send>> {
128        Box::pin(super::connect(ctx))
129    }
130    fn execute_action(&self, v: Value) -> Result<ClientActionResult> {
131        if v["type"] == "disconnect" {
132            return Ok(ClientActionResult::Disconnect);
133        }
134        crate::server::adc_peer::codec::validate(&v)?;
135        Ok(ClientActionResult::Custom {
136            name: v["type"].as_str().expect("validated type").into(),
137            data: v,
138        })
139    }
140}
141pub static EVENTS: LazyLock<Vec<EventType>> = LazyLock::new(|| {
142    vec![
143        EventType::new(
144            "adc_peer_connected",
145            "Peer negotiation completed",
146            json!({"type":"disconnect"}),
147        )
148        .with_actions(requests()),
149        EventType::new(
150            "adc_peer_response",
151            "Protocol response or incoming message",
152            json!({"type":"disconnect"}),
153        )
154        .with_actions(requests()),
155    ]
156});