@@ -1580,6 +1580,85 @@ impl TaskAugmentedRequestParamsMeta for CreateMessageRequestParams {
15801580 }
15811581}
15821582
1583+ impl CreateMessageRequestParams {
1584+ /// Validate the sampling request parameters per SEP-1577 spec requirements.
1585+ ///
1586+ /// Checks:
1587+ /// - ToolUse content is only allowed in assistant messages
1588+ /// - ToolResult content is only allowed in user messages
1589+ /// - Messages with tool result content MUST NOT contain other content types
1590+ /// - Every assistant ToolUse must be balanced with a corresponding user ToolResult
1591+ pub fn validate ( & self ) -> Result < ( ) , String > {
1592+ for msg in & self . messages {
1593+ for content in msg. content . iter ( ) {
1594+ // ToolUse only in assistant messages, ToolResult only in user messages
1595+ match content {
1596+ SamplingMessageContent :: ToolUse ( _) if msg. role != Role :: Assistant => {
1597+ return Err ( "ToolUse content is only allowed in assistant messages" . into ( ) ) ;
1598+ }
1599+ SamplingMessageContent :: ToolResult ( _) if msg. role != Role :: User => {
1600+ return Err ( "ToolResult content is only allowed in user messages" . into ( ) ) ;
1601+ }
1602+ _ => { }
1603+ }
1604+ }
1605+
1606+ // Tool result messages MUST NOT contain other content types
1607+ let contents: Vec < _ > = msg. content . iter ( ) . collect ( ) ;
1608+ let has_tool_result = contents
1609+ . iter ( )
1610+ . any ( |c| matches ! ( c, SamplingMessageContent :: ToolResult ( _) ) ) ;
1611+ if has_tool_result
1612+ && contents
1613+ . iter ( )
1614+ . any ( |c| !matches ! ( c, SamplingMessageContent :: ToolResult ( _) ) )
1615+ {
1616+ return Err (
1617+ "SamplingMessage with tool result content MUST NOT contain other content types"
1618+ . into ( ) ,
1619+ ) ;
1620+ }
1621+ }
1622+
1623+ // Every assistant ToolUse must be balanced with a user ToolResult
1624+ self . validate_tool_use_result_balance ( ) ?;
1625+
1626+ Ok ( ( ) )
1627+ }
1628+
1629+ fn validate_tool_use_result_balance ( & self ) -> Result < ( ) , String > {
1630+ let mut pending_tool_use_ids: Vec < String > = Vec :: new ( ) ;
1631+ for msg in & self . messages {
1632+ if msg. role == Role :: Assistant {
1633+ for content in msg. content . iter ( ) {
1634+ if let SamplingMessageContent :: ToolUse ( tu) = content {
1635+ pending_tool_use_ids. push ( tu. id . clone ( ) ) ;
1636+ }
1637+ }
1638+ } else if msg. role == Role :: User {
1639+ for content in msg. content . iter ( ) {
1640+ if let SamplingMessageContent :: ToolResult ( tr) = content {
1641+ if !pending_tool_use_ids. contains ( & tr. tool_use_id ) {
1642+ return Err ( format ! (
1643+ "ToolResult with toolUseId '{}' has no matching ToolUse" ,
1644+ tr. tool_use_id
1645+ ) ) ;
1646+ }
1647+ pending_tool_use_ids. retain ( |id| id != & tr. tool_use_id ) ;
1648+ }
1649+ }
1650+ }
1651+ }
1652+ if !pending_tool_use_ids. is_empty ( ) {
1653+ return Err ( format ! (
1654+ "ToolUse with id(s) {:?} not balanced with ToolResult" ,
1655+ pending_tool_use_ids
1656+ ) ) ;
1657+ }
1658+ Ok ( ( ) )
1659+ }
1660+ }
1661+
15831662/// Deprecated: Use [`CreateMessageRequestParams`] instead (SEP-1319 compliance).
15841663#[ deprecated( since = "0.13.0" , note = "Use CreateMessageRequestParams instead" ) ]
15851664pub type CreateMessageRequestParam = CreateMessageRequestParams ;
@@ -2229,6 +2308,14 @@ impl CreateMessageResult {
22292308 pub const STOP_REASON_END_SEQUENCE : & str = "stopSequence" ;
22302309 pub const STOP_REASON_END_MAX_TOKEN : & str = "maxTokens" ;
22312310 pub const STOP_REASON_TOOL_USE : & str = "toolUse" ;
2311+
2312+ /// Validate the result per SEP-1577: role must be "assistant".
2313+ pub fn validate ( & self ) -> Result < ( ) , String > {
2314+ if self . message . role != Role :: Assistant {
2315+ return Err ( "CreateMessageResult role must be 'assistant'" . into ( ) ) ;
2316+ }
2317+ Ok ( ( ) )
2318+ }
22322319}
22332320
22342321#[ derive( Debug , Serialize , Deserialize , Clone , PartialEq ) ]
0 commit comments