Sub-project 2: ingest & tagging / review tools #1

Merged
benstull merged 14 commits from sub-project-2-ingest-tagging-review into main 2026-06-04 14:16:28 +00:00
2 changed files with 169 additions and 0 deletions
Showing only changes of commit 3d5c34491c - Show all commits
+79
View File
@@ -0,0 +1,79 @@
from hef.selection import Coordinate
from tools.drafting import Draft, HeuristicProposer, Signals
def _sig(**o):
base = dict(
title="",
description="",
source_archive="nasa",
mode="video",
duration_s=600,
)
base.update(o)
return Signals(**base)
def test_returns_draft_with_coordinate_and_rationale():
d = HeuristicProposer().propose(_sig())
assert isinstance(d, Draft)
assert isinstance(d.coordinate, Coordinate)
assert d.rationale and "\n" not in d.rationale
def test_librivox_seeds_left():
d = HeuristicProposer().propose(
_sig(title="Meditations", source_archive="librivox", mode="audio")
)
assert d.coordinate.left >= 3 and d.rationale
def test_music_archives_seed_right():
for arch in ("musopen", "fma", "nasa", "freesound"):
d = HeuristicProposer().propose(_sig(source_archive=arch, mode="audio"))
assert d.coordinate.right >= 3, arch
def test_internet_archive_seeds_left():
d = HeuristicProposer().propose(_sig(source_archive="internet_archive"))
assert d.coordinate.left >= 3
def test_storm_seeds_dark():
d = HeuristicProposer().propose(
_sig(title="Thunderstorm at Night", source_archive="nasa")
)
assert d.coordinate.dark >= 2
def test_sunrise_seeds_light():
d = HeuristicProposer().propose(
_sig(title="Sunrise over the Garden", source_archive="nasa")
)
assert d.coordinate.light >= 2
def test_coordinates_clamped_to_0_4():
d = HeuristicProposer().propose(
_sig(
title="storm night war funeral decay requiem minor",
description="noir death grief",
source_archive="librivox",
)
)
for v in (d.coordinate.left, d.coordinate.right, d.coordinate.dark, d.coordinate.light):
assert 0 <= v <= 4
def test_rationale_cites_a_signal():
d = HeuristicProposer().propose(
_sig(title="Storm", source_archive="librivox", mode="audio")
)
assert "librivox" in d.rationale.lower()
def test_deterministic():
s = _sig(title="Storm at Dawn", source_archive="nasa")
a = HeuristicProposer().propose(s)
b = HeuristicProposer().propose(s)
assert a == b
+90
View File
@@ -0,0 +1,90 @@
"""Heuristic coordinate proposer — a deterministic, rule-based DRAFT seed.
The four curatorial coordinates are a human act (design §11: no automatic ML
coordinate tagging). This proposer only suggests a starting point written as
review_status='proposed'; a human blesses every coordinate before selection.
There is no ML model here — just archive priors (brain plane) and keyword
nudges (mood plane), grounded in the §8 sourcing→axis map.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Protocol
from hef.selection import Coordinate
COORD_MIN, COORD_MAX = 0, 4
# Brain plane (left = verbal/spoken/educational, right = music/wordless/abstract).
_ARCHIVE_PRIORS = {
"librivox": (4, 0), # spoken readings
"internet_archive": (3, 1), # educational / industrial / Prelinger
"musopen": (0, 4), # classical music
"fma": (0, 4), # music
"nasa": (0, 4), # wordless awe
"freesound": (0, 4), # abstract field recordings
}
_ARCHIVE_DEFAULT = (2, 2)
# Mood plane keywords (substring match so "thunderstorm" trips "storm").
_DARK_WORDS = (
"storm", "night", "noir", "requiem", "minor", "war", "funeral",
"decay", "death", "grief", "dusk", "winter", "mourning", "shadow",
)
_LIGHT_WORDS = (
"sunrise", "dawn", "garden", "spring", "joy", "hope", "major",
"bright", "bloom", "summer", "smile", "light", "morning",
)
def _clamp(v: int) -> int:
return max(COORD_MIN, min(COORD_MAX, v))
@dataclass
class Signals:
title: str
description: str
source_archive: str
mode: str
duration_s: int
@dataclass
class Draft:
coordinate: Coordinate
rationale: str
class Proposer(Protocol):
def propose(self, signals: Signals) -> Draft: ...
class HeuristicProposer:
def propose(self, signals: Signals) -> Draft:
left, right = _ARCHIVE_PRIORS.get(
signals.source_archive, _ARCHIVE_DEFAULT
)
text = f"{signals.title} {signals.description}".lower()
dark_hits = [w for w in _DARK_WORDS if w in text]
light_hits = [w for w in _LIGHT_WORDS if w in text]
dark = _clamp(len(dark_hits))
light = _clamp(len(light_hits))
coordinate = Coordinate(_clamp(left), _clamp(right), dark, light)
# Rationale: name the dominant brain prior + any mood keywords.
brain = (
f"{signals.source_archive}"
+ ("strong left" if left >= right else "strong right")
)
mood_bits = []
if dark_hits:
mood_bits.append(f"{dark_hits[0]!r} → dark")
if light_hits:
mood_bits.append(f"{light_hits[0]!r} → light")
rationale = "; ".join([brain, *mood_bits])
return Draft(coordinate=coordinate, rationale=rationale)