feat(ios): add Talk voice directive hint toggle (#18250)

* feat(ios): add Talk voice directive hint toggle

* docs(changelog): credit voice directive hint slice

---------

Co-authored-by: Mariano Belinky <mariano@mb-server-643.local>
This commit is contained in:
Mariano
2026-02-16 17:33:42 +00:00
committed by GitHub
parent d688188864
commit ad27716d3f
5 changed files with 36 additions and 3 deletions

View File

@@ -7,6 +7,7 @@ Docs: https://docs.openclaw.ai
### Changes
- Cron/Gateway: separate per-job webhook delivery (`delivery.mode = "webhook"`) from announce delivery, enforce valid HTTP(S) webhook URLs, and keep a temporary legacy `notify + cron.webhook` fallback for stored jobs. (#17901) Thanks @advaitpaliwal.
- iOS/Talk: add a `Voice Directive Hint` toggle for Talk Mode prompts so users can disable ElevenLabs voice-switching instructions to save tokens when not needed. (#18250) Thanks @zeulewan.
- Telegram/Agents: add inline button `style` support (`primary|success|danger`) across message tool schema, Telegram action parsing, send pipeline, and runtime prompt guidance. (#18241) Thanks @obviyus.
### Fixes

View File

@@ -15,6 +15,7 @@ struct SettingsTab: View {
@AppStorage("voiceWake.enabled") private var voiceWakeEnabled: Bool = false
@AppStorage("talk.enabled") private var talkEnabled: Bool = false
@AppStorage("talk.button.enabled") private var talkButtonEnabled: Bool = true
@AppStorage("talk.voiceDirectiveHint.enabled") private var talkVoiceDirectiveHintEnabled: Bool = true
@AppStorage("camera.enabled") private var cameraEnabled: Bool = true
@AppStorage("location.enabledMode") private var locationEnabledModeRaw: String = OpenClawLocationMode.off.rawValue
@AppStorage("location.preciseEnabled") private var locationPreciseEnabled: Bool = true
@@ -255,6 +256,10 @@ struct SettingsTab: View {
Text("Use this local override when gateway config redacts talk.apiKey for mobile clients.")
.font(.footnote)
.foregroundStyle(.secondary)
Toggle("Voice Directive Hint", isOn: self.$talkVoiceDirectiveHintEnabled)
Text("Include ElevenLabs voice switching instructions in the Talk Mode prompt. Disable to save tokens.")
.font(.footnote)
.foregroundStyle(.secondary)
// Keep this separate so users can hide the side bubble without disabling Talk Mode.
Toggle("Show Talk Button", isOn: self.$talkButtonEnabled)

View File

@@ -828,7 +828,11 @@ final class TalkModeManager: NSObject {
private func buildPrompt(transcript: String) -> String {
let interrupted = self.lastInterruptedAtSeconds
self.lastInterruptedAtSeconds = nil
return TalkPromptBuilder.build(transcript: transcript, interruptedAtSeconds: interrupted)
let includeVoiceDirectiveHint = (UserDefaults.standard.object(forKey: "talk.voiceDirectiveHint.enabled") as? Bool) ?? true
return TalkPromptBuilder.build(
transcript: transcript,
interruptedAtSeconds: interrupted,
includeVoiceDirectiveHint: includeVoiceDirectiveHint)
}
private enum ChatCompletionState: CustomStringConvertible {

View File

@@ -1,10 +1,19 @@
public enum TalkPromptBuilder: Sendable {
public static func build(transcript: String, interruptedAtSeconds: Double?) -> String {
public static func build(
transcript: String,
interruptedAtSeconds: Double?,
includeVoiceDirectiveHint: Bool = true
) -> String {
var lines: [String] = [
"Talk Mode active. Reply in a concise, spoken tone.",
"You may optionally prefix the response with JSON (first line) to set ElevenLabs voice (id or alias), e.g. {\"voice\":\"<id>\",\"once\":true}.",
]
if includeVoiceDirectiveHint {
lines.append(
"You may optionally prefix the response with JSON (first line) to set ElevenLabs voice (id or alias), e.g. {\"voice\":\"<id>\",\"once\":true}."
)
}
if let interruptedAtSeconds {
let formatted = String(format: "%.1f", interruptedAtSeconds)
lines.append("Assistant speech interrupted at \(formatted)s.")

View File

@@ -12,4 +12,18 @@ final class TalkPromptBuilderTests: XCTestCase {
let prompt = TalkPromptBuilder.build(transcript: "Hi", interruptedAtSeconds: 1.234)
XCTAssertTrue(prompt.contains("Assistant speech interrupted at 1.2s."))
}
func testBuildIncludesVoiceDirectiveHintByDefault() {
let prompt = TalkPromptBuilder.build(transcript: "Hello", interruptedAtSeconds: nil)
XCTAssertTrue(prompt.contains("ElevenLabs voice"))
}
func testBuildExcludesVoiceDirectiveHintWhenDisabled() {
let prompt = TalkPromptBuilder.build(
transcript: "Hello",
interruptedAtSeconds: nil,
includeVoiceDirectiveHint: false)
XCTAssertFalse(prompt.contains("ElevenLabs voice"))
XCTAssertTrue(prompt.contains("Talk Mode active."))
}
}