steer_core/api/
util.rs

1/// Normalize a chat completions URL.
2/// Ensures the URL ends with the correct path for chat completions.
3pub fn normalize_chat_url(base_url: Option<&str>, default_url: &str) -> String {
4    let base_url = base_url
5        .map(|s| s.to_string())
6        .unwrap_or_else(|| default_url.to_string());
7
8    // If URL already ends with chat/completions, return as-is
9    if base_url.ends_with("/chat/completions") || base_url.ends_with("/v1/chat/completions") {
10        return base_url;
11    }
12
13    // Parse the URL to better handle path segments
14    if let Ok(mut parsed) = url::Url::parse(&base_url) {
15        let path = parsed.path().trim_end_matches('/');
16
17        // If the path already contains /v1, append just /chat/completions
18        if path.ends_with("/v1") {
19            parsed.set_path(&format!("{path}/chat/completions"));
20        } else if path.is_empty() || path == "/" {
21            // No meaningful path, add /v1/chat/completions
22            parsed.set_path("/v1/chat/completions");
23        } else {
24            // Has some path but not /v1, append /v1/chat/completions
25            parsed.set_path(&format!("{path}/v1/chat/completions"));
26        }
27        parsed.to_string()
28    } else {
29        // Fallback for non-URL strings (shouldn't happen with valid base URLs)
30        if base_url.ends_with('/') {
31            format!("{base_url}v1/chat/completions")
32        } else {
33            format!("{base_url}/v1/chat/completions")
34        }
35    }
36}
37
38/// Normalize a responses URL.
39/// Ensures the URL ends with the correct path for Responses API.
40pub fn normalize_responses_url(base_url: Option<&str>, default_url: &str) -> String {
41    let base_url = base_url
42        .map(|s| s.to_string())
43        .unwrap_or_else(|| default_url.to_string());
44
45    if base_url.ends_with("/responses") || base_url.ends_with("/v1/responses") {
46        return base_url;
47    }
48
49    if let Ok(mut parsed) = url::Url::parse(&base_url) {
50        let path = parsed.path().trim_end_matches('/');
51        if path.ends_with("/v1") {
52            parsed.set_path(&format!("{path}/responses"));
53        } else if path.is_empty() || path == "/" {
54            parsed.set_path("/v1/responses");
55        } else {
56            parsed.set_path(&format!("{path}/v1/responses"));
57        }
58        parsed.to_string()
59    } else if base_url.ends_with('/') {
60        format!("{base_url}v1/responses")
61    } else {
62        format!("{base_url}/v1/responses")
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn test_normalize_chat_url() {
72        assert_eq!(
73            normalize_chat_url(Some("https://api.example.com"), ""),
74            "https://api.example.com/v1/chat/completions"
75        );
76
77        assert_eq!(
78            normalize_chat_url(Some("https://api.example.com/"), ""),
79            "https://api.example.com/v1/chat/completions"
80        );
81
82        assert_eq!(
83            normalize_chat_url(Some("https://api.example.com/v1"), ""),
84            "https://api.example.com/v1/chat/completions"
85        );
86
87        assert_eq!(
88            normalize_chat_url(Some("https://api.example.com/chat/completions"), ""),
89            "https://api.example.com/chat/completions"
90        );
91
92        assert_eq!(
93            normalize_chat_url(Some("https://api.example.com/v1/chat/completions"), ""),
94            "https://api.example.com/v1/chat/completions"
95        );
96
97        assert_eq!(
98            normalize_chat_url(None, "https://default.com/v1/chat/completions"),
99            "https://default.com/v1/chat/completions"
100        );
101    }
102
103    #[test]
104    fn test_normalize_responses_url() {
105        assert_eq!(
106            normalize_responses_url(Some("https://api.example.com"), ""),
107            "https://api.example.com/v1/responses"
108        );
109        assert_eq!(
110            normalize_responses_url(Some("https://api.example.com/"), ""),
111            "https://api.example.com/v1/responses"
112        );
113        assert_eq!(
114            normalize_responses_url(Some("https://api.example.com/v1"), ""),
115            "https://api.example.com/v1/responses"
116        );
117        assert_eq!(
118            normalize_responses_url(Some("https://api.example.com/responses"), ""),
119            "https://api.example.com/responses"
120        );
121        assert_eq!(
122            normalize_responses_url(Some("https://api.example.com/v1/responses"), ""),
123            "https://api.example.com/v1/responses"
124        );
125        assert_eq!(
126            normalize_responses_url(None, "https://default.com/v1/responses"),
127            "https://default.com/v1/responses"
128        );
129    }
130}