import type { ChatMessage } from '../../agent'; /** * LLM 엔진 호출 직전 메시지 배열 정규화 + 엔진별 variant 생성. * * - normalizeMessages — content 가 객체면 stringify (Ollama Vision 의 images * 필드는 보존). 엔진 API 가 항상 plain object array 만 받기 때문. * - buildEngineMessageVariants — LM Studio 한정으로 system role 이 가끔 무시되는 * 버그 우회용 fallback variant 도 함께 만들어 호출자가 차례로 시도하게 한다 * (native-system 실패 → flattened-system-fallback). Ollama 는 그냥 native 1개. * * 둘 다 stateless — agent.ts 의 private 메서드를 그대로 추출. */ export function normalizeMessages(messages: ChatMessage[]) { return messages.map((message) => { const normalizedContent = typeof message.content === 'string' ? message.content : JSON.stringify(message.content); const result: any = { role: message.role, content: normalizedContent, }; // Ollama Vision: images 필드 보존 if ((message as any).images) { result.images = (message as any).images; } return result; }); } export function buildEngineMessageVariants(messages: ChatMessage[], engine: 'lmstudio' | 'ollama') { const normalized = normalizeMessages(messages); if (engine !== 'lmstudio') { return [{ name: 'native', messages: normalized }]; } // LM Studio system-role bug 우회: system 메시지를 "[System Instruction - do not // answer this message]\n…" 로 감싸 user role 로 변환. 호출자는 native 가 실패하면 // 이 flattened variant 로 재시도한다. const flattened = normalized.map((message) => { if (message.role === 'system') { return { role: 'user' as const, content: `[System Instruction - do not answer this message]\n${message.content}`, }; } return message; }); return [ { name: 'native-system', messages: normalized }, { name: 'flattened-system-fallback', messages: flattened }, ]; }