Skip to content

Commit

Permalink
#170: moved error handling to a sep component
Browse files Browse the repository at this point in the history
  • Loading branch information
roma-glushko committed Mar 17, 2024
1 parent 58ef8f2 commit 29c38d4
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 103 deletions.
34 changes: 1 addition & 33 deletions pkg/providers/anthropic/chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import (
"net/http"
"time"

"glide/pkg/providers/clients"

"glide/pkg/api/schemas"
"go.uber.org/zap"
)
Expand Down Expand Up @@ -117,37 +115,7 @@ func (c *Client) doChatRequest(ctx context.Context, payload *ChatRequest) (*sche
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
c.telemetry.Logger.Error("failed to read anthropic chat response", zap.Error(err))
}

c.telemetry.Logger.Error(
"anthropic chat request failed",
zap.Int("status_code", resp.StatusCode),
zap.String("response", string(bodyBytes)),
zap.Any("headers", resp.Header),
)

if resp.StatusCode == http.StatusTooManyRequests {
// Read the value of the "Retry-After" header to get the cooldown delay
retryAfter := resp.Header.Get("Retry-After")

// Parse the value to get the duration
cooldownDelay, err := time.ParseDuration(retryAfter)
if err != nil {
return nil, fmt.Errorf("failed to parse cooldown delay from headers: %w", err)
}

return nil, clients.NewRateLimitError(&cooldownDelay)
}

if resp.StatusCode == http.StatusUnauthorized {
return nil, clients.ErrUnauthorized
}

// Server & client errors result in the same error to keep gateway resilient
return nil, clients.ErrProviderUnavailable
return nil, c.errMapper.Map(resp)
}

