Merge pull request #151 from xixu-me/hf-api-support-4915912814412665047

Support Hugging Face API operations
This commit is contained in:
xixu-me authored and GitHub committed 2026-01-06 22:54:41 +08:00
commit 708d0ecc83
3 files changed
+87 -13

No files matched your search

+18 -9
View File
@@ -11,6 +11,7 @@
import { CONFIG, createConfig } from './config/index.js';
import { SORTED_PLATFORMS, transformPath } from './config/platforms.js';
import { configureAIHeaders, isAIInferenceRequest } from './protocols/ai.js';
import { configureHuggingFaceHeaders, isHuggingFaceAPIRequest } from './protocols/huggingface.js';
import {
fetchToken,
getScopeFromUrl,
@@ -126,7 +127,10 @@ async function handleRequest(request, env, ctx) {
// Check if this is an AI inference request
const isAI = isAIInferenceRequest(request, url);
// Check cache first (skip cache for Git, Git LFS, Docker, and AI inference operations)
// Check if this is a Hugging Face API request
const isHF = isHuggingFaceAPIRequest(request, url);
// Check cache first (skip cache for Git, Git LFS, Docker, AI inference, and HF API operations)
/** @type {Cache | null} */
// @ts-ignore - Cloudflare Workers cache API
const cache =
@@ -134,7 +138,7 @@ async function handleRequest(request, env, ctx) {
? /** @type {any} */ (caches).default // eslint-disable-line jsdoc/reject-any-type
: null;
if (cache && !isGit && !isGitLFS && !isDocker && !isAI) {
if (cache && !isGit && !isGitLFS && !isDocker && !isAI && !isHF) {
try {
// For Range requests, try cache match first
const cacheKey = new Request(targetUrl, {
@@ -177,10 +181,10 @@ async function handleRequest(request, env, ctx) {
redirect: 'follow'
};
// Add body for POST/PUT/PATCH requests (Git/Docker/AI inference operations)
// Add body for POST/PUT/PATCH/DELETE requests (Git/Docker/AI/HF operations)
if (
['POST', 'PUT', 'PATCH'].includes(request.method) &&
(isGit || isGitLFS || isDocker || isAI)
['POST', 'PUT', 'PATCH', 'DELETE'].includes(request.method) &&
(isGit || isGitLFS || isDocker || isAI || isHF)
) {
fetchOptions.body = request.body;
}
@@ -188,9 +192,9 @@ async function handleRequest(request, env, ctx) {
// Cast headers to Headers for proper typing
const requestHeaders = /** @type {Headers} */ (fetchOptions.headers);
// Set appropriate headers for Git/Docker/AI vs regular requests
if (isGit || isGitLFS || isDocker || isAI) {
// For Git/Docker/AI operations, copy all headers from the original request
// Set appropriate headers for Git/Docker/AI/HF vs regular requests
if (isGit || isGitLFS || isDocker || isAI || isHF) {
// For Git/Docker/AI/HF operations, copy all headers from the original request
// This ensures protocol compliance
for (const [key, value] of request.headers.entries()) {
// Skip headers that might cause issues with proxying
@@ -209,6 +213,10 @@ async function handleRequest(request, env, ctx) {
if (isAI) {
configureAIHeaders(requestHeaders, request);
}
if (isHF) {
configureHuggingFaceHeaders(requestHeaders, request);
}
} else {
// Regular file download headers
Object.assign(fetchOptions, {
@@ -617,8 +625,9 @@ async function handleRequest(request, env, ctx) {
const isDocker = isDockerRequest(request, new URL(request.url));
const isAI = isAIInferenceRequest(request, new URL(request.url));
const isGitLFS = isGitLFSRequest(request, new URL(request.url));
const isHF = isHuggingFaceAPIRequest(request, new URL(request.url));
return isGit || isGitLFS || isDocker || isAI
return isGit || isGitLFS || isDocker || isAI || isHF
? response
: addPerformanceHeaders(response, monitor);
}
+63
View File
@@ -0,0 +1,63 @@
/**
* Xget - High-performance acceleration engine for developer resources
* Copyright (C) 2025 Xi Xu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* Hugging Face protocol handler for Xget
*/
/**
* Detects if a request is a Hugging Face API operation.
*
* Identifies Hugging Face API requests by checking for:
* - Hugging Face platform prefix (/hf/)
* - API path segment (/api/)
*
* @param {Request} request - The incoming request object
* @param {URL} url - Parsed URL object
* @returns {boolean} True if this is a Hugging Face API operation
*/
export function isHuggingFaceAPIRequest(request, url) {
// Check for Hugging Face API endpoints
if (url.pathname.startsWith('/hf/api/')) {
return true;
}
// Also check for token endpoint which is often used
if (url.pathname.startsWith('/hf/token')) {
return true;
}
return false;
}
/**
* Configures headers for Hugging Face API requests.
*
* @param {Headers} headers - The headers object to modify
* @param {Request} request - The original request
*/
export function configureHuggingFaceHeaders(headers, request) {
// Pass through Authorization header if present
if (request.headers.has('Authorization')) {
headers.set('Authorization', request.headers.get('Authorization'));
}
if (request.method === 'POST' && !headers.has('Content-Type')) {
headers.set('Content-Type', 'application/json');
}
}
+6 -4
View File
@@ -25,6 +25,7 @@ import { CONFIG } from '../config/index.js';
// Imported protocol checks
import { isAIInferenceRequest } from '../protocols/ai.js';
import { isGitLFSRequest, isGitRequest } from '../protocols/git.js';
import { isHuggingFaceAPIRequest } from '../protocols/huggingface.js';
/**
* Detects if a request is a container registry operation (Docker/OCI).
@@ -72,7 +73,7 @@ export function isDockerRequest(request, url) {
}
// Re-export for standard usage
export { isAIInferenceRequest, isGitLFSRequest, isGitRequest };
export { isAIInferenceRequest, isGitLFSRequest, isGitRequest, isHuggingFaceAPIRequest };
/**
* Validates incoming requests against security rules.
@@ -90,15 +91,16 @@ export { isAIInferenceRequest, isGitLFSRequest, isGitRequest };
* @returns {{valid: boolean, error?: string, status?: number}} Validation result object
*/
export function validateRequest(request, url, config = CONFIG) {
// Allow POST method for Git, Git LFS, Docker, and AI inference operations
// Allow POST method for Git, Git LFS, Docker, AI inference, and HF API operations
const isGit = isGitRequest(request, url);
const isGitLFS = isGitLFSRequest(request, url);
const isDocker = isDockerRequest(request, url);
const isAI = isAIInferenceRequest(request, url);
const isHF = isHuggingFaceAPIRequest(request, url);
const allowedMethods =
isGit || isGitLFS || isDocker || isAI
? ['GET', 'HEAD', 'POST', 'PUT', 'PATCH']
isGit || isGitLFS || isDocker || isAI || isHF
? ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE']
: config.SECURITY.ALLOWED_METHODS;
if (!allowedMethods.includes(request.method)) {