fix(chat): reprise robuste au retour d'un layout non conversationnel (composer verrouillé selon l'état réel, replay complet du scrollback, plus de fausse terminaison 'no reply') (#190)
This commit is contained in:
@ -59,6 +59,7 @@ interface CachedChatView {
|
||||
}
|
||||
|
||||
const chatSessionViewCache = new Map<string, CachedChatView>();
|
||||
const EMPTY_MODEL_RESPONSE_MESSAGE = "Le modèle n'a renvoyé aucune réponse.";
|
||||
|
||||
export function __clearCustomAgentChatViewCacheForTests() {
|
||||
chatSessionViewCache.clear();
|
||||
@ -299,17 +300,6 @@ function appendProgress(turns: ChatTurn[], progress: ReplyProgress): ChatTurn[]
|
||||
return [...turns, { role: "progress", progress }];
|
||||
}
|
||||
|
||||
function reattachNotice(missedChunks: number): ChatTurn | null {
|
||||
if (missedChunks <= 0) return null;
|
||||
return {
|
||||
role: "tool",
|
||||
label:
|
||||
missedChunks === 1
|
||||
? "Reprise effectuée: 1 événement rattrapé pendant l'absence."
|
||||
: `Reprise effectuée: ${missedChunks} événements rattrapés pendant l'absence.`,
|
||||
};
|
||||
}
|
||||
|
||||
function mergeCompletedProgress(
|
||||
running: ReplyProgress,
|
||||
completed: ReplyProgress,
|
||||
@ -362,6 +352,26 @@ function completeRunningProgress(turns: ChatTurn[]): ChatTurn[] {
|
||||
});
|
||||
}
|
||||
|
||||
function isRecoverableNoResponseTurn(turn: ChatTurn | undefined): boolean {
|
||||
return turn?.role === "error" && turn.text === EMPTY_MODEL_RESPONSE_MESSAGE;
|
||||
}
|
||||
|
||||
function turnsIndicateBusy(turns: ChatTurn[]): boolean {
|
||||
for (let index = turns.length - 1; index >= 0; index -= 1) {
|
||||
const turn = turns[index];
|
||||
if (turn.role === "final" || turn.role === "error") return false;
|
||||
if (turn.role === "agent" && turn.pending) return true;
|
||||
if (
|
||||
turn.role === "progress" &&
|
||||
!turn.settled &&
|
||||
progressStageBucket(turn.progress) !== "completed"
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function fileExtension(mime: string): string {
|
||||
if (mime === "image/png") return "png";
|
||||
if (mime === "image/jpeg") return "jpg";
|
||||
@ -490,6 +500,9 @@ function foldChunk(turns: ChatTurn[], raw: unknown): ChatTurn[] {
|
||||
raw.kind !== "userPrompt" &&
|
||||
raw.kind !== "UserPrompt"
|
||||
) {
|
||||
if (isRecoverableNoResponseTurn(last) && raw.kind !== "error") {
|
||||
return foldChunk(turns.slice(0, -1), raw);
|
||||
}
|
||||
return turns;
|
||||
}
|
||||
switch (raw.kind) {
|
||||
@ -556,7 +569,7 @@ export function CustomAgentChatView({
|
||||
const [slashActiveIndex, setSlashActiveIndex] = useState(0);
|
||||
const [profileDialog, setProfileDialog] =
|
||||
useState<ProfileCommandDialogState | null>(null);
|
||||
const [opening, setOpening] = useState(false);
|
||||
const [opening, setOpening] = useState(Boolean(sessionId));
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [stopping, setStopping] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@ -793,14 +806,10 @@ export function CustomAgentChatView({
|
||||
if (!mountedRef.current) return reattached.sessionId;
|
||||
setCurrentSession(reattached.sessionId);
|
||||
if (options.applyScrollback) {
|
||||
const cachedCount = chatSessionViewCache.get(sid)?.chunkCount ?? 0;
|
||||
scrollbackChunkCountRef.current = Math.max(
|
||||
scrollbackChunkCountRef.current,
|
||||
reattached.scrollback.length,
|
||||
);
|
||||
scrollbackChunkCountRef.current = reattached.scrollback.length;
|
||||
const replayed = reattached.scrollback.reduce(foldChunk, [] as ChatTurn[]);
|
||||
const notice = reattachNotice(reattached.scrollback.length - cachedCount);
|
||||
setTurns(notice ? [...replayed, notice] : replayed);
|
||||
setTurns(replayed);
|
||||
setBusy(turnsIndicateBusy(replayed));
|
||||
}
|
||||
return reattached.sessionId;
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user