Skip to content

Commit d9c4acc

Browse files
authored
server : (webui) rename has_multimodal --> modalities (#13393)
* server : (webui) rename has_multimodal --> modalities * allow converting SVG to PNG * less complicated code
1 parent 15e0328 commit d9c4acc

File tree

3 files changed

+67
-5
lines changed

3 files changed

+67
-5
lines changed

tools/server/public/index.html.gz

102 Bytes
Binary file not shown.

tools/server/webui/src/components/useChatExtraContext.tsx

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,26 @@ export function useChatExtraContext(): ChatExtraContextApi {
3737
break;
3838
}
3939

40-
if (mimeType.startsWith('image/') && mimeType !== 'image/svg+xml') {
41-
if (!serverProps?.has_multimodal) {
40+
if (mimeType.startsWith('image/')) {
41+
if (!serverProps?.modalities?.vision) {
4242
toast.error('Multimodal is not supported by this server or model.');
4343
break;
4444
}
4545
const reader = new FileReader();
46-
reader.onload = (event) => {
46+
reader.onload = async (event) => {
4747
if (event.target?.result) {
48+
let base64Url = event.target.result as string;
49+
50+
if (mimeType === 'image/svg+xml') {
51+
// Convert SVG to PNG
52+
base64Url = await svgBase64UrlToPngDataURL(base64Url);
53+
}
54+
4855
addItems([
4956
{
5057
type: 'imageFile',
5158
name: file.name,
52-
base64Url: event.target.result as string,
59+
base64Url,
5360
},
5461
]);
5562
}
@@ -172,3 +179,56 @@ export function isLikelyNotBinary(str: string): boolean {
172179
const ratio = suspiciousCharCount / sampleLength;
173180
return ratio <= options.suspiciousCharThresholdRatio;
174181
}
182+
183+
// WARN: vibe code below
184+
// Converts a Base64URL encoded SVG string to a PNG Data URL using browser Canvas API.
185+
function svgBase64UrlToPngDataURL(base64UrlSvg: string): Promise<string> {
186+
const backgroundColor = 'white'; // Default background color for PNG
187+
188+
return new Promise((resolve, reject) => {
189+
try {
190+
const img = new Image();
191+
192+
img.onload = () => {
193+
const canvas = document.createElement('canvas');
194+
const ctx = canvas.getContext('2d');
195+
196+
if (!ctx) {
197+
reject(new Error('Failed to get 2D canvas context.'));
198+
return;
199+
}
200+
201+
// Use provided dimensions or SVG's natural dimensions, with fallbacks
202+
// Fallbacks (e.g., 300x300) are for SVGs without explicit width/height
203+
// or when naturalWidth/Height might be 0 before full processing.
204+
const targetWidth = img.naturalWidth || 300;
205+
const targetHeight = img.naturalHeight || 300;
206+
207+
canvas.width = targetWidth;
208+
canvas.height = targetHeight;
209+
210+
if (backgroundColor) {
211+
ctx.fillStyle = backgroundColor;
212+
ctx.fillRect(0, 0, canvas.width, canvas.height);
213+
}
214+
215+
ctx.drawImage(img, 0, 0, targetWidth, targetHeight);
216+
resolve(canvas.toDataURL('image/png'));
217+
};
218+
219+
img.onerror = () => {
220+
reject(
221+
new Error('Failed to load SVG image. Ensure the SVG data is valid.')
222+
);
223+
};
224+
225+
// Load SVG string into an Image element
226+
img.src = base64UrlSvg;
227+
} catch (error) {
228+
const message = error instanceof Error ? error.message : String(error);
229+
const errorMessage = `Error converting SVG to PNG: ${message}`;
230+
toast.error(errorMessage);
231+
reject(new Error(errorMessage));
232+
}
233+
});
234+
}

tools/server/webui/src/utils/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ export interface LlamaCppServerProps {
118118
build_info: string;
119119
model_path: string;
120120
n_ctx: number;
121-
has_multimodal: boolean;
121+
modalities?: {
122+
vision: boolean;
123+
};
122124
// TODO: support params
123125
}

0 commit comments

Comments
 (0)