






























@@ -594,6 +594,100 @@ describe("DiscordVoiceManager", () => {
594594);
595595});
596596597+it("interrupts realtime playback when an already-active speaker keeps talking", async () => {
598+const connection = createConnectionMock();
599+joinVoiceChannelMock.mockReturnValueOnce(connection);
600+const manager = createManager({
601+groupPolicy: "open",
602+allowFrom: ["discord:u1"],
603+voice: {
604+enabled: true,
605+mode: "bidi",
606+realtime: {
607+provider: "openai",
608+bargeIn: true,
609+providers: {
610+openai: {
611+interruptResponseOnInputAudio: false,
612+},
613+},
614+},
615+},
616+});
617+618+await manager.join({ guildId: "g1", channelId: "1001" });
619+620+const entry = getSessionEntry(manager) as {
621+realtime?: {
622+beginSpeakerTurn: (
623+context: { extraSystemPrompt?: string; senderIsOwner: boolean; speakerLabel: string },
624+userId: string,
625+) => { close: () => void; sendInputAudio: (audio: Buffer) => void };
626+};
627+};
628+const bridgeParams = createRealtimeVoiceBridgeSessionMock.mock.calls.at(-1)?.[0] as
629+| {
630+audioSink?: {
631+sendAudio: (audio: Buffer) => void;
632+};
633+}
634+| undefined;
635+const player = getLastAudioPlayer();
636+const turn = entry.realtime?.beginSpeakerTurn(
637+{ extraSystemPrompt: undefined, senderIsOwner: true, speakerLabel: "Owner" },
638+"u1",
639+);
640+641+bridgeParams?.audioSink?.sendAudio(Buffer.alloc(480));
642+turn?.sendInputAudio(Buffer.alloc(3840));
643+644+expect(realtimeSessionMock.handleBargeIn).toHaveBeenCalled();
645+expect(player.stop).toHaveBeenCalledWith(true);
646+expect(realtimeSessionMock.sendAudio).toHaveBeenCalled();
647+});
648+649+it("interrupts provider response state even after local realtime playback has ended", async () => {
650+const manager = createManager({
651+groupPolicy: "open",
652+allowFrom: ["discord:u1"],
653+voice: {
654+enabled: true,
655+mode: "bidi",
656+realtime: {
657+provider: "openai",
658+bargeIn: true,
659+providers: {
660+openai: {
661+interruptResponseOnInputAudio: false,
662+},
663+},
664+},
665+},
666+});
667+668+await manager.join({ guildId: "g1", channelId: "1001" });
669+670+const entry = getSessionEntry(manager) as {
671+realtime?: {
672+beginSpeakerTurn: (
673+context: { extraSystemPrompt?: string; senderIsOwner: boolean; speakerLabel: string },
674+userId: string,
675+) => { close: () => void; sendInputAudio: (audio: Buffer) => void };
676+};
677+};
678+const player = getLastAudioPlayer();
679+const turn = entry.realtime?.beginSpeakerTurn(
680+{ extraSystemPrompt: undefined, senderIsOwner: true, speakerLabel: "Owner" },
681+"u1",
682+);
683+684+turn?.sendInputAudio(Buffer.alloc(3840));
685+686+expect(realtimeSessionMock.handleBargeIn).toHaveBeenCalled();
687+expect(player.stop).toHaveBeenCalledWith(true);
688+expect(realtimeSessionMock.sendAudio).toHaveBeenCalled();
689+});
690+597691it("ignores realtime capture during playback when barge-in is disabled", async () => {
598692const connection = createConnectionMock();
599693joinVoiceChannelMock.mockReturnValueOnce(connection);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。