11import * as webllm from "https://esm.run/@mlc-ai/web-llm" ;
22let engine ;
3+ const modelSelector = document . querySelector ( 'select[name=model]' ) ;
4+ const runButton = document . querySelector ( '#run' )
5+ const agentsOutput = document . querySelector ( '#output' ) ;
6+ const status = document . querySelector ( '#status' ) ;
37
4- function setInfo ( id , text , cssClass ) {
5- const e = document . getElementById ( id ) ;
6- if ( e == null ) {
7- throw Error ( "Cannot find label " + id ) ;
8- }
9- if ( cssClass ) {
10- e . classList . add ( cssClass ) ;
11- }
12- e . innerText = text ;
8+ // This example was tested with these models
9+ const knownToWorkModels = [
10+ 'SmolLM2-360M-Instruct-q0f16-MLC' ,
11+ 'SmolLM2-360M-Instruct-q0f32-MLC' ,
12+ 'TinyLlama-1.1B-Chat-v1.0-q4f16_1-MLC' ,
13+ 'Qwen2-0.5B-Instruct-q4f16_1-MLC' ,
14+ 'Llama-3.2-1B-Instruct-q4f32_1-MLC' ,
15+ 'Llama-3.2-1B-Instruct-q4f16_1-MLC' ,
16+ 'DeepSeek-R1-Distill-Qwen-7B-q4f16_1-MLC'
17+ ] ;
18+ const defaultModel = 'Llama-3.2-1B-Instruct-q4f16_1-MLC' ;
19+
20+ function getModelSizeFactor ( model ) {
21+ return Math . floor ( model . vram_required_MB / 100 ) / 10 ;
22+ }
23+
24+ function enableControls ( enabled ) {
25+ [ runButton , modelSelector ] . forEach ( e => {
26+ if ( enabled ) {
27+ e . removeAttribute ( 'disabled' ) ;
28+ } else {
29+ e . setAttribute ( 'disabled' , true ) ;
30+ }
31+ } )
32+ }
33+
34+ function setInfo ( text ) {
35+ status . innerText = text ;
1336}
1437
1538function getAgentsDefinitions ( ) {
1639 const agents = [ ] ;
17- setInfo ( 'status' , "Running agents..." ) ;
40+ setInfo ( "Running agents..." ) ;
1841 document . querySelectorAll ( 'agent-definition' ) . forEach ( a => {
1942 const agent = { } ;
2043 agent . name = a . querySelector ( '[class=name]' ) ?. value . trim ( ) ;
@@ -26,70 +49,94 @@ function getAgentsDefinitions() {
2649}
2750
2851async function runAgents ( ) {
29- const run = document . querySelector ( '#run' )
30- run . setAttribute ( 'disabled' , 'true' ) ;
31- const agents = getAgentsDefinitions ( ) ;
32- setInfo ( 'status' , `${ agents . length } agent definitions found on the page` ) ;
33- var input = document . querySelector ( '#input' ) . value ;
34- const out = document . querySelector ( '#output' ) ;
35- out . innerHTML = '' ;
36- let displayedInput = input ;
37-
38- for ( var a of agents ) {
39- setInfo ( 'status' , `Running '${ a . name } ' agent..` ) ;
40- const start = performance . now ( ) ;
41-
42- const messages = [
43- { role : "system" , content : `${ a . backstory } - ${ a . role } ` } ,
44- { role : "user" , content : input }
45- ] ;
46-
47- console . log ( 'running agent' , a , 'input' , messages ) ;
48- const reply = await engine . chat . completions . create ( { messages } ) ;
49- const duration = performance . now ( ) - start ;
50- const output = reply ?. choices [ 0 ] ?. message ?. content ;
51- console . log ( 'output' , output ) ;
52- const elapsed = Math . floor ( duration / 1000 ) ;
53- console . log ( `Agent '${ a . name } ' took ${ elapsed } seconds` ) ;
54-
55- const t = document . querySelector ( '#output-one' ) . content . cloneNode ( true ) ;
56- t . querySelector ( '[class=name]' ) . textContent = a . name ;
57- t . querySelector ( '[class=elapsed]' ) . textContent = elapsed ;
58- t . querySelector ( '[class=input]' ) . textContent = displayedInput ;
59- displayedInput = '(the output of the previous agent is used)' ;
60- t . querySelector ( '[class=output]' ) . textContent = output ;
61- out . append ( t ) ;
62- input = output ;
52+ agentsOutput . innerHTML = '' ;
53+ enableControls ( false ) ;
54+
55+ const initProgressCallback = ( report ) => {
56+ setInfo ( report . text ) ;
57+ } ;
58+
59+ try {
60+ if ( ! engine ) {
61+ engine = await webllm . CreateMLCEngine (
62+ modelSelector . value ,
63+ {
64+ initProgressCallback : initProgressCallback ,
65+ logLevel : "INFO" ,
66+ } ,
67+ {
68+ context_window_size : 4096 ,
69+ //sliding_window_size: 1024,
70+ attention_sink_size : 4 ,
71+ } ,
72+ ) ;
73+ }
74+
75+ const agents = getAgentsDefinitions ( ) ;
76+ setInfo ( `${ agents . length } agent definitions found on the page` ) ;
77+ var input = document . querySelector ( '#input' ) . value ;
78+ let displayedInput = input ;
79+
80+ for ( var a of agents ) {
81+ setInfo ( `Running '${ a . name } ' agent..` ) ;
82+ const start = performance . now ( ) ;
83+
84+ const messages = [
85+ { role : "system" , content : `${ a . backstory } - ${ a . role } ` } ,
86+ { role : "user" , content : input }
87+ ] ;
88+
89+ const reply = await engine . chat . completions . create ( { messages } ) ;
90+ const duration = performance . now ( ) - start ;
91+ const output = reply ?. choices [ 0 ] ?. message ?. content ;
92+ const elapsed = Math . floor ( duration / 1000 ) ;
93+
94+ const t = document . querySelector ( '#output-one' ) . content . cloneNode ( true ) ;
95+ t . querySelector ( '[class=name]' ) . textContent = a . name ;
96+ t . querySelector ( '[class=elapsed]' ) . textContent = elapsed ;
97+ t . querySelector ( '[class=input]' ) . textContent = displayedInput ;
98+ displayedInput = '(the output of the previous agent is used)' ;
99+ t . querySelector ( '[class=output]' ) . textContent = output ;
100+ agentsOutput . append ( t ) ;
101+ input = output ;
102+ }
103+ setInfo ( `Done running ${ agents . length } agents.` ) ;
104+ } catch ( e ) {
105+ setInfo ( e ) ;
63106 }
64- setInfo ( 'status' , `Done running ${ agents . length } agents.` ) ;
65- run . removeAttribute ( 'disabled' ) ;
107+ enableControls ( true ) ;
66108}
67109
68110async function main ( ) {
69- setInfo ( 'status' , 'Initializing..' ) ;
70- const initProgressCallback = ( report ) => {
71- setInfo ( 'status' , report . text ) ;
72- } ;
73- //const selectedModel = 'Llama-3.2-1B-Instruct-q0f16-MLC';
74- const selectedModel = 'Llama-3.2-1B-Instruct-q4f16_1-MLC' ;
75- setInfo ( 'model' , selectedModel ) ;
76- engine = await webllm . CreateMLCEngine (
77- selectedModel ,
78- {
79- initProgressCallback : initProgressCallback ,
80- logLevel : "INFO" ,
81- } ,
82- {
83- context_window_size : 4096 ,
84- //sliding_window_size: 1024,
85- attention_sink_size : 4 ,
86- } ,
87- ) ;
88-
89- document . querySelector ( '#run' ) . addEventListener ( 'click' , ( ) => runAgents ( ) ) ;
90-
91- runAgents ( ) ;
111+ setInfo ( 'Initializing..' ) ;
112+
113+ // Load the list of models
114+ modelSelector . innerHTML = '' ;
115+ for ( var model of webllm . prebuiltAppConfig . model_list ) {
116+ const option = document . createElement ( 'option' ) ;
117+ if ( ! knownToWorkModels . includes ( model . model_id ) ) {
118+ continue ;
119+ }
120+ option . textContent = model . model_id ;
121+ option . value = option . textContent ;
122+ option . textContent = `${ option . textContent } (size:${ getModelSizeFactor ( model ) } )` ;
123+ if ( model . model_id === defaultModel ) {
124+ option . selected = true ;
125+ }
126+ modelSelector . append ( option ) ;
127+ }
128+
129+ // Setup controls
130+ modelSelector . addEventListener ( 'change' , ( ) => {
131+ engine = null ;
132+ agentsOutput . innerHTML = '' ;
133+ enableControls ( true ) ;
134+ } ) ;
135+
136+ runButton . addEventListener ( 'click' , ( ) => runAgents ( ) ) ;
137+ enableControls ( true ) ;
138+ setInfo ( 'Initialized.' ) ;
92139}
93140
94141main ( )
95- . catch ( e => { setInfo ( 'status' , e , 'error' ) ; } )
142+ . catch ( e => { setInfo ( e ) } )
0 commit comments