alith_interface/llms/api/generic_openai/
mod.rs1use super::{
2 client::ApiClient,
3 config::{ApiConfig, ApiConfigTrait},
4 openai::completion::OpenAICompletionRequest,
5};
6use crate::requests::{
7 completion::{
8 error::CompletionError, request::CompletionRequest, response::CompletionResponse,
9 },
10 embeddings::{EmbeddingsError, EmbeddingsRequest, EmbeddingsResponse},
11};
12use alith_devices::logging::LoggingConfig;
13use alith_models::api_model::ApiLLMModel;
14use reqwest::header::{AUTHORIZATION, HeaderMap, HeaderValue};
15use secrecy::{ExposeSecret, SecretString};
16use serde_json::json;
17
18pub struct GenericApiBackend {
19 pub(crate) client: ApiClient<GenericApiConfig>,
20 pub model: ApiLLMModel,
21}
22
23impl GenericApiBackend {
24 pub fn new(mut config: GenericApiConfig, model: ApiLLMModel) -> crate::Result<Self> {
25 config.logging_config.load_logger()?;
26 if let Ok(api_key) = config.api_config.load_api_key() {
27 config.api_config.api_key = Some(api_key);
28 }
29 Ok(Self {
30 client: ApiClient::new(config),
31 model,
32 })
33 }
34
35 pub(crate) async fn completion_request(
36 &self,
37 request: &CompletionRequest,
38 ) -> crate::Result<CompletionResponse, CompletionError> {
39 match self
40 .client
41 .post(
42 &self.client.config.completion_path,
43 OpenAICompletionRequest::new(request)?,
44 )
45 .await
46 {
47 Err(e) => Err(CompletionError::ClientError(e)),
48 Ok(res) => Ok(CompletionResponse::new_from_openai(request, res)?),
49 }
50 }
51
52 pub(crate) async fn embeddings_request(
53 &self,
54 request: &EmbeddingsRequest,
55 ) -> crate::Result<EmbeddingsResponse, EmbeddingsError> {
56 match self
57 .client
58 .post(
59 "/embeddings",
60 json!({
61 "input": request.input,
62 "model": request.model,
63 }),
64 )
65 .await
66 {
67 Ok(res) => Ok(res),
68 Err(e) => Err(EmbeddingsError::ClientError(e)),
69 }
70 }
71}
72
73#[derive(Clone, Debug)]
74pub struct GenericApiConfig {
75 pub api_config: ApiConfig,
76 pub logging_config: LoggingConfig,
77 pub completion_path: String,
78}
79
80impl Default for GenericApiConfig {
81 fn default() -> Self {
82 Self {
83 api_config: ApiConfig {
84 host: Default::default(),
85 port: None,
86 api_key: None,
87 api_key_env_var: Default::default(),
88 },
89 logging_config: LoggingConfig {
90 logger_name: "generic".to_string(),
91 ..Default::default()
92 },
93 completion_path: "/chat/completions".to_string(),
94 }
95 }
96}
97
98impl GenericApiConfig {
99 pub fn new() -> Self {
100 Default::default()
101 }
102
103 pub fn completion_path<S: Into<String>>(mut self, path: S) -> Self {
104 self.completion_path = path.into();
105 self
106 }
107}
108
109impl ApiConfigTrait for GenericApiConfig {
110 fn headers(&self) -> HeaderMap {
111 let mut headers = HeaderMap::new();
112 if let Some(api_key) = self.api_key() {
113 if let Ok(header_value) =
114 HeaderValue::from_str(&format!("Bearer {}", api_key.expose_secret()))
115 {
116 headers.insert(AUTHORIZATION, header_value);
117 } else {
118 crate::error!("Failed to create header value from authorization value");
119 }
120 }
121
122 headers
123 }
124
125 fn url(&self, path: &str) -> String {
126 if let Some(port) = &self.api_config.port {
127 format!("https://{}:{}{}", self.api_config.host, port, path)
128 } else {
129 format!("https://{}:{}", self.api_config.host, path)
130 }
131 }
132
133 fn api_key(&self) -> &Option<SecretString> {
134 &self.api_config.api_key
135 }
136}