|
|
|
|
@@ -93,8 +93,8 @@ func TestProxyHandler_HandleProxy_NonStreamSuccess(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
|
@@ -118,8 +118,8 @@ func TestProxyHandler_HandleProxy_RoutingError_WithBody(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"unknown/model","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"unknown/model","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 404, w.Code)
|
|
|
|
|
@@ -146,8 +146,8 @@ func TestProxyHandler_HandleProxy_ConversionError(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 502, w.Code)
|
|
|
|
|
@@ -174,8 +174,8 @@ func TestProxyHandler_HandleProxy_ClientSendError(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 502, w.Code)
|
|
|
|
|
@@ -211,8 +211,8 @@ func TestProxyHandler_HandleProxy_StreamSuccess(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}],"stream":true}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}],"stream":true}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
|
@@ -241,8 +241,8 @@ func TestProxyHandler_HandleProxy_StreamError(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}],"stream":true}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}],"stream":true}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 502, w.Code)
|
|
|
|
|
@@ -266,8 +266,8 @@ func TestProxyHandler_ForwardPassthrough_GET(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/models"}}
|
|
|
|
|
c.Request = httptest.NewRequest("GET", "/openai/models", nil)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/models"}}
|
|
|
|
|
c.Request = httptest.NewRequest("GET", "/openai/v1/models", nil)
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
|
@@ -287,7 +287,7 @@ func TestProxyHandler_ForwardPassthrough_UnsupportedProtocol(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "unknown"}, {Key: "path", Value: "/models"}}
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "unknown"}, {Key: "path", Value: "/v1/models"}}
|
|
|
|
|
c.Request = httptest.NewRequest("GET", "/unknown/models", nil)
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
@@ -309,8 +309,8 @@ func TestProxyHandler_ForwardPassthrough_NoProviders(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/models"}}
|
|
|
|
|
c.Request = httptest.NewRequest("GET", "/openai/models", nil)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/models"}}
|
|
|
|
|
c.Request = httptest.NewRequest("GET", "/openai/v1/models", nil)
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
|
@@ -352,8 +352,8 @@ func TestProxyHandler_HandleProxy_ProviderProtocolDefault(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
|
@@ -449,8 +449,8 @@ func TestProxyHandler_HandleProxy_EmptyBody(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/models"}}
|
|
|
|
|
c.Request = httptest.NewRequest("GET", "/openai/models", nil)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/models"}}
|
|
|
|
|
c.Request = httptest.NewRequest("GET", "/openai/v1/models", nil)
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
|
@@ -483,8 +483,8 @@ func TestProxyHandler_HandleStream_MidStreamError(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}],"stream":true}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}],"stream":true}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
|
@@ -521,8 +521,8 @@ func TestProxyHandler_HandleStream_FlushOutput(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}],"stream":true}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}],"stream":true}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
|
@@ -555,8 +555,8 @@ func TestProxyHandler_HandleStream_CreateStreamConverterError(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}],"stream":true}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}],"stream":true}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 500, w.Code)
|
|
|
|
|
@@ -582,8 +582,8 @@ func TestProxyHandler_HandleStream_ConvertRequestError(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}],"stream":true}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}],"stream":true}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 500, w.Code)
|
|
|
|
|
@@ -617,8 +617,8 @@ func TestProxyHandler_HandleNonStream_ConvertResponseError(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 500, w.Code)
|
|
|
|
|
@@ -649,8 +649,8 @@ func TestProxyHandler_HandleNonStream_ResponseHeaders(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
|
@@ -681,8 +681,8 @@ func TestProxyHandler_ForwardPassthrough_CrossProtocol(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/models"}}
|
|
|
|
|
c.Request = httptest.NewRequest("GET", "/openai/models", nil)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/models"}}
|
|
|
|
|
c.Request = httptest.NewRequest("GET", "/openai/v1/models", nil)
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
|
@@ -705,8 +705,8 @@ func TestProxyHandler_ForwardPassthrough_NoBody_NoModel(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/models"}}
|
|
|
|
|
c.Request = httptest.NewRequest("GET", "/openai/models", nil)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/models"}}
|
|
|
|
|
c.Request = httptest.NewRequest("GET", "/openai/v1/models", nil)
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
|
@@ -729,10 +729,10 @@ func TestIsStreamRequest_EdgeCases(t *testing.T) {
|
|
|
|
|
path string
|
|
|
|
|
expected bool
|
|
|
|
|
}{
|
|
|
|
|
{"stream at end of JSON", `{"messages":[],"stream":true}`, "/chat/completions", true},
|
|
|
|
|
{"stream with spaces", `{"stream" : true}`, "/chat/completions", true},
|
|
|
|
|
{"stream embedded in string value", `{"model":"stream:true"}`, "/chat/completions", false},
|
|
|
|
|
{"empty body", "", "/chat/completions", false},
|
|
|
|
|
{"stream at end of JSON", `{"messages":[],"stream":true}`, "/v1/chat/completions", true},
|
|
|
|
|
{"stream with spaces", `{"stream" : true}`, "/v1/chat/completions", true},
|
|
|
|
|
{"stream embedded in string value", `{"model":"stream:true"}`, "/v1/chat/completions", false},
|
|
|
|
|
{"empty body", "", "/v1/chat/completions", false},
|
|
|
|
|
{"stream true embeddings", `{"model":"text-emb","stream":true}`, "/v1/embeddings", false},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -781,8 +781,8 @@ func TestProxyHandler_HandleProxy_RouteEmptyBody_NoModel(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/models"}}
|
|
|
|
|
c.Request = httptest.NewRequest("GET", "/openai/models", nil)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/models"}}
|
|
|
|
|
c.Request = httptest.NewRequest("GET", "/openai/v1/models", nil)
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
|
@@ -805,35 +805,35 @@ func TestIsStreamRequest(t *testing.T) {
|
|
|
|
|
name: "stream true",
|
|
|
|
|
body: []byte(`{"model": "gpt-4", "stream": true}`),
|
|
|
|
|
clientProtocol: "openai",
|
|
|
|
|
nativePath: "/chat/completions",
|
|
|
|
|
nativePath: "/v1/chat/completions",
|
|
|
|
|
expected: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "stream false",
|
|
|
|
|
body: []byte(`{"model": "gpt-4", "stream": false}`),
|
|
|
|
|
clientProtocol: "openai",
|
|
|
|
|
nativePath: "/chat/completions",
|
|
|
|
|
nativePath: "/v1/chat/completions",
|
|
|
|
|
expected: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "no stream field",
|
|
|
|
|
body: []byte(`{"model": "gpt-4"}`),
|
|
|
|
|
clientProtocol: "openai",
|
|
|
|
|
nativePath: "/chat/completions",
|
|
|
|
|
nativePath: "/v1/chat/completions",
|
|
|
|
|
expected: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "invalid json",
|
|
|
|
|
body: []byte(`{invalid}`),
|
|
|
|
|
clientProtocol: "openai",
|
|
|
|
|
nativePath: "/chat/completions",
|
|
|
|
|
nativePath: "/v1/chat/completions",
|
|
|
|
|
expected: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "not chat endpoint",
|
|
|
|
|
body: []byte(`{"model": "gpt-4", "stream": true}`),
|
|
|
|
|
clientProtocol: "openai",
|
|
|
|
|
nativePath: "/models",
|
|
|
|
|
nativePath: "/v1/models",
|
|
|
|
|
expected: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
@@ -871,8 +871,8 @@ func TestProxyHandler_HandleProxy_Models_LocalAggregation(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/models"}}
|
|
|
|
|
c.Request = httptest.NewRequest("GET", "/openai/models", nil)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/models"}}
|
|
|
|
|
c.Request = httptest.NewRequest("GET", "/openai/v1/models", nil)
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
|
@@ -902,8 +902,8 @@ func TestProxyHandler_HandleProxy_ModelInfo_LocalQuery(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/models/openai/gpt-4"}}
|
|
|
|
|
c.Request = httptest.NewRequest("GET", "/openai/models/openai/gpt-4", nil)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/models/openai/gpt-4"}}
|
|
|
|
|
c.Request = httptest.NewRequest("GET", "/openai/v1/models/openai/gpt-4", nil)
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
|
@@ -936,8 +936,8 @@ func TestProxyHandler_HandleProxy_Models_EmptySuffix_ForwardPassthrough(t *testi
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/models/"}}
|
|
|
|
|
c.Request = httptest.NewRequest("GET", "/openai/models/", nil)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/models/"}}
|
|
|
|
|
c.Request = httptest.NewRequest("GET", "/openai/v1/models/", nil)
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
|
@@ -974,8 +974,8 @@ func TestProxyHandler_HandleProxy_SmartPassthrough_UnifiedID(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"openai_p/gpt-4","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"openai_p/gpt-4","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
|
@@ -1012,8 +1012,8 @@ func TestProxyHandler_HandleProxy_CrossProtocol_NonStream_UnifiedID(t *testing.T
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"anthropic_p/claude-3","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"anthropic_p/claude-3","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
|
@@ -1061,8 +1061,8 @@ data: {"type":"message_stop"}
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"anthropic_p/claude-3","messages":[{"role":"user","content":"hi"}],"stream":true}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"anthropic_p/claude-3","messages":[{"role":"user","content":"hi"}],"stream":true}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
|
@@ -1099,8 +1099,8 @@ func TestProxyHandler_HandleProxy_SmartPassthrough_Fidelity(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"openai_p/gpt-4","messages":[{"role":"user","content":"hi"}],"custom_param":"should_be_preserved"}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"openai_p/gpt-4","messages":[{"role":"user","content":"hi"}],"custom_param":"should_be_preserved"}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
|
@@ -1130,8 +1130,8 @@ func TestProxyHandler_HandleProxy_UnifiedID_ModelNotFound(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"unknown/model","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"unknown/model","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
assert.Equal(t, 404, w.Code)
|
|
|
|
|
@@ -1154,10 +1154,10 @@ func TestProxyHandler_HandleProxy_OpenAIAndAnthropicNativePaths(t *testing.T) {
|
|
|
|
|
responseModel string
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "openai path has no v1 after gateway prefix",
|
|
|
|
|
name: "openai path keeps v1 after gateway prefix",
|
|
|
|
|
protocol: "openai",
|
|
|
|
|
path: "/chat/completions",
|
|
|
|
|
requestPath: "/openai/chat/completions",
|
|
|
|
|
path: "/v1/chat/completions",
|
|
|
|
|
requestPath: "/openai/v1/chat/completions",
|
|
|
|
|
baseURL: "https://api.test.com/v1",
|
|
|
|
|
expectedURL: "https://api.test.com/v1/chat/completions",
|
|
|
|
|
body: `{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}]}`,
|
|
|
|
|
@@ -1240,8 +1240,8 @@ func TestProxyHandler_UpstreamNon2xx_Passthrough(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}]}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
require.Equal(t, http.StatusTooManyRequests, w.Code)
|
|
|
|
|
@@ -1272,8 +1272,8 @@ func TestProxyHandler_StreamUpstreamNon2xx_Passthrough(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}],"stream":true}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":"hi"}],"stream":true}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
require.Equal(t, http.StatusServiceUnavailable, w.Code)
|
|
|
|
|
@@ -1347,8 +1347,8 @@ func TestProxyHandler_InvalidJSON_UsesGatewayError(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
require.Equal(t, http.StatusBadRequest, w.Code)
|
|
|
|
|
@@ -1373,8 +1373,8 @@ func TestProxyHandler_CrossProtocolMultimodal_Unsupported(t *testing.T) {
|
|
|
|
|
body := []byte(`{"model":"anthropic_p/claude","messages":[{"role":"user","content":[{"type":"text","text":"describe"},{"type":"image_url","image_url":{"url":"data:image/png;base64,abc"}}]}]}`)
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader(body))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader(body))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
require.Equal(t, http.StatusBadRequest, w.Code)
|
|
|
|
|
@@ -1409,8 +1409,8 @@ func TestProxyHandler_SameProtocolMultimodal_SmartPassthrough(t *testing.T) {
|
|
|
|
|
body := []byte(`{"model":"p1/gpt-4","messages":[{"role":"user","content":[{"type":"text","text":"describe"},{"type":"image_url","image_url":{"url":"data:image/png;base64,abc"}}]}]}`)
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader(body))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader(body))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
|
|
|
@@ -1444,8 +1444,8 @@ func TestProxyHandler_RawStreamPassthrough_PreservesSSEFrames(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
c, _ := gin.CreateTestContext(w)
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader([]byte(`{"model":"gpt-4","messages":[{"role":"user","content":"hi"}],"stream":true}`)))
|
|
|
|
|
c.Params = gin.Params{{Key: "protocol", Value: "openai"}, {Key: "path", Value: "/v1/chat/completions"}}
|
|
|
|
|
c.Request = httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader([]byte(`{"model":"gpt-4","messages":[{"role":"user","content":"hi"}],"stream":true}`)))
|
|
|
|
|
|
|
|
|
|
h.HandleProxy(c)
|
|
|
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
|
|
|
|