Add local spoken passages with Google retrieval and isolated runtime setup

This commit is contained in:
karl committed 2026-10-13 20:18:44 +02:00
1 parent bc9459785d
commit dab703413e
55 files changed
+3633 -70

No files matched your search

+63 -2
View File
@@ -2,9 +2,12 @@
//! filtered HNSW retrieval. Production-scale validation remains a release gate.
mod coordination;
mod media;
mod speech;
pub use coordination::{
BudgetedProvider, InferenceBudget, InferencePermit, InferenceRole, PendingForeground,
};
pub use searchi_speech::{SpeechMetrics, SpeechPassage, SpeechTranscription};
pub use speech::{speech_policy_key, BudgetedSpeechProvider, SpeechExcerpt, SpeechProvider};
mod prepared;
pub use prepared::PreparedGraphs;
mod pipeline;
@@ -39,7 +42,23 @@ fn generation_fingerprint(model: &str) -> String {
)
}
fn raw_media_policy(policy: &str) -> &str {
if let Some((base, hash)) = policy.rsplit_once(":speech-v1-") {
if hash.len() == 64 && hash.bytes().all(|byte| byte.is_ascii_hexdigit()) {
return base;
}
}
policy
}
fn speech_component_policy(policy: &str) -> String {
let raw = raw_media_policy(policy);
format!("{}{}", policy_base(policy), &policy[raw.len()..])
}
fn reusable_policy(expected: &str, active: &str) -> bool {
let expected = raw_media_policy(expected);
let active = raw_media_policy(active);
if active == expected || active == format!("{expected}:text-only") {
return true;
}
@@ -56,6 +75,7 @@ fn reusable_policy(expected: &str, active: &str) -> bool {
/// Resolve compatible publication coverage without broadening model or window
/// policy compatibility. A retained reader can start under either coverage.
fn policy_base(policy: &str) -> &str {
let policy = raw_media_policy(policy);
if let Some(base) = policy.strip_suffix(":text-only") {
return base;
}
@@ -213,12 +233,21 @@ pub struct EmbeddingReport {
pub image_inference_calls: usize,
pub audio_inference_calls: usize,
pub resumed: bool,
pub speech_requested: bool,
pub speech_windows: usize,
pub speech_chunks: usize,
pub speech_reused_sources: usize,
pub speech_failures: Vec<MediaFailure>,
pub speech_metrics: std::collections::BTreeMap<String, u64>,
}
#[derive(Debug, Serialize)]
pub struct EmbeddingStatus {
/// Coverage of a compatible, prepared active generation, if any.
pub coverage: Option<SemanticCoverage>,
pub speech_policy: Option<String>,
pub speech_failures: Vec<MediaFailure>,
pub corpus_revision: i64,
pub requested_coverage: SemanticCoverage,
/// Early setup must preserve an existing compatible full generation.
pub text_indexing_allowed: bool,
@@ -241,6 +270,7 @@ enum QueryTask {
pub struct HybridSearch {
lexical: SearchIndex,
embedder: Box<dyn EmbeddingProvider>,
speech: Option<Box<dyn SpeechProvider>>,
store: Store,
generation_fingerprint: String,
// Real provider outputs only, scoped to this model session. Queries never
@@ -300,6 +330,9 @@ impl HybridSearch {
if !exists {
return Ok(EmbeddingStatus {
coverage: None,
speech_policy: None,
speech_failures: Vec::new(),
corpus_revision: 0,
requested_coverage,
text_indexing_allowed: true,
failures: Vec::new(),
@@ -318,6 +351,9 @@ impl HybridSearch {
if version < 6 {
return Ok(EmbeddingStatus {
coverage: None,
speech_policy: None,
speech_failures: Vec::new(),
corpus_revision: 0,
requested_coverage,
text_indexing_allowed: true,
failures: Vec::new(),
@@ -329,7 +365,7 @@ impl HybridSearch {
});
}
ensure!(
(6..=8).contains(&version),
(6..=9).contains(&version),
"Unsupported semantic schema version {version}"
);
let (generation,fingerprint,revision,current,dimensions):(Option<i64>,Option<String>,Option<i64>,i64,Option<usize>)=snapshot.query_row(
@@ -349,6 +385,25 @@ impl HybridSearch {
})
})?
.collect::<rusqlite::Result<Vec<_>>>()?;
let speech_policy = fingerprint
.as_deref()
.filter(|policy| raw_media_policy(policy) != *policy)
.and_then(|policy| policy.rsplit_once(":speech-v1-"))
.map(|(_, hash)| format!("speech-v1-{hash}"));
let speech_failures = if version >= 8 {
let mut speech_query=snapshot.prepare("SELECT path,message FROM semantic_tasks WHERE generation=?1 AND source_type='speech' AND state='failed' ORDER BY path LIMIT 1000")?;
let failures = speech_query
.query_map([generation], |row| {
Ok(MediaFailure {
path: row.get(0)?,
message: row.get(1)?,
})
})?
.collect::<rusqlite::Result<Vec<_>>>()?;
failures
} else {
Vec::new()
};
let expected = generation_fingerprint(GEMMA_FINGERPRINT);
let coverage = if dimensions == Some(GEMMA_DIMENSIONS) {
fingerprint
@@ -361,11 +416,16 @@ impl HybridSearch {
let policy_matches = match requested_coverage {
SemanticCoverage::TextOnly => prepared_coverage.is_some(),
// Keep the default full-media status's exact configured policy rule.
SemanticCoverage::FullMedia => fingerprint.as_deref() == Some(expected.as_str()),
SemanticCoverage::FullMedia => {
fingerprint.as_deref().map(raw_media_policy) == Some(expected.as_str())
}
};
let stale = revision != Some(current) || !policy_matches;
Ok(EmbeddingStatus {
coverage: prepared_coverage,
speech_policy,
speech_failures,
corpus_revision: current,
requested_coverage,
text_indexing_allowed: coverage != Some(SemanticCoverage::FullMedia),
failures,
@@ -416,6 +476,7 @@ impl HybridSearch {
query_cache: VecDeque::new(),
lexical,
embedder,
speech: None,
store,
generation_fingerprint,
})