Persist fastMode, drop refused user message on retry
Two follow-ups on #5715: 1) sanitizeInferenceParams stripped fastMode. fastMode is in PERSISTED_INFERENCE_PARAM_KEYS but the storage sanitizer only kept numeric fields plus systemPrompt and trustRemoteCode, so the new toggle was silently dropped on reload and on the /api/chat/settings round-trip. Save it the same way trustRemoteCode is saved. 2) Refusal recovery now also drops the triggering user turn. Returning null from toOpenAIMessage on the assistant side left the user prompt that caused the refusal in the outbound history, so the very next request would re-trigger the same classifier. Anthropic's refusal-handling guidance is explicit on this: remove the refused turn AND the user message that triggered it before the next call. Implemented via a pre-pass that pops the trailing user message when an assistant carries the refusal sentinel. Typecheck clean.
This commit is contained in:
parent
3a807a598d
commit
41531cdbe2
2 changed files with 31 additions and 1 deletions
|
|
@ -933,7 +933,29 @@ export function createOpenAIStreamAdapter(): ChatModelAdapter {
|
|||
),
|
||||
);
|
||||
|
||||
const outboundMessages = messages
|
||||
// Two-pass build so a refused assistant turn drops the user
|
||||
// message that triggered it as well. Anthropic's refusal-handling
|
||||
// guidance is explicit that leaving the offending user prompt in
|
||||
// context causes the next request to re-trigger the classifier;
|
||||
// returning null on just the assistant side was not enough.
|
||||
const survivingMessages: RunMessage[] = [];
|
||||
for (const message of messages) {
|
||||
if (
|
||||
message.role === "assistant" &&
|
||||
collectTextParts(message)
|
||||
.join("\n")
|
||||
.includes(ANTHROPIC_REFUSAL_SENTINEL)
|
||||
) {
|
||||
const last = survivingMessages.at(-1);
|
||||
if (last && last.role === "user") {
|
||||
survivingMessages.pop();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
survivingMessages.push(message);
|
||||
}
|
||||
|
||||
const outboundMessages = survivingMessages
|
||||
.map(toOpenAIMessage)
|
||||
.filter((message): message is NonNullable<typeof message> =>
|
||||
Boolean(message),
|
||||
|
|
|
|||
|
|
@ -140,6 +140,14 @@ function sanitizeInferenceParams(
|
|||
if (typeof value.trustRemoteCode === "boolean") {
|
||||
params.trustRemoteCode = value.trustRemoteCode;
|
||||
}
|
||||
// fastMode is in PERSISTED_INFERENCE_PARAM_KEYS but used to be
|
||||
// stripped here because the sanitizer only kept numeric fields plus
|
||||
// the two explicit string/bool fields above. Save the toggle the
|
||||
// same way trustRemoteCode is saved so the value survives reload
|
||||
// and the /api/chat/settings round-trip.
|
||||
if (typeof value.fastMode === "boolean") {
|
||||
params.fastMode = value.fastMode;
|
||||
}
|
||||
return hasKeys(params) ? params : undefined;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue