Skip to main content

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