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
45 changes: 45 additions & 0 deletions crates/protocols/src/responses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,30 @@ pub enum ResponseTool {
LocalShell,
}

impl ResponseTool {
/// Wire `type` tag for this variant — matches the variant's
/// `#[serde(rename = ...)]` attribute. Stable across serde
/// roundtrips and safe to use as a discriminator string.
pub fn as_str(&self) -> &'static str {
match self {
ResponseTool::Function(_) => "function",
ResponseTool::WebSearchPreview(_) => "web_search_preview",
ResponseTool::WebSearch(_) => "web_search",
ResponseTool::CodeInterpreter(_) => "code_interpreter",
ResponseTool::Mcp(_) => "mcp",
ResponseTool::FileSearch(_) => "file_search",
ResponseTool::ImageGeneration(_) => "image_generation",
ResponseTool::Computer => "computer",
ResponseTool::ComputerUsePreview(_) => "computer_use_preview",
ResponseTool::Custom(_) => "custom",
ResponseTool::Namespace(_) => "namespace",
ResponseTool::Shell(_) => "shell",
ResponseTool::ApplyPatch => "apply_patch",
ResponseTool::LocalShell => "local_shell",
}
}
}

/// Payload carried by [`ResponseTool::Namespace`].
///
/// Using a dedicated struct (rather than inline struct-variant fields) lets
Expand Down Expand Up @@ -3883,3 +3907,24 @@ impl ResponseReasoningContent {
Self::ReasoningText { text }
}
}

#[cfg(test)]
mod tests {
use super::*;

/// Lock `as_str()` to the canonical serde tag for the unit variants
/// — drift between the two would produce inconsistent wire labels
/// across dispatch paths and serializers.
#[test]
fn response_tool_as_str_matches_serde_tag_for_unit_variants() {
for tool in [
ResponseTool::Computer,
ResponseTool::ApplyPatch,
ResponseTool::LocalShell,
] {
let serialized = serde_json::to_value(&tool).unwrap();
let serde_tag = serialized.get("type").and_then(|v| v.as_str()).unwrap();
assert_eq!(tool.as_str(), serde_tag);
}
}
Comment thread
slin1237 marked this conversation as resolved.
}
23 changes: 1 addition & 22 deletions model_gateway/src/routers/grpc/harmony/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -543,28 +543,7 @@ impl HarmonyBuilder {
let tool_types: Vec<&str> = request
.tools
.as_ref()
.map(|tools| {
tools
.iter()
.map(|tool| match tool {
ResponseTool::Function(_) => "function",
ResponseTool::WebSearchPreview(_) => "web_search_preview",
ResponseTool::WebSearch(_) => "web_search",
ResponseTool::CodeInterpreter(_) => "code_interpreter",
ResponseTool::Mcp(_) => "mcp",
ResponseTool::FileSearch(_) => "file_search",
ResponseTool::ImageGeneration(_) => "image_generation",
ResponseTool::Computer => "computer",
ResponseTool::ComputerUsePreview(_) => "computer_use_preview",
ResponseTool::Custom(_) => "custom",
ResponseTool::Namespace(_) => "namespace",
ResponseTool::Shell(_) => "shell",
ResponseTool::ApplyPatch => "apply_patch",
// T5 schema-only: forced-cascade arm, no behavior.
ResponseTool::LocalShell => "local_shell",
})
.collect()
})
.map(|tools| tools.iter().map(ResponseTool::as_str).collect())
.unwrap_or_default();

let with_custom_tools = has_custom_tools(&tool_types);
Expand Down
Loading