|
1 | 1 | import * as webllm from "@mlc-ai/web-llm"; |
| 2 | +import { Type, Static } from "@sinclair/typebox"; |
2 | 3 |
|
3 | 4 | function setLabel(id: string, text: string) { |
4 | | - const label = document.getElementById(id); |
5 | | - if (label == null) { |
6 | | - throw Error("Cannot find label " + id); |
7 | | - } |
8 | | - label.innerText = text; |
| 5 | + const label = document.getElementById(id); |
| 6 | + if (label == null) { |
| 7 | + throw Error("Cannot find label " + id); |
| 8 | + } |
| 9 | + label.innerText = text; |
9 | 10 | } |
10 | 11 |
|
11 | | -// There are several options of providing such a schema |
12 | | -// 1. You can directly define a schema in string |
13 | | -const schema1 = `{ |
14 | | - "properties": { |
15 | | - "size": {"title": "Size", "type": "integer"}, |
16 | | - "is_accepted": {"title": "Is Accepted", "type": "boolean"}, |
17 | | - "num": {"title": "Num", "type": "number"} |
18 | | - }, |
19 | | - "required": ["size", "is_accepted", "num"], |
20 | | - "title": "Schema", "type": "object" |
21 | | -}`; |
| 12 | +async function simpleStructuredTextExample() { |
| 13 | + // There are several options of providing such a schema |
| 14 | + // 1. You can directly define a schema in string |
| 15 | + const schema1 = `{ |
| 16 | + "properties": { |
| 17 | + "size": {"title": "Size", "type": "integer"}, |
| 18 | + "is_accepted": {"title": "Is Accepted", "type": "boolean"}, |
| 19 | + "num": {"title": "Num", "type": "number"} |
| 20 | + }, |
| 21 | + "required": ["size", "is_accepted", "num"], |
| 22 | + "title": "Schema", "type": "object" |
| 23 | + }`; |
22 | 24 |
|
23 | | -// 2. You can use 3rdparty libraries like typebox to create a schema |
24 | | -import { Type, type Static } from '@sinclair/typebox' |
25 | | -const T = Type.Object({ |
| 25 | + // 2. You can use 3rdparty libraries like typebox to create a schema |
| 26 | + const T = Type.Object({ |
26 | 27 | size: Type.Integer(), |
27 | 28 | is_accepted: Type.Boolean(), |
28 | 29 | num: Type.Number(), |
29 | | -}) |
30 | | -type T = Static<typeof T>; |
31 | | -const schema2 = JSON.stringify(T); |
32 | | -console.log(schema2); |
33 | | -// {"type":"object","properties":{"size":{"type":"integer"},"is_accepted":{"type":"boolean"}, |
34 | | -// "num":{"type":"number"}},"required":["size","is_accepted","num"]} |
| 30 | + }); |
| 31 | + type T = Static<typeof T>; |
| 32 | + const schema2 = JSON.stringify(T); |
| 33 | + console.log(schema2); |
| 34 | + // {"type":"object","properties":{"size":{"type":"integer"},"is_accepted":{"type":"boolean"}, |
| 35 | + // "num":{"type":"number"}},"required":["size","is_accepted","num"]} |
| 36 | + |
| 37 | + const initProgressCallback = (report: webllm.InitProgressReport) => { |
| 38 | + setLabel("init-label", report.text); |
| 39 | + }; |
| 40 | + const engine: webllm.EngineInterface = await webllm.CreateEngine( |
| 41 | + "Llama-2-7b-chat-hf-q4f16_1", |
| 42 | + { initProgressCallback: initProgressCallback } |
| 43 | + ); |
| 44 | + |
| 45 | + const request: webllm.ChatCompletionRequest = { |
| 46 | + stream: false, // works with streaming, logprobs, top_logprobs as well |
| 47 | + messages: [ |
| 48 | + { |
| 49 | + role: "user", |
| 50 | + content: |
| 51 | + "Generate a json containing three fields: an integer field named size, a " + |
| 52 | + "boolean field named is_accepted, and a float field named num.", |
| 53 | + }, |
| 54 | + ], |
| 55 | + max_gen_len: 128, |
| 56 | + response_format: { |
| 57 | + type: "json_object", |
| 58 | + schema: schema2, |
| 59 | + } as webllm.ResponseFormat, |
| 60 | + }; |
| 61 | + |
| 62 | + const reply0 = await engine.chatCompletion(request); |
| 63 | + console.log(reply0); |
| 64 | + console.log("Output:\n" + (await engine.getMessage())); |
| 65 | + console.log(await engine.runtimeStatsText()); |
| 66 | +} |
| 67 | + |
| 68 | +// The json schema and prompt is taken from |
| 69 | +// https://github.com/sgl-project/sglang/tree/main?tab=readme-ov-file#json-decoding |
| 70 | +async function harryPotterExample() { |
| 71 | + const T = Type.Object({ |
| 72 | + name: Type.String(), |
| 73 | + house: Type.Enum({ |
| 74 | + Gryffindor: "Gryffindor", |
| 75 | + Hufflepuff: "Hufflepuff", |
| 76 | + Ravenclaw: "Ravenclaw", |
| 77 | + Slytherin: "Slytherin", |
| 78 | + }), |
| 79 | + blood_status: Type.Enum({ |
| 80 | + "Pure-blood": "Pure-blood", |
| 81 | + "Half-blood": "Half-blood", |
| 82 | + "Muggle-born": "Muggle-born", |
| 83 | + }), |
| 84 | + occupation: Type.Enum({ |
| 85 | + Student: "Student", |
| 86 | + Professor: "Professor", |
| 87 | + "Ministry of Magic": "Ministry of Magic", |
| 88 | + Other: "Other", |
| 89 | + }), |
| 90 | + wand: Type.Object({ |
| 91 | + wood: Type.String(), |
| 92 | + core: Type.String(), |
| 93 | + length: Type.Number(), |
| 94 | + }), |
| 95 | + alive: Type.Boolean(), |
| 96 | + patronus: Type.String(), |
| 97 | + }); |
| 98 | + |
| 99 | + type T = Static<typeof T>; |
| 100 | + const schema = JSON.stringify(T); |
| 101 | + console.log(schema); |
| 102 | + |
| 103 | + const initProgressCallback = (report: webllm.InitProgressReport) => { |
| 104 | + setLabel("init-label", report.text); |
| 105 | + }; |
| 106 | + |
| 107 | + const engine: webllm.EngineInterface = await webllm.CreateEngine( |
| 108 | + "Llama-2-7b-chat-hf-q4f16_1", |
| 109 | + { initProgressCallback: initProgressCallback } |
| 110 | + ); |
| 111 | + |
| 112 | + const request: webllm.ChatCompletionRequest = { |
| 113 | + stream: false, |
| 114 | + messages: [ |
| 115 | + { |
| 116 | + role: "user", |
| 117 | + content: |
| 118 | + "Hermione Granger is a character in Harry Potter. Please fill in the following information about this character in JSON format." + |
| 119 | + "Name is a string of character name. House is one of Gryffindor, Hufflepuff, Ravenclaw, Slytherin. Blood status is one of Pure-blood, Half-blood, Muggle-born. Occupation is one of Student, Professor, Ministry of Magic, Other. Wand is an object with wood, core, and length. Alive is a boolean. Patronus is a string.", |
| 120 | + }, |
| 121 | + ], |
| 122 | + max_gen_len: 128, |
| 123 | + response_format: { |
| 124 | + type: "json_object", |
| 125 | + schema: schema, |
| 126 | + } as webllm.ResponseFormat, |
| 127 | + }; |
| 128 | + |
| 129 | + const reply = await engine.chatCompletion(request); |
| 130 | + console.log(reply); |
| 131 | + console.log("Output:\n" + (await engine.getMessage())); |
| 132 | + console.log(await engine.runtimeStatsText()); |
| 133 | +} |
| 134 | + |
| 135 | +async function functionCallingExample() { |
| 136 | + const T = Type.Object({ |
| 137 | + tool_calls: Type.Array( |
| 138 | + Type.Object({ |
| 139 | + arguments: Type.Any(), |
| 140 | + name: Type.String(), |
| 141 | + }) |
| 142 | + ), |
| 143 | + }); |
| 144 | + type T = Static<typeof T>; |
| 145 | + const schema = JSON.stringify(T); |
| 146 | + console.log(schema); |
| 147 | + |
| 148 | + const tools: Array<webllm.ChatCompletionTool> = [ |
| 149 | + { |
| 150 | + type: "function", |
| 151 | + function: { |
| 152 | + name: "get_current_weather", |
| 153 | + description: "Get the current weather in a given location", |
| 154 | + parameters: { |
| 155 | + type: "object", |
| 156 | + properties: { |
| 157 | + location: { |
| 158 | + type: "string", |
| 159 | + description: "The city and state, e.g. San Francisco, CA", |
| 160 | + }, |
| 161 | + unit: { type: "string", enum: ["celsius", "fahrenheit"] }, |
| 162 | + }, |
| 163 | + required: ["location"], |
| 164 | + }, |
| 165 | + }, |
| 166 | + }, |
| 167 | + ]; |
| 168 | + |
| 169 | + const initProgressCallback = (report: webllm.InitProgressReport) => { |
| 170 | + setLabel("init-label", report.text); |
| 171 | + }; |
| 172 | + |
| 173 | + const selectedModel = "Hermes-2-Pro-Mistral-7B-q4f16_1"; |
| 174 | + const engine: webllm.EngineInterface = await webllm.CreateEngine( |
| 175 | + selectedModel, |
| 176 | + { |
| 177 | + initProgressCallback: initProgressCallback, |
| 178 | + } |
| 179 | + ); |
| 180 | + |
| 181 | + const request: webllm.ChatCompletionRequest = { |
| 182 | + stream: false, |
| 183 | + messages: [ |
| 184 | + { |
| 185 | + role: "system", |
| 186 | + content: `You are a function calling AI model. You are provided with function signatures within <tools></tools> XML tags. You may call one or more functions to assist with the user query. Don't make assumptions about what values to plug into functions. Here are the available tools: <tools> ${JSON.stringify( |
| 187 | + tools |
| 188 | + )} </tools>. Do not stop calling functions until the task has been accomplished or you've reached max iteration of 10. |
| 189 | + Calling multiple functions at once can overload the system and increase cost so call one function at a time please. |
| 190 | + If you plan to continue with analysis, always call another function. |
| 191 | + Return a valid json object (using double quotes) in the following schema: ${JSON.stringify( |
| 192 | + schema |
| 193 | + )}.`, |
| 194 | + }, |
| 195 | + { |
| 196 | + role: "user", |
| 197 | + content: |
| 198 | + "What is the current weather in celsius in Pittsburgh and Tokyo?", |
| 199 | + }, |
| 200 | + ], |
| 201 | + response_format: { |
| 202 | + type: "json_object", |
| 203 | + schema: schema, |
| 204 | + } as webllm.ResponseFormat, |
| 205 | + }; |
| 206 | + |
| 207 | + const reply = await engine.chat.completions.create(request); |
| 208 | + console.log(reply.choices[0].message.content); |
| 209 | + |
| 210 | + console.log(await engine.runtimeStatsText()); |
| 211 | +} |
35 | 212 |
|
36 | 213 | async function main() { |
37 | | - const initProgressCallback = (report: webllm.InitProgressReport) => { |
38 | | - setLabel("init-label", report.text); |
39 | | - }; |
40 | | - const engine: webllm.EngineInterface = await webllm.CreateEngine( |
41 | | - "Llama-2-7b-chat-hf-q4f16_1", { initProgressCallback: initProgressCallback } |
42 | | - ); |
43 | | - |
44 | | - const request: webllm.ChatCompletionRequest = { |
45 | | - stream: false, // works with streaming, logprobs, top_logprobs as well |
46 | | - messages: [ |
47 | | - { |
48 | | - "role": "user", |
49 | | - "content": "Generate a json containing three fields: an integer field named size, a " + |
50 | | - "boolean field named is_accepted, and a float field named num." |
51 | | - } |
52 | | - ], |
53 | | - max_gen_len: 128, |
54 | | - response_format: { type: "json_object", schema: schema2 } as webllm.ResponseFormat |
55 | | - }; |
56 | | - |
57 | | - const reply0 = await engine.chatCompletion(request); |
58 | | - console.log(reply0); |
59 | | - console.log("Output:\n" + await engine.getMessage()); |
60 | | - console.log(await engine.runtimeStatsText()); |
| 214 | + // await simpleStructuredTextExample(); |
| 215 | + // await harryPotterExample(); |
| 216 | + await functionCallingExample(); |
61 | 217 | } |
62 | 218 |
|
63 | 219 | main(); |
0 commit comments