Skip to main content

lash_core/plugin/
services.rs

1use std::sync::Arc;
2
3use super::*;
4
5#[derive(Debug, thiserror::Error)]
6pub enum PluginActionInvokeError {
7    #[error("unknown plugin action `{0}`")]
8    Unknown(String),
9    #[error("unknown plugin session `{0}`")]
10    UnknownSession(String),
11    #[error("plugin action `{0}` requires a session")]
12    MissingSession(String),
13    #[error("plugin action `{0}` does not accept a session")]
14    UnexpectedSession(String),
15    #[error("plugin session registry is unavailable")]
16    SessionRegistryPoisoned,
17}
18
19#[derive(Clone)]
20pub struct RuntimeServices {
21    pub plugins: Arc<PluginSession>,
22    pub turn_injection_bridge: crate::session::TurnInjectionBridge,
23    pub turn_input_injection_bridge: crate::session::TurnInputInjectionBridge,
24    pub attachment_store: Arc<dyn crate::AttachmentStore>,
25    pub(crate) store: Option<Arc<dyn crate::store::RuntimePersistence>>,
26}
27
28#[derive(Clone)]
29pub struct PersistentRuntimeServices(RuntimeServices);
30
31impl std::ops::Deref for PersistentRuntimeServices {
32    type Target = RuntimeServices;
33
34    fn deref(&self) -> &Self::Target {
35        &self.0
36    }
37}
38
39pub(crate) struct NoopSessionManager;
40
41#[async_trait::async_trait]
42impl SessionSnapshotHost for NoopSessionManager {
43    async fn snapshot_current(&self) -> Result<SessionSnapshot, PluginError> {
44        Err(PluginError::Session(
45            "session snapshots are unavailable in this runtime".to_string(),
46        ))
47    }
48
49    async fn snapshot_session(&self, _session_id: &str) -> Result<SessionSnapshot, PluginError> {
50        Err(PluginError::Session(
51            "session lookup is unavailable in this runtime".to_string(),
52        ))
53    }
54}
55
56#[async_trait::async_trait]
57impl ToolCatalogHost for NoopSessionManager {
58    async fn tool_catalog(&self, _session_id: &str) -> Result<Vec<serde_json::Value>, PluginError> {
59        Err(PluginError::Session(
60            "tool catalogs are unavailable in this runtime".to_string(),
61        ))
62    }
63}
64
65impl ToolStateHost for NoopSessionManager {}
66
67#[async_trait::async_trait]
68impl SessionLifecycleHost for NoopSessionManager {
69    async fn create_session(
70        &self,
71        _request: SessionCreateRequest,
72    ) -> Result<SessionHandle, PluginError> {
73        Err(PluginError::Session(
74            "session creation is unavailable in this runtime".to_string(),
75        ))
76    }
77
78    async fn close_session(&self, _session_id: &str) -> Result<(), PluginError> {
79        Err(PluginError::Session(
80            "session closing is unavailable in this runtime".to_string(),
81        ))
82    }
83}
84
85#[async_trait::async_trait]
86impl TurnHost for NoopSessionManager {
87    async fn start_turn_stream(
88        &self,
89        _session_id: &str,
90        _input: TurnInput,
91    ) -> Result<crate::plugin::SessionTurnHandle, PluginError> {
92        Err(PluginError::Session(
93            "session execution is unavailable in this runtime".to_string(),
94        ))
95    }
96
97    async fn await_turn(&self, _turn_id: &str) -> Result<AssembledTurn, PluginError> {
98        Err(PluginError::Session(
99            "session execution is unavailable in this runtime".to_string(),
100        ))
101    }
102
103    async fn cancel_turn(&self, _turn_id: &str) -> Result<(), PluginError> {
104        Err(PluginError::Session(
105            "session execution is unavailable in this runtime".to_string(),
106        ))
107    }
108}
109
110impl TaskHost for NoopSessionManager {}
111impl MonitorHost for NoopSessionManager {}
112impl SessionGraphHost for NoopSessionManager {}
113impl DirectCompletionHost for NoopSessionManager {}
114impl TraceHost for NoopSessionManager {}
115
116impl RuntimeServices {
117    pub fn new(plugins: Arc<PluginSession>) -> Self {
118        Self::new_with_bridges(
119            plugins,
120            crate::session::TurnInjectionBridge::new(),
121            crate::session::TurnInputInjectionBridge::new(),
122        )
123    }
124
125    pub fn new_with_bridges(
126        plugins: Arc<PluginSession>,
127        turn_injection_bridge: crate::session::TurnInjectionBridge,
128        turn_input_injection_bridge: crate::session::TurnInputInjectionBridge,
129    ) -> Self {
130        Self {
131            plugins,
132            turn_injection_bridge,
133            turn_input_injection_bridge,
134            attachment_store: Arc::new(crate::InMemoryAttachmentStore::new()),
135            store: None,
136        }
137    }
138
139    pub(crate) fn with_attachment_store(
140        mut self,
141        attachment_store: Arc<dyn crate::AttachmentStore>,
142    ) -> Self {
143        self.attachment_store = attachment_store;
144        self
145    }
146}
147
148impl PersistentRuntimeServices {
149    pub fn new(
150        plugins: Arc<PluginSession>,
151        store: Arc<dyn crate::store::RuntimePersistence>,
152    ) -> Self {
153        Self::new_with_bridges(
154            plugins,
155            crate::session::TurnInjectionBridge::new(),
156            crate::session::TurnInputInjectionBridge::new(),
157            store,
158        )
159    }
160
161    pub fn new_with_bridges(
162        plugins: Arc<PluginSession>,
163        turn_injection_bridge: crate::session::TurnInjectionBridge,
164        turn_input_injection_bridge: crate::session::TurnInputInjectionBridge,
165        store: Arc<dyn crate::store::RuntimePersistence>,
166    ) -> Self {
167        Self(RuntimeServices {
168            plugins,
169            turn_injection_bridge,
170            turn_input_injection_bridge,
171            attachment_store: Arc::new(crate::InMemoryAttachmentStore::new()),
172            store: Some(store),
173        })
174    }
175
176    pub(crate) fn into_runtime_services(self) -> RuntimeServices {
177        self.0
178    }
179
180    pub fn store(&self) -> Arc<dyn crate::store::RuntimePersistence> {
181        self.0
182            .store
183            .as_ref()
184            .expect("persistent runtime services must carry a store")
185            .clone()
186    }
187}