use axum::http::{request::Parts, HeaderMap, Method};
pub const OPENAI_BASE: &str = "https://api.openai.com";
pub const GOOGLE_BASE: &str = "https://generativelanguage.googleapis.com";
pub const OLLAMA_BASE: &str = "http://127.0.0.1:11434";
pub const CHATGPT_BASE: &str = "https://chatgpt.com/backend-api/codex";
const CHATGPT_ACCOUNT_HEADER: &str = "chatgpt-account-id";
const ORIGINATOR_HEADER: &str = "originator";
const CODEX_ORIGINATOR_PREFIX: &str = "codex";
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AuthMode {
ChatGptSubscription,
Platform,
}
impl AuthMode {
pub fn resolve(headers: &HeaderMap) -> Self {
if headers.contains_key(CHATGPT_ACCOUNT_HEADER) {
Self::ChatGptSubscription
} else {
Self::Platform
}
}
}
fn ollama_native_route(path: &str) -> Option<&'static str> {
let path = path
.strip_suffix('/')
.filter(|p| !p.is_empty())
.unwrap_or(path);
if path.ends_with("/chat/completions") {
return None;
}
const NATIVE: &[&str] = &[
"/api/chat",
"/api/generate",
"/api/embed",
"/api/embeddings",
"/api/tags",
"/api/show",
"/api/ps",
"/api/version",
];
NATIVE.iter().copied().find(|r| path.ends_with(r))
}
const OLLAMA_NATIVE_TURNS: &[&str] = &["/api/chat", "/api/generate"];
fn is_codex_request(headers: &HeaderMap) -> bool {
headers.contains_key(CHATGPT_ACCOUNT_HEADER)
|| headers
.get(ORIGINATOR_HEADER)
.and_then(|v| v.to_str().ok())
.is_some_and(|v| v.starts_with(CODEX_ORIGINATOR_PREFIX))
}
const MAX_DECLARED_SESSION_ID: usize = 128;
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WireFormat {
AnthropicMessages,
OpenAiResponses,
OpenAiChatCompletions,
GoogleGenerateContent,
OllamaNative,
Unknown,
}
impl WireFormat {
pub const ALL: [WireFormat; 6] = [
Self::AnthropicMessages,
Self::OpenAiResponses,
Self::OpenAiChatCompletions,
Self::GoogleGenerateContent,
Self::OllamaNative,
Self::Unknown,
];
pub fn resolve(parts: &Parts) -> Self {
if parts.method != Method::POST {
return Self::Unknown;
}
if ollama_native_route(parts.uri.path()).is_some_and(|r| OLLAMA_NATIVE_TURNS.contains(&r)) {
return Self::OllamaNative;
}
let path = parts.uri.path();
let path = path
.strip_suffix('/')
.filter(|p| !p.is_empty())
.unwrap_or(path);
match path {
"/v1/messages" => Self::AnthropicMessages,
"/v1/responses" => Self::OpenAiResponses,
_ if path.ends_with("/chat/completions") => Self::OpenAiChatCompletions,
_ => {
if path.ends_with(":generateContent") || path.ends_with(":streamGenerateContent") {
Self::GoogleGenerateContent
} else {
Self::Unknown
}
}
}
}
pub fn resolve_upstream(parts: &Parts) -> Self {
match Self::resolve(parts) {
Self::Unknown if ollama_native_route(parts.uri.path()).is_some() => Self::OllamaNative,
Self::Unknown if is_codex_request(&parts.headers) => Self::OpenAiResponses,
resolved => resolved,
}
}
pub fn resolve_with_family(parts: &Parts, family: Option<Self>) -> Self {
match (Self::resolve(parts), family) {
(Self::Unknown, Some(family))
if parts.method == Method::POST && family.is_turn_route(parts.uri.path()) =>
{
family
}
(resolved, _) => resolved,
}
}
fn is_turn_route(self, path: &str) -> bool {
let path = path
.strip_suffix('/')
.filter(|p| !p.is_empty())
.unwrap_or(path);
match self {
Self::AnthropicMessages => path.ends_with("/messages"),
Self::OpenAiResponses => path.ends_with("/responses"),
Self::OpenAiChatCompletions => path.ends_with("/chat/completions"),
Self::GoogleGenerateContent => {
path.ends_with(":generateContent") || path.ends_with(":streamGenerateContent")
}
Self::OllamaNative => OLLAMA_NATIVE_TURNS.iter().any(|r| path.ends_with(r)),
Self::Unknown => false,
}
}
pub fn is_captured(self) -> bool {
match self {
Self::AnthropicMessages => true,
Self::OpenAiResponses => true,
Self::OpenAiChatCompletions => true,
Self::GoogleGenerateContent => true,
Self::OllamaNative => true,
Self::Unknown => false,
}
}
pub fn as_str(self) -> &'static str {
match self {
Self::AnthropicMessages => "anthropic-messages",
Self::OpenAiResponses => "openai-responses",
Self::OpenAiChatCompletions => "openai-chat-completions",
Self::GoogleGenerateContent => "google-generate-content",
Self::OllamaNative => "ollama-native",
Self::Unknown => "unknown",
}
}
pub fn provider(self) -> &'static str {
match self {
Self::AnthropicMessages => "anthropic",
Self::OpenAiResponses => "openai",
Self::OpenAiChatCompletions => "openai-compatible",
Self::GoogleGenerateContent => "google",
Self::OllamaNative => "ollama",
Self::Unknown => "unknown",
}
}
pub fn default_upstream(self) -> &'static str {
match self {
Self::AnthropicMessages => super::ANTHROPIC_BASE,
Self::OpenAiResponses => OPENAI_BASE,
Self::OpenAiChatCompletions => OPENAI_BASE,
Self::GoogleGenerateContent => GOOGLE_BASE,
Self::OllamaNative => OLLAMA_BASE,
Self::Unknown => super::ANTHROPIC_BASE,
}
}
pub fn has_decoder(self) -> bool {
matches!(
self,
Self::AnthropicMessages
| Self::OpenAiResponses
| Self::OpenAiChatCompletions
| Self::GoogleGenerateContent
| Self::OllamaNative
)
}
pub fn transforms_apply(self) -> bool {
matches!(self, Self::AnthropicMessages)
}
}
pub fn declared_session_id(fmt: WireFormat, body: &serde_json::Value) -> Option<String> {
match fmt {
WireFormat::OllamaNative => None,
WireFormat::AnthropicMessages => anthropic_declared_session_id(body),
WireFormat::OpenAiResponses => codex_declared_session_id(body),
WireFormat::OpenAiChatCompletions => None,
WireFormat::GoogleGenerateContent => None,
WireFormat::Unknown => None,
}
}
fn usable_session_id(sid: &str) -> Option<String> {
let sid = sid.trim();
if sid.is_empty() || sid.len() > MAX_DECLARED_SESSION_ID {
return None;
}
if sid.chars().any(char::is_control) {
return None;
}
Some(sid.to_string())
}
fn anthropic_declared_session_id(body: &serde_json::Value) -> Option<String> {
let raw = body.get("metadata")?.get("user_id")?.as_str()?;
if raw.len() > 4096 {
return None;
}
let parsed: serde_json::Value = serde_json::from_str(raw).ok()?;
usable_session_id(parsed.get("session_id")?.as_str()?)
}
fn codex_declared_session_id(body: &serde_json::Value) -> Option<String> {
usable_session_id(body.get("client_metadata")?.get("session_id")?.as_str()?)
}
pub struct ConversationView<'a> {
pub user_turns: Vec<&'a serde_json::Value>,
pub content_field: &'static str,
pub text_block_type: Option<&'static str>,
pub tool_result_ids: Vec<String>,
}
pub fn conversation_view(
fmt: WireFormat,
body: &serde_json::Value,
) -> Option<ConversationView<'_>> {
match fmt {
WireFormat::AnthropicMessages => anthropic_conversation_view(body),
WireFormat::OpenAiResponses => responses_conversation_view(body),
WireFormat::OpenAiChatCompletions => chat_completions_conversation_view(body),
WireFormat::GoogleGenerateContent => google_conversation_view(body),
WireFormat::OllamaNative => None,
WireFormat::Unknown => None,
}
}
fn anthropic_conversation_view(body: &serde_json::Value) -> Option<ConversationView<'_>> {
let messages = body.get("messages")?.as_array()?;
let user_turns: Vec<&serde_json::Value> = messages
.iter()
.filter(|m| m.get("role").and_then(|r| r.as_str()) == Some("user"))
.collect();
let mut tool_result_ids = Vec::new();
if let Some(blocks) = user_turns
.last()
.and_then(|last| last.get("content"))
.and_then(|c| c.as_array())
{
for b in blocks {
if b.get("type").and_then(|t| t.as_str()) != Some("tool_result") {
continue;
}
if let Some(id) = b.get("tool_use_id").and_then(|v| v.as_str()) {
if !id.is_empty() {
tool_result_ids.push(id.to_string());
}
}
}
}
Some(ConversationView {
user_turns,
content_field: "content",
text_block_type: Some("text"),
tool_result_ids,
})
}
fn chat_completions_conversation_view(body: &serde_json::Value) -> Option<ConversationView<'_>> {
let messages = body.get("messages")?.as_array()?;
let user_turns: Vec<&serde_json::Value> = messages
.iter()
.filter(|m| m.get("role").and_then(|r| r.as_str()) == Some("user"))
.collect();
let mut tool_result_ids: Vec<String> = Vec::new();
for m in messages.iter().rev() {
if tool_result_ids.len() >= super::session::RECENT_TOOL_USE_IDS {
break;
}
let Some(id) = m.get("tool_call_id").and_then(|v| v.as_str()) else {
continue;
};
if id.is_empty() || tool_result_ids.iter().any(|k| k == id) {
continue;
}
tool_result_ids.push(id.to_string());
}
Some(ConversationView {
user_turns,
content_field: "content",
text_block_type: Some("text"),
tool_result_ids,
})
}
fn responses_conversation_view(body: &serde_json::Value) -> Option<ConversationView<'_>> {
let items = body.get("input")?.as_array()?;
let user_turns: Vec<&serde_json::Value> = items
.iter()
.filter(|m| {
matches!(
m.get("type").and_then(|t| t.as_str()),
None | Some("message")
) && m.get("role").and_then(|r| r.as_str()) == Some("user")
})
.collect();
let mut tool_result_ids: Vec<String> = Vec::new();
for item in items.iter().rev() {
if tool_result_ids.len() >= super::session::RECENT_TOOL_USE_IDS {
break;
}
let Some(id) = item.get("call_id").and_then(|v| v.as_str()) else {
continue;
};
if id.is_empty() || tool_result_ids.iter().any(|k| k == id) {
continue;
}
tool_result_ids.push(id.to_string());
}
Some(ConversationView {
user_turns,
content_field: "content",
text_block_type: Some("input_text"),
tool_result_ids,
})
}
fn google_conversation_view(body: &serde_json::Value) -> Option<ConversationView<'_>> {
let contents = body.get("contents")?.as_array()?;
let user_turns: Vec<&serde_json::Value> = contents
.iter()
.filter(|m| m.get("role").and_then(|r| r.as_str()) == Some("user"))
.collect();
let mut tool_result_ids: Vec<String> = Vec::new();
'outer: for turn in contents.iter().rev() {
let Some(parts) = turn.get("parts").and_then(|p| p.as_array()) else {
continue;
};
for part in parts.iter().rev() {
if tool_result_ids.len() >= super::session::RECENT_TOOL_USE_IDS {
break 'outer;
}
let Some(id) = part
.get("functionResponse")
.and_then(|f| f.get("id"))
.and_then(|v| v.as_str())
else {
continue;
};
if id.is_empty() || tool_result_ids.iter().any(|k| k == id) {
continue;
}
tool_result_ids.push(id.to_string());
}
}
Some(ConversationView {
user_turns,
content_field: "parts",
text_block_type: None,
tool_result_ids,
})
}
#[cfg(test)]
mod tests {
use super::*;
use axum::http::Request;
fn parts_of(method: Method, uri: &str) -> Parts {
let req = Request::builder()
.method(method)
.uri(uri)
.body(())
.expect("build request");
req.into_parts().0
}
#[test]
fn an_endpoint_family_measures_a_prefixed_turn() {
let anthropic = Some(WireFormat::AnthropicMessages);
for uri in [
"/anthropic/v1/messages",
"/anthropic/messages",
"/gw/v1/messages/",
] {
assert_eq!(
WireFormat::resolve_with_family(&parts_of(Method::POST, uri), anthropic),
WireFormat::AnthropicMessages,
"{uri}"
);
}
assert_eq!(
WireFormat::resolve_with_family(
&parts_of(Method::POST, "/openai/v1/responses"),
Some(WireFormat::OpenAiResponses)
),
WireFormat::OpenAiResponses
);
}
#[test]
fn an_endpoint_family_is_a_hint_not_an_override() {
let anthropic = Some(WireFormat::AnthropicMessages);
for (method, uri) in [
(Method::POST, "/anthropic/v1/messages/count_tokens"),
(Method::GET, "/anthropic/v1/messages"),
(Method::GET, "/anthropic/v1/models"),
(Method::POST, "/anthropic/v1/batches"),
] {
assert_eq!(
WireFormat::resolve_with_family(&parts_of(method.clone(), uri), anthropic),
WireFormat::Unknown,
"{method} {uri}"
);
}
assert_eq!(
WireFormat::resolve_with_family(
&parts_of(Method::POST, "/v1/chat/completions"),
anthropic
),
WireFormat::OpenAiChatCompletions
);
assert_eq!(
WireFormat::resolve_with_family(
&parts_of(Method::POST, "/anthropic/v1/messages"),
None
),
WireFormat::Unknown
);
assert_eq!(
WireFormat::resolve_with_family(
&parts_of(Method::POST, "/ollama/api/show"),
Some(WireFormat::OllamaNative)
),
WireFormat::Unknown
);
}
fn parts_with(method: Method, uri: &str, headers: &[(&str, &str)]) -> Parts {
let mut req = Request::builder().method(method).uri(uri);
for (k, v) in headers {
req = req.header(*k, *v);
}
req.body(()).expect("build request").into_parts().0
}
#[test]
fn ollama_native_routes_do_not_leak_to_anthropic() {
for (method, uri) in [
(Method::POST, "/api/chat"),
(Method::POST, "/api/generate"),
(Method::POST, "/api/embed"),
(Method::POST, "/api/show"),
(Method::GET, "/api/tags"),
(Method::GET, "/api/version"),
(Method::POST, "/ollama/api/chat"),
(Method::POST, "/api/chat/"),
] {
assert_eq!(
WireFormat::resolve_upstream(&parts_of(method.clone(), uri)),
WireFormat::OllamaNative,
"{method} {uri}"
);
}
assert_eq!(
WireFormat::OllamaNative.default_upstream(),
OLLAMA_BASE,
"a local model server has no cloud origin to fall back to"
);
assert_ne!(
WireFormat::OllamaNative.default_upstream(),
super::super::ANTHROPIC_BASE,
"the whole point: this must not be Anthropic's"
);
}
#[test]
fn only_ollama_native_turns_are_captured() {
for (method, uri) in [
(Method::POST, "/api/chat"),
(Method::POST, "/api/generate"),
(Method::POST, "/ollama/api/chat/"),
] {
let parts = parts_of(method.clone(), uri);
assert_eq!(
WireFormat::resolve(&parts),
WireFormat::OllamaNative,
"{method} {uri}"
);
assert!(WireFormat::resolve(&parts).is_captured(), "{method} {uri}");
}
for (method, uri) in [
(Method::GET, "/api/tags"),
(Method::GET, "/api/version"),
(Method::GET, "/api/ps"),
(Method::POST, "/api/show"),
(Method::POST, "/api/embed"),
(Method::POST, "/api/embeddings"),
(Method::GET, "/api/chat"),
] {
assert!(
!WireFormat::resolve(&parts_of(method.clone(), uri)).is_captured(),
"{method} {uri} is not a model call"
);
}
}
#[test]
fn ollama_native_never_steals_chat_completions() {
for uri in [
"/v1/chat/completions",
"/api/v1/chat/completions",
"/api/chat/completions",
] {
assert_eq!(
WireFormat::resolve(&parts_of(Method::POST, uri)),
WireFormat::OpenAiChatCompletions,
"{uri}"
);
}
}
#[test]
fn auth_mode_reads_the_account_header_and_nothing_else() {
assert_eq!(
AuthMode::resolve(
&parts_with(
Method::POST,
"/v1/responses",
&[("chatgpt-account-id", "ef1a0c98-317c-4a79-9ed0-9e75361027ed")],
)
.headers
),
AuthMode::ChatGptSubscription
);
assert_eq!(
AuthMode::resolve(
&parts_with(
Method::POST,
"/v1/responses",
&[("authorization", "Bearer sk-proj-abc")],
)
.headers
),
AuthMode::Platform
);
assert_eq!(
AuthMode::resolve(&parts_of(Method::POST, "/v1/responses").headers),
AuthMode::Platform
);
}
#[test]
fn an_uncaptured_codex_route_follows_codex_upstream() {
let models = parts_with(
Method::GET,
"/v1/models?client_version=0.150.1",
&[("originator", "codex_exec")],
);
assert_eq!(WireFormat::resolve(&models), WireFormat::Unknown);
assert_eq!(
WireFormat::resolve_upstream(&models),
WireFormat::OpenAiResponses
);
let with_account = parts_with(
Method::GET,
"/v1/models",
&[("chatgpt-account-id", "ef1a0c98")],
);
assert_eq!(
WireFormat::resolve_upstream(&with_account),
WireFormat::OpenAiResponses
);
}
#[test]
fn an_unmarked_uncaptured_route_still_resolves_unknown() {
for headers in [
&[][..],
&[("authorization", "Bearer sk-ant-oat01-abc")][..],
&[("x-api-key", "sk-ant-api03-abc")][..],
&[("originator", "not-codex")][..],
] {
let parts = parts_with(Method::GET, "/v1/models", headers);
assert_eq!(
WireFormat::resolve_upstream(&parts),
WireFormat::Unknown,
"unmarked route promoted with headers {headers:?}"
);
}
}
#[test]
fn promotion_never_widens_what_is_captured() {
let promoted = parts_with(Method::GET, "/v1/models", &[("originator", "codex_cli_rs")]);
assert!(!WireFormat::resolve(&promoted).is_captured());
let turn = parts_with(
Method::POST,
"/v1/responses",
&[("originator", "codex_exec")],
);
assert_eq!(
WireFormat::resolve_upstream(&turn),
WireFormat::resolve(&turn)
);
}
#[test]
fn resolve_keys_on_route_and_method_only() {
assert_eq!(
WireFormat::resolve(&parts_of(Method::POST, "/v1/messages")),
WireFormat::AnthropicMessages
);
assert_eq!(
WireFormat::resolve(&parts_of(Method::POST, "/v1/responses")),
WireFormat::OpenAiResponses
);
assert_eq!(
WireFormat::resolve(&parts_of(Method::GET, "/v1/responses")),
WireFormat::Unknown
);
assert_eq!(
WireFormat::resolve(&parts_of(Method::POST, "/v1/messages/count_tokens")),
WireFormat::Unknown
);
assert_eq!(
WireFormat::resolve(&parts_of(Method::POST, "/v1/messages/extra")),
WireFormat::Unknown
);
assert_eq!(
WireFormat::resolve(&parts_of(
Method::GET,
"/v1beta/models/gemini-2.5-pro:generateContent"
)),
WireFormat::Unknown
);
}
#[test]
fn all_names_every_variant_exactly_once() {
let names: Vec<_> = WireFormat::ALL.iter().map(|f| f.as_str()).collect();
assert_eq!(
names,
vec![
"anthropic-messages",
"openai-responses",
"openai-chat-completions",
"google-generate-content",
"ollama-native",
"unknown"
]
);
}
#[test]
fn unknown_forwards_to_the_anthropic_base_as_it_does_today() {
assert_eq!(
WireFormat::Unknown.default_upstream(),
crate::model_relay::ANTHROPIC_BASE
);
assert_eq!(WireFormat::OpenAiResponses.default_upstream(), OPENAI_BASE);
assert_eq!(
WireFormat::OpenAiChatCompletions.default_upstream(),
OPENAI_BASE
);
assert_eq!(
WireFormat::GoogleGenerateContent.default_upstream(),
GOOGLE_BASE
);
assert_eq!(GOOGLE_BASE, "https://generativelanguage.googleapis.com");
}
#[test]
fn chat_completions_resolves_through_every_real_world_path_shape() {
for path in [
"/v1/chat/completions", "/v1/chat/completions/", "/chat/completions", "/v2/chat/completions", "/gateway/openai/v1/chat/completions", "/openai/deployments/gpt-4o/chat/completions", ] {
assert_eq!(
WireFormat::resolve(&parts_of(Method::POST, path)),
WireFormat::OpenAiChatCompletions,
"{path} must not fall through to Unknown — its upstream is Anthropic's"
);
}
}
#[test]
fn widening_chat_completions_did_not_capture_the_shipped_routes() {
assert_eq!(
WireFormat::resolve(&parts_of(Method::POST, "/v1/messages")),
WireFormat::AnthropicMessages
);
assert_eq!(
WireFormat::resolve(&parts_of(Method::POST, "/v1/responses")),
WireFormat::OpenAiResponses
);
assert_eq!(
WireFormat::resolve(&parts_of(Method::POST, "/v1/chat/completions/extra")),
WireFormat::Unknown,
"the segment must END the path, not merely appear in it"
);
assert_eq!(
WireFormat::resolve(&parts_of(Method::POST, "/")),
WireFormat::Unknown
);
}
#[test]
fn resolve_matches_chat_completions_exactly() {
assert_eq!(
WireFormat::resolve(&parts_of(Method::POST, "/v1/chat/completions")),
WireFormat::OpenAiChatCompletions
);
}
#[test]
fn resolve_matches_both_google_suffixes() {
for path in [
"/v1beta/models/gemini-2.5-pro:generateContent",
"/v1beta/models/gemini-2.5-pro:streamGenerateContent?alt=sse",
"/v1/projects/p/locations/us-central1/publishers/google/models/\
gemini-2.5-pro:generateContent",
"/v1/projects/p/locations/us-central1/publishers/google/models/\
gemini-2.5-pro:streamGenerateContent",
] {
assert_eq!(
WireFormat::resolve(&parts_of(Method::POST, path)),
WireFormat::GoogleGenerateContent,
"route must resolve to the Google format: {path}"
);
}
}
#[test]
fn is_captured_pins_every_arm() {
assert!(WireFormat::AnthropicMessages.is_captured());
assert!(WireFormat::OpenAiResponses.is_captured());
assert!(WireFormat::OpenAiChatCompletions.is_captured());
assert!(WireFormat::GoogleGenerateContent.is_captured());
assert!(!WireFormat::Unknown.is_captured());
}
#[test]
fn provider_never_says_openai_for_chat_completions() {
assert_eq!(
WireFormat::OpenAiChatCompletions.provider(),
"openai-compatible"
);
assert_eq!(WireFormat::GoogleGenerateContent.provider(), "google");
assert_eq!(WireFormat::OpenAiResponses.provider(), "openai");
assert_eq!(WireFormat::AnthropicMessages.provider(), "anthropic");
}
#[test]
fn chat_completions_is_captured_and_now_decoded() {
assert!(WireFormat::OpenAiChatCompletions.is_captured());
assert!(
WireFormat::OpenAiChatCompletions.has_decoder(),
"the decoder landed — see has_decoder_true_for_chat_completions"
);
}
#[test]
fn has_decoder_true_for_chat_completions() {
assert!(WireFormat::OpenAiChatCompletions.has_decoder());
}
#[test]
fn google_is_captured_and_now_decoded() {
assert!(WireFormat::GoogleGenerateContent.is_captured());
assert!(
WireFormat::GoogleGenerateContent.has_decoder(),
"the decoder landed — see has_decoder_true_for_google"
);
}
#[test]
fn has_decoder_true_for_google() {
assert!(WireFormat::GoogleGenerateContent.has_decoder());
}
#[test]
fn unknown_format_declares_no_session_and_no_view() {
let google = serde_json::json!({
"contents": [{"role": "user", "parts": [{"text": "build the parser"}]}]
});
let anthropic = serde_json::json!({
"messages": [{"role": "user", "content": "build the parser"}],
"metadata": {"user_id": "{\"session_id\":\"5c7d9833\"}"}
});
for body in [&google, &anthropic] {
assert_eq!(declared_session_id(WireFormat::Unknown, body), None);
assert!(conversation_view(WireFormat::Unknown, body).is_none());
}
}
#[test]
fn google_declares_no_session_id() {
let body = serde_json::json!({
"contents": [{"role": "user", "parts": [{"text": "build the parser"}]}],
"systemInstruction": {"parts": [{"text": "You are Cline."}]},
"labels": {"team": "bea"}
});
assert_eq!(
declared_session_id(WireFormat::GoogleGenerateContent, &body),
None
);
}
fn google_turn() -> serde_json::Value {
serde_json::json!({
"systemInstruction": {"parts": [{"text": "You are Cline."}]},
"contents": [
{"role": "user", "parts": [{"text": "build the parser"}]},
{"role": "model", "parts": [
{"functionCall": {"id": "fc_a", "name": "read_file", "args": {}}}
]},
{"role": "user", "parts": [
{"functionResponse": {"id": "fc_a", "name": "read_file",
"response": {"content": "fn main() {}"}}},
{"text": "now fix it"}
]}
],
"generationConfig": {"temperature": 0}
})
}
#[test]
fn google_view_reads_contents_and_parts() {
let body = google_turn();
let view = conversation_view(WireFormat::GoogleGenerateContent, &body).expect("a view");
assert_eq!(
view.user_turns.len(),
2,
"the `model` turn is not a user turn, and `systemInstruction` is not a turn at all"
);
assert_eq!(view.user_turns[0]["parts"][0]["text"], "build the parser");
assert_eq!(
view.content_field, "parts",
"a turn keeps its content under `parts`, not `content`"
);
assert_eq!(
view.text_block_type, None,
"a Gemini Part is a oneof with no `type` key — matching one reads every turn as empty"
);
assert_eq!(
view.tool_result_ids,
vec!["fc_a".to_string()],
"read from the NESTED functionResponse.id, not from a top-level item"
);
}
#[test]
fn google_view_without_ids_is_still_a_view() {
let body = serde_json::json!({"contents": [
{"role": "user", "parts": [{"text": "build the parser"}]},
{"role": "model", "parts": [{"text": "on it"}]},
{"role": "user", "parts": [
{"functionResponse": {"name": "read_file", "response": {}}},
{"text": "now fix it"}
]}
]});
let view = conversation_view(WireFormat::GoogleGenerateContent, &body).expect("a view");
assert_eq!(view.user_turns.len(), 2);
assert!(view.tool_result_ids.is_empty());
}
#[test]
fn google_view_is_none_without_contents() {
let body = serde_json::json!({"messages": [{"role": "user", "content": "hi"}]});
assert!(conversation_view(WireFormat::GoogleGenerateContent, &body).is_none());
}
fn chat_turn() -> serde_json::Value {
serde_json::json!({
"model": "qwen2.5-coder:14b",
"user": "cline-user-42",
"messages": [
{"role": "system", "content": "You are Cline."},
{"role": "user", "content": "build the parser"},
{"role": "assistant", "content": null, "tool_calls": [
{"id": "call_a", "type": "function",
"function": {"name": "read_file", "arguments": "{}"}}
]},
{"role": "tool", "tool_call_id": "call_a", "content": "fn main() {}"},
{"role": "user", "content": [{"type": "text", "text": "now fix it"}]}
],
"stream": true,
"stream_options": {"include_usage": true}
})
}
#[test]
fn chat_completions_declares_no_session_id() {
let body = chat_turn();
assert_eq!(
body["user"], "cline-user-42",
"the fixture must actually carry the tempting field, or this proves nothing"
);
assert_eq!(
declared_session_id(WireFormat::OpenAiChatCompletions, &body),
None,
"`user` is an opaque caller string, never a session id"
);
}
#[test]
fn chat_completions_view_reads_messages_and_top_level_tool_results() {
let body = chat_turn();
let view = conversation_view(WireFormat::OpenAiChatCompletions, &body).expect("a view");
assert_eq!(
view.user_turns.len(),
2,
"the system, assistant and tool messages are not user turns"
);
assert_eq!(view.user_turns[0]["content"], "build the parser");
assert_eq!(
view.content_field, "content",
"same content field as the Messages API"
);
assert_eq!(
view.text_block_type,
Some("text"),
"same block type as the Messages API"
);
assert_eq!(
view.tool_result_ids,
vec!["call_a".to_string()],
"read from the top-level `role: \"tool\"` message's `tool_call_id`"
);
}
#[test]
fn chat_completions_tool_ids_are_newest_first_deduped_and_bounded() {
let mut messages = vec![serde_json::json!({"role": "user", "content": "go"})];
for i in 0..(super::super::session::RECENT_TOOL_USE_IDS + 3) {
messages.push(serde_json::json!({
"role": "assistant",
"tool_calls": [{"id": format!("call_{i}"), "type": "function",
"function": {"name": "f", "arguments": "{}"}}]
}));
messages.push(serde_json::json!({
"role": "tool", "tool_call_id": format!("call_{i}"), "content": "ok"
}));
}
let body = serde_json::json!({"messages": messages});
let view = conversation_view(WireFormat::OpenAiChatCompletions, &body).expect("a view");
assert_eq!(
view.tool_result_ids.len(),
super::super::session::RECENT_TOOL_USE_IDS
);
let newest = super::super::session::RECENT_TOOL_USE_IDS + 2;
assert_eq!(
view.tool_result_ids[0],
format!("call_{newest}"),
"newest first — the oldest ids have nothing left in the registry to match"
);
let mut sorted = view.tool_result_ids.clone();
sorted.sort();
sorted.dedup();
assert_eq!(sorted.len(), view.tool_result_ids.len(), "no id twice");
}
fn codex_turn() -> serde_json::Value {
serde_json::json!({
"model": "gpt-5-codex",
"input": [
{"type": "message", "id": "msg_1", "role": "developer",
"content": [{"type": "input_text", "text": "<skills_instructions>…"}]},
{"type": "message", "id": "msg_2", "role": "user",
"content": [{"type": "input_text", "text": "<environment_context>\n <cwd>/repo</cwd>\n</environment_context>"}]},
{"type": "message", "id": "msg_3", "role": "user",
"content": [{"type": "input_text", "text": "build the parser"}]},
{"type": "function_call", "id": "fc_1", "call_id": "call_aaa",
"name": "shell", "arguments": "{}"},
{"type": "function_call_output", "id": "fco_1", "call_id": "call_aaa",
"output": "ok"}
]
})
}
#[test]
fn the_responses_view_reads_input_and_leaves_the_preamble_alone() {
let body = codex_turn();
let view = conversation_view(WireFormat::OpenAiResponses, &body).expect("a view");
assert_eq!(view.user_turns.len(), 2);
assert_eq!(
view.user_turns[1]["content"][0]["text"], "build the parser",
"the typed prompt is the last user turn"
);
assert_eq!(view.content_field, "content");
assert_eq!(view.text_block_type, Some("input_text"));
assert_eq!(view.tool_result_ids, vec!["call_aaa".to_string()]);
}
#[test]
fn responses_call_ids_are_newest_first_deduped_and_bounded() {
let mut items = Vec::new();
for i in 0..40 {
items.push(serde_json::json!({
"type": "function_call", "call_id": format!("call_{i:02}"), "name": "shell"
}));
items.push(serde_json::json!({
"type": "function_call_output", "call_id": format!("call_{i:02}"), "output": "ok"
}));
}
let body = serde_json::json!({ "input": items });
let view = conversation_view(WireFormat::OpenAiResponses, &body).expect("a view");
assert_eq!(
view.tool_result_ids.len(),
super::super::session::RECENT_TOOL_USE_IDS
);
assert_eq!(
view.tool_result_ids[0], "call_39",
"newest first — the request's newest ids are the ones still in the registry"
);
assert_eq!(view.tool_result_ids[7], "call_32");
}
#[test]
fn every_responses_item_that_carries_an_id_names_it_call_id() {
let body = serde_json::json!({"input": [
{"type": "custom_tool_call_output", "call_id": "call_custom", "output": "ok"},
{"type": "mcp_tool_call_output", "call_id": "call_mcp", "output": {}},
{"type": "tool_search_output", "call_id": "call_search", "status": "ok"},
{"type": "a_variant_that_does_not_exist_yet", "call_id": "call_future"}
]});
let view = conversation_view(WireFormat::OpenAiResponses, &body).expect("a view");
assert_eq!(
view.tool_result_ids,
vec!["call_future", "call_search", "call_mcp", "call_custom"]
);
}
#[test]
fn each_format_reads_only_its_own_body_shape() {
let codex = codex_turn();
let claude = serde_json::json!({"messages": [
{"role": "user", "content": [{"type": "text", "text": "build the parser"}]}
]});
assert!(conversation_view(WireFormat::AnthropicMessages, &codex).is_none());
assert!(conversation_view(WireFormat::OpenAiResponses, &claude).is_none());
assert!(conversation_view(WireFormat::Unknown, &codex).is_none());
assert!(conversation_view(WireFormat::Unknown, &claude).is_none());
}
#[test]
fn the_messages_view_is_unchanged_by_the_move() {
let body = serde_json::json!({"messages": [
{"role": "user", "content": [{"type": "text", "text": "build the parser"}]},
{"role": "assistant", "content": [
{"type": "tool_use", "id": "toolu_old", "name": "Bash", "input": {}}
]},
{"role": "user", "content": [
{"type": "tool_result", "tool_use_id": "toolu_old", "content": "ok"}
]},
{"role": "assistant", "content": [
{"type": "tool_use", "id": "toolu_new", "name": "Bash", "input": {}}
]},
{"role": "user", "content": [
{"type": "tool_result", "tool_use_id": "toolu_new", "content": "ok"},
{"type": "text", "text": "and now the lexer"}
]}
]});
let view = conversation_view(WireFormat::AnthropicMessages, &body).expect("a view");
assert_eq!(view.user_turns.len(), 3);
assert_eq!(view.content_field, "content");
assert_eq!(view.text_block_type, Some("text"));
assert_eq!(view.tool_result_ids, vec!["toolu_new".to_string()]);
}
#[test]
fn each_format_reads_only_its_own_declaration() {
let claude = serde_json::json!({
"metadata": {"user_id": r#"{"session_id":"5c7d9833"}"#}
});
let codex = serde_json::json!({
"client_metadata": {
"session_id": "01a07677-988f-7901-af48-225c7386aabc",
"thread_id": "01a07677-988f-7901-af48-225c7386aabc",
"turn_id": "01a07677-7334-74a2-be87-bdd0bbe8fb77",
"x-codex-installation-id": "5532dc79-4512-4bae-ab73-0dd3e814d329"
}
});
assert_eq!(
declared_session_id(WireFormat::AnthropicMessages, &claude).as_deref(),
Some("5c7d9833")
);
assert_eq!(
declared_session_id(WireFormat::OpenAiResponses, &claude),
None
);
assert_eq!(
declared_session_id(WireFormat::OpenAiResponses, &codex).as_deref(),
Some("01a07677-988f-7901-af48-225c7386aabc")
);
assert_eq!(
declared_session_id(WireFormat::AnthropicMessages, &codex),
None
);
assert_eq!(declared_session_id(WireFormat::Unknown, &claude), None);
assert_eq!(declared_session_id(WireFormat::Unknown, &codex), None);
}
#[test]
fn codex_reads_session_id_and_not_a_neighbouring_field() {
let body = serde_json::json!({
"client_metadata": {"session_id": "sess-real", "turn_id": "turn-other",
"thread_id": "thread-other"}
});
assert_eq!(
declared_session_id(WireFormat::OpenAiResponses, &body).as_deref(),
Some("sess-real")
);
let cache_key_only = serde_json::json!({
"prompt_cache_key": "01a07677-988f-7901-af48-225c7386aabc"
});
assert_eq!(
declared_session_id(WireFormat::OpenAiResponses, &cache_key_only),
None
);
}
#[test]
fn a_codex_declaration_that_is_not_usable_is_silently_ignored() {
let cases = [
serde_json::json!({}), serde_json::json!({"client_metadata": {}}), serde_json::json!({"client_metadata": {"session_id": 42}}), serde_json::json!({"client_metadata": {"session_id": ""}}), serde_json::json!({"client_metadata": {"session_id": " "}}), serde_json::json!({"client_metadata": {"session_id": "a\u{0000}b"}}), serde_json::json!({"client_metadata": {"session_id": "x".repeat(500)}}), ];
for body in cases {
assert_eq!(
declared_session_id(WireFormat::OpenAiResponses, &body),
None,
"must not trust {body}"
);
}
}
}