Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
| const INDENT_STEP = " "; // 4 spaces for indentation | |
| function stringifyJsJsonRecursive(value: unknown, currentIndent: string): string { | |
| if (typeof value === "string") return `"${value}"`; | |
| if (value === null) return "null"; | |
| if (typeof value === "boolean" || typeof value === "number") return String(value); | |
| if (typeof value === "object" && value !== null) { | |
| const nextIndent = currentIndent + INDENT_STEP; | |
| if (Array.isArray(value)) { | |
| if (value.length === 0) return "[]"; | |
| const items = value.map(v => stringifyJsJsonRecursive(v, nextIndent)); | |
| return "[\n" + items.map(item => nextIndent + item).join(",\n") + "\n" + currentIndent + "]"; | |
| } | |
| const entries = Object.entries(value); | |
| if (entries.length === 0) return "{}"; | |
| const properties = entries.map(([k, v]) => `${nextIndent}"${k}": ${stringifyJsJsonRecursive(v, nextIndent)}`); | |
| return "{\n" + properties.join(",\n") + "\n" + currentIndent + "}"; | |
| } | |
| return String(value); // Fallback for other types | |
| } | |
| function formatJsJsonValue(value: unknown, baseIndent: string): string { | |
| return stringifyJsJsonRecursive(value, baseIndent); | |
| } | |
| function stringifyPythonRecursive(value: unknown, currentIndent: string): string { | |
| if (typeof value === "string") return `"${value}"`; | |
| if (typeof value === "boolean") return value ? "True" : "False"; | |
| if (value === null) return "None"; | |
| if (typeof value === "number") return String(value); | |
| if (typeof value === "object" && value !== null) { | |
| const nextIndent = currentIndent + INDENT_STEP; | |
| if (Array.isArray(value)) { | |
| if (value.length === 0) return "[]"; | |
| const items = value.map(v => stringifyPythonRecursive(v, nextIndent)); | |
| return "[\n" + items.map(item => nextIndent + item).join(",\n") + "\n" + currentIndent + "]"; | |
| } | |
| const entries = Object.entries(value); | |
| if (entries.length === 0) return "{}"; | |
| // In Python, dictionary keys are typically strings. | |
| const properties = entries.map(([k, v]) => `${nextIndent}"${k}": ${stringifyPythonRecursive(v, nextIndent)}`); | |
| return "{\n" + properties.join(",\n") + "\n" + currentIndent + "}"; | |
| } | |
| return String(value); // Fallback | |
| } | |
| function formatPythonValue(value: unknown, baseIndent: string): string { | |
| return stringifyPythonRecursive(value, baseIndent); | |
| } | |
| /** | |
| * Inserts new properties into a code snippet block (like a JS object or Python dict). | |
| */ | |
| function insertPropertiesInternal( | |
| snippet: string, | |
| newProperties: Record<string, unknown>, | |
| blockStartMarker: RegExp, // Regex to find the character *opening* the block (e.g., '{' or '(') | |
| openChar: string, // The opening character, e.g., '{' or '(' | |
| closeChar: string, // The closing character, e.g., '}' or ')' | |
| propFormatter: (key: string, formattedValue: string, indent: string) => string, | |
| valueFormatter: (value: unknown, baseIndent: string) => string | |
| ): string { | |
| if (Object.keys(newProperties).length === 0) { | |
| return snippet; | |
| } | |
| const match = snippet.match(blockStartMarker); | |
| // match.index is the start of the whole marker, e.g. "client.chatCompletionStream(" | |
| // We need the index of the openChar itself. | |
| if (!match || typeof match.index !== "number") { | |
| return snippet; | |
| } | |
| const openCharIndex = snippet.indexOf(openChar, match.index + match[0].length - 1); | |
| if (openCharIndex === -1) { | |
| return snippet; | |
| } | |
| let balance = 1; | |
| let closeCharIndex = -1; | |
| for (let i = openCharIndex + 1; i < snippet.length; i++) { | |
| if (snippet[i] === openChar) { | |
| balance++; | |
| } else if (snippet[i] === closeChar) { | |
| balance--; | |
| } | |
| if (balance === 0) { | |
| closeCharIndex = i; | |
| break; | |
| } | |
| } | |
| if (closeCharIndex === -1) { | |
| return snippet; // Malformed or not found | |
| } | |
| const contentBeforeBlock = snippet.substring(0, openCharIndex + 1); | |
| const currentContent = snippet.substring(openCharIndex + 1, closeCharIndex); | |
| const contentAfterBlock = snippet.substring(closeCharIndex); | |
| // Determine indentation | |
| let indent = ""; | |
| const lines = currentContent.split("\n"); | |
| if (lines.length > 1) { | |
| for (const line of lines) { | |
| const lineIndentMatch = line.match(/^(\s+)\S/); | |
| if (lineIndentMatch) { | |
| indent = lineIndentMatch[1] ?? ""; | |
| break; | |
| } | |
| } | |
| } | |
| if (!indent) { | |
| // If no indent found, or content is empty/single line, derive from openChar line | |
| const lineOfOpenCharStart = snippet.lastIndexOf("\n", openCharIndex) + 1; | |
| const lineOfOpenChar = snippet.substring(lineOfOpenCharStart, openCharIndex); | |
| const openCharLineIndentMatch = lineOfOpenChar.match(/^(\s*)/); | |
| indent = (openCharLineIndentMatch ? openCharLineIndentMatch[1] : "") + " "; // Default to 4 spaces more | |
| } | |
| let newPropsStr = ""; | |
| Object.entries(newProperties).forEach(([key, value]) => { | |
| newPropsStr += propFormatter(key, valueFormatter(value, indent), indent); | |
| }); | |
| const trimmedOriginalContent = currentContent.trim(); | |
| let combinedContent; | |
| if (trimmedOriginalContent) { | |
| // There was actual non-whitespace content. | |
| // Preserve original currentContent structure as much as possible. | |
| // Find the end of the textual part of currentContent (before any pure trailing whitespace). | |
| let endOfTextualPart = currentContent.length; | |
| while (endOfTextualPart > 0 && /\s/.test(currentContent.charAt(endOfTextualPart - 1))) { | |
| endOfTextualPart--; | |
| } | |
| const textualPartOfCurrentContent = currentContent.substring(0, endOfTextualPart); | |
| const trailingWhitespaceOfCurrentContent = currentContent.substring(endOfTextualPart); | |
| let processedTextualPart = textualPartOfCurrentContent; | |
| if (processedTextualPart && !processedTextualPart.endsWith(",")) { | |
| processedTextualPart += ","; | |
| } | |
| // Add a newline separator if the original trailing whitespace doesn't end with one. | |
| const separator = | |
| trailingWhitespaceOfCurrentContent.endsWith("\n") || trailingWhitespaceOfCurrentContent.endsWith("\r") | |
| ? "" | |
| : "\n"; | |
| combinedContent = processedTextualPart + trailingWhitespaceOfCurrentContent + separator + newPropsStr; | |
| } else { | |
| // currentContent was empty or contained only whitespace. | |
| // Check if the original block opening (e.g., '{' or '(') was immediately followed by a newline. | |
| const openCharFollowedByNewline = | |
| snippet[openCharIndex + 1] === "\n" || | |
| (snippet[openCharIndex + 1] === "\r" && snippet[openCharIndex + 2] === "\n"); | |
| if (openCharFollowedByNewline) { | |
| combinedContent = newPropsStr; // newPropsStr already starts with indent | |
| } else { | |
| combinedContent = "\n" + newPropsStr; // Add a newline first, then newPropsStr | |
| } | |
| } | |
| // Remove the trailing comma (and its trailing whitespace/newline) from the last property added. | |
| combinedContent = combinedContent.replace(/,\s*$/, ""); | |
| // Ensure the block content ends with a newline, and the closing character is on its own line, indented. | |
| if (combinedContent.trim()) { | |
| // If there's any actual content in the block | |
| if (!combinedContent.endsWith("\n")) { | |
| combinedContent += "\n"; | |
| } | |
| // Determine the base indent for the closing character's line | |
| const lineOfOpenCharStart = snippet.lastIndexOf("\n", openCharIndex) + 1; | |
| const openCharLine = snippet.substring(lineOfOpenCharStart, openCharIndex); | |
| const baseIndentMatch = openCharLine.match(/^(\s*)/); | |
| const baseIndent = baseIndentMatch ? baseIndentMatch[1] : ""; | |
| combinedContent += baseIndent; | |
| } else { | |
| // Block is effectively empty (e.g., was {} and no properties added, or newPropsStr was empty - though current logic prevents this if newProperties is not empty). | |
| // Format as an empty block with the closing char on a new, indented line. | |
| const lineOfOpenCharStart = snippet.lastIndexOf("\n", openCharIndex) + 1; | |
| const openCharLine = snippet.substring(lineOfOpenCharStart, openCharIndex); | |
| const baseIndentMatch = openCharLine.match(/^(\s*)/); | |
| const baseIndent = baseIndentMatch ? baseIndentMatch[1] : ""; | |
| const openCharFollowedByNewline = | |
| snippet[openCharIndex + 1] === "\n" || | |
| (snippet[openCharIndex + 1] === "\r" && snippet[openCharIndex + 2] === "\n"); | |
| if (openCharFollowedByNewline) { | |
| // Original was like {\n} | |
| combinedContent = baseIndent; // Just the indent for the closing char | |
| } else { | |
| // Original was like {} | |
| combinedContent = "\n" + baseIndent; // Newline, then indent for closing char | |
| } | |
| } | |
| return contentBeforeBlock + combinedContent + contentAfterBlock; | |
| } | |
| export function modifySnippet(snippet: string, newProperties: Record<string, unknown>): string { | |
| // JS: HuggingFace InferenceClient (streaming) | |
| if (snippet.includes("client.chatCompletionStream")) { | |
| return insertPropertiesInternal( | |
| snippet, | |
| newProperties, | |
| /client\.chatCompletionStream\s*\(\s*/, // Finds "client.chatCompletionStream(" | |
| "{", // The parameters are in an object literal | |
| "}", | |
| (key, value, indent) => `${indent}${key}: ${value},\n`, // JS object literal style | |
| formatJsJsonValue | |
| ); | |
| } | |
| // JS: HuggingFace InferenceClient (non-streaming) | |
| else if (snippet.includes("client.chatCompletion") && snippet.includes("InferenceClient")) { | |
| // Ensure it's not the OpenAI client by also checking for InferenceClient | |
| return insertPropertiesInternal( | |
| snippet, | |
| newProperties, | |
| /client\.chatCompletion\s*\(\s*/, // Finds "client.chatCompletion(" | |
| "{", // The parameters are in an object literal | |
| "}", | |
| (key, value, indent) => `${indent}${key}: ${value},\n`, // JS object literal style | |
| formatJsJsonValue | |
| ); | |
| } | |
| // JS: OpenAI Client | |
| // Check for client.chat.completions.create and a common JS import pattern | |
| else if ( | |
| snippet.includes("client.chat.completions.create") && | |
| (snippet.includes('import { OpenAI } from "openai"') || snippet.includes("new OpenAI(")) | |
| ) { | |
| return insertPropertiesInternal( | |
| snippet, | |
| newProperties, | |
| /client\.chat\.completions\.create\s*\(\s*/, // Finds "client.chat.completions.create(" | |
| "{", // The parameters are in an object literal | |
| "}", | |
| (key, value, indent) => `${indent}${key}: ${value},\n`, | |
| formatJsJsonValue | |
| ); | |
| } | |
| // Python: OpenAI or HuggingFace Client using client.chat.completions.create | |
| else if (snippet.includes("client.chat.completions.create")) { | |
| return insertPropertiesInternal( | |
| snippet, | |
| newProperties, | |
| /client\.chat\.completions\.create\s*\(/, // Finds "client.chat.completions.create(" | |
| "(", // The parameters are directly in the function call tuple | |
| ")", | |
| (key, value, indent) => { | |
| const snakeKey = key.replace(/([A-Z])/g, "_$1").toLowerCase(); | |
| return `${indent}${snakeKey}=${value},\n`; | |
| }, | |
| formatPythonValue | |
| ); | |
| } | |
| // Python: requests example with query({...}) | |
| else if (snippet.includes("def query(payload):") && snippet.includes("query({")) { | |
| return insertPropertiesInternal( | |
| snippet, | |
| newProperties, | |
| /query\s*\(\s*/, // Finds "query(" and expects a dictionary literal next | |
| "{", // The parameters are in a dictionary literal | |
| "}", | |
| // Python dict keys are strings, values formatted for Python | |
| (key, formattedValue, indent) => `${indent}"${key}": ${formattedValue},\n`, | |
| formatPythonValue // Use formatPythonValue for the values themselves | |
| ); | |
| } | |
| // Shell/curl (JSON content) | |
| else if (snippet.includes("curl") && snippet.includes("-d")) { | |
| return insertPropertiesInternal( | |
| snippet, | |
| newProperties, | |
| /-d\s*'(?:\\n)?\s*/, | |
| "{", | |
| "}", | |
| (key, value, indent) => { | |
| const snakeKey = key.replace(/([A-Z])/g, "_$1").toLowerCase(); | |
| return `${indent}"${snakeKey}": ${value},\n`; | |
| }, | |
| formatJsJsonValue | |
| ); | |
| } | |
| return snippet; | |
| } | |