feat!: relax tool result structuredContent type (#919)
This commit is contained in:
parent
415852806d
commit
b8a936c4f5
6 changed files with 38 additions and 30 deletions
|
|
@ -10,7 +10,7 @@
|
||||||
// ToolUseContent/ToolResultContent are SEP-2577-deprecated; internal references are expected.
|
// ToolUseContent/ToolResultContent are SEP-2577-deprecated; internal references are expected.
|
||||||
#![expect(deprecated)]
|
#![expect(deprecated)]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::{Value, json};
|
||||||
|
|
||||||
use super::{Annotations, Meta, resource::ResourceContents};
|
use super::{Annotations, Meta, resource::ResourceContents};
|
||||||
|
|
||||||
|
|
@ -207,7 +207,7 @@ pub struct ToolResultContent {
|
||||||
pub tool_use_id: String,
|
pub tool_use_id: String,
|
||||||
pub content: Vec<ContentBlock>,
|
pub content: Vec<ContentBlock>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub structured_content: Option<super::JsonObject>,
|
pub structured_content: Option<Value>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub is_error: Option<bool>,
|
pub is_error: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2352,13 +2352,7 @@
|
||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"structuredContent": {
|
"structuredContent": true,
|
||||||
"type": [
|
|
||||||
"object",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"additionalProperties": true
|
|
||||||
},
|
|
||||||
"toolUseId": {
|
"toolUseId": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2352,13 +2352,7 @@
|
||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"structuredContent": {
|
"structuredContent": true,
|
||||||
"type": [
|
|
||||||
"object",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"additionalProperties": true
|
|
||||||
},
|
|
||||||
"toolUseId": {
|
"toolUseId": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3454,13 +3454,7 @@
|
||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"structuredContent": {
|
"structuredContent": true,
|
||||||
"type": [
|
|
||||||
"object",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"additionalProperties": true
|
|
||||||
},
|
|
||||||
"toolUseId": {
|
"toolUseId": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3454,13 +3454,7 @@
|
||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"structuredContent": {
|
"structuredContent": true,
|
||||||
"type": [
|
|
||||||
"object",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"additionalProperties": true
|
|
||||||
},
|
|
||||||
"toolUseId": {
|
"toolUseId": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -370,6 +370,38 @@ fn test_tool_result_content_requires_content() {
|
||||||
assert!(err.to_string().contains("missing field `content`"));
|
assert!(err.to_string().contains("missing field `content`"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_tool_result_content_with_array_structured_content() -> Result<()> {
|
||||||
|
let structured =
|
||||||
|
serde_json::json!([{ "city": "SF", "temp": 72 }, { "city": "NY", "temp": 65 }]);
|
||||||
|
let mut tool_result = ToolResultContent::new("call_123", vec![ContentBlock::text("forecast")]);
|
||||||
|
tool_result.structured_content = Some(structured);
|
||||||
|
|
||||||
|
let json = serde_json::to_string(&tool_result)?;
|
||||||
|
let deserialized: ToolResultContent = serde_json::from_str(&json)?;
|
||||||
|
assert_eq!(tool_result, deserialized);
|
||||||
|
assert!(deserialized.structured_content.unwrap().is_array());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_tool_result_content_with_primitive_structured_content() -> Result<()> {
|
||||||
|
let structured = serde_json::json!(42);
|
||||||
|
let mut tool_result = ToolResultContent::new("call_123", vec![ContentBlock::text("count")]);
|
||||||
|
tool_result.structured_content = Some(structured);
|
||||||
|
|
||||||
|
let json = serde_json::to_string(&tool_result)?;
|
||||||
|
let deserialized: ToolResultContent = serde_json::from_str(&json)?;
|
||||||
|
assert_eq!(tool_result, deserialized);
|
||||||
|
assert!(matches!(
|
||||||
|
deserialized.structured_content,
|
||||||
|
Some(serde_json::Value::Number(_))
|
||||||
|
));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_sampling_message_with_tool_use() -> Result<()> {
|
async fn test_sampling_message_with_tool_use() -> Result<()> {
|
||||||
let message = SamplingMessage::assistant_tool_use(
|
let message = SamplingMessage::assistant_tool_use(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue