sync: UI animations, select styling, TLS verify flag via proxy second line, brand spacing

This commit is contained in:
2025-09-14 14:01:25 +03:00
parent 338e65624f
commit 563663f9f1
9 changed files with 255 additions and 48 deletions

View File

@@ -11,6 +11,7 @@ from agentui.pipeline.executor import PipelineExecutor
from agentui.pipeline.defaults import default_pipeline
from agentui.pipeline.storage import load_pipeline, save_pipeline, list_presets, load_preset, save_preset, load_var_store
from agentui.common.vendors import detect_vendor
from agentui.common.cancel import request_cancel, clear_cancel, is_cancelled
class UnifiedParams(BaseModel):
@@ -677,6 +678,36 @@ def create_app() -> FastAPI:
raise HTTPException(status_code=400, detail="Invalid pipeline format")
save_preset(name, payload)
return JSONResponse({"ok": True})
# --- Manual cancel/clear for pipeline execution ---
@app.post("/admin/cancel")
async def admin_cancel() -> JSONResponse:
try:
p = load_pipeline()
pid = p.get("id", "pipeline_editor")
except Exception:
p = default_pipeline()
pid = p.get("id", "pipeline_editor")
try:
request_cancel(pid)
except Exception:
pass
return JSONResponse({"ok": True, "pipeline_id": pid, "cancelled": True})
@app.post("/admin/cancel/clear")
async def admin_cancel_clear() -> JSONResponse:
try:
p = load_pipeline()
pid = p.get("id", "pipeline_editor")
except Exception:
p = default_pipeline()
pid = p.get("id", "pipeline_editor")
try:
clear_cancel(pid)
except Exception:
pass
return JSONResponse({"ok": True, "pipeline_id": pid, "cancelled": False})
# --- SSE endpoint for live pipeline trace ---
@app.get("/admin/trace/stream")
async def sse_trace() -> StreamingResponse:

30
agentui/common/cancel.py Normal file
View File

@@ -0,0 +1,30 @@
from __future__ import annotations
from typing import Dict
import threading
# Simple in-process cancel flags storage (per pipeline_id)
# Thread-safe for FastAPI workers in same process
_cancel_flags: Dict[str, bool] = {}
_lock = threading.Lock()
def request_cancel(pipeline_id: str) -> None:
"""Set cancel flag for given pipeline id."""
pid = str(pipeline_id or "pipeline_editor")
with _lock:
_cancel_flags[pid] = True
def clear_cancel(pipeline_id: str) -> None:
"""Clear cancel flag for given pipeline id."""
pid = str(pipeline_id or "pipeline_editor")
with _lock:
_cancel_flags.pop(pid, None)
def is_cancelled(pipeline_id: str) -> bool:
"""Check cancel flag for given pipeline id."""
pid = str(pipeline_id or "pipeline_editor")
with _lock:
return bool(_cancel_flags.get(pid, False))

View File

@@ -25,6 +25,7 @@ from agentui.pipeline.templating import (
eval_condition_expr,
)
from agentui.pipeline.storage import load_var_store, save_var_store, clear_var_store
from agentui.common.cancel import is_cancelled, clear_cancel
# --- Templating helpers are imported from agentui.pipeline.templating ---
@@ -186,6 +187,12 @@ class PipelineExecutor:
except Exception:
self._store = {}
# Перед стартом прогона сбрасываем возможный «старый» флаг отмены
try:
clear_cancel(self.pipeline_id)
except Exception:
pass
mode = (self.loop_mode or "dag").lower()
if mode == "iterative":
res = await self._run_iterative(context, trace)
@@ -388,6 +395,36 @@ class PipelineExecutor:
wave_idx = 0
while ready:
# Ручная отмена исполнения (DAG)
try:
if is_cancelled(self.pipeline_id):
if trace is not None:
try:
await trace({
"event": "cancelled",
"node_id": "",
"node_type": "Pipeline",
"wave": wave_idx,
"ts": int(time.time() * 1000),
})
except Exception:
pass
# Снимок и финальный EXEC TRACE
try:
self._commit_snapshot(context, values, last_node_id or "")
except Exception:
pass
try:
summary = " -> ".join(self._exec_log) if getattr(self, "_exec_log", None) else ""
if summary and isinstance(self._store.get("snapshot"), dict):
self._store["snapshot"]["EXEC_TRACE"] = summary
save_var_store(self.pipeline_id, self._store)
except Exception:
pass
return last_result
except Exception:
pass
wave_nodes = list(ready)
ready = []
wave_results: Dict[str, Dict[str, Any]] = {}
@@ -745,6 +782,36 @@ class PipelineExecutor:
# Главный цикл
while q:
# Ручная отмена исполнения (iterative)
try:
if is_cancelled(self.pipeline_id):
if trace is not None:
try:
await trace({
"event": "cancelled",
"node_id": "",
"node_type": "Pipeline",
"wave": step,
"ts": int(time.time() * 1000),
})
except Exception:
pass
# Снимок и финальный EXEC TRACE
try:
self._commit_snapshot(context, values, last_node_id or "")
except Exception:
pass
try:
summary = " -> ".join(self._exec_log) if getattr(self, "_exec_log", None) else ""
if summary and isinstance(self._store.get("snapshot"), dict):
self._store["snapshot"]["EXEC_TRACE"] = summary
save_var_store(self.pipeline_id, self._store)
except Exception:
pass
return last_result
except Exception:
pass
# Проверяем лимиты
if total_runs >= self.loop_max_iters:
raise ExecutionError(f"Iterative mode exceeded loop_max_iters={self.loop_max_iters}")