// Read the response body into a byte slice
Expand Down
2 changes: 2 additions & 0 deletions pkg/providers/anthropic/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Client struct {
baseURL string
chatURL string
chatRequestTemplate *ChatRequest
errMapper *ErrorMapper
config *Config
httpClient *http.Client
telemetry *telemetry.Telemetry
Expand All @@ -40,6 +41,7 @@ func NewClient(providerConfig *Config, clientConfig *clients.ClientConfig, tel *
chatURL: chatURL,
config: providerConfig,
chatRequestTemplate: NewChatRequestFromConfig(providerConfig),
errMapper: NewErrorMapper(tel),
httpClient: &http.Client{
Timeout: *clientConfig.Timeout,
// TODO: use values from the config
Expand Down
56 changes: 56 additions & 0 deletions pkg/providers/anthropic/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package anthropic

import (
"fmt"
"io"
"net/http"
"time"

"glide/pkg/providers/clients"
"glide/pkg/telemetry"
"go.uber.org/zap"
)

type ErrorMapper struct {
tel *telemetry.Telemetry
}

func NewErrorMapper(tel *telemetry.Telemetry) *ErrorMapper {
return &ErrorMapper{
tel: tel,
}
}

func (m *ErrorMapper) Map(resp *http.Response) error {
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
m.tel.Logger.Error("failed to read anthropic chat response", zap.Error(err))
}

m.tel.Logger.Error(
"anthropic chat request failed",
zap.Int("status_code", resp.StatusCode),
zap.String("response", string(bodyBytes)),
zap.Any("headers", resp.Header),
)

if resp.StatusCode == http.StatusTooManyRequests {
// Read the value of the "Retry-After" header to get the cooldown delay
retryAfter := resp.Header.Get("Retry-After")

// Parse the value to get the duration
cooldownDelay, err := time.ParseDuration(retryAfter)
if err != nil {
return fmt.Errorf("failed to parse cooldown delay from headers: %w", err)
}

return clients.NewRateLimitError(&cooldownDelay)
}

if resp.StatusCode == http.StatusUnauthorized {
return clients.ErrUnauthorized
}

// Server & client errors result in the same error to keep gateway resilient
return clients.ErrProviderUnavailable
}
35 changes: 1 addition & 34 deletions pkg/providers/azureopenai/chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,10 @@ import (
"fmt"
"io"
"net/http"
"time"

"glide/pkg/api/schemas"
"glide/pkg/providers/openai"

"glide/pkg/providers/clients"

"go.uber.org/zap"
)

Expand Down Expand Up @@ -129,37 +126,7 @@ func (c *Client) doChatRequest(ctx context.Context, payload *ChatRequest) (*sche
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
c.telemetry.Logger.Error("failed to read azure openai chat response", zap.Error(err))
}

c.telemetry.Logger.Error(
"azure openai chat request failed",
zap.Int("status_code", resp.StatusCode),
zap.String("response", string(bodyBytes)),
zap.Any("headers", resp.Header),
)

if resp.StatusCode == http.StatusTooManyRequests {
// Read the value of the "Retry-After" header to get the cooldown delay
retryAfter := resp.Header.Get("Retry-After")

// Parse the value to get the duration
cooldownDelay, err := time.ParseDuration(retryAfter)
if err != nil {
return nil, fmt.Errorf("failed to parse cooldown delay from headers: %w", err)
}

return nil, clients.NewRateLimitError(&cooldownDelay)
}

if resp.StatusCode == http.StatusUnauthorized {
return nil, clients.ErrUnauthorized
}

// Server & client errors result in the same error to keep gateway resilient
return nil, clients.ErrProviderUnavailable
return nil, c.errMapper.Map(resp)
}

// Read the response body into a byte slice
Expand Down
2 changes: 2 additions & 0 deletions pkg/providers/azureopenai/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Client struct {
baseURL string // The name of your Azure OpenAI Resource (e.g https://glide-test.openai.azure.com/)
chatURL string
chatRequestTemplate *ChatRequest
errMapper *ErrorMapper
config *Config
httpClient *http.Client
telemetry *telemetry.Telemetry
Expand All @@ -42,6 +43,7 @@ func NewClient(providerConfig *Config, clientConfig *clients.ClientConfig, tel *
chatURL: chatURL,
config: providerConfig,
chatRequestTemplate: NewChatRequestFromConfig(providerConfig),
errMapper: NewErrorMapper(tel),
httpClient: &http.Client{
// TODO: use values from the config
Timeout: *clientConfig.Timeout,
Expand Down
56 changes: 56 additions & 0 deletions pkg/providers/azureopenai/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package azureopenai

import (
"fmt"
"io"
"net/http"
"time"

"glide/pkg/providers/clients"
"glide/pkg/telemetry"
"go.uber.org/zap"
)

type ErrorMapper struct {
tel *telemetry.Telemetry
}

func NewErrorMapper(tel *telemetry.Telemetry) *ErrorMapper {
return &ErrorMapper{
tel: tel,
}
}

func (m *ErrorMapper) Map(resp *http.Response) error {
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
m.tel.L().Error("failed to read azure openai chat response", zap.Error(err))
}

m.tel.L().Error(
"azure openai chat request failed",
zap.Int("status_code", resp.StatusCode),
zap.String("response", string(bodyBytes)),
zap.Any("headers", resp.Header),
)

if resp.StatusCode == http.StatusTooManyRequests {
// Read the value of the "Retry-After" header to get the cooldown delay
retryAfter := resp.Header.Get("Retry-After")

// Parse the value to get the duration
cooldownDelay, err := time.ParseDuration(retryAfter)
if err != nil {
return fmt.Errorf("failed to parse cooldown delay from headers: %w", err)
}

return clients.NewRateLimitError(&cooldownDelay)
}

if resp.StatusCode == http.StatusUnauthorized {
return clients.ErrUnauthorized
}

// Server & client errors result in the same error to keep gateway resilient
return clients.ErrProviderUnavailable
}
4 changes: 2 additions & 2 deletions pkg/providers/lang.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,10 @@ func (m *LanguageModel) ChatStream(ctx context.Context, req *schemas.ChatRequest
m.chatStreamLatency.Add(float64(chunkLatency))

if err != nil {
// if connection was not even open, we should not send our clients any messages about this failure

m.healthTracker.TrackErr(err)

// if connection was not even open, we should not send our clients any messages about this failure

return nil, err
}

Expand Down
35 changes: 1 addition & 34 deletions pkg/providers/octoml/chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@ import (
"fmt"
"io"
"net/http"
"time"

"glide/pkg/providers/openai"

"glide/pkg/providers/clients"

"glide/pkg/api/schemas"
"go.uber.org/zap"
)
Expand Down Expand Up @@ -117,37 +114,7 @@ func (c *Client) doChatRequest(ctx context.Context, payload *ChatRequest) (*sche
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
c.telemetry.Logger.Error("failed to read octoml chat response", zap.Error(err))
}

c.telemetry.Logger.Error(
"octoml chat request failed",
zap.Int("status_code", resp.StatusCode),
zap.String("response", string(bodyBytes)),
zap.Any("headers", resp.Header),
)

if resp.StatusCode == http.StatusTooManyRequests {
// Read the value of the "Retry-After" header to get the cooldown delay
retryAfter := resp.Header.Get("Retry-After")

// Parse the value to get the duration
cooldownDelay, err := time.ParseDuration(retryAfter)
if err != nil {
return nil, fmt.Errorf("failed to parse cooldown delay from headers: %w", err)
}

return nil, clients.NewRateLimitError(&cooldownDelay)
}

if resp.StatusCode == http.StatusUnauthorized {
return nil, clients.ErrUnauthorized
}

// Server & client errors result in the same error to keep gateway resilient
return nil, clients.ErrProviderUnavailable
return nil, c.errMapper.Map(resp)
}

// Read the response body into a byte slice
Expand Down
2 changes: 2 additions & 0 deletions pkg/providers/octoml/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Client struct {
baseURL string
chatURL string
chatRequestTemplate *ChatRequest
errMapper *ErrorMapper
config *Config
httpClient *http.Client
telemetry *telemetry.Telemetry
Expand All @@ -40,6 +41,7 @@ func NewClient(providerConfig *Config, clientConfig *clients.ClientConfig, tel *
chatURL: chatURL,
config: providerConfig,
chatRequestTemplate: NewChatRequestFromConfig(providerConfig),
errMapper: NewErrorMapper(tel),
httpClient: &http.Client{
Timeout: *clientConfig.Timeout,
// TODO: use values from the config
Expand Down
56 changes: 56 additions & 0 deletions pkg/providers/octoml/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package octoml

import (
"fmt"
"io"
"net/http"
"time"

"glide/pkg/providers/clients"
"glide/pkg/telemetry"
"go.uber.org/zap"
)

type ErrorMapper struct {
tel *telemetry.Telemetry
}

func NewErrorMapper(tel *telemetry.Telemetry) *ErrorMapper {
return &ErrorMapper{
tel: tel,
}
}

func (m *ErrorMapper) Map(resp *http.Response) error {
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
m.tel.L().Error("failed to read octoml chat response", zap.Error(err))
}

m.tel.L().Error(
"octoml chat request failed",
zap.Int("status_code", resp.StatusCode),
zap.String("response", string(bodyBytes)),
zap.Any("headers", resp.Header),
)

if resp.StatusCode == http.StatusTooManyRequests {
// Read the value of the "Retry-After" header to get the cooldown delay
retryAfter := resp.Header.Get("Retry-After")

// Parse the value to get the duration
cooldownDelay, err := time.ParseDuration(retryAfter)
if err != nil {
return fmt.Errorf("failed to parse cooldown delay from headers: %w", err)
}

return clients.NewRateLimitError(&cooldownDelay)
}

if resp.StatusCode == http.StatusUnauthorized {
return clients.ErrUnauthorized
}

// Server & client errors result in the same error to keep gateway resilient
return clients.ErrProviderUnavailable
}

0 comments on commit 29c38d4

Please sign in to comment.