@@ -643,82 +643,55 @@ func TestResolveCandidates_SelfGuard(t *testing.T) {
643643 }
644644}
645645
646- // TestHandleHTTP_AnthropicMessages_InferenceRouting proves /v1/messages is
647- // treated as an inference endpoint: model-based candidate filtering is applied
648- // and the request body is forwarded unchanged to the matching node.
649- func TestHandleHTTP_AnthropicMessages_InferenceRouting (t * testing.T ) {
650- var gotBody string
651- var gotPath string
652- good := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
653- b , _ := io .ReadAll (r .Body )
654- gotBody = string (b )
655- gotPath = r .URL .Path
656- w .WriteHeader (http .StatusOK )
657- _ , _ = io .WriteString (w , `{"id":"msg_test","type":"message","role":"assistant","content":[{"type":"text","text":"Hello!"}]}` )
658- }))
659- defer good .Close ()
660-
661- wrongModel := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , _ * http.Request ) {
662- t .Error ("wrong-model node should not receive request" )
663- w .WriteHeader (http .StatusOK )
664- }))
665- defer wrongModel .Close ()
666-
667- disc := NewDiscovery ()
668- disc .AddManual (nodeForModel (t , "wrong" , wrongModel .URL , "mistral" ))
669- disc .AddManual (nodeForModel (t , "good" , good .URL , "claude-3-5-sonnet-20241022" ))
670- p := testProxy (disc , 11434 )
671- p .SetSelected ("wrong" )
672-
673- body := `{"model":"claude-3-5-sonnet-20241022","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}`
674- rec := httptest .NewRecorder ()
675- p .handleHTTP (rec , httptest .NewRequest (http .MethodPost , "/v1/messages" , strings .NewReader (body )))
676-
677- if rec .Code != http .StatusOK {
678- t .Fatalf ("status = %d, want 200" , rec .Code )
679- }
680- if gotBody != body {
681- t .Errorf ("node got body %q, want the original request body" , gotBody )
682- }
683- if gotPath != "/v1/messages" {
684- t .Errorf ("path = %q, want /v1/messages" , gotPath )
646+ // TestHandleHTTP_InferenceRouting proves each request family applies
647+ // model-based candidate filtering and forwards the request unchanged.
648+ func TestHandleHTTP_InferenceRouting (t * testing.T ) {
649+ test := func (name , path string ) {
650+ t .Run (name , func (t * testing.T ) {
651+ var gotBody string
652+ var gotPath string
653+ good := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
654+ b , _ := io .ReadAll (r .Body )
655+ gotBody = string (b )
656+ gotPath = r .URL .Path
657+ w .WriteHeader (http .StatusOK )
658+ }))
659+ defer good .Close ()
660+
661+ wrongModel := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , _ * http.Request ) {
662+ t .Error ("wrong-model node should not receive request" )
663+ w .WriteHeader (http .StatusOK )
664+ }))
665+ defer wrongModel .Close ()
666+
667+ disc := NewDiscovery ()
668+ disc .AddManual (nodeForModel (t , "wrong" , wrongModel .URL , "different-model" ))
669+ disc .AddManual (nodeForModel (t , "good" , good .URL , "requested-model" ))
670+ p := testProxy (disc , 11434 )
671+ p .SetSelected ("wrong" )
672+
673+ body := `{"model":"requested-model","messages":[]}`
674+ rec := httptest .NewRecorder ()
675+ p .handleHTTP (rec , httptest .NewRequest (http .MethodPost , path , strings .NewReader (body )))
676+
677+ if rec .Code != http .StatusOK {
678+ t .Fatalf ("status = %d, want 200" , rec .Code )
679+ }
680+ if gotBody != body {
681+ t .Errorf ("node got body %q, want %q" , gotBody , body )
682+ }
683+ if gotPath != path {
684+ t .Errorf ("path = %q, want %q" , gotPath , path )
685+ }
686+ })
685687 }
686- }
687-
688- // TestHandleHTTP_AnthropicMessages_404Failover proves /v1/messages is treated
689- // as an inference endpoint for 404 failover: when the first eligible candidate
690- // returns 404, the request retries to the second candidate and the body is
691- // preserved.
692- func TestHandleHTTP_AnthropicMessages_404Failover (t * testing.T ) {
693- missing := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
694- w .WriteHeader (http .StatusNotFound )
695- _ , _ = io .WriteString (w , `{"error":{"type":"not_found_error","message":"model not found"}}` )
696- }))
697- defer missing .Close ()
698-
699- var gotBody string
700- has := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
701- b , _ := io .ReadAll (r .Body )
702- gotBody = string (b )
703- w .WriteHeader (http .StatusOK )
704- _ , _ = io .WriteString (w , `{"id":"msg_test","type":"message","role":"assistant","content":[{"type":"text","text":"Hello!"}]}` )
705- }))
706- defer has .Close ()
707-
708- disc := NewDiscovery ()
709- disc .AddManual (nodeForModel (t , "missing" , missing .URL , "claude-3-5-sonnet-20241022" ))
710- disc .AddManual (nodeForModel (t , "has" , has .URL , "claude-3-5-sonnet-20241022" ))
711- p := testProxy (disc , 11434 )
712- p .SetSelected ("missing" )
713688
714- body := `{"model":"claude-3-5-sonnet-20241022","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}`
715- rec := httptest .NewRecorder ()
716- p .handleHTTP (rec , httptest .NewRequest (http .MethodPost , "/v1/messages" , strings .NewReader (body )))
717-
718- if rec .Code != http .StatusOK {
719- t .Fatalf ("status = %d, want 200 (should fail over past the 404)" , rec .Code )
720- }
721- if gotBody != body {
722- t .Errorf ("failover node got body %q, want the original request body" , gotBody )
723- }
689+ test ("native Ollama generate" , "/api/generate" )
690+ test ("native Ollama chat" , "/api/chat" )
691+ test ("native Ollama embeddings" , "/api/embeddings" )
692+ test ("native Ollama embed" , "/api/embed" )
693+ test ("OpenAI chat completions" , "/v1/chat/completions" )
694+ test ("OpenAI completions" , "/v1/completions" )
695+ test ("OpenAI embeddings" , "/v1/embeddings" )
696+ test ("Anthropic messages" , "/v1/messages" )
724697}
0 commit comments