mirror of
https://github.com/zed-industries/zed.git
synced 2026-10-19 11:04:06 +00:00
mistral: Add Z.ai GLM (latest version) and show thinking toggle for supported models (#63535)
# Objective - Add Z.ai GLM, now hosted by Mistral as a third-party open model, to the built-in Mistral model list. - Fix the thinking selector never appearing for Mistral models that support reasoning (mistral-medium-latest, mistral-small-latest). ## Solution - Added a `ZaiGlmLatest` variant to `mistral::Model` that uses Mistral's `zai-glm-latest` model id: 1M token context window, tool calling enabled, image input disabled, thinking enabled. The provider builds its model list by iterating this enum, so no further wiring was needed. - This PR initially targeted GLM 5.2 (`zai-glm-5-2`), but Mistral released 5.3 while it was open. Switching to the `zai-glm-latest` alias keeps Zed on the newest GLM release without requiring a new PR each time Mistral bumps the version, mirroring how Mistral's own model ids work for their flagship models. - Implemented the missing `LanguageModel::supports_thinking()` override in the Mistral provider, delegating to `mistral::Model::supports_thinking()`. The request-building code already sent `reasoning_effort` and serialized thinking parts, but the UI toggle was hidden because the trait method fell back to the default `false`. ## Testing - `./script/clippy -p mistral` and `cargo check -p language_models` pass. - Existing unit tests in the provider cover the `reasoning_effort` request mapping; no new behavior beyond the enum variant and the trait delegation. - Reviewers can test with a Mistral API key: `zai-glm-latest` appears in the model dropdown, and the thinking toggle now shows for zai-glm-latest, mistral-medium-latest, and mistral-small-latest. - Tested on macOS; changes are platform-independent. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Added Z.ai GLM to the Mistral provider and fixed the thinking toggle for Mistral Small and Medium. --------- Co-authored-by: dino <dinojoaocosta@gmail.com>
This commit is contained in:
parent
02238cd4b7
commit
a4b9dc5f52
2 changed files with 79 additions and 16 deletions
|
|
@ -319,6 +319,14 @@ impl LanguageModel for MistralLanguageModel {
|
|||
self.model.supports_images()
|
||||
}
|
||||
|
||||
fn supports_thinking(&self) -> bool {
|
||||
self.model.supports_thinking()
|
||||
}
|
||||
|
||||
fn supports_disabling_thinking(&self) -> bool {
|
||||
self.model.supports_disabling_thinking()
|
||||
}
|
||||
|
||||
fn telemetry_id(&self) -> String {
|
||||
format!("mistral/{}", self.model.id())
|
||||
}
|
||||
|
|
@ -583,9 +591,18 @@ pub fn into_mistral(
|
|||
})
|
||||
})
|
||||
.collect::<Result<_>>()?,
|
||||
reasoning_effort: if model.supports_thinking() && request.thinking_allowed {
|
||||
reasoning_effort: if !model.supports_thinking() {
|
||||
None
|
||||
} else if request.thinking_allowed {
|
||||
Some(mistral::ReasoningEffort::High)
|
||||
} else if model.supports_disabling_thinking() {
|
||||
// Explicitly disable thinking rather than relying on the API's
|
||||
// default.
|
||||
Some(mistral::ReasoningEffort::None)
|
||||
} else {
|
||||
// Models for which thinking can't be disabled will reject
|
||||
// "none" as the `reasoning_effort` value. Omitting the field is
|
||||
// the only way to request the API's default effort.
|
||||
None
|
||||
},
|
||||
},
|
||||
|
|
@ -937,20 +954,52 @@ mod tests {
|
|||
max_output_tokens: None,
|
||||
};
|
||||
|
||||
let (mistral_request, _) =
|
||||
into_mistral(request(true), mistral::Model::MistralMediumLatest, None).unwrap();
|
||||
assert_eq!(
|
||||
mistral_request.reasoning_effort,
|
||||
Some(mistral::ReasoningEffort::High)
|
||||
);
|
||||
let cases = vec![
|
||||
(
|
||||
mistral::Model::MistralSmallLatest,
|
||||
true,
|
||||
Some(mistral::ReasoningEffort::High),
|
||||
),
|
||||
(
|
||||
mistral::Model::MistralSmallLatest,
|
||||
false,
|
||||
Some(mistral::ReasoningEffort::None),
|
||||
),
|
||||
(
|
||||
mistral::Model::MistralMediumLatest,
|
||||
true,
|
||||
Some(mistral::ReasoningEffort::High),
|
||||
),
|
||||
(
|
||||
mistral::Model::MistralMediumLatest,
|
||||
false,
|
||||
Some(mistral::ReasoningEffort::None),
|
||||
),
|
||||
(
|
||||
mistral::Model::ZaiGlmLatest,
|
||||
true,
|
||||
Some(mistral::ReasoningEffort::High),
|
||||
),
|
||||
// Z.ai GLM always thinks and rejects "none", so the field is
|
||||
// omitted when the toggle is off instead of sending an explicit
|
||||
// value.
|
||||
(mistral::Model::ZaiGlmLatest, false, None),
|
||||
// Ensure that, for non-thinking models, `reasoning_effort` is
|
||||
// always omitted.
|
||||
(mistral::Model::CodestralLatest, true, None),
|
||||
(mistral::Model::CodestralLatest, false, None),
|
||||
];
|
||||
|
||||
let (mistral_request, _) =
|
||||
into_mistral(request(false), mistral::Model::MistralMediumLatest, None).unwrap();
|
||||
assert_eq!(mistral_request.reasoning_effort, None);
|
||||
for (model, thinking_allowed, reasoning_effort) in cases {
|
||||
let (mistral_request, _) = into_mistral(request(thinking_allowed), model, None)
|
||||
.expect("should be able to convert request");
|
||||
|
||||
let (mistral_request, _) =
|
||||
into_mistral(request(true), mistral::Model::CodestralLatest, None).unwrap();
|
||||
assert_eq!(mistral_request.reasoning_effort, None);
|
||||
assert_eq!(
|
||||
mistral_request.reasoning_effort, reasoning_effort,
|
||||
"reasoning_effort should match, expected {:?}, got {:?}",
|
||||
reasoning_effort, mistral_request.reasoning_effort
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -66,6 +66,9 @@ pub enum Model {
|
|||
#[serde(rename = "ministral-14b-latest", alias = "ministral-14b-latest")]
|
||||
Ministral14bLatest,
|
||||
|
||||
#[serde(rename = "zai-glm-latest", alias = "zai-glm-latest")]
|
||||
ZaiGlmLatest,
|
||||
|
||||
#[serde(rename = "custom")]
|
||||
Custom {
|
||||
name: String,
|
||||
|
|
@ -94,6 +97,7 @@ impl Model {
|
|||
"ministral-3b-latest" => Ok(Self::Ministral3bLatest),
|
||||
"ministral-8b-latest" => Ok(Self::Ministral8bLatest),
|
||||
"ministral-14b-latest" => Ok(Self::Ministral14bLatest),
|
||||
"zai-glm-latest" => Ok(Self::ZaiGlmLatest),
|
||||
invalid_id => anyhow::bail!("invalid model id '{invalid_id}'"),
|
||||
}
|
||||
}
|
||||
|
|
@ -107,6 +111,7 @@ impl Model {
|
|||
Self::Ministral3bLatest => "ministral-3b-latest",
|
||||
Self::Ministral8bLatest => "ministral-8b-latest",
|
||||
Self::Ministral14bLatest => "ministral-14b-latest",
|
||||
Self::ZaiGlmLatest => "zai-glm-latest",
|
||||
Self::Custom { name, .. } => name,
|
||||
}
|
||||
}
|
||||
|
|
@ -120,6 +125,7 @@ impl Model {
|
|||
Self::Ministral3bLatest => "ministral-3b-latest",
|
||||
Self::Ministral8bLatest => "ministral-8b-latest",
|
||||
Self::Ministral14bLatest => "ministral-14b-latest",
|
||||
Self::ZaiGlmLatest => "zai-glm-latest",
|
||||
Self::Custom {
|
||||
name, display_name, ..
|
||||
} => display_name.as_ref().unwrap_or(name),
|
||||
|
|
@ -135,6 +141,7 @@ impl Model {
|
|||
Self::Ministral3bLatest => 256000,
|
||||
Self::Ministral8bLatest => 256000,
|
||||
Self::Ministral14bLatest => 256000,
|
||||
Self::ZaiGlmLatest => 1_048_576,
|
||||
Self::Custom { max_tokens, .. } => *max_tokens,
|
||||
}
|
||||
}
|
||||
|
|
@ -156,7 +163,8 @@ impl Model {
|
|||
| Self::MistralSmallLatest
|
||||
| Self::Ministral3bLatest
|
||||
| Self::Ministral8bLatest
|
||||
| Self::Ministral14bLatest => true,
|
||||
| Self::Ministral14bLatest
|
||||
| Self::ZaiGlmLatest => true,
|
||||
Self::Custom { supports_tools, .. } => supports_tools.unwrap_or(false),
|
||||
}
|
||||
}
|
||||
|
|
@ -169,7 +177,7 @@ impl Model {
|
|||
| Self::Ministral3bLatest
|
||||
| Self::Ministral8bLatest
|
||||
| Self::Ministral14bLatest => true,
|
||||
Self::CodestralLatest => false,
|
||||
Self::CodestralLatest | Self::ZaiGlmLatest => false,
|
||||
Self::Custom {
|
||||
supports_images, ..
|
||||
} => supports_images.unwrap_or(false),
|
||||
|
|
@ -178,13 +186,19 @@ impl Model {
|
|||
|
||||
pub fn supports_thinking(&self) -> bool {
|
||||
match self {
|
||||
Self::MistralMediumLatest | Self::MistralSmallLatest => true,
|
||||
Self::MistralMediumLatest | Self::MistralSmallLatest | Self::ZaiGlmLatest => true,
|
||||
Self::Custom {
|
||||
supports_thinking, ..
|
||||
} => supports_thinking.unwrap_or(false),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether thinking can be turned off. Z.ai GLM models always think and
|
||||
/// reject `reasoning_effort: "none"`.
|
||||
pub fn supports_disabling_thinking(&self) -> bool {
|
||||
!matches!(self, Self::ZaiGlmLatest)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue