rocca commited on
Commit
b09f300
·
verified ·
1 Parent(s): de0095c

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +362 -19
index.html CHANGED
@@ -1,19 +1,362 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>A/B Test Captioning UI</title>
8
+ <style>
9
+ html {
10
+ color-scheme: light dark;
11
+ font-family: sans-serif;
12
+ }
13
+ table {
14
+ border-collapse: collapse;
15
+ min-width: 3000px;
16
+ }
17
+
18
+ table th {
19
+ text-align: center;
20
+ }
21
+ table td {
22
+ text-align: left;
23
+ white-space: pre-wrap;
24
+ }
25
+
26
+ button {
27
+ cursor: pointer;
28
+ }
29
+
30
+ th,
31
+ td {
32
+ border: 1px solid light-dark(black, #515151);
33
+ padding: 8px;
34
+ vertical-align: top;
35
+ min-width: 500px;
36
+ max-width: 500px;
37
+ }
38
+
39
+ textarea {
40
+ width: 100%;
41
+ box-sizing: border-box;
42
+ height: 60px;
43
+ }
44
+
45
+ img {
46
+ max-width: min(100%, 20vw);
47
+ }
48
+ </style>
49
+ </head>
50
+
51
+ <body>
52
+
53
+ <button onclick="readmeCtn.hidden=!readmeCtn.hidden">ℹ️ toggle readme</button>
54
+ <pre hidden id="readmeCtn">
55
+ <b>Note: This should work for any OpenAI-compatible API server.</b>
56
+
57
+ <b>STEP 1:</b> Start an OpenAI-compatible API server, for example you can use these params on Runpod:
58
+ <span style="opacity:0.5">IMAGE:</span> openmmlab/lmdeploy:v0.6.2.post1-cu12
59
+ <span style="opacity:0.5">COMMAND:</span> bash -c "pip install timm==1.0.7 && lmdeploy serve api_server OpenGVLab/InternVL2-40B-AWQ --model-name OpenGVLab/InternVL2-40B-AWQ --backend turbomind --server-port 3000 --model-format awq --quant-policy 4"
60
+ <span style="opacity:0.5">VOLUME PATH:</span> /root
61
+ <span style="opacity:0.5">CONTAINER DISK:</span> 10GB
62
+ <span style="opacity:0.5">VOLUME DISK:</span> 60GB
63
+ <span style="opacity:0.5">PORT:</span> 3000
64
+
65
+ <b>STEP 2:</b> Enter your server endpoint url in the input below (e.g. something like like 'https://uf1kmzigq0p5bd-3000.proxy.runpod.net' if you're using the above server command).
66
+
67
+ <b>STEP 3:</b> Enter a system prompt and a prompt at the top of one or more columns, and optionally a prefix that you want the resulting caption to start with.
68
+
69
+ <b>STEP 4:</b> Select some images to test your prompt on (Ctrl+Click to select multiple images).
70
+
71
+ <b>STEP 5:</b> Click 'Compute captions for all columns' or click one of the individual column buttons.
72
+ </pre>
73
+ <br><br>
74
+
75
+ <div style="margin:0.5rem 0;">Scale images down if larger than: <input id="maxImageSizeEl" placeholder="Max image size (px)" style="max-width:70px;" oninput="localStorage.maxImageSize=this.value; updateTable()"></div>
76
+ <script>maxImageSizeEl.value = localStorage.maxImageSize || '768';</script>
77
+
78
+ <div style="margin:0.5rem 0;">
79
+ Defaults:
80
+ <input id="serverOriginEl" placeholder="OpenAI-compatible server URL" style="width:300px;" oninput="localStorage.serverOrigin=this.value">
81
+ <script>serverOriginEl.value = localStorage.serverOrigin || '';</script>
82
+ <input id="modelNameEl" placeholder="Model name (huggingface repo)" style="width:300px;" oninput="localStorage.modelName=this.value">
83
+ <script>modelNameEl.value = localStorage.modelName || 'OpenGVLab/InternVL2-40B-AWQ';</script>
84
+ </div>
85
+
86
+ <button id="imageLoader"><b>1. Select test images</b></button>
87
+ <button onclick="computeCaptions()"><b>2. Compute captions for all columns</b></button>
88
+
89
+ <table id="captionTable" style="margin-top:0.5rem;">
90
+ <tr>
91
+ <th>Images</th>
92
+ <th>
93
+ <div style="display:flex;"><input style="flex-grow:1;" id="prompt1ServerOriginEl" placeholder="(Optional) Override server URL"><input style="flex-grow:1;" id="prompt1ModelNameEl" placeholder="(Optional) Override model name"></div>
94
+ <div style="text-align:center; font-weight:bold; margin-top:0.5rem;">Prompt 1</div>
95
+ <textarea id="systemPrompt1El" placeholder="System prompt 1, e.g. 'You are an expert image captioner, ...'" oninput="localStorage.systemPrompt1=this.value"></textarea>
96
+ <textarea id="prompt1El" placeholder="Prompt 1, e.g. 'Caption this image.'" oninput="localStorage.prompt1=this.value"></textarea>
97
+ <textarea id="prefix1El" placeholder="(Optional) Prefix 1, e.g. 'It's an image of'" oninput="localStorage.prefix1=this.value"></textarea>
98
+ <script>prompt1El.value = localStorage.prompt1 || ''; prefix1El.value = localStorage.prefix1 || '';</script>
99
+ <button onclick="computeCaptions(0); this.disabled=true; setTimeout(() => this.disabled=false, 1000)">(re)compute captions for this column</button>
100
+ </th>
101
+ <th>
102
+ <div style="display:flex;"><input style="flex-grow:1;" id="prompt2ServerOriginEl" placeholder="(Optional) Override server URL"><input style="flex-grow:1;" id="prompt2ModelNameEl" placeholder="(Optional) Override model name"></div>
103
+ <div style="text-align:center; font-weight:bold; margin-top:0.5rem;">Prompt 2</div>
104
+ <textarea id="systemPrompt2El" placeholder="System prompt 2, e.g. 'You are an assistant who accurately ...'" oninput="localStorage.systemPrompt2=this.value"></textarea>
105
+ <textarea id="prompt2El" placeholder="Prompt 2, e.g. 'Describe this image in extreme detail.'" oninput="localStorage.prompt2=this.value"></textarea>
106
+ <textarea id="prefix2El" placeholder="(Optional) Prefix 2, e.g. 'The image depicts'" oninput="localStorage.prefix2=this.value"></textarea>
107
+ <script>prompt2El.value = localStorage.prompt2 || ''; prefix2El.value = localStorage.prefix2 || '';</script>
108
+ <button onclick="computeCaptions(1); this.disabled=true; setTimeout(() => this.disabled=false, 1000)">(re)compute captions for this column</button>
109
+ </th>
110
+ <th>
111
+ <div style="display:flex;"><input style="flex-grow:1;" id="prompt3ServerOriginEl" placeholder="(Optional) Override server URL"><input style="flex-grow:1;" id="prompt3ModelNameEl" placeholder="(Optional) Override model name"></div>
112
+ <div style="text-align:center; font-weight:bold; margin-top:0.5rem;">Prompt 3</div>
113
+ <textarea id="systemPrompt3El" placeholder="System prompt 3" oninput="localStorage.systemPrompt3=this.value"></textarea>
114
+ <textarea id="prompt3El" placeholder="Prompt 3" oninput="localStorage.prompt3=this.value"></textarea>
115
+ <textarea id="prefix3El" placeholder="Prefix 3" oninput="localStorage.prefix3=this.value"></textarea>
116
+ <script>prompt3El.value = localStorage.prompt3 || ''; prefix3El.value = localStorage.prefix3 || '';</script>
117
+ <button onclick="computeCaptions(2); this.disabled=true; setTimeout(() => this.disabled=false, 1000)">(re)compute captions for this column</button>
118
+ </th>
119
+ <th>
120
+ <div style="display:flex;"><input style="flex-grow:1;" id="prompt4ServerOriginEl" placeholder="(Optional) Override server URL"><input style="flex-grow:1;" id="prompt4ModelNameEl" placeholder="(Optional) Override model name"></div>
121
+ <div style="text-align:center; font-weight:bold; margin-top:0.5rem;">Prompt 4</div>
122
+ <textarea id="systemPrompt4El" placeholder="System prompt 4" oninput="localStorage.systemPrompt4=this.value"></textarea>
123
+ <textarea id="prompt4El" placeholder="Prompt 4" oninput="localStorage.prompt4=this.value"></textarea>
124
+ <textarea id="prefix4El" placeholder="Prefix 4" oninput="localStorage.prefix4=this.value"></textarea>
125
+ <script>prompt4El.value = localStorage.prompt4 || ''; prefix4El.value = localStorage.prefix4 || '';</script>
126
+ <button onclick="computeCaptions(3); this.disabled=true; setTimeout(() => this.disabled=false, 1000)">(re)compute captions for this column</button>
127
+ </th>
128
+ <th>
129
+ <div style="display:flex;"><input style="flex-grow:1;" id="prompt5ServerOriginEl" placeholder="(Optional) Override server URL"><input style="flex-grow:1;" id="prompt5ModelNameEl" placeholder="(Optional) Override model name"></div>
130
+ <div style="text-align:center; font-weight:bold; margin-top:0.5rem;">Prompt 5</div>
131
+ <textarea id="systemPrompt5El" placeholder="System prompt 5" oninput="localStorage.systemPrompt5=this.value"></textarea>
132
+ <textarea id="prompt5El" placeholder="Prompt 5" oninput="localStorage.prompt5=this.value"></textarea>
133
+ <textarea id="prefix5El" placeholder="Prefix 5" oninput="localStorage.prefix5=this.value"></textarea>
134
+ <script>prompt5El.value = localStorage.prompt5 || ''; prefix5El.value = localStorage.prefix5 || '';</script>
135
+ <button onclick="computeCaptions(4); this.disabled=true; setTimeout(() => this.disabled=false, 1000)">(re)compute captions for this column</button>
136
+ </th>
137
+ </tr>
138
+ </table>
139
+
140
+ <script>
141
+ window.testImages = [];
142
+
143
+ // At the top of your script, declare these variables
144
+ let imageHandles = [];
145
+
146
+ // Replace the existing image loader code with this:
147
+ document.getElementById('imageLoader').addEventListener('click', async function (e) {
148
+ try {
149
+ const handles = await window.showOpenFilePicker({
150
+ multiple: true,
151
+ types: [{
152
+ description: 'Images',
153
+ accept: {
154
+ 'image/*': ['.png', '.gif', '.jpeg', '.jpg', '.webp', '.avif', '.svg']
155
+ }
156
+ }]
157
+ });
158
+ imageHandles = handles;
159
+ let { get, set } = await import('https://unpkg.com/[email protected]/dist/esm/index.js');
160
+ await set('imageHandles', imageHandles);
161
+ await loadImagesFromHandles();
162
+ } catch (err) {
163
+ console.error("Error selecting files:", err);
164
+ }
165
+ });
166
+
167
+ async function loadImagesFromHandles() {
168
+ window.testImages = [];
169
+ for (const handle of imageHandles) {
170
+ const file = await handle.getFile();
171
+ const dataUrl = await fileToDataUrl(file);
172
+ window.testImages.push(dataUrl);
173
+ }
174
+ updateTable();
175
+ }
176
+
177
+ function fileToDataUrl(file) {
178
+ return new Promise((resolve, reject) => {
179
+ const reader = new FileReader();
180
+ reader.onload = () => resolve(reader.result);
181
+ reader.onerror = reject;
182
+ reader.readAsDataURL(file);
183
+ });
184
+ }
185
+
186
+ // Add this function to load images when the page loads
187
+ async function loadSavedImages() {
188
+ let { get, set } = await import('https://unpkg.com/[email protected]/dist/esm/index.js');
189
+ imageHandles = await get('imageHandles') || [];
190
+ if (imageHandles.length > 0) {
191
+ await loadImagesFromHandles();
192
+ }
193
+ }
194
+
195
+ // Call this function when the page loads
196
+ window.addEventListener('load', async () => {
197
+ // await new Promise(rs => document.addEventListener('click', rs, { once: true }));
198
+ loadSavedImages();
199
+ });
200
+
201
+ function resizeDataUrl({ dataUrl, maxWidth, maxHeight }) {
202
+ return new Promise((resolve) => {
203
+ const img = new Image();
204
+ img.onload = () => {
205
+ const canvas = document.createElement('canvas');
206
+ let { width, height } = img;
207
+ if (width > maxWidth || height > maxHeight) {
208
+ const ratio = Math.min(maxWidth / width, maxHeight / height);
209
+ width *= ratio;
210
+ height *= ratio;
211
+ }
212
+ canvas.width = width;
213
+ canvas.height = height;
214
+ canvas.getContext('2d').drawImage(img, 0, 0, width, height);
215
+ resolve(canvas.toDataURL("image/jpeg"));
216
+ };
217
+ img.src = dataUrl;
218
+ });
219
+ };
220
+
221
+ async function updateTable() {
222
+ const table = document.getElementById('captionTable');
223
+ // Clear existing rows except header
224
+ while (table.rows.length > 1) {
225
+ table.deleteRow(1);
226
+ }
227
+
228
+ for (let i = 0; i < window.testImages.length; i++) {
229
+ const row = table.insertRow(-1);
230
+ const cell = row.insertCell(0);
231
+ const img = document.createElement('img');
232
+ img.style.pointerEvents = "auto";
233
+
234
+ let maxImageSize = Number(maxImageSizeEl.value);
235
+ let imageUrl = await resizeDataUrl({ dataUrl: window.testImages[i], maxWidth: maxImageSize, maxHeight: maxImageSize });
236
+ img.src = imageUrl;
237
+ cell.appendChild(img);
238
+
239
+ cell.style.pointerEvents = "none";
240
+ cell.style.position = 'sticky';
241
+ cell.style.left = '0';
242
+
243
+ for (let j = 0; j < 5; j++) {
244
+ const cell = row.insertCell(-1);
245
+ cell.textContent = `Caption will appear here`;
246
+ }
247
+ }
248
+ }
249
+
250
+ async function computeCaptions(columnI=null) {
251
+
252
+ if(window.testImages.length === 0) return alert("Choose images first.");
253
+ if(!serverOriginEl.value.startsWith("https://") && !serverOriginEl.value.startsWith("http://")) return alert("Please enter a valid server URL. It should start with 'https://' or 'http://'.");
254
+
255
+ const serverOrigins = [
256
+ prompt1ServerOriginEl.value.trim() || serverOriginEl.value.trim(),
257
+ prompt2ServerOriginEl.value.trim() || serverOriginEl.value.trim(),
258
+ prompt3ServerOriginEl.value.trim() || serverOriginEl.value.trim(),
259
+ prompt4ServerOriginEl.value.trim() || serverOriginEl.value.trim(),
260
+ prompt5ServerOriginEl.value.trim() || serverOriginEl.value.trim(),
261
+ ];
262
+
263
+ const modelNames = [
264
+ prompt1ModelNameEl.value.trim() || modelNameEl.value.trim(),
265
+ prompt2ModelNameEl.value.trim() || modelNameEl.value.trim(),
266
+ prompt3ModelNameEl.value.trim() || modelNameEl.value.trim(),
267
+ prompt4ModelNameEl.value.trim() || modelNameEl.value.trim(),
268
+ prompt5ModelNameEl.value.trim() || modelNameEl.value.trim(),
269
+ ];
270
+
271
+ const systemPrompts = [
272
+ systemPrompt1El.value || '',
273
+ systemPrompt2El.value || '',
274
+ systemPrompt3El.value || '',
275
+ systemPrompt4El.value || '',
276
+ systemPrompt5El.value || '',
277
+ ];
278
+
279
+ const prompts = [
280
+ prompt1El.value || '',
281
+ prompt2El.value || '',
282
+ prompt3El.value || '',
283
+ prompt4El.value || '',
284
+ prompt5El.value || '',
285
+ ];
286
+
287
+ const prefixes = [
288
+ prefix1El.value || '',
289
+ prefix2El.value || '',
290
+ prefix3El.value || '',
291
+ prefix4El.value || '',
292
+ prefix5El.value || '',
293
+ ];
294
+
295
+ const table = document.getElementById('captionTable');
296
+
297
+ for (let i = 1; i < table.rows.length; i++) {
298
+ for (let j = 1; j < 6; j++) {
299
+ if(columnI !== null && j !== columnI+1) continue;
300
+ let systemPrompt = systemPrompts[j-1].trim();
301
+ let prompt = prompts[j-1].trim();
302
+ let prefix = prefixes[j-1].trim();
303
+ let serverOrigin = serverOrigins[j-1].trim();
304
+ let modelName = modelNames[j-1].trim();
305
+
306
+ if(!systemPrompt && !prompt && !prefix) continue;
307
+
308
+ const cell = table.rows[i].cells[j];
309
+ cell.textContent = 'Computing...';
310
+
311
+ computeCaption(window.testImages[i-1], systemPrompt, prompt, prefix, serverOrigin, modelName).then(caption => {
312
+ cell.textContent = caption;
313
+ });
314
+
315
+ }
316
+ }
317
+ }
318
+
319
+ async function computeCaption(imageUrl, systemPrompt, prompt, prefix, serverOrigin, modelName) {
320
+
321
+ let maxImageSize = Number(maxImageSizeEl.value);
322
+ let originalImageSize = imageUrl.length;
323
+ imageUrl = await resizeDataUrl({ dataUrl: imageUrl, maxWidth: maxImageSize, maxHeight: maxImageSize });
324
+ console.log(`original size: ${originalImageSize}, new size: ${imageUrl.length}`);
325
+
326
+ let startTime = Date.now();
327
+ let result = await fetch(`${serverOrigin.trim().replace(/\/$/, "")}/v1/chat/completions`, {
328
+ headers: { "content-type": "application/json" },
329
+ body: JSON.stringify({
330
+ model: modelName.trim(),
331
+ stream: false,
332
+ messages: [
333
+ {
334
+ role: "system",
335
+ content: systemPrompt,
336
+ },
337
+ {
338
+ role: "user",
339
+ content: [
340
+ { "type": "text", "text": prompt.trim() },
341
+ { "type": "image_url", "image_url": { "url": imageUrl } },
342
+ ],
343
+ },
344
+ {
345
+ role: "assistant",
346
+ content: (prefix || "").trim(),
347
+ },
348
+ ]
349
+ }),
350
+ method: "POST",
351
+ }).then(r => r.json()).catch(e => console.error("Error computing caption:", e));
352
+ if(result?.object === "error") return alert(result.message);
353
+
354
+ console.log(result.choices[0].message.content);
355
+ console.log("tokens:", result.usage.total_tokens);
356
+ console.log("time:", Date.now() - startTime);
357
+ return (prefix || "") + result.choices[0].message.content;
358
+ }
359
+ </script>
360
+ </body>
361
+
362
+ </html>