1
1
/* eslint-disable @typescript-eslint/no-non-null-assertion */
2
- ' use strict' ;
2
+ " use strict" ;
3
3
4
4
// This code is partially adapted from the openai-chatgpt-chrome-extension repo:
5
5
// https://github.com/jessedi0n/openai-chatgpt-chrome-extension
6
6
7
- import ' ./popup.css' ;
7
+ import " ./popup.css" ;
8
8
9
- import { MLCEngineInterface , InitProgressReport , CreateMLCEngine , ChatCompletionMessageParam } from "@mlc-ai/web-llm" ;
9
+ import {
10
+ MLCEngineInterface ,
11
+ InitProgressReport ,
12
+ CreateMLCEngine ,
13
+ ChatCompletionMessageParam ,
14
+ } from "@mlc-ai/web-llm" ;
10
15
import { ProgressBar , Line } from "progressbar.js" ;
11
16
12
17
const sleep = ( ms : number ) => new Promise ( ( r ) => setTimeout ( r , ms ) ) ;
@@ -21,135 +26,149 @@ fetchPageContents();
21
26
22
27
( < HTMLButtonElement > submitButton ) . disabled = true ;
23
28
24
- const progressBar : ProgressBar = new Line ( ' #loadingContainer' , {
25
- strokeWidth : 4 ,
26
- easing : ' easeInOut' ,
27
- duration : 1400 ,
28
- color : ' #ffd166' ,
29
- trailColor : ' #eee' ,
30
- trailWidth : 1 ,
31
- svgStyle : { width : ' 100%' , height : ' 100%' }
29
+ const progressBar : ProgressBar = new Line ( " #loadingContainer" , {
30
+ strokeWidth : 4 ,
31
+ easing : " easeInOut" ,
32
+ duration : 1400 ,
33
+ color : " #ffd166" ,
34
+ trailColor : " #eee" ,
35
+ trailWidth : 1 ,
36
+ svgStyle : { width : " 100%" , height : " 100%" } ,
32
37
} ) ;
33
38
34
39
const initProgressCallback = ( report : InitProgressReport ) => {
35
- console . log ( report . text , report . progress ) ;
36
- progressBar . animate ( report . progress , {
37
- duration : 50
38
- } ) ;
39
- if ( report . progress == 1.0 ) {
40
- enableInputs ( ) ;
41
- }
40
+ console . log ( report . text , report . progress ) ;
41
+ progressBar . animate ( report . progress , {
42
+ duration : 50 ,
43
+ } ) ;
44
+ if ( report . progress == 1.0 ) {
45
+ enableInputs ( ) ;
46
+ }
42
47
} ;
43
48
44
- // const selectedModel = "TinyLlama-1.1B-Chat-v0.4-q4f16_1-1k";
45
- const selectedModel = "Mistral-7B-Instruct-v0.2-q4f16_1" ;
46
- const engine : MLCEngineInterface = await CreateMLCEngine (
47
- selectedModel ,
48
- { initProgressCallback : initProgressCallback }
49
- ) ;
49
+ // const selectedModel = "TinyLlama-1.1B-Chat-v0.4-q4f16_1-MLC-1k";
50
+ const selectedModel = "Mistral-7B-Instruct-v0.2-q4f16_1-MLC" ;
51
+ const engine : MLCEngineInterface = await CreateMLCEngine ( selectedModel , {
52
+ initProgressCallback : initProgressCallback ,
53
+ } ) ;
50
54
const chatHistory : ChatCompletionMessageParam [ ] = [ ] ;
51
55
52
56
isLoadingParams = true ;
53
57
54
58
function enableInputs ( ) {
55
- if ( isLoadingParams ) {
56
- sleep ( 500 ) ;
57
- ( < HTMLButtonElement > submitButton ) . disabled = false ;
58
- const loadingBarContainer = document . getElementById ( "loadingContainer" ) ! ;
59
- loadingBarContainer . remove ( ) ;
60
- queryInput . focus ( ) ;
61
- isLoadingParams = false ;
62
- }
59
+ if ( isLoadingParams ) {
60
+ sleep ( 500 ) ;
61
+ ( < HTMLButtonElement > submitButton ) . disabled = false ;
62
+ const loadingBarContainer = document . getElementById ( "loadingContainer" ) ! ;
63
+ loadingBarContainer . remove ( ) ;
64
+ queryInput . focus ( ) ;
65
+ isLoadingParams = false ;
66
+ }
63
67
}
64
68
65
69
// Disable submit button if input field is empty
66
70
queryInput . addEventListener ( "keyup" , ( ) => {
67
- if ( ( < HTMLInputElement > queryInput ) . value === "" ) {
68
- ( < HTMLButtonElement > submitButton ) . disabled = true ;
69
- } else {
70
- ( < HTMLButtonElement > submitButton ) . disabled = false ;
71
- }
71
+ if ( ( < HTMLInputElement > queryInput ) . value === "" ) {
72
+ ( < HTMLButtonElement > submitButton ) . disabled = true ;
73
+ } else {
74
+ ( < HTMLButtonElement > submitButton ) . disabled = false ;
75
+ }
72
76
} ) ;
73
77
74
78
// If user presses enter, click submit button
75
79
queryInput . addEventListener ( "keyup" , ( event ) => {
76
- if ( event . code === "Enter" ) {
77
- event . preventDefault ( ) ;
78
- submitButton . click ( ) ;
79
- }
80
+ if ( event . code === "Enter" ) {
81
+ event . preventDefault ( ) ;
82
+ submitButton . click ( ) ;
83
+ }
80
84
} ) ;
81
85
82
86
// Listen for clicks on submit button
83
87
async function handleClick ( ) {
84
- // Get the message from the input field
85
- const message = ( < HTMLInputElement > queryInput ) . value ;
86
- console . log ( "message" , message ) ;
87
- // Clear the answer
88
- document . getElementById ( "answer" ) ! . innerHTML = "" ;
89
- // Hide the answer
90
- document . getElementById ( "answerWrapper" ) ! . style . display = "none" ;
91
- // Show the loading indicator
92
- document . getElementById ( "loading-indicator" ) ! . style . display = "block" ;
93
-
94
- // Generate response
95
- let inp = message ;
96
- if ( context . length > 0 ) {
97
- inp = "Use only the following context when answering the question at the end. Don't use any other knowledge.\n" + context + "\n\nQuestion: " + message + "\n\nHelpful Answer: " ;
98
- }
99
- console . log ( "Input:" , inp ) ;
100
- chatHistory . push ( { "role" : "user" , "content" : inp } ) ;
101
-
102
- let curMessage = "" ;
103
- const completion = await engine . chat . completions . create ( { stream : true , messages : chatHistory } ) ;
104
- for await ( const chunk of completion ) {
105
- const curDelta = chunk . choices [ 0 ] . delta . content ;
106
- if ( curDelta ) {
107
- curMessage += curDelta ;
108
- }
109
- updateAnswer ( curMessage ) ;
88
+ // Get the message from the input field
89
+ const message = ( < HTMLInputElement > queryInput ) . value ;
90
+ console . log ( "message" , message ) ;
91
+ // Clear the answer
92
+ document . getElementById ( "answer" ) ! . innerHTML = "" ;
93
+ // Hide the answer
94
+ document . getElementById ( "answerWrapper" ) ! . style . display = "none" ;
95
+ // Show the loading indicator
96
+ document . getElementById ( "loading-indicator" ) ! . style . display = "block" ;
97
+
98
+ // Generate response
99
+ let inp = message ;
100
+ if ( context . length > 0 ) {
101
+ inp =
102
+ "Use only the following context when answering the question at the end. Don't use any other knowledge.\n" +
103
+ context +
104
+ "\n\nQuestion: " +
105
+ message +
106
+ "\n\nHelpful Answer: " ;
107
+ }
108
+ console . log ( "Input:" , inp ) ;
109
+ chatHistory . push ( { role : "user" , content : inp } ) ;
110
+
111
+ let curMessage = "" ;
112
+ const completion = await engine . chat . completions . create ( {
113
+ stream : true ,
114
+ messages : chatHistory ,
115
+ } ) ;
116
+ for await ( const chunk of completion ) {
117
+ const curDelta = chunk . choices [ 0 ] . delta . content ;
118
+ if ( curDelta ) {
119
+ curMessage += curDelta ;
110
120
}
111
- const response = await engine . getMessage ( ) ;
112
- chatHistory . push ( { "role" : "assistant" , "content" : await engine . getMessage ( ) } ) ;
113
- console . log ( "response" , response ) ;
121
+ updateAnswer ( curMessage ) ;
122
+ }
123
+ const response = await engine . getMessage ( ) ;
124
+ chatHistory . push ( { role : "assistant" , content : await engine . getMessage ( ) } ) ;
125
+ console . log ( "response" , response ) ;
114
126
}
115
127
submitButton . addEventListener ( "click" , handleClick ) ;
116
128
117
129
// Listen for messages from the background script
118
130
chrome . runtime . onMessage . addListener ( ( { answer, error } ) => {
119
- if ( answer ) {
120
- updateAnswer ( answer ) ;
121
- }
131
+ if ( answer ) {
132
+ updateAnswer ( answer ) ;
133
+ }
122
134
} ) ;
123
135
124
136
function updateAnswer ( answer : string ) {
125
- // Show answer
126
- document . getElementById ( "answerWrapper" ) ! . style . display = "block" ;
127
- const answerWithBreaks = answer . replace ( / \n / g, '<br>' ) ;
128
- document . getElementById ( "answer" ) ! . innerHTML = answerWithBreaks ;
129
- // Add event listener to copy button
130
- document . getElementById ( "copyAnswer" ) ! . addEventListener ( "click" , ( ) => {
131
- // Get the answer text
132
- const answerText = answer ;
133
- // Copy the answer text to the clipboard
134
- navigator . clipboard . writeText ( answerText )
135
- . then ( ( ) => console . log ( "Answer text copied to clipboard" ) )
136
- . catch ( ( err ) => console . error ( "Could not copy text: " , err ) ) ;
137
- } ) ;
138
- const options : Intl . DateTimeFormatOptions = { month : 'short' , day : '2-digit' , hour : '2-digit' , minute : '2-digit' , second : '2-digit' } ;
139
- const time = new Date ( ) . toLocaleString ( 'en-US' , options ) ;
140
- // Update timestamp
141
- document . getElementById ( "timestamp" ) ! . innerText = time ;
142
- // Hide loading indicator
143
- document . getElementById ( "loading-indicator" ) ! . style . display = "none" ;
137
+ // Show answer
138
+ document . getElementById ( "answerWrapper" ) ! . style . display = "block" ;
139
+ const answerWithBreaks = answer . replace ( / \n / g, "<br>" ) ;
140
+ document . getElementById ( "answer" ) ! . innerHTML = answerWithBreaks ;
141
+ // Add event listener to copy button
142
+ document . getElementById ( "copyAnswer" ) ! . addEventListener ( "click" , ( ) => {
143
+ // Get the answer text
144
+ const answerText = answer ;
145
+ // Copy the answer text to the clipboard
146
+ navigator . clipboard
147
+ . writeText ( answerText )
148
+ . then ( ( ) => console . log ( "Answer text copied to clipboard" ) )
149
+ . catch ( ( err ) => console . error ( "Could not copy text: " , err ) ) ;
150
+ } ) ;
151
+ const options : Intl . DateTimeFormatOptions = {
152
+ month : "short" ,
153
+ day : "2-digit" ,
154
+ hour : "2-digit" ,
155
+ minute : "2-digit" ,
156
+ second : "2-digit" ,
157
+ } ;
158
+ const time = new Date ( ) . toLocaleString ( "en-US" , options ) ;
159
+ // Update timestamp
160
+ document . getElementById ( "timestamp" ) ! . innerText = time ;
161
+ // Hide loading indicator
162
+ document . getElementById ( "loading-indicator" ) ! . style . display = "none" ;
144
163
}
145
164
146
165
function fetchPageContents ( ) {
147
- chrome . tabs . query ( { currentWindow : true , active : true } , function ( tabs ) {
148
- var port = chrome . tabs . connect ( tabs [ 0 ] . id , { name : "channelName" } ) ;
149
- port . postMessage ( { } ) ;
150
- port . onMessage . addListener ( function ( msg ) {
151
- console . log ( "Page contents:" , msg . contents ) ;
152
- context = msg . contents
153
- } ) ;
166
+ chrome . tabs . query ( { currentWindow : true , active : true } , function ( tabs ) {
167
+ const port = chrome . tabs . connect ( tabs [ 0 ] . id , { name : "channelName" } ) ;
168
+ port . postMessage ( { } ) ;
169
+ port . onMessage . addListener ( function ( msg ) {
170
+ console . log ( "Page contents:" , msg . contents ) ;
171
+ context = msg . contents ;
154
172
} ) ;
173
+ } ) ;
155
174
}
0 commit comments