Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/config/entities/providers-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"deepseek",
"gemini",
"groq",
"mistral",
"openai",
"openrouter"
]
Expand Down Expand Up @@ -52,6 +53,7 @@
"deepseek",
"gemini",
"groq",
"mistral",
"openai",
"openrouter"
]
Expand Down
8 changes: 8 additions & 0 deletions src/config/entities/providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ pub enum ProviderConfig {
Gemini(configs::GeminiProviderConfig),
#[serde(rename = "groq")]
Groq(configs::GroqProviderConfig),
#[serde(rename = "mistral")]
Mistral(configs::MistralProviderConfig),
#[serde(rename = "openai")]
OpenAI(configs::OpenAIProviderConfig),
#[serde(rename = "openrouter")]
Expand All @@ -49,6 +51,7 @@ impl ProviderConfig {
Self::DeepSeek(_) => identifiers::DEEPSEEK,
Self::Gemini(_) => identifiers::GEMINI,
Self::Groq(_) => identifiers::GROQ,
Self::Mistral(_) => identifiers::MISTRAL,
Self::OpenAI(_) => identifiers::OPENAI,
Self::OpenRouter(_) => identifiers::OPENROUTER,
}
Expand Down Expand Up @@ -151,6 +154,11 @@ mod tests {
"type": "groq",
"config": { "api_key": "test_key" }
}), true, None)]
#[case::mistral_ok(json!({
"name": "mistral-primary",
"type": "mistral",
"config": { "api_key": "test_key" }
}), true, None)]
#[case::missing_type(json!({
"name": "openai-primary",
"config": { "api_key": "test_key" }
Expand Down
45 changes: 45 additions & 0 deletions src/gateway/providers/mistral.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use serde::{Deserialize, Serialize};

use crate::gateway::providers::macros::provider;

/// Provider identifier string used to look up Mistral in the gateway registry.
pub const IDENTIFIER: &str = "mistral";

/// Configuration for a Mistral provider deployment.
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct MistralProviderConfig {
pub api_key: String,

#[serde(skip_serializing_if = "Option::is_none")]
pub api_base: Option<String>,
}

provider!(Mistral {
display_name: "mistral",
base_url: "https://api.mistral.ai",
auth: bearer,
quirks: {
tool_args_may_be_object: true,
}
});

#[cfg(test)]
mod tests {
use super::Mistral;
use crate::gateway::traits::{ChatTransform, ProviderMeta};

#[test]
fn provider_macro_expands_correctly() {
let provider = Mistral;

pretty_assertions::assert_eq!(provider.name(), "mistral");
pretty_assertions::assert_eq!(provider.default_base_url(), "https://api.mistral.ai");

pretty_assertions::assert_eq!(
provider.build_url(provider.default_base_url(), "ignored"),
"https://api.mistral.ai/v1/chat/completions"
);

pretty_assertions::assert_eq!(provider.default_quirks().tool_args_may_be_object, true);
}
}
11 changes: 8 additions & 3 deletions src/gateway/providers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod deepseek;
pub mod gemini;
pub mod groq;
pub mod macros;
pub mod mistral;
pub mod openai;
pub mod openrouter;

Expand All @@ -14,18 +15,20 @@ pub use bedrock::BedrockDef;
pub use deepseek::DeepSeek;
pub use gemini::GoogleDef;
pub use groq::Groq;
pub use mistral::Mistral;
pub use openai::OpenAIDef;
pub use openrouter::OpenRouter;

pub mod identifiers {
use super::{anthropic, azure, bedrock, deepseek, gemini, groq, openai, openrouter};
use super::{anthropic, azure, bedrock, deepseek, gemini, groq, mistral, openai, openrouter};

pub const ANTHROPIC: &str = anthropic::IDENTIFIER;
pub const AZURE: &str = azure::IDENTIFIER;
pub const BEDROCK: &str = bedrock::IDENTIFIER;
pub const DEEPSEEK: &str = deepseek::IDENTIFIER;
pub const GEMINI: &str = gemini::IDENTIFIER;
pub const GROQ: &str = groq::IDENTIFIER;
pub const MISTRAL: &str = mistral::IDENTIFIER;
pub const OPENAI: &str = openai::IDENTIFIER;
pub const OPENROUTER: &str = openrouter::IDENTIFIER;
}
Expand All @@ -34,8 +37,8 @@ pub mod configs {
pub use super::{
anthropic::AnthropicProviderConfig, azure::AzureProviderConfig,
bedrock::BedrockProviderConfig, deepseek::DeepSeekProviderConfig,
gemini::GeminiProviderConfig, groq::GroqProviderConfig, openai::OpenAIProviderConfig,
openrouter::OpenRouterProviderConfig,
gemini::GeminiProviderConfig, groq::GroqProviderConfig, mistral::MistralProviderConfig,
openai::OpenAIProviderConfig, openrouter::OpenRouterProviderConfig,
};
}

