From ecb30406158ee4633b9dc4cdadeda51ac5743bad Mon Sep 17 00:00:00 2001 From: DuckTieCorpMember Date: Tue, 6 Oct 2026 16:47:13 +0500 Subject: [PATCH] Demo: Chat-Message Streaming and Chat-Prompt Suggestions imporved error handling --- .../Angular/app/ai/ai.service.ts | 2 +- .../Angular/app/app.service.ts | 18 ++++++++++++----- .../Chat/MessageStreaming/React/service.ts | 4 ++-- .../Chat/MessageStreaming/React/useApi.ts | 20 +++++++++++++------ .../Chat/MessageStreaming/ReactJs/service.js | 3 +-- .../Chat/MessageStreaming/ReactJs/useApi.js | 19 ++++++++++++------ .../Demos/Chat/MessageStreaming/Vue/App.vue | 18 ++++++++++++----- .../Chat/MessageStreaming/Vue/service.ts | 2 +- .../Chat/MessageStreaming/jQuery/index.js | 19 +++++++++++++----- .../Angular/app/ai/ai.service.ts | 2 +- .../Angular/app/app.service.ts | 14 +++++++++---- .../Chat/PromptSuggestions/React/service.ts | 2 +- .../Chat/PromptSuggestions/React/useApi.ts | 16 ++++++++++----- .../Chat/PromptSuggestions/ReactJs/service.js | 2 +- .../Chat/PromptSuggestions/ReactJs/useApi.js | 15 +++++++++----- .../Demos/Chat/PromptSuggestions/Vue/App.vue | 14 +++++++++---- .../Chat/PromptSuggestions/Vue/service.ts | 2 +- .../Chat/PromptSuggestions/jQuery/index.js | 16 ++++++++++----- 18 files changed, 128 insertions(+), 60 deletions(-) diff --git a/apps/demos/Demos/Chat/MessageStreaming/Angular/app/ai/ai.service.ts b/apps/demos/Demos/Chat/MessageStreaming/Angular/app/ai/ai.service.ts index 8b666ea30df5..299cdf8bda59 100644 --- a/apps/demos/Demos/Chat/MessageStreaming/Angular/app/ai/ai.service.ts +++ b/apps/demos/Demos/Chat/MessageStreaming/Angular/app/ai/ai.service.ts @@ -44,7 +44,7 @@ export class AiService { }; try { - const stream = await this.chatService.chat.completions.create(params, { signal }); + const stream = await this.chatService.chat.completions.create(params, { signal, maxRetries: 0 }); for await (const event of stream) { const delta = event.choices?.[0]?.delta?.content; diff --git a/apps/demos/Demos/Chat/MessageStreaming/Angular/app/app.service.ts b/apps/demos/Demos/Chat/MessageStreaming/Angular/app/app.service.ts index 07b186a910a2..686bfd9d6b27 100644 --- a/apps/demos/Demos/Chat/MessageStreaming/Angular/app/app.service.ts +++ b/apps/demos/Demos/Chat/MessageStreaming/Angular/app/app.service.ts @@ -163,8 +163,14 @@ export class AppService { return id; } - private alertLimitReached(): void { - this.alertsSubject.next([{ message: 'Request limit reached, try again in a minute.' }]); + private getErrorMessage(err: unknown): string { + if (err instanceof Error) return err.message; + if (typeof err === 'string') return err; + return 'Unknown error'; + } + + private alertError(message: string): void { + this.alertsSubject.next([{ message }]); setTimeout(() => { this.alertsSubject.next([]); @@ -224,9 +230,11 @@ export class AppService { } catch (e: unknown) { this.typingUsersSubject.next([]); - if ((e as Error)?.name !== 'AbortError' && assistantId !== undefined) { - this.updateMessageText(assistantId, ''); - this.alertLimitReached(); + if (!this.abortController?.signal.aborted) { + if (assistantId !== undefined) { + this.updateMessageText(assistantId, ''); + } + this.alertError(this.getErrorMessage(e)); } } finally { this.abortController = null; diff --git a/apps/demos/Demos/Chat/MessageStreaming/React/service.ts b/apps/demos/Demos/Chat/MessageStreaming/React/service.ts index b93b51a71e5c..a8efec64dc11 100644 --- a/apps/demos/Demos/Chat/MessageStreaming/React/service.ts +++ b/apps/demos/Demos/Chat/MessageStreaming/React/service.ts @@ -37,8 +37,8 @@ export async function getAIResponseStream( }; try { - const stream = await chatService.chat.completions.create(params, { signal }); - // eslint-disable-next-line no-restricted-syntax + const stream = await chatService.chat.completions.create(params, { signal, maxRetries: 0 }); + for await (const event of stream) { const delta = event.choices?.[0]?.delta?.content; if (delta) { diff --git a/apps/demos/Demos/Chat/MessageStreaming/React/useApi.ts b/apps/demos/Demos/Chat/MessageStreaming/React/useApi.ts index fd5d459b3553..cfd32c48da33 100644 --- a/apps/demos/Demos/Chat/MessageStreaming/React/useApi.ts +++ b/apps/demos/Demos/Chat/MessageStreaming/React/useApi.ts @@ -77,6 +77,12 @@ function createDelayedRenderer({ delay = 20, onRender }: DelayedRendererOptions) return { pushChunk, stop }; } +const getErrorMessage = (err: unknown): string => { + if (err instanceof Error) return err.message; + if (typeof err === 'string') return err; + return 'Unknown error'; +}; + export const useApi = () => { const [alerts, setAlerts] = useState([]); const [typingUsers, setTypingUsers] = useState([]); @@ -109,9 +115,9 @@ export const useApi = () => { return id; }, []); - const alertLimitReached = useCallback((): void => { + const alertError = useCallback((message: string): void => { setAlerts([{ - message: 'Request limit reached, try again in a minute.', + message, }]); setTimeout(() => { @@ -167,15 +173,17 @@ export const useApi = () => { } catch (e: unknown) { setTypingUsers([]); - if ((e as Error)?.name !== 'AbortError' && assistantId !== undefined) { - updateMessageText(assistantId, ''); - alertLimitReached(); + if (!abortControllerRef.current?.signal.aborted) { + if (assistantId !== undefined) { + updateMessageText(assistantId, ''); + } + alertError(getErrorMessage(e)); } } finally { abortControllerRef.current = null; setIsStreaming(false); } - }, [alertLimitReached, insertAssistantPlaceholder, updateMessageText]); + }, [alertError, insertAssistantPlaceholder, updateMessageText]); return { alerts, diff --git a/apps/demos/Demos/Chat/MessageStreaming/ReactJs/service.js b/apps/demos/Demos/Chat/MessageStreaming/ReactJs/service.js index 7fe2faa8d287..21c8f72c3fb4 100644 --- a/apps/demos/Demos/Chat/MessageStreaming/ReactJs/service.js +++ b/apps/demos/Demos/Chat/MessageStreaming/ReactJs/service.js @@ -19,8 +19,7 @@ export async function getAIResponseStream(messages, { stream: true, }; try { - const stream = await chatService.chat.completions.create(params, { signal }); - // eslint-disable-next-line no-restricted-syntax + const stream = await chatService.chat.completions.create(params, { signal, maxRetries: 0 }); for await (const event of stream) { const delta = event.choices?.[0]?.delta?.content; if (delta) { diff --git a/apps/demos/Demos/Chat/MessageStreaming/ReactJs/useApi.js b/apps/demos/Demos/Chat/MessageStreaming/ReactJs/useApi.js index 8ad6561ef71e..a0fcb2ac1ce1 100644 --- a/apps/demos/Demos/Chat/MessageStreaming/ReactJs/useApi.js +++ b/apps/demos/Demos/Chat/MessageStreaming/ReactJs/useApi.js @@ -56,6 +56,11 @@ function createDelayedRenderer({ delay = 20, onRender }) { } return { pushChunk, stop }; } +const getErrorMessage = (err) => { + if (err instanceof Error) return err.message; + if (typeof err === 'string') return err; + return 'Unknown error'; +}; export const useApi = () => { const [alerts, setAlerts] = useState([]); const [typingUsers, setTypingUsers] = useState([]); @@ -88,10 +93,10 @@ export const useApi = () => { ]); return id; }, []); - const alertLimitReached = useCallback(() => { + const alertError = useCallback((message) => { setAlerts([ { - message: 'Request limit reached, try again in a minute.', + message, }, ]); setTimeout(() => { @@ -137,16 +142,18 @@ export const useApi = () => { setTypingUsers([]); } catch (e) { setTypingUsers([]); - if (e?.name !== 'AbortError' && assistantId !== undefined) { - updateMessageText(assistantId, ''); - alertLimitReached(); + if (!abortControllerRef.current?.signal.aborted) { + if (assistantId !== undefined) { + updateMessageText(assistantId, ''); + } + alertError(getErrorMessage(e)); } } finally { abortControllerRef.current = null; setIsStreaming(false); } }, - [alertLimitReached, insertAssistantPlaceholder, updateMessageText], + [alertError, insertAssistantPlaceholder, updateMessageText], ); return { alerts, diff --git a/apps/demos/Demos/Chat/MessageStreaming/Vue/App.vue b/apps/demos/Demos/Chat/MessageStreaming/Vue/App.vue index 9d4e6db2b85d..8b2bd9938fa8 100644 --- a/apps/demos/Demos/Chat/MessageStreaming/Vue/App.vue +++ b/apps/demos/Demos/Chat/MessageStreaming/Vue/App.vue @@ -92,8 +92,14 @@ function insertAssistantPlaceholder(): number { return id; } -function alertLimitReached(): void { - alerts.value = [{ message: 'Request limit reached, try again in a minute.' }]; +function getErrorMessage(err: unknown): string { + if (err instanceof Error) return err.message; + if (typeof err === 'string') return err; + return 'Unknown error'; +} + +function alertError(message: string): void { + alerts.value = [{ message }]; setTimeout(() => { alerts.value = []; }, ALERT_TIMEOUT); } @@ -162,9 +168,11 @@ async function fetchAIResponse(message: DxChatTypes.Message): Promise { typingUsers.value = []; } catch (e: unknown) { typingUsers.value = []; - if ((e as Error)?.name !== 'AbortError' && assistantId !== undefined) { - updateMessageText(assistantId, ''); - alertLimitReached(); + if (!abortController?.signal.aborted) { + if (assistantId !== undefined) { + updateMessageText(assistantId, ''); + } + alertError(getErrorMessage(e)); } } finally { abortController = null; diff --git a/apps/demos/Demos/Chat/MessageStreaming/Vue/service.ts b/apps/demos/Demos/Chat/MessageStreaming/Vue/service.ts index f165bc78d08d..86ac7baef06f 100644 --- a/apps/demos/Demos/Chat/MessageStreaming/Vue/service.ts +++ b/apps/demos/Demos/Chat/MessageStreaming/Vue/service.ts @@ -37,7 +37,7 @@ export async function getAIResponseStream( }; try { - const stream = await chatService.chat.completions.create(params, { signal }); + const stream = await chatService.chat.completions.create(params, { signal, maxRetries: 0 }); for await (const event of stream) { const delta = event.choices?.[0]?.delta?.content; diff --git a/apps/demos/Demos/Chat/MessageStreaming/jQuery/index.js b/apps/demos/Demos/Chat/MessageStreaming/jQuery/index.js index 8cd75318f7d0..9be70a53cc9a 100644 --- a/apps/demos/Demos/Chat/MessageStreaming/jQuery/index.js +++ b/apps/demos/Demos/Chat/MessageStreaming/jQuery/index.js @@ -66,6 +66,7 @@ $(() => { try { const stream = await chatService.chat.completions.create(params, { signal, + maxRetries: 0, }); // eslint-disable-next-line no-restricted-syntax @@ -85,11 +86,17 @@ $(() => { } } - function alertLimitReached() { + function getErrorMessage(err) { + if (err instanceof Error) return err.message; + if (typeof err === 'string') return err; + return 'Unknown error'; + } + + function alertError(message) { instance.option({ alerts: [ { - message: 'Request limit reached, try again in a minute.', + message, }, ], }); @@ -170,9 +177,11 @@ $(() => { messages.pop(); - if (e?.name !== 'AbortError' && assistantId) { - updateMessageText(assistantId, ''); - alertLimitReached(); + if (!abortController?.signal.aborted) { + if (assistantId) { + updateMessageText(assistantId, ''); + } + alertError(getErrorMessage(e)); } } finally { abortController = null; diff --git a/apps/demos/Demos/Chat/PromptSuggestions/Angular/app/ai/ai.service.ts b/apps/demos/Demos/Chat/PromptSuggestions/Angular/app/ai/ai.service.ts index c90a0efe9aba..fe312c947559 100644 --- a/apps/demos/Demos/Chat/PromptSuggestions/Angular/app/ai/ai.service.ts +++ b/apps/demos/Demos/Chat/PromptSuggestions/Angular/app/ai/ai.service.ts @@ -33,7 +33,7 @@ export class AiService { temperature: 0.7, }; - const response = await this.chatService.chat.completions.create(params); + const response = await this.chatService.chat.completions.create(params, { maxRetries: 0 }); const data = { choices: response.choices }; return data.choices[0].message?.content; diff --git a/apps/demos/Demos/Chat/PromptSuggestions/Angular/app/app.service.ts b/apps/demos/Demos/Chat/PromptSuggestions/Angular/app/app.service.ts index 37a538bcdf42..fb93d364007f 100644 --- a/apps/demos/Demos/Chat/PromptSuggestions/Angular/app/app.service.ts +++ b/apps/demos/Demos/Chat/PromptSuggestions/Angular/app/app.service.ts @@ -168,13 +168,19 @@ export class AppService { this.messages.push({ role: 'assistant', content: aiResponse }); this.renderAssistantMessage(aiResponse); }, 200); - } catch { + } catch (err: unknown) { this.typingUsersSubject.next([]); this.messages.pop(); - this.alertLimitReached(); + this.alertError(this.getErrorMessage(err)); } } + getErrorMessage(err: unknown): string { + if (err instanceof Error) return err.message; + if (typeof err === 'string') return err; + return 'Unknown error'; + } + renderAssistantMessage(text: string): void { this.insertMessage({ id: Date.now(), @@ -184,8 +190,8 @@ export class AppService { }); } - alertLimitReached(): void { - this.setAlerts([{ message: 'Request limit reached, try again in a minute.' }]); + alertError(message: string): void { + this.setAlerts([{ message }]); setTimeout(() => { this.setAlerts([]); diff --git a/apps/demos/Demos/Chat/PromptSuggestions/React/service.ts b/apps/demos/Demos/Chat/PromptSuggestions/React/service.ts index 37aee0842731..7d1698e6736d 100644 --- a/apps/demos/Demos/Chat/PromptSuggestions/React/service.ts +++ b/apps/demos/Demos/Chat/PromptSuggestions/React/service.ts @@ -31,7 +31,7 @@ export async function getAIResponse(messages: AIMessage[], delay?: number): Prom temperature: 0.7, }; - const response = await chatService.chat.completions.create(params); + const response = await chatService.chat.completions.create(params, { maxRetries: 0 }); const data = { choices: response.choices }; if (delay) { diff --git a/apps/demos/Demos/Chat/PromptSuggestions/React/useApi.ts b/apps/demos/Demos/Chat/PromptSuggestions/React/useApi.ts index 7eb0e1bfcf28..9112fd8ab50b 100644 --- a/apps/demos/Demos/Chat/PromptSuggestions/React/useApi.ts +++ b/apps/demos/Demos/Chat/PromptSuggestions/React/useApi.ts @@ -38,6 +38,12 @@ const dataItemToMessage = (item: ChatTypes.Message): AIMessage => ({ const getMessageHistory = (): AIMessage[] => [...dataSource.items()].map(dataItemToMessage); +const getErrorMessage = (err: unknown): string => { + if (err instanceof Error) return err.message; + if (typeof err === 'string') return err; + return 'Unknown error'; +}; + export const useApi = () => { const [alerts, setAlerts] = useState([]); @@ -45,9 +51,9 @@ export const useApi = () => { dataSource.store().push([{ type: 'insert', data }]); }, []); - const alertLimitReached = useCallback((): void => { + const alertError = useCallback((message: string): void => { setAlerts([{ - message: 'Request limit reached, try again in a minute.', + message, }]); setTimeout(() => { @@ -71,10 +77,10 @@ export const useApi = () => { author: assistant, text: aiResponse, }); - } catch { - alertLimitReached(); + } catch (err: unknown) { + alertError(getErrorMessage(err)); } - }, [alertLimitReached, insertMessage]); + }, [alertError, insertMessage]); return { alerts, diff --git a/apps/demos/Demos/Chat/PromptSuggestions/ReactJs/service.js b/apps/demos/Demos/Chat/PromptSuggestions/ReactJs/service.js index e1eeaa9fc9d1..89d12a3bcde0 100644 --- a/apps/demos/Demos/Chat/PromptSuggestions/ReactJs/service.js +++ b/apps/demos/Demos/Chat/PromptSuggestions/ReactJs/service.js @@ -19,7 +19,7 @@ export async function getAIResponse(messages, delay) { max_completion_tokens: 1000, temperature: 0.7, }; - const response = await chatService.chat.completions.create(params); + const response = await chatService.chat.completions.create(params, { maxRetries: 0 }); const data = { choices: response.choices }; if (delay) { await wait(delay); diff --git a/apps/demos/Demos/Chat/PromptSuggestions/ReactJs/useApi.js b/apps/demos/Demos/Chat/PromptSuggestions/ReactJs/useApi.js index ded7268f83fd..58e5fd5aa925 100644 --- a/apps/demos/Demos/Chat/PromptSuggestions/ReactJs/useApi.js +++ b/apps/demos/Demos/Chat/PromptSuggestions/ReactJs/useApi.js @@ -29,15 +29,20 @@ const dataItemToMessage = (item) => ({ content: item.text, }); const getMessageHistory = () => [...dataSource.items()].map(dataItemToMessage); +const getErrorMessage = (err) => { + if (err instanceof Error) return err.message; + if (typeof err === 'string') return err; + return 'Unknown error'; +}; export const useApi = () => { const [alerts, setAlerts] = useState([]); const insertMessage = useCallback((data) => { dataSource.store().push([{ type: 'insert', data }]); }, []); - const alertLimitReached = useCallback(() => { + const alertError = useCallback((message) => { setAlerts([ { - message: 'Request limit reached, try again in a minute.', + message, }, ]); setTimeout(() => { @@ -59,11 +64,11 @@ export const useApi = () => { author: assistant, text: aiResponse, }); - } catch { - alertLimitReached(); + } catch (err) { + alertError(getErrorMessage(err)); } }, - [alertLimitReached, insertMessage], + [alertError, insertMessage], ); return { alerts, diff --git a/apps/demos/Demos/Chat/PromptSuggestions/Vue/App.vue b/apps/demos/Demos/Chat/PromptSuggestions/Vue/App.vue index a5e1ef4b675c..4c402f386213 100644 --- a/apps/demos/Demos/Chat/PromptSuggestions/Vue/App.vue +++ b/apps/demos/Demos/Chat/PromptSuggestions/Vue/App.vue @@ -119,17 +119,23 @@ async function processMessageSending( messages.push({ role: 'assistant', content: aiResponse }); renderAssistantMessage(aiResponse); }, 200); - } catch { + } catch (err: unknown) { typingUsers.value = []; messages.pop(); - alertLimitReached(); + alertError(getErrorMessage(err)); } finally { toggleDisabledState(false, event); } } -function alertLimitReached(): void { - alerts.value = [{ message: 'Request limit reached, try again in a minute.' }]; +function getErrorMessage(err: unknown): string { + if (err instanceof Error) return err.message; + if (typeof err === 'string') return err; + return 'Unknown error'; +} + +function alertError(message: string): void { + alerts.value = [{ message }]; setTimeout(() => { alerts.value = []; diff --git a/apps/demos/Demos/Chat/PromptSuggestions/Vue/service.ts b/apps/demos/Demos/Chat/PromptSuggestions/Vue/service.ts index 89c54de11d95..269218f9ef7d 100644 --- a/apps/demos/Demos/Chat/PromptSuggestions/Vue/service.ts +++ b/apps/demos/Demos/Chat/PromptSuggestions/Vue/service.ts @@ -26,7 +26,7 @@ export async function getAIResponse(messages: AIMessage[]): Promise temperature: 0.7, }; - const response = await chatService.chat.completions.create(params as any); + const response = await chatService.chat.completions.create(params as any, { maxRetries: 0 }); const data = { choices: response.choices }; return data.choices[0].message?.content || ''; diff --git a/apps/demos/Demos/Chat/PromptSuggestions/jQuery/index.js b/apps/demos/Demos/Chat/PromptSuggestions/jQuery/index.js index a113e039a7c0..61e71664e3f8 100644 --- a/apps/demos/Demos/Chat/PromptSuggestions/jQuery/index.js +++ b/apps/demos/Demos/Chat/PromptSuggestions/jQuery/index.js @@ -28,16 +28,22 @@ $(() => { temperature: 0.7, }; - const response = await chatService.chat.completions.create(params); + const response = await chatService.chat.completions.create(params, { maxRetries: 0 }); const data = { choices: response.choices }; return data.choices[0].message?.content; } - function alertLimitReached() { + function getErrorMessage(err) { + if (err instanceof Error) return err.message; + if (typeof err === 'string') return err; + return 'Unknown error'; + } + + function alertError(message) { instance.option({ alerts: [{ - message: 'Request limit reached, try again in a minute.', + message, }], }); @@ -74,10 +80,10 @@ $(() => { renderAssistantMessage(aiResponse); }, 200); - } catch { + } catch (err) { instance.option({ typingUsers: [] }); messages.pop(); - alertLimitReached(); + alertError(getErrorMessage(err)); } finally { toggleDisabledState(false, event); }