Skip to content

Commit c38a9a8

Browse files
authored
fix: label meeting note transcript speakers
Include speaker-labeled transcript lines in Meeting Notes summaries and structured summary artifacts.
1 parent 8f783cd commit c38a9a8

4 files changed

Lines changed: 34 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ Docs: https://docs.openclaw.ai
6565
### Fixes
6666

6767
- Gateway/restart: honor the configured restart drain budget for embedded runs and avoid spending the deferral timeout twice after forced restart timeouts. (#85708) Thanks @Kaspre.
68+
- Meeting Notes: include a speaker-labeled transcript section in generated summaries so Discord group voice captures show who said each captured utterance.
6869
- Discord/voice: recover stale realtime playback state when Discord stream-close/player-idle events do not arrive, and keep generated runtime plugin aliases available after postbuild rewrites.
6970
- Discord/voice: keep realtime playback running when meeting notes attaches to an existing voice session or a realtime consult starts, and route realtime user transcripts into meeting notes.
7071
- Config/secrets: preflight active runtime SecretRefs before root and include config writes persist, and roll back unchanged file/env state when post-write refresh fails. Fixes #46531. (#84454) Thanks @samzong.

docs/plugins/meeting-notes.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,9 +249,10 @@ Each file has one job:
249249
and provider metadata.
250250
- `transcript.jsonl`: append-only speaker utterances. Each line is one JSON
251251
object with the utterance text and the session id.
252-
- `summary.json`: structured summary data used by tooling.
252+
- `summary.json`: structured summary data used by tooling, including the
253+
speaker-labeled transcript window used for the generated summary.
253254
- `summary.md`: human-readable notes for terminals, editors, and document
254-
workflows.
255+
workflows, including a speaker-labeled transcript section.
255256

256257
The date directory comes from the session start time, so multiple meetings per
257258
day stay grouped. If a human session id repeats across days, use the
@@ -299,7 +300,8 @@ multi-hour call does not require unbounded summary memory.
299300

300301
This means the transcript can keep growing on disk, while summarization stays
301302
bounded. Increase `maxUtterances` when you need more of a multi-hour meeting in
302-
the generated summary. Decrease it when summaries are too slow or too large.
303+
the generated summary and speaker-labeled transcript section. Decrease it when
304+
summaries are too slow or too large.
303305

304306
Current summaries are generated when a session stops, after an import, or when
305307
the `summarize` action runs. They are not continuously rewritten for every

extensions/meeting-notes/index.test.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,13 @@ describe("meeting-notes plugin", () => {
9696
path.join(stateDir, "meeting-notes", currentDateDir(), "design-review", "summary.md"),
9797
"utf8",
9898
),
99-
).resolves.toContain("Action item: add Slack import later.");
99+
).resolves.toContain("Sam: Action item: add Slack import later.");
100+
await expect(
101+
fs.readFile(
102+
path.join(stateDir, "meeting-notes", currentDateDir(), "design-review", "summary.json"),
103+
"utf8",
104+
),
105+
).resolves.toContain('"Alex: We decided to ship Discord first."');
100106
await expect(
101107
fs.readFile(
102108
path.join(stateDir, "meeting-notes", currentDateDir(), "design-review", "transcript.jsonl"),
@@ -129,6 +135,8 @@ describe("meeting-notes plugin", () => {
129135
);
130136
expect(summary).toContain("Decision: ship the final plan.");
131137
expect(summary).not.toContain("Action item: write the first draft.");
138+
expect(summary).toContain("## Transcript");
139+
expect(summary).toContain("Sam: Decision: ship the final plan.");
132140
const transcript = await fs.readFile(
133141
path.join(stateDir, "meeting-notes", currentDateDir(), "long-meeting", "transcript.jsonl"),
134142
"utf8",

extensions/meeting-notes/src/summary.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export type MeetingNotesSummary = {
88
title: string;
99
generatedAt: string;
1010
overview: string;
11+
transcript: string[];
1112
decisions: string[];
1213
actionItems: string[];
1314
risks: string[];
@@ -36,14 +37,24 @@ function firstSentences(utterances: MeetingNotesUtterance[], limit: number): str
3637
function collectMatches(utterances: MeetingNotesUtterance[], pattern: RegExp): string[] {
3738
return utterances
3839
.filter((utterance) => pattern.test(utterance.text))
39-
.map((utterance) => {
40-
const speaker = utterance.speaker?.label ? `${utterance.speaker.label}: ` : "";
41-
return `${speaker}${utterance.text.trim()}`;
42-
})
40+
.map(formatSpeakerLine)
4341
.filter(Boolean)
4442
.slice(0, 12);
4543
}
4644

45+
function formatSpeakerLine(utterance: MeetingNotesUtterance): string {
46+
const text = utterance.text.trim();
47+
if (!text) {
48+
return "";
49+
}
50+
const speaker = utterance.speaker?.label?.trim();
51+
return speaker ? `${speaker}: ${text}` : text;
52+
}
53+
54+
function formatTranscript(utterances: MeetingNotesUtterance[]): string[] {
55+
return utterances.map(formatSpeakerLine).filter(Boolean);
56+
}
57+
4758
export function summarizeMeetingNotes(params: {
4859
session: MeetingNotesSessionDescriptor;
4960
utterances: MeetingNotesUtterance[];
@@ -55,6 +66,7 @@ export function summarizeMeetingNotes(params: {
5566
title,
5667
generatedAt: new Date().toISOString(),
5768
overview,
69+
transcript: formatTranscript(params.utterances),
5870
decisions: collectMatches(params.utterances, DECISION_PATTERNS),
5971
actionItems: collectMatches(params.utterances, ACTION_PATTERNS),
6072
risks: collectMatches(params.utterances, RISK_PATTERNS),
@@ -76,6 +88,9 @@ export function renderMeetingNotesMarkdown(summary: MeetingNotesSummary): string
7688
"## Overview",
7789
summary.overview,
7890
"",
91+
"## Transcript",
92+
renderList(summary.transcript),
93+
"",
7994
"## Decisions",
8095
renderList(summary.decisions),
8196
"",

0 commit comments

Comments
 (0)