1use std::collections::HashMap;
7use std::time::Duration;
8
9use serde_json::Value;
10
11use crate::error::GatewayError;
12
13pub const DEFAULT_BASE_URL: &str = "https://api.typesafe.ai";
16pub const DEFAULT_MODEL: &str = "jev-latest";
19
20#[derive(Debug, Clone)]
21pub struct JevNativeProvider {
22 http: reqwest::Client,
23 api_key: String,
24 base_url: String,
25}
26
27impl JevNativeProvider {
28 pub fn new(api_key: String, base_url: Option<String>, request_timeout: Duration) -> Self {
29 Self {
30 http: reqwest::Client::builder()
31 .timeout(request_timeout)
32 .build()
33 .unwrap(),
34 api_key,
35 base_url: base_url.unwrap_or_else(|| DEFAULT_BASE_URL.to_string()),
36 }
37 }
38
39 pub fn from_env(env: &HashMap<String, String>, request_timeout: Duration) -> Option<Self> {
42 let api_key = env
43 .get("TYPESAFE_API_KEY")
44 .map(|s| s.trim())
45 .filter(|s| !s.is_empty())?;
46 let base_url = env
47 .get("TYPESAFE_BASE_URL")
48 .map(|s| s.trim())
49 .filter(|s| !s.is_empty())
50 .map(str::to_string);
51 Some(Self::new(api_key.to_string(), base_url, request_timeout))
52 }
53
54 pub async fn evaluate(&self, body: Value) -> Result<reqwest::Response, GatewayError> {
58 self.http
59 .post(format!("{}/v1/systemone", self.base_url))
60 .bearer_auth(&self.api_key)
61 .json(&body)
62 .send()
63 .await
64 .map_err(|e| GatewayError::Upstream {
65 status: 502,
66 body: e.to_string(),
67 })
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn from_env_is_none_without_key() {
77 assert!(JevNativeProvider::from_env(&HashMap::new(), Duration::from_secs(1)).is_none());
78 let blank = HashMap::from([("TYPESAFE_API_KEY".to_string(), " ".to_string())]);
79 assert!(JevNativeProvider::from_env(&blank, Duration::from_secs(1)).is_none());
80 }
81
82 #[test]
83 fn from_env_picks_up_key_and_base_url_override() {
84 let env = HashMap::from([
85 ("TYPESAFE_API_KEY".to_string(), "key".to_string()),
86 (
87 "TYPESAFE_BASE_URL".to_string(),
88 "http://localhost:9".to_string(),
89 ),
90 ]);
91 let provider = JevNativeProvider::from_env(&env, Duration::from_secs(1)).unwrap();
92 assert_eq!(provider.base_url, "http://localhost:9");
93 }
94
95 #[test]
96 fn defaults_to_the_hosted_api() {
97 let provider = JevNativeProvider::new("key".into(), None, Duration::from_secs(1));
98 assert_eq!(provider.base_url, DEFAULT_BASE_URL);
99 }
100}