on main help sign in
fix: status messages
4385fb3adamdottv
internal/llm/provider/anthropic.go+31−6
@@ -5,7 +5,6 @@ import (
5 "encoding/json"
5 "encoding/json"
6 "errors"
6 "errors"
7 "fmt"
7 "fmt"
8− "log"
9 "strings"
8 "strings"
10 "time"
9 "time"
11
10
@@ -88,7 +87,6 @@ func (a *anthropicProvider) SendMessages(ctx context.Context, messages []message
88 },
87 },
89 },
88 },
90 },
89 },
91− option.WithMaxRetries(8),
92 )
90 )
93 if err != nil {
91 if err != nil {
94 return nil, err
92 return nil, err
@@ -256,11 +254,37 @@ func (a *anthropicProvider) StreamResponse(ctx context.Context, messages []messa
256 // Check for stream errors
254 // Check for stream errors
257 err := stream.Err()
255 err := stream.Err()
258 if err != nil {
256 if err != nil {
259− log.Println("error", err)
260−
261 var apierr *anthropic.Error
257 var apierr *anthropic.Error
262− if errors.As(err, &apierr) && apierr.StatusCode == 429 {
258+ if errors.As(err, &apierr) {
259+ if apierr.StatusCode == 429 || apierr.StatusCode == 529 {
260+ // Check for Retry-After header
261+ if retryAfterValues := apierr.Response.Header.Values("Retry-After"); len(retryAfterValues) > 0 {
262+ // Parse the retry after value (seconds)
263+ var retryAfterSec int
264+ if _, err := fmt.Sscanf(retryAfterValues[0], "%d", &retryAfterSec); err == nil {
265+ retryMs := retryAfterSec * 1000
266+
267+ // Inform user of retry with specific wait time
268+ eventChan <- ProviderEvent{
269+ Type: EventWarning,
270+ Info: fmt.Sprintf("[Rate limited: waiting %d seconds as specified by API]", retryAfterSec),
271+ }
272+
273+ // Sleep respecting context cancellation
274+ select {
275+ case <-ctx.Done():
276+ eventChan <- ProviderEvent{Type: EventError, Error: ctx.Err()}
277+ return
278+ case <-time.After(time.Duration(retryMs) * time.Millisecond):
279+ // Continue with retry after specified delay
280+ continue
281+ }
282+ }
283+ }
284+
285+ // Fall back to exponential backoff if Retry-After parsing failed
263− continue
286+ continue
287+ }
264 }
288 }
265
289
266 // For non-rate limit errors, report and exit
290 // For non-rate limit errors, report and exit
@@ -380,3 +404,4 @@ func (a *anthropicProvider) convertToAnthropicMessages(messages []message.Messag
380
404
381 return anthropicMessages
405 return anthropicMessages
382}
406}
407+
internal/tui/components/core/status.go+5−1
@@ -7,6 +7,7 @@ import (
7 "github.com/charmbracelet/lipgloss"
7 "github.com/charmbracelet/lipgloss"
8 "github.com/kujtimiihoxha/termai/internal/config"
8 "github.com/kujtimiihoxha/termai/internal/config"
9 "github.com/kujtimiihoxha/termai/internal/llm/models"
9 "github.com/kujtimiihoxha/termai/internal/llm/models"
10+ "github.com/kujtimiihoxha/termai/internal/pubsub"
10 "github.com/kujtimiihoxha/termai/internal/tui/styles"
11 "github.com/kujtimiihoxha/termai/internal/tui/styles"
11 "github.com/kujtimiihoxha/termai/internal/tui/util"
12 "github.com/kujtimiihoxha/termai/internal/tui/util"
12 "github.com/kujtimiihoxha/termai/internal/version"
13 "github.com/kujtimiihoxha/termai/internal/version"
@@ -34,6 +35,9 @@ func (m statusCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
34 case tea.WindowSizeMsg:
35 case tea.WindowSizeMsg:
35 m.width = msg.Width
36 m.width = msg.Width
36 return m, m.clearMessageCmd()
37 return m, m.clearMessageCmd()
38+ case pubsub.Event[util.InfoMsg]:
39+ m.info = &msg.Payload
40+ return m, m.clearMessageCmd()
37 case util.InfoMsg:
41 case util.InfoMsg:
38 m.info = &msg
42 m.info = &msg
39 return m, m.clearMessageCmd()
43 return m, m.clearMessageCmd()
@@ -87,6 +91,6 @@ func (m statusCmp) model() string {
87
91
88func NewStatusCmp() tea.Model {
92func NewStatusCmp() tea.Model {
89 return &statusCmp{
93 return &statusCmp{
90− messageTTL: 5 * time.Second,
94+ messageTTL: 15 * time.Second,
91 }
95 }
92}
96}