diff --git a/.pi/iop-rule-loader.ts b/.pi/iop-rule-loader.ts index 363f39a..baccbbb 100644 --- a/.pi/iop-rule-loader.ts +++ b/.pi/iop-rule-loader.ts @@ -19,7 +19,15 @@ type ProviderMessage = { }; type ProviderPayload = { + model?: unknown; messages?: ProviderMessage[]; + temperature?: unknown; + top_p?: unknown; + seed?: unknown; + think?: unknown; + reasoning_effort?: unknown; + thinking_token_budget?: unknown; + include_reasoning?: unknown; [key: string]: unknown; }; @@ -43,6 +51,7 @@ const responseLanguageUserPrefix = `[IOP 응답 언어: 이번 요청에서 사 const responseLanguageUserSuffix = `[응답 전 확인: 자연어 설명이 한국어인지 확인하고, 한국어가 아니면 한국어로 다시 작성한다.]`; +const defaultThinkingTokenBudget = 8192; const nonKoreanCJKPattern = /[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff]/; const commonRequiredSessionRules: RuleSpec[] = [ @@ -208,8 +217,29 @@ function contentAsText(content: unknown): string { return ""; } -function messageText(message: PiMessage): string { - return contentAsText(message.content); +function visibleMessageText(message: PiMessage): string { + const content = message.content; + if (typeof content === "string") { + return content; + } + if (!Array.isArray(content)) { + return ""; + } + return content + .map((item) => { + if (typeof item === "string") { + return item; + } + if (!item || typeof item !== "object") { + return ""; + } + const type = String((item as { type?: unknown }).type ?? ""); + if (type !== "text" || !("text" in item)) { + return ""; + } + return String((item as { text?: unknown }).text ?? ""); + }) + .join("\n"); } function messageHasToolCall(message: PiMessage): boolean { @@ -237,10 +267,49 @@ function replaceMessageText(message: PiMessage, text: string): PiMessage { }; } +function applyIopOrnithDefaults(payload: ProviderPayload): boolean { + if (payload.model !== "ornith:35b") { + return false; + } + + let changed = false; + if (payload.temperature === undefined) { + payload.temperature = 0; + changed = true; + } + if (payload.top_p === undefined) { + payload.top_p = 1; + changed = true; + } + if (payload.seed === undefined) { + payload.seed = 1; + changed = true; + } + if (payload.think === undefined && payload.reasoning_effort !== "none") { + payload.think = true; + changed = true; + } + if ( + payload.thinking_token_budget === undefined && + payload.think !== false && + payload.reasoning_effort !== "none" + ) { + payload.thinking_token_budget = defaultThinkingTokenBudget; + changed = true; + } + if (payload.include_reasoning === undefined) { + payload.include_reasoning = false; + changed = true; + } + + return changed; +} + function injectResponseLanguagePolicy(payload: ProviderPayload): ProviderPayload | undefined { const messages = payload.messages; if (!Array.isArray(messages)) { - return undefined; + const changed = applyIopOrnithDefaults(payload); + return changed ? payload : undefined; } const systemIndex = messages.findIndex((message) => message.role === "system" || message.role === "developer"); @@ -254,7 +323,8 @@ function injectResponseLanguagePolicy(payload: ProviderPayload): ProviderPayload const needsUserPolicy = userIndex >= 0 && !contentHasUserPolicy(messages[userIndex].content); if (!needsSystemPolicy && !needsUserPolicy) { - return undefined; + const changed = applyIopOrnithDefaults(payload); + return changed ? payload : undefined; } const nextMessages = [...messages]; @@ -287,15 +357,7 @@ function injectResponseLanguagePolicy(payload: ProviderPayload): ProviderPayload ...payload, messages: nextMessages, }; - if (nextPayload.temperature === undefined) { - nextPayload.temperature = 0; - } - if (nextPayload.top_p === undefined) { - nextPayload.top_p = 1; - } - if (nextPayload.seed === undefined) { - nextPayload.seed = 1; - } + applyIopOrnithDefaults(nextPayload); return nextPayload; } @@ -342,7 +404,7 @@ ${buildRulesPrompt(projectRoot, loadedRules)}`, return; } - const text = messageText(message); + const text = visibleMessageText(message); if (!text || !nonKoreanCJKPattern.test(text)) { return; }