use super::{
PatternReferenceResolver, ReferenceResolver, ResolvedReference, manifest_builder::detect_config,
};
use crate::core::{
config::Config, context_kernel::ContextState, providers::registry::global_registry,
};
#[derive(Debug, Default)]
pub struct KnowledgeGateAdvisor;
#[derive(Debug, Clone, Default)]
pub struct KnowledgeAdvice {
pub references_found: Vec<ResolvedReference>,
pub suggested_sources: Vec<String>,
pub additional_context_hint: Option<String>,
pub budget_tokens: u64,
}
impl KnowledgeAdvice {
#[must_use]
pub fn none() -> Self {
Self::default()
}
}
impl KnowledgeGateAdvisor {
#[must_use]
pub fn advise(query: &str, current_context: &ContextState) -> KnowledgeAdvice {
Self::advise_with(query, current_context, &PatternReferenceResolver)
}
fn advise_with(
query: &str,
_current_context: &ContextState,
resolver: &dyn ReferenceResolver,
) -> KnowledgeAdvice {
let config = Config::load().knowledge_routing;
if !config.enabled {
return KnowledgeAdvice::none();
}
let Ok(mut references) =
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| resolver.resolve(query)))
else {
return KnowledgeAdvice::none();
};
references.truncate(config.max_references);
if references.is_empty() {
return KnowledgeAdvice::none();
}
let suggested_sources = references
.iter()
.map(|reference| reference.source_id.clone())
.filter(|source| source_available(source))
.collect::<Vec<_>>();
let mut hint = format!(
"{}\nReferenced: {}",
config.hint_prefix,
references
.iter()
.map(reference_label)
.collect::<Vec<_>>()
.join(", ")
);
if !suggested_sources.is_empty() {
hint.push_str(&format!(
"\nConsider fetching additional context from: {}",
suggested_sources.join(", ")
));
}
hint.push_str("\n---");
KnowledgeAdvice {
budget_tokens: (references.len() as u64 * 500).min(5_000),
references_found: references,
suggested_sources,
additional_context_hint: Some(hint),
}
}
}
fn source_available(source: &str) -> bool {
let configured = detect_config();
matches!(source, "jira") && configured.jira_configured
|| matches!(source, "github") && configured.github_configured
|| matches!(source, "gitlab") && configured.gitlab_configured
|| global_registry()
.available_provider_ids()
.iter()
.any(|id| id == source)
}
fn reference_label(reference: &ResolvedReference) -> String {
let kind = match reference.ref_type {
super::reference_resolver::ReferenceType::GitHubPR => "PR",
super::reference_resolver::ReferenceType::JiraIssue
| super::reference_resolver::ReferenceType::GitHubIssue => "issue",
_ => "reference",
};
format!("{} ({kind})", reference.identifier)
}
#[cfg(test)]
#[path = "gate_integration_tests.rs"]
mod tests;