use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ListPromptsRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub cursor: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ListPromptsResponse {
pub prompts: Vec<Prompt>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Prompt {
pub name: String,
pub description: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub arguments: Option<Value>,
}
impl Prompt {
pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
Self {
name: name.into(),
description: description.into(),
arguments: None,
}
}
pub fn with_arguments(mut self, arguments: Value) -> Self {
self.arguments = Some(arguments);
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GetPromptRequest {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub arguments: Option<Value>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GetPromptResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default)]
pub messages: Vec<PromptMessage>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PromptMessage {
pub role: MessageRole,
pub content: PromptContent,
}
impl PromptMessage {
pub fn new(role: MessageRole, content: PromptContent) -> Self {
Self { role, content }
}
pub fn system(content: impl Into<String>) -> Self {
Self::new(MessageRole::System, PromptContent::text(content))
}
pub fn user(content: impl Into<String>) -> Self {
Self::new(MessageRole::User, PromptContent::text(content))
}
pub fn assistant(content: impl Into<String>) -> Self {
Self::new(MessageRole::Assistant, PromptContent::text(content))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum MessageRole {
System,
User,
Assistant,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum PromptContent {
#[serde(rename = "text")]
Text {
text: String,
},
#[serde(rename = "image")]
Image {
data: String,
#[serde(rename = "mimeType")]
mime_type: String,
},
#[serde(rename = "resource")]
Resource {
resource: ResourceReference,
},
}
impl PromptContent {
pub fn text(text: impl Into<String>) -> Self {
Self::Text { text: text.into() }
}
pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
Self::Image {
data: data.into(),
mime_type: mime_type.into(),
}
}
pub fn resource(uri: impl Into<String>) -> Self {
Self::Resource {
resource: ResourceReference {
uri: uri.into(),
text: None,
},
}
}
pub fn resource_with_text(uri: impl Into<String>, text: impl Into<String>) -> Self {
Self::Resource {
resource: ResourceReference {
uri: uri.into(),
text: Some(text.into()),
},
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ResourceReference {
pub uri: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct PromptListChangedNotification {
#[serde(flatten)]
pub metadata: HashMap<String, Value>,
}
impl PromptListChangedNotification {
pub fn new() -> Self {
Self::default()
}
pub fn with_metadata(mut self, key: impl Into<String>, value: Value) -> Self {
self.metadata.insert(key.into(), value);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_prompt_creation() {
let prompt =
Prompt::new("code_review", "Review code for best practices").with_arguments(json!({
"type": "object",
"properties": {
"language": {"type": "string"},
"code": {"type": "string"}
},
"required": ["code"]
}));
assert_eq!(prompt.name, "code_review");
assert_eq!(prompt.description, "Review code for best practices");
assert!(prompt.arguments.is_some());
}
#[test]
fn test_list_prompts_request() {
let request = ListPromptsRequest { cursor: None };
let json = serde_json::to_string(&request).unwrap();
let deserialized: ListPromptsRequest = serde_json::from_str(&json).unwrap();
assert_eq!(request, deserialized);
}
#[test]
fn test_get_prompt_request() {
let request = GetPromptRequest {
name: "code_review".to_string(),
arguments: Some(json!({"language": "rust", "code": "fn main() {}"})),
};
let json = serde_json::to_string(&request).unwrap();
let deserialized: GetPromptRequest = serde_json::from_str(&json).unwrap();
assert_eq!(request, deserialized);
}
#[test]
fn test_prompt_message_creation() {
let system_msg = PromptMessage::system("You are a helpful assistant");
let user_msg = PromptMessage::user("Hello, how are you?");
let assistant_msg = PromptMessage::assistant("I'm doing well, thank you!");
assert_eq!(system_msg.role, MessageRole::System);
assert_eq!(user_msg.role, MessageRole::User);
assert_eq!(assistant_msg.role, MessageRole::Assistant);
}
#[test]
fn test_prompt_content_text() {
let content = PromptContent::text("Hello world");
let json = serde_json::to_value(&content).unwrap();
assert_eq!(json["type"], "text");
assert_eq!(json["text"], "Hello world");
}
#[test]
fn test_prompt_content_image() {
let content = PromptContent::image("base64data", "image/png");
let json = serde_json::to_value(&content).unwrap();
assert_eq!(json["type"], "image");
assert_eq!(json["data"], "base64data");
assert_eq!(json["mimeType"], "image/png");
}
#[test]
fn test_prompt_content_resource() {
let content = PromptContent::resource_with_text("file:///test.txt", "A test file");
let json = serde_json::to_value(&content).unwrap();
assert_eq!(json["type"], "resource");
assert_eq!(json["resource"]["uri"], "file:///test.txt");
assert_eq!(json["resource"]["text"], "A test file");
}
#[test]
fn test_message_role_serialization() {
let system_role = MessageRole::System;
let json = serde_json::to_string(&system_role).unwrap();
assert_eq!(json, "\"system\"");
let user_role = MessageRole::User;
let json = serde_json::to_string(&user_role).unwrap();
assert_eq!(json, "\"user\"");
let assistant_role = MessageRole::Assistant;
let json = serde_json::to_string(&assistant_role).unwrap();
assert_eq!(json, "\"assistant\"");
}
}