feat(plugin): inject 🔚 ending instruction for bypass/end-symbol discord turns
This commit is contained in:
@@ -9,6 +9,10 @@ WhisperGate evaluates in strict order:
|
|||||||
3. message ending symbol check
|
3. message ending symbol check
|
||||||
4. fallback to no-reply model override
|
4. fallback to no-reply model override
|
||||||
|
|
||||||
|
Additional prompt behavior:
|
||||||
|
- when decision is `bypass_sender` or `end_symbol:*`, plugin prepends:
|
||||||
|
- `你的这次发言必须以🔚作为结尾。`
|
||||||
|
|
||||||
## Why before_model_resolve
|
## Why before_model_resolve
|
||||||
|
|
||||||
- deterministic
|
- deterministic
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
|
|
||||||
- `message:received` caches a per-session decision from deterministic rules.
|
- `message:received` caches a per-session decision from deterministic rules.
|
||||||
- `before_model_resolve` applies `providerOverride + modelOverride` when decision says no-reply.
|
- `before_model_resolve` applies `providerOverride + modelOverride` when decision says no-reply.
|
||||||
|
- `before_prompt_build` prepends instruction `你的这次发言必须以🔚作为结尾。` when decision is:
|
||||||
|
- `bypass_sender`
|
||||||
|
- `end_symbol:*`
|
||||||
|
|
||||||
## Rules (in order)
|
## Rules (in order)
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ type DecisionRecord = {
|
|||||||
const sessionDecision = new Map<string, DecisionRecord>();
|
const sessionDecision = new Map<string, DecisionRecord>();
|
||||||
const MAX_SESSION_DECISIONS = 2000;
|
const MAX_SESSION_DECISIONS = 2000;
|
||||||
const DECISION_TTL_MS = 5 * 60 * 1000;
|
const DECISION_TTL_MS = 5 * 60 * 1000;
|
||||||
|
const END_MARKER_INSTRUCTION = "你的这次发言必须以🔚作为结尾。";
|
||||||
|
|
||||||
function normalizeChannel(ctx: Record<string, unknown>): string {
|
function normalizeChannel(ctx: Record<string, unknown>): string {
|
||||||
const candidates = [ctx.commandSource, ctx.messageProvider, ctx.channelId, ctx.channel];
|
const candidates = [ctx.commandSource, ctx.messageProvider, ctx.channelId, ctx.channel];
|
||||||
@@ -48,6 +49,10 @@ function pruneDecisionMap(now = Date.now()) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shouldInjectEndMarker(reason: string): boolean {
|
||||||
|
return reason === "bypass_sender" || reason.startsWith("end_symbol:");
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
id: "whispergate",
|
id: "whispergate",
|
||||||
name: "WhisperGate",
|
name: "WhisperGate",
|
||||||
@@ -87,10 +92,10 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// one-shot decision per inbound turn
|
|
||||||
sessionDecision.delete(key);
|
|
||||||
if (!rec.decision.shouldUseNoReply) return;
|
if (!rec.decision.shouldUseNoReply) return;
|
||||||
|
|
||||||
|
// no-reply path is consumed here
|
||||||
|
sessionDecision.delete(key);
|
||||||
api.logger.info(
|
api.logger.info(
|
||||||
`whispergate: override model for session=${key}, provider=${config.noReplyProvider}, model=${config.noReplyModel}, reason=${rec.decision.reason}`,
|
`whispergate: override model for session=${key}, provider=${config.noReplyProvider}, model=${config.noReplyModel}, reason=${rec.decision.reason}`,
|
||||||
);
|
);
|
||||||
@@ -100,5 +105,27 @@ export default {
|
|||||||
modelOverride: config.noReplyModel,
|
modelOverride: config.noReplyModel,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
api.on("before_prompt_build", async (_event, ctx) => {
|
||||||
|
const key = ctx.sessionKey;
|
||||||
|
if (!key) return;
|
||||||
|
const rec = sessionDecision.get(key);
|
||||||
|
if (!rec) return;
|
||||||
|
|
||||||
|
if (Date.now() - rec.createdAt > DECISION_TTL_MS) {
|
||||||
|
sessionDecision.delete(key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// consume non-no-reply paths here to avoid stale carry-over
|
||||||
|
sessionDecision.delete(key);
|
||||||
|
|
||||||
|
if (!shouldInjectEndMarker(rec.decision.reason)) return;
|
||||||
|
|
||||||
|
api.logger.info(`whispergate: prepend end marker instruction for session=${key}, reason=${rec.decision.reason}`);
|
||||||
|
return {
|
||||||
|
prependContext: END_MARKER_INSTRUCTION,
|
||||||
|
};
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user