Spaces:
Sleeping
Sleeping
Download src/utils.ts from Momoxiao111/P.help: direct link, hf CLI and curl.
- Browser
- Download file 5.81 kB
-
https://huggingface.co/spaces/Momoxiao111/P.help/resolve/main/src/utils.ts
- Command line
-
hf download hf://spaces/Momoxiao111/P.help/src/utils.ts
-
curl -L -o utils.ts https://huggingface.co/spaces/Momoxiao111/P.help/resolve/main/src/utils.ts
5.81 kB
| export function getApiHeaders(): Record<string, string> { | |
| const saved = localStorage.getItem("patent_api_settings"); | |
| if (!saved) return {}; | |
| try { | |
| const settings = JSON.parse(saved); | |
| if (settings) { | |
| return { | |
| "x-user-api-provider": settings.defaultProvider || settings.provider || "gemini", | |
| "x-user-gemini-api-key": settings.geminiApiKey || "", | |
| "x-user-gemini-model": settings.geminiModel || "", | |
| "x-user-deepseek-api-key": settings.deepseekApiKey || "", | |
| "x-user-deepseek-base-url": settings.deepseekBaseUrl || "", | |
| "x-user-deepseek-model": settings.deepseekModel || "", | |
| // Backward compatibility | |
| "x-user-api-key": settings.apiKey || "", | |
| "x-user-api-base-url": settings.baseUrl || "", | |
| "x-user-api-model": settings.model || "", | |
| }; | |
| } | |
| } catch (e) { | |
| console.error("Failed to load custom API settings", e); | |
| } | |
| return {}; | |
| } | |
| export async function customFetch(input: RequestInfo | URL, init?: RequestInit & { timeout?: number }): Promise<Response> { | |
| const url = typeof input === "string" ? input : (input instanceof URL ? input.href : (input && "url" in input ? input.url : "")); | |
| const timeoutMs = init?.timeout ?? (url.includes("/api/") ? 40000 : undefined); | |
| let controller: AbortController | null = null; | |
| let timeoutId: any = null; | |
| if (timeoutMs) { | |
| controller = new AbortController(); | |
| timeoutId = setTimeout(() => { | |
| if (controller) controller.abort(); | |
| }, timeoutMs); | |
| init = init || {}; | |
| init.signal = controller.signal; | |
| } | |
| if (url.includes("/api/")) { | |
| const customHeaders = getApiHeaders(); | |
| if (Object.keys(customHeaders).length > 0) { | |
| init = init || {}; | |
| const newHeaders = new Headers(init.headers || {}); | |
| Object.entries(customHeaders).forEach(([key, val]) => { | |
| if (val) { | |
| newHeaders.set(key, val); | |
| } | |
| }); | |
| init.headers = newHeaders; | |
| } | |
| } | |
| try { | |
| const response = await fetch(input, init); | |
| if (timeoutId) clearTimeout(timeoutId); | |
| return response; | |
| } catch (error: any) { | |
| if (timeoutId) clearTimeout(timeoutId); | |
| if (error.name === "AbortError") { | |
| throw new Error("请求超时:AI 接口响应时间过长。请稍后重试,或检查页面右上角「API设置」以配置更稳定的专属 API Key。"); | |
| } | |
| throw error; | |
| } | |
| } | |
| /** | |
| * Parses PDF files entirely in the client browser using pdfjs-dist via CDN. | |
| * This completely avoids sending large binary files through the reverse proxy, | |
| * bypassing any "413 Request Entity Too Large" errors. | |
| */ | |
| export async function parsePdfClient(file: File): Promise<string> { | |
| return new Promise((resolve, reject) => { | |
| const executeParsing = async () => { | |
| try { | |
| const pdfjsLib = (window as any).pdfjsLib; | |
| if (!pdfjsLib) { | |
| throw new Error("浏览器中未加载 pdfjsLib 组件"); | |
| } | |
| // Use matching worker CDN link | |
| pdfjsLib.GlobalWorkerOptions.workerSrc = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js"; | |
| const arrayBuffer = await file.arrayBuffer(); | |
| const loadingTask = pdfjsLib.getDocument({ data: arrayBuffer }); | |
| const pdfDoc = await loadingTask.promise; | |
| let textContent = ""; | |
| for (let i = 1; i <= pdfDoc.numPages; i++) { | |
| const page = await pdfDoc.getPage(i); | |
| const textObj = await page.getTextContent(); | |
| const pageText = textObj.items | |
| .map((item: any) => item.str) | |
| .join(" "); | |
| textContent += pageText + "\n"; | |
| } | |
| if (textContent.trim().length < 5) { | |
| throw new Error("未在 PDF 中提取到有效文本。该文件可能为扫描件、图片、或已加密。建议在页面右上角「API设置」中配置您个人的专属 Google Gemini Key 启用多模态 OCR 识别,或直接复制文本发送。"); | |
| } | |
| resolve(textContent); | |
| } catch (err: any) { | |
| reject(err); | |
| } | |
| }; | |
| if ((window as any).pdfjsLib) { | |
| executeParsing(); | |
| } else { | |
| const script = document.createElement("script"); | |
| script.src = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"; | |
| script.onload = () => { | |
| executeParsing(); | |
| }; | |
| script.onerror = () => { | |
| reject(new Error("加载在线 PDF 解析组件失败,请检查您的网络。")); | |
| }; | |
| document.head.appendChild(script); | |
| } | |
| }); | |
| } | |
| /** | |
| * Parses DOCX files entirely in the client browser using mammoth.js via CDN. | |
| */ | |
| export async function parseDocxClient(file: File): Promise<string> { | |
| return new Promise((resolve, reject) => { | |
| const executeParsing = async () => { | |
| try { | |
| const mammoth = (window as any).mammoth; | |
| if (!mammoth) { | |
| throw new Error("浏览器中未加载 mammoth 组件"); | |
| } | |
| const arrayBuffer = await file.arrayBuffer(); | |
| const result = await mammoth.extractRawText({ arrayBuffer }); | |
| if (result.value && result.value.trim().length > 0) { | |
| resolve(result.value); | |
| } else { | |
| throw new Error("Word 文档中未提取到有效文本"); | |
| } | |
| } catch (err: any) { | |
| reject(err); | |
| } | |
| }; | |
| if ((window as any).mammoth) { | |
| executeParsing(); | |
| } else { | |
| const script = document.createElement("script"); | |
| script.src = "https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.6.0/mammoth.browser.min.js"; | |
| script.onload = () => { | |
| executeParsing(); | |
| }; | |
| script.onerror = () => { | |
| reject(new Error("加载在线 Word 解析组件失败,请检查您的网络。")); | |
| }; | |
| document.head.appendChild(script); | |
| } | |
| }); | |
| } | |