fix(teacher): 独立刷新老师目录以缩短配置更新延迟
This commit is contained in:
@@ -37,6 +37,7 @@ export const teacherApi = {
|
||||
sendConversation: (projectId: string, agentId: string, input: TeacherSend) => hostApiFetch<TeacherTopic>(
|
||||
teacherConversationsPath(projectId) + '/' + encodeURIComponent(agentId) + '/messages', { method: 'POST', body: JSON.stringify(input) }),
|
||||
catalog: () => hostApiFetch<TeacherCatalog>('/api/coding/teacher/teachers'),
|
||||
catalogRevision: () => hostApiFetch<{ revision: number }>('/api/coding/teacher/teachers/revision'),
|
||||
checkIn: (projectId: string, input: TeacherCheckInInput) =>
|
||||
hostApiFetch<TeacherCheckInResult>(`/api/coding/projects/${encodeURIComponent(projectId)}/teacher-check-in`, {
|
||||
method: 'POST',
|
||||
|
||||
@@ -141,11 +141,14 @@ export function useTeacherCompanion(options: Options) {
|
||||
const base = projectId ? teacherTopicsPath(projectId) : '';
|
||||
type LoadedCatalog = { enabled: boolean; selected?: TeacherCatalog['items'][number]; selectedId?: string };
|
||||
let configFlight: Promise<LoadedCatalog> | null = null;
|
||||
let catalogRevision: number | undefined;
|
||||
let checkingCatalog = false;
|
||||
const loadConfig = (): Promise<LoadedCatalog> => {
|
||||
if (configFlight) return configFlight;
|
||||
configFlight = (async () => {
|
||||
const [config, catalog] = await Promise.all([teacherApi.config(), teacherApi.catalog()]);
|
||||
if (!alive || scopeRef.current !== scope) return { enabled: false };
|
||||
catalogRevision = catalog.revision;
|
||||
const selection = (current: CompanionState) => {
|
||||
const selectedId = current.selectedAgent?.teacher_id ?? current.topic?.definition.config_id
|
||||
?? readLocal<string | undefined>(storageKey + ':selected-agent', undefined);
|
||||
@@ -215,10 +218,7 @@ export function useTeacherCompanion(options: Options) {
|
||||
}
|
||||
// Delivered agents own one chat per project. Project observations
|
||||
// have their own Main scheduler; this legacy timer must not create topics.
|
||||
if (currentState.selectedAgent || currentState.agents.length) {
|
||||
try { await loadConfig(); } catch { /* Keep last known delivered contacts. */ }
|
||||
return;
|
||||
}
|
||||
if (currentState.selectedAgent || currentState.agents.length) return;
|
||||
if (!projectId || current.sourceBusy || current.sourceArchived || !current.sourceId || (current.teacherOpen && current.teacherComposing)) return;
|
||||
if (Date.now() - lastAttempt < TEACHER_CHECK_IN_INTERVAL_MS) return;
|
||||
if (unreadInvitation(currentState)) return;
|
||||
@@ -252,13 +252,25 @@ export function useTeacherCompanion(options: Options) {
|
||||
}
|
||||
};
|
||||
const timer = window.setInterval(() => { void check(); }, 15_000);
|
||||
const onVisible = () => { void check(); };
|
||||
// Catalog freshness is independent of chat/check-in state and never dispatches a model.
|
||||
const refreshCatalog = async () => {
|
||||
if (!alive || checkingCatalog || document.visibilityState !== 'visible') return;
|
||||
checkingCatalog = true;
|
||||
try {
|
||||
const current = await teacherApi.catalogRevision();
|
||||
if (alive && scopeRef.current === scope && current.revision !== catalogRevision) await loadConfig();
|
||||
} catch { /* Keep the last usable catalog; the next visible tick retries. */ }
|
||||
finally { checkingCatalog = false; }
|
||||
};
|
||||
const catalogTimer = window.setInterval(() => { void refreshCatalog(); }, 5_000);
|
||||
const onVisible = () => { void refreshCatalog(); void check(); };
|
||||
window.addEventListener('focus', onVisible);
|
||||
document.addEventListener('visibilitychange', onVisible);
|
||||
return () => {
|
||||
alive = false;
|
||||
if (configLoader.current === loadConfig) configLoader.current = null;
|
||||
window.clearInterval(timer);
|
||||
window.clearInterval(catalogTimer);
|
||||
window.removeEventListener('focus', onVisible);
|
||||
document.removeEventListener('visibilitychange', onVisible);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user