refactor(plugin): extract before_model_resolve and message_sent hooks
This commit is contained in:
168
plugin/hooks/before-model-resolve.ts
Normal file
168
plugin/hooks/before-model-resolve.ts
Normal file
@@ -0,0 +1,168 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
||||
import { evaluateDecision, type Decision, type DirigentConfig } from "../rules.js";
|
||||
import { checkTurn } from "../turn-manager.js";
|
||||
import { deriveDecisionInputFromPrompt } from "../decision-input.js";
|
||||
|
||||
type DebugConfig = {
|
||||
enableDebugLogs?: boolean;
|
||||
debugLogChannelIds?: string[];
|
||||
};
|
||||
|
||||
type DecisionRecord = {
|
||||
decision: Decision;
|
||||
createdAt: number;
|
||||
needsRestore?: boolean;
|
||||
};
|
||||
|
||||
type BeforeModelResolveDeps = {
|
||||
api: OpenClawPluginApi;
|
||||
baseConfig: DirigentConfig;
|
||||
sessionDecision: Map<string, DecisionRecord>;
|
||||
sessionAllowed: Map<string, boolean>;
|
||||
sessionChannelId: Map<string, string>;
|
||||
sessionAccountId: Map<string, string>;
|
||||
policyState: { channelPolicies: Record<string, unknown> };
|
||||
DECISION_TTL_MS: number;
|
||||
ensurePolicyStateLoaded: (api: OpenClawPluginApi, config: DirigentConfig) => void;
|
||||
getLivePluginConfig: (api: OpenClawPluginApi, fallback: DirigentConfig) => DirigentConfig;
|
||||
resolveAccountId: (api: OpenClawPluginApi, agentId: string) => string | undefined;
|
||||
pruneDecisionMap: () => void;
|
||||
shouldDebugLog: (config: DirigentConfig & DebugConfig, channelId?: string) => boolean;
|
||||
ensureTurnOrder: (api: OpenClawPluginApi, channelId: string) => void;
|
||||
};
|
||||
|
||||
export function registerBeforeModelResolveHook(deps: BeforeModelResolveDeps): void {
|
||||
const {
|
||||
api,
|
||||
baseConfig,
|
||||
sessionDecision,
|
||||
sessionAllowed,
|
||||
sessionChannelId,
|
||||
sessionAccountId,
|
||||
policyState,
|
||||
DECISION_TTL_MS,
|
||||
ensurePolicyStateLoaded,
|
||||
getLivePluginConfig,
|
||||
resolveAccountId,
|
||||
pruneDecisionMap,
|
||||
shouldDebugLog,
|
||||
ensureTurnOrder,
|
||||
} = deps;
|
||||
|
||||
api.on("before_model_resolve", async (event, ctx) => {
|
||||
const key = ctx.sessionKey;
|
||||
if (!key) return;
|
||||
|
||||
const live = getLivePluginConfig(api, baseConfig as DirigentConfig) as DirigentConfig & DebugConfig;
|
||||
ensurePolicyStateLoaded(api, live);
|
||||
|
||||
const prompt = ((event as Record<string, unknown>).prompt as string) || "";
|
||||
|
||||
if (live.enableDebugLogs) {
|
||||
api.logger.info(
|
||||
`dirigent: DEBUG_BEFORE_MODEL_RESOLVE ctx=${JSON.stringify({ sessionKey: ctx.sessionKey, messageProvider: ctx.messageProvider, agentId: ctx.agentId })} ` +
|
||||
`promptPreview=${prompt.slice(0, 300)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const derived = deriveDecisionInputFromPrompt({
|
||||
prompt,
|
||||
messageProvider: ctx.messageProvider,
|
||||
sessionKey: key,
|
||||
ctx: ctx as Record<string, unknown>,
|
||||
event: event as Record<string, unknown>,
|
||||
});
|
||||
|
||||
const hasConvMarker = prompt.includes("Conversation info (untrusted metadata):");
|
||||
if (live.discordOnly !== false && (!hasConvMarker || derived.channel !== "discord")) return;
|
||||
|
||||
if (derived.channelId) {
|
||||
sessionChannelId.set(key, derived.channelId);
|
||||
}
|
||||
const resolvedAccountId = resolveAccountId(api, ctx.agentId || "");
|
||||
if (resolvedAccountId) {
|
||||
sessionAccountId.set(key, resolvedAccountId);
|
||||
}
|
||||
|
||||
let rec = sessionDecision.get(key);
|
||||
if (!rec || Date.now() - rec.createdAt > DECISION_TTL_MS) {
|
||||
if (rec) sessionDecision.delete(key);
|
||||
const decision = evaluateDecision({
|
||||
config: live,
|
||||
channel: derived.channel,
|
||||
channelId: derived.channelId,
|
||||
channelPolicies: policyState.channelPolicies as Record<string, any>,
|
||||
senderId: derived.senderId,
|
||||
content: derived.content,
|
||||
});
|
||||
rec = { decision, createdAt: Date.now() };
|
||||
sessionDecision.set(key, rec);
|
||||
pruneDecisionMap();
|
||||
if (shouldDebugLog(live, derived.channelId)) {
|
||||
api.logger.info(
|
||||
`dirigent: debug before_model_resolve recompute session=${key} ` +
|
||||
`channel=${derived.channel} channelId=${derived.channelId ?? ""} senderId=${derived.senderId ?? ""} ` +
|
||||
`convSenderId=${String((derived.conv as Record<string, unknown>).sender_id ?? "")} ` +
|
||||
`convSender=${String((derived.conv as Record<string, unknown>).sender ?? "")} ` +
|
||||
`convChannelId=${String((derived.conv as Record<string, unknown>).channel_id ?? "")} ` +
|
||||
`decision=${decision.reason} shouldNoReply=${decision.shouldUseNoReply} shouldInject=${decision.shouldInjectEndMarkerPrompt}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (derived.channelId) {
|
||||
ensureTurnOrder(api, derived.channelId);
|
||||
const accountId = resolveAccountId(api, ctx.agentId || "");
|
||||
if (accountId) {
|
||||
const turnCheck = checkTurn(derived.channelId, accountId);
|
||||
if (!turnCheck.allowed) {
|
||||
sessionAllowed.set(key, false);
|
||||
api.logger.info(
|
||||
`dirigent: turn gate blocked session=${key} accountId=${accountId} currentSpeaker=${turnCheck.currentSpeaker} reason=${turnCheck.reason}`,
|
||||
);
|
||||
return {
|
||||
providerOverride: live.noReplyProvider,
|
||||
modelOverride: live.noReplyModel,
|
||||
};
|
||||
}
|
||||
sessionAllowed.set(key, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!rec.decision.shouldUseNoReply) {
|
||||
if (rec.needsRestore) {
|
||||
sessionDecision.delete(key);
|
||||
return {
|
||||
providerOverride: undefined,
|
||||
modelOverride: undefined,
|
||||
};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
rec.needsRestore = true;
|
||||
sessionDecision.set(key, rec);
|
||||
|
||||
if (live.enableDebugLogs) {
|
||||
const hasConvMarker2 = prompt.includes("Conversation info (untrusted metadata):");
|
||||
api.logger.info(
|
||||
`dirigent: DEBUG_NO_REPLY_TRIGGER session=${key} ` +
|
||||
`channel=${derived.channel} channelId=${derived.channelId ?? ""} senderId=${derived.senderId ?? ""} ` +
|
||||
`convSenderId=${String((derived.conv as Record<string, unknown>).sender_id ?? "")} ` +
|
||||
`convSender=${String((derived.conv as Record<string, unknown>).sender ?? "")} ` +
|
||||
`decision=${rec.decision.reason} ` +
|
||||
`shouldNoReply=${rec.decision.shouldUseNoReply} shouldInject=${rec.decision.shouldInjectEndMarkerPrompt} ` +
|
||||
`hasConvMarker=${hasConvMarker2} promptLen=${prompt.length}`,
|
||||
);
|
||||
}
|
||||
|
||||
api.logger.info(
|
||||
`dirigent: override model for session=${key}, provider=${live.noReplyProvider}, model=${live.noReplyModel}, reason=${rec.decision.reason}`,
|
||||
);
|
||||
|
||||
return {
|
||||
providerOverride: live.noReplyProvider,
|
||||
modelOverride: live.noReplyModel,
|
||||
};
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user