1use std::{future::Future, marker::PhantomData};
8
9#[cfg(not(feature = "local"))]
10use futures::future::BoxFuture;
11use serde::de::DeserializeOwned;
12
13use super::common::{AsRequestContext, FromContextPart};
14pub use super::common::{Extension, RequestId};
15use crate::{
16 RoleServer,
17 handler::server::wrapper::Parameters,
18 model::{GetPromptResponse, GetPromptResult, InputRequiredResult, PromptMessage},
19 service::{MaybeBoxFuture, MaybeSend, MaybeSendFuture, RequestContext},
20};
21
22#[non_exhaustive]
24pub struct PromptContext<'a, S> {
25 pub server: &'a S,
26 pub name: String,
27 pub arguments: Option<serde_json::Map<String, serde_json::Value>>,
28 pub context: RequestContext<RoleServer>,
29}
30
31impl<'a, S> PromptContext<'a, S> {
32 pub fn new(
33 server: &'a S,
34 name: String,
35 arguments: Option<serde_json::Map<String, serde_json::Value>>,
36 context: RequestContext<RoleServer>,
37 ) -> Self {
38 Self {
39 server,
40 name,
41 arguments,
42 context,
43 }
44 }
45}
46
47impl<S> AsRequestContext for PromptContext<'_, S> {
48 fn as_request_context(&self) -> &RequestContext<RoleServer> {
49 &self.context
50 }
51
52 fn as_request_context_mut(&mut self) -> &mut RequestContext<RoleServer> {
53 &mut self.context
54 }
55}
56
57pub trait GetPromptHandler<S, A> {
59 fn handle(
60 self,
61 context: PromptContext<'_, S>,
62 ) -> MaybeBoxFuture<'_, Result<GetPromptResponse, crate::ErrorData>>;
63}
64
65#[cfg(not(feature = "local"))]
67pub type DynGetPromptHandler<S> = dyn for<'a> Fn(PromptContext<'a, S>) -> BoxFuture<'a, Result<GetPromptResponse, crate::ErrorData>>
68 + Send
69 + Sync;
70
71#[cfg(feature = "local")]
72pub type DynGetPromptHandler<S> = dyn for<'a> Fn(
73 PromptContext<'a, S>,
74) -> futures::future::LocalBoxFuture<
75 'a,
76 Result<GetPromptResponse, crate::ErrorData>,
77>;
78
79pub struct AsyncMethodAdapter<T>(PhantomData<T>);
81
82pub struct AsyncMethodWithArgsAdapter<T>(PhantomData<T>);
84
85#[allow(clippy::type_complexity)]
87pub struct AsyncPromptAdapter<P, Fut, R>(PhantomData<fn(P) -> fn(Fut) -> R>);
88pub struct SyncPromptAdapter<P, R>(PhantomData<fn(P) -> R>);
89pub struct AsyncPromptMethodAdapter<P, R>(PhantomData<fn(P) -> R>);
90pub struct SyncPromptMethodAdapter<P, R>(PhantomData<fn(P) -> R>);
91
92pub trait IntoGetPromptResult {
94 fn into_get_prompt_result(self) -> Result<GetPromptResponse, crate::ErrorData>;
95}
96
97impl IntoGetPromptResult for GetPromptResult {
98 fn into_get_prompt_result(self) -> Result<GetPromptResponse, crate::ErrorData> {
99 Ok(self.into())
100 }
101}
102
103impl IntoGetPromptResult for InputRequiredResult {
104 fn into_get_prompt_result(self) -> Result<GetPromptResponse, crate::ErrorData> {
105 Ok(self.into())
106 }
107}
108
109impl IntoGetPromptResult for Vec<PromptMessage> {
110 fn into_get_prompt_result(self) -> Result<GetPromptResponse, crate::ErrorData> {
111 Ok(GetPromptResult::new(self).into())
112 }
113}
114
115impl<T: IntoGetPromptResult> IntoGetPromptResult for Result<T, crate::ErrorData> {
116 fn into_get_prompt_result(self) -> Result<GetPromptResponse, crate::ErrorData> {
117 self.and_then(|v| v.into_get_prompt_result())
118 }
119}
120
121pin_project_lite::pin_project! {
123 #[project = IntoGetPromptResultFutProj]
124 #[non_exhaustive]
125 pub enum IntoGetPromptResultFut<F, R> {
126 Pending {
127 #[pin]
128 fut: F,
129 _marker: PhantomData<R>,
130 },
131 Ready {
132 #[pin]
133 result: futures::future::Ready<Result<GetPromptResponse, crate::ErrorData>>,
134 }
135 }
136}
137
138impl<F, R> Future for IntoGetPromptResultFut<F, R>
139where
140 F: Future<Output = R>,
141 R: IntoGetPromptResult,
142{
143 type Output = Result<GetPromptResponse, crate::ErrorData>;
144
145 fn poll(
146 self: std::pin::Pin<&mut Self>,
147 cx: &mut std::task::Context<'_>,
148 ) -> std::task::Poll<Self::Output> {
149 match self.project() {
150 IntoGetPromptResultFutProj::Pending { fut, _marker } => fut
151 .poll(cx)
152 .map(IntoGetPromptResult::into_get_prompt_result),
153 IntoGetPromptResultFutProj::Ready { result } => result.poll(cx),
154 }
155 }
156}
157
158#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
160pub struct PromptName(pub String);
161
162impl<S> FromContextPart<PromptContext<'_, S>> for PromptName {
163 fn from_context_part(context: &mut PromptContext<S>) -> Result<Self, crate::ErrorData> {
164 Ok(Self(context.name.clone()))
165 }
166}
167
168impl<S, P> FromContextPart<PromptContext<'_, S>> for Parameters<P>
170where
171 P: DeserializeOwned,
172{
173 fn from_context_part(context: &mut PromptContext<S>) -> Result<Self, crate::ErrorData> {
174 let params = if let Some(args_map) = context.arguments.take() {
175 let args_value = serde_json::Value::Object(args_map);
176 serde_json::from_value::<P>(args_value).map_err(|e| {
177 crate::ErrorData::invalid_params(format!("Failed to parse parameters: {}", e), None)
178 })?
179 } else {
180 serde_json::from_value::<P>(serde_json::json!({})).map_err(|e| {
182 crate::ErrorData::invalid_params(
183 format!("Missing required parameters: {}", e),
184 None,
185 )
186 })?
187 };
188 Ok(Parameters(params))
189 }
190}
191
192macro_rules! impl_prompt_handler_for {
194 ($($T: ident)*) => {
195 impl_prompt_handler_for!([] [$($T)*]);
196 };
197 ([$($Tn: ident)*] []) => {
199 impl_prompt_handler_for!(@impl $($Tn)*);
200 };
201 ([$($Tn: ident)*] [$Tn_1: ident $($Rest: ident)*]) => {
202 impl_prompt_handler_for!(@impl $($Tn)*);
203 impl_prompt_handler_for!([$($Tn)* $Tn_1] [$($Rest)*]);
204 };
205 (@impl $($Tn: ident)*) => {
206 impl<$($Tn,)* S, F, R> GetPromptHandler<S, ($($Tn,)*)> for F
208 where
209 $(
210 $Tn: for<'a> FromContextPart<PromptContext<'a, S>> + MaybeSendFuture,
211 )*
212 F: FnOnce(&S, $($Tn,)*) -> MaybeBoxFuture<'_, R> + MaybeSendFuture,
213 R: IntoGetPromptResult + MaybeSendFuture + 'static,
214 S: MaybeSend + 'static,
215 {
216 #[allow(unused_variables, non_snake_case, unused_mut)]
217 fn handle(
218 self,
219 mut context: PromptContext<'_, S>,
220 ) -> MaybeBoxFuture<'_, Result<GetPromptResponse, crate::ErrorData>>
221 {
222 $(
223 let result = $Tn::from_context_part(&mut context);
224 let $Tn = match result {
225 Ok(value) => value,
226 Err(e) => return Box::pin(std::future::ready(Err(e))),
227 };
228 )*
229 let service = context.server;
230 let fut = self(service, $($Tn,)*);
231 Box::pin(async move {
232 let result = fut.await;
233 result.into_get_prompt_result()
234 })
235 }
236 }
237
238
239 impl<$($Tn,)* S, F, R> GetPromptHandler<S, SyncPromptMethodAdapter<($($Tn,)*), R>> for F
241 where
242 $(
243 $Tn: for<'a> FromContextPart<PromptContext<'a, S>> + MaybeSendFuture,
244 )*
245 F: FnOnce(&S, $($Tn,)*) -> R + MaybeSendFuture,
246 R: IntoGetPromptResult + MaybeSendFuture,
247 S: MaybeSend,
248 {
249 #[allow(unused_variables, non_snake_case, unused_mut)]
250 fn handle(
251 self,
252 mut context: PromptContext<'_, S>,
253 ) -> MaybeBoxFuture<'_, Result<GetPromptResponse, crate::ErrorData>>
254 {
255 $(
256 let result = $Tn::from_context_part(&mut context);
257 let $Tn = match result {
258 Ok(value) => value,
259 Err(e) => return Box::pin(std::future::ready(Err(e))),
260 };
261 )*
262 let service = context.server;
263 let result = self(service, $($Tn,)*);
264 Box::pin(std::future::ready(result.into_get_prompt_result()))
265 }
266 }
267
268
269 impl<$($Tn,)* S, F, Fut, R> GetPromptHandler<S, AsyncPromptAdapter<($($Tn,)*), Fut, R>> for F
271 where
272 $(
273 $Tn: for<'a> FromContextPart<PromptContext<'a, S>> + MaybeSendFuture + 'static,
274 )*
275 F: FnOnce($($Tn,)*) -> Fut + MaybeSendFuture + 'static,
276 Fut: Future<Output = Result<R, crate::ErrorData>> + MaybeSendFuture + 'static,
277 R: IntoGetPromptResult + MaybeSendFuture + 'static,
278 S: MaybeSend + 'static,
279 {
280 #[allow(unused_variables, non_snake_case, unused_mut)]
281 fn handle(
282 self,
283 mut context: PromptContext<'_, S>,
284 ) -> MaybeBoxFuture<'_, Result<GetPromptResponse, crate::ErrorData>>
285 {
286 $(
288 let result = $Tn::from_context_part(&mut context);
289 let $Tn = match result {
290 Ok(value) => value,
291 Err(e) => return Box::pin(std::future::ready(Err(e))),
292 };
293 )*
294
295 Box::pin(async move {
298 let result = self($($Tn,)*).await?;
299 result.into_get_prompt_result()
300 })
301 }
302 }
303
304
305 impl<$($Tn,)* S, F, R> GetPromptHandler<S, SyncPromptAdapter<($($Tn,)*), R>> for F
307 where
308 $(
309 $Tn: for<'a> FromContextPart<PromptContext<'a, S>> + MaybeSendFuture + 'static,
310 )*
311 F: FnOnce($($Tn,)*) -> Result<R, crate::ErrorData> + MaybeSendFuture + 'static,
312 R: IntoGetPromptResult + MaybeSendFuture + 'static,
313 S: MaybeSend,
314 {
315 #[allow(unused_variables, non_snake_case, unused_mut)]
316 fn handle(
317 self,
318 mut context: PromptContext<'_, S>,
319 ) -> MaybeBoxFuture<'_, Result<GetPromptResponse, crate::ErrorData>>
320 {
321 $(
322 let result = $Tn::from_context_part(&mut context);
323 let $Tn = match result {
324 Ok(value) => value,
325 Err(e) => return Box::pin(std::future::ready(Err(e))),
326 };
327 )*
328 let result = self($($Tn,)*);
329 Box::pin(std::future::ready(result.and_then(|r| r.into_get_prompt_result())))
330 }
331 }
332
333 };
334}
335
336impl_prompt_handler_for!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15);
338
339pub fn cached_arguments_from_schema<T: schemars::JsonSchema + std::any::Any>()
343-> Option<Vec<crate::model::PromptArgument>> {
344 let schema = super::common::schema_for_type::<T>();
345 let schema_value = serde_json::Value::Object((*schema).clone());
346
347 let properties = schema_value.get("properties").and_then(|p| p.as_object());
348
349 if let Some(props) = properties {
350 let required = schema_value
351 .get("required")
352 .and_then(|r| r.as_array())
353 .map(|arr| {
354 arr.iter()
355 .filter_map(|v| v.as_str())
356 .collect::<std::collections::HashSet<_>>()
357 })
358 .unwrap_or_default();
359
360 let mut arguments = Vec::new();
361 for (name, prop_schema) in props {
362 let description = prop_schema
363 .get("description")
364 .and_then(|d| d.as_str())
365 .map(|s| s.to_string());
366
367 arguments.push(crate::model::PromptArgument {
368 name: name.clone(),
369 title: None,
370 description,
371 required: Some(required.contains(name.as_str())),
372 });
373 }
374
375 if arguments.is_empty() {
376 None
377 } else {
378 Some(arguments)
379 }
380 } else {
381 None
382 }
383}