update
This commit is contained in:
@@ -220,7 +220,7 @@ def _fk_of(e: dict) -> dict:
|
||||
async def _generate_block(ctx: GenContext, files: dict, title: str, description: str,
|
||||
instructions: str = "", ns: str = "", lbl: str = "",
|
||||
sources: list[str] | None = None,
|
||||
seeds: list[str] | None = None) -> dict | None:
|
||||
seeds: list[str] | None = None, melde=None) -> dict | None:
|
||||
"""GEN_PANEL unabhängige Generatoren liefern je Subs+Facts+Level/Relevanz in EINEM Call;
|
||||
Konsens im Code (Variant-Cluster über beide Ausgaben, ≥2 unabhängige Generatoren =
|
||||
consensus). Einzelnennungen und ungedeckte Seeds werden „unsicher" — der Prüfer
|
||||
@@ -237,6 +237,8 @@ async def _generate_block(ctx: GenContext, files: dict, title: str, description:
|
||||
"the material; if backed AND not already covered by another entry, include "
|
||||
"them — rephrased as a standalone statement):\n"
|
||||
+ "\n".join(f"- {s}" for s in dict.fromkeys(seeds) if s) + "\n")
|
||||
if melde:
|
||||
melde("Generate")
|
||||
h = _h8(title, description, "gen")
|
||||
paths = [work_dir / f"gen-{h}-g{g}.json" for g in range(1, GEN_PANEL + 1)]
|
||||
prompt = _prompt("Subblock-Generate", topic=topic,
|
||||
@@ -335,7 +337,7 @@ def _default_vote(stimmen: list[str], default: str) -> str:
|
||||
|
||||
async def _verify_block(ctx: GenContext, files: dict, title: str, gen: dict, q: dict,
|
||||
instructions: str = "", ns: str = "", lbl: str = "",
|
||||
sources: list[str] | None = None) -> dict | None:
|
||||
sources: list[str] | None = None, melde=None) -> dict | None:
|
||||
"""VERIFY_PANEL unabhängige Prüfer auditieren den Block in EINEM Call (MECE-Faltung,
|
||||
Fremd, Lücken, Unsicher-Übernahme, Facts-Korrektheit, Level/Relevanz). Auswertung mit
|
||||
Schnittmengen-Semantik pro Befundklasse (Faltung/Fremd/Übernahme einstimmig, Discard
|
||||
@@ -356,6 +358,8 @@ async def _verify_block(ctx: GenContext, files: dict, title: str, gen: dict, q:
|
||||
(u for u in unsicher if u["title"] == t), {})
|
||||
return (fk.get("key_points") or [])[:3]
|
||||
|
||||
if melde:
|
||||
melde("Verify")
|
||||
verdicts: list[dict] = []
|
||||
if n:
|
||||
zeilen = "\n".join(f"{k}. {t}" + "".join(f"\n - {p}" for p in _kp(t))
|
||||
@@ -524,6 +528,8 @@ async def _verify_block(ctx: GenContext, files: dict, title: str, gen: dict, q:
|
||||
|
||||
# 8. Fix-Tail (0–1 Call): Korrekturen + belegte Lücken
|
||||
if (korrekturen or luecken) and not ctx.is_cancelled():
|
||||
if melde:
|
||||
melde("Fix")
|
||||
neu = await _fix_befunde(ctx, files, title, korrekturen, luecken,
|
||||
[s["title"] for s in sidecar_subs], instructions, ns, lbl, sources)
|
||||
for e in neu or []:
|
||||
@@ -603,7 +609,7 @@ def _subs_text(title: str, sidecar_subs: list[dict]) -> str:
|
||||
|
||||
async def _artefakte_block(ctx: GenContext, files: dict, title: str,
|
||||
sidecar_subs: list[dict], instructions: str = "",
|
||||
ns: str = "", lbl: str = "") -> dict | None:
|
||||
ns: str = "", lbl: str = "", melde=None) -> dict | None:
|
||||
"""EIN Generator-Call liefert Fragen + Flashcards + Beispiele (Split in 2 parallele
|
||||
Calls bei > ART_SPLIT_SUBS Subs), EIN Prüfer-Call verifiziert Beispiele, bereinigt
|
||||
die Fragen und ergänzt fehlende. → {pattern, artefacts} | None (nur Cancel)."""
|
||||
@@ -611,6 +617,8 @@ async def _artefakte_block(ctx: GenContext, files: dict, title: str,
|
||||
work_dir = files["arbeit"]
|
||||
if not sidecar_subs:
|
||||
return {"pattern": {title: []}, "artefacts": {"flashcard": [], "example": []}}
|
||||
if melde:
|
||||
melde("Artefakte gen")
|
||||
sh = _subs_hash({title: sidecar_subs})
|
||||
haelften = ([sidecar_subs] if len(sidecar_subs) <= ART_SPLIT_SUBS
|
||||
else [sidecar_subs[:len(sidecar_subs) // 2], sidecar_subs[len(sidecar_subs) // 2:]])
|
||||
@@ -647,6 +655,8 @@ async def _artefakte_block(ctx: GenContext, files: dict, title: str,
|
||||
fehlend = [t for t in sub_titles
|
||||
if _norm_title(t) not in {_norm_title(p["subblock"]) for p in pattern}]
|
||||
if pattern or examples:
|
||||
if melde:
|
||||
melde("Artefakte check")
|
||||
tabelle = "\n".join(f"({p['subblock']}) {p['question']}" for p in pattern) or "(keine)"
|
||||
beisp = "\n\n".join(
|
||||
f"{k}. PROBLEM: {e['problem']}\n SCHRITTE: " + " | ".join(e["steps"])
|
||||
|
||||
@@ -167,6 +167,19 @@ async def _seed_map(topic: str) -> dict[str, list[str]]:
|
||||
return seeds
|
||||
|
||||
|
||||
def _melder(flow: Flow, norm: str):
|
||||
"""Live-Stepper der Karte: Phasenname → card_info (ging bei der Call-Verschmelzung
|
||||
verloren — Karten liefen ohne Badge/Stepper durch das Board)."""
|
||||
set_p = _card_set_p(flow, norm)
|
||||
|
||||
def melde(schritt: str) -> None:
|
||||
try:
|
||||
set_p(f"{schritt}…", step=blocks._step_idx(flow.topic, schritt))
|
||||
except ValueError:
|
||||
set_p(f"{schritt}…")
|
||||
return melde
|
||||
|
||||
|
||||
async def _proc_generate(ctx: GenContext, flow: Flow, files: dict, instructions: str, cards):
|
||||
"""Verschmolzener Erzeuger: 2 unabhängige Generatoren liefern Subs+Facts+Einstufung
|
||||
in EINEM Call, Konsens im Code (block_calls._generate_block)."""
|
||||
@@ -181,7 +194,8 @@ async def _proc_generate(ctx: GenContext, flow: Flow, files: dict, instructions:
|
||||
p.get("description", ""), instructions,
|
||||
ns=f"{_safe(norm)}-", lbl=f"{p.get('title', norm)} · ",
|
||||
sources=p.get("sources"),
|
||||
seeds=[s for s in seeds.get(norm, []) if s] or None)
|
||||
seeds=[s for s in seeds.get(norm, []) if s] or None,
|
||||
melde=_melder(flow, norm))
|
||||
if gen is None:
|
||||
return _fail_or_cancel(ctx, f"Generate {p.get('title', norm)}")
|
||||
p["raw"], p["facts"] = gen["raw"], gen["facts"]
|
||||
@@ -207,7 +221,7 @@ async def _proc_verify(ctx: GenContext, flow: Flow, files: dict, q: dict, instru
|
||||
"unsicher": p.get("unsicher") or [], "votes": p.get("votes") or {}}
|
||||
res = await _verify_block(ctx, _pfiles(files, norm), title, gen, q, instructions,
|
||||
ns=f"{_safe(norm)}-", lbl=f"{title or norm} · ",
|
||||
sources=p.get("sources"))
|
||||
sources=p.get("sources"), melde=_melder(flow, norm))
|
||||
if res is None:
|
||||
return None # nur Cancel — Karte bleibt liegen
|
||||
p["raw"], p["facts"], p["sidecar"] = res["raw"], res["facts"], res["sidecar"]
|
||||
@@ -230,7 +244,8 @@ async def _proc_artefakte(ctx: GenContext, flow: Flow, files: dict, instructions
|
||||
title = p.get("title", "")
|
||||
res = await _artefakte_block(ctx, _pfiles(files, norm), title,
|
||||
(p.get("sidecar") or {}).get(title) or [],
|
||||
instructions, ns=f"{_safe(norm)}-", lbl=f"{title or norm} · ")
|
||||
instructions, ns=f"{_safe(norm)}-", lbl=f"{title or norm} · ",
|
||||
melde=_melder(flow, norm))
|
||||
if res is None:
|
||||
return None # nur Cancel
|
||||
p["pattern"] = res["pattern"]
|
||||
|
||||
@@ -266,7 +266,10 @@ FORMAT_PURPOSE = {
|
||||
# "judge" = mapping/judge/check agents — cold (low temperature,
|
||||
# no thinking) for stable verdicts; Claude/local map to "fast",
|
||||
# "guide" = large generation (proposals, writer).
|
||||
DEFAULT_PROVIDER = "claude"
|
||||
# Kein Provider-Default im Code (Betreiber-Vorgabe): die .env entscheidet.
|
||||
DEFAULT_PROVIDER = os.getenv("DEFAULT_PROVIDER", "")
|
||||
if not DEFAULT_PROVIDER:
|
||||
raise RuntimeError("DEFAULT_PROVIDER fehlt in der .env (z. B. DEFAULT_PROVIDER=minimax)")
|
||||
PROVIDERS = {
|
||||
"claude": {
|
||||
"cli": "claude",
|
||||
|
||||
@@ -290,7 +290,7 @@ def aktivieren(welt: Welt, setattr_fn=setattr) -> None:
|
||||
import qa
|
||||
import repair
|
||||
|
||||
async def fake_run_agent(agent_key, prompt, timeout, provider="claude", role="fast",
|
||||
async def fake_run_agent(agent_key, prompt, timeout, provider="", role="fast",
|
||||
capabilities="none", lane="batch", scope=None, on_line=None, label=""):
|
||||
return welt.respond(agent_key, prompt, capabilities)
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ FormatType = Literal[
|
||||
"Rest",
|
||||
]
|
||||
|
||||
from config import DEFAULT_PROVIDER
|
||||
|
||||
ProviderType = Literal["claude", "minimax", "lokal"]
|
||||
|
||||
SourceType = Literal["thema", "projekt", "uni", "link"]
|
||||
@@ -16,7 +18,7 @@ class GuideCreateRequest(BaseModel):
|
||||
topic: str = Field(min_length=1, max_length=100)
|
||||
format: FormatType
|
||||
instructions: str = Field(default="", max_length=2000)
|
||||
provider: ProviderType = "claude"
|
||||
provider: ProviderType = DEFAULT_PROVIDER
|
||||
ab_step: int | None = Field(default=None, ge=0, le=5) # re-run from board stage (0 lernziele … 5 lesbarkeit); None = full/resume
|
||||
|
||||
|
||||
@@ -42,7 +44,7 @@ class RepairRequest(BaseModel):
|
||||
class BlocksCreateRequest(BaseModel):
|
||||
topic: str = Field(min_length=1, max_length=100)
|
||||
instructions: str = Field(default="", max_length=2000)
|
||||
provider: ProviderType = "claude"
|
||||
provider: ProviderType = DEFAULT_PROVIDER
|
||||
source_type: SourceType = "thema"
|
||||
source_location: str = Field(default="", max_length=2000)
|
||||
research: bool = True # False = Continue: drain the existing kanban queue, no new search
|
||||
@@ -157,7 +159,7 @@ class GuideChatRequest(BaseModel):
|
||||
section: str = Field(default="", max_length=20000)
|
||||
outline: str = Field(default="", max_length=8000)
|
||||
messages: list[ChatMessage] = Field(min_length=1)
|
||||
provider: ProviderType = "claude"
|
||||
provider: ProviderType = DEFAULT_PROVIDER
|
||||
|
||||
|
||||
class GuideChatResponse(BaseModel):
|
||||
@@ -172,7 +174,7 @@ class BlockChatRequest(BaseModel):
|
||||
section: str = Field(default="", max_length=20000) # detailed version
|
||||
section_compact: str = Field(default="", max_length=20000) # compact version (mnemonics)
|
||||
messages: list[ChatMessage] = Field(min_length=1)
|
||||
provider: ProviderType = "claude"
|
||||
provider: ProviderType = DEFAULT_PROVIDER
|
||||
|
||||
|
||||
class BlockChatResponse(BaseModel):
|
||||
@@ -203,7 +205,7 @@ class BlockExamRequest(BaseModel):
|
||||
# Base + cap are kept server-side (anchor / subs×25) — the client cap is only a hint.
|
||||
cap: int = Field(default=10, ge=1, le=10000) # score cap = unlocked subblocks × 25
|
||||
messages: list[ChatMessage] = [] # dialog so far; empty = first question
|
||||
provider: ProviderType = "claude"
|
||||
provider: ProviderType = DEFAULT_PROVIDER
|
||||
thorough: bool = False # "thorough check": rating with a strong model (role guide)
|
||||
|
||||
|
||||
@@ -246,7 +248,7 @@ class BlockPruefenRequest(BaseModel):
|
||||
spot: str = "ausführlich" # "compact" | "ausführlich" (displayed field)
|
||||
snippet: str = Field(min_length=1, max_length=20000) # raw markdown block
|
||||
hint: str = Field(default="", max_length=2000) # optional addition (✏️)
|
||||
provider: ProviderType = "claude"
|
||||
provider: ProviderType = DEFAULT_PROVIDER
|
||||
|
||||
|
||||
class BlockPruefenResponse(BaseModel):
|
||||
@@ -258,7 +260,7 @@ class BlockUebernehmenRequest(BaseModel):
|
||||
spot: str = "ausführlich"
|
||||
alt: str = Field(min_length=1, max_length=20000)
|
||||
revised: str = Field(default="", max_length=20000)
|
||||
provider: ProviderType = "claude"
|
||||
provider: ProviderType = DEFAULT_PROVIDER
|
||||
|
||||
|
||||
class BlockUebernehmenResponse(BaseModel):
|
||||
|
||||
@@ -217,7 +217,7 @@ async def run_repair_route(req: RepairRequest):
|
||||
|
||||
|
||||
@router.post("/blocks/research")
|
||||
async def add_blocks_research(topic: str, provider: str = "claude"):
|
||||
async def add_blocks_research(topic: str, provider: str = DEFAULT_PROVIDER):
|
||||
"""Attach one more research agent — to the live flow, or attach-or-start."""
|
||||
if add_research_agent(topic):
|
||||
return {"ok": True, "attached": True}
|
||||
|
||||
@@ -71,14 +71,14 @@ async def board_env(testdb, tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(bi, "_emb_ok", no_emb)
|
||||
|
||||
async def fake_generate(ctx, files, title, description, instructions="", ns="", lbl="",
|
||||
sources=None, seeds=None):
|
||||
sources=None, seeds=None, melde=None):
|
||||
subs = ["Sub Eins", "Sub Zwei"]
|
||||
return {"raw": {title: list(subs)},
|
||||
"facts": {title: {_norm_title(s): {"key_points": [f"Fakt zu {s}"], "cited_facts": []}
|
||||
for s in subs}},
|
||||
"unsicher": [], "votes": {}}
|
||||
|
||||
async def fake_verify(ctx, files, title, gen, q, instructions="", ns="", lbl="", sources=None):
|
||||
async def fake_verify(ctx, files, title, gen, q, instructions="", ns="", lbl="", sources=None, melde=None):
|
||||
subs = gen["raw"].get(title) or []
|
||||
bfacts = gen["facts"].get(title) or {}
|
||||
sidecar = [{"title": s, "level": "beginner",
|
||||
@@ -87,7 +87,7 @@ async def board_env(testdb, tmp_path, monkeypatch):
|
||||
for i, s in enumerate(subs)]
|
||||
return {"raw": {title: list(subs)}, "facts": {title: bfacts}, "sidecar": {title: sidecar}}
|
||||
|
||||
async def fake_artefakte(ctx, files, title, sidecar_subs, instructions="", ns="", lbl=""):
|
||||
async def fake_artefakte(ctx, files, title, sidecar_subs, instructions="", ns="", lbl="", melde=None):
|
||||
if not sidecar_subs:
|
||||
return {"pattern": {title: []}, "artefacts": {"flashcard": [], "example": []}}
|
||||
first = sidecar_subs[0]["title"]
|
||||
@@ -242,7 +242,7 @@ async def test_empty_subblocks_completes_without_deadletter(board_env, monkeypat
|
||||
db, ctx, files = board_env
|
||||
|
||||
async def empty_gen(ctx, files, title, description, instructions="", ns="", lbl="",
|
||||
sources=None, seeds=None):
|
||||
sources=None, seeds=None, melde=None):
|
||||
return {"raw": {title: []}, "facts": {title: {}}, "unsicher": [], "votes": {}}
|
||||
monkeypatch.setattr(ba, "_generate_block", empty_gen)
|
||||
await db.kanban_upsert_card(TOPIC, "artefacts", "leer", "ablock", "generate",
|
||||
@@ -719,7 +719,7 @@ async def test_outline_runs_before_artefacts_finish(board_env, monkeypatch):
|
||||
base_verify = ba._verify_block
|
||||
snapshot = {}
|
||||
|
||||
async def slow_verify(ctx, files, title, gen, q, instructions="", ns="", lbl="", sources=None):
|
||||
async def slow_verify(ctx, files, title, gen, q, instructions="", ns="", lbl="", sources=None, melde=None):
|
||||
await asyncio.sleep(0.8) # keeps one card in `verify` while the outline fires
|
||||
return await base_verify(ctx, files, title, gen, q, instructions, ns=ns, lbl=lbl, sources=sources)
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ from pathlib import Path
|
||||
|
||||
# WICHTIG: config (mit CREATOR_PARAMS) lädt vor allen Pipeline-Modulen
|
||||
import database
|
||||
from config import DEFAULT_PROVIDER
|
||||
from fake_agents import Welt, aktivieren
|
||||
from fsutil import atomic_write_json
|
||||
|
||||
@@ -40,7 +41,7 @@ async def f0(out: str) -> None:
|
||||
"artefakte": tmp / "artefakte.json", "outline": tmp / "outline.json",
|
||||
"outline_slots": [tmp / f"outline-{i}.json" for i in (1, 2, 3)],
|
||||
"research": [work / f"research-{i}.md" for i in (1, 2, 3, 4, 5)]}
|
||||
ctx = GenContext(topic="f0", provider="claude", is_cancelled=lambda: False)
|
||||
ctx = GenContext(topic="f0", provider=DEFAULT_PROVIDER, is_cancelled=lambda: False)
|
||||
start = time.monotonic()
|
||||
ok = await asyncio.wait_for(
|
||||
bi.run_boards(ctx, lambda *a, **k: None, files, {"type": "thema"}, None, "",
|
||||
|
||||
Reference in New Issue
Block a user