package handler import ( "bytes" "encoding/json" "io" "mime" "mime/multipart" "net/http" "strings" "github.com/basketikun/infinite-canvas/service" ) func AIImagesGenerations(w http.ResponseWriter, r *http.Request) { proxyAIRequest(w, r, "/images/generations") } func AIImagesEdits(w http.ResponseWriter, r *http.Request) { proxyAIRequest(w, r, "/images/edits") } func AIChatCompletions(w http.ResponseWriter, r *http.Request) { proxyAIRequest(w, r, "/chat/completions") } func proxyAIRequest(w http.ResponseWriter, r *http.Request, path string) { body, contentType, modelName, err := readAIRequest(r) if err != nil { Fail(w, err.Error()) return } channel, err := service.SelectModelChannel(modelName) if err != nil { Fail(w, err.Error()) return } request, err := http.NewRequest(http.MethodPost, service.BuildModelChannelURL(channel, path), bytes.NewReader(body)) if err != nil { Fail(w, err.Error()) return } request.Header.Set("Authorization", "Bearer "+channel.APIKey) if contentType != "" { request.Header.Set("Content-Type", contentType) } response, err := http.DefaultClient.Do(request) if err != nil { Fail(w, err.Error()) return } defer response.Body.Close() for key, values := range response.Header { if strings.EqualFold(key, "Content-Length") { continue } for _, value := range values { w.Header().Add(key, value) } } w.WriteHeader(response.StatusCode) _, _ = io.Copy(w, response.Body) } func readAIRequest(r *http.Request) ([]byte, string, string, error) { contentType := r.Header.Get("Content-Type") body, err := io.ReadAll(r.Body) if err != nil { return nil, "", "", err } modelName := "" if strings.HasPrefix(contentType, "multipart/form-data") { modelName = readMultipartModel(body, contentType) } else { var payload struct { Model string `json:"model"` } _ = json.Unmarshal(body, &payload) modelName = payload.Model } if strings.TrimSpace(modelName) == "" { return nil, "", "", errMissingModel } return body, contentType, modelName, nil } func readMultipartModel(body []byte, contentType string) string { _, params, err := mime.ParseMediaType(contentType) if err != nil { return "" } reader := multipart.NewReader(bytes.NewReader(body), params["boundary"]) form, err := reader.ReadForm(32 << 20) if err != nil { return "" } defer form.RemoveAll() if values := form.Value["model"]; len(values) > 0 { return values[0] } return "" } var errMissingModel = &aiError{"缺少模型名称"} type aiError struct { message string } func (err *aiError) Error() string { return err.message }