1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//! The meeting *ingest* seam: the minimal contract the hardware ambient-audio
//! path needs from the meeting-notes capability, inverted so this kernel crate has
//! ZERO compile-time dependency on `ryu_meetings`.
//!
//! ## Why this exists
//!
//! An ambient-capable device (necklace / desk) opens a long-running "ambient"
//! meeting and streams ~1 s WAV chunks into it: [`session::HardwareSession`] buffers
//! decoded Opus uplink and, once a second of PCM has accumulated, feeds one WAV
//! segment to the meeting transcript. That transcription + persistence is a
//! *meetings* concern that had been welded into this crate as a direct
//! `ryu_meetings::MeetingEngine` field (a compile-time `ryu_hardware -> ryu_meetings`
//! edge welding the kernel hardware crate to a swappable app). Meetings is now a
//! swappable, out-of-process app; a kernel crate cannot hard-link it.
//!
//! [`MeetingIngest`] is the inversion. It exposes ONLY what the ambient path needs
//! (open/resume the ambient meeting, append a captured audio segment), in terms of
//! plain owned types — never a `ryu_meetings` type. Core provides the impl:
//!
//! - in-process (`meetings_ingest::in_proc`) — wraps the in-process engine;
//! - out-of-process (`meetings_client::MeetingsClient`) — proxies to the
//! `ryu-meetings` sidecar over loopback (`POST /api/meetings/:id/chunk`).
//!
//! ## Hot-path note
//!
//! [`MeetingIngest::append_segment`] is called at *segment rate*, not frame rate:
//! [`session::HardwareSession::on_audio`] only accumulates each ~20 ms Opus frame,
//! and the append fires once per ~1 s of buffered PCM (`AMBIENT_FLUSH_SAMPLES`). So
//! the sidecar-backed impl's HTTP hop is ~1 POST/s/device carrying a ~32 KB WAV
//! (transcription happens on the sidecar side) — acceptable at segment rate.
//!
//! ## What is deliberately absent: `finalize`
//!
//! There is no `finalize` on this seam. The device link never ends the ambient
//! meeting — it is a continuous 24/7 transcript that a device *resumes* on each
//! reconnect. Finalizing a meeting (stop capture → generate notes → save) is a
//! user-driven action through the meetings API/UI, never the hardware path, so the
//! seam only needs open/resume + append.
use async_trait;
/// The meeting-notes capability, seen through the narrow hole the hardware ambient
/// path needs. Implemented by Core (in-process or sidecar-backed).