@@ -184,6 +184,9 @@ func (s *ClassificationAPIServer) setupRoutes() *http.ServeMux {
184184 // Health check endpoint
185185 mux .HandleFunc ("GET /health" , s .handleHealth )
186186
187+ // API discovery endpoint
188+ mux .HandleFunc ("GET /api/v1" , s .handleAPIOverview )
189+
187190 // Classification endpoints
188191 mux .HandleFunc ("POST /api/v1/classify/intent" , s .handleIntentClassification )
189192 mux .HandleFunc ("POST /api/v1/classify/pii" , s .handlePIIDetection )
@@ -224,6 +227,105 @@ func (s *ClassificationAPIServer) handleHealth(w http.ResponseWriter, r *http.Re
224227 w .Write ([]byte (`{"status": "healthy", "service": "classification-api"}` ))
225228}
226229
230+ // APIOverviewResponse represents the response for GET /api/v1
231+ type APIOverviewResponse struct {
232+ Service string `json:"service"`
233+ Version string `json:"version"`
234+ Description string `json:"description"`
235+ Endpoints []EndpointInfo `json:"endpoints"`
236+ TaskTypes []TaskTypeInfo `json:"task_types"`
237+ Links map [string ]string `json:"links"`
238+ }
239+
240+ // EndpointInfo represents information about an API endpoint
241+ type EndpointInfo struct {
242+ Path string `json:"path"`
243+ Method string `json:"method"`
244+ Description string `json:"description"`
245+ }
246+
247+ // TaskTypeInfo represents information about a task type
248+ type TaskTypeInfo struct {
249+ Name string `json:"name"`
250+ Description string `json:"description"`
251+ }
252+
253+ // handleAPIOverview handles GET /api/v1 for API discovery
254+ func (s * ClassificationAPIServer ) handleAPIOverview (w http.ResponseWriter , r * http.Request ) {
255+ response := APIOverviewResponse {
256+ Service : "Semantic Router Classification API" ,
257+ Version : "v1" ,
258+ 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+ },
319+ Links : map [string ]string {
320+ "documentation" : "https://vllm-project.github.io/semantic-router/" ,
321+ "models_info" : "/info/models" ,
322+ "health" : "/health" ,
323+ },
324+ }
325+
326+ s .writeJSONResponse (w , http .StatusOK , response )
327+ }
328+
227329// handleIntentClassification handles intent classification requests
228330func (s * ClassificationAPIServer ) handleIntentClassification (w http.ResponseWriter , r * http.Request ) {
229331 var req services.IntentRequest
@@ -335,6 +437,13 @@ func (s *ClassificationAPIServer) handleBatchClassification(w http.ResponseWrite
335437 return
336438 }
337439
440+ // Validate task_type if provided
441+ if err := validateTaskType (req .TaskType ); err != nil {
442+ metrics .RecordBatchClassificationError ("unified" , "invalid_task_type" )
443+ s .writeErrorResponse (w , http .StatusBadRequest , "INVALID_TASK_TYPE" , err .Error ())
444+ return
445+ }
446+
338447 // Record the number of texts being processed
339448 metrics .RecordBatchClassificationTexts ("unified" , len (req .Texts ))
340449
@@ -622,6 +731,24 @@ func (s *ClassificationAPIServer) getSystemInfo() SystemInfo {
622731 }
623732}
624733
734+ // validateTaskType validates the task_type parameter for batch classification
735+ // Returns an error if the task_type is invalid, nil if valid or empty
736+ func validateTaskType (taskType string ) error {
737+ // Empty task_type defaults to "intent", so it's valid
738+ if taskType == "" {
739+ return nil
740+ }
741+
742+ validTaskTypes := []string {"intent" , "pii" , "security" , "all" }
743+ for _ , valid := range validTaskTypes {
744+ if taskType == valid {
745+ return nil
746+ }
747+ }
748+
749+ return fmt .Errorf ("invalid task_type '%s'. Supported values: %v" , taskType , validTaskTypes )
750+ }
751+
625752// extractRequestedResults converts unified results to batch format based on task type
626753func (s * ClassificationAPIServer ) extractRequestedResults (unifiedResults * services.UnifiedBatchResponse , taskType string , options * ClassificationOptions ) []BatchClassificationResult {
627754 // Determine the correct batch size based on task type
0 commit comments