Skip to content

Commit 8a2c551

Browse files
authored
refactor(config): reorganize configuration structure with hierarchical grouping (#574)
1 parent 21b6d6c commit 8a2c551

File tree

20 files changed

+2346
-1744
lines changed

20 files changed

+2346
-1744
lines changed

src/semantic-router/cmd/main.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func main() {
4646
}
4747

4848
// Load configuration to initialize tracing
49-
cfg, err := config.ParseConfigFile(*configPath)
49+
cfg, err := config.Parse(*configPath)
5050
if err != nil {
5151
logging.Fatalf("Failed to load config: %v", err)
5252
}
@@ -114,18 +114,18 @@ func main() {
114114
logging.Infof("Starting vLLM Semantic Router ExtProc with config: %s", *configPath)
115115

116116
// Initialize embedding models if configured (Long-context support)
117-
cfg, err = config.LoadConfig(*configPath)
117+
cfg, err = config.Load(*configPath)
118118
if err != nil {
119119
logging.Warnf("Failed to load config for embedding models: %v", err)
120-
} else if cfg.EmbeddingModels.Qwen3ModelPath != "" || cfg.EmbeddingModels.GemmaModelPath != "" {
120+
} else if cfg.Qwen3ModelPath != "" || cfg.GemmaModelPath != "" {
121121
logging.Infof("Initializing embedding models...")
122-
logging.Infof(" Qwen3 model: %s", cfg.EmbeddingModels.Qwen3ModelPath)
123-
logging.Infof(" Gemma model: %s", cfg.EmbeddingModels.GemmaModelPath)
122+
logging.Infof(" Qwen3 model: %s", cfg.Qwen3ModelPath)
123+
logging.Infof(" Gemma model: %s", cfg.GemmaModelPath)
124124
logging.Infof(" Use CPU: %v", cfg.EmbeddingModels.UseCPU)
125125

126126
if err := candle_binding.InitEmbeddingModels(
127-
cfg.EmbeddingModels.Qwen3ModelPath,
128-
cfg.EmbeddingModels.GemmaModelPath,
127+
cfg.Qwen3ModelPath,
128+
cfg.GemmaModelPath,
129129
cfg.EmbeddingModels.UseCPU,
130130
); err != nil {
131131
logging.Errorf("Failed to initialize embedding models: %v", err)

src/semantic-router/go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ require (
2121
github.com/openai/openai-go v1.12.0
2222
github.com/prometheus/client_golang v1.23.0
2323
github.com/prometheus/client_model v0.6.2
24+
github.com/samber/lo v1.52.0
2425
github.com/stretchr/testify v1.11.1
2526
github.com/vllm-project/semantic-router/candle-binding v0.0.0-00010101000000-000000000000
2627
go.opentelemetry.io/otel v1.38.0
@@ -31,6 +32,7 @@ require (
3132
go.uber.org/zap v1.27.0
3233
golang.org/x/sys v0.37.0
3334
google.golang.org/grpc v1.75.0
35+
gopkg.in/yaml.v2 v2.4.0
3436
gopkg.in/yaml.v3 v3.0.1
3537
k8s.io/apimachinery v0.31.4
3638
sigs.k8s.io/yaml v1.6.0
@@ -100,7 +102,6 @@ require (
100102
google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9 // indirect
101103
google.golang.org/protobuf v1.36.9 // indirect
102104
gopkg.in/inf.v0 v0.9.1 // indirect
103-
gopkg.in/yaml.v2 v2.4.0 // indirect
104105
k8s.io/klog/v2 v2.130.1 // indirect
105106
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
106107
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect

src/semantic-router/go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,8 @@ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR
259259
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
260260
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
261261
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
262+
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
263+
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
262264
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
263265
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
264266
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=

src/semantic-router/pkg/apiserver/route_model_info.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func (s *ClassificationAPIServer) getLoadedModelsInfo() []ModelInfo {
8080
}
8181

8282
// Category classifier model
83-
if s.config.Classifier.CategoryModel.CategoryMappingPath != "" {
83+
if s.config.CategoryMappingPath != "" {
8484
categories := []string{}
8585
// Extract category names from config.Categories
8686
for _, cat := range s.config.Categories {
@@ -91,27 +91,27 @@ func (s *ClassificationAPIServer) getLoadedModelsInfo() []ModelInfo {
9191
Name: "category_classifier",
9292
Type: "intent_classification",
9393
Loaded: true,
94-
ModelPath: s.config.Classifier.CategoryModel.ModelID,
94+
ModelPath: s.config.CategoryModel.ModelID,
9595
Categories: categories,
9696
Metadata: map[string]string{
97-
"mapping_path": s.config.Classifier.CategoryModel.CategoryMappingPath,
97+
"mapping_path": s.config.CategoryMappingPath,
9898
"model_type": "modernbert",
99-
"threshold": fmt.Sprintf("%.2f", s.config.Classifier.CategoryModel.Threshold),
99+
"threshold": fmt.Sprintf("%.2f", s.config.CategoryModel.Threshold),
100100
},
101101
})
102102
}
103103

104104
// PII classifier model
105-
if s.config.Classifier.PIIModel.PIIMappingPath != "" {
105+
if s.config.PIIMappingPath != "" {
106106
models = append(models, ModelInfo{
107107
Name: "pii_classifier",
108108
Type: "pii_detection",
109109
Loaded: true,
110-
ModelPath: s.config.Classifier.PIIModel.ModelID,
110+
ModelPath: s.config.PIIModel.ModelID,
111111
Metadata: map[string]string{
112-
"mapping_path": s.config.Classifier.PIIModel.PIIMappingPath,
112+
"mapping_path": s.config.PIIMappingPath,
113113
"model_type": "modernbert_token",
114-
"threshold": fmt.Sprintf("%.2f", s.config.Classifier.PIIModel.Threshold),
114+
"threshold": fmt.Sprintf("%.2f", s.config.PIIModel.Threshold),
115115
},
116116
})
117117
}
@@ -130,15 +130,15 @@ func (s *ClassificationAPIServer) getLoadedModelsInfo() []ModelInfo {
130130
}
131131

132132
// BERT similarity model
133-
if s.config.BertModel.ModelID != "" {
133+
if s.config.ModelID != "" {
134134
models = append(models, ModelInfo{
135135
Name: "bert_similarity_model",
136136
Type: "similarity",
137137
Loaded: true,
138-
ModelPath: s.config.BertModel.ModelID,
138+
ModelPath: s.config.ModelID,
139139
Metadata: map[string]string{
140140
"model_type": "sentence_transformer",
141-
"threshold": fmt.Sprintf("%.2f", s.config.BertModel.Threshold),
141+
"threshold": fmt.Sprintf("%.2f", s.config.Threshold),
142142
"use_cpu": fmt.Sprintf("%t", s.config.BertModel.UseCPU),
143143
},
144144
})

src/semantic-router/pkg/apiserver/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
// Init starts the API server
1919
func Init(configPath string, port int, enableSystemPromptAPI bool) error {
2020
// Load configuration
21-
cfg, err := config.LoadConfig(configPath)
21+
cfg, err := config.Load(configPath)
2222
if err != nil {
2323
return fmt.Errorf("failed to load config: %w", err)
2424
}

src/semantic-router/pkg/apiserver/server_test.go

Lines changed: 102 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -214,12 +214,14 @@ func TestBatchClassificationConfiguration(t *testing.T) {
214214
{
215215
name: "Custom max batch size",
216216
config: &config.RouterConfig{
217-
API: config.APIConfig{
218-
BatchClassification: struct {
219-
Metrics config.BatchClassificationMetricsConfig `yaml:"metrics,omitempty"`
220-
}{
221-
Metrics: config.BatchClassificationMetricsConfig{
222-
Enabled: true,
217+
APIServer: config.APIServer{
218+
API: config.APIConfig{
219+
BatchClassification: struct {
220+
Metrics config.BatchClassificationMetricsConfig `yaml:"metrics,omitempty"`
221+
}{
222+
Metrics: config.BatchClassificationMetricsConfig{
223+
Enabled: true,
224+
},
223225
},
224226
},
225227
},
@@ -248,12 +250,14 @@ func TestBatchClassificationConfiguration(t *testing.T) {
248250
{
249251
name: "Valid request within custom limits",
250252
config: &config.RouterConfig{
251-
API: config.APIConfig{
252-
BatchClassification: struct {
253-
Metrics config.BatchClassificationMetricsConfig `yaml:"metrics,omitempty"`
254-
}{
255-
Metrics: config.BatchClassificationMetricsConfig{
256-
Enabled: true,
253+
APIServer: config.APIServer{
254+
API: config.APIConfig{
255+
BatchClassification: struct {
256+
Metrics config.BatchClassificationMetricsConfig `yaml:"metrics,omitempty"`
257+
}{
258+
Metrics: config.BatchClassificationMetricsConfig{
259+
Enabled: true,
260+
},
257261
},
258262
},
259263
},
@@ -305,23 +309,27 @@ func TestBatchClassificationConfiguration(t *testing.T) {
305309
func TestOpenAIModelsEndpoint(t *testing.T) {
306310
// Test with default config (IncludeConfigModelsInList = false)
307311
cfg := &config.RouterConfig{
308-
VLLMEndpoints: []config.VLLMEndpoint{
309-
{
310-
Name: "primary",
311-
Address: "127.0.0.1",
312-
Port: 8000,
313-
Weight: 1,
314-
},
315-
},
316-
ModelConfig: map[string]config.ModelParams{
317-
"gpt-4o-mini": {
318-
PreferredEndpoints: []string{"primary"},
312+
BackendModels: config.BackendModels{
313+
VLLMEndpoints: []config.VLLMEndpoint{
314+
{
315+
Name: "primary",
316+
Address: "127.0.0.1",
317+
Port: 8000,
318+
Weight: 1,
319+
},
319320
},
320-
"llama-3.1-8b-instruct": {
321-
PreferredEndpoints: []string{"primary"},
321+
ModelConfig: map[string]config.ModelParams{
322+
"gpt-4o-mini": {
323+
PreferredEndpoints: []string{"primary"},
324+
},
325+
"llama-3.1-8b-instruct": {
326+
PreferredEndpoints: []string{"primary"},
327+
},
322328
},
323329
},
324-
IncludeConfigModelsInList: false,
330+
RouterOptions: config.RouterOptions{
331+
IncludeConfigModelsInList: false,
332+
},
325333
}
326334

327335
apiServer := &ClassificationAPIServer{
@@ -371,23 +379,27 @@ func TestOpenAIModelsEndpoint(t *testing.T) {
371379
func TestOpenAIModelsEndpointWithConfigModels(t *testing.T) {
372380
// Test with IncludeConfigModelsInList = true
373381
cfg := &config.RouterConfig{
374-
VLLMEndpoints: []config.VLLMEndpoint{
375-
{
376-
Name: "primary",
377-
Address: "127.0.0.1",
378-
Port: 8000,
379-
Weight: 1,
380-
},
381-
},
382-
ModelConfig: map[string]config.ModelParams{
383-
"gpt-4o-mini": {
384-
PreferredEndpoints: []string{"primary"},
382+
BackendModels: config.BackendModels{
383+
VLLMEndpoints: []config.VLLMEndpoint{
384+
{
385+
Name: "primary",
386+
Address: "127.0.0.1",
387+
Port: 8000,
388+
Weight: 1,
389+
},
385390
},
386-
"llama-3.1-8b-instruct": {
387-
PreferredEndpoints: []string{"primary"},
391+
ModelConfig: map[string]config.ModelParams{
392+
"gpt-4o-mini": {
393+
PreferredEndpoints: []string{"primary"},
394+
},
395+
"llama-3.1-8b-instruct": {
396+
PreferredEndpoints: []string{"primary"},
397+
},
388398
},
389399
},
390-
IncludeConfigModelsInList: true,
400+
RouterOptions: config.RouterOptions{
401+
IncludeConfigModelsInList: true,
402+
},
391403
}
392404

393405
apiServer := &ClassificationAPIServer{
@@ -441,18 +453,32 @@ func TestOpenAIModelsEndpointWithConfigModels(t *testing.T) {
441453
func TestSystemPromptEndpointSecurity(t *testing.T) {
442454
// Create test configuration with categories that have system prompts
443455
cfg := &config.RouterConfig{
444-
Categories: []config.Category{
445-
{
446-
Name: "math",
447-
SystemPrompt: "You are a math expert.",
448-
SystemPromptEnabled: &[]bool{true}[0], // Pointer to true
449-
SystemPromptMode: "replace",
450-
},
451-
{
452-
Name: "coding",
453-
SystemPrompt: "You are a coding assistant.",
454-
SystemPromptEnabled: &[]bool{false}[0], // Pointer to false
455-
SystemPromptMode: "insert",
456+
IntelligentRouting: config.IntelligentRouting{
457+
Categories: []config.Category{
458+
{
459+
CategoryMetadata: config.CategoryMetadata{
460+
Name: "math",
461+
},
462+
DomainAwarePolicies: config.DomainAwarePolicies{
463+
SystemPromptPolicy: config.SystemPromptPolicy{
464+
SystemPrompt: "You are a math expert.",
465+
SystemPromptEnabled: &[]bool{true}[0], // Pointer to true
466+
SystemPromptMode: "replace",
467+
},
468+
},
469+
},
470+
{
471+
CategoryMetadata: config.CategoryMetadata{
472+
Name: "coding",
473+
},
474+
DomainAwarePolicies: config.DomainAwarePolicies{
475+
SystemPromptPolicy: config.SystemPromptPolicy{
476+
SystemPrompt: "You are a coding assistant.",
477+
SystemPromptEnabled: &[]bool{false}[0], // Pointer to false
478+
SystemPromptMode: "insert",
479+
},
480+
},
481+
},
456482
},
457483
},
458484
}
@@ -633,16 +659,30 @@ func TestSystemPromptEndpointSecurity(t *testing.T) {
633659
func TestSystemPromptEndpointFunctionality(t *testing.T) {
634660
// Create test configuration
635661
cfg := &config.RouterConfig{
636-
Categories: []config.Category{
637-
{
638-
Name: "math",
639-
SystemPrompt: "You are a math expert.",
640-
SystemPromptEnabled: &[]bool{true}[0],
641-
SystemPromptMode: "replace",
642-
},
643-
{
644-
Name: "no-prompt",
645-
SystemPrompt: "", // No system prompt
662+
IntelligentRouting: config.IntelligentRouting{
663+
Categories: []config.Category{
664+
{
665+
CategoryMetadata: config.CategoryMetadata{
666+
Name: "math",
667+
},
668+
DomainAwarePolicies: config.DomainAwarePolicies{
669+
SystemPromptPolicy: config.SystemPromptPolicy{
670+
SystemPrompt: "You are a math expert.",
671+
SystemPromptEnabled: &[]bool{true}[0],
672+
SystemPromptMode: "replace",
673+
},
674+
},
675+
},
676+
{
677+
CategoryMetadata: config.CategoryMetadata{
678+
Name: "no-prompt",
679+
},
680+
DomainAwarePolicies: config.DomainAwarePolicies{
681+
SystemPromptPolicy: config.SystemPromptPolicy{
682+
SystemPrompt: "", // No system prompt
683+
},
684+
},
685+
},
646686
},
647687
},
648688
}

0 commit comments

Comments
 (0)