liter_llm 1.0.0.pre.rc.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +239 -0
- data/ext/liter_llm_rb/extconf.rb +65 -0
- data/ext/liter_llm_rb/native/.cargo/config.toml +23 -0
- data/ext/liter_llm_rb/native/Cargo.lock +3713 -0
- data/ext/liter_llm_rb/native/Cargo.toml +32 -0
- data/ext/liter_llm_rb/native/build.rs +15 -0
- data/ext/liter_llm_rb/native/src/lib.rs +1079 -0
- data/lib/liter_llm.rb +8 -0
- data/sig/liter_llm.rbs +416 -0
- data/vendor/Cargo.toml +54 -0
- data/vendor/liter-llm/Cargo.toml +92 -0
- data/vendor/liter-llm/README.md +252 -0
- data/vendor/liter-llm/schemas/pricing.json +40 -0
- data/vendor/liter-llm/schemas/providers.json +1662 -0
- data/vendor/liter-llm/src/auth/azure_ad.rs +264 -0
- data/vendor/liter-llm/src/auth/bedrock_sts.rs +353 -0
- data/vendor/liter-llm/src/auth/mod.rs +68 -0
- data/vendor/liter-llm/src/auth/vertex_oauth.rs +353 -0
- data/vendor/liter-llm/src/client/config.rs +351 -0
- data/vendor/liter-llm/src/client/managed.rs +622 -0
- data/vendor/liter-llm/src/client/mod.rs +864 -0
- data/vendor/liter-llm/src/cost.rs +212 -0
- data/vendor/liter-llm/src/error.rs +190 -0
- data/vendor/liter-llm/src/http/eventstream.rs +860 -0
- data/vendor/liter-llm/src/http/mod.rs +12 -0
- data/vendor/liter-llm/src/http/request.rs +438 -0
- data/vendor/liter-llm/src/http/retry.rs +72 -0
- data/vendor/liter-llm/src/http/streaming.rs +289 -0
- data/vendor/liter-llm/src/lib.rs +37 -0
- data/vendor/liter-llm/src/provider/anthropic.rs +2250 -0
- data/vendor/liter-llm/src/provider/azure.rs +579 -0
- data/vendor/liter-llm/src/provider/bedrock.rs +1543 -0
- data/vendor/liter-llm/src/provider/cohere.rs +654 -0
- data/vendor/liter-llm/src/provider/custom.rs +404 -0
- data/vendor/liter-llm/src/provider/google_ai.rs +281 -0
- data/vendor/liter-llm/src/provider/mistral.rs +188 -0
- data/vendor/liter-llm/src/provider/mod.rs +616 -0
- data/vendor/liter-llm/src/provider/vertex.rs +1504 -0
- data/vendor/liter-llm/src/tests.rs +1425 -0
- data/vendor/liter-llm/src/tokenizer.rs +281 -0
- data/vendor/liter-llm/src/tower/budget.rs +599 -0
- data/vendor/liter-llm/src/tower/cache.rs +502 -0
- data/vendor/liter-llm/src/tower/cache_opendal.rs +270 -0
- data/vendor/liter-llm/src/tower/cooldown.rs +231 -0
- data/vendor/liter-llm/src/tower/cost.rs +404 -0
- data/vendor/liter-llm/src/tower/fallback.rs +121 -0
- data/vendor/liter-llm/src/tower/health.rs +219 -0
- data/vendor/liter-llm/src/tower/hooks.rs +369 -0
- data/vendor/liter-llm/src/tower/mod.rs +77 -0
- data/vendor/liter-llm/src/tower/rate_limit.rs +300 -0
- data/vendor/liter-llm/src/tower/router.rs +436 -0
- data/vendor/liter-llm/src/tower/service.rs +181 -0
- data/vendor/liter-llm/src/tower/tests.rs +539 -0
- data/vendor/liter-llm/src/tower/tests_common.rs +252 -0
- data/vendor/liter-llm/src/tower/tracing.rs +209 -0
- data/vendor/liter-llm/src/tower/types.rs +170 -0
- data/vendor/liter-llm/src/types/audio.rs +52 -0
- data/vendor/liter-llm/src/types/batch.rs +77 -0
- data/vendor/liter-llm/src/types/chat.rs +214 -0
- data/vendor/liter-llm/src/types/common.rs +244 -0
- data/vendor/liter-llm/src/types/embedding.rs +84 -0
- data/vendor/liter-llm/src/types/files.rs +58 -0
- data/vendor/liter-llm/src/types/image.rs +40 -0
- data/vendor/liter-llm/src/types/mod.rs +27 -0
- data/vendor/liter-llm/src/types/models.rs +21 -0
- data/vendor/liter-llm/src/types/moderation.rs +80 -0
- data/vendor/liter-llm/src/types/ocr.rs +87 -0
- data/vendor/liter-llm/src/types/rerank.rs +46 -0
- data/vendor/liter-llm/src/types/responses.rs +55 -0
- data/vendor/liter-llm/src/types/search.rs +45 -0
- data/vendor/liter-llm/tests/contract.rs +332 -0
- data/vendor/liter-llm-ffi/Cargo.toml +30 -0
- data/vendor/liter-llm-ffi/build.rs +66 -0
- data/vendor/liter-llm-ffi/cbindgen.toml +60 -0
- data/vendor/liter-llm-ffi/liter_llm.h +850 -0
- data/vendor/liter-llm-ffi/src/lib.rs +2488 -0
- metadata +286 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
use std::borrow::Cow;
|
|
2
|
+
|
|
3
|
+
use serde_json::Value;
|
|
4
|
+
|
|
5
|
+
use crate::error::Result;
|
|
6
|
+
use crate::provider::Provider;
|
|
7
|
+
|
|
8
|
+
/// Parameters that Mistral does not support and should be stripped from requests.
|
|
9
|
+
const UNSUPPORTED_PARAMS: &[&str] = &[
|
|
10
|
+
"parallel_tool_calls",
|
|
11
|
+
"logit_bias",
|
|
12
|
+
"presence_penalty",
|
|
13
|
+
"frequency_penalty",
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
/// Mistral AI provider.
|
|
17
|
+
///
|
|
18
|
+
/// Mistral's API is largely OpenAI-compatible with a few differences:
|
|
19
|
+
/// - `tool_choice: "required"` must be mapped to `"any"`.
|
|
20
|
+
/// - Several OpenAI parameters are not supported and must be stripped.
|
|
21
|
+
/// - Response format is OpenAI-compatible (no transform needed).
|
|
22
|
+
pub struct MistralProvider;
|
|
23
|
+
|
|
24
|
+
impl Provider for MistralProvider {
|
|
25
|
+
fn name(&self) -> &str {
|
|
26
|
+
"mistral"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
fn base_url(&self) -> &str {
|
|
30
|
+
"https://api.mistral.ai/v1"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
fn auth_header<'a>(&'a self, api_key: &'a str) -> Option<(Cow<'static, str>, Cow<'a, str>)> {
|
|
34
|
+
Some((Cow::Borrowed("Authorization"), Cow::Owned(format!("Bearer {api_key}"))))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
fn matches_model(&self, model: &str) -> bool {
|
|
38
|
+
model.starts_with("mistral-")
|
|
39
|
+
|| model.starts_with("codestral-")
|
|
40
|
+
|| model.starts_with("pixtral-")
|
|
41
|
+
|| model.starts_with("ministral-")
|
|
42
|
+
|| model.starts_with("open-mistral-")
|
|
43
|
+
|| model.starts_with("mistral/")
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
fn strip_model_prefix<'m>(&self, model: &'m str) -> &'m str {
|
|
47
|
+
model.strip_prefix("mistral/").unwrap_or(model)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// Transform the request body for Mistral compatibility.
|
|
51
|
+
///
|
|
52
|
+
/// - Maps `tool_choice: "required"` to `"any"` (Mistral's equivalent).
|
|
53
|
+
/// - Strips unsupported parameters: `parallel_tool_calls`, `logit_bias`,
|
|
54
|
+
/// `presence_penalty`, `frequency_penalty`.
|
|
55
|
+
fn transform_request(&self, body: &mut Value) -> Result<()> {
|
|
56
|
+
if let Some(obj) = body.as_object_mut() {
|
|
57
|
+
// Map tool_choice "required" -> "any".
|
|
58
|
+
if let Some(tc) = obj.get_mut("tool_choice")
|
|
59
|
+
&& tc.as_str() == Some("required")
|
|
60
|
+
{
|
|
61
|
+
*tc = Value::String("any".to_owned());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Strip unsupported parameters.
|
|
65
|
+
for param in UNSUPPORTED_PARAMS {
|
|
66
|
+
obj.remove(*param);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
Ok(())
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Response is OpenAI-compatible; no transform_response override needed.
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#[cfg(test)]
|
|
76
|
+
mod tests {
|
|
77
|
+
use serde_json::json;
|
|
78
|
+
|
|
79
|
+
use super::*;
|
|
80
|
+
|
|
81
|
+
#[test]
|
|
82
|
+
fn test_mistral_name_and_base_url() {
|
|
83
|
+
let provider = MistralProvider;
|
|
84
|
+
assert_eq!(provider.name(), "mistral");
|
|
85
|
+
assert_eq!(provider.base_url(), "https://api.mistral.ai/v1");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
#[test]
|
|
89
|
+
fn test_mistral_auth_header() {
|
|
90
|
+
let provider = MistralProvider;
|
|
91
|
+
let (name, value) = provider.auth_header("test-key").expect("should return auth header");
|
|
92
|
+
assert_eq!(name, "Authorization");
|
|
93
|
+
assert_eq!(value, "Bearer test-key");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
#[test]
|
|
97
|
+
fn test_mistral_matches_model() {
|
|
98
|
+
let provider = MistralProvider;
|
|
99
|
+
assert!(provider.matches_model("mistral-large-latest"));
|
|
100
|
+
assert!(provider.matches_model("mistral-small-latest"));
|
|
101
|
+
assert!(provider.matches_model("codestral-latest"));
|
|
102
|
+
assert!(provider.matches_model("pixtral-large-latest"));
|
|
103
|
+
assert!(provider.matches_model("mistral/mistral-large-latest"));
|
|
104
|
+
assert!(!provider.matches_model("gpt-4"));
|
|
105
|
+
assert!(!provider.matches_model("claude-3"));
|
|
106
|
+
assert!(!provider.matches_model("command-r"));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
#[test]
|
|
110
|
+
fn test_mistral_strip_prefix() {
|
|
111
|
+
let provider = MistralProvider;
|
|
112
|
+
assert_eq!(
|
|
113
|
+
provider.strip_model_prefix("mistral/mistral-large-latest"),
|
|
114
|
+
"mistral-large-latest"
|
|
115
|
+
);
|
|
116
|
+
assert_eq!(
|
|
117
|
+
provider.strip_model_prefix("mistral-large-latest"),
|
|
118
|
+
"mistral-large-latest"
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
#[test]
|
|
123
|
+
fn test_mistral_endpoints_are_openai_compatible() {
|
|
124
|
+
let provider = MistralProvider;
|
|
125
|
+
assert_eq!(provider.chat_completions_path(), "/chat/completions");
|
|
126
|
+
assert_eq!(provider.embeddings_path(), "/embeddings");
|
|
127
|
+
assert_eq!(provider.models_path(), "/models");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
#[test]
|
|
131
|
+
fn test_mistral_transform_request_maps_tool_choice() {
|
|
132
|
+
let provider = MistralProvider;
|
|
133
|
+
let mut body = json!({
|
|
134
|
+
"model": "mistral-large-latest",
|
|
135
|
+
"messages": [{"role": "user", "content": "hello"}],
|
|
136
|
+
"tool_choice": "required"
|
|
137
|
+
});
|
|
138
|
+
provider.transform_request(&mut body).expect("transform should succeed");
|
|
139
|
+
assert_eq!(body["tool_choice"], "any");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
#[test]
|
|
143
|
+
fn test_mistral_transform_request_preserves_other_tool_choices() {
|
|
144
|
+
let provider = MistralProvider;
|
|
145
|
+
|
|
146
|
+
let mut body = json!({"tool_choice": "auto"});
|
|
147
|
+
provider.transform_request(&mut body).expect("transform should succeed");
|
|
148
|
+
assert_eq!(body["tool_choice"], "auto");
|
|
149
|
+
|
|
150
|
+
let mut body = json!({"tool_choice": "none"});
|
|
151
|
+
provider.transform_request(&mut body).expect("transform should succeed");
|
|
152
|
+
assert_eq!(body["tool_choice"], "none");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
#[test]
|
|
156
|
+
fn test_mistral_transform_request_strips_unsupported_params() {
|
|
157
|
+
let provider = MistralProvider;
|
|
158
|
+
let mut body = json!({
|
|
159
|
+
"model": "mistral-large-latest",
|
|
160
|
+
"messages": [{"role": "user", "content": "hello"}],
|
|
161
|
+
"parallel_tool_calls": true,
|
|
162
|
+
"logit_bias": {"123": 1.0},
|
|
163
|
+
"presence_penalty": 0.5,
|
|
164
|
+
"frequency_penalty": 0.5,
|
|
165
|
+
"temperature": 0.7
|
|
166
|
+
});
|
|
167
|
+
provider.transform_request(&mut body).expect("transform should succeed");
|
|
168
|
+
|
|
169
|
+
assert!(body.get("parallel_tool_calls").is_none());
|
|
170
|
+
assert!(body.get("logit_bias").is_none());
|
|
171
|
+
assert!(body.get("presence_penalty").is_none());
|
|
172
|
+
assert!(body.get("frequency_penalty").is_none());
|
|
173
|
+
// Supported params preserved.
|
|
174
|
+
assert_eq!(body["temperature"], 0.7);
|
|
175
|
+
assert_eq!(body["model"], "mistral-large-latest");
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
#[test]
|
|
179
|
+
fn test_mistral_transform_request_no_tool_choice() {
|
|
180
|
+
let provider = MistralProvider;
|
|
181
|
+
let mut body = json!({
|
|
182
|
+
"model": "mistral-large-latest",
|
|
183
|
+
"messages": [{"role": "user", "content": "hello"}]
|
|
184
|
+
});
|
|
185
|
+
provider.transform_request(&mut body).expect("transform should succeed");
|
|
186
|
+
assert!(body.get("tool_choice").is_none());
|
|
187
|
+
}
|
|
188
|
+
}
|