Buckets:
| /** | |
| * Utility functions for handling Gradio endpoint detection and configuration | |
| */ | |
| import type { SpaceTool } from '../../shared/settings.js'; | |
| import { GRADIO_PREFIX, GRADIO_PRIVATE_PREFIX } from '../../shared/constants.js'; | |
| import { logger } from './logger.js'; | |
| import { getGradioSpaces } from './gradio-discovery.js'; | |
| import { repoExists } from '@huggingface/hub'; | |
| import type { FileListingSource } from '@llmindset/hf-mcp'; | |
| /** | |
| * Determines if a tool name represents a Gradio endpoint | |
| * Gradio tools follow the pattern: gr<number>_<name> or grp<number>_<name> | |
| * | |
| * @param toolName - The name of the tool to check | |
| * @returns true if the tool is a Gradio endpoint, false otherwise | |
| * | |
| * @example | |
| * isGradioTool('gr1_evalstate_flux1_schnell') // true | |
| * isGradioTool('grp2_private_tool') // true | |
| * isGradioTool('hf_doc_search') // false | |
| * isGradioTool('regular_tool') // false | |
| */ | |
| export function isGradioTool(toolName: string): boolean { | |
| // Gradio tools follow pattern: gr<number>_<name> or grp<number>_<name> | |
| return /^grp?\d+_/.test(toolName); | |
| } | |
| /** | |
| * Creates a Gradio tool name based on tool name, index, and privacy status | |
| * This is the core logic used throughout the application for generating tool names | |
| * | |
| * @param toolName - The tool name (e.g., "flux1_schnell", "EasyGhibli") | |
| * @param index - Zero-based index position (will be converted to 1-based) | |
| * @param isPrivate - Whether this is a private space (determines gr vs grp prefix) | |
| * @param toolIndex - Optional tool index within the endpoint for uniqueness when truncating | |
| * @returns The generated tool name following Gradio naming convention | |
| * | |
| * @example | |
| * createGradioToolName('flux1_schnell', 0, false) // 'gr1_flux1_schnell' | |
| * createGradioToolName('EasyGhibli', 1, false) // 'gr2_easyghibli' | |
| * createGradioToolName('private.model', 2, true) // 'grp3_private_model' | |
| */ | |
| export function createGradioToolName( | |
| toolName: string, | |
| index: number, | |
| isPrivate: boolean | undefined, | |
| toolIndex?: number | |
| ): string { | |
| // Choose prefix based on privacy status | |
| const prefix = isPrivate ? GRADIO_PRIVATE_PREFIX : GRADIO_PREFIX; | |
| const indexStr = (index + 1).toString(); | |
| // Calculate available space for the sanitized name (49 - prefix - index - underscore) | |
| const maxNameLength = 49 - prefix.length - indexStr.length - 1; | |
| // Sanitize the tool name: replace special characters with underscores, normalize multiple underscores, and lowercase | |
| let sanitizedName = toolName | |
| ? toolName | |
| .replace(/[-\s.]+/g, '_') // Replace special chars with underscores | |
| .toLowerCase() | |
| : 'unknown'; | |
| // Handle based on length | |
| if (sanitizedName.length > maxNameLength) { | |
| // Over limit: insert tool index at beginning if provided, then truncate | |
| if (toolIndex !== undefined) { | |
| // Insert tool index after the underscore: gr1_0_toolname | |
| const toolIndexPrefix = `${toolIndex}_`; | |
| const availableForName = maxNameLength - toolIndexPrefix.length; | |
| // Keep first 20 chars, add underscore, then keep as many chars from the end as possible | |
| const keepFromEnd = availableForName - 20 - 1; // -1 for the underscore | |
| sanitizedName = toolIndexPrefix + sanitizedName.substring(0, 20) + '_' + sanitizedName.slice(-keepFromEnd); | |
| } else { | |
| // No tool index, just do middle truncation as before | |
| const keepFromEnd = maxNameLength - 20 - 1; // -1 for the underscore | |
| sanitizedName = sanitizedName.substring(0, 20) + '_' + sanitizedName.slice(-keepFromEnd); | |
| } | |
| } | |
| // Under limit: keep as-is, no normalization | |
| // Create tool name: {prefix}{1-based-index}_{sanitized_name} | |
| return `${prefix}${indexStr}_${sanitizedName}`; | |
| } | |
| /** | |
| * Parsed Gradio space ID before subdomain resolution | |
| */ | |
| interface ParsedGradioSpace { | |
| name: string; // e.g., "microsoft/Florence-2-large" | |
| } | |
| /** | |
| * Parses the gradio parameter to extract space IDs. | |
| * Does NOT construct subdomains - they must be fetched from the HuggingFace API. | |
| * | |
| * @param gradioParam - Comma-separated list of space IDs (e.g., "microsoft/Florence-2-large,acme/foo") | |
| * @returns Array of parsed space IDs | |
| * | |
| * @example | |
| * parseGradioSpaceIds('microsoft/Florence-2-large') // [{ name: 'microsoft/Florence-2-large' }] | |
| * parseGradioSpaceIds('microsoft/Florence-2-large,acme/foo') // [{ name: 'microsoft/Florence-2-large' }, { name: 'acme/foo' }] | |
| * parseGradioSpaceIds('none') // [] | |
| */ | |
| export function parseGradioSpaceIds(gradioParam: string): ParsedGradioSpace[] { | |
| const spaces: ParsedGradioSpace[] = []; | |
| const trimmed = gradioParam.trim(); | |
| // Treat special sentinel "none" as "disable gradio" | |
| if (trimmed.toLowerCase() === 'none') { | |
| return spaces; | |
| } | |
| const entries = gradioParam | |
| .split(',') | |
| .map((s) => s.trim()) | |
| .filter((s) => s.length > 0); | |
| for (const entry of entries) { | |
| // Skip explicit "none" entries within a list | |
| if (entry.toLowerCase() === 'none') { | |
| continue; | |
| } | |
| // Validate exactly one slash in the middle (username/space-name format) | |
| const slashCount = (entry.match(/\//g) || []).length; | |
| const slashIndex = entry.indexOf('/'); | |
| const isValidFormat = slashCount === 1 && slashIndex > 0 && slashIndex < entry.length - 1; | |
| if (!isValidFormat) { | |
| logger.warn( | |
| `Skipping invalid gradio entry "${entry}": must contain exactly one slash with content on both sides (format: username/space-name)` | |
| ); | |
| continue; | |
| } | |
| spaces.push({ name: entry }); | |
| logger.debug(`Parsed gradio space ID: ${entry}`); | |
| } | |
| return spaces; | |
| } | |
| /** | |
| * Fetches real subdomains from the HuggingFace API for the given space IDs. | |
| * Now uses the optimized discovery API with caching for better performance. | |
| * | |
| * @param spaceIds - Array of space IDs to fetch subdomains for | |
| * @param hfToken - Optional HuggingFace token for authentication | |
| * @param hubUrl - Optional hub URL for custom HuggingFace instances (not used with new API) | |
| * @returns Array of SpaceTool objects with real subdomains from the API | |
| * | |
| * @example | |
| * const spaces = [{ name: 'microsoft/Florence-2-large' }]; | |
| * const tools = await fetchGradioSubdomains(spaces, 'hf_token'); | |
| * // Returns: [{ _id: 'gradio_...', name: 'microsoft/Florence-2-large', subdomain: 'microsoft-florence-2-large', emoji: '🔧' }] | |
| */ | |
| async function fetchGradioSubdomains(spaceIds: ParsedGradioSpace[], hfToken?: string): Promise<SpaceTool[]> { | |
| if (spaceIds.length === 0) { | |
| return []; | |
| } | |
| const spaceNames = spaceIds.map((s) => s.name); | |
| // Use the new optimized discovery API with caching | |
| // Skip schemas since we only need metadata here | |
| const spaces = await getGradioSpaces(spaceNames, hfToken, { skipSchemas: true }); | |
| // Convert to SpaceTool format | |
| const spaceTools: SpaceTool[] = spaces.map((space) => ({ | |
| _id: space._id, | |
| name: space.name, | |
| subdomain: space.subdomain, | |
| emoji: space.emoji, | |
| })); | |
| logger.debug( | |
| { | |
| requested: spaceIds.length, | |
| successful: spaceTools.length, | |
| }, | |
| 'Fetched Gradio subdomains' | |
| ); | |
| return spaceTools; | |
| } | |
| /** | |
| * Parses gradio parameter and fetches real subdomains from HuggingFace API. | |
| * This is the main entry point that combines parsing and API fetching. | |
| * | |
| * @param gradioParam - Comma-separated list of space IDs | |
| * @param hfToken - Optional HuggingFace token for authentication | |
| * @param hubUrl - Optional hub URL for custom HuggingFace instances | |
| * @returns Array of SpaceTool objects with real subdomains | |
| * | |
| * @example | |
| * const tools = await parseAndFetchGradioEndpoints('microsoft/Florence-2-large', 'hf_token'); | |
| * // Returns array of SpaceTool objects with real subdomains from HuggingFace API | |
| */ | |
| /** @lintignore retained for potential future direct endpoint parsing API */ | |
| export async function parseAndFetchGradioEndpoints(gradioParam: string, hfToken?: string): Promise<SpaceTool[]> { | |
| const parsedSpaces = parseGradioSpaceIds(gradioParam); | |
| if (parsedSpaces.length === 0) { | |
| return []; | |
| } | |
| return fetchGradioSubdomains(parsedSpaces, hfToken); | |
| } | |
| interface FileListingSourceResolutionParams { | |
| username?: string; | |
| token?: string; | |
| gradioSpaceCount: number; | |
| builtInTools: string[]; | |
| dynamicSpaceToolId: string; | |
| } | |
| export type ResolvedFileListingSource = FileListingSource | null; | |
| function isFileListingRelevant(params: { | |
| gradioSpaceCount: number; | |
| builtInTools: string[]; | |
| dynamicSpaceToolId: string; | |
| }): boolean { | |
| return params.gradioSpaceCount > 0 || params.builtInTools.includes(params.dynamicSpaceToolId); | |
| } | |
| // async function bucketExists(bucketId: string, token: string): Promise<boolean> { | |
| // try { | |
| // return await repoExists({ | |
| // repo: { type: 'bucket', name: bucketId }, | |
| // accessToken: token, | |
| // }); | |
| // } catch (error) { | |
| // if (error instanceof HubApiError && error.statusCode === 403) { | |
| // return false; | |
| // } | |
| // throw error; | |
| // } | |
| // } | |
| export async function resolveMcpFileListingSource( | |
| params: FileListingSourceResolutionParams | |
| ): Promise<ResolvedFileListingSource> { | |
| const { username, token, gradioSpaceCount, builtInTools, dynamicSpaceToolId } = params; | |
| if (!username || !token || !isFileListingRelevant({ gradioSpaceCount, builtInTools, dynamicSpaceToolId })) { | |
| return null; | |
| } | |
| /** THIS IS NOW INTENDED TO BE DELIVERED VIA AN MCP APPS PROXY */ | |
| // const bucketId = `${username}/mcp`; | |
| // if (await bucketExists(bucketId, token)) { | |
| // return { kind: 'bucket', id: bucketId }; | |
| // } | |
| /** RETAIN FOR BACKWARDS COMPAT */ | |
| const datasetId = `${username}/gradio-files`; | |
| if ( | |
| await repoExists({ | |
| repo: { type: 'dataset', name: datasetId }, | |
| accessToken: token, | |
| }) | |
| ) { | |
| return { kind: 'dataset', id: datasetId }; | |
| } | |
| return null; | |
| } | |
Xet Storage Details
- Size:
- 9.47 kB
- Xet hash:
- e7d9c369899e793fd996e6075e6b84294cfa2ec1ab03a94f52577283a350c1a5
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.