use std::future::Future;
use std::sync::Arc;
use crate::adapters::secondary::codec::result::verbatim;
use crate::application::server::DispatchError;
use crate::application::server::builder::{BoxReadFuture, PromptHandler, ResourceHandler};
use crate::application::server::context::RequestContext;
use crate::application::server::result::OutcomeError;
use crate::domain::ProtocolVersion;
use crate::domain::ir::{
Content, GetPromptResult, JsonObject, PromptMessage, ResourceBody, ResourceContents, ToolResult,
};
pub fn read_contents(contents: &[ResourceContents]) -> Result<JsonObject, OutcomeError> {
let mut entries = Vec::with_capacity(contents.len());
for entry in contents {
let mut object = serde_json::Map::new();
object.insert("uri".to_owned(), entry.uri.clone().into());
match &entry.body {
ResourceBody::Text(text) => {
object.insert("text".to_owned(), text.clone().into());
}
ResourceBody::Blob(data) => {
object.insert("blob".to_owned(), data.clone().into());
}
}
if let Some(mime_type) = &entry.mime_type {
object.insert("mimeType".to_owned(), mime_type.clone().into());
}
entries.push(serde_json::Value::Object(object));
}
JsonObject::from_value(serde_json::json!({ "contents": entries }))
.map_err(OutcomeError::NotAnObject)
}
pub fn prompt_messages(
result: &GetPromptResult,
version: ProtocolVersion,
) -> Result<JsonObject, OutcomeError> {
let mut messages = Vec::with_capacity(result.messages.len());
for message in &result.messages {
let block = one_block(&message.content, version)?;
let block: serde_json::Value = serde_json::from_str(block.as_str())
.map_err(|error| OutcomeError::Answer(error.to_string()))?;
messages.push(serde_json::json!({
"role": message.role.as_str(),
"content": block,
}));
}
let mut document = serde_json::json!({ "messages": messages });
if let (serde_json::Value::Object(map), Some(description)) =
(&mut document, result.description.as_deref())
{
map.insert("description".to_string(), description.into());
}
JsonObject::from_value(document).map_err(OutcomeError::NotAnObject)
}
fn one_block(content: &Content, version: ProtocolVersion) -> Result<JsonObject, OutcomeError> {
let document = verbatim(version, &ToolResult::new(vec![content.clone()]))?;
let parsed: serde_json::Value = serde_json::from_str(document.as_str())
.map_err(|error| OutcomeError::Answer(error.to_string()))?;
let block = parsed
.get("content")
.and_then(serde_json::Value::as_array)
.and_then(|blocks| blocks.first())
.cloned()
.ok_or_else(|| OutcomeError::Answer("the lowered result carried no block".to_owned()))?;
JsonObject::from_value(block).map_err(OutcomeError::NotAnObject)
}
pub fn resource_reader<F, Fut>(read: F) -> ResourceHandler
where
F: Fn(RequestContext, String) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Vec<ResourceContents>> + Send + 'static,
{
let read = Arc::new(read);
let handler: ResourceHandler = Arc::new(move |context, uri: String| {
let read = Arc::clone(&read);
Box::pin(async move {
let contents = read(context, uri).await;
read_contents(&contents).map_err(DispatchError::from)
}) as BoxReadFuture
});
handler
}
pub fn prompt_getter<F, Fut>(get: F) -> PromptHandler
where
F: Fn(RequestContext, JsonObject) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Vec<PromptMessage>> + Send + 'static,
{
let get = Arc::new(get);
let handler: PromptHandler = Arc::new(move |context, arguments: JsonObject| {
let get = Arc::clone(&get);
Box::pin(async move {
let version = context.peer().negotiated();
let messages = get(context, arguments).await;
let result = GetPromptResult::new(messages);
prompt_messages(&result, version).map_err(DispatchError::from)
}) as BoxReadFuture
});
handler
}