update
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||
@@ -8,6 +9,26 @@ DB_PATH = STORAGE_DIR / "creator.db"
|
||||
PROJECTS_DIR = PROJECT_ROOT / "projects"
|
||||
UNI_DIR = PROJECT_ROOT / "uni"
|
||||
|
||||
|
||||
def _load_env(path: Path) -> None:
|
||||
"""Mini .env loader (no dependency): KEY=VALUE lines; existing env always wins
|
||||
(`make dev` already exports .env — this covers bare `uvicorn`/pytest starts)."""
|
||||
try:
|
||||
text = path.read_text(encoding="utf-8")
|
||||
except OSError:
|
||||
return
|
||||
for line in text.splitlines():
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
key, _, value = line.partition("=")
|
||||
key, value = key.strip(), value.strip().strip('"').strip("'")
|
||||
if key and key not in os.environ:
|
||||
os.environ[key] = value
|
||||
|
||||
|
||||
_load_env(PROJECT_ROOT / ".env")
|
||||
|
||||
MAX_CONCURRENT_GENERATIONS = 10
|
||||
|
||||
# Readability gate: deterministic checker (small German complexity model,
|
||||
@@ -21,12 +42,9 @@ READABILITY_MAX = 3.5 # section too hard when the sentence average is a
|
||||
READABILITY_HARD = 4.0 # an individual sentence is "hard" from here on
|
||||
READABILITY_HARD_SHARE = 0.30 # … OR when this share of sentences is hard
|
||||
|
||||
# Block consolidation: semantic embedding clustering instead of an LLM list merge.
|
||||
# A small multilingual sentence embedding (mean-pool) builds the candidate clusters
|
||||
# GLOBALLY (no chunk loss) via cosine + union-find. Title variants of the same concept
|
||||
# ("Vertex Cover" / "Vertex Cover Definition") merge; the consensus then counts the
|
||||
# real readers per cluster (≥2 = consensus). If transformers/torch are missing or the model
|
||||
# won't load → embedding silently off, `_consolidate` falls back to the old panel-judge path.
|
||||
# Kanban clustering: semantic embeddings drive the online title clustering and the
|
||||
# candidate pairs of the pair check. If transformers/torch are missing or the model
|
||||
# won't load → embedding silently off (all pairs go to the judge, clusters stay singletons).
|
||||
EMBEDDING_AKTIV = True
|
||||
EMBEDDING_MODELL = "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2" # CPU, multilingual, ~470 MB
|
||||
# Stronger (larger) CPU alternative if needed: "BAAI/bge-m3".
|
||||
@@ -66,10 +84,12 @@ GROUP_RECONCILE_FLOOR = 0.75
|
||||
# chain (random-pair baseline), set BELOW the legitimate heterogeneous minimum so it never kills a real model.
|
||||
GROUP_MIN_COS_FLOOR = 0.15
|
||||
|
||||
# Cap for concurrent CLI agent processes (across all generations).
|
||||
# Own lane for interactive calls (chat, elements) so they don't hang behind
|
||||
# running writers in the queue.
|
||||
MAX_CONCURRENT_AGENTS = 10
|
||||
# Caps for concurrent CLI agent processes (env-overridable). Two nested limits, both always active:
|
||||
# a per-topic cap and a global cap across all topics. Defaults 10/10 = previous behavior (global
|
||||
# dominates). Locally raise the global cap to actually parallelize across topics (per-topic stays 10).
|
||||
# Own lane for interactive calls (chat, elements) so they don't hang behind running writers.
|
||||
MAX_CONCURRENT_AGENTS = int(os.getenv("MAX_CONCURRENT_AGENTS", "16")) # global, all topics
|
||||
MAX_CONCURRENT_AGENTS_PER_TOPIC = int(os.getenv("MAX_CONCURRENT_AGENTS_PER_TOPIC", "12")) # per topic
|
||||
MAX_CONCURRENT_INTERACTIVE = 8
|
||||
|
||||
# Grace window of the consensus races (blocks, guide, OnePager): after the first
|
||||
@@ -123,6 +143,10 @@ TIMEOUTS = {
|
||||
"question_pattern_check": (300, 10), # critic cleans up the pattern table per block
|
||||
"writer": (600, 120), # per section in the chunk
|
||||
"lese_check": (300, 10), # per section in the package
|
||||
# guide board (per card = one block)
|
||||
"lernziele": (300, 5), # backward-design objectives per block
|
||||
"fakten_gate": (600, 5), # CoVe claim check per block
|
||||
"coverage": (300, 5), # objective↔section mapping per block
|
||||
}
|
||||
|
||||
# Purpose per format — flows into the outline judge (what the guide should achieve).
|
||||
@@ -169,3 +193,27 @@ PROVIDERS = {
|
||||
"check_url": "http://localhost:11434/api/tags", # Ollama reachable?
|
||||
},
|
||||
}
|
||||
|
||||
# Role routing ACROSS provider stacks: generation (quick/guide) and judging (judge)
|
||||
# may run on different providers within ONE run — judge model ≠ generator model
|
||||
# (research-backed: cross-model judging avoids self-preference bias).
|
||||
# Value: "" = provider of the run; "minimax" = that stack's role model;
|
||||
# "provider:model" = explicit model override.
|
||||
ROLE_ROUTING = {
|
||||
"quick": os.getenv("ROLE_QUICK", "minimax"),
|
||||
"judge": os.getenv("ROLE_JUDGE", "claude"),
|
||||
"guide": os.getenv("ROLE_GUIDE", "minimax"),
|
||||
"fast": os.getenv("ROLE_FAST", ""),
|
||||
}
|
||||
|
||||
|
||||
def resolve_role(run_provider: str, role: str) -> tuple[str, str]:
|
||||
"""→ (provider, model) for one agent call. Pure routing, no availability check —
|
||||
the caller (agents.run_agent) falls back to run_provider if the target is unavailable."""
|
||||
target = ROLE_ROUTING.get(role, "") or run_provider
|
||||
provider, _, model = target.partition(":")
|
||||
if provider not in PROVIDERS:
|
||||
provider, model = run_provider, ""
|
||||
if not model:
|
||||
model = PROVIDERS.get(provider, {}).get(role, "")
|
||||
return provider, model
|
||||
|
||||
Reference in New Issue
Block a user