Implement SEP-1577: Sampling With Tools (#628)

* feat: implement SEP-1577 sampling with tools support

* feat: add TryFrom<Content> for backward-compatible migration
This commit is contained in:
Dale Seo 2026-02-06 06:51:26 -05:00 • committed by GitHub
commit 8bd3fcb890
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -59,6 +59,137 @@ pub struct RawAudioContent {
pub type AudioContent = Annotated<RawAudioContent>;
/// Tool call request from assistant (SEP-1577).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct ToolUseContent {
/// Unique identifier for this tool call
pub id: String,
/// Name of the tool to call
pub name: String,
/// Input arguments for the tool
pub input: super::JsonObject,
/// Optional metadata (preserved for caching)
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
pub meta: Option<super::Meta>,
}
/// Tool execution result in user message (SEP-1577).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct ToolResultContent {
/// Optional metadata
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
pub meta: Option<super::Meta>,
/// ID of the corresponding tool use
pub tool_use_id: String,
/// Content blocks returned by the tool
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub content: Vec<Content>,
/// Optional structured result
#[serde(skip_serializing_if = "Option::is_none")]
pub structured_content: Option<super::JsonObject>,
/// Whether tool execution failed
#[serde(skip_serializing_if = "Option::is_none")]
pub is_error: Option<bool>,
}
impl ToolUseContent {
pub fn new(id: impl Into<String>, name: impl Into<String>, input: super::JsonObject) -> Self {
Self {
id: id.into(),
name: name.into(),
input,
meta: None,
}
}
}
impl ToolResultContent {
pub fn new(tool_use_id: impl Into<String>, content: Vec<Content>) -> Self {
Self {
meta: None,
tool_use_id: tool_use_id.into(),
content,
structured_content: None,
is_error: None,
}
}
pub fn error(tool_use_id: impl Into<String>, content: Vec<Content>) -> Self {
Self {
meta: None,
tool_use_id: tool_use_id.into(),
content,
structured_content: None,
is_error: Some(true),
}
}
}
/// Assistant message content types (SEP-1577).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum AssistantMessageContent {
Text(RawTextContent),
Image(RawImageContent),
Audio(RawAudioContent),
ToolUse(ToolUseContent),
}
/// User message content types (SEP-1577).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum UserMessageContent {
Text(RawTextContent),
Image(RawImageContent),
Audio(RawAudioContent),
ToolResult(ToolResultContent),
}
impl AssistantMessageContent {
/// Create a text content
pub fn text(text: impl Into<String>) -> Self {
Self::Text(RawTextContent {
text: text.into(),
meta: None,
})
}
/// Create a tool use content
pub fn tool_use(
id: impl Into<String>,
name: impl Into<String>,
input: super::JsonObject,
) -> Self {
Self::ToolUse(ToolUseContent::new(id, name, input))
}
}
impl UserMessageContent {
/// Create a text content
pub fn text(text: impl Into<String>) -> Self {
Self::Text(RawTextContent {
text: text.into(),
meta: None,
})
}
/// Create a tool result content
pub fn tool_result(tool_use_id: impl Into<String>, content: Vec<Content>) -> Self {
Self::ToolResult(ToolResultContent::new(tool_use_id, content))
}
/// Create an error tool result content
pub fn tool_result_error(tool_use_id: impl Into<String>, content: Vec<Content>) -> Self {
Self::ToolResult(ToolResultContent::error(tool_use_id, content))
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]