Skip to content

Commit

Permalink
Address Common vs REST Defaults for Clients
Browse files Browse the repository at this point in the history
  • Loading branch information
dvonthenen committed Jul 3, 2024
1 parent c795e99 commit 4ad60ed
Show file tree
Hide file tree
Showing 13 changed files with 118 additions and 81 deletions.
12 changes: 7 additions & 5 deletions examples/text-to-speech/websocket/simple/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,19 +82,21 @@ func (c MyCallback) Open(or *msginterfaces.OpenResponse) error {

func main() {
// init library
speak.InitWithDefault()
speak.Init(speak.InitLib{
LogLevel: speak.LogLevelDefault, // LogLevelDefault, LogLevelFull, LogLevelDebug, LogLevelTrace
})

// Go context
ctx := context.Background()

// set the Client options
cOptions := &interfaces.ClientOptions{}

// set the TTS options
ttsOptions := &interfaces.SpeakOptions{
Model: "aura-asteria-en",
}

// set the Client options
cOptions := &interfaces.ClientOptions{}

// create the callback
callback := MyCallback{}

Expand Down Expand Up @@ -127,7 +129,7 @@ func main() {
}

// wait for user input to exit
time.Sleep(3 * time.Second)
time.Sleep(5 * time.Second)

// close the connection
dgClient.Stop()
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/manage/v1/manage.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func New(client interface{}) *Client {
return &Client{
&manage.Client{
Client: &common.Client{
Client: client,
RESTClient: client,
},
},
}
Expand Down
2 changes: 0 additions & 2 deletions pkg/api/speak/v1/rest/speak.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ package restv1

import (
"context"
"fmt"
"io"
"os"
"strconv"
Expand Down Expand Up @@ -135,7 +134,6 @@ func (c *Client) performAction(action func() (map[string]string, error)) (*api.S
klog.V(1).Infof("Platform Supplied Err: %v\n", err)
return nil, err
}
fmt.Printf("retVal: %v\n", retVal)

charCnt, err := strconv.Atoi(retVal["char-count"])
if err != nil {
Expand Down
29 changes: 17 additions & 12 deletions pkg/client/analyze/v1/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,20 +150,27 @@ func (c *Client) DoText(ctx context.Context, text string, options *interfaces.An
return err
}

// using the Common SetupRequest (c.SetupRequest vs c.RESTClient.SetupRequest) method which
// also sets the common headers including the content-type (for example)
req, err := c.SetupRequest(ctx, "POST", uri, strings.NewReader(buf.String()))
if err != nil {
klog.V(1).Infof("SetupRequest failed. Err: %v\n", err)
klog.V(6).Infof("analyze.DoText() LEAVE\n")
return err
}

err = c.HTTPClient.Do(ctx, req, func(res *http.Response) error {
_, err := c.HandleResponse(res, nil, resBody)
return err
})

// altertatively, we could have used the Common Client Do method, like this
// but the default one also sets additional "typical" headers like
// content-type, etc.
// This is the Common Client way...
// err = c.Do(ctx, req, func(res *http.Response) error {
// _, err := c.HandleResponse(res, nil, resBody)
// return err
// })
// This uses the RESTClient Do method
err = c.Do(ctx, req, resBody)
if err != nil {
klog.V(1).Infof("HTTPClient.Do() failed. Err: %v\n", err)
klog.V(1).Infof("RESTClient.Do() failed. Err: %v\n", err)
} else {
klog.V(4).Infof("DoText successful\n")
}
Expand Down Expand Up @@ -212,6 +219,8 @@ func (c *Client) DoURL(ctx context.Context, uri string, options *interfaces.Anal
return err
}

// using the Common SetupRequest (c.SetupRequest vs c.RESTClient.SetupRequest) method which
// also sets the common headers including the content-type (for example)
req, err := c.SetupRequest(ctx, "POST", uri, &buf)
if err != nil {
klog.V(1).Infof("SetupRequest failed. Err: %v\n", err)
Expand All @@ -225,13 +234,9 @@ func (c *Client) DoURL(ctx context.Context, uri string, options *interfaces.Anal
req.Header.Set("Content-Type", "application/json")
}

err = c.HTTPClient.Do(ctx, req, func(res *http.Response) error {
_, err := c.HandleResponse(res, nil, resBody)
return err
})

err = c.Do(ctx, req, resBody)
if err != nil {
klog.V(1).Infof("HTTPClient.Do() failed. Err: %v\n", err)
klog.V(1).Infof("RESTClient.Do() failed. Err: %v\n", err)
} else {
klog.V(4).Infof("DoURL successful\n")
}
Expand Down
16 changes: 1 addition & 15 deletions pkg/client/common/v1/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,25 +62,11 @@ func (c *Client) SetupRequest(ctx context.Context, method, uri string, body io.R
req.Header.Set("Accept", "application/json")
req.Header.Set("Authorization", "token "+c.Options.APIKey)
req.Header.Set("User-Agent", interfaces.DgAgent)
req.Header.Set("Content-Type", "application/json")

return req, nil
}

// ProcessRequest sends the HTTP request and processes the response.
func (c *Client) ProcessRequest(ctx context.Context, req *http.Request, result interface{}) error {
err := c.Client.Do(ctx, req, result)
if err != nil {
if e, ok := err.(*interfaces.StatusError); ok {
klog.V(1).Infof("HTTP Code: %v\n", e.Resp.StatusCode)
return err
}
klog.V(1).Infof("Platform Supplied Err: %v\n", err)
return err
}

return nil
}

// HandleResponse processes the HTTP response for both streaming and URL-based API requests.
func (c *Client) HandleResponse(res *http.Response, keys []string, resBody interface{}) (map[string]string, error) {
klog.V(6).Infof("Handle HTTP response\n")
Expand Down
4 changes: 3 additions & 1 deletion pkg/client/common/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import (
rest "github.com/deepgram/deepgram-go-sdk/pkg/client/rest/v1"
)

type RESTClient = rest.Client

// Client implements helper functionality for Prerecorded API
type Client struct {
*rest.Client
*RESTClient
}
2 changes: 1 addition & 1 deletion pkg/client/interfaces/v1/types-stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type LiveTranscriptionOptions struct {
Search []string `json:"search,omitempty" schema:"search,omitempty"`
SmartFormat bool `json:"smart_format,omitempty" schema:"smart_format,omitempty"`
Tag []string `json:"tag,omitempty" schema:"tag,omitempty"`
Tier string `json:"tier,omitempty" schema:"tier,omitempty"`
Tier string `json:"tier,omitempty" schema:"tier,omitempty"` // Tier is only used for legacy models. Deprecated: This field is deprecated and will be removed in a future release.
UtteranceEndMs string `json:"utterance_end_ms,omitempty" schema:"utterance_end_ms,omitempty"`
VadEvents bool `json:"vad_events,omitempty" schema:"vad_events,omitempty"`
Version string `json:"version,omitempty" schema:"version,omitempty"`
Expand Down
20 changes: 14 additions & 6 deletions pkg/client/listen/v1/rest/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,18 +123,26 @@ func (c *Client) DoStream(ctx context.Context, src io.Reader, options *interface
return err
}

req, err := c.SetupRequest(ctx, "POST", uri, src)
// the Common.SetupRequest (c.SetupRequest vs c.RESTClient.SetupRequest) method, sets
// additional "typical" headers like content-type, etc.
// but we want RESTClient.SetupRequest only provides the basic headers in this caser
req, err := c.RESTClient.SetupRequest(ctx, "POST", uri, src)
if err != nil {
klog.V(1).Infof("SetupRequest failed. Err: %v\n", err)
klog.V(6).Infof("prerecorded.DoStream() LEAVE\n")
return err
}

err = c.HTTPClient.Do(ctx, req, func(res *http.Response) error {
_, err := c.HandleResponse(res, nil, resBody)
return err
})

// altertatively, we could have used the Common Client Do method, like this
// but the default one also sets additional "typical" headers like
// content-type, etc.
// This is the Common Client way...
// err = c.RESTClient.Do(ctx, req, func(res *http.Response) error {
// _, err := c.HandleResponse(res, nil, resBody)
// return err
// })
// This uses the RESTClient Do method
err = c.Do(ctx, req, resBody)
if err != nil {
klog.V(1).Infof("HTTPClient.Do() failed. Err: %v\n", err)
} else {
Expand Down
2 changes: 1 addition & 1 deletion pkg/client/listen/v1/websocket/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -863,7 +863,7 @@ func (c *Client) sendError(err error) error {
response := c.errorToResponse(err)
sendErr := c.router.ErrorHelper(response)
if err != nil {
klog.V(1).Infof("listen: router.Error failed. Err: %v\n", sendErr)
klog.V(1).Infof("live.listen(): router.Error failed. Err: %v\n", sendErr)
}

return err
Expand Down
15 changes: 12 additions & 3 deletions pkg/client/manage/v1/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,24 @@ func (c *Client) APIRequest(ctx context.Context, method, apiPath string, body io
return err
}

// Setup the HTTP request
// using the Common.SetupRequest (c.SetupRequest vs c.RESTClient.SetupRequest) method which
// also sets the common headers including the content-type (for example)
req, err := c.SetupRequest(ctx, method, uri, body)
if err != nil {
klog.V(6).Infof("manage.%s() LEAVE\n", method+apiPath)
return err
}

// Execute the request
err = c.Client.Do(ctx, req, &resBody)
// altertatively, we could have used the Common Client Do method, like this
// but the default one also sets additional "typical" headers like
// content-type, etc.
// This is the Common Client way...
// err = c.Do(ctx, req, func(res *http.Response) error {
// _, err := c.HandleResponse(res, nil, resBody)
// return err
// })
// This uses the RESTClient Do method
err = c.Do(ctx, req, &resBody)
if err != nil {
klog.V(6).Infof("manage.%s() LEAVE\n", method+apiPath)
return err
Expand Down
54 changes: 35 additions & 19 deletions pkg/client/rest/v1/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,14 @@ func New(options *interfaces.ClientOptions) *Client {
return &c
}

// Do is a generic REST API call to the platform
func (c *Client) Do(ctx context.Context, req *http.Request, resBody interface{}) error {
klog.V(6).Infof("rest.Do() ENTER\n")
// SetupRequest prepares and returns a new REST request with common headers set.
func (c *Client) SetupRequest(ctx context.Context, method, uri string, body io.Reader) (*http.Request, error) {
req, err := http.NewRequestWithContext(ctx, method, uri, body)
if err != nil {
klog.V(1).Infof("http.NewRequestWithContext failed. Err: %v\n", err)
return nil, err
}
klog.V(4).Infof("%s %s\n", req.Method, uri)

if headers, ok := ctx.Value(interfaces.HeadersContext{}).(http.Header); ok {
for k, v := range headers {
Expand All @@ -63,12 +68,12 @@ func (c *Client) Do(ctx context.Context, req *http.Request, resBody interface{})
req.Header.Set("Authorization", "token "+c.Options.APIKey)
req.Header.Set("User-Agent", interfaces.DgAgent)

switch req.Method {
case http.MethodPost, http.MethodPatch, http.MethodPut:
klog.V(3).Infof("Content-Type = application/json\n")
req.Header.Set("Content-Type", "application/json")
}
return req, nil
}

// Do is a generic REST API call to the platform
func (c *Client) Do(ctx context.Context, req *http.Request, resBody interface{}) error {
klog.V(6).Infof("rest.Do() ENTER\n")
klog.V(4).Infof("%s %s\n", req.Method, req.URL.String())

err := c.HTTPClient.Do(ctx, req, func(res *http.Response) error {
Expand All @@ -77,15 +82,27 @@ func (c *Client) Do(ctx context.Context, req *http.Request, resBody interface{})
case http.StatusCreated:
case http.StatusNoContent:
case http.StatusBadRequest:
klog.V(1).Infof("HTTP Error Code: %d\n", res.StatusCode)
detail, errBody := io.ReadAll(res.Body)
if errBody != nil {
klog.V(1).Infof("io.ReadAll failed. Err: %e\n", errBody)
klog.V(6).Infof("rest.Do() LEAVE\n")
klog.V(4).Infof("HTTP Error Code: %d\n", res.StatusCode)
detail, err := io.ReadAll(res.Body)
if err != nil {
klog.V(4).Infof("io.ReadAll failed. Err: %v\n", err)
return &interfaces.StatusError{Resp: res}
}
klog.V(6).Infof("rest.Do() LEAVE\n")
return fmt.Errorf("%s: %s", res.Status, bytes.TrimSpace(detail))

// attempt to parse out Deepgram error
var e interfaces.DeepgramError
if err := json.Unmarshal(detail, &e); err == nil {
klog.V(6).Infof("Parsed Deepgram Specific Error\n")
return &interfaces.StatusError{
Resp: res,
DeepgramError: &e,
}
}

// give standard generic error
byDetails := bytes.TrimSpace(detail)
klog.V(1).Infof("Unable to parse Deepgram Error. Err: %s: %s\n", res.Status, byDetails)
return fmt.Errorf("%s: %s", res.Status, byDetails)
default:
return &interfaces.StatusError{Resp: res}
}
Expand All @@ -98,9 +115,10 @@ func (c *Client) Do(ctx context.Context, req *http.Request, resBody interface{})

switch b := resBody.(type) {
case *interfaces.RawResponse:
_, err := io.Copy(b, res.Body)
klog.V(3).Infof("RawResponse\n")
klog.V(6).Infof("rest.Do() LEAVE\n")
return res.Write(b)
return err
case io.Writer:
_, err := io.Copy(b, res.Body)
klog.V(3).Infof("io.Writer\n")
Expand All @@ -114,10 +132,8 @@ func (c *Client) Do(ctx context.Context, req *http.Request, resBody interface{})
return errRead
}
klog.V(5).Infof("json.NewDecoder Raw:\n\n%s\n\n", resultStr)
d := json.NewDecoder(strings.NewReader(string(resultStr)))
klog.V(3).Infof("json.NewDecoder\n")
klog.V(6).Infof("rest.Do() LEAVE\n")
return d.Decode(resBody)
return json.NewDecoder(strings.NewReader(string(resultStr))).Decode(resBody)
}
})

Expand Down
29 changes: 18 additions & 11 deletions pkg/client/speak/v1/rest/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
package restv1

import (
"bytes"
"context"
"encoding/json"
"net/http"
"strings"

Expand Down Expand Up @@ -79,30 +81,35 @@ func (c *Client) DoText(ctx context.Context, text string, options *interfaces.Sp

// TODO: detect if the source is JSON. If not, then wrap the text in a JSON object
// and then marshal it to bytes
// var buf bytes.Buffer
// err = json.NewEncoder(&buf).Encode(textSource{Text: text})
// if err != nil {
// klog.V(1).Infof("json.NewEncoder().Encode() failed. Err: %v\n", err)
// klog.V(6).Infof("speak.DoURL() LEAVE\n")
// return nil, err
// }

// req, err := c.SetupRequest(ctx, "POST", uri, strings.NewReader(buf.String()))
req, err := c.SetupRequest(ctx, "POST", uri, strings.NewReader(text))
var buf bytes.Buffer
err = json.NewEncoder(&buf).Encode(textSource{Text: text})
if err != nil {
klog.V(1).Infof("json.NewEncoder().Encode() failed. Err: %v\n", err)
klog.V(6).Infof("speak.DoURL() LEAVE\n")
return nil, err
}

req, err := c.SetupRequest(ctx, "POST", uri, strings.NewReader(buf.String()))

// using the RESTClient SetupRequest (c.SetupRequest vs c.RESTClient.SetupRequest) method which
// also sets the common headers including the content-type (for example)
// req, err := c.SetupRequest(ctx, "POST", uri, strings.NewReader(text))
if err != nil {
klog.V(1).Infof("SetupRequest failed. Err: %v\n", err)
klog.V(6).Infof("prerecorded.DoStream() LEAVE\n")
return nil, err
}

// we need to use the HTTPClient + HandleResponse method in order to extract the
// response headers from the HTTP response. HandleResponse allows us to do that.
var kv map[string]string
err = c.HTTPClient.Do(ctx, req, func(res *http.Response) error {
kv, err = c.HandleResponse(res, keys, resBody)
return err
})

if err != nil {
klog.V(1).Infof("HTTPClient.Do() failed. Err: %v\n", err)
klog.V(1).Infof("RESTClient.Do() failed. Err: %v\n", err)
} else {
klog.V(4).Infof("DoStream successful\n")
}
Expand Down
Loading

0 comments on commit 4ad60ed

Please sign in to comment.