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
2 changes: 1 addition & 1 deletion Resources/analytics-events.psv
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ dictation_start_dropped_for_modifier_combo|duration_bucket,trigger
dictation_start_failed|default_input_class,default_output_class,failure_kind,format_ready,hfp_suspected,input_channels,input_device_class,input_rate_hz,output_channels,output_device_class,output_rate_hz,recovering,recovery_latency_bucket,route_shape,sample_flow_started,selected_input_class,selection_overrode_default,selection_reason,start_attempt_bucket,trigger,was_recording
dictation_start_requested|default_input_class,default_output_class,first_since_launch,format_ready,hfp_suspected,input_channels,input_device_class,input_rate_hz,mac_chip,memory_gb_bucket,model_state,output_channels,output_device_class,output_rate_hz,recovering,recovery_latency_bucket,route_shape,sample_flow_started,selected_input_class,selection_overrode_default,selection_reason,start_retry,stt_model,trigger,was_recording
dictation_started|default_input_class,default_output_class,first_since_launch,format_ready,hfp_suspected,input_channels,input_device_class,input_rate_hz,mac_chip,memory_gb_bucket,output_channels,output_device_class,output_rate_hz,recovering,recovery_latency_bucket,route_shape,sample_flow_started,selected_input_class,selection_overrode_default,selection_reason,start_latency_bucket,start_latency_ms,stt_model,trigger,was_recording
dictation_stop_latency_measured|auto_enter_bucket,auto_send,auto_send_block_reason,auto_send_expected,auto_send_key,cleanup_bucket,cleanup_changed,cleanup_enabled,copy_reason,decode_bucket,decode_latency_ms,default_input_class,default_output_class,delivery,first_sound_latency_bucket,first_sound_latency_ms,format_ready,hfp_suspected,input_channels,input_device_class,input_rate_hz,mac_chip,memory_gb_bucket,mic_stop_bucket,model_wait_bucket,outcome,output_channels,output_device_class,output_rate_hz,paste_bucket,recovering,recovery_latency_bucket,route_shape,sample_flow_started,save_bucket,save_outcome,selected_input_class,selection_overrode_default,selection_reason,stop_to_done_bucket,stop_to_paste_bucket,stop_to_paste_latency_ms,stt_model,target_confirmation_mode,trigger,was_recording,word_count_bucket
dictation_stop_latency_measured|auto_enter_bucket,auto_send,auto_send_block_reason,auto_send_expected,auto_send_key,checkpoint_bucket,cleanup_bucket,cleanup_changed,cleanup_enabled,copy_reason,decode_bucket,decode_latency_ms,default_input_class,default_output_class,delivery,first_sound_latency_bucket,first_sound_latency_ms,format_ready,hfp_suspected,input_channels,input_device_class,input_rate_hz,mac_chip,memory_gb_bucket,mic_backend,mic_stop_bucket,model_wait_bucket,outcome,output_channels,output_device_class,output_rate_hz,paste_bucket,paste_confirm_bucket,recovering,recovery_latency_bucket,resample_bucket,route_shape,sample_flow_started,save_bucket,save_outcome,selected_input_class,selection_overrode_default,selection_reason,stop_to_done_bucket,stop_to_paste_bucket,stop_to_paste_dispatch_latency_ms,stop_to_paste_latency_ms,stt_model,target_confirmation_mode,trigger,was_recording,word_count_bucket
dictation_transcription_failed|duration_bucket,route_shape,trigger
dictation_zombie_recovery_finished|failure_kind,hfp_suspected,input_device_class,output_device_class,result,route_shape,stage
launch_models_warmed|dictation_ready,dictation_warmup_ms,hotkeys_ready_ms,login_launch,mac_chip,meeting_recording_ready,meeting_warmup_ms,memory_gb_bucket,status_item_ms,stt_model,warmup_latency_bucket,warmup_start_ms
Expand Down
1 change: 1 addition & 0 deletions Resources/analytics-reviewed-properties.psv
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ status_item_ms
step_id
step_index
stop_timed_out
stop_to_paste_dispatch_latency_ms
stop_to_paste_latency_ms
stt_calls
stt_input_seconds
Expand Down
6 changes: 6 additions & 0 deletions Sources/Speech/ParakeetRecordingTeardown.swift
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,12 @@ extension ParakeetEngine: ParakeetStopRecordingHost {
var hasSharedMeetingMicClaim: Bool { sharedMeetingMicClaim != nil }
var isSharedMeetingMicResumeInProgress: Bool { sharedMeetingMicTransition.isResumeInProgress }
var hasPinnedDictationRecording: Bool { pinnedDictationRecording != nil }
/// The recorder the current take uses, for stop timing (`pinned_ioproc`,
/// `shared_meeting_mic` or `engine`). Read it before the stop clears them.
var dictationMicBackendName: String {
if hasPinnedDictationRecording { return PinnedMicrophoneCapture.diagnosticBackendName }
return hasSharedMeetingMicClaim ? "shared_meeting_mic" : "engine"
}

var activeConfigRecoveryGeneration: UInt64? {
recoveryState.isRecovering ? recoveryState.generation : nil
Expand Down
1 change: 1 addition & 0 deletions Sources/UI/Overlay/DictationSessionController+Stop.swift
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ extension DictationSessionController {
Task { await checkpointSignal.complete() }
}
var stopTiming = DictationStopTiming(requestedAt: stopRequestedAt)
stopTiming.micBackend = appState.sttRouter.parakeetEngine.dictationMicBackendName
// runStopUntilTranscribed (DictationSessionPipeline.swift) owns the
// order: the stale-task fence, then DictationStopCheckpoint (stop
// the mic, play the stop click, save the take to a private WAV),
Expand Down
9 changes: 9 additions & 0 deletions Sources/UI/Overlay/DictationSessionController+Telemetry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,13 @@ extension DictationSessionController {
).rawValue
let timingBuckets: [(metric: String, bucket: String)] = [
("stop_to_mic_stop_ms", "mic_stop_bucket"),
("snapshot_resample_ms", "resample_bucket"),
("recovery_checkpoint_ms", "checkpoint_bucket"),
("model_wait_ms", "model_wait_bucket"),
("decode_ms", "decode_bucket"),
("cleanup_ms", "cleanup_bucket"),
("paste_ms", "paste_bucket"),
("paste_confirmation_wait_ms", "paste_confirm_bucket"),
("auto_enter_ms", "auto_enter_bucket"),
("save_ms", "save_bucket"),
("stop_to_paste_ms", "stop_to_paste_bucket"),
Expand All @@ -246,6 +249,9 @@ extension DictationSessionController {
let exactTimings: [(metric: String, key: String)] = [
("decode_ms", "decode_latency_ms"),
("stop_to_paste_ms", "stop_to_paste_latency_ms"),
// When Cmd+V went out: the text usually shows here, before the
// confirmation wait that stop_to_paste includes.
("stop_to_paste_dispatch_ms", "stop_to_paste_dispatch_latency_ms"),
]
for exactTiming in exactTimings {
guard let milliseconds = measurements[exactTiming.metric] else { continue }
Expand All @@ -255,6 +261,9 @@ extension DictationSessionController {
analyticsProperties["first_sound_latency_bucket"] = AnalyticsReporter.latencyBucket(milliseconds: firstSoundMs)
analyticsProperties["first_sound_latency_ms"] = MachineClassTelemetry.roundedMilliseconds(firstSoundMs)
}
if let micBackend = timing.micBackend {
analyticsProperties["mic_backend"] = micBackend
}
analyticsProperties.merge(dictationSpeedContext(appState: appState)) { current, _ in current }

AnalyticsReporter.track(
Expand Down
2 changes: 2 additions & 0 deletions Sources/UI/Overlay/DictationSessionDeliveryTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import AppKit

struct DictationStopTiming {
let requestedAt: CFAbsoluteTime
/// Which recorder this take used, read before the mic stops.
var micBackend: String?
var micStoppedAt: CFAbsoluteTime?
var snapshotStartedAt: CFAbsoluteTime?
var snapshotFinishedAt: CFAbsoluteTime?
Expand Down
10 changes: 10 additions & 0 deletions Tests/AnalyticsEventPolicyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,11 @@ func testAnalyticsEventPolicy() {
"stop_to_paste_bucket": "500_999ms",
"stop_to_paste_ms": "621",
"stop_to_paste_latency_ms": "620",
"stop_to_paste_dispatch_latency_ms": "270",
"paste_confirm_bucket": "250_499ms",
"checkpoint_bucket": "lt_100ms",
"resample_bucket": "lt_100ms",
"mic_backend": "pinned_ioproc",
"stt_model": "parakeet-tdt-v3",
"target_confirmation_mode": "clipboard_read_only",
"trigger": "physical_key",
Expand All @@ -1108,6 +1113,11 @@ func testAnalyticsEventPolicy() {
assertEqual(sanitized["decode_latency_ms"], "420", "10 ms decode timing should survive for per-model percentiles")
assertEqual(sanitized["stop_to_paste_latency_ms"], "620", "10 ms stop-to-paste timing should survive")
assertEqual(sanitized["first_sound_latency_ms"], "200", "10 ms key-to-first-sound timing should survive")
assertEqual(sanitized["stop_to_paste_dispatch_latency_ms"], "270", "10 ms stop-to-Cmd+V timing should survive")
assertEqual(sanitized["paste_confirm_bucket"], "250_499ms", "the bucketed paste confirmation wait should survive")
assertEqual(sanitized["checkpoint_bucket"], "lt_100ms", "the bucketed recovery checkpoint should survive")
assertEqual(sanitized["resample_bucket"], "lt_100ms", "the bucketed stop resample should survive")
assertEqual(sanitized["mic_backend"], "pinned_ioproc", "which recorder the take used should survive")
assertEqual(sanitized["first_sound_latency_bucket"], "100_249ms", "bucketed key-to-first-sound timing should survive")
assertEqual(sanitized["stt_model"], "parakeet-tdt-v3", "the speech model id should survive")
assertEqual(sanitized["mac_chip"], "m2_pro", "the coarse chip family should survive")
Expand Down
4 changes: 3 additions & 1 deletion docs/privacy-first-observability.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,9 @@ For each new or changed event:
- dictation speed is the other reviewed raw-number case: `dictation_started`
carries `start_latency_ms`, and `dictation_stop_latency_measured` carries
`first_sound_latency_ms` (key press to first audio buffer),
`decode_latency_ms`, and `stop_to_paste_latency_ms`, all rounded to 10 ms
`decode_latency_ms`, `stop_to_paste_latency_ms`, and
`stop_to_paste_dispatch_latency_ms` (stop to Cmd+V sent, before the paste
confirmation wait), all rounded to 10 ms
by `MachineClassTelemetry.roundedMilliseconds`. Both events, and
`dictation_start_requested` so the attempt funnel stays comparable, also carry
`stt_model` (the `TranscriptionModelChoice` raw value), `mac_chip` (chip
Expand Down
Loading