fix: require explicit TTS intent · openclaw/openclaw@c026052
steipete
·
2026-05-02
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -19,6 +19,7 @@ Docs: https://docs.openclaw.ai
|
19 | 19 | ### Fixes |
20 | 20 | |
21 | 21 | - TTS/Telegram: keep trusted local audio generated by the TTS tool queued for voice-note delivery even when the run-level built-in tool list omits the raw `tts` name. Fixes #74752. Thanks @Loveworld3033 and @andyliu. |
| 22 | +- TTS: require explicit user or config audio intent for the agent speech tool so dashboard chats stay text unless audio is requested. Fixes #69777. Thanks @alexandre-leng. |
22 | 23 | - Heartbeat: strip legacy `[TOOL_CALL]...[/TOOL_CALL]` and `[TOOL_RESULT]...[/TOOL_RESULT]` pseudo-call blocks from heartbeat replies before channel delivery. Fixes #54138. Thanks @Deniable9570. |
23 | 24 | - macOS/Voice Wake: send wake-word and Push-to-Talk transcripts through the selected macOS session target instead of always falling back to main WebChat. Fixes #51040. Thanks @carl-jeffrolc. |
24 | 25 | - Providers/xAI: give Grok `web_search` a 60s default timeout, harden malformed xAI Responses parsing, and return structured timeout errors instead of aborting the tool call. Fixes #58063 and #58733. Thanks @dnishimura, @marvcasasola-svg, and @Nanako0129. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -48,6 +48,9 @@ audio attachments everywhere else, and PCM/Ulaw streams for telephony and Talk.
|
48 | 48 | <Note> |
49 | 49 | Auto-TTS is **off** by default. When `messages.tts.provider` is unset, |
50 | 50 | OpenClaw picks the first configured provider in registry auto-select order. |
| 51 | +The built-in `tts` agent tool is explicit-intent only: ordinary chat stays |
| 52 | +text unless the user asks for audio, uses `/tts`, or enables Auto-TTS/directive |
| 53 | +speech. |
51 | 54 | </Note> |
52 | 55 | |
53 | 56 | ## Supported providers |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -168,6 +168,27 @@ describe("createOpenClawTools TTS config wiring", () => {
|
168 | 168 | } |
169 | 169 | }); |
170 | 170 | |
| 171 | +it("keeps direct TTS tool guidance explicit even when the tool is available", async () => { |
| 172 | +const { __testing, createOpenClawTools } = await import("./openclaw-tools.js"); |
| 173 | +__testing.setDepsForTest({ config: {} }); |
| 174 | + |
| 175 | +try { |
| 176 | +const tool = createOpenClawTools({ |
| 177 | +disableMessageTool: true, |
| 178 | +disablePluginTools: true, |
| 179 | +}).find((candidate) => candidate.name === "tts"); |
| 180 | + |
| 181 | +if (!tool) { |
| 182 | +throw new Error("missing tts tool"); |
| 183 | +} |
| 184 | + |
| 185 | +expect(tool.description).toContain("Use only for explicit audio intent"); |
| 186 | +expect(tool.description).toContain("Never use for ordinary text replies"); |
| 187 | +} finally { |
| 188 | +__testing.setDepsForTest(); |
| 189 | +} |
| 190 | +}); |
| 191 | + |
171 | 192 | it("passes the resolved session agent id into the tts tool", async () => { |
172 | 193 | const injectedConfig = { |
173 | 194 | agents: { |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -17,6 +17,14 @@ describe("createTtsTool", () => {
|
17 | 17 | expect(tool.description).toContain(SILENT_REPLY_TOKEN); |
18 | 18 | }); |
19 | 19 | |
| 20 | +it("requires explicit user or config audio intent in guidance text", () => { |
| 21 | +const tool = createTtsTool(); |
| 22 | + |
| 23 | +expect(tool.description).toContain("Use only for explicit audio intent"); |
| 24 | +expect(tool.description).toContain("active TTS config"); |
| 25 | +expect(tool.description).toContain("Never use for ordinary text replies"); |
| 26 | +}); |
| 27 | + |
20 | 28 | it("stores audio delivery in details.media and preserves the spoken text in content", async () => { |
21 | 29 | textToSpeechSpy.mockResolvedValue({ |
22 | 30 | success: true, |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -64,7 +64,9 @@ export function createTtsTool(opts?: {
|
64 | 64 | label: "TTS", |
65 | 65 | name: "tts", |
66 | 66 | displaySummary: "Convert text to speech and return audio.", |
67 | | -description: `Convert text to speech. Audio is delivered automatically from the tool result — reply with ${SILENT_REPLY_TOKEN} after a successful call to avoid duplicate messages.`, |
| 67 | +description: |
| 68 | +"Use only for explicit audio intent (audio, voice, speech, TTS) or active TTS config. Never use for ordinary text replies. " + |
| 69 | +`Audio is delivered automatically from the tool result — reply with ${SILENT_REPLY_TOKEN} after a successful call to avoid duplicate messages.`, |
68 | 70 | parameters: TtsToolSchema, |
69 | 71 | execute: async (_toolCallId, args) => { |
70 | 72 | const params = args as Record<string, unknown>; |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -46,6 +46,17 @@ describe("shouldAttemptTtsPayload", () => {
|
46 | 46 | expect(shouldAttemptTtsPayload({ cfg: {} as OpenClawConfig })).toBe(false); |
47 | 47 | }); |
48 | 48 | |
| 49 | +it("does not infer automatic TTS from a dashboard text turn without opt-in state", () => { |
| 50 | +expect( |
| 51 | +shouldAttemptTtsPayload({ |
| 52 | +cfg: {} as OpenClawConfig, |
| 53 | +agentId: "main", |
| 54 | +channelId: "webchat", |
| 55 | +accountId: "dashboard", |
| 56 | +}), |
| 57 | +).toBe(false); |
| 58 | +}); |
| 59 | + |
49 | 60 | it("honors session auto state before prefs and config", () => { |
50 | 61 | writeFileSync(prefsPath, JSON.stringify({ tts: { auto: "off" } })); |
51 | 62 | const cfg = { messages: { tts: { auto: "off" } } } as OpenClawConfig; |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。