Skip to content

Commit 623c8da

Browse files
authored
fix(ai-proxy): Fix Azure OpenAI Response API handling and service URL type detection (#2948)
1 parent e2d00da commit 623c8da

File tree

3 files changed

+95
-3
lines changed

3 files changed

+95
-3
lines changed

plugins/wasm-go/extensions/ai-proxy/provider/azure.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ var (
3838
ApiNameFiles: true,
3939
ApiNameRetrieveFile: true,
4040
ApiNameRetrieveFileContent: true,
41+
ApiNameResponses: true,
4142
}
4243
regexAzureModelWithPath = regexp.MustCompile("/openai/deployments/(.+?)(?:/(.*)|$)")
4344
)
@@ -100,8 +101,15 @@ func (m *azureProviderInitializer) CreateProvider(config ProviderConfig) (Provid
100101
}
101102
log.Debugf("azureProvider: found default model from serviceUrl: %s", defaultModel)
102103
} else {
103-
serviceUrlType = azureServiceUrlTypeDomainOnly
104-
log.Debugf("azureProvider: no default model found in serviceUrl")
104+
// If path doesn't match the /openai/deployments pattern,
105+
// check if it's a custom full path or domain only
106+
if serviceUrl.Path != "" && serviceUrl.Path != "/" {
107+
serviceUrlType = azureServiceUrlTypeFull
108+
log.Debugf("azureProvider: using custom full path: %s", serviceUrl.Path)
109+
} else {
110+
serviceUrlType = azureServiceUrlTypeDomainOnly
111+
log.Debugf("azureProvider: no default model found in serviceUrl")
112+
}
105113
}
106114
log.Debugf("azureProvider: serviceUrlType=%d", serviceUrlType)
107115

plugins/wasm-go/extensions/ai-proxy/provider/provider.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,7 @@ func ExtractStreamingEvents(ctx wrapper.HttpContext, chunk []byte) []StreamEvent
825825
continue
826826
}
827827

828-
if lineStartIndex != -1 {
828+
if lineStartIndex != -1 && valueStartIndex != -1 {
829829
value := string(body[valueStartIndex:i])
830830
currentEvent.SetValue(currentKey, value)
831831
} else {

plugins/wasm-go/extensions/ai-proxy/test/azure.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,20 @@ var azureInvalidConfigMissingToken = func() json.RawMessage {
146146
return data
147147
}()
148148

149+
// 测试配置:Azure OpenAI Response API配置
150+
var azureResponseAPIConfig = func() json.RawMessage {
151+
data, _ := json.Marshal(map[string]interface{}{
152+
"provider": map[string]interface{}{
153+
"type": "azure",
154+
"apiTokens": []string{
155+
"sk-azure-multi",
156+
},
157+
"azureServiceUrl": "https://multi-resource.openai.azure.com/openai/responses?api-version=2025-04-01-preview",
158+
},
159+
})
160+
return data
161+
}()
162+
149163
func RunAzureParseConfigTests(t *testing.T) {
150164
test.RunGoTest(t, func(t *testing.T) {
151165
// 测试基本Azure OpenAI配置解析
@@ -203,6 +217,17 @@ func RunAzureParseConfigTests(t *testing.T) {
203217
require.NotNil(t, config)
204218
})
205219

220+
// 测试Azure Response API 配置解析
221+
t.Run("azure response api config", func(t *testing.T) {
222+
host, status := test.NewTestHost(azureResponseAPIConfig)
223+
defer host.Reset()
224+
require.Equal(t, types.OnPluginStartStatusOK, status)
225+
226+
config, err := host.GetMatchConfig()
227+
require.NoError(t, err)
228+
require.NotNil(t, config)
229+
})
230+
206231
// 测试Azure OpenAI无效配置(缺少azureServiceUrl)
207232
t.Run("azure invalid config missing url", func(t *testing.T) {
208233
host, status := test.NewTestHost(azureInvalidConfigMissingUrl)
@@ -411,6 +436,61 @@ func RunAzureOnHttpRequestBodyTests(t *testing.T) {
411436
require.Equal(t, "gpt-4", model, "Model should be mapped correctly")
412437
})
413438

439+
// 测试Azure OpenAI Response API 处理
440+
t.Run("azure response api request body", func(t *testing.T) {
441+
host, status := test.NewTestHost(azureResponseAPIConfig)
442+
defer host.Reset()
443+
require.Equal(t, types.OnPluginStartStatusOK, status)
444+
445+
// 设置请求头
446+
action := host.CallOnHttpRequestHeaders([][2]string{
447+
{":authority", "example.com"},
448+
{":path", "/responses/v1/responses"},
449+
{":method", "POST"},
450+
{"Content-Type", "application/json"},
451+
})
452+
require.Equal(t, types.HeaderStopIteration, action)
453+
454+
// 设置请求体
455+
requestBody := `{
456+
"input": [
457+
{
458+
"role": "user",
459+
"content": [
460+
{
461+
"type": "input_text",
462+
"text": "Explain quantum computing"
463+
}
464+
]
465+
}
466+
],
467+
"model": "gpt-5",
468+
"reasoning": {
469+
"effort": "medium"
470+
}
471+
}`
472+
action = host.CallOnHttpRequestBody([]byte(requestBody))
473+
require.Equal(t, types.ActionContinue, action)
474+
475+
// 验证请求体是否被正确处理
476+
transformedBody := host.GetRequestBody()
477+
require.NotNil(t, transformedBody)
478+
479+
var bodyMap map[string]interface{}
480+
err := json.Unmarshal(transformedBody, &bodyMap)
481+
require.NoError(t, err)
482+
483+
model, exists := bodyMap["model"]
484+
require.True(t, exists, "Model should exist in request body")
485+
require.Equal(t, "gpt-5", model, "Model should be mapped correctly")
486+
487+
// 验证请求路径是否被正确转换
488+
requestHeaders := host.GetRequestHeaders()
489+
pathValue, hasPath := test.GetHeaderValue(requestHeaders, ":path")
490+
require.True(t, hasPath, "Path header should exist")
491+
require.Equal(t, pathValue, "/openai/responses?api-version=2025-04-01-preview", "Path should not equal Azure response api path")
492+
})
493+
414494
// 测试Azure OpenAI请求体处理(仅部署配置)
415495
t.Run("azure deployment only request body", func(t *testing.T) {
416496
host, status := test.NewTestHost(azureDeploymentOnlyConfig)
@@ -566,6 +646,10 @@ func RunAzureOnHttpResponseBodyTests(t *testing.T) {
566646
}
567647
]
568648
}`
649+
action = host.CallOnHttpResponseHeaders([][2]string{
650+
{"Content-Type", "application/json"},
651+
})
652+
require.Equal(t, types.ActionContinue, action)
569653
action = host.CallOnHttpRequestBody([]byte(requestBody))
570654
require.Equal(t, types.ActionContinue, action)
571655

0 commit comments

Comments
 (0)