@@ -12,20 +12,20 @@ const CANDIDATE_TARGETS = [
1212 'invoke' ,
1313] ;
1414
15- const CONTAINER_OPEN_RE = / ^ < [ | | ] ? \s * (?: D S M L [ | | ] ? ) ? \s * t o o l _ c a l l s \s * > / i;
16- const CONTAINER_CLOSE_RE = / ^ < \/ [ | | ] ? \s * (?: D S M L [ | | ] ? ) ? \s * t o o l _ c a l l s \s * > / i;
17- const INVOKE_OPEN_RE = / ^ < [ | | ] ? \s * (?: D S M L [ | | ] ? ) ? \s * i n v o k e (?: \s + [ ^ > ] * ) ? > / i;
18- const INVOKE_CLOSE_RE = / < \/ [ | | ] ? \s * (?: D S M L [ | | ] ? ) ? \s * i n v o k e \s * > / i;
15+ const CONTAINER_OPEN_RE = / ^ < \s * [ | | ] ? \s * (?: D S M L \s * [ | | ] ? ) ? \s * t o o l _ c a l l s \s * > / i;
16+ const CONTAINER_CLOSE_RE = / ^ < \/ \s * [ | | ] ? \s * (?: D S M L \s * [ | | ] ? ) ? \s * t o o l _ c a l l s \s * > / i;
17+ const INVOKE_OPEN_RE = / ^ < \s * [ | | ] ? \s * (?: D S M L \s * [ | | ] ? ) ? \s * i n v o k e (?: \s + [ ^ > ] * ) ? > / i;
18+ const INVOKE_CLOSE_RE = / < \/ \s * [ | | ] ? \s * (?: D S M L \s * [ | | ] ? ) ? \s * i n v o k e \s * > / i;
1919const HERMES_OPEN_RE = / ^ < t o o l _ c a l l > / i;
2020const HERMES_CLOSE_RE = / < \/ t o o l _ c a l l > / i;
2121
2222function unescapeXml ( value : string ) : string {
2323 return value
24- . replaceAll ( / & q u o t ; / g , '"' )
25- . replaceAll ( / & a p o s ; / g , "'" )
26- . replaceAll ( / & l t ; / g , '<' )
27- . replaceAll ( / & g t ; / g , '>' )
28- . replaceAll ( / & a m p ; / g , '&' ) ;
24+ . replaceAll ( ' "' , '"' )
25+ . replaceAll ( ' '' , "'" )
26+ . replaceAll ( ' <' , '<' )
27+ . replaceAll ( ' >' , '>' )
28+ . replaceAll ( ' &' , '&' ) ;
2929}
3030
3131function parseParameterValue ( rawVal : string , isStringAttr : boolean | undefined ) : unknown {
@@ -60,7 +60,7 @@ function parseParameterValue(rawVal: string, isStringAttr: boolean | undefined):
6060
6161function parseInvokeBody ( invokeContent : string ) : Record < string , unknown > {
6262 const paramRegex =
63- / < [ | | ] ? \s * (?: D S M L [ | | ] ? ) ? \s * p a r a m e t e r \s + ( [ ^ > ] * ?) > ( [ \s \S ] * ?) < \/ [ | | ] ? \s * (?: D S M L [ | | ] ? ) ? \s * p a r a m e t e r \s * > / gi;
63+ / < \s * [ | | ] ? \s * (?: D S M L \s * [ | | ] ? ) ? \s * p a r a m e t e r \s + ( [ ^ > ] * ?) > ( [ \s \S ] * ?) < \/ \s * [ | | ] ? \s * (?: D S M L \s * [ | | ] ? ) ? \s * p a r a m e t e r \s * > / gi;
6464 const args : Record < string , unknown > = { } ;
6565 let paramFound = false ;
6666 let match : RegExpExecArray | null = null ;
@@ -101,7 +101,7 @@ function parseInvokeBody(invokeContent: string): Record<string, unknown> {
101101}
102102
103103function parseInvokeTag ( invokeBlock : string ) : ToolCall | null {
104- const openMatch = / ^ < [ | | ] ? \s * (?: D S M L [ | | ] ? ) ? \s * i n v o k e \s + ( [ ^ > ] * ?) > / i. exec ( invokeBlock ) ;
104+ const openMatch = / ^ < \s * [ | | ] ? \s * (?: D S M L \s * [ | | ] ? ) ? \s * i n v o k e \s + ( [ ^ > ] * ?) > / i. exec ( invokeBlock ) ;
105105 if ( ! openMatch ) return null ;
106106
107107 const attrStr = openMatch [ 1 ] ?? '' ;
@@ -117,7 +117,7 @@ function parseInvokeTag(invokeBlock: string): ToolCall | null {
117117 const args = parseInvokeBody ( innerContent ) ;
118118 return {
119119 type : 'function' ,
120- id : `call_${ crypto . randomUUID ( ) . replaceAll ( / - / g , '' ) . slice ( 0 , 24 ) } ` ,
120+ id : `call_${ crypto . randomUUID ( ) . replaceAll ( '-' , '' ) . slice ( 0 , 24 ) } ` ,
121121 name : toolName ,
122122 arguments : JSON . stringify ( args ) ,
123123 } ;
@@ -137,7 +137,7 @@ function parseHermesToolCall(toolCallBlock: string): ToolCall | null {
137137 : JSON . stringify ( parsed . arguments ?? { } ) ;
138138 return {
139139 type : 'function' ,
140- id : `call_${ crypto . randomUUID ( ) . replaceAll ( / - / g , '' ) . slice ( 0 , 24 ) } ` ,
140+ id : `call_${ crypto . randomUUID ( ) . replaceAll ( '-' , '' ) . slice ( 0 , 24 ) } ` ,
141141 name : parsed . name ,
142142 arguments : args ,
143143 } ;
@@ -216,12 +216,15 @@ export class DsmlStreamParser {
216216 if ( toolCall ) {
217217 this . _hasExtractedToolCalls = true ;
218218 parts . push ( toolCall ) ;
219- }
220- this . _buffer = this . _buffer . slice ( invokeEnd ) ;
221- if ( / ^ \s * < / . test ( this . _buffer ) ) {
222- this . _buffer = this . _buffer . trimStart ( ) ;
219+ this . _buffer = this . _buffer . slice ( invokeEnd ) ;
220+ if ( / ^ \s * < / . test ( this . _buffer ) ) {
221+ this . _buffer = this . _buffer . trimStart ( ) ;
222+ } else {
223+ this . _buffer = this . _buffer . replace ( / ^ \r ? \n / , '' ) ;
224+ }
223225 } else {
224- this . _buffer = this . _buffer . replace ( / ^ \r ? \n / , '' ) ;
226+ parts . push ( { type : 'text' , text : invokeBlock } ) ;
227+ this . _buffer = this . _buffer . slice ( invokeEnd ) ;
225228 }
226229 continue ;
227230 }
@@ -238,12 +241,15 @@ export class DsmlStreamParser {
238241 if ( toolCall ) {
239242 this . _hasExtractedToolCalls = true ;
240243 parts . push ( toolCall ) ;
241- }
242- this . _buffer = this . _buffer . slice ( toolCallEnd ) ;
243- if ( / ^ \s * < / . test ( this . _buffer ) ) {
244- this . _buffer = this . _buffer . trimStart ( ) ;
244+ this . _buffer = this . _buffer . slice ( toolCallEnd ) ;
245+ if ( / ^ \s * < / . test ( this . _buffer ) ) {
246+ this . _buffer = this . _buffer . trimStart ( ) ;
247+ } else {
248+ this . _buffer = this . _buffer . replace ( / ^ \r ? \n / , '' ) ;
249+ }
245250 } else {
246- this . _buffer = this . _buffer . replace ( / ^ \r ? \n / , '' ) ;
251+ parts . push ( { type : 'text' , text : block } ) ;
252+ this . _buffer = this . _buffer . slice ( toolCallEnd ) ;
247253 }
248254 continue ;
249255 }
@@ -279,6 +285,16 @@ export class DsmlStreamParser {
279285 return parts ;
280286 }
281287 }
288+ const hermesOpen = HERMES_OPEN_RE . exec ( this . _buffer ) ;
289+ if ( hermesOpen ) {
290+ const toolCall = parseHermesToolCall ( this . _buffer ) ;
291+ if ( toolCall ) {
292+ this . _hasExtractedToolCalls = true ;
293+ parts . push ( toolCall ) ;
294+ this . _buffer = '' ;
295+ return parts ;
296+ }
297+ }
282298 parts . push ( { type : 'text' , text : this . _buffer } ) ;
283299 this . _buffer = '' ;
284300 }
@@ -303,6 +319,6 @@ export function extractDsmlToolCalls(text: string): {
303319 }
304320 }
305321
306- const cleanText = textParts . join ( '' ) . trim ( ) ;
322+ const cleanText = toolCalls . length > 0 ? textParts . join ( '' ) . trim ( ) : text ;
307323 return { cleanText, toolCalls } ;
308324}
0 commit comments