
























@@ -9,6 +9,62 @@ import {
99stopTalkTranscriptionRelaySession,
1010} from "./talk-transcription-relay.js";
111112+type BroadcastEvent = { event: string; payload: unknown; connIds: string[] };
13+14+function isRecord(value: unknown): value is Record<string, unknown> {
15+return typeof value === "object" && value !== null && !Array.isArray(value);
16+}
17+18+function requireRecord(value: unknown, label: string): Record<string, unknown> {
19+expect(isRecord(value), `${label} must be an object`).toBe(true);
20+return value as Record<string, unknown>;
21+}
22+23+function expectRecordFields(
24+value: unknown,
25+label: string,
26+expected: Record<string, unknown>,
27+): Record<string, unknown> {
28+const record = requireRecord(value, label);
29+for (const [key, expectedValue] of Object.entries(expected)) {
30+expect(record[key], `${label}.${key}`).toEqual(expectedValue);
31+}
32+return record;
33+}
34+35+function findPayloadByType(events: BroadcastEvent[], type: string): Record<string, unknown> {
36+const event = events.find((candidate) => {
37+const payload = candidate.payload;
38+return isRecord(payload) && payload.type === type;
39+});
40+if (!event) {
41+throw new Error(`expected relay event type ${type}`);
42+}
43+expect(event.event).toBe("talk.event");
44+return requireRecord(event.payload, `${type} payload`);
45+}
46+47+function findPayloadByTalkEventType(
48+events: BroadcastEvent[],
49+type: string,
50+): Record<string, unknown> {
51+const event = events.find((candidate) => {
52+const payload = candidate.payload;
53+return isRecord(payload) && isRecord(payload.talkEvent) && payload.talkEvent.type === type;
54+});
55+if (!event) {
56+throw new Error(`expected talk event type ${type}`);
57+}
58+return requireRecord(event.payload, `${type} payload`);
59+}
60+61+function expectTalkEventFields(
62+payload: Record<string, unknown>,
63+expected: Record<string, unknown>,
64+): Record<string, unknown> {
65+return expectRecordFields(payload.talkEvent, "talk event", expected);
66+}
67+1268describe("talk transcription gateway relay", () => {
1369afterEach(() => {
1470clearTalkTranscriptionRelaySessionsForTest();
@@ -51,16 +107,16 @@ describe("talk transcription gateway relay", () => {
51107});
52108await Promise.resolve();
5310954-expect(session).toMatchObject({
110+expectRecordFields(session, "session", {
55111provider: "stt-test",
56112mode: "transcription",
57113transport: "gateway-relay",
58-audio: {
59-inputEncoding: "pcm16",
60-inputSampleRateHz: 24000,
61-},
62114});
63-expect(sttRequest).toMatchObject({
115+expectRecordFields(session.audio, "session audio", {
116+inputEncoding: "pcm16",
117+inputSampleRateHz: 24000,
118+});
119+expectRecordFields(sttRequest, "stt request", {
64120providerConfig: { model: "stt-model" },
65121});
66122@@ -76,78 +132,72 @@ describe("talk transcription gateway relay", () => {
7613277133expect(sttSession.sendAudio).toHaveBeenCalledWith(Buffer.from("audio-in"));
78134expect(sttSession.close).toHaveBeenCalledOnce();
79-expect(events).toEqual(
80-expect.arrayContaining([
81-expect.objectContaining({
82-event: "talk.event",
83-connIds: ["conn-1"],
84-payload: expect.objectContaining({
85-transcriptionSessionId: session.transcriptionSessionId,
86-type: "ready",
87-talkEvent: expect.objectContaining({
88-sessionId: session.transcriptionSessionId,
89-type: "session.ready",
90-mode: "transcription",
91-transport: "gateway-relay",
92-brain: "none",
93-provider: "stt-test",
94-}),
95-}),
96-}),
97-expect.objectContaining({
98-payload: expect.objectContaining({
99-transcriptionSessionId: session.transcriptionSessionId,
100-type: "speechStart",
101-talkEvent: expect.objectContaining({ type: "turn.started", turnId: "turn-1" }),
102-}),
103-}),
104-expect.objectContaining({
105-payload: expect.objectContaining({
106-transcriptionSessionId: session.transcriptionSessionId,
107-type: "partial",
108-text: "hel",
109-talkEvent: expect.objectContaining({
110-type: "transcript.delta",
111-turnId: "turn-1",
112-payload: { text: "hel" },
113-}),
114-}),
115-}),
116-expect.objectContaining({
117-payload: expect.objectContaining({
118-transcriptionSessionId: session.transcriptionSessionId,
119-type: "transcript",
120-text: "hello world",
121-final: true,
122-talkEvent: expect.objectContaining({
123-type: "transcript.done",
124-turnId: "turn-1",
125-final: true,
126-payload: { text: "hello world" },
127-}),
128-}),
129-}),
130-expect.objectContaining({
131-payload: expect.objectContaining({
132-transcriptionSessionId: session.transcriptionSessionId,
133-type: "inputAudio",
134-byteLength: 8,
135-talkEvent: expect.objectContaining({ type: "input.audio.delta" }),
136-}),
137-}),
138-expect.objectContaining({
139-payload: expect.objectContaining({
140-transcriptionSessionId: session.transcriptionSessionId,
141-type: "close",
142-reason: "completed",
143-talkEvent: expect.objectContaining({
144-type: "session.closed",
145-final: true,
146-}),
147-}),
148-}),
149-]),
150-);
135+const readyPayload = findPayloadByType(events, "ready");
136+expect(events.find((event) => event.payload === readyPayload)?.connIds).toEqual(["conn-1"]);
137+expectRecordFields(readyPayload, "ready payload", {
138+transcriptionSessionId: session.transcriptionSessionId,
139+type: "ready",
140+});
141+expectTalkEventFields(readyPayload, {
142+sessionId: session.transcriptionSessionId,
143+type: "session.ready",
144+mode: "transcription",
145+transport: "gateway-relay",
146+brain: "none",
147+provider: "stt-test",
148+});
149+150+const speechStartPayload = findPayloadByType(events, "speechStart");
151+expectRecordFields(speechStartPayload, "speechStart payload", {
152+transcriptionSessionId: session.transcriptionSessionId,
153+type: "speechStart",
154+});
155+expectTalkEventFields(speechStartPayload, { type: "turn.started", turnId: "turn-1" });
156+157+const partialPayload = findPayloadByType(events, "partial");
158+expectRecordFields(partialPayload, "partial payload", {
159+transcriptionSessionId: session.transcriptionSessionId,
160+type: "partial",
161+text: "hel",
162+});
163+expectTalkEventFields(partialPayload, {
164+type: "transcript.delta",
165+turnId: "turn-1",
166+payload: { text: "hel" },
167+});
168+169+const transcriptPayload = findPayloadByType(events, "transcript");
170+expectRecordFields(transcriptPayload, "transcript payload", {
171+transcriptionSessionId: session.transcriptionSessionId,
172+type: "transcript",
173+text: "hello world",
174+final: true,
175+});
176+expectTalkEventFields(transcriptPayload, {
177+type: "transcript.done",
178+turnId: "turn-1",
179+final: true,
180+payload: { text: "hello world" },
181+});
182+183+const audioPayload = findPayloadByType(events, "inputAudio");
184+expectRecordFields(audioPayload, "input audio payload", {
185+transcriptionSessionId: session.transcriptionSessionId,
186+type: "inputAudio",
187+byteLength: 8,
188+});
189+expectTalkEventFields(audioPayload, { type: "input.audio.delta" });
190+191+const closePayload = findPayloadByType(events, "close");
192+expectRecordFields(closePayload, "close payload", {
193+transcriptionSessionId: session.transcriptionSessionId,
194+type: "close",
195+reason: "completed",
196+});
197+expectTalkEventFields(closePayload, {
198+type: "session.closed",
199+final: true,
200+});
151201});
152202153203it("cancels an active transcription turn and closes the provider session", async () => {
@@ -192,27 +242,22 @@ describe("talk transcription gateway relay", () => {
192242});
193243194244expect(sttSession.close).toHaveBeenCalledOnce();
195-expect(events).toEqual(
196-expect.arrayContaining([
197-expect.objectContaining({
198-payload: expect.objectContaining({
199-transcriptionSessionId: session.transcriptionSessionId,
200-talkEvent: expect.objectContaining({
201-type: "turn.cancelled",
202-turnId: "turn-1",
203-payload: { reason: "barge-in" },
204-final: true,
205-}),
206-}),
207-}),
208-expect.objectContaining({
209-payload: expect.objectContaining({
210-transcriptionSessionId: session.transcriptionSessionId,
211-type: "close",
212-reason: "completed",
213-}),
214-}),
215-]),
216-);
245+const cancelledPayload = findPayloadByTalkEventType(events, "turn.cancelled");
246+expectRecordFields(cancelledPayload, "cancelled payload", {
247+transcriptionSessionId: session.transcriptionSessionId,
248+});
249+expectTalkEventFields(cancelledPayload, {
250+type: "turn.cancelled",
251+turnId: "turn-1",
252+payload: { reason: "barge-in" },
253+final: true,
254+});
255+256+const closePayload = findPayloadByType(events, "close");
257+expectRecordFields(closePayload, "close payload", {
258+transcriptionSessionId: session.transcriptionSessionId,
259+type: "close",
260+reason: "completed",
261+});
217262});
218263});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。