1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//! Scoring strategies that compute relevance for context items.
//!
//! A [`Scorer`] is a pure function: given identical inputs it must produce the same
//! output. Scores are conventionally in \[0.0, 1.0\] but this is not enforced —
//! [`ScaledScorer`] can be used to normalize arbitrary ranges.
//!
//! # Scorer comparison
//!
//! | Scorer | Strategy | Input | Use Case |
//! |--------|----------|-------|----------|
//! | [`RecencyScorer`] | Rank | `timestamp` | Favor recent conversation turns |
//! | [`PriorityScorer`] | Rank | `priority` | Honor explicit priority overrides |
//! | [`KindScorer`] | Absolute | `kind` | Weight system prompts above messages |
//! | [`TagScorer`] | Absolute | `tags` | Boost items matching configured tags |
//! | [`FrequencyScorer`] | Relative | `tags` | Promote items with common themes |
//! | [`ReflexiveScorer`] | Absolute | `future_relevance_hint` | Pass through external relevance signals |
//! | [`DecayScorer`] | Absolute | `timestamp` | Decay score by age (exponential, window, or step curve) |
//! | [`MetadataTrustScorer`] | Absolute | `metadata["cupel:trust"]` | Read trust score from item metadata |
//! | [`MetadataKeyScorer`] | Absolute | `metadata[key]` | Multiplicative boost when metadata key matches a value |
//! | [`CompositeScorer`] | Weighted avg | child scorers | Combine multiple strategies |
//! | [`ScaledScorer`] | Min-max norm | inner scorer | Normalize scores to \[0, 1\] |
//!
//! **Rank** scorers compare each item against all peers (output depends on the full
//! set). **Absolute** scorers read a single field (output is independent of peers).
//! **Relative** scorers compute a proportion across the full set.
//!
//! # Example
//!
//! ```
//! use cupel::{
//! ContextItemBuilder, ContextKind,
//! CompositeScorer, RecencyScorer, KindScorer, Scorer,
//! };
//! use chrono::Utc;
//!
//! let scorer = CompositeScorer::new(vec![
//! (Box::new(RecencyScorer), 2.0),
//! (Box::new(KindScorer::with_default_weights()), 1.0),
//! ])?;
//!
//! let items = vec![
//! ContextItemBuilder::new("recent", 1)
//! .kind(ContextKind::new("Message")?)
//! .timestamp(Utc::now())
//! .build()?,
//! ContextItemBuilder::new("older", 1)
//! .kind(ContextKind::new("Message")?)
//! .timestamp(Utc::now() - chrono::Duration::hours(1))
//! .build()?,
//! ];
//!
//! let recent_score = scorer.score(&items[0], &items);
//! let older_score = scorer.score(&items[1], &items);
//! assert!(recent_score > older_score); // recency weight dominates
//! # Ok::<(), cupel::CupelError>(())
//! ```
pub use CompositeScorer;
pub use ;
pub use FrequencyScorer;
pub use KindScorer;
pub use MetadataKeyScorer;
pub use MetadataTrustScorer;
pub use PriorityScorer;
pub use RecencyScorer;
pub use ReflexiveScorer;
pub use ScaledScorer;
pub use TagScorer;
use crateContextItem;
/// A scorer computes a relevance score for a context item relative to all scoreable items.
///
/// Scorers are pure functions: given identical inputs, they must return the same output.
/// Scores are conventionally in \[0.0, 1.0\] but this is not enforced.
///
/// # Examples
///
/// ```
/// use cupel::{ContextItemBuilder, PriorityScorer, Scorer};
///
/// // All built-in scorers implement this trait
/// let scorer: Box<dyn Scorer> = Box::new(PriorityScorer);
///
/// let items = vec![
/// ContextItemBuilder::new("high", 5).priority(100).build()?,
/// ContextItemBuilder::new("low", 5).priority(1).build()?,
/// ];
/// let hi = scorer.score(&items[0], &items);
/// let lo = scorer.score(&items[1], &items);
/// assert!(hi > lo);
/// # Ok::<(), cupel::CupelError>(())
/// ```