Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions scripts/prepare-berdctl-sidecar.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ BUNDLE_BERD_CALL=0
if [[ "$TRIPLE" == *apple-darwin && "${BERD_CALL_BUNDLE:-1}" == "1" ]]; then
BUNDLE_BERD_CALL=1
CARGO_ARGS+=(-p berd-call)
# The CLI's twin of the app's block-telemetry-enforced, from the same gate.
if [[ "${VITE_TELEMETRY_ENFORCED:-0}" == "1" ]]; then
CARGO_ARGS+=(--features berd-call/telemetry-enforced)
fi
fi

(cd src-tauri && cargo "${CARGO_ARGS[@]}")
Expand Down
14 changes: 13 additions & 1 deletion scripts/release/build-macos.sh
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,19 @@ echo "+++ :hammer: pnpm tauri build (unsigned)"
GOOSE_BUILD_PROFILE=release ./scripts/prepare-goose-sidecar.sh
# ACP bridges are installed into the managed Node runtime on demand; they are
# no longer staged as build resources.
VITE_FEEDBACK="$VITE_FEEDBACK_VALUE" ./scripts/prepare-berdctl-sidecar.sh "$TARGET_TRIPLE"
# berd-call is compiled here rather than under the VITE env below, so it gets
# the app's telemetry policy explicitly. An empty environment, the CLI's twin of
# the renderer's VITE_TELEMETRY=0, compiles its telemetry out.
BERD_CALL_TELEMETRY_ENVIRONMENT="$VITE_ENVIRONMENT_VALUE"
for pair in ${VITE_EXTRA_ENV[@]+"${VITE_EXTRA_ENV[@]}"}; do
if [[ "$pair" == "VITE_TELEMETRY=0" ]]; then BERD_CALL_TELEMETRY_ENVIRONMENT=""; fi
done
BERD_CALL_TELEMETRY_CHANNEL="$(jq -r '.telemetry.channel // "public"' distro/distro.json 2>/dev/null || echo public)"
BERD_CALL_VERSION="$RELEASE_VERSION" \
BERD_CALL_TELEMETRY_ENVIRONMENT="$BERD_CALL_TELEMETRY_ENVIRONMENT" \
BERD_CALL_TELEMETRY_CHANNEL="$BERD_CALL_TELEMETRY_CHANNEL" \
VITE_TELEMETRY_ENFORCED="$VITE_TELEMETRY_ENFORCED_VALUE" \
VITE_FEEDBACK="$VITE_FEEDBACK_VALUE" ./scripts/prepare-berdctl-sidecar.sh "$TARGET_TRIPLE"
if [[ "$VITE_AGENT_TOOLS_VALUE" == "1" ]]; then
./scripts/prepare-bb-cli-resource.sh "$TARGET_TRIPLE"
tmp="$(mktemp)"
Expand Down
9 changes: 9 additions & 0 deletions scripts/release/tests/release-scripts.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,15 @@ describe("build-macos Block-service feature seam", () => {
);
expect(config.bundle.externalBin).toContain("binaries/berd-call");
expect(stage).toContain("CARGO_ARGS+=(-p berd-call)");
expect(stage).toContain(
"CARGO_ARGS+=(--features berd-call/telemetry-enforced)",
);
expect(release).toContain(
'VITE_TELEMETRY_ENFORCED="$VITE_TELEMETRY_ENFORCED_VALUE" \\\n VITE_FEEDBACK="$VITE_FEEDBACK_VALUE" ./scripts/prepare-berdctl-sidecar.sh',
);
expect(release).toContain(
'if [[ "$pair" == "VITE_TELEMETRY=0" ]]; then BERD_CALL_TELEMETRY_ENVIRONMENT=""; fi',
);
expect(stage).toContain("stage_cli berd-call");
expect(stage).toContain('out="$OUT_DIR/$name-$TRIPLE"');
expect(release).toContain(
Expand Down
3 changes: 3 additions & 0 deletions src-tauri/crates/berd-call/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ path = "src/main.rs"
default = ["static"]
static = ["sherpa-onnx/static"]
shared = ["sherpa-onnx/shared"]
# Internal builds report usage without a consent setting, like the app's
# block-telemetry-enforced.
telemetry-enforced = []

[dependencies]
audioadapter-buffers = "3.0"
Expand Down
13 changes: 13 additions & 0 deletions src-tauri/crates/berd-call/src/cli_help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Diagnostics:
benchmark stt Benchmark an STT backend

Other:
telemetry Show or change usage telemetry consent
help [command] Show help for a command
version Print the installed version

Expand Down Expand Up @@ -108,6 +109,17 @@ const STOP_HELP: &str = r#"Stop the active local voice call.
Usage:
berd-call stop [--port PORT]"#;

const TELEMETRY_HELP: &str = r#"Show or change usage telemetry consent.

Usage:
berd-call telemetry [status | enable | disable]

Calls report anonymous usage (start, end, duration, turn counts, end reason,
audio backends, speech rate and voice, with the app version and a random
installation id) only with consent. Consent is shared with Berd: enabling or disabling
here changes Berd's telemetry setting too. Internal builds always report.
Results are JSON with enabled and enforced."#;

const SESSION_HELP: &str = r#"Run the host-facing framed voice-session protocol.

Usage:
Expand Down Expand Up @@ -213,6 +225,7 @@ fn help_for(topic: &[&str]) -> Option<&'static str> {
["catch-up"] | ["wait-for-input"] => Some(POLL_INPUT_HELP),
["settings"] => Some(SETTINGS_HELP),
["stop"] => Some(STOP_HELP),
["telemetry"] | ["telemetry", "status" | "enable" | "disable"] => Some(TELEMETRY_HELP),
["session"] => Some(SESSION_HELP),
["synthesize"] => Some(SYNTHESIZE_HELP),
["voices"] | ["voices", "list" | "download" | "preview"] => Some(VOICES_HELP),
Expand Down
36 changes: 21 additions & 15 deletions src-tauri/crates/berd-call/src/host_session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pub(crate) fn run(options: StartOptions) -> Result<(), String> {
.unwrap_or_else(default_input_during_tts_policy),
restarted: None,
};
loop {
let outcome = loop {
match run_session(
&server,
session,
Expand All @@ -80,32 +80,28 @@ pub(crate) fn run(options: StartOptions) -> Result<(), String> {
&stop_requested,
&status_sounds,
) {
Ok(None) => {
transcript.finish(CLEAN_STOP);
return Ok(());
}
Ok(None) => break Ok(()),
// A stop recorded while the old session was shutting down wins
// over the restart it would otherwise hand off to.
Ok(Some(next)) if stop_requested.load(Ordering::SeqCst) => {
if let Some(response) = next.restarted {
let _ = response.send(Err("voice call stopped".into()));
}
transcript.finish(CLEAN_STOP);
return Ok(());
break Ok(());
}
Ok(Some(next)) => {
session = next;
}
Err(_) if stop_requested.load(Ordering::SeqCst) => {
transcript.finish(CLEAN_STOP);
return Ok(());
}
Err(message) => {
transcript.finish(&format!("Berd Call failed: {message}"));
return Err(message);
}
Err(_) if stop_requested.load(Ordering::SeqCst) => break Ok(()),
Err(message) => break Err(message),
}
};
match &outcome {
Ok(()) => transcript.finish(CLEAN_STOP),
Err(message) => transcript.finish(&format!("Berd Call failed: {message}")),
}
crate::telemetry::end(outcome.is_err());
outcome
}

/// Where live transcript records go: nowhere, stdout TSV, or a Codex task.
Expand Down Expand Up @@ -268,6 +264,13 @@ fn run_session(
)?;
let _ = response.send(control.status());
}
if let Ok(tts) = actor
.session
.lock()
.map(|session| session.tts.settings.clone())
{
crate::telemetry::start(&control.session_arguments, &tts);
}
let control: Arc<dyn HostControl> = control;
while running.load(Ordering::SeqCst) {
server.poll(Arc::clone(&control))?;
Expand Down Expand Up @@ -758,6 +761,7 @@ impl HostControl for SessionControl {
fn stop(&self) -> Result<Value, String> {
// Recorded before enqueueing, so a stop that races a restart handoff
// still ends the call even if this session never reads the command.
crate::telemetry::user_stopped();
self.stop_requested.store(true, Ordering::SeqCst);
let (tx, rx) = mpsc::sync_channel(1);
if self
Expand Down Expand Up @@ -1370,6 +1374,7 @@ impl SessionActor {
self.stream_user_interim(token, &text)?
}
SessionMessage::LiveEvent { token, text } => {
crate::telemetry::user_utterance();
self.transcript.record(Some(token), "user", &text)?
}
SessionMessage::Pending { id, utterances } if self.pending_prepare_id() == Some(id) => {
Expand Down Expand Up @@ -1400,6 +1405,7 @@ impl SessionActor {
}
}
SessionMessage::SpeechCompleted { id, .. } if self.pending_prepare_id() == Some(id) => {
crate::telemetry::assistant_response();
self.respond_speak(Ok(json!({"spoke":true,"status":"completed"})));
}
SessionMessage::SpeechInterrupted {
Expand Down
1 change: 1 addition & 0 deletions src-tauri/crates/berd-call/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub mod session;
pub mod siri;
mod status_sounds;
mod synthesis;
pub mod telemetry_store;
mod tts;
#[cfg(target_os = "macos")]
pub mod update_guard;
Expand Down
7 changes: 7 additions & 0 deletions src-tauri/crates/berd-call/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ mod session_audio;
mod session_framing;
#[cfg(target_os = "macos")]
mod system_input_mute;
mod telemetry;

use session_audio::{
AudioHostAck, AudioOutputControlRequest, AudioPipeTransport, RemotePcmAudioOutput,
Expand Down Expand Up @@ -552,6 +553,12 @@ fn main() {
print_pretty_json(&response)
.unwrap_or_else(|error| operational_error(operation, error));
}
Some("telemetry") => {
let response = telemetry::command(&args[2..])
.unwrap_or_else(|error| operational_error("telemetry", error));
print_pretty_json(&response)
.unwrap_or_else(|error| operational_error("telemetry", error));
}
Some("settings") => {
let (port, request) = parse_or_exit(parse_host_settings_args(&args), &args);
let response = host_control::request(port, request)
Expand Down
Loading
Loading