290 lines
8.5 KiB
Rust
290 lines
8.5 KiB
Rust
//! Shared data types crossing the IPC boundary and between services.
|
|
//! Mirrors `docs/03-data-model.md` and `docs/04-api-contracts.md`.
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
pub type MeetingId = String; // uuid v4
|
|
|
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum BackendId {
|
|
Npu,
|
|
Nvidia,
|
|
Amd,
|
|
Intel,
|
|
Cpu,
|
|
}
|
|
|
|
impl BackendId {
|
|
pub fn as_str(&self) -> &'static str {
|
|
match self {
|
|
BackendId::Npu => "npu",
|
|
BackendId::Nvidia => "nvidia",
|
|
BackendId::Amd => "amd",
|
|
BackendId::Intel => "intel",
|
|
BackendId::Cpu => "cpu",
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct BackendInfo {
|
|
pub id: BackendId,
|
|
pub name: String,
|
|
pub available: bool,
|
|
/// Lower rank = higher priority (NPU=0 … CPU=4).
|
|
pub rank: u8,
|
|
pub vram_mb: Option<u32>,
|
|
}
|
|
|
|
/// A downloadable/installed Whisper model (T3.7, FR-MODEL-1).
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ModelInfo {
|
|
pub id: String, // e.g. "base.en-q5_1" — also the ggml filename stem
|
|
pub label: String,
|
|
pub size_mb: u32, // approximate download size
|
|
pub installed: bool,
|
|
pub active: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum MeetingStatus {
|
|
Recording,
|
|
Transcribing,
|
|
Ready,
|
|
Recovering,
|
|
Error,
|
|
}
|
|
|
|
impl MeetingStatus {
|
|
pub fn as_str(&self) -> &'static str {
|
|
match self {
|
|
MeetingStatus::Recording => "recording",
|
|
MeetingStatus::Transcribing => "transcribing",
|
|
MeetingStatus::Ready => "ready",
|
|
MeetingStatus::Recovering => "recovering",
|
|
MeetingStatus::Error => "error",
|
|
}
|
|
}
|
|
|
|
/// Parses a `meetings.status` DB value. Unrecognized values map to `Error`
|
|
/// rather than panicking — a stored value should always be one written by
|
|
/// this app, but a row is not worth crashing the app over.
|
|
pub fn parse(s: &str) -> Self {
|
|
match s {
|
|
"recording" => MeetingStatus::Recording,
|
|
"transcribing" => MeetingStatus::Transcribing,
|
|
"ready" => MeetingStatus::Ready,
|
|
"recovering" => MeetingStatus::Recovering,
|
|
_ => MeetingStatus::Error,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct TranscriptSegment {
|
|
pub id: u64,
|
|
pub start_ms: u64,
|
|
pub end_ms: u64,
|
|
/// Internal speaker label ("S1"…); name resolved at render time (FR-SPK-5).
|
|
pub speaker: String,
|
|
pub text: String,
|
|
pub confidence: Option<f32>,
|
|
pub interim: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SpeakerInfo {
|
|
pub label: String, // "S1"
|
|
pub display_name: Option<String>,
|
|
pub participant_id: Option<String>,
|
|
}
|
|
|
|
/// A diarization result span before alignment to transcript segments.
|
|
#[derive(Debug, Clone)]
|
|
pub struct SpeakerSpan {
|
|
pub start_ms: u64,
|
|
pub end_ms: u64,
|
|
pub speaker: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct MeetingListItem {
|
|
pub id: MeetingId,
|
|
pub title: String,
|
|
pub started_at: i64,
|
|
pub duration_secs: Option<i64>,
|
|
pub status: MeetingStatus,
|
|
/// Tags (Phase 8, FR-SEARCH-2), sorted.
|
|
pub tags: Vec<String>,
|
|
}
|
|
|
|
/// One full-text search hit (Phase 8, FR-SEARCH-1) — the same shape as
|
|
/// `MeetingListItem` plus a highlighted excerpt of what matched.
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SearchHit {
|
|
pub id: MeetingId,
|
|
pub title: String,
|
|
pub started_at: i64,
|
|
pub duration_secs: Option<i64>,
|
|
pub status: MeetingStatus,
|
|
pub tags: Vec<String>,
|
|
pub snippet: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ActionItem {
|
|
pub id: Option<String>,
|
|
pub text: String,
|
|
pub owner: Option<String>,
|
|
pub due_at: Option<i64>,
|
|
pub confirmed: bool,
|
|
/// Schedule a local OS reminder for `due_at` (Phase 8, T8.6, FR-CAL-5).
|
|
#[serde(default)]
|
|
pub reminder_set: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct CalendarEvent {
|
|
pub id: String,
|
|
pub source: String, // pst|graph|ics
|
|
pub subject: Option<String>,
|
|
pub organizer: Option<String>,
|
|
pub starts_at: Option<i64>,
|
|
pub ends_at: Option<i64>,
|
|
pub description: Option<String>,
|
|
/// The source's own stable id (e.g. an iCalendar UID) — used to dedup
|
|
/// re-imports of the same event rather than creating duplicate rows.
|
|
pub raw_uid: Option<String>,
|
|
}
|
|
|
|
/// A parsed attendee before persistence — no DB row/id exists until storage
|
|
/// upserts it (T6.1/T6.2, FR-CAL-1).
|
|
#[derive(Debug, Clone)]
|
|
pub struct AttendeeInfo {
|
|
pub name: String,
|
|
pub email: Option<String>,
|
|
pub role: Option<String>, // organizer|required|optional
|
|
}
|
|
|
|
/// An imported event with the attendees pulled from the same source record —
|
|
/// PST/ICS both list attendees inline per-appointment, so there's no
|
|
/// separate "fetch attendees for event X" round trip against the source.
|
|
#[derive(Debug, Clone)]
|
|
pub struct ImportedEvent {
|
|
pub event: CalendarEvent,
|
|
pub attendees: Vec<AttendeeInfo>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Participant {
|
|
pub id: String,
|
|
pub name: String,
|
|
pub email: Option<String>,
|
|
pub role: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Settings {
|
|
pub theme: String, // system|light|dark
|
|
pub storage_root: String,
|
|
pub llm_provider: String, // ollama|custom|off
|
|
pub llm_endpoint: String,
|
|
pub llm_model: String,
|
|
/// Advanced Ollama tuning (T5.2): `{ system?, think?, keep_alive?, options: {…} }`.
|
|
/// Sparse — only user-overridden values are stored; `Null`/absent = all defaults.
|
|
#[serde(default)]
|
|
pub llm_advanced: serde_json::Value,
|
|
pub preferred_backend: String, // auto|npu|nvidia|amd|intel|cpu
|
|
pub whisper_model: String, // ModelInfo.id, e.g. "base.en-q5_1"
|
|
pub low_overhead: bool,
|
|
// Recording retention (ADR-0009). Default OFF.
|
|
pub default_record: bool,
|
|
pub consent_acknowledged: bool,
|
|
// Sync master switch (ADR-0010). Default OFF. Target rows live in the DB; secrets in OS keychain.
|
|
pub sync_enabled: bool,
|
|
// Storage retention policy (FR-STORE-2). None = no cap on that dimension.
|
|
pub retention_max_age_days: Option<u32>,
|
|
pub retention_max_size_gb: Option<u32>,
|
|
}
|
|
|
|
// ---- Sync (ADR-0010) ----
|
|
|
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum SyncKind {
|
|
WebDav,
|
|
OneDrive,
|
|
Dropbox,
|
|
Box,
|
|
}
|
|
|
|
/// What the UI sees about a target. NEVER contains the secret (FR-SYNC-6).
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SyncTargetInfo {
|
|
pub id: String,
|
|
pub name: String,
|
|
pub kind: SyncKind,
|
|
pub provider_hint: Option<String>, // nextcloud|owncloud|cloudreve|seafile|synology|generic
|
|
pub base_url: Option<String>,
|
|
pub remote_base_path: String,
|
|
pub username: Option<String>,
|
|
pub enabled: bool,
|
|
pub third_party: bool,
|
|
pub host: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SyncJobInfo {
|
|
pub id: String,
|
|
pub target_id: String,
|
|
pub meeting_id: MeetingId,
|
|
pub artifact: String, // transcript|notes|summary|recording
|
|
pub status: String, // pending|uploading|done|failed|skipped
|
|
pub attempts: u32,
|
|
pub bytes_sent: u64,
|
|
pub bytes_total: Option<u64>,
|
|
pub last_error: Option<String>,
|
|
}
|
|
|
|
// ---- Feature briefs + MCP (ADR-0011) ----
|
|
|
|
/// Agent-ready spec distilled from a meeting; served by the MCP `get_feature_brief` tool.
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct FeatureBrief {
|
|
pub id: String,
|
|
pub meeting_id: MeetingId,
|
|
pub title: String,
|
|
pub problem: String,
|
|
pub desired_outcome: String,
|
|
pub acceptance_criteria: Vec<String>,
|
|
pub target_repo: Option<String>,
|
|
pub context_excerpts: Vec<ContextExcerpt>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ContextExcerpt {
|
|
pub speaker: String,
|
|
pub text: String,
|
|
}
|
|
|
|
/// Lightweight listing row (no full body).
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct FeatureBriefInfo {
|
|
pub id: String,
|
|
pub meeting_id: MeetingId,
|
|
pub title: String,
|
|
pub target_repo: Option<String>,
|
|
pub exposed: bool,
|
|
}
|
|
|
|
/// One row of the MCP audit log (FR-MCP-5).
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct McpAccessEntry {
|
|
pub at: i64,
|
|
pub tool: String,
|
|
pub meeting_id: Option<MeetingId>,
|
|
pub client: Option<String>,
|
|
}
|