ai_runtime_adapters/
lib.rs1#![warn(missing_docs)]
2
3use std::time::Duration;
21
22use anyhow::Context;
23use async_trait::async_trait;
24use dracon_ai_runtime_contracts::models::ChatRequest;
25use dracon_ai_runtime_contracts::traits::AiProvider;
26
27pub struct GenericOpenAIAdapter {
29 api_key: String,
30 endpoint: String,
31 model: String,
32 auth_header_name: String,
33 auth_header_prefix: String,
34 client: reqwest::Client,
35}
36
37impl GenericOpenAIAdapter {
38 pub fn new_with_auth(
40 api_key: String,
41 endpoint: String,
42 model: String,
43 auth_header_name: String,
44 auth_header_prefix: String,
45 ) -> anyhow::Result<Self> {
46 let client = reqwest::Client::builder()
47 .timeout(Duration::from_secs(60))
48 .connect_timeout(Duration::from_secs(10))
49 .build()
50 .context("reqwest client should build")?;
51 Ok(Self {
52 api_key,
53 endpoint,
54 model,
55 auth_header_name,
56 auth_header_prefix,
57 client,
58 })
59 }
60}
61
62#[async_trait]
63impl AiProvider for GenericOpenAIAdapter {
64 async fn ask_and_collect(
65 &self,
66 request: ChatRequest,
67 ) -> anyhow::Result<(String, Option<String>)> {
68 let messages: Vec<serde_json::Value> = request
69 .messages
70 .iter()
71 .map(|m| {
72 serde_json::json!({
73 "role": m.role,
74 "content": m.content,
75 })
76 })
77 .collect();
78
79 let body = serde_json::json!({
80 "model": self.model,
81 "messages": messages,
82 "max_tokens": request.max_tokens.unwrap_or(200),
83 "temperature": request.temperature.unwrap_or(0.7),
84 });
85
86 let resp = self
87 .client
88 .post(&format!("{}/chat/completions", self.endpoint))
89 .header(
90 &self.auth_header_name,
91 format!("{}{}", self.auth_header_prefix, self.api_key),
92 )
93 .header("Content-Type", "application/json")
94 .json(&body)
95 .send()
96 .await?
97 .error_for_status()?;
98
99 let json: serde_json::Value = resp.json().await?;
100 let content = json["choices"][0]["message"]["content"]
101 .as_str()
102 .unwrap_or("")
103 .to_string();
104 let finish_reason = json["choices"][0]["finish_reason"]
105 .as_str()
106 .map(|s| s.to_string());
107
108 Ok((content, finish_reason))
109 }
110}