refactor: simplify response language retry logic in iop-rule-loader

This commit is contained in:
toki 2026-07-04 11:07:29 +09:00
parent 25b171c295
commit 82ad78754e

View file

@ -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;
}