Skip to content

Commit ef0178c

Browse files
committed
feat: implement decision-based routing with plugin architecture
Signed-off-by: bitliu <[email protected]>
1 parent 6ee7e04 commit ef0178c

17 files changed

+124
-56
lines changed

src/semantic-router/pkg/k8s/converter.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package k8s
1818

1919
import (
20+
"encoding/json"
2021
"fmt"
2122

2223
"github.com/vllm-project/semantic-router/src/semantic-router/pkg/apis/vllm.ai/v1alpha1"
@@ -176,6 +177,10 @@ func (c *CRDConverter) convertDecision(decision v1alpha1.Decision) (config.Decis
176177
for _, plugin := range decision.Plugins {
177178
var pluginConfig interface{}
178179
if plugin.Configuration != nil && plugin.Configuration.Raw != nil {
180+
// Validate plugin configuration format
181+
if err := validatePluginConfiguration(plugin.Type, plugin.Configuration.Raw); err != nil {
182+
return config.Decision{}, fmt.Errorf("invalid configuration for plugin %s in decision %s: %w", plugin.Type, decision.Name, err)
183+
}
179184
// Store the raw bytes from RawExtension
180185
// The Get*Config methods will unmarshal this to the appropriate type
181186
pluginConfig = plugin.Configuration.Raw
@@ -188,3 +193,92 @@ func (c *CRDConverter) convertDecision(decision v1alpha1.Decision) (config.Decis
188193

189194
return configDecision, nil
190195
}
196+
197+
// validatePluginConfiguration validates that plugin configuration matches the expected schema
198+
func validatePluginConfiguration(pluginType string, rawConfig []byte) error {
199+
if len(rawConfig) == 0 {
200+
return nil // Empty configuration is allowed
201+
}
202+
203+
switch pluginType {
204+
case "semantic-cache":
205+
var cfg config.SemanticCachePluginConfig
206+
if err := json.Unmarshal(rawConfig, &cfg); err != nil {
207+
return fmt.Errorf("failed to unmarshal semantic-cache config: %w", err)
208+
}
209+
// Check for invalid fields
210+
var rawMap map[string]interface{}
211+
if err := json.Unmarshal(rawConfig, &rawMap); err == nil {
212+
invalidFields := []string{}
213+
for key := range rawMap {
214+
if key != "enabled" && key != "similarity_threshold" {
215+
invalidFields = append(invalidFields, key)
216+
}
217+
}
218+
if len(invalidFields) > 0 {
219+
return fmt.Errorf("semantic-cache plugin has invalid fields: %v. Valid fields are: enabled, similarity_threshold", invalidFields)
220+
}
221+
}
222+
223+
case "jailbreak":
224+
var cfg config.JailbreakPluginConfig
225+
if err := json.Unmarshal(rawConfig, &cfg); err != nil {
226+
return fmt.Errorf("failed to unmarshal jailbreak config: %w", err)
227+
}
228+
229+
case "pii":
230+
var cfg config.PIIPluginConfig
231+
if err := json.Unmarshal(rawConfig, &cfg); err != nil {
232+
return fmt.Errorf("failed to unmarshal pii config: %w", err)
233+
}
234+
// Check for invalid fields that might be present
235+
var rawMap map[string]interface{}
236+
if err := json.Unmarshal(rawConfig, &rawMap); err == nil {
237+
invalidFields := []string{}
238+
for key := range rawMap {
239+
if key != "enabled" && key != "threshold" {
240+
invalidFields = append(invalidFields, key)
241+
}
242+
}
243+
if len(invalidFields) > 0 {
244+
return fmt.Errorf("pii plugin has invalid fields: %v. Valid fields are: enabled, threshold", invalidFields)
245+
}
246+
}
247+
248+
case "system_prompt":
249+
var cfg config.SystemPromptPluginConfig
250+
if err := json.Unmarshal(rawConfig, &cfg); err != nil {
251+
return fmt.Errorf("failed to unmarshal system_prompt config: %w", err)
252+
}
253+
// Validate mode if present
254+
if cfg.Mode != "" && cfg.Mode != "replace" && cfg.Mode != "insert" {
255+
return fmt.Errorf("system_prompt mode must be 'replace' or 'insert', got: %s", cfg.Mode)
256+
}
257+
258+
case "header_mutation":
259+
var cfg config.HeaderMutationPluginConfig
260+
if err := json.Unmarshal(rawConfig, &cfg); err != nil {
261+
return fmt.Errorf("failed to unmarshal header_mutation config: %w", err)
262+
}
263+
// Validate that at least one operation is specified
264+
if len(cfg.Add) == 0 && len(cfg.Update) == 0 && len(cfg.Delete) == 0 {
265+
return fmt.Errorf("header_mutation plugin must specify at least one of: add, update, delete")
266+
}
267+
// Validate header pairs
268+
for _, h := range cfg.Add {
269+
if h.Name == "" {
270+
return fmt.Errorf("header_mutation add: header name cannot be empty")
271+
}
272+
}
273+
for _, h := range cfg.Update {
274+
if h.Name == "" {
275+
return fmt.Errorf("header_mutation update: header name cannot be empty")
276+
}
277+
}
278+
279+
default:
280+
return fmt.Errorf("unknown plugin type: %s", pluginType)
281+
}
282+
283+
return nil
284+
}

src/semantic-router/pkg/k8s/testdata/input/01-basic.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ spec:
6969
- type: "semantic-cache"
7070
configuration:
7171
enabled: true
72-
threshold: 0.9
72+
similarity_threshold: 0.9
7373

7474
- name: "general_tech"
7575
priority: 50

src/semantic-router/pkg/k8s/testdata/input/09-keyword-plugin.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ spec:
5353
- type: "semantic-cache"
5454
configuration:
5555
enabled: true
56-
threshold: 0.95
56+
similarity_threshold: 0.95
5757
- type: "header_mutation"
5858
configuration:
5959
add:

src/semantic-router/pkg/k8s/testdata/input/10-embedding-plugin.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,7 @@ spec:
5959
- type: "pii"
6060
configuration:
6161
enabled: true
62-
redact_mode: "mask"
63-
entities: ["CREDIT_CARD", "SSN", "EMAIL"]
62+
threshold: 0.8
6463
- type: "jailbreak"
6564
configuration:
6665
enabled: true

src/semantic-router/pkg/k8s/testdata/input/11-domain-plugin.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,5 @@ spec:
5555
- type: "semantic-cache"
5656
configuration:
5757
enabled: true
58-
threshold: 0.90
58+
similarity_threshold: 0.90
5959

src/semantic-router/pkg/k8s/testdata/input/12-keyword-embedding-plugin.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ spec:
6767
- type: "jailbreak"
6868
configuration:
6969
enabled: true
70-
threshold: 0.90
70+
similarity_threshold: 0.90
7171
- type: "system_prompt"
7272
configuration:
7373
prompt: "You are a security expert. Provide helpful security guidance while being cautious about potential misuse."

src/semantic-router/pkg/k8s/testdata/input/13-keyword-domain-plugin.yaml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,12 @@ spec:
6262
- type: "pii"
6363
configuration:
6464
enabled: true
65-
redact_mode: "hash"
66-
entities: ["PERSON", "DATE_OF_BIRTH", "MEDICAL_RECORD"]
65+
threshold: 0.9
6766
- type: "system_prompt"
6867
configuration:
6968
prompt: "You are a medical information assistant. Provide general health information but always advise users to consult healthcare professionals."
7069
- type: "semantic-cache"
7170
configuration:
7271
enabled: true
73-
threshold: 0.88
72+
similarity_threshold: 0.88
7473

src/semantic-router/pkg/k8s/testdata/input/14-domain-embedding-plugin.yaml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,7 @@ spec:
6363
- type: "pii"
6464
configuration:
6565
enabled: true
66-
redact_mode: "mask"
67-
entities: ["CREDIT_CARD", "BANK_ACCOUNT", "SSN"]
66+
threshold: 0.85
6867
- type: "system_prompt"
6968
configuration:
7069
prompt: "You are a financial information assistant. Provide general financial education but remind users this is not professional financial advice."
@@ -75,5 +74,5 @@ spec:
7574
- type: "semantic-cache"
7675
configuration:
7776
enabled: true
78-
threshold: 0.92
77+
similarity_threshold: 0.92
7978

src/semantic-router/pkg/k8s/testdata/input/15-keyword-embedding-domain-plugin.yaml

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,19 +87,18 @@ spec:
8787
- type: "pii"
8888
configuration:
8989
enabled: true
90-
redact_mode: "hash"
91-
entities: ["PERSON", "ORGANIZATION", "EMAIL", "PHONE_NUMBER"]
90+
threshold: 0.9
9291
- type: "jailbreak"
9392
configuration:
9493
enabled: true
95-
threshold: 0.88
94+
similarity_threshold: 0.88
9695
- type: "system_prompt"
9796
configuration:
9897
prompt: "You are a legal compliance assistant. Provide accurate information about regulations and compliance requirements. Always remind users to consult legal professionals for specific advice."
9998
- type: "semantic-cache"
10099
configuration:
101100
enabled: true
102-
threshold: 0.93
101+
similarity_threshold: 0.93
103102
- type: "header_mutation"
104103
configuration:
105104
add:
@@ -128,12 +127,11 @@ spec:
128127
- type: "pii"
129128
configuration:
130129
enabled: true
131-
redact_mode: "mask"
132-
entities: ["PERSON", "ORGANIZATION", "FINANCIAL_DATA"]
130+
threshold: 0.85
133131
- type: "semantic-cache"
134132
configuration:
135133
enabled: true
136-
threshold: 0.90
134+
similarity_threshold: 0.90
137135
- type: "header_mutation"
138136
configuration:
139137
add:
@@ -157,5 +155,5 @@ spec:
157155
- type: "semantic-cache"
158156
configuration:
159157
enabled: true
160-
threshold: 0.85
158+
similarity_threshold: 0.85
161159

src/semantic-router/pkg/k8s/testdata/output/01-basic.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ decisions:
122122
- type: semantic-cache
123123
configuration:
124124
enabled: true
125-
threshold: 0.9
125+
similarity_threshold: 0.9
126126
- name: general_tech
127127
description: General technical queries - use small model for efficiency
128128
priority: 50

0 commit comments

Comments
 (0)