feat(ui): summary generation + action-item confirmation in the meetings store (T7.1)
Streams tokens for whichever meeting is generating (gated by summarizingId so a stale stream can't clobber a summary if the user switches selection mid-generation), and refreshes the selected meeting once llm://done or confirm_action_items lands.
This commit is contained in:
@@ -1,7 +1,15 @@
|
||||
// Meetings list + selected-meeting detail (Phase 2, FR-STORE-5).
|
||||
// Svelte 5 runes store, same shape as recording.svelte.ts.
|
||||
|
||||
import { api, events, type Meeting, type MeetingId, type MeetingListItem } from "../api";
|
||||
import {
|
||||
api,
|
||||
errorMessage,
|
||||
events,
|
||||
type ActionItem,
|
||||
type Meeting,
|
||||
type MeetingId,
|
||||
type MeetingListItem,
|
||||
} from "../api";
|
||||
|
||||
class MeetingsStore {
|
||||
list = $state<MeetingListItem[]>([]);
|
||||
@@ -9,6 +17,13 @@ class MeetingsStore {
|
||||
selected = $state<Meeting | null>(null);
|
||||
loading = $state(false);
|
||||
|
||||
// Summary generation (T5.4/T5.5, FR-LLM-2/4) — streamed tokens for whichever
|
||||
// meeting is generating, so a stale stream can't overwrite a different
|
||||
// meeting's summary if the user switches selection mid-generation.
|
||||
summarizingId = $state<MeetingId | null>(null);
|
||||
summaryStream = $state("");
|
||||
summaryError = $state<string | null>(null);
|
||||
|
||||
async init() {
|
||||
await this.load();
|
||||
// A meeting finishing (live stop, or a recovered meeting resuming
|
||||
@@ -17,6 +32,30 @@ class MeetingsStore {
|
||||
await this.load();
|
||||
if (this.selectedId === meetingId) await this.select(meetingId);
|
||||
});
|
||||
await events.onLlmToken(({ meetingId, text }) => {
|
||||
if (this.summarizingId === meetingId) this.summaryStream += text;
|
||||
});
|
||||
await events.onLlmDone(async ({ meetingId }) => {
|
||||
if (this.summarizingId === meetingId) this.summarizingId = null;
|
||||
if (this.selectedId === meetingId) await this.select(meetingId);
|
||||
});
|
||||
}
|
||||
|
||||
async generateSummary(id: MeetingId, templateId?: string) {
|
||||
this.summarizingId = id;
|
||||
this.summaryStream = "";
|
||||
this.summaryError = null;
|
||||
try {
|
||||
await api.generateSummary(id, templateId);
|
||||
} catch (e) {
|
||||
this.summaryError = errorMessage(e);
|
||||
this.summarizingId = null;
|
||||
}
|
||||
}
|
||||
|
||||
async confirmActionItems(id: MeetingId, items: ActionItem[]) {
|
||||
await api.confirmActionItems(id, items);
|
||||
if (this.selectedId === id) await this.select(id);
|
||||
}
|
||||
|
||||
async load(query?: string) {
|
||||
|
||||
Reference in New Issue
Block a user