feat(voice): stub voice cache lookup for behavior text (Phase 3)
Add lookup.rs with voiced_behavior() — ready to wire into a behavior-serving system once one exists (Q-058). Tell behaviors always passthrough (never re-voiced). Cache miss returns base text (graceful degradation). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
//! Voice cache lookup for behavior text (D-138, Phase 3 stub).
|
||||
//!
|
||||
//! Provides the integration point between the voice cache and any system
|
||||
//! that serves NPC text to the client. Not yet wired into a runtime system —
|
||||
//! `observable_behaviors` on `NpcBlueprint` is generator output only.
|
||||
//!
|
||||
//! Wire `voiced_behavior()` into the behavior-serving path once it exists.
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::npc::tell_state::TellCategory;
|
||||
use crate::voice::cache::{CacheKey, VoiceCacheStore};
|
||||
use crate::voice::prompt_builder::ContentType;
|
||||
|
||||
/// Look up a voiced behavior from cache, falling back to base text.
|
||||
///
|
||||
/// Tell behaviors (from `NpcBlueprint.tell_behaviors`) are NEVER re-voiced —
|
||||
/// they are always base text passthrough. Only pass `is_tell_behavior: false`
|
||||
/// for `observable_behaviors` content.
|
||||
pub fn voiced_behavior(
|
||||
cache: &Arc<Mutex<VoiceCacheStore>>,
|
||||
zone_id: u32,
|
||||
culture_id: &str,
|
||||
npc_stable_id: u64,
|
||||
content_type: ContentType,
|
||||
content_index: u16,
|
||||
tell_state: Option<TellCategory>,
|
||||
base_text: &str,
|
||||
is_tell_behavior: bool,
|
||||
) -> String {
|
||||
// Tell behaviors are always passthrough — never re-voiced.
|
||||
if is_tell_behavior {
|
||||
return base_text.to_string();
|
||||
}
|
||||
|
||||
let key = CacheKey {
|
||||
culture_id: culture_id.to_string(),
|
||||
npc_stable_id,
|
||||
content_type,
|
||||
content_index,
|
||||
tell_state,
|
||||
};
|
||||
|
||||
if let Ok(mut store) = cache.lock() {
|
||||
if let Some(voiced) = store.lookup(zone_id, &key) {
|
||||
return voiced;
|
||||
}
|
||||
}
|
||||
|
||||
// Cache miss — serve base text (graceful degradation).
|
||||
base_text.to_string()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn test_cache() -> Arc<Mutex<VoiceCacheStore>> {
|
||||
let dir = std::env::temp_dir().join("sr-voice-lookup-test");
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
Arc::new(Mutex::new(VoiceCacheStore::new(
|
||||
dir,
|
||||
99,
|
||||
"v1".into(),
|
||||
"i1".into(),
|
||||
)))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_hit_returns_voiced_text() {
|
||||
let cache = test_cache();
|
||||
let key = CacheKey {
|
||||
culture_id: "krenn".into(),
|
||||
npc_stable_id: 42,
|
||||
content_type: ContentType::Behavior,
|
||||
content_index: 0,
|
||||
tell_state: None,
|
||||
};
|
||||
cache.lock().unwrap().store(100, key, "Voiced line.".into());
|
||||
|
||||
let result = voiced_behavior(
|
||||
&cache, 100, "krenn", 42, ContentType::Behavior, 0, None,
|
||||
"Base line.", false,
|
||||
);
|
||||
assert_eq!(result, "Voiced line.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_miss_returns_base_text() {
|
||||
let cache = test_cache();
|
||||
let result = voiced_behavior(
|
||||
&cache, 100, "krenn", 42, ContentType::Behavior, 0, None,
|
||||
"Base line.", false,
|
||||
);
|
||||
assert_eq!(result, "Base line.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tell_behavior_always_passthrough() {
|
||||
let cache = test_cache();
|
||||
// Even if cache has a voiced version, tell behaviors return base text
|
||||
let key = CacheKey {
|
||||
culture_id: "krenn".into(),
|
||||
npc_stable_id: 42,
|
||||
content_type: ContentType::Behavior,
|
||||
content_index: 0,
|
||||
tell_state: Some(TellCategory::Angry),
|
||||
};
|
||||
cache.lock().unwrap().store(100, key, "Voiced tell.".into());
|
||||
|
||||
let result = voiced_behavior(
|
||||
&cache, 100, "krenn", 42, ContentType::Behavior, 0,
|
||||
Some(TellCategory::Angry), "Base tell.", true,
|
||||
);
|
||||
assert_eq!(result, "Base tell.");
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
//! - `hardware` — hardware detection + dynamic sr-voice instance management
|
||||
|
||||
pub mod cache;
|
||||
pub mod lookup;
|
||||
pub mod prompt_builder;
|
||||
pub mod queue;
|
||||
pub mod worker;
|
||||
|
||||
Reference in New Issue
Block a user