popoto.fields.decaying_sorted_field¶
popoto.fields.decaying_sorted_field
¶
DecayingSortedField — time-weighted scoring via Lua decay computation.
This module provides a SortedField subclass where records lose relevance over time following power-law decay: base_score * elapsed_days^(-decay_rate).
The sorted set stores timestamps as scores. A Lua script computes decay-ranked results at query time, reading base scores from each member's model hash via cmsgpack.
Design
- Timestamps are always stored as scores (auto_now=True behavior)
- Decay computation happens server-side in Lua (no round trips)
- Base scores come from a companion field on the same model hash
- When base_score_field is None, all items have equal base score (1.0)
Example
class Memory(Model): key = UniqueKeyField() content = StringField() strength = FloatField(default=1.0) last_accessed = DecayingSortedField( decay_rate=0.5, base_score_field="strength", )
Query top-10 memories by decayed relevance¶
top = Memory.query.top_by_decay("last_accessed", n=10)
Refresh a memory's decay clock without full save¶
memory.touch("last_accessed")
DecayingSortedField
¶
Bases: SortedFieldMixin, Field
A SortedField subclass where records lose relevance over time.
Stores timestamps as sorted set scores. A Lua script computes decay-ranked results at query time using power-law decay: decayed_score = base_score * elapsed_days ^ (-decay_rate)
With decay_rate=0.5, a record scores 1.0 after 1 day, 0.5 after 4 days, and 0.1 after 100 days.
Ranking is deterministic: equal-scored members are ordered by member key (redis_key) ascending, byte-wise, broken inside the Lua script before top-N truncation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
decay_rate
|
Controls how fast scores drop. Higher = faster decay.
Defaults to |
required | |
base_score_field
|
Name of a companion field whose value multiplies the decay curve. When None, base score is 1.0. |
required | |
confidence_modulation_field
|
Controls confidence-modulated decay
(issue #491). |
required | |
partition_by
|
Partition the sorted set by key field values. Inherited from SortedFieldMixin. |
required |
Source code in src/popoto/fields/decaying_sorted_field.py
resolve_confidence_modulation_field(model_class, field, field_name)
¶
Resolve which ConfidenceField modulates field's decay.
Resolution order (first match wins):
Defaults.DECAY_CONFIDENCE_MODULATION_ENABLED is False-> off. This is the deploy-level kill switch: it disables modulation without any model-code edit, for adopters who cannot edit model definitions.confidence_modulation_field=False-> off (per-field opt-out).confidence_modulation_field="name"-> that field, orModelExceptionif it is missing or is not aConfidenceField.confidence_modulation_field=None(default) -> auto-detect overmodel_class._meta.fields. Exactly oneConfidenceFieldis used; zero means off; two or more means off plus a warning naming the candidates. Guessing between two confidence signals would silently pick a ranking policy the adopter never chose.
Returns:
| Name | Type | Description |
|---|---|---|
tuple |
Any
|
|
Any
|
|
Source code in src/popoto/fields/decaying_sorted_field.py
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 | |
confidence_modulation_args(model_class, field, field_name, *, filters=None, model_instance=None)
¶
Build (confidence_hash_key, s, c0) for a decay EVAL.
c0 is the resolved field's own initial_confidence -- never a
hard-coded 0.5. It is both the absent-value default and the centering
constant, so an adopter running initial_confidence=0.3 still gets a
bit-exactly neutral score for a record with no evidence.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_class
|
Any
|
The Model class being queried. |
required |
field
|
Any
|
The DecayingSortedField / CyclicDecayField instance. |
required |
field_name
|
str
|
Name of that field. |
required |
filters
|
Optional[dict[Any, Any]]
|
Query filter mapping, used to satisfy the ConfidenceField's
|
None
|
model_instance
|
Any
|
A saved instance to read partition values off of, for callers (the metacognitive proxy) that have records but no filters. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[str, str, str]
|
tuple[str, str, str]: |
Raises:
| Type | Description |
|---|---|
QueryException
|
The ConfidenceField is partitioned by fields the query
does not filter on, so no single |