refactor: move chat error kinds into codersdk (#24955)

Moves the chat error kind taxonomy from `coderd/x/chatd/chaterror` into
`codersdk.ChatErrorKind` and types `ChatError.Kind` /
`ChatStreamRetry.Kind` so generated TypeScript exposes an SDK-owned
union, including `usage_limit`. Backend chat classification now
references the SDK constants directly while preserving the existing JSON
string values.

Keeps chat usage-limit admission failures on their existing 409 response
shape. The frontend maps structured usage-limit responses to the
SDK-owned `usage_limit` kind, uses generated `TypesGen.ChatErrorKind`
directly, and removes the local string union and alias.
This commit is contained in:
Ethan
2026-05-06 11:57:48 +10:00
committed by GitHub
parent dc14ab6b97
commit 46a60e6d5d
29 changed files with 305 additions and 233 deletions
+29 -2
View File
@@ -1442,6 +1442,33 @@ type ChatStreamStatus struct {
Status ChatStatus `json:"status"`
}
// ChatErrorKind classifies chat errors for consistent client rendering.
type ChatErrorKind string
const (
ChatErrorKindGeneric ChatErrorKind = "generic"
ChatErrorKindOverloaded ChatErrorKind = "overloaded"
ChatErrorKindRateLimit ChatErrorKind = "rate_limit"
ChatErrorKindTimeout ChatErrorKind = "timeout"
ChatErrorKindStartupTimeout ChatErrorKind = "startup_timeout"
ChatErrorKindAuth ChatErrorKind = "auth"
ChatErrorKindConfig ChatErrorKind = "config"
ChatErrorKindUsageLimit ChatErrorKind = "usage_limit"
)
// AllChatErrorKinds contains every ChatErrorKind value.
// Update this when adding new constants above.
var AllChatErrorKinds = []ChatErrorKind{
ChatErrorKindGeneric,
ChatErrorKindOverloaded,
ChatErrorKindRateLimit,
ChatErrorKindTimeout,
ChatErrorKindStartupTimeout,
ChatErrorKindAuth,
ChatErrorKindConfig,
ChatErrorKindUsageLimit,
}
// ChatError represents a terminal chat error in persisted chat state or the
// live stream.
type ChatError struct {
@@ -1451,7 +1478,7 @@ type ChatError struct {
// normalized error message when available.
Detail string `json:"detail,omitempty"`
// Kind classifies the error for consistent client rendering.
Kind string `json:"kind,omitempty"`
Kind ChatErrorKind `json:"kind,omitempty"`
// Provider identifies the upstream model provider when known.
Provider string `json:"provider,omitempty"`
// Retryable reports whether the underlying error is transient.
@@ -1470,7 +1497,7 @@ type ChatStreamRetry struct {
// Error is the normalized error message from the failed attempt.
Error string `json:"error"`
// Kind classifies the retry reason for consistent client rendering.
Kind string `json:"kind,omitempty"`
Kind ChatErrorKind `json:"kind,omitempty"`
// Provider identifies the upstream model provider when known.
Provider string `json:"provider,omitempty"`
// StatusCode is the best-effort upstream HTTP status code.
+30 -1
View File
@@ -137,6 +137,35 @@ func TestChatUsageLimitExceededFrom(t *testing.T) {
})
}
func TestChatErrorKind_JSONRoundTrip(t *testing.T) {
t.Parallel()
terminal := codersdk.ChatError{
Message: "limit reached",
Kind: codersdk.ChatErrorKindUsageLimit,
}
data, err := json.Marshal(terminal)
require.NoError(t, err)
require.Contains(t, string(data), `"kind":"usage_limit"`)
var decodedTerminal codersdk.ChatError
require.NoError(t, json.Unmarshal(data, &decodedTerminal))
require.Equal(t, codersdk.ChatErrorKindUsageLimit, decodedTerminal.Kind)
retry := codersdk.ChatStreamRetry{
Attempt: 1,
Error: "retrying",
Kind: codersdk.ChatErrorKindUsageLimit,
}
data, err = json.Marshal(retry)
require.NoError(t, err)
require.Contains(t, string(data), `"kind":"usage_limit"`)
var decodedRetry codersdk.ChatStreamRetry
require.NoError(t, json.Unmarshal(data, &decodedRetry))
require.Equal(t, codersdk.ChatErrorKindUsageLimit, decodedRetry.Kind)
}
func TestChatMessagePart_StripInternal(t *testing.T) {
t.Parallel()
@@ -450,7 +479,7 @@ func TestChat_JSONRoundTrip(t *testing.T) {
lastError := &codersdk.ChatError{
Message: "boom",
Detail: "provider detail",
Kind: "generic",
Kind: codersdk.ChatErrorKindGeneric,
Provider: "openai",
Retryable: true,
StatusCode: 503,