Add matcher mode selection to RuleManagement component

This commit is contained in:
Xin Wang
2026-07-17 21:33:03 +08:00
parent 62f0b5df51
commit 4caa9e9ee8

View File

@@ -28,6 +28,16 @@ import { generateEventId } from '../rule-id';
import LlmConfiguration from './LlmConfiguration';
const MODEL_EVENT_ID_STORAGE_KEY = 'asr-monitor:model-event-ids';
const MATCHER_MODE_STORAGE_KEY = 'asr-monitor:matcher-mode';
type MatcherMode = 'keyword' | 'llm';
function loadMatcherMode(): MatcherMode {
if (typeof window === 'undefined') return 'keyword';
return window.localStorage.getItem(MATCHER_MODE_STORAGE_KEY) === 'llm'
? 'llm'
: 'keyword';
}
function loadModelEventIds(): Record<string, string> {
if (typeof window === 'undefined') return {};
@@ -81,11 +91,23 @@ export default function RuleManagement({
const [formErrors, setFormErrors] = useState<Record<string, string>>({});
const [excelAction, setExcelAction] = useState<'template' | 'import' | 'export' | null>(null);
const [modelEventIds, setModelEventIds] = useState<Record<string, string>>(loadModelEventIds);
const [matcherMode, setMatcherMode] = useState<MatcherMode>(loadMatcherMode);
// Tag editor input ref
const tagInputRef = useRef<HTMLInputElement>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
const handleMatcherModeChange = (mode: MatcherMode) => {
setMatcherMode(mode);
window.localStorage.setItem(MATCHER_MODE_STORAGE_KEY, mode);
addToast(
mode === 'keyword'
? '已切换为关键词匹配,大模型配置已隐藏'
: '已切换为大模型事件匹配',
'info',
);
};
useEffect(() => {
setModelEventIds(current => {
const next = { ...current };
@@ -412,10 +434,57 @@ export default function RuleManagement({
</div>
</div>
{/* 2. LLM matcher configuration */}
<LlmConfiguration enabledRuleCount={activeRulesCount} addToast={addToast} />
{/* 2. Matcher selection */}
<section className="flex flex-col gap-3 rounded-md border border-slate-200 bg-white px-5 py-4 shadow-3xs sm:flex-row sm:items-center sm:justify-between">
<div>
<div className="flex items-center gap-2">
<h2 className="text-xs font-bold text-slate-900"></h2>
<span className={`rounded px-2 py-0.5 text-[9px] font-bold uppercase tracking-widest ${
matcherMode === 'keyword'
? 'bg-blue-50 text-blue-700'
: 'bg-violet-50 text-violet-700'
}`}>
{matcherMode === 'keyword' ? 'Keyword' : 'LLM'}
</span>
</div>
<p className="mt-1 text-[11px] leading-5 text-slate-400">
{matcherMode === 'keyword'
? '按照规则库中的监控关键词进行确定性匹配,不加载模型和提示词配置。'
: '将启用规则与通话上下文注入提示词,由大模型返回命中的短事件 ID。'}
</p>
</div>
{/* 3. Searching & Filter Options Bar */}
<label className="flex shrink-0 items-center gap-2">
<span className="text-[10px] font-bold uppercase tracking-widest text-slate-400">
</span>
<select
value={matcherMode}
onChange={(event) => handleMatcherModeChange(event.target.value as MatcherMode)}
className="min-w-44 rounded-md border border-slate-200 bg-white px-3 py-2 text-xs font-bold text-slate-800 outline-none transition focus:border-slate-900"
>
<option value="keyword"></option>
<option value="llm"></option>
</select>
</label>
</section>
{/* 3. LLM matcher configuration */}
<AnimatePresence initial={false}>
{matcherMode === 'llm' && (
<motion.div
key="llm-configuration"
initial={{ opacity: 0, y: -8 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -8 }}
transition={{ duration: 0.16, ease: 'easeOut' }}
>
<LlmConfiguration enabledRuleCount={activeRulesCount} addToast={addToast} />
</motion.div>
)}
</AnimatePresence>
{/* 4. Searching & Filter Options Bar */}
<div className="flex flex-col space-y-3 rounded-md border border-slate-200 bg-white p-4 shadow-3xs sm:flex-row sm:items-center sm:space-y-0 sm:space-x-4">
<div className="relative flex-1">
<Search className="absolute top-2.5 left-3 h-4 w-4 text-slate-400" />
@@ -439,7 +508,7 @@ export default function RuleManagement({
</div>
{/* 4. High Density Data Table */}
{/* 5. High Density Data Table */}
<div className="overflow-hidden rounded-md border border-slate-200 bg-white shadow-3xs">
<div className="overflow-x-auto">
<table className="w-full border-collapse text-left text-xs text-slate-505">
@@ -571,7 +640,7 @@ export default function RuleManagement({
</div>
</div>
{/* 5. Sliding Right Drawer for Creating/Editing Rules */}
{/* 6. Sliding Right Drawer for Creating/Editing Rules */}
<AnimatePresence>
{isDrawerOpen && (
<>
@@ -599,7 +668,11 @@ export default function RuleManagement({
{editingRule ? '编辑特征规则' : '新增特征规则'}
</h2>
<p className="text-[11px] text-slate-400 mt-0.5">
{editingRule ? `UUID: ${formId}` : 'UUID 负责系统关联,事件 ID 供大模型输出。'}
{editingRule
? `UUID: ${formId}`
: matcherMode === 'llm'
? 'UUID 负责系统关联,事件 ID 供大模型输出。'
: 'UUID 负责系统关联,事件 ID 作为接口侧业务标识。'}
</p>
</div>
<button
@@ -663,7 +736,9 @@ export default function RuleManagement({
</p>
) : (
<p className="mt-1 text-[10px] leading-4 text-slate-400">
ID UUID
{matcherMode === 'llm'
? '提示词会把它注入规则清单,大模型只需返回该短 ID后端再通过 UUID 还原事件名称。建议简短、稳定且创建后不修改。'
: '关键词命中后接口使用该短 ID 标识事件UUID 继续负责内部关联。建议简短、稳定且创建后不修改。'}
</p>
)}
</div>
@@ -744,9 +819,20 @@ export default function RuleManagement({
<div className="rounded border border-slate-200 bg-slate-50 p-3 text-xs text-slate-500 flex items-start space-x-2">
<Info className="h-4 w-4 text-slate-600 shrink-0 mt-0.5" />
<div className="space-y-1 font-sans">
<p className="font-bold text-slate-700"></p>
<p>1. ASR </p>
<p>2. </p>
<p className="font-bold text-slate-700">
{matcherMode === 'llm' ? '大模型规则说明' : '关键词匹配逻辑须知'}
</p>
{matcherMode === 'llm' ? (
<>
<p>1. </p>
<p>2. ID</p>
</>
) : (
<>
<p>1. ASR </p>
<p>2. </p>
</>
)}
</div>
</div>
</div>