Skip to content

Commit 827ef45

Browse files
lgrammelsamuelint
andauthoredJun 26, 2024··
feat (ai/ui): improve error handling in useAssistant (#2112)
Co-authored-by: Samuel <samuelint@gmail.com>
1 parent 354ceb8 commit 827ef45

File tree

6 files changed

+50
-12
lines changed

6 files changed

+50
-12
lines changed
 

‎.changeset/wicked-trainers-confess.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@ai-sdk/svelte': patch
3+
'@ai-sdk/react': patch
4+
'ai': patch
5+
---
6+
7+
feat (ai/ui): improve error handling in useAssistant

‎packages/core/svelte/use-assistant.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export function useAssistant({
115115
input.set('');
116116

117117
try {
118-
const result = await fetch(api, {
118+
const response = await fetch(api, {
119119
method: 'POST',
120120
credentials,
121121
signal: abortController.signal,
@@ -131,13 +131,19 @@ export function useAssistant({
131131
}),
132132
});
133133

134-
if (result.body == null) {
134+
if (!response.ok) {
135+
throw new Error(
136+
(await response.text()) ?? 'Failed to fetch the assistant response.',
137+
);
138+
}
139+
140+
if (response.body == null) {
135141
throw new Error('The response body is empty.');
136142
}
137143

138144
// Read the streamed response data
139145
for await (const { type, value } of readDataStream(
140-
result.body.getReader(),
146+
response.body.getReader(),
141147
)) {
142148
switch (type) {
143149
case 'assistant_message': {

‎packages/react/src/use-assistant.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ Abort the current request immediately, keep the generated tokens if any.
8282
/**
8383
* The error thrown during the assistant message processing, if any.
8484
*/
85-
error: undefined | unknown;
85+
error: undefined | Error;
8686
};
8787

8888
export function useAssistant({
@@ -140,7 +140,7 @@ export function useAssistant({
140140
try {
141141
abortControllerRef.current = abortController;
142142

143-
const result = await fetch(api, {
143+
const response = await fetch(api, {
144144
method: 'POST',
145145
credentials,
146146
signal: abortController.signal,
@@ -156,12 +156,18 @@ export function useAssistant({
156156
}),
157157
});
158158

159-
if (result.body == null) {
159+
if (!response.ok) {
160+
throw new Error(
161+
(await response.text()) ?? 'Failed to fetch the assistant response.',
162+
);
163+
}
164+
165+
if (response.body == null) {
160166
throw new Error('The response body is empty.');
161167
}
162168

163169
for await (const { type, value } of readDataStream(
164-
result.body.getReader(),
170+
response.body.getReader(),
165171
)) {
166172
switch (type) {
167173
case 'assistant_message': {

‎packages/react/src/use-assistant.ui.test.tsx

+14-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { formatStreamPart } from '@ai-sdk/ui-utils';
22
import {
33
mockFetchDataStream,
44
mockFetchDataStreamWithGenerator,
5+
mockFetchError,
56
} from '@ai-sdk/ui-utils/test';
67
import '@testing-library/jest-dom/vitest';
78
import { cleanup, findByText, render, screen } from '@testing-library/react';
@@ -10,13 +11,14 @@ import { useAssistant } from './use-assistant';
1011

1112
describe('stream data stream', () => {
1213
const TestComponent = () => {
13-
const { status, messages, append } = useAssistant({
14+
const { status, messages, error, append } = useAssistant({
1415
api: '/api/assistant',
1516
});
1617

1718
return (
1819
<div>
1920
<div data-testid="status">{status}</div>
21+
{error && <div data-testid="error">{error.toString()}</div>}
2022
{messages.map((m, idx) => (
2123
<div data-testid={`message-${idx}`} key={idx}>
2224
{m.role === 'user' ? 'User: ' : 'AI: '}
@@ -83,6 +85,17 @@ describe('stream data stream', () => {
8385
);
8486
});
8587

88+
it('should show error response', async () => {
89+
mockFetchError({ statusCode: 500, errorMessage: 'Internal Error' });
90+
91+
await userEvent.click(screen.getByTestId('do-append'));
92+
93+
await screen.findByTestId('error');
94+
expect(screen.getByTestId('error')).toHaveTextContent(
95+
'Error: Internal Error',
96+
);
97+
});
98+
8699
describe('loading state', () => {
87100
it('should show loading state', async () => {
88101
let finishGeneration: ((value?: unknown) => void) | undefined;

‎packages/svelte/src/use-assistant.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ export function useAssistant({
112112
input.set('');
113113

114114
try {
115-
const result = await fetch(api, {
115+
const response = await fetch(api, {
116116
method: 'POST',
117117
credentials,
118118
signal: abortController.signal,
@@ -128,13 +128,19 @@ export function useAssistant({
128128
}),
129129
});
130130

131-
if (result.body == null) {
131+
if (!response.ok) {
132+
throw new Error(
133+
(await response.text()) ?? 'Failed to fetch the assistant response.',
134+
);
135+
}
136+
137+
if (response.body == null) {
132138
throw new Error('The response body is empty.');
133139
}
134140

135141
// Read the streamed response data
136142
for await (const { type, value } of readDataStream(
137-
result.body.getReader(),
143+
response.body.getReader(),
138144
)) {
139145
switch (type) {
140146
case 'assistant_message': {

‎packages/ui-utils/src/call-chat-api.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export async function callChatApi({
5656
if (!response.ok) {
5757
restoreMessagesOnFailure();
5858
throw new Error(
59-
(await response.text()) || 'Failed to fetch the chat response.',
59+
(await response.text()) ?? 'Failed to fetch the chat response.',
6060
);
6161
}
6262

0 commit comments

Comments
 (0)
Please sign in to comment.