use std::sync::Arc;
use derive_more::From;
use serde::{Deserialize, Serialize};
use serde_json::value::RawValue;
use serde_with::{DefaultOnError, serde_as, skip_serializing_none};
use super::{InitializeRequest, InitializeResponse, Meta};
use crate::IntoOption;
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "proxy", "x-method" = PROXY_INITIALIZE_METHOD_NAME)))]
#[serde(transparent)]
#[non_exhaustive]
pub struct InitializeProxyRequest {
pub initialize: InitializeRequest,
}
impl InitializeProxyRequest {
#[must_use]
pub fn new(initialize: InitializeRequest) -> Self {
Self { initialize }
}
}
impl From<InitializeRequest> for InitializeProxyRequest {
fn from(initialize: InitializeRequest) -> Self {
Self::new(initialize)
}
}
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "proxy", "x-method" = PROXY_INITIALIZE_METHOD_NAME)))]
#[serde(transparent)]
#[non_exhaustive]
pub struct InitializeProxyResponse {
pub initialize: InitializeResponse,
}
impl InitializeProxyResponse {
#[must_use]
pub fn new(initialize: InitializeResponse) -> Self {
Self { initialize }
}
}
impl From<InitializeResponse> for InitializeProxyResponse {
fn from(initialize: InitializeResponse) -> Self {
Self::new(initialize)
}
}
#[serde_as]
#[skip_serializing_none]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "proxy", "x-method" = PROXY_SUCCESSOR_METHOD_NAME)))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct ProxySuccessorRequest {
pub method: Arc<str>,
#[cfg_attr(feature = "schemars", schemars(with = "Option<serde_json::Value>"))]
#[serde(default)]
pub params: Option<Arc<RawValue>>,
#[serde_as(deserialize_as = "DefaultOnError")]
#[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
#[serde(default)]
#[serde(rename = "_meta")]
pub meta: Option<Meta>,
}
impl ProxySuccessorRequest {
#[must_use]
pub fn new(method: impl Into<Arc<str>>) -> Self {
Self {
method: method.into(),
params: None,
meta: None,
}
}
#[must_use]
pub fn params(mut self, params: impl IntoOption<Arc<RawValue>>) -> Self {
self.params = params.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[serde_as]
#[skip_serializing_none]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "proxy", "x-method" = PROXY_SUCCESSOR_METHOD_NAME)))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct ProxySuccessorNotification {
pub method: Arc<str>,
#[cfg_attr(feature = "schemars", schemars(with = "Option<serde_json::Value>"))]
#[serde(default)]
pub params: Option<Arc<RawValue>>,
#[serde_as(deserialize_as = "DefaultOnError")]
#[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
#[serde(default)]
#[serde(rename = "_meta")]
pub meta: Option<Meta>,
}
impl ProxySuccessorNotification {
#[must_use]
pub fn new(method: impl Into<Arc<str>>) -> Self {
Self {
method: method.into(),
params: None,
meta: None,
}
}
#[must_use]
pub fn params(mut self, params: impl IntoOption<Arc<RawValue>>) -> Self {
self.params = params.into_option();
self
}
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, From)]
#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "proxy", "x-method" = PROXY_SUCCESSOR_METHOD_NAME)))]
#[serde(transparent)]
#[non_exhaustive]
pub struct ProxySuccessorResponse(
#[cfg_attr(feature = "schemars", schemars(with = "serde_json::Value"))] pub Arc<RawValue>,
);
impl ProxySuccessorResponse {
#[must_use]
pub fn new(result: Arc<RawValue>) -> Self {
Self(result)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct ProxyMethodNames {
pub proxy_initialize: &'static str,
pub proxy_successor: &'static str,
}
pub const PROXY_METHOD_NAMES: ProxyMethodNames = ProxyMethodNames {
proxy_initialize: PROXY_INITIALIZE_METHOD_NAME,
proxy_successor: PROXY_SUCCESSOR_METHOD_NAME,
};
pub(crate) const PROXY_INITIALIZE_METHOD_NAME: &str = "proxy/initialize";
pub(crate) const PROXY_SUCCESSOR_METHOD_NAME: &str = "proxy/successor";
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Clone, Debug, Serialize, Deserialize)]
#[allow(clippy::large_enum_variant)]
#[serde(untagged)]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[non_exhaustive]
pub enum ProxyRequest {
InitializeProxyRequest(InitializeProxyRequest),
ProxySuccessorRequest(ProxySuccessorRequest),
}
impl ProxyRequest {
#[must_use]
pub fn method(&self) -> &str {
match self {
Self::InitializeProxyRequest(_) => PROXY_METHOD_NAMES.proxy_initialize,
Self::ProxySuccessorRequest(_) => PROXY_METHOD_NAMES.proxy_successor,
}
}
}
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Clone, Debug, Serialize, Deserialize)]
#[allow(clippy::large_enum_variant)]
#[serde(untagged)]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[non_exhaustive]
pub enum ProxyResponse {
InitializeProxyResponse(InitializeProxyResponse),
ProxySuccessorResponse(ProxySuccessorResponse),
}
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[non_exhaustive]
pub enum ProxyNotification {
ProxySuccessorNotification(ProxySuccessorNotification),
}
impl ProxyNotification {
#[must_use]
pub fn method(&self) -> &str {
match self {
Self::ProxySuccessorNotification(_) => PROXY_METHOD_NAMES.proxy_successor,
}
}
}
#[cfg(test)]
mod tests {
use serde_json::json;
use super::*;
use crate::ProtocolVersion;
fn raw(value: &serde_json::Value) -> Arc<RawValue> {
RawValue::from_string(value.to_string()).unwrap().into()
}
#[test]
fn test_initialize_proxy_request_matches_initialize_params() {
let request = InitializeProxyRequest::new(InitializeRequest::new(ProtocolVersion::V1));
let value = serde_json::to_value(&request).unwrap();
assert_eq!(value, serde_json::to_value(&request.initialize).unwrap());
assert_eq!(
ProxyRequest::InitializeProxyRequest(request).method(),
"proxy/initialize"
);
let response = InitializeProxyResponse::new(InitializeResponse::new(ProtocolVersion::V1));
assert_eq!(
serde_json::to_value(&response).unwrap(),
serde_json::to_value(&response.initialize).unwrap()
);
let parsed: InitializeProxyRequest = serde_json::from_value(value).unwrap();
assert_eq!(parsed.initialize.protocol_version, ProtocolVersion::V1);
}
#[test]
fn test_proxy_successor_round_trip() {
let request = ProxySuccessorRequest::new("session/prompt")
.params(raw(
&json!({"sessionId": "sess_abc123", "_meta": {"inner": true}}),
))
.meta(json!({"outer": true}).as_object().cloned());
let value = serde_json::to_value(&request).unwrap();
assert_eq!(
value,
json!({
"method": "session/prompt",
"params": {"sessionId": "sess_abc123", "_meta": {"inner": true}},
"_meta": {"outer": true}
})
);
assert_eq!(
ProxyRequest::ProxySuccessorRequest(request).method(),
"proxy/successor"
);
for params in [
json!({"method": "session/cancel"}),
json!({"method": "session/cancel", "params": null}),
] {
let notification: ProxySuccessorNotification = serde_json::from_value(params).unwrap();
assert_eq!(&*notification.method, "session/cancel");
assert!(notification.params.is_none());
}
assert!(
serde_json::from_value::<ProxySuccessorNotification>(json!({"params": {}})).is_err()
);
let response = ProxySuccessorResponse::new(raw(&json!({"stopReason": "end_turn"})));
assert_eq!(
serde_json::to_value(&response).unwrap(),
json!({"stopReason": "end_turn"})
);
}
}