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 pub session_ids: bool,
48}
49
50#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
52#[serde(deny_unknown_fields)]
53pub struct CallerIdentity {
54 pub originator: String,
56 pub user_agent: String,
58}
59
60pub(super) fn default_user_agent(originator: &str) -> String {
63 format!(
64 "rig/{} ({} {}; {originator})",
65 env!("CARGO_PKG_VERSION"),
66 std::env::consts::OS,
67 std::env::consts::ARCH,
68 )
69}
70
71impl OpenAIConfig {
72 pub fn with_alternate_key(dialect: &Dialect, api_key: impl Into<Secret>) -> Self {
80 let auth = dialect
81 .alternate_auth
82 .map_or(dialect.quirks.auth, |alternative| alternative.auth);
83 Self {
84 auth,
85 ..Self::with_key(dialect, api_key)
86 }
87 }
88
89 pub(crate) fn credential_from_env(dialect: &Dialect) -> Result<(String, Auth), EnvError> {
93 let quirks = &dialect.quirks;
94 Ok(match dialect.alternate_auth {
95 Some(alternative) => match env::optional(dialect.api_key_env)? {
96 Some(api_key) => (api_key, quirks.auth),
97 None => match env::optional(alternative.api_key_env)? {
98 Some(api_key) => (api_key, alternative.auth),
99 None => {
100 return Err(EnvError::Invalid {
101 name: dialect.api_key_env,
102 detail: format!(
103 "either `{}` or `{}` must be set",
104 dialect.api_key_env, alternative.api_key_env
105 ),
106 });
107 }
108 },
109 },
110 None => (env::required(dialect.api_key_env)?, quirks.auth),
111 })
112 }
113
114 pub fn with_auth(mut self, auth: Auth) -> Self {
116 self.auth = auth;
117 self
118 }
119
120 pub fn with_account_id(mut self, account_id: impl Into<String>) -> Self {
122 self.account_id = Some(account_id.into());
123 self
124 }
125
126 pub(crate) fn authenticate(&self, builder: http::request::Builder) -> http::request::Builder {
128 match self.auth {
129 Auth::Bearer => {
130 builder.header("Authorization", format!("Bearer {}", self.api_key.expose()))
131 }
132 Auth::OptionalBearer if self.api_key.is_empty() => builder,
133 Auth::OptionalBearer => {
134 builder.header("Authorization", format!("Bearer {}", self.api_key.expose()))
135 }
136 Auth::ApiKeyHeader => builder.header("api-key", self.api_key.expose()),
137 }
138 }
139
140 pub(crate) fn headers(&self, builder: http::request::Builder) -> http::request::Builder {
142 let mut builder = self.authenticate(builder);
143 if let Some(identity) = &self.identity {
144 builder = builder
145 .header("originator", &identity.originator)
146 .header(http::header::USER_AGENT, &identity.user_agent);
147 }
148 if self
149 .dialect
150 .quirks
151 .identity
152 .is_some_and(|identity| identity.session_ids)
153 {
154 builder = builder.header("session_id", crate::providers::chatgpt::session_id());
156 }
157 if let Some(account_id) = &self.account_id {
158 builder = builder.header("ChatGPT-Account-Id", account_id);
159 }
160 builder
161 }
162}