Skip to content

Commit

Permalink
ai-proxy e2e 支持通义千问非流式响应
Browse files Browse the repository at this point in the history
  • Loading branch information
hanxiantao committed Jan 30, 2025
1 parent b79fa88 commit 8aa8fa1
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 7 deletions.
23 changes: 23 additions & 0 deletions test/e2e/conformance/tests/go-wasm-ai-proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,29 @@ data: [DONE]
},
},
},
{
Meta: http.AssertionMeta{
TestCaseName: "qwen case 3: non-streaming request",
CompareTarget: http.CompareTargetResponse,
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "dashscope.aliyuncs.com",
Path: "/v1/chat/completions",
Method: "POST",
ContentType: http.ContentTypeApplicationJson,
Body: []byte(`{"model":"gpt-3","messages":[{"role":"user","content":"你好,你是谁?"}],"stream":false}`),
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
ContentType: http.ContentTypeApplicationJson,
JsonBodyIgnoreFields: []string{"created"},
Body: []byte(`{"id":"chatcmpl-llm-mock","choices":[{"index":0,"message":{"role":"assistant","content":"你好,你是谁?"},"finish_reason":"stop"}],"created":1738218357,"model":"qwen-turbo","object":"chat.completion","usage":{"prompt_tokens":9,"completion_tokens":1,"total_tokens":10}}`),
},
},
},
}
t.Run("WasmPlugins ai-proxy", func(t *testing.T) {
for _, testcase := range testcases {
Expand Down
33 changes: 32 additions & 1 deletion test/e2e/conformance/tests/go-wasm-ai-proxy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,25 @@ spec:
port:
number: 3000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: wasmplugin-ai-proxy-qwen
namespace: higress-conformance-ai-backend
spec:
ingressClassName: higress
rules:
- host: "dashscope.aliyuncs.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: llm-mock-service
port:
number: 3000
---
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
Expand Down Expand Up @@ -143,4 +162,16 @@ spec:
qwenEnableCompatible: true
ingress:
- higress-conformance-ai-backend/wasmplugin-ai-proxy-qwen-compatible-mode
url: oci://higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/ai-proxy:1.0.0
- config:
provider:
apiTokens:
- fake_token
modelMapping:
'gpt-3': qwen-turbo
'gpt-35-turbo': qwen-plus
'gpt-4-*': qwen-max
'*': qwen-turbo
type: qwen
ingress:
- higress-conformance-ai-backend/wasmplugin-ai-proxy-qwen
url: file:///opt/plugins/wasm-go/extensions/ai-proxy/plugin.wasm
54 changes: 48 additions & 6 deletions test/e2e/conformance/utils/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,12 @@ type ExpectedRequest struct {

// Response defines expected properties of a response from a backend.
type Response struct {
StatusCode int
Headers map[string]string
Body []byte
ContentType string
AbsentHeaders []string
StatusCode int
Headers map[string]string
Body []byte
JsonBodyIgnoreFields []string
ContentType string
AbsentHeaders []string
}

// requiredConsecutiveSuccesses is the number of requests that must succeed in a row
Expand Down Expand Up @@ -618,7 +619,7 @@ func CompareResponse(cRes *roundtripper.CapturedResponse, expected Assertion) er
return fmt.Errorf("failed to unmarshall CapturedResponse body %s, %s", string(cRes.Body), err.Error())
}

if !reflect.DeepEqual(eResBody, cResBody) {
if err := CompareJSONWithIgnoreFields(eResBody, cResBody, expected.Response.ExpectedResponse.JsonBodyIgnoreFields); err != nil {
return fmt.Errorf("expected %s body to be %s, got %s", cTyp, string(expected.Response.ExpectedResponse.Body), string(cRes.Body))
}
case ContentTypeFormUrlencoded:
Expand Down Expand Up @@ -665,6 +666,47 @@ func CompareResponse(cRes *roundtripper.CapturedResponse, expected Assertion) er
}
return nil
}

// CompareJSONWithIgnoreFields compares two JSON objects, ignoring specified fields
func CompareJSONWithIgnoreFields(eResBody, cResBody map[string]interface{}, ignoreFields []string) error {
for key, eVal := range eResBody {
if contains(ignoreFields, key) {
continue
}

cVal, exists := cResBody[key]
if !exists {
return fmt.Errorf("field %s exists in expected response but not in captured response", key)
}

if !reflect.DeepEqual(eVal, cVal) {
return fmt.Errorf("field %s mismatch: expected %v, got %v", key, eVal, cVal)
}
}

// Check if captured response has extra fields (excluding ignored fields)
for key := range cResBody {
if contains(ignoreFields, key) {
continue
}

if _, exists := eResBody[key]; !exists {
return fmt.Errorf("field %s exists in captured response but not in expected response", key)
}
}

return nil
}

func contains(slice []string, str string) bool {
for _, s := range slice {
if s == str {
return true
}
}
return false
}

func ParseFormUrlencodedBody(body []byte) (map[string][]string, error) {
ret := make(map[string][]string)
kvs, err := url.ParseQuery(string(body))
Expand Down

0 comments on commit 8aa8fa1

Please # to comment.