@@ -114,9 +114,87 @@ function updateEngineInitProgressCallback(report) {
114
114
document . getElementById ( "download-status" ) . textContent = report . text ;
115
115
}
116
116
117
+ const appConfig = { model_list :[ ] } ;
118
+
119
+ for ( const m of webllm . prebuiltAppConfig . model_list ) {
120
+ if ( m . model_id . startsWith ( 'Qwen2.5-7B' )
121
+ || ( m . model_id . startsWith ( 'Llama-3.1-8B-' ) && ! m . model_id . endsWith ( '-1k' ) )
122
+ || m . model_id . startsWith ( 'Hermes-3-Llama-3.1' ) )
123
+ appConfig . model_list . push ( m ) ;
124
+ }
125
+
126
+ appConfig . model_list . push (
127
+ {
128
+ model : "https://huggingface.co/smalinin/Llama-3.1-Storm-8B_q4f32_1-MLC" ,
129
+ model_id : "Llama-3.1-Storm-8B_q4f32_1-MLC" ,
130
+ model_lib : "https://huggingface.co/smalinin/Llama-3.1-Storm-8B_q4f32_1-MLC/resolve/main/Llama-3.1-Storm-8B_q4f32_1-webgpu.wasm" ,
131
+ low_resource_required : false ,
132
+ vram_required_MB : 5750 ,
133
+ overrides : {
134
+ context_window_size : 4096 ,
135
+ prefill_chunk_size : 4096
136
+ } ,
137
+ } ) ;
138
+
139
+ appConfig . model_list . push (
140
+ {
141
+ model : "https://huggingface.co/smalinin/gorilla-openfunctions-v2_q4f32_1-MLC" ,
142
+ model_id : "gorilla-openfunctions-v2-q4f32_1-MLC" ,
143
+ model_lib : "https://huggingface.co/smalinin/gorilla-openfunctions-v2_q4f32_1-MLC/resolve/main/gorilla-openfunctions-v2_q4f32_1-webgpu.wasm" ,
144
+ vram_required_MB : 5660.67 ,
145
+ low_resource_required : false ,
146
+ overrides : {
147
+ context_window_size : 4096 ,
148
+ prefill_chunk_size : 4096
149
+ } ,
150
+ } ) ;
151
+
152
+ appConfig . model_list . push (
153
+ {
154
+ model : "https://huggingface.co/smalinin/Qwen2.5-14B-Instruct_q3f16_1-MLC" ,
155
+ model_id : "Qwen2.5-14B-Instruct_q3f16_1-MLC" ,
156
+ model_lib : "https://huggingface.co/smalinin/Qwen2.5-14B-Instruct_q3f16_1-MLC/resolve/main/Qwen2.5-14B-Instruct_q3f16_1-webgpu.wasm" ,
157
+ low_resource_required : false ,
158
+ vram_required_MB : 9300.0 ,
159
+ required_features : [ "shader-f16" ] ,
160
+ overrides : {
161
+ context_window_size : 4096 ,
162
+ prefill_chunk_size : 2048
163
+ } ,
164
+ } ) ;
165
+
166
+ appConfig . model_list . push (
167
+ {
168
+ model : "https://huggingface.co/smalinin/Qwen2.5-14B-Instruct_q4f16_1-MLC" ,
169
+ model_id : "Qwen2.5-14B-Instruct_q4f16_1-MLC" ,
170
+ model_lib : "https://huggingface.co/smalinin/Qwen2.5-14B-Instruct_q4f16_1-MLC/resolve/main/Qwen2.5-14B-Instruct_q4f16_1-webgpu.wasm" ,
171
+ low_resource_required : false ,
172
+ vram_required_MB : 10900.0 ,
173
+ required_features : [ "shader-f16" ] ,
174
+ overrides : {
175
+ context_window_size : 4096 ,
176
+ prefill_chunk_size : 2048
177
+ } ,
178
+ } ) ;
179
+
180
+
181
+ appConfig . model_list . push (
182
+ {
183
+ model : "https://huggingface.co/smalinin/Qwen2.5-14B-Instruct_q4f32_1-MLC" ,
184
+ model_id : "Qwen2.5-14B-Instruct_q4f32_1-MLC" ,
185
+ model_lib : "https://huggingface.co/smalinin/Qwen2.5-14B-Instruct_q4f32_1-MLC/resolve/main/Qwen2.5-14B-Instruct_q4f32_1-webgpu.wasm" ,
186
+ low_resource_required : false ,
187
+ vram_required_MB : 12000.0 ,
188
+ overrides : {
189
+ context_window_size : 4096 ,
190
+ prefill_chunk_size : 4096
191
+ } ,
192
+ } ) ;
193
+
117
194
118
195
// Create engine instance
119
- const engine = new webllm . MLCEngine ( ) ;
196
+ const engine = new webllm . MLCEngine ( { appConfig : appConfig } ) ;
197
+
120
198
engine . setInitProgressCallback ( updateEngineInitProgressCallback ) ;
121
199
122
200
async function initializeWebLLMEngine ( ) {
@@ -194,16 +272,8 @@ async function streamingGenerating(messages, onUpdate, onFinish, onError) {
194
272
}
195
273
196
274
/*************** UI logic ***************/
197
- const availableModels = webllm . prebuiltAppConfig . model_list
198
- . map ( ( m ) => m . model_id )
199
- . filter ( ( model_id ) => (
200
- model_id . startsWith ( 'Qwen2.5-7B' )
201
- || model_id . startsWith ( 'Hermes-3-Llama-3.1' )
202
- || ( model_id . startsWith ( 'Llama-3.1-8B-' ) && ! model_id . endsWith ( '-1k' ) )
203
- // || model_id.startsWith('DeepSeek-R1-Distill-Llama-')
204
- ) ) ;
205
-
206
- //let selectedModel = "Llama-3.1-8B-Instruct-q4f16_1-1k";
275
+ const availableModels = appConfig . model_list . map ( ( m ) => m . model_id ) ;
276
+
207
277
let selectedModel = "Qwen2.5-7B-Instruct-q4f16_1-MLC" ;
208
278
209
279
async function onMessageStop ( ) {
@@ -443,6 +513,23 @@ class ToolHanler {
443
513
+ 'Always do real call of functions, when it is required.\n'
444
514
+ 'Execute only one function per time.\n'
445
515
516
+ llama31_storm_template =
517
+ `You are a function calling AI model. You may call one or more functions to assist with the user query.`
518
+ + ` Don't make assumptions about what values to plug into function. The user may use the terms function`
519
+ + ` calling or tool use interchangeably.\n\n`
520
+ + `Here are the available functions:\n`
521
+ + `<tools>#{functions_list}</tools>\n\n`
522
+ + `For each function call return a json object with function name and arguments within <tool_call></tool_call>`
523
+ + ` XML tags in the format:\n`
524
+ + `<tool_call>{"tool_name": <function-name>, "tool_arguments": <args-dict>}</tool_call>`
525
+
526
+ gorilla_template =
527
+ `You are an AI programming assistant, utilizing the Gorilla LLM model, developed by Gorilla LLM,`
528
+ + ` and you only answer questions related to computer science. For politically sensitive questions,`
529
+ + ` security and privacy issues, and other non-computer science questions, you will refuse to answer.`
530
+ + `### Instruction\n`
531
+ + `#{functions_list}\n`
532
+
446
533
447
534
deepseek_template =
448
535
'Cutting Knowledge Date: December 2023\n'
@@ -475,12 +562,16 @@ class ToolHanler {
475
562
this . mode = 'qwen' ;
476
563
else if ( model_id . startsWith ( 'Hermes-3-Llama' ) )
477
564
this . mode = 'hermes3_llama'
565
+ else if ( model_id . startsWith ( 'Llama-3.1-Storm' ) )
566
+ this . mode = 'llama31_storm'
478
567
else if ( model_id . startsWith ( 'Llama-3.1-' ) )
479
568
this . mode = 'llama31'
480
569
else if ( model_id . startsWith ( 'Llama-3.2-' ) )
481
570
this . mode = 'llama32'
482
571
else if ( model_id . startsWith ( 'DeepSeek-R1-Distill-Llama' ) )
483
572
this . mode = 'deepseek'
573
+ else if ( model_id . startsWith ( 'gorilla' ) )
574
+ this . mode = 'gorilla'
484
575
else
485
576
this . mode = 'llama31' ;
486
577
this . tool_call_id = 0 ;
@@ -498,10 +589,14 @@ class ToolHanler {
498
589
sys_template = this . hermes2_template . replace ( '#{functions}' , funcs ) ;
499
590
else if ( this . mode === 'llama31' )
500
591
sys_template = this . llama31_template . replace ( '#{functions}' , funcs ) ;
592
+ else if ( this . mode === 'llama31_storm' )
593
+ sys_template = this . llama31_storm_template . replace ( '#{functions_list}' , JSON . stringify ( tools , '\n' , 2 ) ) ;
501
594
else if ( this . mode === 'llama32' )
502
595
sys_template = this . llama32_template . replace ( '#{functions}' , funcs ) ;
503
596
else if ( this . mode === 'deepseek' )
504
597
sys_template = this . deepseek_template . replace ( '#{functions}' , funcs ) ;
598
+ else if ( this . mode === 'gorilla' )
599
+ sys_template = this . deepseek_template . replace ( '#{functions}' , funcs ) ;
505
600
506
601
return sys_template + `\n\n ${ JSON . stringify ( rules , '\n' , 2 ) } \n`
507
602
}
@@ -533,6 +628,15 @@ class ToolHanler {
533
628
is_end = true ;
534
629
}
535
630
}
631
+ else if ( this . mode === 'llama31_storm' ) {
632
+ if ( str . startsWith ( "<tool_call>" ) ) {
633
+ tool_call = str . replace ( "<tool_call>" , "" ) . replace ( "</tool_call>" , "" ) ;
634
+ }
635
+ else if ( tool_end ) {
636
+ tool_call = tool_end [ 0 ] . replace ( "<tool_call>" , "" ) . replace ( "</tool_call>" , "" ) ;
637
+ is_end = true ;
638
+ }
639
+ }
536
640
else if ( this . mode === 'llama31' ) {
537
641
if ( str . startsWith ( "<function>" ) ) {
538
642
tool_call = str . replace ( "<function>" , "" ) . replace ( "</function>" , "" ) ;
@@ -552,12 +656,37 @@ class ToolHanler {
552
656
is_end = true ;
553
657
}
554
658
}
659
+ else if ( this . mode === 'gorilla' ) {
660
+ if ( str . startsWith ( "<<function>>" ) ) {
661
+ tool_call = str . replace ( "<<function>>" , "" ) . trim ( ) ;
662
+ }
663
+ else if ( function_end ) {
664
+ tool_call = function_end [ 0 ] . replace ( "<<function>>" , "" ) . trim ( ) ;
665
+ is_end = true ;
666
+ }
667
+ if ( tool_call ) {
668
+ let i = tool_call . indexOf ( '(' )
669
+ if ( i != - 1 ) {
670
+ const fname = tool_call . substring ( 0 , i ) ;
671
+ const body = this . convertToJSON ( tool_call . substring ( i ) )
672
+ tool_call = `{"name":"${ fname } ", "arguments":${ body } }`
673
+ }
674
+ }
675
+ console . log ( tool_call )
676
+ }
555
677
556
678
if ( tool_call ) {
557
679
try {
558
680
const func = JSON . parse ( tool_call ) ;
681
+
682
+ if ( func . tool_name )
683
+ func [ "name" ] = func . tool_name ;
684
+ if ( func . tool_arguments )
685
+ func [ "arguments" ] = func . tool_arguments ;
686
+
559
687
if ( func . parameters )
560
688
func [ "arguments" ] = func . parameters ;
689
+
561
690
return { func, tool_call, is_end} ;
562
691
} catch ( e ) {
563
692
console . log ( e ) ;
@@ -586,8 +715,78 @@ class ToolHanler {
586
715
this . tool_call_id ++ ;
587
716
return rc ;
588
717
}
718
+
719
+
720
+ convertToJSON ( input ) {
721
+ // Remove the surrounding parentheses
722
+ let content = input . slice ( 1 , - 1 ) ;
723
+
724
+ // Initialize an empty object to store the parsed data
725
+ let result = { } ;
726
+ let key = '' ;
727
+ let value = '' ;
728
+ let inQuotes = false ;
729
+ let escapeNext = false ;
730
+
731
+ let i = 0 ;
732
+ while ( i < content . length ) {
733
+ const char = content [ i ] ;
734
+
735
+ if ( inQuotes ) {
736
+ if ( char === '"' && ! escapeNext ) {
737
+ inQuotes = false ;
738
+ } else if ( char === '\\' && ! escapeNext ) {
739
+ escapeNext = true ;
740
+ } else {
741
+ value += char ;
742
+ escapeNext = false ;
743
+ }
744
+ } else if ( char === '=' ) {
745
+ key = content . slice ( 0 , i ) . trim ( ) ;
746
+ value = '' ;
747
+ } else if ( char === ',' ) {
748
+ value = value . trim ( ) ;
749
+ if ( value . startsWith ( '"' ) && value . endsWith ( '"' ) ) {
750
+ value = value . slice ( 1 , - 1 ) . replace ( / \\ " / g, '"' ) ;
751
+ } else if ( value . startsWith ( "'" ) && value . endsWith ( "'" ) ) {
752
+ value = value . slice ( 1 , - 1 ) . replace ( / \\ " / g, '"' ) ;
753
+ } else if ( value === 'true' || value === 'false' ) {
754
+ value = value === 'true' ;
755
+ } else if ( ! isNaN ( value ) ) {
756
+ value = Number ( value ) ;
757
+ }
758
+ result [ key ] = value ;
759
+ key = '' ;
760
+ value = '' ;
761
+ } else if ( char === '"' ) {
762
+ inQuotes = true ;
763
+ } else {
764
+ value += char ;
765
+ }
766
+
767
+ i ++ ;
768
+ }
769
+
770
+ // Handle the last key-value pair
771
+ value = value . trim ( ) ;
772
+ if ( value . startsWith ( '"' ) && value . endsWith ( '"' ) ) {
773
+ value = value . slice ( 1 , - 1 ) . replace ( / \\ " / g, '"' ) ;
774
+ } else if ( value . startsWith ( "'" ) && value . endsWith ( "'" ) ) {
775
+ value = value . slice ( 1 , - 1 ) . replace ( / \\ " / g, '"' ) ;
776
+ } else if ( value === 'true' || value === 'false' ) {
777
+ value = value === 'true' ;
778
+ } else if ( ! isNaN ( value ) ) {
779
+ value = Number ( value ) ;
780
+ }
781
+ result [ key ] = value ;
782
+
783
+ return JSON . stringify ( result ) ;
784
+ }
785
+
589
786
}
590
787
788
+
789
+
591
790
/****** TOOLS code **************************/
592
791
async function fetch_wikipedia_content ( searchQuery )
593
792
{
0 commit comments