|
1
|
//! Hugging Face token resolution for on-device model loading. |
|
2
|
//! |
|
3
|
//! On iOS there is no way to run `mistralrs login` or place a token file at |
|
4
|
//! `~/.cache/huggingface/token`, so we embed the token at **build time**. |
|
5
|
//! |
|
6
|
//! ## How it works |
|
7
|
//! |
|
8
|
//! 1. The consuming app's `build.rs` sets the `HF_TOKEN` env var (e.g. by |
|
9
|
//! loading a `.env` file with `dotenvy`, or via CI secrets). |
|
10
|
//! 2. `option_env!("HF_TOKEN")` in this module picks it up at compile time |
|
11
|
//! and bakes it into the binary. |
|
12
|
//! |
|
13
|
//! This works regardless of whether the build is triggered from a terminal |
|
14
|
//! (where shell env vars are available) or from Xcode (which strips the |
|
15
|
//! shell environment). |
|
16
|
//! |
|
17
|
//! ## Setup |
|
18
|
//! |
|
19
|
//! Set the `HF_TOKEN` environment variable before building, or add it to |
|
20
|
//! a `.env` file that your app's `build.rs` loads: |
|
21
|
//! |
|
22
|
//! ```text |
|
23
|
//! HF_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
|
24
|
//! ``` |
|
25
|
//! |
|
26
|
//! Then build normally — no extra env vars needed. |
|
27
|
//! |
|
28
|
//! ## Runtime behaviour |
|
29
|
//! |
|
30
|
//! [`hf_token_source`] returns: |
|
31
|
//! |
|
32
|
//! 1. `TokenSource::Literal(token)` — if `HF_TOKEN` was present at compile time. |
|
33
|
//! 2. `TokenSource::CacheToken` — otherwise (reads `~/.cache/huggingface/token`; |
|
34
|
//! works on macOS after `mistralrs login`). |
|
35
|
|
|
36
|
#[cfg(any( |
|
37
|
target_os = "macos", |
|
38
|
target_os = "ios", |
|
39
|
target_os = "tvos", |
|
40
|
target_os = "visionos", |
|
41
|
target_os = "watchos", |
|
42
|
target_os = "windows", |
|
43
|
target_os = "linux", |
|
44
|
target_os = "android" |
|
45
|
))] |
|
46
|
use mistralrs::TokenSource; |
|
47
|
|
|
48
|
/// Token baked in at compile time (if the `HF_TOKEN` env var was set). |
|
49
|
/// |
|
50
|
/// `option_env!` is evaluated by `rustc` during compilation — the resulting |
|
51
|
/// value is a `Option<&'static str>` embedded directly in the binary. |
|
52
|
/// This means the token never needs to exist on the device's filesystem. |
|
53
|
const BUILD_TIME_HF_TOKEN: Option<&str> = option_env!("HF_TOKEN"); |
|
54
|
|
|
55
|
/// Resolve the best available [`TokenSource`] for Hugging Face Hub requests. |
|
56
|
/// |
|
57
|
/// Prefer the compile-time literal when available (required on iOS), otherwise |
|
58
|
/// fall back to the on-disk cache token (the default on macOS where |
|
59
|
/// `mistralrs login` has been run). |
|
60
|
#[cfg(any( |
|
61
|
target_os = "macos", |
|
62
|
target_os = "ios", |
|
63
|
target_os = "tvos", |
|
64
|
target_os = "visionos", |
|
65
|
target_os = "watchos", |
|
66
|
target_os = "windows", |
|
67
|
target_os = "linux", |
|
68
|
target_os = "android" |
|
69
|
))] |
|
70
|
pub fn hf_token_source() -> TokenSource { |
|
71
|
match BUILD_TIME_HF_TOKEN { |
|
72
|
Some(token) if !token.is_empty() => { |
|
73
|
log::debug!("Using build-time HF_TOKEN for Hugging Face authentication."); |
|
74
|
TokenSource::Literal(token.to_string()) |
|
75
|
} |
|
76
|
_ => { |
|
77
|
log::debug!( |
|
78
|
"No build-time HF_TOKEN found; falling back to cached token \ |
|
79
|
(~/.cache/huggingface/token)." |
|
80
|
); |
|
81
|
TokenSource::CacheToken |
|
82
|
} |
|
83
|
} |
|
84
|
} |
|
85
|
|
|
86
|
#[cfg(test)] |
|
87
|
mod tests { |
|
88
|
use super::*; |
|
89
|
|
|
90
|
#[test] |
|
91
|
fn build_time_token_is_option() { |
|
92
|
// This just ensures the constant compiles and is the expected type. |
|
93
|
let _: Option<&str> = BUILD_TIME_HF_TOKEN; |
|
94
|
} |
|
95
|
|
|
96
|
#[test] |
|
97
|
#[cfg(any( |
|
98
|
target_os = "macos", |
|
99
|
target_os = "ios", |
|
100
|
target_os = "tvos", |
|
101
|
target_os = "visionos", |
|
102
|
target_os = "watchos", |
|
103
|
target_os = "windows", |
|
104
|
target_os = "linux", |
|
105
|
target_os = "android" |
|
106
|
))] |
|
107
|
fn token_source_returns_valid_variant() { |
|
108
|
let source = hf_token_source(); |
|
109
|
// We can't know at test time whether HF_TOKEN was set, but we can |
|
110
|
// verify it returns *something* without panicking. |
|
111
|
let display = format!("{}", source); |
|
112
|
assert!( |
|
113
|
display.starts_with("literal:") || display == "cache", |
|
114
|
"Unexpected token source: {display}" |
|
115
|
); |
|
116
|
} |
|
117
|
} |