Backend: globale Agent-Semaphores (batch 12 / interactive 4)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,12 +14,18 @@ import time
|
|||||||
import urllib.request
|
import urllib.request
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from config import PROVIDERS, DEFAULT_PROVIDER
|
from config import PROVIDERS, DEFAULT_PROVIDER, MAX_CONCURRENT_AGENTS, MAX_CONCURRENT_INTERACTIVE
|
||||||
|
|
||||||
log = logging.getLogger("creator.agents")
|
log = logging.getLogger("creator.agents")
|
||||||
|
|
||||||
_active_processes: dict[str, asyncio.subprocess.Process] = {}
|
_active_processes: dict[str, asyncio.subprocess.Process] = {}
|
||||||
|
|
||||||
|
# Deckelt die realen CLI-Prozesse — unabhängig von der Pipeline-Semaphore in
|
||||||
|
# generator.py. Acquire passiert VOR dem Spawn, damit Wartezeit in der Queue
|
||||||
|
# nicht gegen den Agent-Timeout zählt.
|
||||||
|
_batch_sem = asyncio.Semaphore(MAX_CONCURRENT_AGENTS)
|
||||||
|
_interactive_sem = asyncio.Semaphore(MAX_CONCURRENT_INTERACTIVE)
|
||||||
|
|
||||||
# Capability → Claude --allowedTools
|
# Capability → Claude --allowedTools
|
||||||
_CLAUDE_TOOLS = {
|
_CLAUDE_TOOLS = {
|
||||||
"full": "Write,Bash,Read,WebSearch,WebFetch",
|
"full": "Write,Bash,Read,WebSearch,WebFetch",
|
||||||
@@ -73,14 +79,17 @@ async def run_agent(
|
|||||||
provider: str = DEFAULT_PROVIDER,
|
provider: str = DEFAULT_PROVIDER,
|
||||||
role: str = "fast",
|
role: str = "fast",
|
||||||
capabilities: str = "none",
|
capabilities: str = "none",
|
||||||
|
lane: str = "batch",
|
||||||
) -> tuple[int, str, str]:
|
) -> tuple[int, str, str]:
|
||||||
if provider not in PROVIDERS:
|
if provider not in PROVIDERS:
|
||||||
return 1, "", f"Unbekannter Provider: {provider}"
|
return 1, "", f"Unbekannter Provider: {provider}"
|
||||||
if shutil.which(PROVIDERS[provider]["cli"]) is None:
|
if shutil.which(PROVIDERS[provider]["cli"]) is None:
|
||||||
return 1, "", f"CLI '{PROVIDERS[provider]['cli']}' nicht installiert (Provider: {provider})"
|
return 1, "", f"CLI '{PROVIDERS[provider]['cli']}' nicht installiert (Provider: {provider})"
|
||||||
if PROVIDERS[provider]["cli"] == "opencode":
|
sem = _interactive_sem if lane == "interactive" else _batch_sem
|
||||||
return await _run_opencode(agent_key, prompt, timeout, provider, role, capabilities)
|
async with sem:
|
||||||
return await _run_claude_cli(agent_key, prompt, timeout, role, capabilities)
|
if PROVIDERS[provider]["cli"] == "opencode":
|
||||||
|
return await _run_opencode(agent_key, prompt, timeout, provider, role, capabilities)
|
||||||
|
return await _run_claude_cli(agent_key, prompt, timeout, role, capabilities)
|
||||||
|
|
||||||
|
|
||||||
async def _communicate(agent_key: str, cmd: list[str], stdin_data: bytes | None, timeout: int) -> tuple[int, str, str]:
|
async def _communicate(agent_key: str, cmd: list[str], stdin_data: bytes | None, timeout: int) -> tuple[int, str, str]:
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ PROJECTS_DIR = PROJECT_ROOT / "projects"
|
|||||||
|
|
||||||
MAX_CONCURRENT_GENERATIONS = 10
|
MAX_CONCURRENT_GENERATIONS = 10
|
||||||
|
|
||||||
|
# Deckel für gleichzeitige CLI-Agenten-Prozesse (über alle Generierungen hinweg).
|
||||||
|
# Eigene Spur für interaktive Aufrufe (Chat, Elemente), damit sie nicht hinter
|
||||||
|
# laufenden Writern in der Warteschlange hängen.
|
||||||
|
MAX_CONCURRENT_AGENTS = 12
|
||||||
|
MAX_CONCURRENT_INTERACTIVE = 4
|
||||||
|
|
||||||
# Timeouts pro Agenten-Schritt: (Basis-Sekunden, Sekunden pro Baustein/Section).
|
# Timeouts pro Agenten-Schritt: (Basis-Sekunden, Sekunden pro Baustein/Section).
|
||||||
# Gilt für alle Provider gleich — wer zu langsam ist, wird neu gestartet bzw. überholt.
|
# Gilt für alle Provider gleich — wer zu langsam ist, wird neu gestartet bzw. überholt.
|
||||||
TIMEOUTS = {
|
TIMEOUTS = {
|
||||||
|
|||||||
@@ -1349,7 +1349,7 @@ async def chat_with_guide(topic: str, format_name: str, section: str, outline: s
|
|||||||
try:
|
try:
|
||||||
prompt = _build_guide_chat_prompt(topic, format_name, section, outline, messages)
|
prompt = _build_guide_chat_prompt(topic, format_name, section, outline, messages)
|
||||||
returncode, stdout, stderr = await run_agent(
|
returncode, stdout, stderr = await run_agent(
|
||||||
"chat-" + str(uuid.uuid4()), prompt, 240, provider=provider, role="fast", capabilities="none"
|
"chat-" + str(uuid.uuid4()), prompt, 240, provider=provider, role="fast", capabilities="none", lane="interactive"
|
||||||
)
|
)
|
||||||
if returncode != 0:
|
if returncode != 0:
|
||||||
return "Entschuldigung, das hat nicht geklappt. Bitte versuche es erneut."
|
return "Entschuldigung, das hat nicht geklappt. Bitte versuche es erneut."
|
||||||
@@ -1434,7 +1434,7 @@ async def generate_element(topic: str, hint: str, provider: str = DEFAULT_PROVID
|
|||||||
context=_topic_context(topic),
|
context=_topic_context(topic),
|
||||||
)
|
)
|
||||||
returncode, stdout, _ = await run_agent(
|
returncode, stdout, _ = await run_agent(
|
||||||
"element-" + str(uuid.uuid4()), prompt, 240, provider=provider, role="fast", capabilities="none"
|
"element-" + str(uuid.uuid4()), prompt, 240, provider=provider, role="fast", capabilities="none", lane="interactive"
|
||||||
)
|
)
|
||||||
if returncode != 0:
|
if returncode != 0:
|
||||||
return fallback
|
return fallback
|
||||||
@@ -1473,7 +1473,7 @@ async def check_element(element: dict, provider: str = DEFAULT_PROVIDER) -> list
|
|||||||
# Schritt 1: Recherche — breit Kandidaten sammeln
|
# Schritt 1: Recherche — breit Kandidaten sammeln
|
||||||
prompt = _prompt("Element-Check", topic=element["topic"], element_json=element_json, context=context)
|
prompt = _prompt("Element-Check", topic=element["topic"], element_json=element_json, context=context)
|
||||||
returncode, stdout, _ = await run_agent(
|
returncode, stdout, _ = await run_agent(
|
||||||
"element-check-" + str(uuid.uuid4()), prompt, 240, provider=provider, role="fast", capabilities="none"
|
"element-check-" + str(uuid.uuid4()), prompt, 240, provider=provider, role="fast", capabilities="none", lane="interactive"
|
||||||
)
|
)
|
||||||
if returncode != 0:
|
if returncode != 0:
|
||||||
return None
|
return None
|
||||||
@@ -1491,7 +1491,7 @@ async def check_element(element: dict, provider: str = DEFAULT_PROVIDER) -> list
|
|||||||
context=context,
|
context=context,
|
||||||
)
|
)
|
||||||
returncode, stdout, _ = await run_agent(
|
returncode, stdout, _ = await run_agent(
|
||||||
"element-verify-" + str(uuid.uuid4()), prompt, 240, provider=provider, role="fast", capabilities="none"
|
"element-verify-" + str(uuid.uuid4()), prompt, 240, provider=provider, role="fast", capabilities="none", lane="interactive"
|
||||||
)
|
)
|
||||||
if returncode != 0:
|
if returncode != 0:
|
||||||
return None
|
return None
|
||||||
@@ -1544,7 +1544,7 @@ async def chat_with_element(element: dict, messages: list[dict], provider: str =
|
|||||||
)
|
)
|
||||||
prompt = _prompt("Element-Chat", topic=element["topic"], element_json=_element_json(element), transcript=transcript)
|
prompt = _prompt("Element-Chat", topic=element["topic"], element_json=_element_json(element), transcript=transcript)
|
||||||
returncode, stdout, _ = await run_agent(
|
returncode, stdout, _ = await run_agent(
|
||||||
"element-chat-" + str(uuid.uuid4()), prompt, 240, provider=provider, role="fast", capabilities="none"
|
"element-chat-" + str(uuid.uuid4()), prompt, 240, provider=provider, role="fast", capabilities="none", lane="interactive"
|
||||||
)
|
)
|
||||||
if returncode != 0:
|
if returncode != 0:
|
||||||
return fehler, []
|
return fehler, []
|
||||||
@@ -1564,7 +1564,7 @@ async def style_element(element: dict, provider: str = DEFAULT_PROVIDER) -> list
|
|||||||
try:
|
try:
|
||||||
prompt = _prompt("Element-Stil", topic=element["topic"], element_json=_element_json(element))
|
prompt = _prompt("Element-Stil", topic=element["topic"], element_json=_element_json(element))
|
||||||
returncode, stdout, _ = await run_agent(
|
returncode, stdout, _ = await run_agent(
|
||||||
"element-stil-" + str(uuid.uuid4()), prompt, 240, provider=provider, role="fast", capabilities="none"
|
"element-stil-" + str(uuid.uuid4()), prompt, 240, provider=provider, role="fast", capabilities="none", lane="interactive"
|
||||||
)
|
)
|
||||||
if returncode != 0:
|
if returncode != 0:
|
||||||
return None
|
return None
|
||||||
@@ -1587,7 +1587,7 @@ async def refine_suggestion(element: dict, suggestion: dict, instruction: str, p
|
|||||||
instruction=instruction,
|
instruction=instruction,
|
||||||
)
|
)
|
||||||
returncode, stdout, _ = await run_agent(
|
returncode, stdout, _ = await run_agent(
|
||||||
"element-refine-" + str(uuid.uuid4()), prompt, 240, provider=provider, role="fast", capabilities="none"
|
"element-refine-" + str(uuid.uuid4()), prompt, 240, provider=provider, role="fast", capabilities="none", lane="interactive"
|
||||||
)
|
)
|
||||||
if returncode != 0:
|
if returncode != 0:
|
||||||
return None
|
return None
|
||||||
|
|||||||
Reference in New Issue
Block a user