normalize-prompt
Normalize and restructure raw prompts for clarity and consistency. Adjust formatting, tighten language, and preflight prompts for optimal use in code or general contexts.
Instructions
Alias of cleaner. Keywords: normalize, restructure, clarify, tighten, format, preflight. Same input/output schema as 'cleaner'.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | Retouching mode; default 'general'. Use 'code' only for code-related prompts. | |
| prompt | Yes | Raw user prompt | |
| temperature | No | Sampling temperature (0-2); default 0.2 |
Implementation Reference
- src/tools.ts:92-105 (handler)Handler for the 'normalize-prompt' tool (alias of cleaner). Parses input using RetouchInput schema, calls retouchPrompt, validates output with RetouchOutput schema, logs, and returns JSON content.case "cleaner": case "sanitize-text": case "normalize-prompt": { const parsed = RetouchInput.parse(args); const result = await retouchPrompt(parsed); const safe = RetouchOutput.parse(result); logger.info("retouch.prompt", { elapsed_ms: Date.now() - start, input_len: parsed.prompt.length, preview: logger.preview(parsed.prompt), request_id: parsed.requestId, }); return jsonContent(safe); }
- src/tools.ts:56-74 (registration)Registration of the 'normalize-prompt' tool in listTools(), including name, description, and input schema (identical to cleaner's).{ name: "normalize-prompt", description: "Alias of cleaner. Keywords: normalize, restructure, clarify, tighten, format, preflight. Same input/output schema as 'cleaner'.", inputSchema: { type: "object", properties: { prompt: { type: "string", description: "Raw user prompt" }, mode: { type: "string", enum: ["code", "general"], description: "Retouching mode; default 'general'. Use 'code' only for code-related prompts.", }, temperature: { type: "number", description: "Sampling temperature (0-2); default 0.2" }, }, required: ["prompt"], }, },
- src/cleaner.ts:83-180 (helper)Core implementation of prompt normalization/retouching logic (retouchPrompt). Loads system prompt, calls LLM with retries for JSON output, extracts/parses JSON, applies redactions, and returns structured output. Called by normalize-prompt handler.export async function retouchPrompt(input: RetouchInputT): Promise<RetouchOutputT> { const start = Date.now(); const system = await loadCleanerSystemPrompt(); const baseTemperature = input.temperature ?? 0; const userBody = `MODE: ${input.mode || "general"}\nRAW_PROMPT:\n${input.prompt}`; const sys = system; class CleanerNonJsonError extends Error { constructor(message = "Cleaner returned non-JSON") { super(message); this.name = "CleanerNonJsonError"; } } // Retry loop for content-level non-JSON responses const maxAttempts = Math.max(1, 1 + (config.contentMaxRetries ?? 0)); let lastErr: Error | undefined; for (let attempt = 1; attempt <= maxAttempts; attempt++) { // Escalate on retries: enforce temperature 0 and stricter system instructions const strictSuffix = attempt > 1 ? "\n\nSTRICT OUTPUT MODE: Respond with EXACTLY ONE JSON object and nothing else. No prose. No code fences. No prefix/suffix." : ""; const sysAttempt = sys + strictSuffix; const tempAttempt = attempt > 1 ? 0 : baseTemperature; const response = await chatCompletions( { model: config.model, temperature: tempAttempt, max_tokens: 600, messages: [ { role: "system", content: sysAttempt }, { role: "user", content: userBody }, ], }, { requestId: input.requestId }, ); const content = response.choices?.[0]?.message?.content ?? ""; const initial = redactSecrets(content); const redactedText = initial.text; try { const obj = extractFirstJsonObject(redactedText); const parsed = RetouchOutput.safeParse(obj); if (!parsed.success) { throw new Error("shape-error"); } const { value, redactions } = ensureNoSecretsInObject(parsed.data); const totalRedactions = initial.count + redactions; const result: RetouchOutputT = { ...value, redactions: totalRedactions > 0 ? Array(totalRedactions).fill("[REDACTED]") : value.redactions, }; logger.info("retouch.prompt", { elapsed_ms: Date.now() - start, input_len: input.prompt.length, preview: logger.preview(input.prompt), request_id: input.requestId, attempts: attempt, outcome: "ok", }); return result; } catch (e: any) { lastErr = new CleanerNonJsonError("Cleaner returned non-JSON"); if (attempt < maxAttempts) { const base = config.backoffMs ?? 250; const jitter = config.backoffJitter ?? 0.2; const exp = Math.pow(2, attempt - 1); const rand = 1 + (Math.random() * 2 - 1) * jitter; // 1 +/- jitter const delay = Math.max(0, Math.floor(base * exp * rand)); logger.warn("retouch.retry", { request_id: input.requestId, attempt, delay_ms: delay, reason: "non-json", }); await new Promise((r) => setTimeout(r, delay)); continue; } } } logger.info("retouch.prompt", { elapsed_ms: Date.now() - start, input_len: input.prompt.length, preview: logger.preview(input.prompt), request_id: input.requestId, attempts: maxAttempts, outcome: "error", reason: "non-json", }); throw lastErr ?? new CleanerNonJsonError("Cleaner returned non-JSON"); }
- src/shapes.ts:5-18 (schema)Zod schemas for input (RetouchInput) and output (RetouchOutput) validation used by normalize-prompt and cleaner tools.export const RetouchInput = z.object({ prompt: z.string().min(1), mode: z.enum(["code", "general"]).optional(), temperature: z.number().min(0).max(2).optional(), requestId: z.string().uuid().optional(), }); export const RetouchOutput = z.object({ retouched: z.string().min(1), notes: z.array(z.string()).optional(), openQuestions: z.array(z.string()).optional(), risks: z.array(z.string()).optional(), redactions: z.array(z.literal("[REDACTED]")).optional(), });