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; | |
} | |