Skip to content

Commit 09492b8

Browse files
committed
Add models selector
1 parent 6a426b6 commit 09492b8

File tree

3 files changed

+146
-85
lines changed

3 files changed

+146
-85
lines changed

experiments/webllm-agents-playground/agents.css

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ textarea {
99
font-style: italic;
1010
}
1111

12-
.error {
13-
font-weight: bold;
14-
color: red;
15-
}
16-
1712
.status {
1813
color: blue;
1914
font-weight: bold;
15+
}
16+
17+
.warning {
18+
background-color: red;
19+
color:yellow;
20+
padding: 1em;
2021
}
Lines changed: 117 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,43 @@
11
import * as webllm from "https://esm.run/@mlc-ai/web-llm";
22
let 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

1538
function 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

2851
async 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

68110
async 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

94141
main()
95-
.catch(e => { setInfo('status', e, 'error'); })
142+
.catch(e => { setInfo(e) })

experiments/webllm-agents-playground/index.html

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,26 @@
1717
</ul>
1818
</nav>
1919
<h1>AI Agents playground</h1>
20-
<p>
21-
This page is meant to help you understand the basics of how agents work.
22-
It runs 3 agents locally in your browser.
23-
</p>
24-
<p>
20+
<p class="warning">
21+
This is <b>experimental</b>, run at your own risk!
22+
<br/>
2523
The results are meant as examples only, and as is common with large language
26-
models you might get surprising results.
24+
models you might get surprising results, depending on which language model you select.
25+
<br/>
2726
Those results are not meant to be used aynwhere except in this experiment.
27+
<br/>
28+
Note also that this page <b>downloads large model files</b> to your computer, possibly several gigabytes,
29+
when you run the agents below.
30+
</p>
31+
<p>
32+
This page is meant to help you understand the basics of how AI agents work.
33+
It runs 3 agents locally in your browser, using <a href="https://webllm.mlc.ai/">WebLLM</a>.
2834
</p>
2935
<p>
3036
The first run takes some time as the language model is
3137
downloaded in your browser. Once that's done, the example agents
32-
take up to 20 seconds each to run on my macbook pro M1.
38+
take a few seconds or even minutes to run, depending on the selected model and on how
39+
powerful your computer is.
3340
</p>
3441
<p>
3542
The agents are defined below, in plain text that you can
@@ -56,6 +63,10 @@ <h2>Agent Definitions</h2>
5663
Use the "run" button below to re-execute the agents after
5764
modifying their parameters.
5865
</p>
66+
<p>
67+
The code that runs these agents is quite simple, and all agents run the same code.
68+
It is only the textual behavior definitions below that make them perform different actions.
69+
</p>
5970
<agent-definition>
6071
<h3>First agent</h3>
6172
<h4>Name</h4>
@@ -112,15 +123,17 @@ <h4>Backstory</h4>
112123
</agent-definition>
113124

114125
<h2>Input</h2>
115-
<p class="note">You can try different languages, this doesn't have to be English.</p>
126+
<p class="note">Many models support other languages besides English, try it!.</p>
116127
<textarea id="input">
117128
Once upon a time, there was a rabbit named Plastikk Yoda, who liked
118129
to run quickly in the fields, while playing a brightly colored kazoo.
119130
</textarea>
120131

121132
<h2>Output</h2>
122-
<input disabled id="run" type="submit" value="Run the agents again"></input>
123-
<p>Model: <b id='model'>???</b></p>
133+
<p class="note">You can select different models to compare their performance and output.</p>
134+
<label for="models">Language Model:</label>
135+
<select name="model"></select>
136+
<input disabled id="run" type="submit" value="Download model and run agents"></input>
124137
<p>Status: <span class="status" id='status'></span></p>
125138
<div id="output"></div>
126139

0 commit comments

Comments
 (0)