Project Files
src / preprocessor.ts
// Proper prompt preprocessor using LM Studio SDK types.
// - Text/doc attachments are parsed server-side and injected into the user message text.
// - Images are not accepted by the UI for this generator (no image capability flag),
// but we support inline image references written by the user (markdown or [image: URL]).
import {
type PromptPreprocessorController,
type ChatMessage,
} from "@lmstudio/sdk";
export async function preprocess(ctl: PromptPreprocessorController, userMessage: ChatMessage) {
try {
const originalText = userMessage.getText();
// 1) Pull non-image files attached in this message and parse them to text
const files = userMessage.consumeFiles(ctl.client, file => file.type !== "image");
let injected = "";
for (const file of files) {
const { content } = await ctl.client.files.parseDocument(file, { signal: ctl.abortSignal });
const snippet = content.length > 200_000 ? content.slice(0, 200_000) + "\n...[truncated]" : content;
injected += `\n\n[Attachment: ${file.name}]\n${snippet}\n`;
}
// 1b) Pull image files and create wrappers for them (enables UI attachment support)
const imageFiles = userMessage.consumeFiles(ctl.client, file => file.type === "image");
const wrappers: string[] = [];
for (const file of imageFiles) {
// Get file path (FileHandle.getFilePath() is async)
const filePath = await file.getFilePath();
// Create file:// URI wrapper so reconcileAttachments() can find and process them
const fileUri = `file://${filePath}`;
wrappers.push(`[[LMSTUDIO_ATTACHMENT: ${JSON.stringify({ kind: "image", url: fileUri })}]]`);
}
// 2) Inline image references (markdown or [image: URL]) → kept as-is; generator will convert
// them to image_url content parts using wrappers we add below.
const inlineImageUrls = new Set<string>();
const mdImg = /!\[[^\]]*\]\((https?:[^\s)]+)\)/g;
let m: RegExpExecArray | null;
while ((m = mdImg.exec(originalText)) !== null) inlineImageUrls.add(m[1]);
const tagImg = /\[image:\s*(https?:[^\]\s]+)\s*\]/gi;
while ((m = tagImg.exec(originalText)) !== null) inlineImageUrls.add(m[1]);
for (const url of inlineImageUrls) {
// Add inline URL wrappers to the existing wrappers array
wrappers.push(`[[LMSTUDIO_ATTACHMENT: ${JSON.stringify({ kind: "image", url })}]]`);
}
if (injected.length === 0 && wrappers.length === 0) return userMessage;
const combined = `${originalText}${injected}${wrappers.length ? "\n\n" + wrappers.join("\n") : ""}`.trim();
userMessage.replaceText(combined);
return userMessage;
} catch (err) {
return userMessage;
}
}