use crate::protocol::operation::{
ContentGenerationKind, HttpMethod, Operation, OperationKey, OperationKind, Provider,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RequestTarget {
pub method: HttpMethod,
pub path: String,
pub query: Option<String>,
}
impl RequestTarget {
fn get(path: impl Into<String>) -> Self {
Self {
method: HttpMethod::Get,
path: path.into(),
query: None,
}
}
fn post(path: impl Into<String>) -> Self {
Self {
method: HttpMethod::Post,
path: path.into(),
query: None,
}
}
}
pub fn request_target(target: OperationKey, model: &str, stream: bool) -> RequestTarget {
use Provider as P;
let provider = match target.kind {
OperationKind::ContentGeneration(kind) => return content_target(kind, model, stream),
OperationKind::Provider(provider) => provider,
};
match (target.operation, provider) {
(Operation::ListModels, P::OpenAi | P::Claude) => RequestTarget::get("/v1/models"),
(Operation::ListModels, P::Gemini) => RequestTarget::get("/v1beta/models"),
(Operation::GetModel, P::OpenAi | P::Claude) => {
RequestTarget::get(format!("/v1/models/{model}"))
}
(Operation::GetModel, P::Gemini) => RequestTarget::get(format!("/v1beta/models/{model}")),
(Operation::CountTokens, P::OpenAi) => RequestTarget::post("/v1/responses/input_tokens"),
(Operation::CountTokens, P::Claude) => RequestTarget::post("/v1/messages/count_tokens"),
(Operation::CountTokens, P::Gemini) => {
RequestTarget::post(format!("/v1beta/models/{model}:countTokens"))
}
(Operation::CreateEmbedding, P::OpenAi | P::Claude) => {
RequestTarget::post("/v1/embeddings")
}
(Operation::CreateEmbedding, P::Gemini) => {
RequestTarget::post(format!("/v1beta/models/{model}:embedContent"))
}
(Operation::CreateImage, _) => RequestTarget::post("/v1/images/generations"),
(Operation::EditImage, _) => RequestTarget::post("/v1/images/edits"),
(Operation::CompactContent, _) => RequestTarget::post("/v1/responses/compact"),
(Operation::CreateConversation, _) => RequestTarget::post("/v1/conversations"),
(Operation::ConnectRealtime, _) => RequestTarget {
method: HttpMethod::Get,
path: "/v1/realtime".to_owned(),
query: Some(format!("model={model}")),
},
(Operation::GenerateContent | Operation::StreamGenerateContent, p) => {
let kind = match p {
P::OpenAi => ContentGenerationKind::OpenAiResponses,
P::Claude => ContentGenerationKind::ClaudeMessages,
P::Gemini => ContentGenerationKind::GeminiGenerateContent,
};
content_target(kind, model, stream)
}
}
}
fn content_target(kind: ContentGenerationKind, model: &str, stream: bool) -> RequestTarget {
use ContentGenerationKind as K;
match kind {
K::OpenAiChatCompletions => RequestTarget::post("/v1/chat/completions"),
K::OpenAiResponses => RequestTarget::post("/v1/responses"),
K::OpenAiResponsesWebSocket => RequestTarget::get("/v1/responses"),
K::ClaudeMessages => RequestTarget::post("/v1/messages"),
K::GeminiGenerateContent => {
let verb = if stream {
"streamGenerateContent"
} else {
"generateContent"
};
RequestTarget {
method: HttpMethod::Post,
path: format!("/v1beta/models/{model}:{verb}"),
query: stream.then(|| "alt=sse".to_owned()),
}
}
}
}