lash_core/plugin/
services.rs1use 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}