mirror of
https://github.com/alibaba/higress.git
synced 2026-05-11 22:37:32 +08:00
259 lines
7.3 KiB
Go
259 lines
7.3 KiB
Go
package provider
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/alibaba/higress/plugins/wasm-go/extensions/ai-proxy/util"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestVertexProviderBuildChatRequestStructuredOutputMapping(t *testing.T) {
|
|
t.Run("json_object response format", func(t *testing.T) {
|
|
v := &vertexProvider{}
|
|
req := &chatCompletionRequest{
|
|
Model: "gemini-2.5-flash",
|
|
Messages: []chatMessage{
|
|
{Role: roleUser, Content: "hello"},
|
|
},
|
|
ResponseFormat: map[string]interface{}{
|
|
"type": "json_object",
|
|
},
|
|
}
|
|
|
|
vertexReq, err := v.buildVertexChatRequest(req)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, vertexReq)
|
|
|
|
assert.Equal(t, util.MimeTypeApplicationJson, vertexReq.GenerationConfig.ResponseMimeType)
|
|
assert.Nil(t, vertexReq.GenerationConfig.ResponseSchema)
|
|
})
|
|
|
|
t.Run("json_schema response format with nested schema", func(t *testing.T) {
|
|
v := &vertexProvider{}
|
|
schema := map[string]interface{}{
|
|
"type": "object",
|
|
"properties": map[string]interface{}{
|
|
"answer": map[string]interface{}{
|
|
"type": "string",
|
|
},
|
|
},
|
|
"required": []interface{}{"answer"},
|
|
}
|
|
req := &chatCompletionRequest{
|
|
Model: "gemini-2.5-flash",
|
|
Messages: []chatMessage{
|
|
{Role: roleUser, Content: "hello"},
|
|
},
|
|
ResponseFormat: map[string]interface{}{
|
|
"type": "json_schema",
|
|
"json_schema": map[string]interface{}{
|
|
"name": "response",
|
|
"strict": true,
|
|
"schema": schema,
|
|
},
|
|
},
|
|
}
|
|
|
|
vertexReq, err := v.buildVertexChatRequest(req)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, vertexReq)
|
|
|
|
assert.Equal(t, util.MimeTypeApplicationJson, vertexReq.GenerationConfig.ResponseMimeType)
|
|
assert.Equal(t, schema, vertexReq.GenerationConfig.ResponseSchema)
|
|
})
|
|
|
|
t.Run("json_schema response format with direct schema object", func(t *testing.T) {
|
|
v := &vertexProvider{}
|
|
schema := map[string]interface{}{
|
|
"type": "object",
|
|
"properties": map[string]interface{}{
|
|
"city": map[string]interface{}{
|
|
"type": "string",
|
|
},
|
|
},
|
|
"required": []interface{}{"city"},
|
|
}
|
|
req := &chatCompletionRequest{
|
|
Model: "gemini-2.5-flash",
|
|
Messages: []chatMessage{
|
|
{Role: roleUser, Content: "hello"},
|
|
},
|
|
ResponseFormat: map[string]interface{}{
|
|
"type": "json_schema",
|
|
"json_schema": schema,
|
|
},
|
|
}
|
|
|
|
vertexReq, err := v.buildVertexChatRequest(req)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, vertexReq)
|
|
|
|
assert.Equal(t, util.MimeTypeApplicationJson, vertexReq.GenerationConfig.ResponseMimeType)
|
|
assert.Equal(t, schema, vertexReq.GenerationConfig.ResponseSchema)
|
|
})
|
|
|
|
t.Run("json_schema response format without valid schema should return error", func(t *testing.T) {
|
|
v := &vertexProvider{}
|
|
req := &chatCompletionRequest{
|
|
Model: "gemini-2.5-flash",
|
|
Messages: []chatMessage{
|
|
{Role: roleUser, Content: "hello"},
|
|
},
|
|
ResponseFormat: map[string]interface{}{
|
|
"type": "json_schema",
|
|
"json_schema": "invalid",
|
|
},
|
|
}
|
|
|
|
vertexReq, err := v.buildVertexChatRequest(req)
|
|
require.Error(t, err)
|
|
assert.Nil(t, vertexReq)
|
|
assert.Contains(t, err.Error(), "invalid response_format.json_schema")
|
|
})
|
|
|
|
t.Run("direct schema in response_format for compatibility", func(t *testing.T) {
|
|
v := &vertexProvider{}
|
|
schema := map[string]interface{}{
|
|
"type": "object",
|
|
"properties": map[string]interface{}{
|
|
"result": map[string]interface{}{
|
|
"type": "string",
|
|
},
|
|
},
|
|
}
|
|
req := &chatCompletionRequest{
|
|
Model: "gemini-2.5-flash",
|
|
Messages: []chatMessage{
|
|
{Role: roleUser, Content: "hello"},
|
|
},
|
|
ResponseFormat: schema,
|
|
}
|
|
|
|
vertexReq, err := v.buildVertexChatRequest(req)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, vertexReq)
|
|
|
|
assert.Equal(t, util.MimeTypeApplicationJson, vertexReq.GenerationConfig.ResponseMimeType)
|
|
assert.Equal(t, schema, vertexReq.GenerationConfig.ResponseSchema)
|
|
})
|
|
|
|
t.Run("text response format keeps default text output", func(t *testing.T) {
|
|
v := &vertexProvider{}
|
|
req := &chatCompletionRequest{
|
|
Model: "gemini-2.5-flash",
|
|
Messages: []chatMessage{
|
|
{Role: roleUser, Content: "hello"},
|
|
},
|
|
ResponseFormat: map[string]interface{}{
|
|
"type": "text",
|
|
},
|
|
}
|
|
|
|
vertexReq, err := v.buildVertexChatRequest(req)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, vertexReq)
|
|
|
|
assert.Empty(t, vertexReq.GenerationConfig.ResponseMimeType)
|
|
assert.Nil(t, vertexReq.GenerationConfig.ResponseSchema)
|
|
})
|
|
|
|
t.Run("unknown response format does not inject schema config", func(t *testing.T) {
|
|
v := &vertexProvider{}
|
|
req := &chatCompletionRequest{
|
|
Model: "gemini-2.5-flash",
|
|
Messages: []chatMessage{
|
|
{Role: roleUser, Content: "hello"},
|
|
},
|
|
ResponseFormat: map[string]interface{}{
|
|
"type": "xml",
|
|
},
|
|
}
|
|
|
|
vertexReq, err := v.buildVertexChatRequest(req)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, vertexReq)
|
|
|
|
assert.Empty(t, vertexReq.GenerationConfig.ResponseMimeType)
|
|
assert.Nil(t, vertexReq.GenerationConfig.ResponseSchema)
|
|
})
|
|
|
|
t.Run("gemini 2.0 json_schema is ignored for stability", func(t *testing.T) {
|
|
v := &vertexProvider{}
|
|
schema := map[string]interface{}{
|
|
"type": "object",
|
|
"properties": map[string]interface{}{
|
|
"answer": map[string]interface{}{
|
|
"type": "string",
|
|
},
|
|
},
|
|
}
|
|
req := &chatCompletionRequest{
|
|
Model: "gemini-2.0-flash",
|
|
Messages: []chatMessage{
|
|
{Role: roleUser, Content: "hello"},
|
|
},
|
|
ResponseFormat: map[string]interface{}{
|
|
"type": "json_schema",
|
|
"json_schema": map[string]interface{}{
|
|
"name": "response",
|
|
"strict": true,
|
|
"schema": schema,
|
|
},
|
|
},
|
|
}
|
|
|
|
vertexReq, err := v.buildVertexChatRequest(req)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, vertexReq)
|
|
assert.Empty(t, vertexReq.GenerationConfig.ResponseMimeType)
|
|
assert.Nil(t, vertexReq.GenerationConfig.ResponseSchema)
|
|
})
|
|
|
|
t.Run("gemini 2.0 malformed json_schema is also ignored", func(t *testing.T) {
|
|
v := &vertexProvider{}
|
|
req := &chatCompletionRequest{
|
|
Model: "gemini-2.0-flash",
|
|
Messages: []chatMessage{
|
|
{Role: roleUser, Content: "hello"},
|
|
},
|
|
ResponseFormat: map[string]interface{}{
|
|
"type": "json_schema",
|
|
"json_schema": "invalid",
|
|
},
|
|
}
|
|
|
|
vertexReq, err := v.buildVertexChatRequest(req)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, vertexReq)
|
|
assert.Empty(t, vertexReq.GenerationConfig.ResponseMimeType)
|
|
assert.Nil(t, vertexReq.GenerationConfig.ResponseSchema)
|
|
})
|
|
|
|
t.Run("gemini 2.0 json_object is ignored", func(t *testing.T) {
|
|
v := &vertexProvider{}
|
|
req := &chatCompletionRequest{
|
|
Model: "gemini-2.0-flash",
|
|
Messages: []chatMessage{
|
|
{Role: roleUser, Content: "hello"},
|
|
},
|
|
ResponseFormat: map[string]interface{}{
|
|
"type": "json_object",
|
|
},
|
|
}
|
|
|
|
vertexReq, err := v.buildVertexChatRequest(req)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, vertexReq)
|
|
assert.Empty(t, vertexReq.GenerationConfig.ResponseMimeType)
|
|
assert.Nil(t, vertexReq.GenerationConfig.ResponseSchema)
|
|
})
|
|
}
|
|
|
|
func TestVertexProviderApplyResponseFormatNilSafety(t *testing.T) {
|
|
v := &vertexProvider{}
|
|
require.NoError(t, v.applyResponseFormatToGenerationConfig(map[string]interface{}{"type": "json_object"}, nil, "gemini-2.5-flash"))
|
|
require.NoError(t, v.applyResponseFormatToGenerationConfig(nil, &vertexChatGenerationConfig{}, "gemini-2.5-flash"))
|
|
require.NoError(t, v.applyResponseFormatToGenerationConfig(map[string]interface{}{}, &vertexChatGenerationConfig{}, "gemini-2.5-flash"))
|
|
}
|