rig_core/providers/openai/wire/
auth.rs1use serde::{Deserialize, Serialize};
11
12use crate::client::env::{self, EnvError};
13use crate::wire::Secret;
14
15use super::{Dialect, OpenAIConfig};
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
19pub enum Auth {
20 Bearer,
22 OptionalBearer,
24 ApiKeyHeader,
26}
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
30pub struct AuthAlternative {
31 pub api_key_env: &'static str,
33 pub auth: Auth,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub struct Identity {
40 pub originator: &'static str,
42 pub originator_env: &'static str,
44 pub user_agent_env: &'static str,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
50#[serde(deny_unknown_fields)]
51pub struct CallerIdentity {
52 pub originator: String,
54 pub user_agent: String,
56}
57
58pub(super) fn default_user_agent(originator: &str) -> String {
61 format!(
62 "rig/{} ({} {}; {originator})",
63 env!("CARGO_PKG_VERSION"),
64 std::env::consts::OS,
65 std::env::consts::ARCH,
66 )
67}
68
69impl OpenAIConfig {
70 pub fn with_alternate_key(dialect: &Dialect, api_key: impl Into<Secret>) -> Self {
78 let auth = dialect
79 .alternate_auth
80 .map_or(dialect.quirks.auth, |alternative| alternative.auth);
81 Self {
82 auth,
83 ..Self::with_key(dialect, api_key)
84 }
85 }
86
87 pub(crate) fn credential_from_env(dialect: &Dialect) -> Result<(String, Auth), EnvError> {
91 let quirks = &dialect.quirks;
92 Ok(match dialect.alternate_auth {
93 Some(alternative) => match env::optional(dialect.api_key_env)? {
94 Some(api_key) => (api_key, quirks.auth),
95 None => match env::optional(alternative.api_key_env)? {
96 Some(api_key) => (api_key, alternative.auth),
97 None => {
98 return Err(EnvError::Invalid {
99 name: dialect.api_key_env,
100 detail: format!(
101 "either `{}` or `{}` must be set",
102 dialect.api_key_env, alternative.api_key_env
103 ),
104 });
105 }
106 },
107 },
108 None => (env::required(dialect.api_key_env)?, quirks.auth),
109 })
110 }
111
112 pub fn with_auth(mut self, auth: Auth) -> Self {
114 self.auth = auth;
115 self
116 }
117
118 pub fn with_account_id(mut self, account_id: impl Into<String>) -> Self {
120 self.account_id = Some(account_id.into());
121 self
122 }
123
124 pub(crate) fn authenticate(&self, builder: http::request::Builder) -> http::request::Builder {
126 match self.auth {
127 Auth::Bearer => {
128 builder.header("Authorization", format!("Bearer {}", self.api_key.expose()))
129 }
130 Auth::OptionalBearer if self.api_key.is_empty() => builder,
131 Auth::OptionalBearer => {
132 builder.header("Authorization", format!("Bearer {}", self.api_key.expose()))
133 }
134 Auth::ApiKeyHeader => builder.header("api-key", self.api_key.expose()),
135 }
136 }
137
138 pub(crate) fn headers_in_session(
144 &self,
145 builder: http::request::Builder,
146 session: Option<&str>,
147 ) -> http::request::Builder {
148 let mut builder = self.authenticate(builder);
149 if let Some(identity) = &self.identity {
150 builder = builder
151 .header("originator", &identity.originator)
152 .header(http::header::USER_AGENT, &identity.user_agent);
153 }
154 if let Some(name) = self.dialect.quirks.session_header {
155 builder = builder.header(name, crate::providers::internal::session_id(session));
156 }
157 if let Some(account_id) = &self.account_id {
158 builder = builder.header("ChatGPT-Account-Id", account_id);
159 }
160 builder
161 }
162}