mirror of
https://github.com/alibaba/higress.git
synced 2026-05-21 03:07:27 +08:00
fix(vertex): inject api key for express raw endpoints (#3777)
Signed-off-by: wydream <yaodiwu618@gmail.com> Co-authored-by: EndlessSeeker <153817598+EndlessSeeker@users.noreply.github.com>
This commit is contained in:
@@ -56,6 +56,10 @@ const (
|
||||
// 允许任意 basePath 前缀,兼容 basePathHandling 配置
|
||||
var vertexRawPathRegex = regexp.MustCompile(`^.*/([^/]+)/projects/([^/]+)/locations/([^/]+)/publishers/([^/]+)/models/([^/:]+):([^/?]+)`)
|
||||
|
||||
// vertexExpressRawPathRegex 匹配 Vertex AI Express Mode 专用 REST API 路径
|
||||
// 格式: [任意前缀]/{api-version}/publishers/{publisher}/models/{model}:{action}
|
||||
var vertexExpressRawPathRegex = regexp.MustCompile(`^.*/(v[^/]+)/publishers/([^/]+)/models/([^/:]+):([^/?]+)`)
|
||||
|
||||
type vertexProviderInitializer struct{}
|
||||
|
||||
func (v *vertexProviderInitializer) ValidateConfig(config *ProviderConfig) error {
|
||||
@@ -158,7 +162,7 @@ func (v *vertexProvider) GetApiName(path string) ApiName {
|
||||
// 优先匹配原生 Vertex AI REST API 路径,支持任意 basePath 前缀
|
||||
// 格式: [任意前缀]/{api-version}/projects/{project}/locations/{location}/publishers/{publisher}/models/{model}:{action}
|
||||
// 必须在其他 action 检查之前,因为 :predict、:generateContent 等 action 会被其他规则匹配
|
||||
if vertexRawPathRegex.MatchString(path) {
|
||||
if vertexRawPathRegex.MatchString(path) || (v.isExpressMode() && vertexExpressRawPathRegex.MatchString(path)) {
|
||||
return ApiNameVertexRaw
|
||||
}
|
||||
if strings.HasSuffix(path, vertexChatCompletionAction) || strings.HasSuffix(path, vertexChatCompletionStreamAction) {
|
||||
|
||||
@@ -2280,6 +2280,27 @@ func RunVertexRawModeOnHttpRequestHeadersTests(t *testing.T) {
|
||||
"Host header should be changed to vertex domain without region prefix")
|
||||
})
|
||||
|
||||
// 测试 Vertex Raw 模式请求头处理(Express Mode 专用路径,无 project/location)
|
||||
t.Run("vertex raw mode express - request headers with express endpoint path", func(t *testing.T) {
|
||||
host, status := test.NewTestHost(vertexRawModeExpressConfig)
|
||||
defer host.Reset()
|
||||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||||
|
||||
action := host.CallOnHttpRequestHeaders([][2]string{
|
||||
{":authority", "example.com"},
|
||||
{":path", "/v1/publishers/google/models/gemini-3.1-flash-lite-preview:streamGenerateContent"},
|
||||
{":method", "POST"},
|
||||
{"Content-Type", "application/json"},
|
||||
})
|
||||
|
||||
require.Equal(t, types.HeaderStopIteration, action)
|
||||
|
||||
requestHeaders := host.GetRequestHeaders()
|
||||
require.NotNil(t, requestHeaders)
|
||||
require.True(t, test.HasHeaderWithValue(requestHeaders, ":authority", "aiplatform.googleapis.com"),
|
||||
"Host header should be changed to vertex domain without region prefix")
|
||||
})
|
||||
|
||||
// 测试 Vertex Raw 模式请求头处理(标准模式 + 原生 Vertex API 路径)
|
||||
t.Run("vertex raw mode standard - request headers with native vertex path", func(t *testing.T) {
|
||||
host, status := test.NewTestHost(vertexRawModeStandardConfig)
|
||||
@@ -2416,6 +2437,43 @@ func RunVertexRawModeOnHttpRequestBodyTests(t *testing.T) {
|
||||
"Authorization header should be removed in Express Mode")
|
||||
})
|
||||
|
||||
// 测试 Vertex Raw 模式请求体处理(Express Mode 专用路径 + API Key 认证)
|
||||
t.Run("vertex raw mode express - express endpoint request body with api key", func(t *testing.T) {
|
||||
host, status := test.NewTestHost(vertexRawModeExpressConfig)
|
||||
defer host.Reset()
|
||||
require.Equal(t, types.OnPluginStartStatusOK, status)
|
||||
|
||||
host.CallOnHttpRequestHeaders([][2]string{
|
||||
{":authority", "example.com"},
|
||||
{":path", "/v1/publishers/google/models/gemini-3.1-flash-lite-preview:streamGenerateContent"},
|
||||
{":method", "POST"},
|
||||
{"Content-Type", "application/json"},
|
||||
{"Authorization", "Bearer some-token"},
|
||||
})
|
||||
|
||||
requestBody := `{"contents":[{"role":"user","parts":[{"text":"Tell me a story"}]}]}`
|
||||
action := host.CallOnHttpRequestBody([]byte(requestBody))
|
||||
|
||||
require.Equal(t, types.ActionContinue, action)
|
||||
|
||||
processedBody := host.GetRequestBody()
|
||||
require.NotNil(t, processedBody)
|
||||
require.Equal(t, requestBody, string(processedBody), "Request body should be passed through unchanged")
|
||||
|
||||
requestHeaders := host.GetRequestHeaders()
|
||||
var pathHeader string
|
||||
for _, header := range requestHeaders {
|
||||
if header[0] == ":path" {
|
||||
pathHeader = header[1]
|
||||
break
|
||||
}
|
||||
}
|
||||
require.Equal(t, "/v1/publishers/google/models/gemini-3.1-flash-lite-preview:streamGenerateContent?key=test-api-key-for-raw-mode", pathHeader,
|
||||
"API key should be appended to express endpoint path as query parameter")
|
||||
require.False(t, test.HasHeaderWithValue(requestHeaders, "Authorization", "Bearer some-token"),
|
||||
"Authorization header should be removed in Express Mode")
|
||||
})
|
||||
|
||||
// 测试 Vertex Raw 模式请求体处理(标准模式 - 需要 OAuth token)
|
||||
// 注意:使用 countTokens action,因为 generateContent/predict 等会被识别为其他 API 类型
|
||||
// 注意:在单元测试环境中,由于测试配置使用的是无效的私钥,JWT 创建会失败,
|
||||
|
||||
Reference in New Issue
Block a user