Skip to content

Commit fb436a9

Browse files
authored
chore: ws auto reconnect (#195)
1 parent f869e90 commit fb436a9

File tree

4 files changed

+56
-3
lines changed

4 files changed

+56
-3
lines changed

src/components/app/ConnectBanner.tsx

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useEffect, useMemo, useState } from 'react';
1+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
22
import { useTranslation } from 'react-i18next';
33

44
import { APP_EVENTS } from '@/application/constants';
@@ -7,6 +7,7 @@ import { Button } from '@/components/ui/button';
77
import { Progress } from '@/components/ui/progress';
88

99
import { useAppHandlers } from './app.hooks';
10+
import { Log } from '@/utils/log';
1011

1112
// WebSocket readyState enum
1213
const READY_STATE = {
@@ -19,6 +20,7 @@ const READY_STATE = {
1920
export function ConnectBanner() {
2021
const [readyState, setReadyState] = useState<number>(READY_STATE.CONNECTING);
2122
const [isStableConnection, setIsStableConnection] = useState(false);
23+
const autoReconnectAttemptedRef = useRef(false);
2224
const { eventEmitter } = useAppHandlers();
2325
const { t } = useTranslation();
2426

@@ -65,6 +67,48 @@ export function ConnectBanner() {
6567
return readyState === READY_STATE.CLOSED;
6668
}, [readyState]);
6769

70+
useEffect(() => {
71+
if (!isClosed) {
72+
autoReconnectAttemptedRef.current = false;
73+
}
74+
}, [isClosed]);
75+
76+
// Automatically trigger reconnect when the user returns to the page and the socket is closed
77+
useEffect(() => {
78+
if (!isClosed || isLoading) return;
79+
if (typeof window === 'undefined' || typeof document === 'undefined') return;
80+
81+
const tryAutoReconnect = () => {
82+
if (autoReconnectAttemptedRef.current) return;
83+
if (!isClosed || isLoading) return;
84+
if (typeof navigator !== 'undefined' && navigator.onLine === false) return;
85+
if (document.visibilityState !== 'visible') return;
86+
87+
autoReconnectAttemptedRef.current = true;
88+
89+
Log.debug('Trying to auto reconnect');
90+
handleReconnect();
91+
};
92+
93+
const handleVisibilityChange = () => {
94+
if (document.visibilityState === 'visible') {
95+
tryAutoReconnect();
96+
}
97+
};
98+
99+
window.addEventListener('focus', tryAutoReconnect);
100+
window.addEventListener('online', tryAutoReconnect);
101+
document.addEventListener('visibilitychange', handleVisibilityChange);
102+
103+
tryAutoReconnect();
104+
105+
return () => {
106+
window.removeEventListener('focus', tryAutoReconnect);
107+
window.removeEventListener('online', tryAutoReconnect);
108+
document.removeEventListener('visibilitychange', handleVisibilityChange);
109+
};
110+
}, [handleReconnect, isClosed, isLoading]);
111+
68112
// Only hide the banner when the connection is stable
69113
if (isStableConnection && readyState === READY_STATE.OPEN) {
70114
return (

src/components/chat/provider/ai-assistant-provider.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export const AIAssistantProvider = ({
6969
const initialScrollTopRef = useRef<number | null>(null);
7070
const [selectedModelName, setSelectedModelName] = useState<string>('Auto');
7171

72-
const { currentPromptId, updateCurrentPromptId } = usePromptModal();
72+
const { currentPromptId, updateCurrentPromptId, prompts } = usePromptModal();
7373

7474
useEffect(() => {
7575
if (!assistantType) {
@@ -140,6 +140,10 @@ export const AIAssistantProvider = ({
140140
setError(null);
141141
try {
142142
setApplyingState(ApplyingState.analyzing);
143+
// Find the selected prompt to get its content for custom_prompt
144+
const selectedPrompt = currentPromptId
145+
? prompts.find((p) => p.id === currentPromptId)
146+
: undefined;
143147
const { cancel, streamPromise } = await request.fetchAIAssistant(
144148
{
145149
inputText: content,
@@ -148,6 +152,7 @@ export const AIAssistantProvider = ({
148152
ragIds,
149153
completionHistory: completionHistoryRef.current,
150154
promptId: currentPromptId || undefined,
155+
customPrompt: selectedPrompt?.content,
151156
modelName: selectedModelName,
152157
},
153158
handleMessageChange
@@ -175,6 +180,7 @@ export const AIAssistantProvider = ({
175180
[
176181
currentPromptId,
177182
handleMessageChange,
183+
prompts,
178184
ragIds,
179185
request,
180186
responseFormat,

src/components/chat/request/writer-request.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export class WriterRequest {
4242
ragIds: string[];
4343
completionHistory: CompletionResult[];
4444
promptId?: string;
45+
customPrompt?: string;
4546
modelName?: string;
4647
}, onMessage: (text: string, comment: string, done?: boolean) => void) => {
4748
const baseUrl = this.axiosInstance.defaults.baseURL;
@@ -76,6 +77,7 @@ export class WriterRequest {
7677
rag_ids: payload.ragIds.length === 0 ? [this.viewId] : payload.ragIds,
7778
completion_history: payload.completionHistory,
7879
prompt_id: payload.promptId,
80+
custom_prompt: payload.customPrompt ? { system: payload.customPrompt } : undefined,
7981
},
8082
}),
8183
});

src/components/chat/types/writer.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ export enum AIAssistantType {
55
MakeLonger = 4,
66
ContinueWriting = 5,
77
Explain = 6,
8-
AskAIAnything = 7
8+
AskAIAnything = 7,
9+
CustomPrompt = 8
910
}
1011

1112
export enum CompletionRole {

0 commit comments

Comments
 (0)