use serde_json::{Map, Value};
use super::{anthropic, gemini};
use crate::services::providers::{Hosting, WireProtocol};
pub const VERTEX_ANTHROPIC_VERSION: &str = "vertex-2023-10-16";
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct UpstreamDialect {
pub wire: WireProtocol,
pub hosting: Hosting,
}
impl UpstreamDialect {
#[must_use]
pub const fn new(wire: WireProtocol, hosting: Hosting) -> Self {
Self { wire, hosting }
}
#[must_use]
pub fn of(wire: WireProtocol, endpoint: &str) -> Self {
Self::new(wire, Hosting::of(endpoint))
}
#[must_use]
pub fn path(self, upstream_model: &str, stream: bool) -> String {
match (self.wire, self.hosting) {
(WireProtocol::Anthropic, Hosting::FirstParty) => "/messages".to_owned(),
(WireProtocol::Anthropic, Hosting::Vertex) => {
let verb = if stream {
"streamRawPredict"
} else {
"rawPredict"
};
format!("/models/{upstream_model}:{verb}")
},
(WireProtocol::Gemini, _) => gemini::upstream_path(upstream_model, stream),
(WireProtocol::OpenAiChat, _) => "/chat/completions".to_owned(),
(WireProtocol::OpenAiResponses, _) => "/responses".to_owned(),
}
}
#[must_use]
pub fn url(self, endpoint: &str, upstream_model: &str, stream: bool) -> String {
format!(
"{}{}",
endpoint.trim_end_matches('/'),
self.path(upstream_model, stream)
)
}
#[must_use]
pub const fn api_key_header(self) -> Option<&'static str> {
match self.wire {
WireProtocol::Anthropic => Some("x-api-key"),
WireProtocol::Gemini => Some(gemini::API_KEY_HEADER),
WireProtocol::OpenAiChat | WireProtocol::OpenAiResponses => None,
}
}
#[must_use]
pub fn required_headers(self) -> Vec<(&'static str, &'static str)> {
match (self.wire, self.hosting) {
(WireProtocol::Anthropic, Hosting::FirstParty) => {
vec![("anthropic-version", anthropic::ANTHROPIC_VERSION)]
},
_ => Vec::new(),
}
}
#[must_use]
pub const fn drops_forwarded_header(self, name: &str) -> bool {
matches!(
(self.wire, self.hosting),
(WireProtocol::Anthropic, Hosting::Vertex)
) && name.eq_ignore_ascii_case("anthropic-version")
}
pub fn finish_body(self, body: &mut Map<String, Value>) {
if (self.wire, self.hosting) == (WireProtocol::Anthropic, Hosting::Vertex) {
body.remove("model");
body.insert(
"anthropic_version".to_owned(),
Value::String(VERTEX_ANTHROPIC_VERSION.to_owned()),
);
}
}
pub fn finish_value(self, body: &mut Value) {
if let Some(obj) = body.as_object_mut() {
self.finish_body(obj);
}
}
}