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

Address Common vs REST Defaults for Clients #254

Merged
merged 1 commit into from
Jul 3, 2024
Merged
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
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
})

// alternatively, 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
Loading