Skip to main content

ldp_protocol/
protocol.rs

1//! Standalone protocol abstractions for LDP.
2//!
3//! These types define the adapter interface for LDP, allowing it to operate
4//! independently or as a plugin within runtimes like JamJet.
5
6use crate::types::contract::DelegationContract;
7use crate::types::error::LdpError;
8use async_trait::async_trait;
9use futures::Stream;
10use serde::{Deserialize, Serialize};
11use serde_json::Value;
12use std::pin::Pin;
13
14/// A skill exposed by a remote delegate.
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct RemoteSkill {
17    /// Skill name (e.g., "reasoning", "summarization").
18    pub name: String,
19    /// Human-readable description.
20    pub description: Option<String>,
21    /// JSON Schema for expected input.
22    pub input_schema: Option<Value>,
23    /// JSON Schema for expected output.
24    pub output_schema: Option<Value>,
25}
26
27/// Capabilities discovered from a remote delegate.
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct RemoteCapabilities {
30    /// Delegate display name.
31    pub name: String,
32    /// Human-readable description.
33    pub description: Option<String>,
34    /// Available skills.
35    pub skills: Vec<RemoteSkill>,
36    /// Supported protocols (e.g., `["ldp"]`).
37    pub protocols: Vec<String>,
38}
39
40/// A request to execute a task on a remote delegate.
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct TaskRequest {
43    /// The skill to invoke.
44    pub skill: String,
45    /// Input data for the task.
46    pub input: Value,
47    /// Optional delegation contract for bounded task execution.
48    #[serde(skip_serializing_if = "Option::is_none", default)]
49    pub contract: Option<DelegationContract>,
50}
51
52/// Handle returned after submitting a task.
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct TaskHandle {
55    /// Unique identifier for the submitted task.
56    pub task_id: String,
57    /// URL of the remote delegate handling the task.
58    pub remote_url: String,
59}
60
61/// Events emitted during task streaming.
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub enum TaskEvent {
64    /// Progress update.
65    Progress {
66        message: String,
67        progress: Option<f32>,
68    },
69    /// Task completed successfully.
70    Completed { output: Value },
71    /// Task failed.
72    Failed { error: LdpError },
73}
74
75/// Current status of a submitted task.
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub enum TaskStatus {
78    /// Task has been submitted but not yet started.
79    Submitted,
80    /// Task is actively being processed.
81    Working,
82    /// Task completed with output.
83    Completed { output: Value },
84    /// Task failed with an error.
85    Failed { error: LdpError },
86}
87
88/// Async stream of task events.
89pub type TaskStream = Pin<Box<dyn Stream<Item = TaskEvent> + Send>>;
90
91/// Protocol adapter trait — the core abstraction for delegate communication.
92///
93/// Implementations handle the full lifecycle: discovery, invocation, streaming,
94/// status polling, and cancellation.
95#[async_trait]
96pub trait ProtocolAdapter: Send + Sync {
97    /// Discover capabilities of a remote delegate.
98    async fn discover(&self, url: &str) -> Result<RemoteCapabilities, String>;
99
100    /// Submit a task for execution and return a handle.
101    async fn invoke(&self, url: &str, task: TaskRequest) -> Result<TaskHandle, String>;
102
103    /// Submit a task and stream progress events.
104    async fn stream(&self, url: &str, task: TaskRequest) -> Result<TaskStream, String>;
105
106    /// Poll the current status of a submitted task.
107    async fn status(&self, url: &str, task_id: &str) -> Result<TaskStatus, String>;
108
109    /// Cancel a running task.
110    async fn cancel(&self, url: &str, task_id: &str) -> Result<(), String>;
111}
112
113/// Registry for protocol adapters, mapping protocol names to implementations.
114///
115/// Supports URL-based routing: adapters register URL prefixes, and the registry
116/// resolves which adapter handles a given URL.
117pub struct ProtocolRegistry {
118    adapters: Vec<(String, std::sync::Arc<dyn ProtocolAdapter>, Vec<String>)>,
119}
120
121impl ProtocolRegistry {
122    /// Create an empty registry.
123    pub fn new() -> Self {
124        Self {
125            adapters: Vec::new(),
126        }
127    }
128
129    /// Register an adapter with a protocol name and URL prefixes.
130    pub fn register(
131        &mut self,
132        name: &str,
133        adapter: std::sync::Arc<dyn ProtocolAdapter>,
134        url_prefixes: Vec<&str>,
135    ) {
136        self.adapters.push((
137            name.to_string(),
138            adapter,
139            url_prefixes.iter().map(|s| s.to_string()).collect(),
140        ));
141    }
142
143    /// Look up an adapter by protocol name.
144    pub fn adapter(&self, name: &str) -> Option<&dyn ProtocolAdapter> {
145        self.adapters
146            .iter()
147            .find(|(n, _, _)| n == name)
148            .map(|(_, a, _)| a.as_ref())
149    }
150
151    /// Find the adapter that handles a given URL.
152    pub fn adapter_for_url(&self, url: &str) -> Option<&dyn ProtocolAdapter> {
153        self.adapters
154            .iter()
155            .find(|(_, _, prefixes)| prefixes.iter().any(|p| url.starts_with(p)))
156            .map(|(_, a, _)| a.as_ref())
157    }
158
159    /// List all registered protocol names.
160    pub fn protocols(&self) -> Vec<&str> {
161        self.adapters.iter().map(|(n, _, _)| n.as_str()).collect()
162    }
163}
164
165impl Default for ProtocolRegistry {
166    fn default() -> Self {
167        Self::new()
168    }
169}