Skip to content

Commit 430b0d1

Browse files
Nalleyeramitksingh1490autofix-ci[bot]
authored
feature: add minimax_cn provider (#2701)
Co-authored-by: Amit Singh <amitksingh1490@gmail.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent d525b30 commit 430b0d1

10 files changed

Lines changed: 163 additions & 46 deletions

File tree

crates/forge_app/src/infra.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ pub trait StrategyFactory: Send + Sync {
354354
&self,
355355
provider_id: forge_domain::ProviderId,
356356
auth_method: forge_domain::AuthMethod,
357-
required_params: Vec<forge_domain::URLParam>,
357+
required_params: Vec<forge_domain::URLParamSpec>,
358358
) -> anyhow::Result<Self::Strategy>;
359359
}
360360

crates/forge_domain/src/auth/auth_context.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use url::Url;
55

66
use super::{
77
ApiKey, AuthorizationCode, DeviceCode, OAuthConfig, PkceVerifier, State, URLParam,
8-
URLParamValue, UserCode,
8+
URLParamSpec, URLParamValue, UserCode,
99
};
1010

1111
#[derive(Debug, Clone, PartialEq, Deref, From)]
@@ -16,7 +16,7 @@ pub struct URLParameters(HashMap<URLParam, URLParamValue>);
1616
/// Request parameters for API key authentication
1717
#[derive(Debug, Clone)]
1818
pub struct ApiKeyRequest {
19-
pub required_params: Vec<URLParam>,
19+
pub required_params: Vec<URLParamSpec>,
2020
pub existing_params: Option<URLParameters>,
2121
pub api_key: Option<ApiKey>,
2222
}

crates/forge_domain/src/auth/new_types.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,44 @@ pub struct URLParam(String);
7676
#[serde(transparent)]
7777
pub struct URLParamValue(String);
7878

79+
/// A URL parameter specification with its name and optional preset options.
80+
///
81+
/// When `options` is `Some`, the UI presents a dropdown for selection.
82+
/// When `options` is `None`, the UI presents a free-text input.
83+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
84+
pub struct URLParamSpec {
85+
/// The parameter name used as the template variable and credential map key.
86+
pub name: URLParam,
87+
/// Optional list of allowed values. When present, the UI renders a
88+
/// dropdown.
89+
pub options: Option<Vec<String>>,
90+
}
91+
92+
impl URLParamSpec {
93+
/// Creates a `URLParamSpec` with only a name, rendering as a free-text
94+
/// input.
95+
pub fn new(name: impl Into<URLParam>) -> Self {
96+
Self { name: name.into(), options: None }
97+
}
98+
99+
/// Creates a `URLParamSpec` with preset options, rendering as a dropdown.
100+
pub fn with_options(name: impl Into<URLParam>, options: Vec<String>) -> Self {
101+
Self { name: name.into(), options: Some(options) }
102+
}
103+
}
104+
105+
impl From<URLParam> for URLParamSpec {
106+
fn from(name: URLParam) -> Self {
107+
Self::new(name)
108+
}
109+
}
110+
111+
impl From<String> for URLParamSpec {
112+
fn from(name: String) -> Self {
113+
Self::new(URLParam::from(name))
114+
}
115+
}
116+
79117
#[derive(
80118
Clone,
81119
Serialize,

