Skip to main content

ainl_semantic_tagger/
lib.rs

1//! Deterministic, heuristic-only semantic tagging for AINL / ArmaraOS.
2//!
3//! No ML, embeddings, or graph-store dependency.
4
5mod correction;
6mod preference;
7mod tag;
8mod tone;
9mod tool;
10mod topic;
11
12pub use correction::{correction_regexes, extract_correction_behavior, CorrectionRegexes};
13pub use preference::{infer_brevity_preference, tag_user_message};
14pub use tag::{
15    quantize_confidence, SemanticTag, TagNamespace, BEHAVIOR_ADDING_CAVEATS,
16    BEHAVIOR_OVEREXPLAINING, CORRECTION_AVOID_BULLETS, CORRECTION_AVOID_EMOJIS, PREFERENCE_BREVITY,
17    PREFERENCE_DETAIL, PREFERENCE_DIRECTNESS, PREFERENCE_EXAMPLES, TONE_FORMAL, TONE_INFORMAL,
18};
19pub use tone::infer_formality;
20pub use tool::tag_tool_names;
21pub use topic::infer_topic_tags;
22
23use std::collections::HashSet;
24
25/// Tags user + assistant text and tool metadata; deduplicates by `(namespace, value)`.
26pub fn tag_turn(user: &str, assistant: Option<&str>, tools: &[String]) -> Vec<SemanticTag> {
27    let mut combined = user.to_string();
28    if let Some(a) = assistant {
29        if !combined.is_empty() && !a.is_empty() {
30            combined.push(' ');
31        }
32        combined.push_str(a);
33    }
34
35    let mut out: Vec<SemanticTag> = Vec::new();
36    out.extend(infer_topic_tags(&combined));
37    out.extend(tag_user_message(user));
38    if let Some(t) = infer_formality(&combined) {
39        out.push(t);
40    }
41    if let Some(t) = extract_correction_behavior(user) {
42        out.push(t);
43    }
44    if let Some(a) = assistant {
45        if let Some(t) = extract_correction_behavior(a) {
46            out.push(t);
47        }
48    }
49    out.extend(tag_tool_names(tools));
50
51    let mut seen: HashSet<(TagNamespace, String)> = HashSet::new();
52    out.into_iter()
53        .filter(|t| seen.insert((t.namespace, t.value.clone())))
54        .collect()
55}