update
This commit is contained in:
@@ -43,7 +43,7 @@ from blocks import (
|
||||
_filter_schema, _filter_suspect, _is_artifact, _is_named_statement,
|
||||
_is_parentless_noise, _is_reference, _pairs_schema, _read,
|
||||
_relation_conflict, _root, _supplement_schema, _text_sections, _umbrella_schema,
|
||||
_aspect_marker,
|
||||
_aspect_marker, _title_variants, _evidence_pack, _sink_json, source_folder,
|
||||
)
|
||||
from config import (
|
||||
BLOCKS_GRUPPIERUNG_AKTIV, EMBEDDING_AKTIV, EMBEDDING_BLOCK_CAP,
|
||||
@@ -53,8 +53,8 @@ from config import (
|
||||
from fsutil import atomic_write_json, atomic_write_text
|
||||
from jsonio import read_json_file as _json_file
|
||||
from pipeline import (CANCELLED, FAILED, OK, GenContext, _extra, _log, _prompt,
|
||||
_runde_schema, _timeout, run_single_slot)
|
||||
from textkit import _norm_title, _parse_selection, _title
|
||||
_runde_schema, _timeout, _yesno_schema, run_single_slot)
|
||||
from textkit import _norm_title, _parse_selection, _title, clean_title
|
||||
|
||||
log = logging.getLogger("creator.board_inventory")
|
||||
|
||||
@@ -144,13 +144,13 @@ async def _ingest_titles(flow: Flow, text: str, reader: str, source: str = "") -
|
||||
reader union. Repeated drains over a growing buffer are idempotent. → new count."""
|
||||
n, seen = 0, set()
|
||||
for record in _parse_selection(text).values():
|
||||
title = _title(record)
|
||||
title = clean_title(_title(record))
|
||||
norm = _norm_title(title)
|
||||
if not norm or norm in seen:
|
||||
continue
|
||||
seen.add(norm) # one reader = one vote per concept
|
||||
parts = [t.strip() for t in record.split(" — ")]
|
||||
desc = parts[1] if len(parts) >= 2 else ""
|
||||
desc = clean_title(parts[1]) if len(parts) >= 2 else ""
|
||||
src = source or (parts[2] if len(parts) >= 3 else "")
|
||||
async with _ingest_lock:
|
||||
if await db.kanban_add_title(flow.topic, BOARD, norm, title, desc, src, reader):
|
||||
@@ -924,6 +924,15 @@ async def _proc_dedup(ctx: GenContext, flow: Flow, cards):
|
||||
for y in range(x + 1, len(grp)):
|
||||
if _demotable_pair(grp[x], grp[y]):
|
||||
pairs.add((grp[x], grp[y]))
|
||||
# acronym↔expansion pairs ("SAT" vs "Satisfiability Problem (SAT)"): the title cosine
|
||||
# of short vs long form sits far below the floor — variant match makes them candidates,
|
||||
# the judge panel decides as usual (no auto-merge)
|
||||
idents = [{_norm_title(r["title"]), _canonical_key(r["title"])} - {""} for r in allrows]
|
||||
variants = [_title_variants(r["title"]) for r in allrows]
|
||||
for i in range(n_all):
|
||||
for j in range(i + 1, n_all):
|
||||
if _demotable_pair(i, j) and (variants[i] & idents[j] or variants[j] & idents[i]):
|
||||
pairs.add((i, j))
|
||||
ordered = sorted(pairs)
|
||||
h = _h(*[r["card_id"] for r in allrows])
|
||||
if not ordered:
|
||||
@@ -1176,7 +1185,7 @@ async def _proc_grouping(ctx: GenContext, flow: Flow, cards):
|
||||
readers = sorted(set().union(*[set(r["payload"].get("readers") or []) for r in mrows]))
|
||||
srcs = sorted(set().union(*[set(r["payload"].get("sources") or []) for r in mrows]))
|
||||
await db.kanban_upsert_card(topic, BOARD, f"b-u-{uuid.uuid4().hex[:8]}", "block", "gap_check", {
|
||||
"title": c["umbrella"], "description": c["description"],
|
||||
"title": clean_title(c["umbrella"]), "description": c["description"],
|
||||
"readers": readers, "sources": srcs, "umbrella": True,
|
||||
"children": [r["title"] for r in mrows],
|
||||
})
|
||||
@@ -1221,6 +1230,37 @@ async def _proc_gap_check(ctx: GenContext, flow: Flow, cards):
|
||||
flow.wake.set()
|
||||
|
||||
|
||||
async def _supplement_beleg(ctx: GenContext, flow: Flow, supplements: list) -> list:
|
||||
"""Evidence gate for supplement proposals: keyword excerpts per proposal, ONE no-tool
|
||||
judge marks material coverage (ja/nein). Proposals without any matching excerpt drop
|
||||
immediately; a failed gate keeps nothing (creep is costlier than a lost bonus round)."""
|
||||
topic = flow.topic
|
||||
folder = source_folder(topic)
|
||||
packs = [(t, d, _evidence_pack(folder, None, [t], budget=6000)) for t, d in supplements]
|
||||
cands = [(t, d, ev) for t, d, ev in packs if ev]
|
||||
kept: list = []
|
||||
if cands:
|
||||
path = flow.work_dir / "supplement-beleg.json"
|
||||
ids = set(range(1, len(cands) + 1))
|
||||
verdict = _yesno_schema(_json_file(path), ids)
|
||||
if verdict is None:
|
||||
lines = "\n\n".join(f"{k}. {t} — {d}\nAUSZÜGE:\n{ev}"
|
||||
for k, (t, d, ev) in enumerate(cands, 1))
|
||||
status, verdict = await run_single_slot(
|
||||
ctx, "Supplement-Beleg", key=f"blocks-{topic}-supplement-beleg",
|
||||
prompt=_prompt("Blocks-Supplement-Beleg", topic=topic, proposals=lines,
|
||||
extra=_extra(flow.state.get("instructions", ""))),
|
||||
role="judge", capabilities="none",
|
||||
payload=lambda result, p=path, i=ids: _sink_json(result, p, lambda d2: _yesno_schema(d2, i)),
|
||||
timeout=_timeout("selection_mapping", len(cands)))
|
||||
if status != OK or not isinstance(verdict, dict):
|
||||
verdict = {}
|
||||
kept = [(t, d) for k, (t, d, ev) in enumerate(cands, 1) if str(verdict.get(k, "nein")) == "ja"]
|
||||
if len(kept) < len(supplements):
|
||||
_log(topic, f"Supplement: {len(supplements) - len(kept)}/{len(supplements)} ohne Materialbeleg verworfen")
|
||||
return kept
|
||||
|
||||
|
||||
async def _supplement_producer(ctx: GenContext, flow: Flow, titles: list[str]):
|
||||
"""One web agent proposes canonically missing blocks → new title cards (reader
|
||||
'supplement' skips only the ≥2 consensus bar, every other gate applies)."""
|
||||
@@ -1242,6 +1282,11 @@ async def _supplement_producer(ctx: GenContext, flow: Flow, titles: list[str]):
|
||||
if status != OK:
|
||||
_log(topic, "Supplement fehlgeschlagen — übersprungen (optional)")
|
||||
supplements = []
|
||||
# Material anchoring (uni/projekt): the web agent proposes canonical knowledge BLIND
|
||||
# to the source (measured: 22/107 aak blocks were textbook standard the script never
|
||||
# treats). Only proposals the material itself covers may enter the inventory.
|
||||
if supplements and source_folder(topic):
|
||||
supplements = await _supplement_beleg(ctx, flow, supplements)
|
||||
# Dead lineage: blocks demoted by the fragment filter (and their cluster + title cards)
|
||||
# must NOT dedup a supplement proposal — their content is gone. A hit on a dead title
|
||||
# REOPENS the lineage instead: the title card rejoins its cluster (live re-cluster) and
|
||||
@@ -1281,6 +1326,7 @@ async def _supplement_producer(ctx: GenContext, flow: Flow, titles: list[str]):
|
||||
known_keys.add(k)
|
||||
new = reopened = 0
|
||||
for t, d in (supplements or []):
|
||||
t, d = clean_title(t), clean_title(d)
|
||||
norm = _norm_title(t)
|
||||
key = _canonical_key(t)
|
||||
if not norm or norm in known_norms or (key and key in known_keys):
|
||||
|
||||
Reference in New Issue
Block a user