@@ -88,13 +88,73 @@ interface LLMResponse {
8888 toolCalls : ToolCall [ ] ;
8989}
9090
91+ interface InlineToolParseResult {
92+ content : string ;
93+ toolCalls : ToolCall [ ] ;
94+ }
95+
9196function stripThinkTags ( content : string ) : string {
9297 const withoutBlocks = content
9398 . replace ( / < t h i n k \b [ ^ > ] * > [ \s \S ] * ?< \/ t h i n k > / gi, '' )
9499 . replace ( / < \/ ? t h i n k \b [ ^ > ] * > / gi, '' ) ;
95100 return withoutBlocks === content ? content : withoutBlocks . trim ( ) ;
96101}
97102
103+ function parseInlineArgValue ( rawValue : string ) : unknown {
104+ const trimmed = rawValue . trim ( ) ;
105+ if ( ! trimmed ) return '' ;
106+ try {
107+ return JSON . parse ( trimmed ) ;
108+ } catch {
109+ return trimmed ;
110+ }
111+ }
112+
113+ function extractInlineToolCalls ( rawContent : string ) : InlineToolParseResult {
114+ const content = stripThinkTags ( rawContent ) ;
115+ if ( ! content . includes ( '<arg_key>' ) || ! content . includes ( '<arg_value>' ) ) {
116+ return { content, toolCalls : [ ] } ;
117+ }
118+
119+ const blockRegex = / (?: < t o o l _ c a l l > \s * | \( ) ( [ a - z A - Z 0 - 9 _ . - ] + ) \s * ( [ \s \S ] * ?) < \/ t o o l _ c a l l > / g;
120+ const toolCalls : ToolCall [ ] = [ ] ;
121+ let cleanedContent = content ;
122+ let matchIndex = 0 ;
123+
124+ for ( const match of content . matchAll ( blockRegex ) ) {
125+ const toolName = match [ 1 ] ?. trim ( ) ;
126+ const body = match [ 2 ] ?? '' ;
127+ if ( ! toolName ) continue ;
128+
129+ const args : Record < string , unknown > = { } ;
130+ const pairRegex =
131+ / < a r g _ k e y > \s * ( [ \s \S ] * ?) \s * < \/ a r g _ k e y > \s * < a r g _ v a l u e > \s * ( [ \s \S ] * ?) \s * < \/ a r g _ v a l u e > / g;
132+
133+ for ( const pair of body . matchAll ( pairRegex ) ) {
134+ const key = pair [ 1 ] ?. trim ( ) ;
135+ if ( ! key ) continue ;
136+ args [ key ] = parseInlineArgValue ( pair [ 2 ] ?? '' ) ;
137+ }
138+
139+ if ( Object . keys ( args ) . length === 0 ) continue ;
140+
141+ toolCalls . push ( {
142+ id : `inline_tool_${ matchIndex ++ } ` ,
143+ type : 'function' ,
144+ function : {
145+ name : toolName ,
146+ arguments : JSON . stringify ( args ) ,
147+ } ,
148+ } ) ;
149+ cleanedContent = cleanedContent . replace ( match [ 0 ] , '' ) ;
150+ }
151+
152+ return {
153+ content : cleanedContent . trim ( ) ,
154+ toolCalls,
155+ } ;
156+ }
157+
98158function hasVersionSuffix ( url : string ) : boolean {
99159 return / \/ v \d + \/ ? $ / . test ( url ) ;
100160}
@@ -193,7 +253,8 @@ async function chatOpenAI(
193253
194254 const data = JSON . parse ( text ) ;
195255 const choice = data . choices ?. [ 0 ] ?. message ;
196- const toolCalls = choice ?. tool_calls || [ ] ;
256+ const parsedInline = extractInlineToolCalls ( choice ?. content || '' ) ;
257+ const toolCalls = choice ?. tool_calls ?. length ? choice . tool_calls : parsedInline . toolCalls ;
197258 const calledNames = toolCalls
198259 . map ( ( tc : { function ?: { name ?: string } } ) => tc . function ?. name )
199260 . filter ( Boolean ) ;
@@ -205,7 +266,9 @@ async function chatOpenAI(
205266 calledNames ,
206267 ) ;
207268 return {
208- content : stripThinkTags ( choice ?. content || '' ) ,
269+ content : choice ?. tool_calls ?. length
270+ ? stripThinkTags ( choice ?. content || '' )
271+ : parsedInline . content ,
209272 toolCalls,
210273 } ;
211274}
0 commit comments