feat(goose2): voice dictation via direct-ACP pattern (#8609)

Signed-off-by: tulsi <tulsi@block.xyz>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
tulsi
2026-04-20 12:26:50 -07:00
committed by GitHub
parent 2efa625da9
commit 5642e0b81e
40 changed files with 4588 additions and 25 deletions
+149
View File
@@ -1,6 +1,7 @@
use sacp::{JsonRpcRequest, JsonRpcResponse};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
/// Schema descriptor for a single custom method, produced by the
/// `#[custom_methods]` macro's generated `custom_method_schemas()` function.
@@ -309,6 +310,154 @@ pub struct ProviderConfigKey {
pub primary: bool,
}
/// Transcribe audio via a dictation provider.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
#[request(method = "_goose/dictation/transcribe", response = DictationTranscribeResponse)]
#[serde(rename_all = "camelCase")]
pub struct DictationTranscribeRequest {
/// Base64-encoded audio data
pub audio: String,
/// MIME type (e.g. "audio/wav", "audio/webm")
pub mime_type: String,
/// Provider to use: "openai", "groq", "elevenlabs", or "local"
pub provider: String,
}
/// Transcription result.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
pub struct DictationTranscribeResponse {
pub text: String,
}
/// Get the configuration status of all dictation providers.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
#[request(method = "_goose/dictation/config", response = DictationConfigResponse)]
pub struct DictationConfigRequest {}
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
pub struct DictationModelOption {
pub id: String,
pub label: String,
pub description: String,
}
/// Per-provider configuration status.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct DictationProviderStatusEntry {
pub configured: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub host: Option<String>,
pub description: String,
pub uses_provider_config: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub settings_path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub config_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub model_config_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub default_model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub selected_model: Option<String>,
#[serde(default)]
pub available_models: Vec<DictationModelOption>,
}
/// Dictation config response — map of provider name to status.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
pub struct DictationConfigResponse {
pub providers: HashMap<String, DictationProviderStatusEntry>,
}
/// Empty success response for operations that return no data.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
pub struct EmptyResponse {}
/// List available local Whisper models with their download status.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
#[request(
method = "_goose/dictation/models/list",
response = DictationModelsListResponse
)]
#[serde(rename_all = "camelCase")]
pub struct DictationModelsListRequest {}
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
#[serde(rename_all = "camelCase")]
pub struct DictationModelsListResponse {
pub models: Vec<DictationLocalModelStatus>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct DictationLocalModelStatus {
pub id: String,
pub label: String,
pub description: String,
pub size_mb: u32,
pub downloaded: bool,
pub download_in_progress: bool,
}
/// Kick off a background download of a local Whisper model.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
#[request(method = "_goose/dictation/models/download", response = EmptyResponse)]
#[serde(rename_all = "camelCase")]
pub struct DictationModelDownloadRequest {
pub model_id: String,
}
/// Poll the progress of an in-flight download.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
#[request(
method = "_goose/dictation/models/download/progress",
response = DictationModelDownloadProgressResponse
)]
#[serde(rename_all = "camelCase")]
pub struct DictationModelDownloadProgressRequest {
pub model_id: String,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
#[serde(rename_all = "camelCase")]
pub struct DictationModelDownloadProgressResponse {
/// None when no download is active for this model id.
pub progress: Option<DictationDownloadProgress>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct DictationDownloadProgress {
pub bytes_downloaded: u64,
pub total_bytes: u64,
pub progress_percent: f32,
/// serde lowercase of DownloadStatus: "downloading" | "completed" | "failed" | "cancelled"
pub status: String,
pub error: Option<String>,
}
/// Cancel an in-flight download.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
#[request(method = "_goose/dictation/models/cancel", response = EmptyResponse)]
#[serde(rename_all = "camelCase")]
pub struct DictationModelCancelRequest {
pub model_id: String,
}
/// Delete a downloaded local Whisper model from disk.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
#[request(method = "_goose/dictation/models/delete", response = EmptyResponse)]
#[serde(rename_all = "camelCase")]
pub struct DictationModelDeleteRequest {
pub model_id: String,
}
/// Persist the user's model selection for a given provider.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
#[request(method = "_goose/dictation/model/select", response = EmptyResponse)]
#[serde(rename_all = "camelCase")]
pub struct DictationModelSelectRequest {
pub provider: String,
pub model_id: String,
}