package httpapi import ( "encoding/json" "fmt" "net/http" "strings" "github.com/easyai/easyai-ai-gateway/apps/api/internal/publicerror" ) const maxAsyncMediaResultResponseBytes = 64 << 10 func writeJSON(w http.ResponseWriter, status int, value any) { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(value) } func writeAsyncMediaResultJSON(w http.ResponseWriter, value any, observer interface { ObserveResultDelivery(string, int64) }) { payload, err := json.Marshal(value) if err != nil { writeError(w, http.StatusInternalServerError, "encode task result failed", "internal_error") return } if len(payload) > maxAsyncMediaResultResponseBytes { if observer != nil { observer.ObserveResultDelivery("poll_oversized", int64(len(payload))) } writeErrorWithDetails(w, http.StatusInternalServerError, "task result response exceeds the URL-only size limit", map[string]any{ "limit_bytes": maxAsyncMediaResultResponseBytes, "actual_bytes": len(payload), }, "result_response_too_large") return } w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("X-Gateway-Response-Format", "url") w.Header().Set("Deprecation", "true") w.Header().Set("X-Gateway-Deprecated-Fields", "output_content,result") w.WriteHeader(http.StatusOK) _, _ = w.Write(append(payload, '\n')) if observer != nil { observer.ObserveResultDelivery("poll_success", int64(len(payload)+1)) } } func writeError(w http.ResponseWriter, status int, message string, codes ...string) { writeErrorWithDetails(w, status, message, nil, codes...) } func writeErrorWithDetails(w http.ResponseWriter, status int, message string, details map[string]any, codes ...string) { code := "" if len(codes) > 0 { code = strings.TrimSpace(codes[0]) } standard := publicerror.FromFields(code, message, status, status == http.StatusRequestTimeout || status == http.StatusTooManyRequests || status >= 500) standard = publicErrorWithRetryAfter(standard, details) publicerror.Observe(standard) status = standard.HTTPStatus errorPayload := map[string]any{ "message": standard.Message, "status": status, "code": standard.Code, "category": standard.Category, "source": standard.Source, "httpStatus": standard.HTTPStatus, "retryable": standard.Retryable, "action": standard.Action, "version": standard.Version, } if standard.Code == "invalid_parameter" || standard.Code == "unsupported_response_parameter" { errorPayload["type"] = "invalid_request_error" if _, ok := details["param"]; !ok { errorPayload["param"] = nil } } for key, value := range safePublicErrorDetails(details, standard, false) { errorPayload[key] = value } writeJSON(w, status, map[string]any{"error": errorPayload}) } func writeLocalUserRequired(w http.ResponseWriter) { writeError(w, http.StatusForbidden, "该账号尚未开通 EasyAI Gateway", errorCodeGatewayUserNotProvisioned) } func sendSSE(w http.ResponseWriter, event string, payload any) { bytes, _ := json.Marshal(payload) _, _ = fmt.Fprintf(w, "event: %s\n", event) _, _ = fmt.Fprintf(w, "data: %s\n\n", bytes) }