feat: add Responses API compatibility

This commit is contained in:
2026-07-10 23:33:15 +08:00
parent b7351f3b9b
commit 8847d973a8
24 changed files with 5922 additions and 169 deletions
+25 -2
View File
@@ -965,8 +965,6 @@ func (s *Server) listModelRateLimitStatuses(w http.ResponseWriter, r *http.Reque
// @Router /api/v1/voice_clone [post]
// @Router /chat/completions [post]
// @Router /v1/chat/completions [post]
// @Router /responses [post]
// @Router /v1/responses [post]
// @Router /embeddings [post]
// @Router /v1/embeddings [post]
// @Router /reranks [post]
@@ -1085,6 +1083,26 @@ func (s *Server) createAPIV1ChatCompletions() http.Handler {
return s.createTask("chat.completions", false)
}
// openAIResponsesDoc godoc
// @Summary 创建 OpenAI Responses
// @Description 公开 OpenAI-compatible Responses 入口。模型声明 openai_responses 时原生转发,否则使用 Chat Completions 转换;store 缺省为 true。previous_response_id 严格绑定首次成功的平台模型和上游协议,链路不可用时不跨平台续接。未提供 previous_response_id 时由调用方管理完整状态,Gateway 以本轮 input/messages 为准且不追加本地历史。
// @Tags responses
// @Accept json
// @Produce json
// @Produce text/event-stream
// @Security BearerAuth
// @Param input body ResponsesRequest true "Responses 请求;Chat 回退只支持自定义 function tools"
// @Success 200 {object} ResponsesCompatibleResponse
// @Header 200 {string} X-Gateway-Task-Id "网关审计任务 ID"
// @Failure 400 {object} ErrorEnvelope "invalid_previous_response_id / unsupported_response_tool / unsupported_response_parameter"
// @Failure 401 {object} ErrorEnvelope
// @Failure 402 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 503 {object} ErrorEnvelope "response_chain_unavailable"
// @Router /responses [post]
// @Router /v1/responses [post]
func openAIResponsesDoc() {}
func (s *Server) requestExecutionContext(r *http.Request) (context.Context, context.CancelFunc) {
base := context.WithoutCancel(r.Context())
if s.ctx == nil {
@@ -1116,6 +1134,7 @@ type taskExecutor interface {
}
func writeCompatibleTaskResponse(runCtx context.Context, w http.ResponseWriter, r *http.Request, executor taskExecutor, kind string, model string, task store.GatewayTask, user *auth.User, streamMode bool, includeUsage bool) {
w.Header().Set("X-Gateway-Task-Id", task.ID)
if streamMode {
flusher := prepareCompatibleStream(w)
streamWriter := newCompatibleStreamWriter(kind, model, includeUsage)
@@ -1306,6 +1325,10 @@ func scopeForTaskKind(kind string) string {
func statusFromRunError(err error) int {
switch {
case clients.ErrorCode(err) == "invalid_previous_response_id" || clients.ErrorCode(err) == "response_chain_too_deep" || clients.ErrorCode(err) == "unsupported_model_protocol" || clients.ErrorCode(err) == "unsupported_response_tool" || clients.ErrorCode(err) == "unsupported_response_parameter":
return http.StatusBadRequest
case clients.ErrorCode(err) == "response_chain_unavailable":
return http.StatusServiceUnavailable
case clients.ErrorCode(err) == "bad_request" || clients.ErrorCode(err) == "invalid_parameter" || clients.ErrorCode(err) == "cloned_voice_expired" || clients.ErrorCode(err) == "cloned_voice_unavailable" || clients.ErrorCode(err) == "cloned_voice_platform_unavailable" || clients.ErrorCode(err) == "unsupported_operation" || clients.ErrorCode(err) == "invalid_proxy":
return http.StatusBadRequest
case clients.ErrorCode(err) == "cloned_voice_not_found":