[eric] composer: stop rotating ghost-text guesses + only predict after real usage (4+ chats)

This commit is contained in:
ciregenz
2026-07-21 23:59:47 -07:00
parent 237e8f90b2
commit 8dfe6fbda4
2 changed files with 11 additions and 11 deletions
@@ -17,6 +17,10 @@ logger = logging.getLogger(__name__)
MAX_TOPICS = 24
MAX_SUGGESTIONS = 5
# Don't predict someone's next prompt until we ACTUALLY know their patterns. Below this many real
# past chats, any guess is just noise (onboarding starters alone are what they browsed at setup, not
# a read on what they want now), so we stay silent and let the neutral placeholder stand.
MIN_REAL_TOPICS = 4
# Names the aux title-gen hands out for empty/greeting chats; they carry no topic signal.
P_SKIP_NAMES = {"untitled", "new chat", "greeting", "chat", ""}
@@ -70,8 +74,9 @@ async def predict_prompts(count: int = MAX_SUGGESTIONS) -> List[str]:
for s in (global_settings.personalized_starters or [])
if getattr(s, "prompt", None)
]
# Nothing to personalize from: let the composer keep its static placeholder.
if not topics and not starters:
# Only predict once there's a real track record. Onboarding starters can enrich a prediction
# but never trigger one on their own: a brand-new user hasn't shown us what they want yet.
if len(topics) < MIN_REAL_TOPICS:
return []
aux_model = (await resolve_aux_model(global_settings, preferred_tier="haiku"))[0]
@@ -99,7 +99,6 @@ const DashboardToolbar = React.forwardRef<HTMLDivElement, Props>(
// load (cached), then one is shown at a time and cycled while the composer sits idle+empty. Empty
// list (no signal / no provider / error) just leaves the static "What should I do sir..." placeholder.
const [ghostList, setGhostList] = useState<string[]>([]);
const [ghostIdx, setGhostIdx] = useState(0);
const ghostFetchedRef = useRef(false);
useEffect(() => {
if (!inputOpen || ghostFetchedRef.current) return;
@@ -116,14 +115,10 @@ const DashboardToolbar = React.forwardRef<HTMLDivElement, Props>(
} catch { /* fail open: keep the static placeholder */ }
})();
}, [inputOpen]);
// Rotate the visible suggestion every few seconds while the composer is open, so the user sees a
// few different ideas instead of one. Cheap; the list is already fetched and cached.
useEffect(() => {
if (!inputOpen || ghostList.length <= 1) return undefined;
const t = setInterval(() => setGhostIdx((i) => (i + 1) % ghostList.length), 4500);
return () => clearInterval(t);
}, [inputOpen, ghostList.length]);
const ghostSuggestion = ghostList.length ? ghostList[ghostIdx % ghostList.length] : undefined;
// ONE stable suggestion, never a rotating carousel: cycling guesses every few seconds reads as
// "the app is throwing darts." The backend only returns anything when it has real usage history to
// predict from (see predict_prompts.py), so an empty list just leaves the neutral placeholder.
const ghostSuggestion = ghostList.length ? ghostList[0] : undefined;
// Reset defaults on each new compose session so in-session picks don't leak into the next new-chat draft.
const prevInputOpen = useRef(false);