fix: reject http responses continuation ids
This commit is contained in:
@@ -187,6 +187,11 @@ func (h *OpenAIGatewayHandler) Responses(c *gin.Context) {
|
|||||||
h.errorResponse(c, http.StatusBadRequest, "invalid_request_error", "previous_response_id must be a response.id (resp_*), not a message id")
|
h.errorResponse(c, http.StatusBadRequest, "invalid_request_error", "previous_response_id must be a response.id (resp_*), not a message id")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
reqLog.Warn("openai.request_validation_failed",
|
||||||
|
zap.String("reason", "previous_response_id_requires_wsv2"),
|
||||||
|
)
|
||||||
|
h.errorResponse(c, http.StatusBadRequest, "invalid_request_error", "previous_response_id is only supported on Responses WebSocket v2")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setOpsRequestContext(c, reqModel, reqStream, body)
|
setOpsRequestContext(c, reqModel, reqStream, body)
|
||||||
@@ -856,7 +861,7 @@ func (h *OpenAIGatewayHandler) validateFunctionCallOutputRequest(c *gin.Context,
|
|||||||
reqLog.Warn("openai.request_validation_failed",
|
reqLog.Warn("openai.request_validation_failed",
|
||||||
zap.String("reason", "function_call_output_missing_call_id"),
|
zap.String("reason", "function_call_output_missing_call_id"),
|
||||||
)
|
)
|
||||||
h.errorResponse(c, http.StatusBadRequest, "invalid_request_error", "function_call_output requires call_id or previous_response_id; if relying on history, ensure store=true and reuse previous_response_id")
|
h.errorResponse(c, http.StatusBadRequest, "invalid_request_error", "function_call_output requires call_id on HTTP requests; continuation via previous_response_id is only supported on Responses WebSocket v2")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if validation.HasItemReferenceForAllCallIDs {
|
if validation.HasItemReferenceForAllCallIDs {
|
||||||
@@ -866,7 +871,7 @@ func (h *OpenAIGatewayHandler) validateFunctionCallOutputRequest(c *gin.Context,
|
|||||||
reqLog.Warn("openai.request_validation_failed",
|
reqLog.Warn("openai.request_validation_failed",
|
||||||
zap.String("reason", "function_call_output_missing_item_reference"),
|
zap.String("reason", "function_call_output_missing_item_reference"),
|
||||||
)
|
)
|
||||||
h.errorResponse(c, http.StatusBadRequest, "invalid_request_error", "function_call_output requires item_reference ids matching each call_id, or previous_response_id/tool_call context; if relying on history, ensure store=true and reuse previous_response_id")
|
h.errorResponse(c, http.StatusBadRequest, "invalid_request_error", "function_call_output requires item_reference ids matching each call_id on HTTP requests; continuation via previous_response_id is only supported on Responses WebSocket v2")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -494,6 +494,64 @@ func TestOpenAIResponses_RejectsMessageIDAsPreviousResponseID(t *testing.T) {
|
|||||||
require.Contains(t, w.Body.String(), "previous_response_id must be a response.id")
|
require.Contains(t, w.Body.String(), "previous_response_id must be a response.id")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOpenAIResponses_RejectsHTTPContinuationPreviousResponseID(t *testing.T) {
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
c, _ := gin.CreateTestContext(w)
|
||||||
|
c.Request = httptest.NewRequest(http.MethodPost, "/openai/v1/responses", strings.NewReader(
|
||||||
|
`{"model":"gpt-5.1","stream":false,"previous_response_id":"resp_123456","input":[{"type":"input_text","text":"hello"}]}`,
|
||||||
|
))
|
||||||
|
c.Request.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
groupID := int64(2)
|
||||||
|
c.Set(string(middleware.ContextKeyAPIKey), &service.APIKey{
|
||||||
|
ID: 101,
|
||||||
|
GroupID: &groupID,
|
||||||
|
User: &service.User{ID: 1},
|
||||||
|
})
|
||||||
|
c.Set(string(middleware.ContextKeyUser), middleware.AuthSubject{
|
||||||
|
UserID: 1,
|
||||||
|
Concurrency: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
h := newOpenAIHandlerForPreviousResponseIDValidation(t, nil)
|
||||||
|
h.Responses(c)
|
||||||
|
|
||||||
|
require.Equal(t, http.StatusBadRequest, w.Code)
|
||||||
|
require.Contains(t, w.Body.String(), "Responses WebSocket v2")
|
||||||
|
require.Contains(t, w.Body.String(), "previous_response_id")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOpenAIResponses_FunctionCallOutputHTTPGuidanceDoesNotSuggestPreviousResponseReuse(t *testing.T) {
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
c, _ := gin.CreateTestContext(w)
|
||||||
|
c.Request = httptest.NewRequest(http.MethodPost, "/openai/v1/responses", strings.NewReader(
|
||||||
|
`{"model":"gpt-5.1","stream":false,"input":[{"type":"function_call_output","output":"{}"}]}`,
|
||||||
|
))
|
||||||
|
c.Request.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
groupID := int64(2)
|
||||||
|
c.Set(string(middleware.ContextKeyAPIKey), &service.APIKey{
|
||||||
|
ID: 101,
|
||||||
|
GroupID: &groupID,
|
||||||
|
User: &service.User{ID: 1},
|
||||||
|
})
|
||||||
|
c.Set(string(middleware.ContextKeyUser), middleware.AuthSubject{
|
||||||
|
UserID: 1,
|
||||||
|
Concurrency: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
h := newOpenAIHandlerForPreviousResponseIDValidation(t, nil)
|
||||||
|
h.Responses(c)
|
||||||
|
|
||||||
|
require.Equal(t, http.StatusBadRequest, w.Code)
|
||||||
|
require.Contains(t, w.Body.String(), "Responses WebSocket v2")
|
||||||
|
require.NotContains(t, w.Body.String(), "reuse previous_response_id")
|
||||||
|
}
|
||||||
|
|
||||||
func TestOpenAIResponsesWebSocket_SetsClientTransportWSWhenUpgradeValid(t *testing.T) {
|
func TestOpenAIResponsesWebSocket_SetsClientTransportWSWhenUpgradeValid(t *testing.T) {
|
||||||
gin.SetMode(gin.TestMode)
|
gin.SetMode(gin.TestMode)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user