on main help sign in
fix: improve ux for presenting errors from providers (#1388)
07a4738Andrey Nering
go.mod+1−1
@@ -5,7 +5,7 @@ go 1.25.0
5require (
5require (
6 charm.land/bubbles/v2 v2.0.0-beta.1.0.20251104200223-da0b892d1759
6 charm.land/bubbles/v2 v2.0.0-beta.1.0.20251104200223-da0b892d1759
7 charm.land/bubbletea/v2 v2.0.0-rc.1.0.20251106195925-579e174cd7fa
7 charm.land/bubbletea/v2 v2.0.0-rc.1.0.20251106195925-579e174cd7fa
8− charm.land/fantasy v0.1.6
8+ charm.land/fantasy v0.2.0
9 charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410
9 charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410
10 github.com/JohannesKaufmann/html-to-markdown v1.6.0
10 github.com/JohannesKaufmann/html-to-markdown v1.6.0
11 github.com/MakeNowJust/heredoc v1.0.0
11 github.com/MakeNowJust/heredoc v1.0.0
go.sum+2−2
@@ -2,8 +2,8 @@ charm.land/bubbles/v2 v2.0.0-beta.1.0.20251104200223-da0b892d1759 h1:P1MxkVl8ZeI
2charm.land/bubbles/v2 v2.0.0-beta.1.0.20251104200223-da0b892d1759/go.mod h1:G7JWaj3kDT0BDB+h5BLDUhhBLpDoRLKrpOp5QrA2SQs=
2charm.land/bubbles/v2 v2.0.0-beta.1.0.20251104200223-da0b892d1759/go.mod h1:G7JWaj3kDT0BDB+h5BLDUhhBLpDoRLKrpOp5QrA2SQs=
3charm.land/bubbletea/v2 v2.0.0-rc.1.0.20251106195925-579e174cd7fa h1:J30WneaxF2CV3f2ofamxlr+RwyF6LKSJ3UesrtHfU9I=
3charm.land/bubbletea/v2 v2.0.0-rc.1.0.20251106195925-579e174cd7fa h1:J30WneaxF2CV3f2ofamxlr+RwyF6LKSJ3UesrtHfU9I=
4charm.land/bubbletea/v2 v2.0.0-rc.1.0.20251106195925-579e174cd7fa/go.mod h1:lUNldRH4wRBZ9SGFqlss1Pep7QXzgV/Fp+V9BsAhOPc=
4charm.land/bubbletea/v2 v2.0.0-rc.1.0.20251106195925-579e174cd7fa/go.mod h1:lUNldRH4wRBZ9SGFqlss1Pep7QXzgV/Fp+V9BsAhOPc=
5−charm.land/fantasy v0.1.6 h1:laomMUqUaniQoLx7UOb+MLUpIGJPoNwsXvw1PbzgnB8=
5+charm.land/fantasy v0.2.0 h1:BO1eMugePqrXe46zlNQZI6ajzqt/kft0IMiIAYBqsAo=
6−charm.land/fantasy v0.1.6/go.mod h1:JpFcJ5zs/1CjmYYGAZ7GaFmeBv0mPaTzEPRG6Eic5pc=
6+charm.land/fantasy v0.2.0/go.mod h1:JpFcJ5zs/1CjmYYGAZ7GaFmeBv0mPaTzEPRG6Eic5pc=
7charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410 h1:D9PbaszZYpB4nj+d6HTWr1onlmlyuGVNfL9gAi8iB3k=
7charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410 h1:D9PbaszZYpB4nj+d6HTWr1onlmlyuGVNfL9gAi8iB3k=
8charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410/go.mod h1:1qZyvvVCenJO2M1ac2mX0yyiIZJoZmDM4DG4s0udJkU=
8charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410/go.mod h1:1qZyvvVCenJO2M1ac2mX0yyiIZJoZmDM4DG4s0udJkU=
9cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
9cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
internal/agent/agent.go+11−2
@@ -8,6 +8,7 @@
8package agent
8package agent
9
9
10import (
10import (
11+ "cmp"
11 "context"
12 "context"
12 _ "embed"
13 _ "embed"
13 "errors"
14 "errors"
@@ -32,6 +33,7 @@ import (
32 "github.com/charmbracelet/crush/internal/message"
33 "github.com/charmbracelet/crush/internal/message"
33 "github.com/charmbracelet/crush/internal/permission"
34 "github.com/charmbracelet/crush/internal/permission"
34 "github.com/charmbracelet/crush/internal/session"
35 "github.com/charmbracelet/crush/internal/session"
36+ "github.com/charmbracelet/crush/internal/stringext"
35)
37)
36
38
37//go:embed templates/title.md
39//go:embed templates/title.md
@@ -303,7 +305,7 @@ func (a *sessionAgent) Run(ctx context.Context, call SessionAgentCall) (*fantasy
303 currentAssistant.AddToolCall(toolCall)
305 currentAssistant.AddToolCall(toolCall)
304 return a.messages.Update(genCtx, *currentAssistant)
306 return a.messages.Update(genCtx, *currentAssistant)
305 },
307 },
306− OnRetry: func(err *fantasy.APICallError, delay time.Duration) {
308+ OnRetry: func(err *fantasy.ProviderError, delay time.Duration) {
307 // TODO: implement
309 // TODO: implement
308 },
310 },
309 OnToolCall: func(tc fantasy.ToolCallContent) error {
311 OnToolCall: func(tc fantasy.ToolCallContent) error {
@@ -459,12 +461,19 @@ func (a *sessionAgent) Run(ctx context.Context, call SessionAgentCall) (*fantasy
459 return nil, createErr
461 return nil, createErr
460 }
462 }
461 }
463 }
464+ var fantasyErr *fantasy.Error
465+ var providerErr *fantasy.ProviderError
466+ const defaultTitle = "Provider Error"
462 if isCancelErr {
467 if isCancelErr {
463 currentAssistant.AddFinish(message.FinishReasonCanceled, "User canceled request", "")
468 currentAssistant.AddFinish(message.FinishReasonCanceled, "User canceled request", "")
464 } else if isPermissionErr {
469 } else if isPermissionErr {
465 currentAssistant.AddFinish(message.FinishReasonPermissionDenied, "User denied permission", "")
470 currentAssistant.AddFinish(message.FinishReasonPermissionDenied, "User denied permission", "")
471+ } else if errors.As(err, &providerErr) {
472+ currentAssistant.AddFinish(message.FinishReasonError, cmp.Or(stringext.Capitalize(providerErr.Title), defaultTitle), providerErr.Message)
473+ } else if errors.As(err, &fantasyErr) {
474+ currentAssistant.AddFinish(message.FinishReasonError, cmp.Or(stringext.Capitalize(fantasyErr.Title), defaultTitle), fantasyErr.Message)
466 } else {
475 } else {
467− currentAssistant.AddFinish(message.FinishReasonError, "API Error", err.Error())
476+ currentAssistant.AddFinish(message.FinishReasonError, defaultTitle, err.Error())
468 }
477 }
469 // Note: we use the parent context here because the genCtx has been
478 // Note: we use the parent context here because the genCtx has been
470 // cancelled.
479 // cancelled.
internal/stringext/string.gonew file+10
@@ -0,0 +1,10 @@
1+package stringext
2+
3+import (
4+ "golang.org/x/text/cases"
5+ "golang.org/x/text/language"
6+)
7+
8+func Capitalize(text string) string {
9+ return cases.Title(language.English, cases.Compact).String(text)
10+}