fix(protocol): accept upstream model tool mode metadata

This commit is contained in:
Auto Review
2026-05-31 16:27:26 +10:00
parent 6b907dec85
commit efc01e0669
+66
View File
@@ -8,7 +8,9 @@ use std::str::FromStr;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::de::DeserializeOwned;
use strum::IntoEnumIterator;
use strum_macros::Display;
use strum_macros::EnumIter;
@@ -228,6 +230,25 @@ pub enum TruncationMode {
Tokens,
}
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, TS, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ToolMode {
Direct,
CodeMode,
CodeModeOnly,
}
fn deserialize_optional_model_selector<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
where
D: Deserializer<'de>,
T: DeserializeOwned,
{
let Some(value) = Option::<String>::deserialize(deserializer)? else {
return Ok(None);
};
Ok(serde_json::from_value(serde_json::Value::String(value)).ok())
}
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, TS, JsonSchema)]
pub struct TruncationPolicyConfig {
pub mode: TruncationMode,
@@ -314,6 +335,12 @@ pub struct ModelInfo {
pub input_modalities: Vec<InputModality>,
#[serde(default)]
pub supports_search_tool: bool,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_optional_model_selector"
)]
pub tool_mode: Option<ToolMode>,
/// When true, this model should use websocket transport even when websocket features are off.
#[serde(default)]
pub prefer_websockets: bool,
@@ -613,6 +640,7 @@ mod tests {
experimental_supported_tools: vec![],
input_modalities: default_input_modalities(),
supports_search_tool: false,
tool_mode: None,
prefer_websockets: false,
used_fallback_model_metadata: false,
}
@@ -829,6 +857,44 @@ mod tests {
assert!(!model.supports_image_detail_original);
assert!(!model.supports_search_tool);
assert_eq!(model.web_search_tool_type, WebSearchToolType::Text);
assert_eq!(model.tool_mode, None);
}
#[test]
fn model_info_deserializes_known_tool_mode() {
let mut value = serde_json::to_value(test_model(None)).expect("serialize test model");
let object = value
.as_object_mut()
.expect("model info should be an object");
object.insert(
"tool_mode".to_string(),
serde_json::Value::String("code_mode_only".to_string()),
);
let model = serde_json::from_value::<ModelInfo>(value).expect("deserialize model info");
assert_eq!(model.tool_mode, Some(ToolMode::CodeModeOnly));
}
#[test]
fn model_info_treats_unknown_tool_mode_as_omitted() {
let mut value = serde_json::to_value(test_model(None)).expect("serialize test model");
let object = value
.as_object_mut()
.expect("model info should be an object");
object.insert(
"tool_mode".to_string(),
serde_json::Value::String("future_tool_mode".to_string()),
);
let model = serde_json::from_value::<ModelInfo>(value).expect("deserialize model info");
assert_eq!(model.tool_mode, None);
let serialized = serde_json::to_value(model).expect("serialize model info");
let object = serialized
.as_object()
.expect("model info should be an object");
assert!(!object.contains_key("tool_mode"));
}
#[test]