Integrate LLM support into the application, enhancing the rule management system with a new matcher mode. Update the frontend to handle LLM configurations, including API key management and prompt definitions. Modify the backend to support LLM event matching alongside existing keyword matching, ensuring seamless integration and improved event detection capabilities.

This commit is contained in:
Xin Wang
2026-07-17 22:18:29 +08:00
parent 4caa9e9ee8
commit c23ef80d3a
42 changed files with 1248 additions and 454 deletions

View File

@@ -16,9 +16,16 @@ import {
restoreRuleVersion,
saveAndActivateRules,
} from './api';
import { Rule, RuleSet, RuleVersionSummary } from './types';
import { LlmConfig, Rule, RuleSet, RuleVersionSummary } from './types';
const TRANSFER_SET_ID = 'transfer';
const DEFAULT_LLM_CONFIG: LlmConfig = {
baseUrl: 'https://api.openai.com/v1',
model: 'gpt-4.1-mini',
timeoutMs: 3000,
maxOutputTokens: 64,
prompt: '',
};
function emptyTransferSet(): RuleSet {
return {
@@ -43,12 +50,32 @@ function replaceTransferRules(ruleSets: RuleSet[], rules: Rule[]): RuleSet[] {
);
}
function transferMatcher(ruleSets: RuleSet[]): 'ac-keyword' | 'llm' {
return ruleSets.find(ruleSet => ruleSet.id === TRANSFER_SET_ID)?.matcher === 'llm'
? 'llm'
: 'ac-keyword';
}
function replaceTransferMatcher(
ruleSets: RuleSet[],
matcher: 'ac-keyword' | 'llm',
): RuleSet[] {
if (!ruleSets.some(ruleSet => ruleSet.id === TRANSFER_SET_ID)) {
return [{ ...emptyTransferSet(), matcher }, ...ruleSets];
}
return ruleSets.map(ruleSet =>
ruleSet.id === TRANSFER_SET_ID ? { ...ruleSet, matcher } : ruleSet
);
}
export default function App() {
// Global Active Tab: 'rules' | 'sandbox'
const [activeTab, setActiveTab] = useState<'rules' | 'sandbox'>('rules');
const [ruleSets, setRuleSets] = useState<RuleSet[]>([]);
const [publishedRuleSets, setPublishedRuleSets] = useState<RuleSet[]>([]);
const [llmConfig, setLlmConfig] = useState<LlmConfig>(DEFAULT_LLM_CONFIG);
const [publishedLlmConfig, setPublishedLlmConfig] = useState<LlmConfig>(DEFAULT_LLM_CONFIG);
const [publishedVersion, setPublishedVersion] = useState(0);
const [loadingRules, setLoadingRules] = useState(true);
const [savingRules, setSavingRules] = useState(false);
@@ -82,6 +109,8 @@ export default function App() {
if (!active) return;
setRuleSets(data.ruleSets);
setPublishedRuleSets(data.ruleSets);
setLlmConfig(data.llmConfig);
setPublishedLlmConfig(data.llmConfig);
setPublishedVersion(data.version);
})
.catch(error => {
@@ -101,17 +130,24 @@ export default function App() {
setRuleSets(current => replaceTransferRules(current, updatedRules));
};
const handleUpdateMatcher = (matcher: 'ac-keyword' | 'llm') => {
setRuleSets(current => replaceTransferMatcher(current, matcher));
};
const isDirty = useMemo(() => {
return JSON.stringify(ruleSets) !== JSON.stringify(publishedRuleSets);
}, [ruleSets, publishedRuleSets]);
return JSON.stringify(ruleSets) !== JSON.stringify(publishedRuleSets)
|| JSON.stringify(llmConfig) !== JSON.stringify(publishedLlmConfig);
}, [llmConfig, publishedLlmConfig, publishedRuleSets, ruleSets]);
const handlePublish = async () => {
if (!isDirty || savingRules || restoringVersion !== null) return;
setSavingRules(true);
try {
const result = await saveAndActivateRules(publishedVersion, ruleSets);
const result = await saveAndActivateRules(publishedVersion, llmConfig, ruleSets);
setRuleSets(result.ruleSets);
setPublishedRuleSets(result.ruleSets);
setLlmConfig(result.llmConfig);
setPublishedLlmConfig(result.llmConfig);
setPublishedVersion(result.version);
addToast(`保存成功,监控规则 V${result.version} 已发布生效`, 'success');
result.warnings.forEach(warning => addToast(warning, 'warning'));
@@ -155,6 +191,8 @@ export default function App() {
const result = await restoreRuleVersion(sourceVersion, publishedVersion);
setRuleSets(result.ruleSets);
setPublishedRuleSets(result.ruleSets);
setLlmConfig(result.llmConfig);
setPublishedLlmConfig(result.llmConfig);
setPublishedVersion(result.version);
addToast(`已基于 V${sourceVersion} 创建并生效 V${result.version}`, 'success');
result.warnings.forEach(warning => addToast(warning, 'warning'));
@@ -205,6 +243,10 @@ export default function App() {
<RuleManagement
rules={rules}
setRules={handleUpdateRules}
matcher={transferMatcher(ruleSets)}
setMatcher={handleUpdateMatcher}
llmConfig={llmConfig}
setLlmConfig={setLlmConfig}
onSavePublish={handlePublish}
isDirty={isDirty}
loading={loadingRules}
@@ -223,6 +265,7 @@ export default function App() {
>
<SandboxSimulation
rules={activeRules}
matcher={transferMatcher(publishedRuleSets)}
addToast={addToast}
/>
</motion.div>