87 lines
4.6 KiB
Python
87 lines
4.6 KiB
Python
"""Erneut beantragte Vorschläge (→ Konzept 4.5): ohne Modell, nur Snapshot neu bauen,
|
|
Freigabe abwarten, ausführen und das Ergebnis in den Chat schreiben."""
|
|
|
|
from sqlalchemy import select
|
|
|
|
from app.approvals import service as approvals
|
|
from app.approvals.models import ApprovalRequest
|
|
from app.core.crypto import Vault
|
|
from app.core.errors import Invalid
|
|
from app.data_sources.connectors.git_repo import RepoCache
|
|
from app.data_sources.models import DataSource
|
|
from app.mcp import service as mcp
|
|
from app.tasks import states
|
|
from app.tasks.models import TaskRun
|
|
from app.tasks.worker import RunContext, RunStopped, handler
|
|
from app.tools import service as tools
|
|
from app.tools.context import ToolContext
|
|
from app.tools.models import DENY
|
|
from app.tools.registry import ToolError
|
|
from app.workspaces.models import Workspace
|
|
|
|
TITLE = "**Erneut beantragte Änderung:** "
|
|
|
|
|
|
@handler("proposal")
|
|
def proposal(context: RunContext) -> str | None:
|
|
with context.db.session() as session:
|
|
run = session.get(TaskRun, context.run_id)
|
|
state = dict(run.state or {})
|
|
old = session.get(ApprovalRequest, context.input["replaces"])
|
|
workspace = session.get(Workspace, context.workspace_id)
|
|
sources = list(session.scalars(select(DataSource).where(DataSource.workspace_id == workspace.id,
|
|
DataSource.status == "ok")))
|
|
verdict = tools.evaluate(session, workspace, old.tool, old.source_id)
|
|
tool = tools.catalog(session).get(old.tool)
|
|
mcp_targets = mcp.targets(session, Vault(context.settings.secret_key), context.settings, workspace.id)
|
|
for source in sources:
|
|
session.expunge(source)
|
|
arguments, source_id, tool_name, old_id = dict(old.arguments), old.source_id, old.tool, old.id
|
|
tool_context = ToolContext(settings=context.settings, vault=Vault(context.settings.secret_key),
|
|
workspace_id=context.workspace_id, sources=sources, cache=RepoCache(context.settings),
|
|
run_id=context.run_id, user_id=context.created_by, mcp=mcp_targets,
|
|
db=context.db, chat_session_id=context.chat_session_id)
|
|
|
|
if "approval_id" in state:
|
|
try:
|
|
output, _ = approvals.carry_out(context.db, tool_context, state["approval_id"], for_model=False)
|
|
except approvals.Unclear as e:
|
|
raise RunStopped(states.NEEDS_RECONCILIATION, "Ob die Änderung im Zielsystem angekommen ist, ist "
|
|
"unklar. Ein Workspace-Admin muss den Abgleich starten.", detail=str(e)) from e
|
|
return complete(context, TITLE + output)
|
|
|
|
if tool is None or verdict.decision == DENY:
|
|
return complete(context, TITLE + "nicht möglich, das Tool ist in diesem Workspace nicht mehr erlaubt.")
|
|
try:
|
|
# Quelle kann umbenannt worden sein: maßgeblich ist die ID aus dem alten Antrag
|
|
if source_id is not None:
|
|
arguments["source"] = tool_context.by_id(source_id).name
|
|
built = tool.propose(tool_context, tool.validate(arguments))
|
|
except (ToolError, Invalid, ValueError) as e:
|
|
return complete(context, TITLE + f"lässt sich gegen den aktuellen Stand nicht neu bauen: {e}")
|
|
with context.db.session() as session:
|
|
context.guard(session)
|
|
approval = approvals.propose(session, run_id=context.run_id, workspace=session.get(Workspace, context.workspace_id),
|
|
user_id=context.created_by, call_id=f"rerequest-{old_id}", tool=tool_name,
|
|
proposal=built, verdict=verdict, hours=context.settings.approval_hours,
|
|
replaces_id=old_id)
|
|
session.get(TaskRun, context.run_id).state = {"approval_id": approval.id}
|
|
approval_id, pending = approval.id, approval.status == approvals.PENDING
|
|
session.flush()
|
|
context.events.emit("approval", {"call_id": approval.call_id, "approval_id": approval_id}, session=session)
|
|
if not pending:
|
|
output, _ = approvals.carry_out(context.db, tool_context, approval_id, for_model=False)
|
|
return complete(context, TITLE + output)
|
|
return states.WAITING_APPROVAL
|
|
|
|
|
|
def complete(context: RunContext, text: str) -> None:
|
|
context.events.flush()
|
|
with context.db.session() as session:
|
|
context.guard(session)
|
|
run = session.get(TaskRun, context.run_id)
|
|
run.state = {}
|
|
message = approvals.post_message(session, run, text)
|
|
if message is not None:
|
|
context.events.emit("message", {"message_id": message.id}, session=session)
|
|
return None
|