Skip to content

Commit 854e076

Browse files
feat(knowledge): broaden extraction gate to all observation types above noise floor
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
1 parent 175ce6a commit 854e076

1 file changed

Lines changed: 49 additions & 36 deletions

File tree

crates/llm/src/knowledge.rs

Lines changed: 49 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
use opencode_mem_core::{
2-
Concept, KnowledgeExtractionResult, KnowledgeInput, KnowledgeType, Observation,
3-
};
1+
use opencode_mem_core::{KnowledgeExtractionResult, KnowledgeInput, KnowledgeType, Observation};
42

53
use crate::ai_types::{ChatRequest, Message, ResponseFormat, ResponseFormatType};
64
use crate::client::LlmClient;
@@ -27,18 +25,6 @@ impl LlmClient {
2725
return Ok(None);
2826
}
2927

30-
let dominated_by_generalizable = matches!(
31-
observation.observation_type,
32-
opencode_mem_core::ObservationType::Gotcha
33-
) || observation
34-
.concepts
35-
.iter()
36-
.any(|c| matches!(c, Concept::Pattern | Concept::Gotcha | Concept::HowItWorks));
37-
38-
if !dominated_by_generalizable {
39-
return Ok(None);
40-
}
41-
4228
let facts_str = observation.facts.join("\n- ");
4329
let concepts_str = observation
4430
.concepts
@@ -124,29 +110,56 @@ Return JSON: {{"extract": false, "reason": "..."}}"#,
124110
#[cfg(test)]
125111
mod tests {
126112

127-
use opencode_mem_core::{Concept, Observation, ObservationType};
113+
use opencode_mem_core::{NoiseLevel, Observation, ObservationType};
128114

129115
#[test]
130-
fn test_knowledge_extraction_type_bypass() {
131-
let mut obs = Observation::builder(
132-
"test".to_string(),
133-
"manual".to_string(),
116+
fn test_noise_level_gate_skips_low_and_negligible() {
117+
for noise in [NoiseLevel::Low, NoiseLevel::Negligible] {
118+
let mut obs = Observation::builder(
119+
"test".to_string(),
120+
"manual".to_string(),
121+
ObservationType::Discovery,
122+
"Test Discovery".to_string(),
123+
)
124+
.build();
125+
obs.noise_level = noise;
126+
127+
let should_skip = matches!(obs.noise_level, NoiseLevel::Low | NoiseLevel::Negligible);
128+
assert!(
129+
should_skip,
130+
"Low/Negligible noise observations must be skipped"
131+
);
132+
}
133+
}
134+
135+
#[test]
136+
fn test_all_observation_types_eligible_when_not_noisy() {
137+
let types = [
138+
ObservationType::Discovery,
139+
ObservationType::Bugfix,
140+
ObservationType::Decision,
141+
ObservationType::Change,
142+
ObservationType::Feature,
143+
ObservationType::Refactor,
144+
ObservationType::Preference,
134145
ObservationType::Gotcha,
135-
"Test Gotcha".to_string(),
136-
)
137-
.build();
138-
obs.concepts = vec![Concept::WhyItExists, Concept::WhatChanged];
139-
140-
// Simulated logic from maybe_extract_knowledge
141-
let dominated = matches!(obs.observation_type, ObservationType::Gotcha)
142-
|| obs
143-
.concepts
144-
.iter()
145-
.any(|c| matches!(c, Concept::Pattern | Concept::Gotcha | Concept::HowItWorks));
146-
147-
assert!(
148-
dominated,
149-
"Vulnerability fixed: ObservationType::Gotcha triggers extraction even if concepts are generic"
150-
);
146+
];
147+
148+
for obs_type in types {
149+
let mut obs = Observation::builder(
150+
"test".to_string(),
151+
"manual".to_string(),
152+
obs_type.clone(),
153+
format!("Test {obs_type:?}"),
154+
)
155+
.build();
156+
obs.noise_level = NoiseLevel::Medium;
157+
158+
let should_skip = matches!(obs.noise_level, NoiseLevel::Low | NoiseLevel::Negligible);
159+
assert!(
160+
!should_skip,
161+
"{obs_type:?} with Medium noise must proceed to LLM extraction"
162+
);
163+
}
151164
}
152165
}

0 commit comments

Comments
 (0)