@@ -664,15 +664,54 @@ function isTitleRequest(provider, body) {
664664 )
665665}
666666
667- function requestMatchesQuestion ( provider , body , question ) {
667+ function contentToText ( value ) {
668+ if ( typeof value === "string" ) return value
669+ if ( value == null ) return ""
670+
671+ if ( Array . isArray ( value ) ) {
672+ return value . map ( ( entry ) => contentToText ( entry ) ) . join ( "\n" )
673+ }
674+
675+ if ( typeof value === "object" ) {
676+ return [
677+ value . text ,
678+ value . content ,
679+ value . output ,
680+ value . arguments ,
681+ value . input ,
682+ ]
683+ . filter ( ( part ) => part != null )
684+ . map ( ( part ) => contentToText ( part ) )
685+ . join ( "\n" )
686+ }
687+
688+ return String ( value )
689+ }
690+
691+ function getUserTexts ( provider , body ) {
668692 if ( provider === "openai" ) {
669- return body . input ?. [ 0 ] ?. content === question
693+ const userInputs = ( body . input || [ ] ) . filter (
694+ ( item ) => item ?. role === "user" ,
695+ )
696+ return userInputs . map ( ( item ) => contentToText ( item . content ) ) . filter ( Boolean )
670697 }
671- // Both openai-chat-completions and anthropic use messages array
672- const firstUserMessage = body . messages ?. find (
673- ( msg ) => msg . role === "user" && typeof msg . content === "string" ,
698+
699+ const userMessages = ( body . messages || [ ] ) . filter (
700+ ( msg ) => msg ?. role === "user" ,
701+ )
702+ return userMessages . map ( ( msg ) => contentToText ( msg . content ) ) . filter ( Boolean )
703+ }
704+
705+ function requestMatchesQuestion ( provider , body , question ) {
706+ const userTexts = getUserTexts ( provider , body )
707+ if ( userTexts . length === 0 ) return false
708+
709+ const lastUserText = userTexts [ userTexts . length - 1 ]
710+ return (
711+ userTexts . some ( ( text ) => text === question ) ||
712+ userTexts . some ( ( text ) => text . includes ( question ) ) ||
713+ lastUserText . includes ( `User request: ${ question } ` )
674714 )
675- return firstUserMessage ?. content === question
676715}
677716
678717function extractToolOutputContent ( provider , body ) {
@@ -699,30 +738,68 @@ function extractToolOutputContent(provider, body) {
699738 msg . content . some ( ( c ) => c . type === "tool_result" ) ,
700739 )
701740 const latestMessage = toolResultMessages ?. [ toolResultMessages . length - 1 ]
702- const latestToolResult = latestMessage ?. content ?. find (
703- ( c ) => c . type === "tool_result" ,
704- )
741+ const latestToolResult = [ ... ( latestMessage ?. content || [ ] ) ]
742+ . reverse ( )
743+ . find ( ( c ) => c . type === "tool_result" )
705744 return latestToolResult ?. content || null
706745}
707746
708747function extractAllInputContent ( provider , body ) {
709748 if ( provider === "openai" ) {
710- return body . input ?. map ( ( item ) => item . content || "" ) . join ( "\n" ) || ""
749+ return (
750+ body . input
751+ ?. map ( ( item ) =>
752+ contentToText ( item . content ?? item . output ?? item . arguments ?? "" ) ,
753+ )
754+ . join ( "\n" ) || ""
755+ )
711756 }
712757 // Both openai-chat-completions and anthropic use messages
713758 return (
714- body . messages
715- ?. map ( ( msg ) => {
716- if ( typeof msg . content === "string" ) return msg . content
717- if ( Array . isArray ( msg . content ) ) {
718- return msg . content . map ( ( c ) => c . text || c . content || "" ) . join ( "\n" )
719- }
720- return ""
721- } )
722- . join ( "\n" ) || ""
759+ body . messages ?. map ( ( msg ) => contentToText ( msg . content ) ) . join ( "\n" ) || ""
723760 )
724761}
725762
763+ function normalizeRequestBodyForAssertions ( provider , body ) {
764+ if ( ! body || typeof body !== "object" ) return body
765+
766+ if ( provider === "openai" ) {
767+ const input = Array . isArray ( body . input ) ? body . input : [ ]
768+ return {
769+ ...body ,
770+ input : input . filter (
771+ ( item ) =>
772+ item ?. type !== "function_call" &&
773+ item ?. type !== "function_call_output" ,
774+ ) ,
775+ }
776+ }
777+
778+ if ( provider === "openai-chat-completions" ) {
779+ const messages = Array . isArray ( body . messages ) ? body . messages : [ ]
780+ return {
781+ ...body ,
782+ messages : messages . filter ( ( msg ) => msg ?. role !== "tool" ) ,
783+ }
784+ }
785+
786+ if ( provider === "anthropic" ) {
787+ const messages = Array . isArray ( body . messages ) ? body . messages : [ ]
788+ return {
789+ ...body ,
790+ messages : messages . map ( ( msg ) => {
791+ if ( msg ?. role !== "user" || ! Array . isArray ( msg . content ) ) return msg
792+ return {
793+ ...msg ,
794+ content : msg . content . filter ( ( block ) => block ?. type !== "tool_result" ) ,
795+ }
796+ } ) ,
797+ }
798+ }
799+
800+ return body
801+ }
802+
726803// =============================================================================
727804// TOOL CALL FLOW
728805// =============================================================================
@@ -891,7 +968,10 @@ function createMultiTurnFlow(config) {
891968
892969 const turn = turns [ requestCount ]
893970 if ( turn ) {
894- requestBodies [ requestCount ] = req . body
971+ requestBodies [ requestCount ] = normalizeRequestBodyForAssertions (
972+ provider ,
973+ req . body ,
974+ )
895975
896976 if ( turn . expectSystemMessage ) {
897977 const allInputContent = extractAllInputContent ( provider , req . body )
0 commit comments