Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added an option to provide file for speech recognition not as a local file, but as bytes #305

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 36 additions & 12 deletions audio.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package openai
import (
"bytes"
"context"
"errors"
"fmt"
"net/http"
"os"
"strings"
)

// Whisper Defines the models provided by OpenAI to use when processing audio with OpenAI.
Expand All @@ -26,10 +28,12 @@ const (
// ResponseFormat is not supported for now. We only return JSON text, which may be sufficient.
type AudioRequest struct {
Model string
FilePath string
Prompt string // For translation, it should be in English
FilePath string // Local file path - leave empty if using FileBytes + FileName
FileBytes *[]byte // File as bytes, also requires FileName to be set (see below)
FileName *string // File name for usage together with FileBytes. The API requires this parameter and use them as file format, so at least correct extension is required.
Prompt string // For translation, it should be 'English'
Temperature float32
Language string // For translation, just do not use it. It seems "en" works, not confirmed...
Language string // For better and faster recognition, but optional.
Format AudioResponseFormat
}

Expand Down Expand Up @@ -93,18 +97,38 @@ func (r AudioRequest) HasJSONResponse() bool {
// audioMultipartForm creates a form with audio file contents and the name of the model to use for
// audio processing.
func audioMultipartForm(request AudioRequest, b formBuilder) error {
f, err := os.Open(request.FilePath)
if err != nil {
return fmt.Errorf("opening audio file: %w", err)
}
defer f.Close()

err = b.createFormFile("file", f)
if err != nil {
return fmt.Errorf("creating form file: %w", err)
// Create from filesystem path
if request.FilePath != "" {
f, err := os.Open(request.FilePath)
if err != nil {
return fmt.Errorf("opening audio file: %w", err)
}
defer f.Close()

err = b.createFormFile("file", f)
if err != nil {
return fmt.Errorf("creating form file: %w", err)
}

// Create from provided bytes
} else if request.FileBytes != nil {

if request.FileName == nil || strings.Contains(*request.FileName, ".") == false {
return errors.New("FileName with correct extension is required while FileBytes is used")
} else {

err := b.createFormFileFromBytes("file", *request.FileName, *request.FileBytes)
if err != nil {
return fmt.Errorf("creating form bytes: %w", err)
}
}

} else {
return errors.New("either FilePath or FileBytes should be specified")
}

err = b.writeField("model", request.Model)
err := b.writeField("model", request.Model)
if err != nil {
return fmt.Errorf("writing model name: %w", err)
}
Expand Down
14 changes: 14 additions & 0 deletions form_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

type formBuilder interface {
createFormFile(fieldname string, file *os.File) error
createFormFileFromBytes(fieldname, fileName string, data []byte) error
writeField(fieldname, value string) error
close() error
formDataContentType() string
Expand Down Expand Up @@ -36,6 +37,19 @@ func (fb *defaultFormBuilder) createFormFile(fieldname string, file *os.File) er
return nil
}

func (fb *defaultFormBuilder) createFormFileFromBytes(fieldname, fileName string, data []byte) error {
fieldWriter, err := fb.writer.CreateFormFile(fieldname, fileName)
if err != nil {
return err
}

_, err = fieldWriter.Write(data)
if err != nil {
return err
}
return nil
}

func (fb *defaultFormBuilder) writeField(fieldname, value string) error {
return fb.writer.WriteField(fieldname, value)
}
Expand Down
11 changes: 8 additions & 3 deletions image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,15 +263,20 @@ func handleVariateImageEndpoint(w http.ResponseWriter, r *http.Request) {
}

type mockFormBuilder struct {
mockCreateFormFile func(string, *os.File) error
mockWriteField func(string, string) error
mockClose func() error
mockCreateFormFile func(string, *os.File) error
mockCreateFormBytes func(string, string, []byte) error
mockWriteField func(string, string) error
mockClose func() error
}

func (fb *mockFormBuilder) createFormFile(fieldname string, file *os.File) error {
return fb.mockCreateFormFile(fieldname, file)
}

func (fb *mockFormBuilder) createFormFileFromBytes(fieldname, fileName string, data []byte) error {
return fb.mockCreateFormBytes(fieldname, fileName, data)
}

func (fb *mockFormBuilder) writeField(fieldname, value string) error {
return fb.mockWriteField(fieldname, value)
}
Expand Down