Skip to content

Conversation

@zanesq
Copy link
Collaborator

@zanesq zanesq commented Oct 17, 2025

Summary

  • fixed flickering when rendering messages (used a ref instead of a cache)
  • fixed initial load flickering popular topics before starting chat
  • brought back goose is working on it chatstate display
  • enhanced the logging and added a flag

@zanesq zanesq requested a review from DOsinga October 17, 2025 23:11
@DOsinga
Copy link
Collaborator

DOsinga commented Oct 18, 2025

this seems to remove the pre-caching though

<LoadingGoose
message={
messages.length === 0
messages.length === 0 && chatState === ChatState.Thinking
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should catch this all with chatState and then just have a mapping from chatState to message here I think

handleSubmit(initialMessage);
}
}, [initialMessage, session, messages, handleSubmit]);
}, [initialMessage, session, handleSubmit]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we just removed so much crufty hard to follow conditional code and now it is already coming back :(

this is trying to auto-submit initialMessage exactly once, but I don't think it works when switching sessions - hasSubmittedInitialMessage and initialMessagesLength never reset when sessionId changes. Even if we fixed that bug, the code is hard to follow with three separate refs, two effects with overlapping dependencies, and implicit coordination between them

I think we can make this work better if we move this into useChatStream; it can just decide after it has loaded the session from the server whether it should submit immediately and it is already scoped to the sessionId

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good call 👍

response: Response,
initialMessages: Message[],
setMessages: (messages: Message[], log: string) => void,
updateMessages: (messages: Message[]) => void,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any particular reason to rename this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just trying to avoid confusion and make it clear that its doing more than setMessages (setMessagesAndLog), really we're updating messages happy to change it back if you think that is confusing also

setMessagesAndLog(session?.conversation || [], 'load-session');
if (cancelled) return;

const sessionData = response.data;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why rename this? it is the session here

setSessionLoadError(undefined);
setChatState(ChatState.Idle);
}
}, [sessionId, setMessagesAndLog]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this useEffect has the same dependencies as the one below. I don't think there's a reason to have both. it also introduces a sessionIdRef that only checks if the sessionId has changed, but the sessionId is in the dependencies, so the useEffect already only runs when sessionId changes, no need for that ref.

I think all we want to do is add the resets to the block below

if (error instanceof Error && error.name !== 'AbortError') {
log.error('submit failed', error);
onFinish('Submit error: ' + error.message);
} else if (error instanceof Error && error.name === 'AbortError') {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can just be an else; but is also feels like duplication of what we're doing in stream from. so if we want to keep the exception catch here, we should remove the other one

})();

return () => {
cancelled = true;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea

@zanesq
Copy link
Collaborator Author

zanesq commented Oct 19, 2025

Unless I'm missing something I don't think the pre-caching was needed as we still have to wait for the server response and it was creating a race condition where the component had to coordinate between its own loaded state and useChatStream's state. Now we have a single source of truth in useChatStream and no flickering issues.

Was the pre-cache intended to avoid rendering the existing chat messages when switching back to the chat?

@zanesq zanesq requested a review from DOsinga October 19, 2025 16:55
return 'loading conversation...';
}
return undefined;
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I meant to say, but we can maybe do this in some other PR that we should move to a world where the useChatStream determines the chatState and BaseChat does not contain any logic. So isCompacting should just be a chatState, as should loading be. I checked and we are already doing this in the LoadingGoose component, so let's move the messaging we generate to one point


const resultsCache = new Map<string, { messages: Message[]; session: Session }>();
// Debug logging - set to false in production
const DEBUG_CHAT_STREAM = true;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should delete this once we are sure it works really

);

useEffect(() => {
if (initialMessage && session && messages.length === 0 && chatState === ChatState.Idle) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an improvement. I think we can do even better (maybe in a follow up) and make ChatState into a true statemachine that allows us to see exactly what's going on.

that way all we have to do here is depend on chatState and if it is autoSubmit and there is an initialMessage

@DOsinga
Copy link
Collaborator

DOsinga commented Oct 20, 2025

Unless I'm missing something I don't think the pre-caching was needed as we still have to wait for the server response and it was creating a race condition where the component had to coordinate between its own loaded state and useChatStream's state. Now we have a single source of truth in useChatStream and no flickering issues.

Was the pre-cache intended to avoid rendering the existing chat messages when switching back to the chat?

Be careful deleting things that you don't know what they are for; Chesterton had his fence.

so the goal of the precaching is that we have a version of the chat available immediately upon load when we hit a chat we've already worked with. in the current world switching back to a chat that you've worked with will reset the provider, model & extensions (in line with how it works now). this can be slow, so getting a copy immediately would be preferable.

I'm not sure I am convinced this was the reason for the flickering though; the flickering only seemed to happen when we created a new conversation from the hub, in which cache there was no pre-cache by definition - the flickering happened during the streaming when we already have a session and the precache should be overwritten. there could be a bug in all of this of course, but I think this is the way to do it.

@zanesq zanesq merged commit 26b8b42 into next-camp Oct 21, 2025
4 checks passed
@zanesq zanesq deleted the zane/nextcamp/flashing branch October 21, 2025 16:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants