- SvelteKit project scaffolded with TypeScript - Type definitions for Style, StyleCategory, ConversionRequest, ConversionResponse, LLMConfig - Style definitions with 6 categories and 25 sub-styles - Intensity mapping (1-5) with prompt modifier placeholders - LLM client using OpenAI-compatible API (Ollama default) - POST /api/convert endpoint with input validation - Animated loading modal with per-letter animations - Main page UI with category/style selectors, intensity slider - Copy to clipboard, collapsible prompt display - Vitest tests for styles, LLM prompt building, and API validation - Environment configuration for LLM settings
62 lines
1.8 KiB
TypeScript
62 lines
1.8 KiB
TypeScript
import { json } from '@sveltejs/kit';
|
|
import type { RequestHandler } from './$types';
|
|
import { getStyleById, getIntensityConfig } from '$lib/styles';
|
|
import { convertText } from '$lib/llm';
|
|
import type { ConversionRequest, ConversionResponse } from '$lib/types';
|
|
|
|
export const POST: RequestHandler = async ({ request }) => {
|
|
let body: ConversionRequest;
|
|
try {
|
|
body = await request.json();
|
|
} catch {
|
|
return json({ error: 'Invalid JSON body' }, { status: 400 });
|
|
}
|
|
|
|
const { text, styleId, intensity } = body;
|
|
|
|
// Validate text
|
|
if (!text || typeof text !== 'string' || text.trim().length === 0) {
|
|
return json({ error: 'Text is required and must be non-empty' }, { status: 400 });
|
|
}
|
|
|
|
// Validate styleId
|
|
if (!styleId || typeof styleId !== 'string') {
|
|
return json({ error: 'styleId is required' }, { status: 400 });
|
|
}
|
|
|
|
const style = getStyleById(styleId);
|
|
if (!style) {
|
|
return json({ error: `Unknown style: ${styleId}` }, { status: 400 });
|
|
}
|
|
|
|
// Validate intensity
|
|
if (typeof intensity !== 'number' || !Number.isInteger(intensity) || intensity < 1 || intensity > 5) {
|
|
return json({ error: 'Intensity must be an integer between 1 and 5' }, { status: 400 });
|
|
}
|
|
|
|
const intensityConfig = getIntensityConfig(intensity);
|
|
if (!intensityConfig) {
|
|
return json({ error: 'Invalid intensity level' }, { status: 400 });
|
|
}
|
|
|
|
try {
|
|
const result = convertText(text, style.promptModifier, intensityConfig.instruction);
|
|
|
|
// Await the promise
|
|
const { converted, systemPrompt, userMessage } = await result;
|
|
|
|
const response: ConversionResponse = {
|
|
original: text,
|
|
converted,
|
|
styleId,
|
|
intensity,
|
|
systemPrompt,
|
|
userMessage
|
|
};
|
|
|
|
return json(response);
|
|
} catch (err) {
|
|
const message = err instanceof Error ? err.message : 'LLM call failed';
|
|
return json({ error: `Failed to convert text: ${message}` }, { status: 502 });
|
|
}
|
|
}; |