crates/forge_domain/src/provider.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ pub struct Provider<T> {
211211
pub models: Option<ModelSource<T>>,
212212
pub auth_methods: Vec<crate::AuthMethod>,
213213
#[serde(default)]
214-
pub url_params: Vec<crate::URLParam>,
214+
pub url_params: Vec<crate::URLParamSpec>,
215215
pub credential: Option<AuthCredential>,
216216
/// Custom HTTP headers to include in API requests for this provider.
217217
#[serde(default, skip_serializing_if = "Option::is_none")]
@@ -298,7 +298,7 @@ impl AnyProvider {
298298
AnyProvider::Template(_) => None,
299299
}
300300
}
301-
pub fn url_params(&self) -> &[crate::URLParam] {
301+
pub fn url_params(&self) -> &[crate::URLParamSpec] {
302302
match self {
303303
AnyProvider::Url(p) => &p.url_params,
304304
AnyProvider::Template(p) => &p.url_params,

crates/forge_infra/src/auth/strategy.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::time::Duration;
33
use forge_app::{AuthStrategy, OAuthHttpProvider, StrategyFactory};
44
use forge_domain::{
55
ApiKey, ApiKeyRequest, AuthContextRequest, AuthContextResponse, AuthCredential, CodeRequest,
6-
DeviceCodeRequest, OAuthConfig, OAuthTokenResponse, OAuthTokens, ProviderId, URLParam,
6+
DeviceCodeRequest, OAuthConfig, OAuthTokenResponse, OAuthTokens, ProviderId, URLParamSpec,
77
};
88
use google_cloud_auth::credentials::Builder;
99
use oauth2::basic::BasicClient;
@@ -18,11 +18,11 @@ use crate::auth::util::*;
1818
/// API Key Strategy - Simple static key authentication
1919
pub struct ApiKeyStrategy {
2020
provider_id: ProviderId,
21-
required_params: Vec<URLParam>,
21+
required_params: Vec<URLParamSpec>,
2222
}
2323

2424
impl ApiKeyStrategy {
25-
pub fn new(provider_id: ProviderId, required_params: Vec<URLParam>) -> Self {
25+
pub fn new(provider_id: ProviderId, required_params: Vec<URLParamSpec>) -> Self {
2626
Self { provider_id, required_params }
2727
}
2828
}
@@ -348,11 +348,11 @@ impl AuthStrategy for OAuthWithApiKeyStrategy {
348348
/// Uses Google Cloud SDK's ADC mechanism with automatic token refresh
349349
pub struct GoogleAdcStrategy {
350350
provider_id: ProviderId,
351-
required_params: Vec<URLParam>,
351+
required_params: Vec<URLParamSpec>,
352352
}
353353

354354
impl GoogleAdcStrategy {
355-
pub fn new(provider_id: ProviderId, required_params: Vec<URLParam>) -> Self {
355+
pub fn new(provider_id: ProviderId, required_params: Vec<URLParamSpec>) -> Self {
356356
Self { provider_id, required_params }
357357
}
358358
}
@@ -1036,7 +1036,7 @@ impl StrategyFactory for ForgeAuthStrategyFactory {
10361036
&self,
10371037
provider_id: ProviderId,
10381038
auth_method: forge_domain::AuthMethod,
1039-
required_params: Vec<URLParam>,
1039+
required_params: Vec<URLParamSpec>,
10401040
) -> anyhow::Result<Self::Strategy> {
10411041
match auth_method {
10421042
forge_domain::AuthMethod::ApiKey => Ok(AnyAuthStrategy::ApiKey(ApiKeyStrategy::new(
@@ -1093,6 +1093,7 @@ impl StrategyFactory for ForgeAuthStrategyFactory {
10931093
mod tests {
10941094
use std::collections::HashMap;
10951095

1096+
use forge_domain::URLParam;
10961097
use pretty_assertions::assert_eq;
10971098

10981099
use super::*;

crates/forge_infra/src/forge_infra.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use forge_app::{
1010
StrategyFactory, UserInfra, WalkerInfra,
1111
};
1212
use forge_domain::{
13-
AuthMethod, CommandOutput, FileInfo as FileInfoData, McpServerConfig, ProviderId, URLParam,
13+
AuthMethod, CommandOutput, FileInfo as FileInfoData, McpServerConfig, ProviderId, URLParamSpec,
1414
};
1515
use reqwest::header::HeaderMap;
1616
use reqwest::{Response, Url};
@@ -312,7 +312,7 @@ impl StrategyFactory for ForgeInfra {
312312
&self,
313313
provider_id: ProviderId,
314314
method: AuthMethod,
315-
required_params: Vec<URLParam>,
315+
required_params: Vec<URLParamSpec>,
316316
) -> anyhow::Result<Self::Strategy> {
317317
self.strategy_factory
318318
.create_auth_strategy(provider_id, method, required_params)

crates/forge_main/src/ui.rs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2193,22 +2193,39 @@ impl<A: API + ConsoleWriter + 'static, F: Fn() -> A + Send + Sync> UI<A, F> {
21932193
.required_params
21942194
.iter()
21952195
.map(|param| {
2196-
let mut input = ForgeWidget::input(format!("Enter {param}"));
2197-
2198-
// Add default value if it exists in the credential
2199-
if let Some(params) = existing_url_params
2200-
&& let Some(default_value) = params.get(param)
2201-
{
2202-
input = input.with_default(default_value.as_str());
2203-
}
2196+
let param_value = if let Some(options) = &param.options {
2197+
// Dropdown path: user selects from preset options
2198+
let starting = existing_url_params
2199+
.and_then(|p| p.get(&param.name))
2200+
.and_then(|v| options.iter().position(|o| o.as_str() == v.as_str()))
2201+
.unwrap_or(0);
2202+
ForgeWidget::select(format!("Select {}", param.name), options.clone())
2203+
.with_starting_cursor(starting)
2204+
.prompt()?
2205+
.context("Parameter selection cancelled")?
2206+
} else {
2207+
// Free-text path (existing behavior)
2208+
let mut input = ForgeWidget::input(format!("Enter {}", param.name));
2209+
2210+
// Add default value if it exists in the credential
2211+
if let Some(params) = existing_url_params
2212+
&& let Some(default_value) = params.get(&param.name)
2213+
{
2214+
input = input.with_default(default_value.as_str());
2215+
}
22042216

2205-
let param_value = input.prompt()?.context("Parameter input cancelled")?;
2217+
let param_value = input.prompt()?.context("Parameter input cancelled")?;
22062218

2207-
anyhow::ensure!(!param_value.trim().is_empty(), "{param} cannot be empty");
2219+
anyhow::ensure!(
2220+
!param_value.trim().is_empty(),
2221+
"{} cannot be empty",
2222+
param.name
2223+
);
22082224

2209-
let param_value = param_value.trim_end_matches('/').to_string();
2225+
param_value.trim_end_matches('/').to_string()
2226+
};
22102227

2211-
Ok((param.to_string(), param_value))
2228+
Ok((param.name.to_string(), param_value))
22122229
})
22132230
.collect::<anyhow::Result<HashMap<_, _>>>()?;
22142231

crates/forge_repo/src/forge_repo.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ impl<F: StrategyFactory> StrategyFactory for ForgeRepo<F> {
480480
&self,
481481
provider_id: ProviderId,
482482
auth_method: forge_domain::AuthMethod,
483-
required_params: Vec<forge_domain::URLParam>,
483+
required_params: Vec<forge_domain::URLParamSpec>,
484484
) -> anyhow::Result<Self::Strategy> {
485485
self.infra
486486
.create_auth_strategy(provider_id, auth_method, required_params)

crates/forge_repo/src/provider/provider.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1851,9 +1851,9 @@
18511851
{
18521852
"id": "minimax",
18531853
"api_key_vars": "MINIMAX_API_KEY",
1854-
"url_param_vars": [],
1854+
"url_param_vars": [{"name": "MINIMAX_DOMAIN", "options": ["api.minimax.io", "api.minimaxi.com"]}],
18551855
"response_type": "Anthropic",
1856-
"url": "https://api.minimax.io/anthropic/v1/messages",
1856+
"url": "https://{{#if MINIMAX_DOMAIN}}{{MINIMAX_DOMAIN}}{{else}}api.minimax.io{{/if}}/anthropic/v1/messages",
18571857
"models": [
18581858
{
18591859
"id": "MiniMax-M2.7",

0 commit comments

Comments
 (0)