|
const INDENT_STEP = " "; |
|
|
|
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); |
|
} |
|
|
|
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 "{}"; |
|
|
|
const properties = entries.map(([k, v]) => `${nextIndent}"${k}": ${stringifyPythonRecursive(v, nextIndent)}`); |
|
|
|
return "{\n" + properties.join(",\n") + "\n" + currentIndent + "}"; |
|
} |
|
return String(value); |
|
} |
|
|
|
function formatPythonValue(value: unknown, baseIndent: string): string { |
|
return stringifyPythonRecursive(value, baseIndent); |
|
} |
|
|
|
|
|
|
|
|
|
function insertPropertiesInternal( |
|
snippet: string, |
|
newProperties: Record<string, unknown>, |
|
blockStartMarker: RegExp, |
|
openChar: string, |
|
closeChar: string, |
|
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); |
|
|
|
|
|
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; |
|
} |
|
|
|
const contentBeforeBlock = snippet.substring(0, openCharIndex + 1); |
|
const currentContent = snippet.substring(openCharIndex + 1, closeCharIndex); |
|
const contentAfterBlock = snippet.substring(closeCharIndex); |
|
|
|
|
|
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) { |
|
|
|
const lineOfOpenCharStart = snippet.lastIndexOf("\n", openCharIndex) + 1; |
|
const lineOfOpenChar = snippet.substring(lineOfOpenCharStart, openCharIndex); |
|
const openCharLineIndentMatch = lineOfOpenChar.match(/^(\s*)/); |
|
indent = (openCharLineIndentMatch ? openCharLineIndentMatch[1] : "") + " "; |
|
} |
|
|
|
let newPropsStr = ""; |
|
Object.entries(newProperties).forEach(([key, value]) => { |
|
newPropsStr += propFormatter(key, valueFormatter(value, indent), indent); |
|
}); |
|
|
|
const trimmedOriginalContent = currentContent.trim(); |
|
let combinedContent; |
|
|
|
if (trimmedOriginalContent) { |
|
|
|
|
|
|
|
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 += ","; |
|
} |
|
|
|
|
|
const separator = |
|
trailingWhitespaceOfCurrentContent.endsWith("\n") || trailingWhitespaceOfCurrentContent.endsWith("\r") |
|
? "" |
|
: "\n"; |
|
combinedContent = processedTextualPart + trailingWhitespaceOfCurrentContent + separator + newPropsStr; |
|
} else { |
|
|
|
|
|
const openCharFollowedByNewline = |
|
snippet[openCharIndex + 1] === "\n" || |
|
(snippet[openCharIndex + 1] === "\r" && snippet[openCharIndex + 2] === "\n"); |
|
if (openCharFollowedByNewline) { |
|
combinedContent = newPropsStr; |
|
} else { |
|
combinedContent = "\n" + newPropsStr; |
|
} |
|
} |
|
|
|
|
|
combinedContent = combinedContent.replace(/,\s*$/, ""); |
|
|
|
|
|
if (combinedContent.trim()) { |
|
|
|
if (!combinedContent.endsWith("\n")) { |
|
combinedContent += "\n"; |
|
} |
|
|
|
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 { |
|
|
|
|
|
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) { |
|
|
|
combinedContent = baseIndent; |
|
} else { |
|
|
|
combinedContent = "\n" + baseIndent; |
|
} |
|
} |
|
|
|
return contentBeforeBlock + combinedContent + contentAfterBlock; |
|
} |
|
|
|
export function modifySnippet(snippet: string, newProperties: Record<string, unknown>): string { |
|
|
|
if (snippet.includes("client.chatCompletionStream")) { |
|
return insertPropertiesInternal( |
|
snippet, |
|
newProperties, |
|
/client\.chatCompletionStream\s*\(\s*/, |
|
"{", |
|
"}", |
|
(key, value, indent) => `${indent}${key}: ${value},\n`, |
|
formatJsJsonValue |
|
); |
|
} |
|
|
|
else if (snippet.includes("client.chatCompletion") && snippet.includes("InferenceClient")) { |
|
|
|
return insertPropertiesInternal( |
|
snippet, |
|
newProperties, |
|
/client\.chatCompletion\s*\(\s*/, |
|
"{", |
|
"}", |
|
(key, value, indent) => `${indent}${key}: ${value},\n`, |
|
formatJsJsonValue |
|
); |
|
} |
|
|
|
|
|
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*/, |
|
"{", |
|
"}", |
|
(key, value, indent) => `${indent}${key}: ${value},\n`, |
|
formatJsJsonValue |
|
); |
|
} |
|
|
|
else if (snippet.includes("client.chat.completions.create")) { |
|
return insertPropertiesInternal( |
|
snippet, |
|
newProperties, |
|
/client\.chat\.completions\.create\s*\(/, |
|
"(", |
|
")", |
|
(key, value, indent) => { |
|
const snakeKey = key.replace(/([A-Z])/g, "_$1").toLowerCase(); |
|
return `${indent}${snakeKey}=${value},\n`; |
|
}, |
|
formatPythonValue |
|
); |
|
} |
|
|
|
else if (snippet.includes("def query(payload):") && snippet.includes("query({")) { |
|
return insertPropertiesInternal( |
|
snippet, |
|
newProperties, |
|
/query\s*\(\s*/, |
|
"{", |
|
"}", |
|
|
|
(key, formattedValue, indent) => `${indent}"${key}": ${formattedValue},\n`, |
|
formatPythonValue |
|
); |
|
} |
|
|
|
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; |
|
} |
|
|