use std::collections::BTreeMap; use serde_json::{json, Value}; use uuid::Uuid; use super::headers::{declared_connection_header_names, remove_declared_connection_headers}; use crate::rules::{ apply_local_body_rules_with_request_headers, apply_local_header_rules_with_request_headers, body_rules_are_locally_supported, header_rules_are_locally_supported, }; use crate::snapshot::GatewayProviderTransportSnapshot; use crate::url::build_passthrough_path_url; use crate::{ resolve_transport_profile, should_skip_upstream_passthrough_header, supports_local_oauth_request_auth_resolution, transport_profile_is_configured, transport_proxy_is_locally_supported, }; pub mod cascade; pub mod models; pub mod proto; pub const PROVIDER_TYPE: &str = "windsurf"; pub const WINDSURF_ENVELOPE_NAME: &str = "windsurf:GetChatMessage"; pub const GET_CHAT_MESSAGE_PATH: &str = "/exa.api_server_pb.ApiServerService/GetChatMessage"; const DEFAULT_IDE_VERSION: &str = "1.9600.41"; const PLACEHOLDER_API_KEY: &str = "__placeholder__"; pub fn is_windsurf_provider_transport(transport: &GatewayProviderTransportSnapshot) -> bool { transport .provider .provider_type .trim() .eq_ignore_ascii_case(PROVIDER_TYPE) } pub fn local_windsurf_request_transport_unsupported_reason_with_network( transport: &GatewayProviderTransportSnapshot, ) -> Option<&'static str> { if !transport.provider.is_active { return Some("provider_inactive"); } if !transport.endpoint.is_active { return Some("endpoint_inactive"); } if !transport.key.is_active { return Some("key_inactive"); } if !is_windsurf_provider_transport(transport) { return Some("transport_provider_type_unsupported"); } if !transport .endpoint .api_format .trim() .eq_ignore_ascii_case("openai:chat") { return Some("transport_api_format_mismatch"); } if !header_rules_are_locally_supported(transport.endpoint.header_rules.as_ref()) { return Some("transport_header_rules_unsupported"); } if !body_rules_are_locally_supported(transport.endpoint.body_rules.as_ref()) { return Some("transport_body_rules_unsupported"); } if transport.key.decrypted_auth_config.is_some() && !supports_local_oauth_request_auth_resolution(transport) && !supports_local_windsurf_request_auth_resolution(transport) { return Some("transport_oauth_resolution_unsupported"); } if !transport_proxy_is_locally_supported(transport) { return Some("transport_proxy_unsupported"); } if transport_profile_is_configured(transport) && resolve_transport_profile(transport).is_none() { return Some("transport_profile_unsupported"); } None } pub fn supports_local_windsurf_request_auth_resolution( transport: &GatewayProviderTransportSnapshot, ) -> bool { resolve_windsurf_cascade_auth(transport).is_some() } pub fn resolve_windsurf_cascade_auth( transport: &GatewayProviderTransportSnapshot, ) -> Option<(String, String)> { if !is_windsurf_provider_transport(transport) { return None; } let auth_type = transport.key.auth_type.trim().to_ascii_lowercase(); if !matches!(auth_type.as_str(), "oauth" | "api_key" | "bearer") { return None; } let secret = transport.key.decrypted_api_key.trim(); if secret.is_empty() || secret == PLACEHOLDER_API_KEY { return None; } Some(("authorization".to_string(), format!("Bearer {secret}"))) } pub fn build_windsurf_cascade_upstream_url( upstream_base_url: &str, query: Option<&str>, ) -> Option { build_passthrough_path_url(upstream_base_url, GET_CHAT_MESSAGE_PATH, query, &[]) } pub fn build_windsurf_cascade_request_body( body_json: &Value, mapped_model: &str, auth_value: &str, body_rules: Option<&Value>, request_headers: Option<&http::HeaderMap>, upstream_is_stream: bool, ) -> Option { let mapped_model = mapped_model.trim(); if mapped_model.is_empty() { return None; } let messages = body_json.get("messages")?.as_array()?.clone(); if messages.is_empty() { return None; } let conversation_id = extract_conversation_id(body_json).unwrap_or_else(|| Uuid::new_v4().to_string()); let message_text = latest_message_snapshot_text(&messages).unwrap_or_else(|| "Continue.".to_string()); let mut provider_request_body = json!({ "metadata": windsurf_metadata_from_auth(auth_value), "model": mapped_model, "modelName": mapped_model, "stream": upstream_is_stream, "conversationId": conversation_id, "message": message_text, "messages": messages, }); if let Some(max_tokens) = body_json .get("max_tokens") .or_else(|| body_json.get("maxTokens")) { provider_request_body .as_object_mut()? .insert("maxTokens".to_string(), max_tokens.clone()); } if let Some(temperature) = body_json.get("temperature") { provider_request_body .as_object_mut()? .insert("temperature".to_string(), temperature.clone()); } if let Some(top_p) = body_json.get("top_p").or_else(|| body_json.get("topP")) { provider_request_body .as_object_mut()? .insert("topP".to_string(), top_p.clone()); } for field in [ "tools", "tool_choice", "toolChoice", "parallel_tool_calls", "response_format", ] { if let Some(value) = body_json.get(field) { provider_request_body .as_object_mut()? .insert(field.to_string(), value.clone()); } } if !apply_local_body_rules_with_request_headers( &mut provider_request_body, body_rules, Some(body_json), request_headers, ) { return None; } Some(provider_request_body) } pub fn build_windsurf_cascade_headers( headers: &http::HeaderMap, provider_request_body: &Value, original_request_body: &Value, header_rules: Option<&Value>, auth_header: &str, auth_value: &str, _upstream_is_stream: bool, ) -> Option> { let declared_connection_headers = declared_connection_header_names(headers, &BTreeMap::new()); let mut out = BTreeMap::new(); for (name, value) in headers { let Ok(value) = value.to_str() else { continue; }; let key = name.as_str().to_ascii_lowercase(); if should_skip_upstream_passthrough_header(&key) || declared_connection_headers.contains(&key) { continue; } let value = value.trim(); if !value.is_empty() { out.insert(key, value.to_string()); } } let auth_header = auth_header.trim().to_ascii_lowercase(); if !apply_local_header_rules_with_request_headers( &mut out, header_rules, &[ auth_header.as_str(), "content-type", "connect-protocol-version", ], provider_request_body, Some(original_request_body), Some(headers), ) { return None; } out.insert( "content-type".to_string(), "application/connect+json".to_string(), ); out.insert("connect-protocol-version".to_string(), "1".to_string()); out.insert( "user-agent".to_string(), format!("windsurf/{DEFAULT_IDE_VERSION}"), ); out.insert("accept".to_string(), "application/connect+json".to_string()); if !auth_header.is_empty() { out.insert(auth_header, auth_value.trim().to_string()); } remove_declared_connection_headers(&mut out, &declared_connection_headers); out.remove("content-length"); Some(out) } fn windsurf_metadata_from_auth(auth_value: &str) -> Value { json!({ "apiKey": auth_secret_from_header_value(auth_value), "ideName": "windsurf", "ideVersion": DEFAULT_IDE_VERSION, "extensionName": "windsurf", "extensionVersion": DEFAULT_IDE_VERSION, "locale": "en", }) } fn auth_secret_from_header_value(auth_value: &str) -> String { let value = auth_value.trim(); value .strip_prefix("Bearer ") .or_else(|| value.strip_prefix("bearer ")) .unwrap_or(value) .trim() .to_string() } fn extract_conversation_id(body_json: &Value) -> Option { let object = body_json.as_object()?; string_value(object.get("conversation_id")) .or_else(|| string_value(object.get("conversationId"))) .or_else(|| string_value(object.get("session_id"))) .or_else(|| string_value(object.get("sessionId"))) .or_else(|| { object .get("metadata") .and_then(Value::as_object) .and_then(|metadata| { string_value(metadata.get("conversation_id")) .or_else(|| string_value(metadata.get("conversationId"))) .or_else(|| string_value(metadata.get("session_id"))) .or_else(|| string_value(metadata.get("sessionId"))) }) }) } fn string_value(value: Option<&Value>) -> Option { value .and_then(Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .map(ToOwned::to_owned) } fn latest_message_snapshot_text(messages: &[Value]) -> Option { messages .iter() .rev() .filter_map(Value::as_object) .find_map(|message| { let role = message.get("role").and_then(Value::as_str)?; match role { "user" => openai_content_to_text(message.get("content")) .map(|value| value.trim().to_string()) .filter(|value| !value.is_empty()), "tool" => { let content = openai_content_to_text(message.get("content")) .map(|value| value.trim().to_string()) .filter(|value| !value.is_empty())?; let tool_call_id = message .get("tool_call_id") .and_then(Value::as_str) .unwrap_or("unknown"); Some(format!( "\n{content}\n", escape_xml_attr(tool_call_id) )) } "assistant" => openai_content_to_text(message.get("content")) .map(|value| value.trim().to_string()) .filter(|value| !value.is_empty()), _ => None, } }) } fn openai_content_to_text(value: Option<&Value>) -> Option { match value? { Value::String(text) => Some(text.clone()), Value::Array(items) => { let parts = items .iter() .filter_map(|item| { item.as_object() .and_then(|object| object.get("text")) .and_then(Value::as_str) }) .map(str::trim) .filter(|value| !value.is_empty()) .collect::>(); (!parts.is_empty()).then(|| parts.join("\n")) } _ => None, } } fn escape_xml_attr(value: &str) -> String { value .replace('&', "&") .replace('"', """) .replace('<', "<") .replace('>', ">") } #[cfg(test)] mod tests { use http::HeaderMap; use serde_json::json; use crate::snapshot::{ GatewayProviderTransportEndpoint, GatewayProviderTransportKey, GatewayProviderTransportProvider, GatewayProviderTransportSnapshot, }; use super::{ build_windsurf_cascade_headers, build_windsurf_cascade_request_body, build_windsurf_cascade_upstream_url, local_windsurf_request_transport_unsupported_reason_with_network, resolve_windsurf_cascade_auth, GET_CHAT_MESSAGE_PATH, }; fn sample_windsurf_transport(auth_type: &str) -> GatewayProviderTransportSnapshot { GatewayProviderTransportSnapshot { provider: GatewayProviderTransportProvider { id: "provider-windsurf".to_string(), name: "Windsurf".to_string(), provider_type: "windsurf".to_string(), website: None, is_active: true, keep_priority_on_conversion: false, enable_format_conversion: true, concurrent_limit: None, max_retries: None, proxy: None, request_timeout_secs: None, stream_first_byte_timeout_secs: None, config: None, }, endpoint: GatewayProviderTransportEndpoint { id: "endpoint-windsurf-chat".to_string(), provider_id: "provider-windsurf".to_string(), api_format: "openai:chat".to_string(), api_family: Some("openai".to_string()), endpoint_kind: Some("chat".to_string()), is_active: true, base_url: "https://server.codeium.com".to_string(), header_rules: None, body_rules: None, max_retries: None, custom_path: None, config: None, format_acceptance_config: None, proxy: None, }, key: GatewayProviderTransportKey { id: "key-windsurf".to_string(), provider_id: "provider-windsurf".to_string(), name: "windsurf@example.com".to_string(), auth_type: auth_type.to_string(), is_active: true, api_formats: None, auth_type_by_format: None, allow_auth_channel_mismatch_formats: None, allowed_models: None, capabilities: None, rate_multipliers: None, global_priority_by_format: None, expires_at_unix_secs: None, proxy: None, fingerprint: None, decrypted_api_key: "devin-session-token$abc".to_string(), decrypted_auth_config: Some(r#"{"provider_type":"windsurf"}"#.to_string()), upstream_metadata: None, }, } } #[test] fn builds_windsurf_cascade_url() { assert_eq!( build_windsurf_cascade_upstream_url("https://server.codeium.com", Some("debug=1")) .as_deref(), Some( "https://server.codeium.com/exa.api_server_pb.ApiServerService/GetChatMessage?debug=1" ) ); assert!(GET_CHAT_MESSAGE_PATH.ends_with("/GetChatMessage")); } #[test] fn builds_cascade_request_body_with_metadata_and_messages() { let body = build_windsurf_cascade_request_body( &json!({ "model": "gpt-5", "conversation_id": "conv-1", "messages": [ {"role": "system", "content": "brief"}, {"role": "user", "content": [{"type": "text", "text": "hello"}]} ], "max_tokens": 128 }), "windsurf-model", "Bearer devin-session-token$abc", None, None, true, ) .expect("body should build"); assert_eq!(body["metadata"]["apiKey"], json!("devin-session-token$abc")); assert_eq!(body["modelName"], json!("windsurf-model")); assert_eq!(body["stream"], json!(true)); assert_eq!(body["conversationId"], json!("conv-1")); assert_eq!(body["message"], json!("hello")); assert_eq!(body["maxTokens"], json!(128)); } #[test] fn preserves_openai_tool_fields_for_native_windsurf_runtime() { let body = build_windsurf_cascade_request_body( &json!({ "model": "gpt-5-5-low", "messages": [ {"role": "user", "content": "read Cargo.toml"} ], "tools": [{ "type": "function", "function": { "name": "Read", "parameters": { "type": "object", "properties": {"file_path": {"type": "string"}} } } }], "tool_choice": "required", "parallel_tool_calls": false, "response_format": {"type": "json_object"} }), "gpt-5-5-low", "Bearer devin-session-token$abc", None, None, false, ) .expect("body should build"); assert_eq!(body["tools"][0]["function"]["name"], json!("Read")); assert_eq!(body["tool_choice"], json!("required")); assert_eq!(body["parallel_tool_calls"], json!(false)); assert_eq!(body["response_format"]["type"], json!("json_object")); } #[test] fn builds_cascade_request_body_message_snapshot_from_latest_tool_result() { let body = build_windsurf_cascade_request_body( &json!({ "model": "gpt-5-5-low", "messages": [ {"role": "user", "content": "read Cargo.toml"}, { "role": "assistant", "content": null, "tool_calls": [{ "id": "call_1", "type": "function", "function": {"name": "Read", "arguments": "{\"file_path\":\"Cargo.toml\"}"} }] }, {"role": "tool", "tool_call_id": "call_1", "content": "workspace Cargo.toml content"} ] }), "gpt-5-5-low", "Bearer devin-session-token$abc", None, None, false, ) .expect("body should build"); assert!(body["message"] .as_str() .expect("message should be a string") .contains(r#""#)); assert!(body["message"] .as_str() .expect("message should be a string") .contains("workspace Cargo.toml content")); } #[test] fn builds_cascade_headers_with_connect_protocol_and_auth() { let headers = build_windsurf_cascade_headers( &HeaderMap::new(), &json!({"metadata": {"apiKey": "secret"}}), &json!({"messages": []}), None, "authorization", "Bearer secret", false, ) .expect("headers should build"); assert_eq!( headers.get("connect-protocol-version").map(String::as_str), Some("1") ); assert_eq!( headers.get("authorization").map(String::as_str), Some("Bearer secret") ); assert_eq!( headers.get("content-type").map(String::as_str), Some("application/connect+json") ); assert_eq!( headers.get("accept").map(String::as_str), Some("application/connect+json") ); } #[test] fn oauth_windsurf_transport_resolves_direct_bearer_auth() { let transport = sample_windsurf_transport("oauth"); assert_eq!( local_windsurf_request_transport_unsupported_reason_with_network(&transport), None ); assert_eq!( resolve_windsurf_cascade_auth(&transport), Some(( "authorization".to_string(), "Bearer devin-session-token$abc".to_string() )) ); } }