Expand All @@ -49,6 +52,7 @@ pub fn default_provider_registry() -> Result<ProviderRegistry> {
.register(DeepSeek)?
.register(GoogleDef)?
.register(Groq)?
.register(Mistral)?
.register(OpenAIDef)?
.register(OpenRouter)?;
Ok(builder.build())
Expand All @@ -68,6 +72,7 @@ mod tests {
assert_eq!(registry.get("bedrock").unwrap().name(), "bedrock");
assert_eq!(registry.get("gemini").unwrap().name(), "gemini");
assert_eq!(registry.get("groq").unwrap().name(), "groq");
assert_eq!(registry.get("mistral").unwrap().name(), "mistral");
Comment thread
bzp2010 marked this conversation as resolved.
assert_eq!(registry.get("deepseek").unwrap().name(), "deepseek");
assert_eq!(registry.get("openrouter").unwrap().name(), "openrouter");
assert!(registry.get("missing").is_none());
Expand Down
22 changes: 21 additions & 1 deletion src/proxy/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ fn provider_auth_and_base_url(config: &ProviderConfig) -> Result<(ProviderAuth,
ProviderAuth::ApiKey(config.api_key.clone()),
parse_base_url(config.api_base.as_deref())?,
),
ProviderConfig::Mistral(config) => (
ProviderAuth::ApiKey(config.api_key.clone()),
parse_base_url(config.api_base.as_deref())?,
),
ProviderConfig::OpenAI(config) => (
ProviderAuth::ApiKey(config.api_key.clone()),
parse_base_url(config.api_base.as_deref())?,
Expand Down Expand Up @@ -147,7 +151,7 @@ mod tests {
use crate::{
config::entities::providers::ProviderConfig,
gateway::providers::configs::{
AzureProviderConfig, BedrockProviderConfig, GroqProviderConfig,
AzureProviderConfig, BedrockProviderConfig, GroqProviderConfig, MistralProviderConfig,
OpenRouterProviderConfig,
},
};
Expand Down Expand Up @@ -223,6 +227,22 @@ mod tests {
);
}

#[test]
fn provider_auth_and_base_url_returns_mistral_api_key_and_optional_base_url() {
let config = ProviderConfig::Mistral(MistralProviderConfig {
api_key: "mistral-key".into(),
api_base: Some("https://api.mistral.ai".into()),
});

let (auth, base_url_override) = provider_auth_and_base_url(&config).unwrap();

assert_eq!(auth.api_key_for("mistral").unwrap(), "mistral-key");
assert_eq!(
base_url_override.as_ref().map(Url::as_str),
Some("https://api.mistral.ai/")
);
}
Comment thread
bzp2010 marked this conversation as resolved.

#[test]
fn provider_auth_and_base_url_returns_bedrock_static_credentials() {
let config = ProviderConfig::Bedrock(BedrockProviderConfig {
Expand Down
1 change: 1 addition & 0 deletions ui/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@
"openai": "OpenAI",
"openrouter": "OpenRouter",
"groq": "Groq",
"mistral": "Mistral",
"azure": "Azure OpenAI",
"anthropic": "Anthropic",
"gemini": "Gemini",
Expand Down
1 change: 1 addition & 0 deletions ui/src/i18n/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@
"openai": "OpenAI",
"openrouter": "OpenRouter",
"groq": "Groq",
"mistral": "Mistral",
"azure": "Azure OpenAI",
"anthropic": "Anthropic",
"gemini": "Gemini",
Expand Down
6 changes: 6 additions & 0 deletions ui/src/lib/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const PROVIDER_TYPE_VARIANTS = [
'openai',
'openrouter',
'groq',
'mistral',
'azure',
'anthropic',
'gemini',
Expand Down Expand Up @@ -105,6 +106,11 @@ export type Provider =
type: 'groq';
config: ApiBaseProviderConfig;
}
| {
name: string;
type: 'mistral';
config: ApiBaseProviderConfig;
}
| {
name: string;
type: 'bedrock';
Expand Down