Skip to content

Commit 34001e2

Browse files
Copilotrootfs
andcommitted
Refactor API discovery to use centralized registry pattern
- Replace hardcoded endpoint list with endpointRegistry - Replace hardcoded task types with taskTypeRegistry - Generate API documentation dynamically from registries - Add filtering logic for system prompt endpoints - Add test for system prompt endpoint filtering - Enables future OpenAPI spec generation from registry - Makes API documentation easier to maintain and extend Co-authored-by: rootfs <[email protected]>
1 parent 9cd696b commit 34001e2

File tree

2 files changed

+88
-60
lines changed

2 files changed

+88
-60
lines changed

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

Lines changed: 50 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -250,72 +250,62 @@ type TaskTypeInfo struct {
250250
Description string `json:"description"`
251251
}
252252

253+
// EndpointMetadata stores metadata about an endpoint for API documentation
254+
type EndpointMetadata struct {
255+
Path string
256+
Method string
257+
Description string
258+
}
259+
260+
// endpointRegistry is a centralized registry of all API endpoints with their metadata
261+
var endpointRegistry = []EndpointMetadata{
262+
{Path: "/health", Method: "GET", Description: "Health check endpoint"},
263+
{Path: "/api/v1", Method: "GET", Description: "API discovery and documentation"},
264+
{Path: "/api/v1/classify/intent", Method: "POST", Description: "Classify user queries into routing categories"},
265+
{Path: "/api/v1/classify/pii", Method: "POST", Description: "Detect personally identifiable information in text"},
266+
{Path: "/api/v1/classify/security", Method: "POST", Description: "Detect jailbreak attempts and security threats"},
267+
{Path: "/api/v1/classify/combined", Method: "POST", Description: "Perform combined classification (intent, PII, and security)"},
268+
{Path: "/api/v1/classify/batch", Method: "POST", Description: "Batch classification with configurable task_type parameter"},
269+
{Path: "/info/models", Method: "GET", Description: "Get information about loaded models"},
270+
{Path: "/info/classifier", Method: "GET", Description: "Get classifier information and status"},
271+
{Path: "/v1/models", Method: "GET", Description: "OpenAI-compatible model listing"},
272+
{Path: "/metrics/classification", Method: "GET", Description: "Get classification metrics and statistics"},
273+
{Path: "/config/classification", Method: "GET", Description: "Get classification configuration"},
274+
{Path: "/config/classification", Method: "PUT", Description: "Update classification configuration"},
275+
{Path: "/config/system-prompts", Method: "GET", Description: "Get system prompt configuration (requires explicit enablement)"},
276+
{Path: "/config/system-prompts", Method: "PUT", Description: "Update system prompt configuration (requires explicit enablement)"},
277+
}
278+
279+
// taskTypeRegistry is a centralized registry of all supported task types
280+
var taskTypeRegistry = []TaskTypeInfo{
281+
{Name: "intent", Description: "Intent/category classification (default for batch endpoint)"},
282+
{Name: "pii", Description: "Personally Identifiable Information detection"},
283+
{Name: "security", Description: "Jailbreak and security threat detection"},
284+
{Name: "all", Description: "All classification types combined"},
285+
}
286+
253287
// handleAPIOverview handles GET /api/v1 for API discovery
254288
func (s *ClassificationAPIServer) handleAPIOverview(w http.ResponseWriter, r *http.Request) {
289+
// Build endpoints list from registry, filtering out disabled endpoints
290+
endpoints := make([]EndpointInfo, 0, len(endpointRegistry))
291+
for _, metadata := range endpointRegistry {
292+
// Filter out system prompt endpoints if they are disabled
293+
if !s.enableSystemPromptAPI && (metadata.Path == "/config/system-prompts") {
294+
continue
295+
}
296+
endpoints = append(endpoints, EndpointInfo{
297+
Path: metadata.Path,
298+
Method: metadata.Method,
299+
Description: metadata.Description,
300+
})
301+
}
302+
255303
response := APIOverviewResponse{
256304
Service: "Semantic Router Classification API",
257305
Version: "v1",
258306
Description: "API for intent classification, PII detection, and security analysis",
259-
Endpoints: []EndpointInfo{
260-
{
261-
Path: "/api/v1/classify/intent",
262-
Method: "POST",
263-
Description: "Classify user queries into routing categories",
264-
},
265-
{
266-
Path: "/api/v1/classify/pii",
267-
Method: "POST",
268-
Description: "Detect personally identifiable information in text",
269-
},
270-
{
271-
Path: "/api/v1/classify/security",
272-
Method: "POST",
273-
Description: "Detect jailbreak attempts and security threats",
274-
},
275-
{
276-
Path: "/api/v1/classify/combined",
277-
Method: "POST",
278-
Description: "Perform combined classification (intent, PII, and security)",
279-
},
280-
{
281-
Path: "/api/v1/classify/batch",
282-
Method: "POST",
283-
Description: "Batch classification with configurable task_type parameter",
284-
},
285-
{
286-
Path: "/health",
287-
Method: "GET",
288-
Description: "Health check endpoint",
289-
},
290-
{
291-
Path: "/info/models",
292-
Method: "GET",
293-
Description: "Get information about loaded models",
294-
},
295-
{
296-
Path: "/v1/models",
297-
Method: "GET",
298-
Description: "OpenAI-compatible model listing",
299-
},
300-
},
301-
TaskTypes: []TaskTypeInfo{
302-
{
303-
Name: "intent",
304-
Description: "Intent/category classification (default for batch endpoint)",
305-
},
306-
{
307-
Name: "pii",
308-
Description: "Personally Identifiable Information detection",
309-
},
310-
{
311-
Name: "security",
312-
Description: "Jailbreak and security threat detection",
313-
},
314-
{
315-
Name: "all",
316-
Description: "All classification types combined",
317-
},
318-
},
307+
Endpoints: endpoints,
308+
TaskTypes: taskTypeRegistry,
319309
Links: map[string]string{
320310
"documentation": "https://vllm-project.github.io/semantic-router/",
321311
"models_info": "/info/models",

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,4 +864,42 @@ func TestAPIOverviewEndpoint(t *testing.T) {
864864
t.Errorf("Expected to find endpoint '%s' in response", path)
865865
}
866866
}
867+
868+
// Verify system prompt endpoints are not included when disabled (default)
869+
if endpointPaths["/config/system-prompts"] {
870+
t.Error("Expected system prompt endpoints to be excluded when enableSystemPromptAPI is false")
871+
}
872+
}
873+
874+
// TestAPIOverviewEndpointWithSystemPrompts tests API discovery with system prompts enabled
875+
func TestAPIOverviewEndpointWithSystemPrompts(t *testing.T) {
876+
apiServer := &ClassificationAPIServer{
877+
classificationSvc: services.NewPlaceholderClassificationService(),
878+
config: &config.RouterConfig{},
879+
enableSystemPromptAPI: true,
880+
}
881+
882+
req := httptest.NewRequest("GET", "/api/v1", nil)
883+
rr := httptest.NewRecorder()
884+
885+
apiServer.handleAPIOverview(rr, req)
886+
887+
if rr.Code != http.StatusOK {
888+
t.Fatalf("Expected 200 OK, got %d", rr.Code)
889+
}
890+
891+
var response APIOverviewResponse
892+
if err := json.Unmarshal(rr.Body.Bytes(), &response); err != nil {
893+
t.Fatalf("Failed to unmarshal response: %v", err)
894+
}
895+
896+
// Verify system prompt endpoints are included when enabled
897+
endpointPaths := make(map[string]bool)
898+
for _, endpoint := range response.Endpoints {
899+
endpointPaths[endpoint.Path] = true
900+
}
901+
902+
if !endpointPaths["/config/system-prompts"] {
903+
t.Error("Expected system prompt endpoints to be included when enableSystemPromptAPI is true")
904+
}
867905
}

0 commit comments

Comments
 (0)