1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//! The Copilot client: a [`CopilotConfig`] on a transport, and the models it
//! builds.
use crate::client::macros::http_client;
use crate::driver::Model;
use crate::error::ProviderError;
use crate::model::ModelList;
use crate::providers::copilot::auth::{AuthError, Authenticator};
use crate::providers::copilot::wire::{CopilotConfig, CopilotWire, Embeddings};
http_client!(
/// GitHub Copilot: its [`CopilotConfig`] on a transport. Every model it
/// builds sends through that transport.
Copilot,
CopilotConfig
);
impl Copilot {
/// Copilot with an exchanged session token, on the shared reqwest
/// client. See [`CopilotConfig::new`] for how the endpoint is chosen.
#[cfg(feature = "reqwest")]
#[cfg_attr(docsrs, doc(cfg(feature = "reqwest")))]
pub fn new(api_key: impl Into<crate::wire::Secret>) -> Self {
CopilotConfig::new(api_key).client()
}
/// Copilot from the environment ([`CopilotConfig::from_env`]), on the
/// shared reqwest client. This does not exchange a token.
#[cfg(feature = "reqwest")]
#[cfg_attr(docsrs, doc(cfg(feature = "reqwest")))]
pub fn from_env() -> Result<Self, crate::client::env::EnvError> {
Ok(CopilotConfig::from_env()?.client())
}
/// The completion model for `model`, on whichever route answers it.
pub fn completion(&self, model: impl Into<String>) -> Model<CopilotWire> {
self.model(self.config.completion(model))
}
/// The embedding model for `model`, `ndims` wide when set.
pub fn embedding(&self, model: impl Into<String>, ndims: Option<usize>) -> Model<Embeddings> {
self.model(self.config.embedding(model, ndims))
}
/// The models this session can use.
pub async fn list_models(&self) -> Result<ModelList, ProviderError> {
self.model(self.config.models()).list().await
}
/// A client on this client's transport, configured with the session
/// `authenticator` resolves: the device login, the GitHub token
/// exchange and the refresh all send through this client's transport.
/// The session token replaces the credential; the API root is the one
/// the exchange names, unless this client's was set explicitly.
///
/// ```no_run
/// use rig_core::providers::copilot::{CopilotConfig, auth::{AuthSource, Authenticator, DeviceCodeHandler}};
///
/// # async fn run(http: rig_core::http_client::DynHttpClient) -> Result<(), Box<dyn std::error::Error>> {
/// let authenticator = Authenticator::new(AuthSource::OAuth, None, None, DeviceCodeHandler::default(), true);
/// let copilot = CopilotConfig::new("").connect(http).authenticate(&authenticator).await?;
/// # let _ = copilot;
/// # Ok(())
/// # }
/// ```
pub async fn authenticate(self, authenticator: &Authenticator) -> Result<Self, AuthError> {
let context = authenticator.auth_context(&self.http).await?;
let explicit = self.config.base_url != CopilotConfig::new(self.config.api_key).base_url;
let signed_in = CopilotConfig::from_auth(&context);
let config = if explicit {
signed_in.with_base_url(self.config.base_url)
} else {
signed_in
};
Ok(Self {
config,
http: self.http,
})
}
}