ui: Restructure repo to use tools/ui folder and ui / UI / llama-ui / LLAMA_UI naming (#23064)

* webui: Move static build output from `tools/server/public` to `build/ui` directory

* refactor: Move to `tools/ui`

* refactor: rename CMake variables and preprocessor defines

- Rename LLAMA_BUILD_WEBUI -> LLAMA_BUILD_UI (old kept as deprecated)
- Rename LLAMA_USE_PREBUILT_WEBUI -> LLAMA_USE_PREBUILT_UI (old kept as deprecated)
- Backward compat: old vars auto-forward to new ones with DEPRECATION warning
- Rename internal vars: WEBUI_SOURCE -> UI_SOURCE, WEBUI_SOURCE_DIR -> UI_SOURCE_DIR, etc.
- Rename HF bucket: LLAMA_WEBUI_HF_BUCKET -> LLAMA_UI_HF_BUCKET
- Emit both LLAMA_BUILD_WEBUI and LLAMA_BUILD_UI preprocessor defines
- Emit both LLAMA_WEBUI_DEFAULT_ENABLED and LLAMA_UI_DEFAULT_ENABLED

* refactor: rename CLI flags (--webui -> --ui) with backward compat

- Add --ui/--no-ui (old --webui/--no-webui kept as deprecated aliases)
- Add --ui-config (old --webui-config kept as deprecated alias)
- Add --ui-config-file (old --webui-config-file kept as deprecated alias)
- Add --ui-mcp-proxy/--no-ui-mcp-proxy (old --webui-mcp-proxy kept as deprecated)
- Add new env vars: LLAMA_ARG_UI, LLAMA_ARG_UI_CONFIG, LLAMA_ARG_UI_CONFIG_FILE, LLAMA_ARG_UI_MCP_PROXY
- C++ struct fields: params.ui, params.ui_config_json, params.ui_mcp_proxy added alongside old fields
- Backward compat: old fields synced to new ones in g_params_to_internals

* refactor: update C++ server internals with backward compat

- Rename json_webui_settings -> json_ui_settings (both kept in server_context_meta)
- Rename params.webui usage -> params.ui (both synced, old still works)
- JSON API emits both "ui"/"ui_settings" and "webui"/"webui_settings" keys
- Server routes use params.ui_mcp_proxy || params.webui_mcp_proxy
- Preprocessor guards use #if defined(LLAMA_BUILD_UI) || defined(LLAMA_BUILD_WEBUI)

* refactor: rename CI/CD workflows, artifacts, and build script

- Rename webui-build.yml -> ui-build.yml; artifact webui-build -> ui-build
- Rename webui-publish.yml -> ui-publish.yml; var HF_BUCKET_WEBUI_STATIC_OUTPUT -> HF_BUCKET_UI_STATIC_OUTPUT
- Rename server-webui.yml -> server-ui.yml; job webui-build/checks -> ui-build/checks
- Update server.yml: job/artifact refs webui-build -> ui-build
- Update release.yml: all webui-build/publish refs -> ui-build/publish; HF_TOKEN_WEBUI_STATIC_OUTPUT -> HF_TOKEN_UI_STATIC_OUTPUT
- Update server-self-hosted.yml: webui-build -> ui-build
- Update build-self-hosted.yml: HF_WEBUI_VERSION -> HF_UI_VERSION
- Rename webui-download.cmake -> ui-download.cmake (internal refs updated)
- Update labeler.yml: server/webui -> server/ui path label

* docs: update CODEOWNERS and server README docs

- Update CODEOWNERS: team ggml-org/llama-webui -> ggml-org/llama-ui, path /tools/server/webui/ -> /tools/ui/
- Update server README.md: CLI tables show --ui flags with deprecated --webui aliases
- Update server README-dev.md: "WebUI" -> "UI", paths updated to tools/ui/

* fix: Small fixes for UI build

* fix: CMake.txt syntax

* chore: Formatting

* fix: `.editorconfig` for llama-ui

* chore: Formatting

* refactor: Use `APP_NAME` in Error route

* refactor: Cleanup

* refactor: Single migration service

* make llama-ui a linkable target

* fix: UI Build output

* fix: Missing change

* fix: separate llama-ui npm build output into build/tools/ui/dist subfolder + use cmake npm build instead of downloading ui-build.yml artifacts in CI

* refactor: UI workflows cleanup

---------

Co-authored-by: Xuan Son Nguyen <son@huggingface.co>
This commit is contained in:
Aleksander Grygier
2026-05-16 02:02:40 +02:00
committed by GitHub
co-authored by Xuan Son Nguyen
parent 49d1701bd2
commit 59778f0196
565 changed files with 1610 additions and 694 deletions
@@ -0,0 +1,157 @@
/**
* File validation utilities based on model modalities
* Ensures only compatible file types are processed based on model capabilities
*/
import { getFileTypeCategory } from '$lib/utils';
import { FileTypeCategory } from '$lib/enums';
import type { ModalityCapabilities } from '$lib/types';
/**
* Check if a file type is supported by the given modalities
* @param filename - The filename to check
* @param mimeType - The MIME type of the file
* @param capabilities - The modality capabilities to check against
* @returns true if the file type is supported
*/
export function isFileTypeSupportedByModel(
filename: string,
mimeType: string | undefined,
capabilities: ModalityCapabilities
): boolean {
const category = mimeType ? getFileTypeCategory(mimeType) : null;
// If we can't determine the category from MIME type, fall back to general support check
if (!category) {
// For unknown types, only allow if they might be text files
// This is a conservative approach for edge cases
return true; // Let the existing isFileTypeSupported handle this
}
switch (category) {
case FileTypeCategory.TEXT:
// Text files are always supported
return true;
case FileTypeCategory.PDF:
// PDFs are always supported (will be processed as text for non-vision models)
return true;
case FileTypeCategory.IMAGE:
// Images require vision support
return capabilities.hasVision;
case FileTypeCategory.AUDIO:
// Audio files require audio support
return capabilities.hasAudio;
default:
// Unknown categories - be conservative and allow
return true;
}
}
/**
* Filter files based on model modalities and return supported/unsupported lists
* @param files - Array of files to filter
* @param capabilities - The modality capabilities to check against
* @returns Object with supportedFiles and unsupportedFiles arrays
*/
export function filterFilesByModalities(
files: File[],
capabilities: ModalityCapabilities
): {
supportedFiles: File[];
unsupportedFiles: File[];
modalityReasons: Record<string, string>;
} {
const supportedFiles: File[] = [];
const unsupportedFiles: File[] = [];
const modalityReasons: Record<string, string> = {};
const { hasVision, hasAudio } = capabilities;
for (const file of files) {
const category = getFileTypeCategory(file.type);
let isSupported = true;
let reason = '';
switch (category) {
case FileTypeCategory.IMAGE:
if (!hasVision) {
isSupported = false;
reason = 'Images require a vision-capable model';
}
break;
case FileTypeCategory.AUDIO:
if (!hasAudio) {
isSupported = false;
reason = 'Audio files require an audio-capable model';
}
break;
case FileTypeCategory.TEXT:
case FileTypeCategory.PDF:
// Always supported
break;
default:
// For unknown types, check if it's a generally supported file type
// This handles edge cases and maintains backward compatibility
break;
}
if (isSupported) {
supportedFiles.push(file);
} else {
unsupportedFiles.push(file);
modalityReasons[file.name] = reason;
}
}
return { supportedFiles, unsupportedFiles, modalityReasons };
}
/**
* Generate a user-friendly error message for unsupported files
* @param unsupportedFiles - Array of unsupported files
* @param modalityReasons - Reasons why files are unsupported
* @param capabilities - The modality capabilities to check against
* @returns Formatted error message
*/
export function generateModalityErrorMessage(
unsupportedFiles: File[],
modalityReasons: Record<string, string>,
capabilities: ModalityCapabilities
): string {
if (unsupportedFiles.length === 0) return '';
const { hasVision, hasAudio } = capabilities;
let message = '';
if (unsupportedFiles.length === 1) {
const file = unsupportedFiles[0];
const reason = modalityReasons[file.name];
message = `The file "${file.name}" cannot be uploaded: ${reason}.`;
} else {
const fileNames = unsupportedFiles.map((f) => f.name).join(', ');
message = `The following files cannot be uploaded: ${fileNames}.`;
}
// Add helpful information about what is supported
const supportedTypes: string[] = ['text files', 'PDFs'];
if (hasVision) supportedTypes.push('images');
if (hasAudio) supportedTypes.push('audio files');
message += ` This model supports: ${supportedTypes.join(', ')}.`;
return message;
}
/**
* Generate file input accept string based on model modalities
* @param capabilities - The modality capabilities to check against
* @returns Accept string for HTML file input element
*/