gproxy-protocol 3.0.0-alpha.0

GPROXY v3 protocol model: operation taxonomy, wire kinds, and the OperationSpec registry
Documentation
mod actions;
mod agent;
mod content;
mod message;
mod typed;

pub use actions::*;
pub use agent::*;
pub use content::*;
pub use message::*;
pub use typed::*;

use serde::{Deserialize, Serialize, de};
use serde_json::Value;

use crate::openai::common::{ResponseItemType, ResponseItemTypeKnown, Rest};

#[derive(Debug, Clone, PartialEq, Serialize)]
#[serde(untagged)]
#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
pub enum ResponseItem {
    Message(ResponseMessageItem),
    Typed(Box<TypedResponseItem>),
    Unknown(Value),
}

impl<'de> Deserialize<'de> for ResponseItem {
    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        let value = Value::deserialize(deserializer)?;
        let Some(type_name) = value.get("type").and_then(Value::as_str) else {
            if let Ok(message) = serde_json::from_value::<ResponseMessageItem>(value.clone()) {
                return Ok(Self::Message(message));
            }
            if let Some(item) = item_reference_without_type(&value) {
                return Ok(Self::Typed(Box::new(item)));
            }
            return Ok(Self::Unknown(value));
        };
        let item_type = serde_json::from_value::<ResponseItemType>(Value::String(type_name.into()))
            .map_err(de::Error::custom)?;
        match item_type {
            ResponseItemType::Known(ResponseItemTypeKnown::Message) => {
                serde_json::from_value(value)
                    .map(Self::Message)
                    .map_err(de::Error::custom)
            }
            ResponseItemType::Known(_) => match serde_json::from_value(value.clone()) {
                Ok(item) => Ok(Self::Typed(Box::new(item))),
                Err(_) => Ok(Self::Unknown(value)),
            },
            ResponseItemType::Unknown(_) => Ok(Self::Unknown(value)),
        }
    }
}

fn item_reference_without_type(value: &Value) -> Option<TypedResponseItem> {
    let object = value.as_object()?;
    let id = object.get("id")?.as_str()?.to_owned();
    let rest: Rest = object
        .iter()
        .filter(|(key, _)| key.as_str() != "id")
        .map(|(key, value)| (key.clone(), value.clone()))
        .collect();
    Some(TypedResponseItem::ItemReference { id, rest })
}

pub type ResponseOutputItem = ResponseItem;