Merge pull request #10 from Hao19911125/main

fallback修复
This commit is contained in:
youzini
2026-04-06 10:53:49 +08:00
committed by GitHub
6 changed files with 166 additions and 40 deletions

View File

@@ -189,13 +189,10 @@ export const DEFAULT_TASK_PROFILE_TEMPLATES = {
"character": true
},
"stages": {
"finalPrompt": true,
"input.userMessage": false,
"input.recentMessages": false,
"input.candidateText": false,
"input.finalPrompt": false,
"rawResponse": false,
"beforeParse": false,
"output.rawResponse": false,
"output.beforeParse": false,
"input": true,
@@ -396,13 +393,10 @@ export const DEFAULT_TASK_PROFILE_TEMPLATES = {
"character": true
},
"stages": {
"finalPrompt": true,
"input.userMessage": false,
"input.recentMessages": false,
"input.candidateText": false,
"input.finalPrompt": false,
"rawResponse": false,
"beforeParse": false,
"output.rawResponse": false,
"output.beforeParse": false,
"input": true,
@@ -579,13 +573,10 @@ export const DEFAULT_TASK_PROFILE_TEMPLATES = {
"character": true
},
"stages": {
"finalPrompt": true,
"input.userMessage": false,
"input.recentMessages": false,
"input.candidateText": false,
"input.finalPrompt": false,
"rawResponse": false,
"beforeParse": false,
"output.rawResponse": false,
"output.beforeParse": false,
"input": true,
@@ -774,13 +765,10 @@ export const DEFAULT_TASK_PROFILE_TEMPLATES = {
"character": true
},
"stages": {
"finalPrompt": true,
"input.userMessage": false,
"input.recentMessages": false,
"input.candidateText": false,
"input.finalPrompt": false,
"rawResponse": false,
"beforeParse": false,
"output.rawResponse": false,
"output.beforeParse": false,
"input": true,
@@ -981,13 +969,10 @@ export const DEFAULT_TASK_PROFILE_TEMPLATES = {
"character": true
},
"stages": {
"finalPrompt": true,
"input.userMessage": false,
"input.recentMessages": false,
"input.candidateText": false,
"input.finalPrompt": false,
"rawResponse": false,
"beforeParse": false,
"output.rawResponse": false,
"output.beforeParse": false,
"input": true,
@@ -1200,13 +1185,10 @@ export const DEFAULT_TASK_PROFILE_TEMPLATES = {
"character": true
},
"stages": {
"finalPrompt": true,
"input.userMessage": false,
"input.recentMessages": false,
"input.candidateText": false,
"input.finalPrompt": false,
"rawResponse": false,
"beforeParse": false,
"output.rawResponse": false,
"output.beforeParse": false,
"input": true,

View File

@@ -411,6 +411,33 @@ export async function extractMemories({
systemPrompt,
);
// 诊断:追踪 promptPayload
{
const pm = Array.isArray(promptPayload.promptMessages) ? promptPayload.promptMessages : [];
const pmUser = pm.filter((m) => m?.role === "user");
const am = Array.isArray(promptPayload.additionalMessages) ? promptPayload.additionalMessages : [];
console.log(
`[ST-BME][prompt-diag] resolveTaskPromptPayload: ` +
`promptMessages=${pm.length} (user=${pmUser.length}), ` +
`additionalMessages=${am.length}, ` +
`userPrompt length=${String(promptPayload.userPrompt || "").length}, ` +
`systemPrompt length=${String(promptPayload.systemPrompt || "").length}, ` +
`llmSystemPrompt length=${String(llmSystemPrompt || "").length}`,
);
if (pmUser.length > 0) {
for (const m of pmUser) {
console.log(
`[ST-BME][prompt-diag] user msg: contentLen=${String(m.content || "").length}, ` +
`blockName="${m.blockName || ""}", preview="${String(m.content || "").slice(0, 60)}..."`,
);
}
} else {
console.warn(
`[ST-BME][prompt-diag] NO user messages in promptMessages! Fallback userPrompt will be used.`,
);
}
}
// 调用 LLM
const result = await callLLMForJSON({
systemPrompt: llmSystemPrompt,

33
llm.js
View File

@@ -1583,10 +1583,43 @@ export async function callLLMForJSON({
additionalMessages,
promptMessages,
);
{
const asmUser = assembledMessages.filter((m) => m?.role === "user");
console.log(
`[ST-BME][prompt-diag] buildJsonAttemptMessages: ` +
`total=${assembledMessages.length}, user=${asmUser.length}, ` +
`roles=[${assembledMessages.map((m) => m?.role).join(",")}]`,
);
for (const m of asmUser) {
console.log(
`[ST-BME][prompt-diag] assembled user: len=${String(m.content || "").length}, ` +
`preview="${String(m.content || "").slice(0, 80)}..."`,
);
}
}
const requestCleaning = applyTaskFinalInputRegex(
taskType,
assembledMessages,
);
{
const rcMsgs = Array.isArray(requestCleaning.messages) ? requestCleaning.messages : [];
const rcUser = rcMsgs.filter((m) => m?.role === "user");
const dbg = requestCleaning.debug || {};
console.log(
`[ST-BME][prompt-diag] applyTaskFinalInputRegex: ` +
`total=${rcMsgs.length}, user=${rcUser.length}, ` +
`changed=${dbg.changed}, applied=${dbg.applied}, ` +
`roles=[${rcMsgs.map((m) => m?.role).join(",")}]`,
);
if (rcUser.length === 0 && assembledMessages.filter((m) => m?.role === "user").length > 0) {
console.warn(
`[ST-BME][prompt-diag] *** USER MESSAGES LOST during applyTaskFinalInputRegex! ***`,
);
for (const rule of dbg.appliedRules || []) {
console.warn(`[ST-BME][prompt-diag] applied rule: ${JSON.stringify(rule)}`);
}
}
}
const promptExecutionSnapshot = attachRequestCleaningToPromptExecution(
promptExecutionSummary,
requestCleaning.debug,

View File

@@ -1098,6 +1098,12 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
let assistantRoleBlockCount = 0;
let systemRoleBlockCount = 0;
console.log(
`[ST-BME][prompt-diag] buildTaskPrompt: taskType=${taskType}, ` +
`total blocks=${blocks.length}, ` +
`block roles=[${blocks.map((b) => `${b.name}(${b.role},${b.enabled !== false ? "on" : "off"})`).join(", ")}]`,
);
for (const block of blocks) {
if (!block || block.enabled === false) continue;
@@ -1118,6 +1124,15 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
content = interpolateVariables(block.content || "", resolvedContext);
}
if (role === "user") {
console.log(
`[ST-BME][prompt-diag] user block "${block.name || block.id}": ` +
`type=${block.type}, contentLen=${String(content || "").length}, ` +
`rawContentLen=${String(block.content || "").length}, ` +
`blockedContentsCount=${worldInfoRuntimeBlockedContents.length}`,
);
}
const sanitizedBlockContent = sanitizeTaskPromptText(
settings,
taskType,
@@ -1137,7 +1152,19 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
}
content = sanitizedBlockContent.text;
if (!String(content || "").trim()) continue;
if (!String(content || "").trim()) {
if (role === "user" && String(block.content || "").trim()) {
console.warn(
`[ST-BME] buildTaskPrompt: user block "${block.name || block.id}" ` +
`content emptied during sanitization! ` +
`original length=${String(block.content || "").length}, ` +
`dropped=${sanitizedBlockContent.dropped}, ` +
`reasons=[${(sanitizedBlockContent.reasons || []).join(", ")}], ` +
`blockedHitCount=${sanitizedBlockContent.blockedHitCount}`,
);
}
continue;
}
const mode = normalizeInjectionMode(block.injectionMode);
renderedBlocks.push({
@@ -1193,6 +1220,13 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
}
}
console.log(
`[ST-BME][prompt-diag] buildTaskPrompt done: ` +
`executionMessages=${executionMessages.length}, ` +
`userBlocks=${userRoleBlockCount}, systemBlocks=${systemRoleBlockCount}, ` +
`customMessages=${customMessages.length}`,
);
for (const message of worldInfoResolution.additionalMessages || []) {
const executionMessage = createExecutionMessage(
message.role,
@@ -1361,6 +1395,39 @@ export function buildTaskLlmPayload(promptBuild = null, fallbackUserPrompt = "")
const hasUserMessage = executionMessages.some(
(message) => message.role === "user",
);
if (!hasUserMessage && rawExecutionMessages.length > 0) {
const userBlocksBefore = (promptBuild?.executionMessages || []).filter(
(m) => m?.role === "user",
);
const userBlocksAfterRaw = rawExecutionMessages.filter(
(m) => m?.role === "user",
);
const userBlocksAfterSanitize = executionMessages.filter(
(m) => m?.role === "user",
);
console.warn(
`[ST-BME] buildTaskLlmPayload fallback triggered: ` +
`user blocks in promptBuild=${userBlocksBefore.length}, ` +
`after recreate=${userBlocksAfterRaw.length}, ` +
`after sanitize=${userBlocksAfterSanitize.length}, ` +
`blockedContents count=${blockedContents.length}, ` +
`total executionMessages=${executionMessages.length}`,
);
if (userBlocksBefore.length > 0) {
for (const block of userBlocksBefore) {
console.warn(
`[ST-BME] user block "${block.blockName || block.blockId}": ` +
`content length=${String(block.content || "").length}, ` +
`content preview="${String(block.content || "").slice(0, 80)}..."`,
);
}
}
if (blockedContents.length > 0) {
console.warn(
`[ST-BME] blockedContents lengths: [${blockedContents.map((c) => String(c || "").length).join(", ")}]`,
);
}
}
const sanitizedFallbackUserPrompt = sanitizeTaskPromptText(
{},
promptBuild?.debug?.taskType || "",

View File

@@ -637,15 +637,13 @@ export function normalizeTaskRegexStages(stages = {}) {
for (const [legacyKey, canonicalKey] of Object.entries(
TASK_REGEX_STAGE_ALIAS_MAP,
)) {
if (Object.prototype.hasOwnProperty.call(source, legacyKey)) {
// Older exports may carry both legacy and canonical keys at the same
// time. When that happens, keep the legacy intent instead of letting a
// newer placeholder default silently flip stage timing.
normalized[canonicalKey] = Boolean(source[legacyKey]);
if (Object.prototype.hasOwnProperty.call(source, canonicalKey)) {
// Respect an explicitly stored canonical key when both forms are
// present. Legacy aliases should only backfill older exports.
continue;
}
if (Object.prototype.hasOwnProperty.call(source, canonicalKey)) {
normalized[canonicalKey] = Boolean(source[canonicalKey]);
if (Object.prototype.hasOwnProperty.call(source, legacyKey)) {
normalized[canonicalKey] = Boolean(source[legacyKey]);
}
}
@@ -893,13 +891,10 @@ function createFallbackDefaultTaskProfile(taskType) {
character: true,
},
stages: normalizeTaskRegexStages({
finalPrompt: true,
"input.userMessage": false,
"input.recentMessages": false,
"input.candidateText": false,
"input.finalPrompt": false,
rawResponse: false,
beforeParse: false,
"output.rawResponse": false,
"output.beforeParse": false,
}),
@@ -954,10 +949,10 @@ export function createDefaultTaskProfile(taskType) {
...fallback.regex.sources,
...(template?.regex?.sources || {}),
},
stages: normalizeTaskRegexStages({
...fallback.regex.stages,
...(template?.regex?.stages || {}),
}),
stages: {
...normalizeTaskRegexStages(fallback.regex.stages || {}),
...normalizeTaskRegexStages(template?.regex?.stages || {}),
},
localRules: Array.isArray(template?.regex?.localRules)
? template.regex.localRules.map((rule, index) =>
normalizeRegexLocalRule(rule, taskType, index),
@@ -1148,10 +1143,10 @@ export function normalizeTaskProfile(taskType, profile = {}, settings = {}) {
...base.regex.sources,
...(profile?.regex?.sources || {}),
},
stages: normalizeTaskRegexStages({
...base.regex.stages,
...(profile?.regex?.stages || {}),
}),
stages: {
...normalizeTaskRegexStages(base.regex.stages || {}),
...normalizeTaskRegexStages(profile?.regex?.stages || {}),
},
localRules: Array.isArray(profile?.regex?.localRules)
? profile.regex.localRules.map((rule, index) =>
normalizeRegexLocalRule(rule, taskType, index),

View File

@@ -156,6 +156,7 @@ try {
const {
createDefaultTaskProfiles,
isTaskRegexStageEnabled,
normalizeTaskProfile,
normalizeTaskRegexStages,
} = await import("../prompt-profiles.js");
@@ -170,7 +171,7 @@ try {
"output.rawResponse": false,
"output.beforeParse": false,
});
assert.equal(normalizedLegacyStages["input.finalPrompt"], true);
assert.equal(normalizedLegacyStages["input.finalPrompt"], false);
assert.equal(normalizedLegacyStages["input.userMessage"], false);
assert.equal(normalizedLegacyStages["input.recentMessages"], false);
assert.equal(normalizedLegacyStages["input.candidateText"], false);
@@ -178,7 +179,7 @@ try {
assert.equal(normalizedLegacyStages["output.beforeParse"], false);
assert.equal(
isTaskRegexStageEnabled(normalizedLegacyStages, "input.finalPrompt"),
true,
false,
);
assert.equal(
isTaskRegexStageEnabled(normalizedLegacyStages, "input.userMessage"),
@@ -198,7 +199,7 @@ try {
defaultProfiles.extract?.profiles?.[0]?.regex?.stages || {};
assert.equal(
isTaskRegexStageEnabled(defaultExtractStages, "input.finalPrompt"),
true,
false,
);
assert.equal(
isTaskRegexStageEnabled(defaultExtractStages, "input.userMessage"),
@@ -213,6 +214,27 @@ try {
false,
);
const normalizedLegacyOnlyProfile = normalizeTaskProfile(
"extract",
{
id: "legacy-only-profile",
name: "legacy only",
regex: {
stages: {
finalPrompt: true,
},
},
},
{},
);
assert.equal(
isTaskRegexStageEnabled(
normalizedLegacyOnlyProfile.regex?.stages || {},
"input.finalPrompt",
),
true,
);
globalThis.getTavernRegexes = () => {
throw new Error("legacy global getter should not be used in regex tests");
};