Some checks failed
CodeQL / Analyze (go) (push) Successful in 6m28s
Docker Image / build-docker (push) Failing after 13m26s
Lint and Testing / lint (push) Successful in 11m17s
Lint and Testing / test (push) Successful in 11m17s
Lint and Testing / golangci (push) Successful in 2m40s
122 lines
3.5 KiB
Go
122 lines
3.5 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func (s *Server) handleOpenAITranscriptions(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
writeOpenAIError(w, http.StatusMethodNotAllowed, "method not allowed")
|
|
return
|
|
}
|
|
if err := r.ParseMultipartForm(128 << 20); err != nil {
|
|
writeOpenAIError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
modelID, err := s.models.Resolve(r.FormValue("model"), s.cfg.DefaultModel)
|
|
if err != nil {
|
|
writeOpenAIError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
modelPath, err := s.models.Path(modelID)
|
|
if err != nil {
|
|
writeOpenAIError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
audioPath, cleanup, err := s.saveUploadedOpenAI(r)
|
|
if err != nil {
|
|
writeOpenAIError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
defer cleanup()
|
|
|
|
stt := s.parseOpenAISTTOptions(r)
|
|
result, err := s.transcribe(r.Context(), modelPath, audioPath, stt)
|
|
if err != nil {
|
|
writeOpenAIError(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
|
|
switch strings.ToLower(strings.TrimSpace(r.FormValue("response_format"))) {
|
|
case "text":
|
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte(result.Text))
|
|
default:
|
|
writeJSON(w, http.StatusOK, map[string]string{"text": result.Text})
|
|
}
|
|
}
|
|
|
|
func (s *Server) handleOpenAIModels(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
writeOpenAIError(w, http.StatusMethodNotAllowed, "method not allowed")
|
|
return
|
|
}
|
|
ids, err := s.models.List()
|
|
if err != nil {
|
|
writeOpenAIError(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
now := time.Now().Unix()
|
|
data := make([]map[string]any, 0, len(ids))
|
|
for _, id := range ids {
|
|
data = append(data, map[string]any{
|
|
"id": id,
|
|
"object": "model",
|
|
"created": now,
|
|
"owned_by": "go-whisper-api",
|
|
})
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]any{
|
|
"object": "list",
|
|
"data": data,
|
|
})
|
|
}
|
|
|
|
func (s *Server) parseOpenAISTTOptions(r *http.Request) sttOptions {
|
|
lang := strings.TrimSpace(r.FormValue("language"))
|
|
if lang == "" {
|
|
lang = s.cfg.Language
|
|
}
|
|
return sttOptions{
|
|
language: lang,
|
|
punctuate: s.punctCfg.ShouldApplyAPI(r, s.cfg.DefaultPunctuation),
|
|
}
|
|
}
|
|
|
|
func (s *Server) saveUploadedOpenAI(r *http.Request) (path string, cleanup func(), err error) {
|
|
if r.MultipartForm == nil {
|
|
if err := r.ParseMultipartForm(128 << 20); err != nil {
|
|
return "", nil, err
|
|
}
|
|
}
|
|
return saveUploadedRawFields(r, []string{"file", "audio", "wav"})
|
|
}
|
|
|
|
func writeOpenAIError(w http.ResponseWriter, code int, msg string) {
|
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
w.WriteHeader(code)
|
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
|
"error": map[string]any{
|
|
"message": msg,
|
|
"type": openAIErrorType(code),
|
|
},
|
|
})
|
|
}
|
|
|
|
func openAIErrorType(code int) string {
|
|
switch code {
|
|
case http.StatusBadRequest:
|
|
return "invalid_request_error"
|
|
case http.StatusUnauthorized:
|
|
return "authentication_error"
|
|
case http.StatusNotFound:
|
|
return "not_found_error"
|
|
default:
|
|
return "server_error"
|
|
}
|
|
}
|