mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-28 13:32:23 +00:00
Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
This commit is contained in:
co-authored by
Claude
Steven Enamakel
parent
c5087d83da
commit
e4666f9be4
@@ -150,15 +150,20 @@ impl EntityExtractor for LlmEntityExtractor {
|
||||
for attempt in 0..MAX_ATTEMPTS {
|
||||
match self.try_extract(text).await {
|
||||
Some(extracted) => {
|
||||
// #002 (T013): a completed extraction that yielded
|
||||
// structure means the extraction model is working —
|
||||
// clear any prior "structure degraded" flag so the
|
||||
// status/doctor surface recovers. (An empty-but-valid
|
||||
// result, e.g. genuinely entity-free text, is left
|
||||
// alone — it isn't evidence the model is broken.)
|
||||
if !extracted.entities.is_empty() || !extracted.topics.is_empty() {
|
||||
crate::openhuman::memory_tree::health::clear_structure_degraded();
|
||||
}
|
||||
// #3365: the structure-degraded latch means "the extraction
|
||||
// model is timing out / unreachable" — set ONLY after
|
||||
// MAX_ATTEMPTS transport failures (below). A *completed* call
|
||||
// disproves that: `try_extract` returns `Some` whenever the
|
||||
// provider responded, including the valid-but-empty and
|
||||
// malformed-JSON-as-empty cases. So a completed extraction
|
||||
// clears the latch regardless of whether this particular text
|
||||
// yielded entities — liveness, not content, is what it tracks.
|
||||
//
|
||||
// (Clearing only on a non-empty result left the latch stuck
|
||||
// after the model recovered whenever later texts were
|
||||
// entity-light, so the surface kept warning "extraction model
|
||||
// is timing out" long after it had stopped — #3365.)
|
||||
crate::openhuman::memory_tree::health::clear_structure_degraded();
|
||||
return Ok(extracted);
|
||||
}
|
||||
None => {
|
||||
|
||||
@@ -291,6 +291,49 @@ async fn extract_returns_empty_on_malformed_provider_response() {
|
||||
assert!(out.llm_importance.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn extract_clears_structure_latch_on_completed_empty_extraction() {
|
||||
// #3365: the structure-degraded latch is a LIVENESS signal ("the extraction
|
||||
// model is timing out"), set only after transport-failure retries are
|
||||
// exhausted. A *completed* call — even one that yields no entities (a
|
||||
// valid-but-empty result for entity-free text) — proves the model is
|
||||
// responding, so it must clear the latch. Previously only a NON-empty result
|
||||
// cleared it, so after the model recovered the latch stayed stuck whenever
|
||||
// later texts were entity-light.
|
||||
let _health_guard = crate::openhuman::memory_tree::health::test_guard();
|
||||
use crate::openhuman::memory::chat::{ChatPrompt, ChatProvider};
|
||||
use async_trait::async_trait;
|
||||
use std::sync::Arc;
|
||||
|
||||
struct EmptyButOkProvider;
|
||||
#[async_trait]
|
||||
impl ChatProvider for EmptyButOkProvider {
|
||||
fn name(&self) -> &str {
|
||||
"test:empty-ok"
|
||||
}
|
||||
async fn chat_for_json(&self, _p: &ChatPrompt) -> anyhow::Result<String> {
|
||||
Ok(r#"{"entities": [], "topics": []}"#.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
crate::openhuman::memory_tree::health::mark_structure_degraded(
|
||||
crate::openhuman::memory_tree::health::FailureCode::ExtractionTimeout,
|
||||
);
|
||||
assert!(
|
||||
crate::openhuman::memory_tree::health::current_degraded_state().structure,
|
||||
"precondition: structure-degraded latch is set"
|
||||
);
|
||||
|
||||
let ex = LlmEntityExtractor::new(LlmExtractorConfig::default(), Arc::new(EmptyButOkProvider));
|
||||
let out = ex.extract("entity-free text").await.unwrap();
|
||||
assert!(out.entities.is_empty(), "no entities in the response");
|
||||
|
||||
assert!(
|
||||
!crate::openhuman::memory_tree::health::current_degraded_state().structure,
|
||||
"a completed extraction must clear the structure-degraded latch even when empty"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn into_extracted_entities_drops_hallucinations() {
|
||||
let out = LlmExtractionOutput {
|
||||
|
||||
Reference in New Issue
Block a user