Skip to content

Commit 7d33cc9

Browse files
authored
Merge pull request #280 from huangye123/worktree-prompt-filter-rules-edit-support
feat: 支持 Prompt 检查额外规则编辑
2 parents 83002e8 + 9d06ed5 commit 7d33cc9

8 files changed

Lines changed: 328 additions & 44 deletions

File tree

admin/handler.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ func (h *Handler) RegisterRoutes(r *gin.Engine) {
356356
api.GET("/prompt-filter/logs/match", h.MatchPromptFilterLog)
357357
api.DELETE("/prompt-filter/logs", h.ClearPromptFilterLogs)
358358
api.POST("/prompt-filter/test", h.TestPromptFilter)
359+
api.POST("/prompt-filter/rules/test", h.TestPromptFilterRulePattern)
359360
api.GET("/prompt-filter/rules", h.GetPromptFilterRules)
360361
api.GET("/models", h.ListModels)
361362
api.POST("/models/sync", h.SyncModels)

admin/prompt_filter.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package admin
33
import (
44
"context"
55
"net/http"
6+
"regexp"
67
"strconv"
78
"strings"
89
"time"
@@ -29,6 +30,16 @@ type promptFilterTestResponse struct {
2930
Verdict promptfilter.Verdict `json:"verdict"`
3031
}
3132

33+
type promptFilterRulePatternTestRequest struct {
34+
Pattern string `json:"pattern"`
35+
Text string `json:"text"`
36+
}
37+
38+
type promptFilterRulePatternTestResponse struct {
39+
Matched bool `json:"matched"`
40+
Error string `json:"error,omitempty"`
41+
}
42+
3243
type promptFilterRuleItem struct {
3344
Name string `json:"name"`
3445
Pattern string `json:"pattern"`
@@ -205,6 +216,37 @@ func (h *Handler) TestPromptFilter(c *gin.Context) {
205216
c.JSON(http.StatusOK, promptFilterTestResponse{Verdict: verdict})
206217
}
207218

219+
func (h *Handler) TestPromptFilterRulePattern(c *gin.Context) {
220+
var req promptFilterRulePatternTestRequest
221+
if err := c.ShouldBindJSON(&req); err != nil {
222+
writeError(c, http.StatusBadRequest, "请求体无效")
223+
return
224+
}
225+
trimmedPattern := strings.TrimSpace(req.Pattern)
226+
if trimmedPattern == "" {
227+
writeError(c, http.StatusBadRequest, "pattern 不能为空")
228+
return
229+
}
230+
if req.Text == "" {
231+
writeError(c, http.StatusBadRequest, "text 不能为空")
232+
return
233+
}
234+
if len([]rune(req.Pattern)) > 5000 {
235+
writeError(c, http.StatusBadRequest, "pattern 不能超过 5000 个字符")
236+
return
237+
}
238+
if len([]rune(req.Text)) > 20000 {
239+
writeError(c, http.StatusBadRequest, "text 不能超过 20000 个字符")
240+
return
241+
}
242+
re, err := regexp.Compile(req.Pattern)
243+
if err != nil {
244+
c.JSON(http.StatusOK, promptFilterRulePatternTestResponse{Matched: false, Error: err.Error()})
245+
return
246+
}
247+
c.JSON(http.StatusOK, promptFilterRulePatternTestResponse{Matched: re.MatchString(req.Text)})
248+
}
249+
208250
func (h *Handler) GetPromptFilterRules(c *gin.Context) {
209251
cfg := h.store.GetPromptFilterConfig()
210252
disabled := map[string]bool{}

frontend/src/api.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import type {
3232
OpsOverviewResponse,
3333
PromptFilterLog,
3434
PromptFilterLogsResponse,
35+
PromptFilterRulePatternTestResponse,
3536
PromptFilterRulesResponse,
3637
PromptFilterTestResponse,
3738
PublicAPIKeyUsageResponse,
@@ -511,6 +512,8 @@ export const api = {
511512
},
512513
testPromptFilter: (data: { text: string; endpoint?: string; model?: string }) =>
513514
request<PromptFilterTestResponse>('/prompt-filter/test', { method: 'POST', body: JSON.stringify(data) }),
515+
testPromptFilterRulePattern: (data: { pattern: string; text: string }) =>
516+
request<PromptFilterRulePatternTestResponse>('/prompt-filter/rules/test', { method: 'POST', body: JSON.stringify(data) }),
514517
getPromptFilterRules: () =>
515518
request<PromptFilterRulesResponse>('/prompt-filter/rules'),
516519
getModels: () => request<ModelsResponse>('/models'),

frontend/src/components/ui/select.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ interface SelectProps {
1717
disabled?: boolean
1818
className?: string
1919
compact?: boolean
20+
triggerClassName?: string
2021
}
2122

2223
interface DropdownPosition {
@@ -39,6 +40,7 @@ export function Select({
3940
disabled = false,
4041
className,
4142
compact = false,
43+
triggerClassName,
4244
}: SelectProps) {
4345
const [open, setOpen] = useState(false)
4446
const [position, setPosition] = useState<DropdownPosition | null>(null)
@@ -128,7 +130,8 @@ export function Select({
128130
'hover:border-primary/30 hover:bg-accent/50',
129131
'focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/20',
130132
'disabled:cursor-not-allowed disabled:opacity-60',
131-
open && 'border-primary/35 ring-[3px] ring-primary/10'
133+
open && 'border-primary/35 ring-[3px] ring-primary/10',
134+
triggerClassName
132135
)}
133136
onClick={() => {
134137
if (!disabled) {

frontend/src/locales/en.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,6 +1462,19 @@
14621462
"customRulesTitle": "Extra Rules",
14631463
"customRulesDesc": "Extra rules are stored in system settings and can be toggled or removed independently.",
14641464
"addCustomRule": "Add Rule",
1465+
"addCustomRuleDesc": "Fill in the extra rule name, regex, weight, category, and strict flag. Saving adds it to system settings immediately.",
1466+
"editCustomRule": "Edit Rule",
1467+
"editCustomRuleDesc": "Update the extra rule name, regex, weight, category, and strict flag. Saving applies the change to system settings immediately.",
1468+
"rulePatternTesterTitle": "Regex Hit Test",
1469+
"rulePatternTesterDesc": "Enter a prompt and test whether the current rule matches it using backend Go regex.",
1470+
"rulePatternTest": "Test Hit",
1471+
"rulePatternTesting": "Testing...",
1472+
"ruleTestTextPlaceholder": "Enter a prompt to test against this regex",
1473+
"ruleTestMatched": "Matched: the current regex matches this prompt.",
1474+
"ruleTestNotMatched": "Not matched: the current regex does not match this prompt.",
1475+
"rulePatternInvalid": "Invalid regex",
1476+
"rulePatternRequired": "Enter a regex first",
1477+
"ruleTestTextRequired": "Enter test prompt text",
14651478
"noCustomRules": "No extra rules yet",
14661479
"filterByCategory": "Filter by Category",
14671480
"selectAll": "Select All",

frontend/src/locales/zh.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,6 +1462,19 @@
14621462
"customRulesTitle": "额外规则",
14631463
"customRulesDesc": "添加的额外规则会保存到系统设置中,可独立启停或删除。",
14641464
"addCustomRule": "添加规则",
1465+
"addCustomRuleDesc": "填写额外规则的名称、正则、权重、分类和强规则标记。保存后会立即加入系统设置。",
1466+
"editCustomRule": "编辑规则",
1467+
"editCustomRuleDesc": "修改额外规则的名称、正则、权重、分类和强规则标记。保存后会立即更新系统设置。",
1468+
"rulePatternTesterTitle": "正则命中测试",
1469+
"rulePatternTesterDesc": "输入一段提示词,使用后端 Go 正则验证当前规则是否会命中。",
1470+
"rulePatternTest": "测试命中",
1471+
"rulePatternTesting": "测试中...",
1472+
"ruleTestTextPlaceholder": "输入用于测试命中的提示词",
1473+
"ruleTestMatched": "已命中:当前正则会匹配这段提示词。",
1474+
"ruleTestNotMatched": "未命中:当前正则不会匹配这段提示词。",
1475+
"rulePatternInvalid": "正则无效",
1476+
"rulePatternRequired": "请先填写正则",
1477+
"ruleTestTextRequired": "请输入测试提示词",
14651478
"noCustomRules": "暂无额外规则",
14661479
"filterByCategory": "按分类筛选",
14671480
"selectAll": "全选",

0 commit comments

Comments
 (0)