惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

U
Unit 42
A
About on SuperTechFans
J
Java Code Geeks
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Stack Overflow Blog
Stack Overflow Blog
Recent Announcements
Recent Announcements
GbyAI
GbyAI
F
Fortinet All Blogs
Blog — PlanetScale
Blog — PlanetScale
MongoDB | Blog
MongoDB | Blog
MyScale Blog
MyScale Blog
aimingoo的专栏
aimingoo的专栏
H
Help Net Security
月光博客
月光博客
博客园 - Franky
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Apple Machine Learning Research
Apple Machine Learning Research
The Cloudflare Blog
WordPress大学
WordPress大学
S
SegmentFault 最新的问题
T
Tailwind CSS Blog
Jina AI
Jina AI
有赞技术团队
有赞技术团队
博客园_首页

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
fix(openai): respect disabled realtime input interruption...
steipete · 2026-05-10 · via Recent Commits to openclaw:main
Original file line numberDiff line numberDiff line change

@@ -16,6 +16,7 @@ Docs: https://docs.openclaw.ai

1616
1717

### Fixes

1818
19+

- OpenAI/realtime voice: honor disabled input-audio interruption locally so server VAD speech-start events do not clear Discord playback after operators set `interruptResponseOnInputAudio: false`.

1920

- Telegram: handle managed select button callbacks before the raw callback fallback while preserving delimiter-containing option values such as `env|prod`. (#79816) Thanks @moeedahmed.

2021

- CLI/media: let explicit image description model refs use bundled static provider catalogs and generic model-backed image hooks, so `openclaw infer image describe --model zai/glm-4.6v` works like direct model runs and Anthropic auth probes avoid stale Claude 3 Haiku catalog entries.

2122

- Browser: wait longer for existing-session Chrome MCP status and non-deep doctor probes so slow first attaches do not falsely report offline while keeping raw CDP status probes short. (#77473) Thanks @rubencu.

Original file line numberDiff line numberDiff line change

@@ -1377,6 +1377,7 @@ Expected voice logs:

13771377

- On realtime interruption: `discord voice: realtime model interrupt requested client:response.cancel reason=barge-in`, followed by either `discord voice: realtime model audio truncated client:conversation.item.truncate reason=barge-in audioEndMs=...` or `discord voice: realtime model interrupt confirmed server:response.done status=cancelled ...`

13781378

- On ignored echo/noise: `discord voice: realtime model interrupt ignored client:conversation.item.truncate.skipped reason=barge-in audioEndMs=0 minAudioEndMs=250`

13791379

- On disabled barge-in: `discord voice: realtime capture ignored during playback (barge-in disabled) ...`

1380+

- On idle playback: `discord voice: realtime barge-in ignored reason=... outputActive=false ... playbackChunks=0`

13801381
13811382

To debug cut-off audio, read the realtime voice logs as a timeline:

13821383

@@ -1404,6 +1405,7 @@ Common patterns:

14041405

- `source=speaker-start` followed by `speaker turn closed ... hasAudio=false` means Discord reported a speaker start but no audio reached OpenClaw. That can be a transient Discord voice event, noise gate behavior, or a client briefly keying the mic.

14051406

- `audio playback stopped reason=stream-close` without a nearby barge-in or `provider-clear-audio` means the local Discord playback stream ended unexpectedly. Check the preceding provider and Discord player logs.

14061407

- `capture ignored during playback (barge-in disabled)` means OpenClaw intentionally dropped input while assistant audio was active. Enable `voice.realtime.bargeIn` if you want speech to interrupt playback.

1408+

- `barge-in ignored ... outputActive=false` means Discord or provider VAD reported speech, but OpenClaw had no active playback to interrupt. This should not cut off audio.

14071409
14081410

Credentials are resolved per component: LLM route auth for `voice.model`, STT auth for `tools.media.audio`, TTS auth for `messages.tts`/`voice.tts`, and realtime provider auth for `voice.realtime.providers` or the provider's normal auth config.

14091411
Original file line numberDiff line numberDiff line change

@@ -579,7 +579,15 @@ describe("DiscordVoiceManager", () => {

579579
580580

const player = getLastAudioPlayer();

581581

const entry = getSessionEntry(manager);

582+

const bridgeParams = createRealtimeVoiceBridgeSessionMock.mock.calls.at(-1)?.[0] as

583+

| {

584+

audioSink?: {

585+

sendAudio: (audio: Buffer) => void;

586+

};

587+

}

588+

| undefined;

582589

player.state.status = "playing";

590+

bridgeParams?.audioSink?.sendAudio(Buffer.alloc(480));

583591
584592

await (

585593

manager as unknown as {

@@ -652,7 +660,7 @@ describe("DiscordVoiceManager", () => {

652660

expect(realtimeSessionMock.sendAudio).toHaveBeenCalled();

653661

});

654662
655-

it("interrupts provider response state even after local realtime playback has ended", async () => {

663+

it("does not interrupt realtime provider state when local playback is already idle", async () => {

656664

const manager = createManager({

657665

groupPolicy: "open",

658666

allowFrom: ["discord:u1"],

@@ -689,7 +697,7 @@ describe("DiscordVoiceManager", () => {

689697
690698

turn?.sendInputAudio(Buffer.alloc(3840));

691699
692-

expect(realtimeSessionMock.handleBargeIn).toHaveBeenCalled();

700+

expect(realtimeSessionMock.handleBargeIn).not.toHaveBeenCalled();

693701

expect(player.stop).not.toHaveBeenCalled();

694702

expect(realtimeSessionMock.sendAudio).toHaveBeenCalled();

695703

});

Original file line numberDiff line numberDiff line change

@@ -461,7 +461,8 @@ export class DiscordRealtimeVoiceSession implements VoiceRealtimeSession {

461461

`discord voice: realtime input audio started guild=${this.params.entry.guildId} channel=${this.params.entry.channelId} user=${turn.context.userId} speaker=${turn.context.speakerLabel} discordBytes=${discordPcm48kStereo.length} realtimeBytes=${realtimePcm.length} outputAudioMs=${Math.floor(this.outputAudioTimestampMs)} outputActive=${this.isOutputAudioActive()}`,

462462

);

463463

}

464-

if (!turn.interruptedPlayback && this.isBargeInEnabled()) {

464+

const outputActive = this.hasInterruptibleOutputAudio();

465+

if (!turn.interruptedPlayback && this.isBargeInEnabled() && outputActive) {

465466

turn.interruptedPlayback = true;

466467

logVoiceVerbose(

467468

`realtime barge-in from active speaker audio: guild ${this.params.entry.guildId} channel ${this.params.entry.channelId} user ${turn.context.userId}`,

@@ -482,7 +483,13 @@ export class DiscordRealtimeVoiceSession implements VoiceRealtimeSession {

482483

);

483484

return;

484485

}

485-

this.syncOutputAudioTimestamp();

486+

const outputActive = this.hasInterruptibleOutputAudio();

487+

if (!outputActive) {

488+

logger.info(

489+

`discord voice: realtime barge-in ignored reason=${reason} outputActive=false guild=${this.params.entry.guildId} channel=${this.params.entry.channelId} playbackChunks=${this.outputAudioChunks}`,

490+

);

491+

return;

492+

}

486493

logger.info(

487494

`discord voice: realtime barge-in requested reason=${reason} guild=${this.params.entry.guildId} channel=${this.params.entry.channelId} outputAudioMs=${Math.floor(this.outputAudioTimestampMs)} outputActive=${this.isOutputAudioActive()} playbackChunks=${this.outputAudioChunks}`,

488495

);

@@ -497,6 +504,13 @@ export class DiscordRealtimeVoiceSession implements VoiceRealtimeSession {

497504

});

498505

}

499506
507+

private hasInterruptibleOutputAudio(): boolean {

508+

this.syncOutputAudioTimestamp();

509+

return (

510+

this.isOutputAudioActive() || this.outputAudioChunks > 0 || this.outputAudioTimestampMs > 0

511+

);

512+

}

513+
500514

private get realtimeConfig(): DiscordRealtimeVoiceConfig {

501515

return this.params.discordConfig.voice?.realtime;

502516

}

Original file line numberDiff line numberDiff line change

@@ -574,6 +574,55 @@ describe("buildOpenAIRealtimeVoiceProvider", () => {

574574

});

575575

});

576576
577+

it("does not locally clear playback on speech-start events when input interruption is disabled", async () => {

578+

const provider = buildOpenAIRealtimeVoiceProvider();

579+

const onAudio = vi.fn();

580+

const onClearAudio = vi.fn();

581+

const bridge = provider.createBridge({

582+

providerConfig: { apiKey: "sk-test" }, // pragma: allowlist secret

583+

autoRespondToAudio: true,

584+

interruptResponseOnInputAudio: false,

585+

onAudio,

586+

onClearAudio,

587+

});

588+

const connecting = bridge.connect();

589+

const socket = FakeWebSocket.instances[0];

590+

if (!socket) {

591+

throw new Error("expected bridge to create a websocket");

592+

}

593+
594+

socket.readyState = FakeWebSocket.OPEN;

595+

socket.emit("open");

596+

socket.emit("message", Buffer.from(JSON.stringify({ type: "session.updated" })));

597+

await connecting;

598+
599+

socket.emit(

600+

"message",

601+

Buffer.from(JSON.stringify({ type: "response.created", response: { id: "resp_1" } })),

602+

);

603+

socket.emit(

604+

"message",

605+

Buffer.from(

606+

JSON.stringify({

607+

type: "response.audio.delta",

608+

item_id: "item_1",

609+

delta: Buffer.from("assistant audio").toString("base64"),

610+

}),

611+

),

612+

);

613+

socket.emit(

614+

"message",

615+

Buffer.from(JSON.stringify({ type: "input_audio_buffer.speech_started" })),

616+

);

617+
618+

expect(onAudio).toHaveBeenCalledTimes(1);

619+

expect(onClearAudio).not.toHaveBeenCalled();

620+

expect(parseSent(socket)).not.toContainEqual({ type: "response.cancel" });

621+

expect(parseSent(socket)).not.toContainEqual(

622+

expect.objectContaining({ type: "conversation.item.truncate" }),

623+

);

624+

});

625+
577626

it("keeps assistant playback active on server VAD when automatic audio responses are disabled", async () => {

578627

const provider = buildOpenAIRealtimeVoiceProvider();

579628

const onAudio = vi.fn();

Original file line numberDiff line numberDiff line change

@@ -741,7 +741,7 @@ class OpenAIRealtimeVoiceBridge implements RealtimeVoiceBridge {

741741

}

742742
743743

case "input_audio_buffer.speech_started":

744-

if (this.config.autoRespondToAudio ?? true) {

744+

if (this.config.interruptResponseOnInputAudio ?? this.config.autoRespondToAudio ?? true) {

745745

this.handleBargeIn();

746746

}

747747

return;