import referencing fixes
This commit is contained in:
parent
9be2d8aab5
commit
ce612ffcfc
96 changed files with 249 additions and 217 deletions
2
app.py
2
app.py
|
|
@ -552,7 +552,7 @@ async def lifespan(app: FastAPI):
|
|||
# to finish (up to 120 s keepalive timeout) before the rest of
|
||||
# the shutdown can proceed.
|
||||
try:
|
||||
from modules.serviceCenter.core.serviceStreaming.eventManager import get_event_manager as _getStreamingEM
|
||||
from modules.shared.eventManager import get_event_manager as _getStreamingEM
|
||||
_getStreamingEM().shutdown()
|
||||
except Exception as e:
|
||||
logger.warning(f"Streaming EventManager shutdown failed: {e}")
|
||||
|
|
|
|||
|
|
@ -10,14 +10,16 @@ import importlib
|
|||
import os
|
||||
import time
|
||||
import threading
|
||||
from typing import Dict, List, Optional, Any, Tuple
|
||||
from typing import Dict, List, Optional, Any, Tuple, TYPE_CHECKING
|
||||
from modules.datamodels.datamodelAi import AiModel
|
||||
from modules.datamodels.datamodelRbac import AccessRuleContext
|
||||
from .aicoreBase import BaseConnectorAi
|
||||
from modules.datamodels.datamodelUam import User
|
||||
from modules.security.rbacHelpers import checkResourceAccess
|
||||
from modules.security.rbac import RbacClass
|
||||
from modules.connectors.connectorDbPostgre import DatabaseConnector
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from modules.security.rbac import RbacClass
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# TODO TESTING: Override maxTokens for all models during testing
|
||||
|
|
@ -186,7 +188,7 @@ class ModelRegistry:
|
|||
def getAvailableModels(
|
||||
self,
|
||||
currentUser: Optional[User] = None,
|
||||
rbacInstance: Optional[RbacClass] = None,
|
||||
rbacInstance: Optional["RbacClass"] = None,
|
||||
mandateId: Optional[str] = None,
|
||||
featureInstanceId: Optional[str] = None
|
||||
) -> List[AiModel]:
|
||||
|
|
@ -237,7 +239,7 @@ class ModelRegistry:
|
|||
self,
|
||||
models: List[AiModel],
|
||||
currentUser: User,
|
||||
rbacInstance: RbacClass,
|
||||
rbacInstance: "RbacClass",
|
||||
mandateId: Optional[str] = None,
|
||||
featureInstanceId: Optional[str] = None
|
||||
) -> List[AiModel]:
|
||||
|
|
@ -262,7 +264,7 @@ class ModelRegistry:
|
|||
logger.debug(f"User {currentUser.username} does not have access to model {model.displayName} (connector: {model.connectorType})")
|
||||
return filteredModels
|
||||
|
||||
def getModel(self, displayName: str, currentUser: Optional[User] = None, rbacInstance: Optional[RbacClass] = None) -> Optional[AiModel]:
|
||||
def getModel(self, displayName: str, currentUser: Optional[User] = None, rbacInstance: Optional["RbacClass"] = None) -> Optional[AiModel]:
|
||||
"""Get a specific model by displayName, optionally checking RBAC permissions.
|
||||
|
||||
Args:
|
||||
|
|
@ -284,8 +286,15 @@ class ModelRegistry:
|
|||
connectorResourcePath = f"ai.model.{model.connectorType}"
|
||||
modelResourcePath = f"ai.model.{model.connectorType}.{model.displayName}"
|
||||
|
||||
hasConnectorAccess = checkResourceAccess(rbacInstance, currentUser, connectorResourcePath)
|
||||
hasModelAccess = checkResourceAccess(rbacInstance, currentUser, modelResourcePath)
|
||||
try:
|
||||
connPerms = rbacInstance.getUserPermissions(currentUser, AccessRuleContext.RESOURCE, connectorResourcePath)
|
||||
modelPerms = rbacInstance.getUserPermissions(currentUser, AccessRuleContext.RESOURCE, modelResourcePath)
|
||||
hasConnectorAccess = connPerms.view if connPerms else False
|
||||
hasModelAccess = modelPerms.view if modelPerms else False
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking resource access for {modelResourcePath}: {e}")
|
||||
hasConnectorAccess = False
|
||||
hasModelAccess = False
|
||||
|
||||
if not (hasConnectorAccess or hasModelAccess):
|
||||
logger.warning(f"User {currentUser.username} does not have access to model {displayName}")
|
||||
|
|
|
|||
|
|
@ -549,3 +549,24 @@ PORT_TYPE_CATALOG: Dict[str, PortSchema] = {
|
|||
PRIMITIVE_TYPES: frozenset = frozenset({
|
||||
"str", "int", "bool", "float", "Any", "Dict", "List",
|
||||
})
|
||||
|
||||
|
||||
def stripContainer(typeStr: str) -> List[str]:
|
||||
"""
|
||||
Extract referenced type names from a PortField.type string.
|
||||
|
||||
Examples:
|
||||
"str" -> ["str"]
|
||||
"List[Document]" -> ["Document"]
|
||||
"Dict[str,Any]" -> ["str", "Any"]
|
||||
"ConnectionRef" -> ["ConnectionRef"]
|
||||
"List[ProcessError]" -> ["ProcessError"]
|
||||
"""
|
||||
s = (typeStr or "").strip()
|
||||
if not s:
|
||||
return []
|
||||
if "[" in s and s.endswith("]"):
|
||||
inner = s[s.index("[") + 1 : -1]
|
||||
parts = [p.strip() for p in inner.split(",") if p.strip()]
|
||||
return parts or [s]
|
||||
return [s]
|
||||
|
|
|
|||
|
|
@ -561,7 +561,7 @@ class PwgDemo2026(BaseDemoConfig):
|
|||
summary["errors"].append(f"WorkflowAutomation DB connection failed: {exc}")
|
||||
return
|
||||
|
||||
from modules.workflowAutomation.editor._workflowFileSchema import (
|
||||
from modules.nodeCatalog._workflowFileSchema import (
|
||||
envelopeToWorkflowData,
|
||||
validateFileEnvelope,
|
||||
)
|
||||
|
|
|
|||
3
modules/features/redmine/workflows/__init__.py
Normal file
3
modules/features/redmine/workflows/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Copyright (c) 2025 Patrick Motsch
|
||||
# All rights reserved.
|
||||
"""Feature-owned workflow methods for Redmine."""
|
||||
|
|
@ -27,7 +27,7 @@ from modules.interfaces import interfaceDbChat, interfaceDbManagement
|
|||
from modules.features.workspace import interfaceFeatureWorkspace
|
||||
from modules.interfaces.interfaceDbKnowledge import getInterface as getKnowledgeInterface
|
||||
from modules.interfaces.interfaceAiObjects import AiObjects
|
||||
from modules.serviceCenter.core.serviceStreaming import get_event_manager
|
||||
from modules.shared.eventManager import get_event_manager
|
||||
from modules.serviceCenter.services.serviceAgent.datamodelAgent import AgentEventTypeEnum, PendingFileEdit
|
||||
from modules.shared.timeUtils import parseTimestamp
|
||||
from modules.shared.i18nRegistry import apiRouteContext, resolveText
|
||||
|
|
|
|||
|
|
@ -692,7 +692,7 @@ class WorkflowAutomationObjects:
|
|||
envelope) and can be JSON-serialized as-is. Returns ``None`` if the
|
||||
workflow does not exist for this mandate.
|
||||
"""
|
||||
from modules.workflowAutomation.editor._workflowFileSchema import buildFileFromWorkflow
|
||||
from modules.nodeCatalog._workflowFileSchema import buildFileFromWorkflow
|
||||
|
||||
wf = self.getWorkflow(workflowId)
|
||||
if not wf:
|
||||
|
|
@ -711,11 +711,11 @@ class WorkflowAutomationObjects:
|
|||
``existingWorkflowId`` is given. Imports are always saved with
|
||||
``active=False`` so operators can review before scheduling.
|
||||
"""
|
||||
from modules.workflowAutomation.editor._workflowFileSchema import (
|
||||
from modules.nodeCatalog._workflowFileSchema import (
|
||||
envelopeToWorkflowData,
|
||||
validateFileEnvelope,
|
||||
)
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
|
||||
knownTypes = [n.get("id") for n in STATIC_NODE_TYPES if isinstance(n, dict) and n.get("id")]
|
||||
normalizedEnvelope, warnings = validateFileEnvelope(envelope, knownNodeTypes=knownTypes)
|
||||
|
|
|
|||
8
modules/nodeCatalog/__init__.py
Normal file
8
modules/nodeCatalog/__init__.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2025 Patrick Motsch
|
||||
"""
|
||||
nodeCatalog (L2) — neutraler Node-Definitions-Container.
|
||||
|
||||
Statische Node-Schemas, Port-Typen, Node-Adapter und das Workflow-File-Schema.
|
||||
Haengt NUR von shared (L0) + datamodels (L1) ab. Wird von workflowAutomation,
|
||||
serviceCenter, interfaces, system, routes und demoConfigs importiert.
|
||||
"""
|
||||
|
|
@ -3,10 +3,10 @@
|
|||
|
||||
from modules.shared.i18nRegistry import t
|
||||
|
||||
from modules.workflowAutomation.editor.nodeDefinitions.contextPickerHelp import (
|
||||
from modules.nodeCatalog.nodeDefinitions.contextPickerHelp import (
|
||||
CONTEXT_BUILDER_PARAM_DESCRIPTION,
|
||||
)
|
||||
from modules.workflowAutomation.editor.nodeDefinitions.flow import (
|
||||
from modules.nodeCatalog.nodeDefinitions.flow import (
|
||||
CONTEXT_ENVELOPE_DATA_PICK_OPTIONS,
|
||||
)
|
||||
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
from modules.shared.i18nRegistry import t
|
||||
|
||||
from modules.workflowAutomation.editor.nodeDefinitions.ai import ACTION_RESULT_DATA_PICK_OPTIONS
|
||||
from modules.nodeCatalog.nodeDefinitions.ai import ACTION_RESULT_DATA_PICK_OPTIONS
|
||||
|
||||
TASK_LIST_DATA_PICK_OPTIONS = [
|
||||
{
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
from modules.shared.i18nRegistry import t
|
||||
|
||||
from modules.workflowAutomation.editor.nodeDefinitions.flow import (
|
||||
from modules.nodeCatalog.nodeDefinitions.flow import (
|
||||
CONTEXT_ENVELOPE_DATA_PICK_OPTIONS,
|
||||
CONTEXT_MERGE_ACTION_RESULT_DATA_PICK_OPTIONS,
|
||||
)
|
||||
|
|
@ -245,7 +245,7 @@ CONTEXT_NODES = [
|
|||
"description": t(
|
||||
"Filtert fuer die Presentation-Schicht nach typeGroup/MIME "
|
||||
"(gilt fuer alle Dokumenttypen analog, nicht nur PDF). "
|
||||
"Passt zum Inhaltsfilter „Alles"; „Text & Tabellen" blendet Bild-Parts in der Presentation aus."
|
||||
"Passt zum Inhaltsfilter „Alles“; „Text & Tabellen“ blendet Bild-Parts in der Presentation aus."
|
||||
),
|
||||
},
|
||||
{
|
||||
|
|
@ -4,14 +4,14 @@
|
|||
from modules.shared.i18nRegistry import t
|
||||
|
||||
CONTEXT_BUILDER_PARAM_DESCRIPTION = t(
|
||||
"Inhalt aus vorherigen Schritten wählen (DataRef / Daten-Picker): z. B. „response" für Klartext, "
|
||||
"Inhalt aus vorherigen Schritten wählen (DataRef / Daten-Picker): z. B. „response“ für Klartext, "
|
||||
"Handover-Pfade für strukturiertes JSON oder Medienlisten. "
|
||||
"Die Auflösung erfolgt vollständig serverseitig (`resolveParameterReferences`). "
|
||||
"Formular-Schritte speichern Antworten unter „payload" — fehlt ein gewählter Pfad am Root, "
|
||||
"wird derselbe Pfad automatisch unter „payload" nachgeschlagen (Kompatibilität mit älteren "
|
||||
"Formular-Schritte speichern Antworten unter „payload“ — fehlt ein gewählter Pfad am Root, "
|
||||
"wird derselbe Pfad automatisch unter „payload“ nachgeschlagen (Kompatibilität mit älteren "
|
||||
"und neuen Picker-Pfaden). "
|
||||
"In Freitext-/Template-Feldern werden weiterhin Platzhalter `{{KnotenId.feld.b.z.}}` ersetzt "
|
||||
"(gleiche Semantik inkl. optionalem Nachschlagen unter „payload")."
|
||||
"(gleiche Semantik inkl. optionalem Nachschlagen unter „payload“)."
|
||||
)
|
||||
|
||||
# Kurzreferenz für Node-Beschreibungen (optional einbinden): dieselbe Auflösungslogik
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
from modules.shared.i18nRegistry import t
|
||||
|
||||
from modules.workflowAutomation.editor.nodeDefinitions.ai import CONSOLIDATE_RESULT_DATA_PICK_OPTIONS
|
||||
from modules.nodeCatalog.nodeDefinitions.ai import CONSOLIDATE_RESULT_DATA_PICK_OPTIONS
|
||||
|
||||
AGGREGATE_RESULT_DATA_PICK_OPTIONS = [
|
||||
{
|
||||
|
|
@ -3,10 +3,10 @@
|
|||
|
||||
from modules.shared.i18nRegistry import t
|
||||
|
||||
from modules.workflowAutomation.editor.nodeDefinitions.contextPickerHelp import (
|
||||
from modules.nodeCatalog.nodeDefinitions.contextPickerHelp import (
|
||||
CONTEXT_BUILDER_PARAM_DESCRIPTION,
|
||||
)
|
||||
from modules.workflowAutomation.editor.nodeDefinitions.ai import ACTION_RESULT_DATA_PICK_OPTIONS
|
||||
from modules.nodeCatalog.nodeDefinitions.ai import ACTION_RESULT_DATA_PICK_OPTIONS
|
||||
|
||||
EMAIL_LIST_DATA_PICK_OPTIONS = [
|
||||
{
|
||||
|
|
@ -3,10 +3,10 @@
|
|||
|
||||
from modules.shared.i18nRegistry import t
|
||||
|
||||
from modules.workflowAutomation.editor.nodeDefinitions.contextPickerHelp import (
|
||||
from modules.nodeCatalog.nodeDefinitions.contextPickerHelp import (
|
||||
CONTEXT_BUILDER_PARAM_DESCRIPTION,
|
||||
)
|
||||
from modules.workflowAutomation.editor.nodeDefinitions.ai import DOCUMENT_LIST_DATA_PICK_OPTIONS
|
||||
from modules.nodeCatalog.nodeDefinitions.ai import DOCUMENT_LIST_DATA_PICK_OPTIONS
|
||||
|
||||
FILE_NODES = [
|
||||
{
|
||||
|
|
@ -14,7 +14,7 @@ FILE_NODES = [
|
|||
"category": "file",
|
||||
"label": t("Datei erstellen"),
|
||||
"description": t(
|
||||
"Erstellt eine Datei aus der Presentation von „Inhalt extrahieren" "
|
||||
"Erstellt eine Datei aus der Presentation von „Inhalt extrahieren“ "
|
||||
"(``data`` oder Schleifen-``bodyResults``). Ausgabe über den Generation-Service."
|
||||
),
|
||||
"parameters": [
|
||||
|
|
@ -96,7 +96,7 @@ MERGE_RESULT_DATA_PICK_OPTIONS = [
|
|||
{
|
||||
"path": ["first"],
|
||||
"pickerLabel": t("Erster Zweig"),
|
||||
"detail": t("Daten vom ersten verbundenen Eingang (Modus „first")."),
|
||||
"detail": t("Daten vom ersten verbundenen Eingang (Modus „first“)."),
|
||||
"recommended": False,
|
||||
"type": "Any",
|
||||
},
|
||||
|
|
@ -243,9 +243,9 @@ FLOW_NODES = [
|
|||
"category": "flow",
|
||||
"label": t("Schleife / Für jedes"),
|
||||
"description": t(
|
||||
"Zwei Ausgänge: „Schleife" verbindet den Rumpf (pro Element); optional führt der Rumpf "
|
||||
"Zwei Ausgänge: „Schleife“ verbindet den Rumpf (pro Element); optional führt der Rumpf "
|
||||
"mit einem Rücklauf-Pfeil wieder zum **gleichen Eingang** wie der vorherige Schritt (wie in n8n). "
|
||||
"„Fertig" führt genau einmal fort, wenn alle Iterationen beendet sind. "
|
||||
"„Fertig“ führt genau einmal fort, wenn alle Iterationen beendet sind. "
|
||||
"Die zu durchlaufende Liste wählen Sie wie bisher; UDM-/Strukturdaten werden automatisch sinnvoll in Elemente aufgelöst."
|
||||
),
|
||||
"parameters": [
|
||||
|
|
@ -266,7 +266,7 @@ FLOW_NODES = [
|
|||
},
|
||||
"description": t(
|
||||
"Welche Elemente die Schleife besucht: alle, nur das erste/letzte, jedes zweite/dritte "
|
||||
"oder jedes n-te (Schritt dann unter „Schrittweite")."
|
||||
"oder jedes n-te (Schritt dann unter „Schrittweite“)."
|
||||
),
|
||||
"default": "all",
|
||||
},
|
||||
|
|
@ -276,7 +276,7 @@ FLOW_NODES = [
|
|||
"required": False,
|
||||
"frontendType": "number",
|
||||
"frontendOptions": {"min": 2, "max": 100},
|
||||
"description": t("Nur bei „jedes n-te": Schrittweite (z. B. 5 = jedes 5. Element ab Index 0)."),
|
||||
"description": t("Nur bei „jedes n-te“: Schrittweite (z. B. 5 = jedes 5. Element ab Index 0)."),
|
||||
"default": 2,
|
||||
},
|
||||
{
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
from modules.shared.i18nRegistry import t
|
||||
|
||||
from modules.workflowAutomation.editor.nodeDefinitions.ai import DOCUMENT_LIST_DATA_PICK_OPTIONS
|
||||
from modules.nodeCatalog.nodeDefinitions.ai import DOCUMENT_LIST_DATA_PICK_OPTIONS
|
||||
|
||||
BOOL_RESULT_DATA_PICK_OPTIONS = [
|
||||
{
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
from modules.shared.i18nRegistry import t
|
||||
|
||||
from modules.workflowAutomation.editor.nodeDefinitions.ai import ACTION_RESULT_DATA_PICK_OPTIONS
|
||||
from modules.nodeCatalog.nodeDefinitions.ai import ACTION_RESULT_DATA_PICK_OPTIONS
|
||||
|
||||
# Typed FeatureInstance binding (replaces legacy `string, hidden`).
|
||||
# - type FeatureInstanceRef[redmine] is filtered by the DataPicker.
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
from modules.shared.i18nRegistry import t
|
||||
|
||||
from modules.workflowAutomation.editor.nodeDefinitions.ai import (
|
||||
from modules.nodeCatalog.nodeDefinitions.ai import (
|
||||
ACTION_RESULT_DATA_PICK_OPTIONS,
|
||||
DOCUMENT_LIST_DATA_PICK_OPTIONS,
|
||||
)
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
from modules.shared.i18nRegistry import t
|
||||
|
||||
from modules.workflowAutomation.editor.nodeDefinitions.ai import ACTION_RESULT_DATA_PICK_OPTIONS
|
||||
from modules.nodeCatalog.nodeDefinitions.ai import ACTION_RESULT_DATA_PICK_OPTIONS
|
||||
|
||||
TRIGGER_NODES = [
|
||||
{
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
from modules.shared.i18nRegistry import t
|
||||
|
||||
from modules.workflowAutomation.editor.nodeDefinitions.ai import ACTION_RESULT_DATA_PICK_OPTIONS
|
||||
from modules.nodeCatalog.nodeDefinitions.ai import ACTION_RESULT_DATA_PICK_OPTIONS
|
||||
|
||||
# Typed FeatureInstance binding (replaces legacy `string, hidden`).
|
||||
# - type uses the discriminator notation `FeatureInstanceRef[<code>]` so the
|
||||
|
|
@ -418,7 +418,7 @@ def deriveFormPayloadSchemaFromParam(
|
|||
- Group-fields: ``type == "group"`` recursed via ``fields``.
|
||||
- List[str]: each string is taken as a leaf path key (used for ``filterContext.keys``).
|
||||
"""
|
||||
from modules.workflowAutomation.editor.nodeDefinitions.input import FORM_FIELD_TYPES
|
||||
from modules.nodeCatalog.nodeDefinitions.input import FORM_FIELD_TYPES
|
||||
_FORM_TYPE_TO_PORT: Dict[str, str] = {f["id"]: f["portType"] for f in FORM_FIELD_TYPES}
|
||||
|
||||
fields_param = (node.get("parameters") or {}).get(param_key)
|
||||
|
|
@ -12,22 +12,32 @@ RBAC model:
|
|||
- isPlatformAdmin bypasses all checks
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import math
|
||||
import time
|
||||
import uuid
|
||||
from typing import Optional, List, Dict, Any
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException, Path, Query, Request
|
||||
from fastapi.responses import JSONResponse, Response, StreamingResponse
|
||||
from slowapi import Limiter
|
||||
from slowapi.util import get_remote_address
|
||||
|
||||
from modules.auth.authentication import getRequestContext, RequestContext
|
||||
from modules.connectors.connectorDbPostgre import DatabaseConnector
|
||||
from modules.datamodels.datamodelPagination import PaginationParams, PaginationMetadata, normalize_pagination_dict
|
||||
from modules.datamodels.datamodelWorkflowAutomation import (
|
||||
AutoWorkflow, AutoVersion, AutoRun, AutoStepLog, AutoTask,
|
||||
)
|
||||
from modules.dbHelpers.paginationHelpers import applyFiltersAndSort
|
||||
from modules.interfaces.interfaceDbApp import getRootInterface
|
||||
from modules.shared.i18nRegistry import apiRouteContext, resolveText
|
||||
from modules.shared.workflowAutomationHelpers import (
|
||||
from modules.workflowAutomation.helpers import (
|
||||
_getWorkflowAutomationDb,
|
||||
_getUserMandateIds,
|
||||
_isUserMandateAdmin,
|
||||
_validateWorkflowAccess,
|
||||
_scopedWorkflowFilter,
|
||||
_scopedRunFilter,
|
||||
|
|
@ -736,7 +746,7 @@ def _importWorkflow(
|
|||
if not context.user:
|
||||
raise HTTPException(status_code=401, detail=routeApiMsg("Authentication required"))
|
||||
|
||||
from modules.workflowAutomation.editor._workflowFileSchema import WorkflowFileSchemaError
|
||||
from modules.nodeCatalog._workflowFileSchema import WorkflowFileSchemaError
|
||||
|
||||
mandateId = body.get("mandateId") if isinstance(body, dict) else None
|
||||
userId = str(context.user.id)
|
||||
|
|
@ -799,7 +809,7 @@ def _exportWorkflow(
|
|||
if not context.user:
|
||||
raise HTTPException(status_code=401, detail=routeApiMsg("Authentication required"))
|
||||
|
||||
from modules.workflowAutomation.editor._workflowFileSchema import buildFileName
|
||||
from modules.nodeCatalog._workflowFileSchema import buildFileName
|
||||
|
||||
db = _getWorkflowAutomationDb()
|
||||
try:
|
||||
|
|
@ -909,7 +919,7 @@ def _getFeatureInstanceOptions(
|
|||
targetMandateIds = userMandateIds if not context.isPlatformAdmin else []
|
||||
if context.isPlatformAdmin:
|
||||
try:
|
||||
from modules.datamodels.datamodelMandate import Mandate
|
||||
from modules.datamodels.datamodelUam import Mandate
|
||||
mandates = rootInterface.db.getRecordset(Mandate) or []
|
||||
targetMandateIds = [str(m.get("id") if isinstance(m, dict) else getattr(m, "id", "")) for m in mandates]
|
||||
except Exception:
|
||||
|
|
@ -1038,7 +1048,7 @@ async def _getRunStream(
|
|||
else:
|
||||
raise HTTPException(status_code=403, detail=routeApiMsg("Access denied"))
|
||||
|
||||
from modules.serviceCenter.core.serviceStreaming.eventManager import get_event_manager
|
||||
from modules.shared.eventManager import get_event_manager
|
||||
sseEventManager = get_event_manager()
|
||||
queueId = f"run-trace-{runId}"
|
||||
sseEventManager.create_queue(queueId)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
# All rights reserved.
|
||||
"""Streaming core service for SSE event management."""
|
||||
|
||||
from .eventManager import EventManager, get_event_manager
|
||||
from .mainServiceStreaming import StreamingService
|
||||
|
||||
__all__ = ["EventManager", "get_event_manager", "StreamingService"]
|
||||
__all__ = ["StreamingService"]
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
# Copyright (c) 2025 Patrick Motsch
|
||||
# All rights reserved.
|
||||
"""Re-export shim — canonical source is modules.shared.eventManager."""
|
||||
|
||||
from modules.shared.eventManager import ( # noqa: F401
|
||||
EventManager,
|
||||
get_event_manager,
|
||||
)
|
||||
|
|
@ -8,7 +8,7 @@ Core service - not requested by features directly.
|
|||
import logging
|
||||
from typing import Any, Callable
|
||||
|
||||
from modules.serviceCenter.core.serviceStreaming.eventManager import EventManager, get_event_manager
|
||||
from modules.shared.eventManager import EventManager, get_event_manager
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
# Copyright (c) 2025 Patrick Motsch
|
||||
# All rights reserved.
|
||||
"""
|
||||
External agent-tool provider registry.
|
||||
|
||||
Allows higher-layer components (e.g. ``workflowAutomation``) to register their
|
||||
agent tool definitions WITHOUT the agent service importing them. The agent
|
||||
reads registered definitions by toolbox id during toolbox activation.
|
||||
|
||||
This inverts the dependency: ``workflowAutomation -> serviceCenter`` (push at
|
||||
boot), instead of ``serviceCenter -> workflowAutomation`` (pull). The agent
|
||||
never imports workflowAutomation.
|
||||
|
||||
Tool definition dicts are the same shape consumed by
|
||||
``ToolRegistry.registerFromDefinition`` (``name``, ``description``,
|
||||
``parameters``, optional ``handler`` / ``readOnly`` / ``toolSet`` ...).
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Dict, List, Any
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_externalTools: Dict[str, List[Dict[str, Any]]] = {}
|
||||
|
||||
|
||||
def registerExternalTools(toolboxId: str, toolDefinitions: List[Dict[str, Any]]) -> None:
|
||||
"""Register agent tool definitions (with handlers) for a toolbox id."""
|
||||
_externalTools[toolboxId] = list(toolDefinitions or [])
|
||||
logger.info(
|
||||
"Registered %d external agent tool(s) for toolbox '%s'",
|
||||
len(_externalTools[toolboxId]),
|
||||
toolboxId,
|
||||
)
|
||||
|
||||
|
||||
def getExternalTools(toolboxId: str) -> List[Dict[str, Any]]:
|
||||
"""Return registered tool definitions for a toolbox id (empty if none)."""
|
||||
return _externalTools.get(toolboxId, [])
|
||||
|
|
@ -420,20 +420,21 @@ class AgentService:
|
|||
for tb in activeToolboxes:
|
||||
activeToolNames.update(tb.tools)
|
||||
|
||||
from modules.serviceCenter.services.serviceAgent.externalToolRegistry import getExternalTools
|
||||
from modules.serviceCenter.services.serviceAgent.datamodelAgent import ToolDefinition
|
||||
for tb in activeToolboxes:
|
||||
if tb.id == "workflow":
|
||||
try:
|
||||
from modules.serviceCenter.services.serviceAgent.workflowTools import getWorkflowToolDefinitions
|
||||
from modules.serviceCenter.services.serviceAgent.datamodelAgent import ToolDefinition
|
||||
wfDefs = getWorkflowToolDefinitions()
|
||||
for rawDef in wfDefs:
|
||||
handler = rawDef.get("handler")
|
||||
defFields = {k: v for k, v in rawDef.items() if k != "handler"}
|
||||
toolDef = ToolDefinition(**defFields)
|
||||
registry.registerFromDefinition(toolDef, handler)
|
||||
logger.info("Registered %d workflow tools from toolbox", len(wfDefs))
|
||||
except Exception as e:
|
||||
logger.warning("Could not register workflow tools: %s", e)
|
||||
extDefs = getExternalTools(tb.id)
|
||||
if not extDefs:
|
||||
continue
|
||||
try:
|
||||
for rawDef in extDefs:
|
||||
handler = rawDef.get("handler")
|
||||
defFields = {k: v for k, v in rawDef.items() if k != "handler"}
|
||||
toolDef = ToolDefinition(**defFields)
|
||||
registry.registerFromDefinition(toolDef, handler)
|
||||
logger.info("Registered %d external tool(s) for toolbox '%s'", len(extDefs), tb.id)
|
||||
except Exception as e:
|
||||
logger.warning("Could not register external tools for toolbox '%s': %s", tb.id, e)
|
||||
|
||||
inactiveToolNames = set()
|
||||
for tb in tbRegistry.getAllToolboxes():
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
# Re-export shim — canonical source: modules.serviceCenter.serviceHub
|
||||
from modules.serviceCenter.serviceHub import ( # noqa: F401
|
||||
PublicService,
|
||||
ServiceHub,
|
||||
Services,
|
||||
getInterface,
|
||||
)
|
||||
|
|
@ -242,7 +242,7 @@ def _registerNodeLabels():
|
|||
added += 1
|
||||
|
||||
try:
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
for nd in STATIC_NODE_TYPES:
|
||||
_reg(_extractRegistrySourceText(nd.get("label")), "node.label")
|
||||
_reg(_extractRegistrySourceText(nd.get("description")), "node.desc")
|
||||
|
|
@ -265,7 +265,7 @@ def _registerNodeLabels():
|
|||
pass
|
||||
|
||||
try:
|
||||
from modules.workflowAutomation.editor.portTypes import PORT_TYPE_CATALOG
|
||||
from modules.nodeCatalog.portTypes import PORT_TYPE_CATALOG
|
||||
for schema in PORT_TYPE_CATALOG.values():
|
||||
for field in getattr(schema, "fields", []) or []:
|
||||
desc = getattr(field, "description", None)
|
||||
|
|
|
|||
|
|
@ -436,7 +436,7 @@ async def _listAvailableNodeTypes(params: Dict[str, Any], context: Any) -> ToolR
|
|||
"""
|
||||
name = "listAvailableNodeTypes"
|
||||
try:
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
nodeTypes = []
|
||||
for n in STATIC_NODE_TYPES:
|
||||
if not isinstance(n, dict):
|
||||
|
|
@ -462,7 +462,7 @@ async def _describeNodeType(params: Dict[str, Any], context: Any) -> ToolResult:
|
|||
nodeType = params.get("nodeType") or params.get("id")
|
||||
if not nodeType:
|
||||
return _err(name, "nodeType required")
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
target: Dict[str, Any] = {}
|
||||
for n in STATIC_NODE_TYPES:
|
||||
if isinstance(n, dict) and n.get("id") == nodeType:
|
||||
|
|
@ -875,7 +875,7 @@ async def _exportWorkflowToFile(params: Dict[str, Any], context: Any) -> ToolRes
|
|||
envelope = iface.exportWorkflowToDict(workflowId)
|
||||
if envelope is None:
|
||||
return _err(name, f"Workflow {workflowId} not found")
|
||||
from modules.workflowAutomation.editor._workflowFileSchema import buildFileName
|
||||
from modules.nodeCatalog._workflowFileSchema import buildFileName
|
||||
return _ok(name, {
|
||||
"fileName": buildFileName(envelope.get("label", "workflow")),
|
||||
"envelope": envelope,
|
||||
|
|
@ -26,7 +26,7 @@ from __future__ import annotations
|
|||
from dataclasses import dataclass, field
|
||||
from typing import Any, Dict, List, Mapping
|
||||
|
||||
from modules.workflowAutomation.editor.nodeAdapter import (
|
||||
from modules.nodeCatalog.nodeAdapter import (
|
||||
NodeAdapter,
|
||||
_adapterFromLegacyNode,
|
||||
_isMethodBoundNode,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import re
|
|||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.shared.i18nRegistry import resolveText, t
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ import logging
|
|||
from typing import Dict, List, Any, Optional
|
||||
|
||||
from modules.workflowAutomation.editor.conditionOperators import localize_operator_catalog
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.workflowAutomation.editor.nodeDefinitions.input import FORM_FIELD_TYPES
|
||||
from modules.workflowAutomation.editor.nodeAdapter import bindsActionFromLegacy
|
||||
from modules.workflowAutomation.editor.portTypes import PORT_TYPE_CATALOG, SYSTEM_VARIABLES
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.nodeDefinitions.input import FORM_FIELD_TYPES
|
||||
from modules.nodeCatalog.nodeAdapter import bindsActionFromLegacy
|
||||
from modules.nodeCatalog.portTypes import PORT_TYPE_CATALOG, SYSTEM_VARIABLES
|
||||
from modules.shared.i18nRegistry import normalizePrimaryLanguageTag, resolveText
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import copy
|
|||
import re
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from modules.workflowAutomation.editor.portTypes import unwrapTransit
|
||||
from modules.nodeCatalog.portTypes import unwrapTransit
|
||||
|
||||
_CONTEXT_FILTER_OPERATORS = frozenset({"contains_content"})
|
||||
_BLOB_IMAGE_CHUNK_RE = re.compile(r"^\[image(?:\:([^\]]+))?\]$")
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ from __future__ import annotations
|
|||
from typing import Any, Dict, List, Set
|
||||
|
||||
from modules.workflowAutomation.editor.conditionOperators import catalog_type_to_value_kind, resolve_value_kind
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.workflowAutomation.editor.portTypes import PORT_TYPE_CATALOG, PortSchema, parse_graph_defined_output_schema
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.portTypes import PORT_TYPE_CATALOG, PortSchema, parse_graph_defined_output_schema
|
||||
from modules.workflowAutomation.engine.graphUtils import buildConnectionMap, getLoopBodyNodeIds, getLoopDoneNodeIds
|
||||
|
||||
_NODE_BY_TYPE = {n["id"]: n for n in STATIC_NODE_TYPES}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ from modules.workflowAutomation.engine.executors import (
|
|||
PauseForHumanTaskError,
|
||||
PauseForEmailWaitError,
|
||||
)
|
||||
from modules.workflowAutomation.editor.portTypes import normalizeToSchema, wrapTransit, unwrapTransit
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.portTypes import normalizeToSchema, wrapTransit, unwrapTransit
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.datamodels.serviceExceptions import SubscriptionInactiveException as _SubscriptionInactiveException, BillingContextError as _BillingContextError
|
||||
from modules.workflowAutomation.engine.runFileLogger import (
|
||||
RunFileLogger,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import re
|
|||
import time
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from modules.workflowAutomation.editor.portTypes import (
|
||||
from modules.nodeCatalog.portTypes import (
|
||||
_normalizeError,
|
||||
normalizeToSchema,
|
||||
)
|
||||
|
|
@ -35,6 +35,7 @@ def _attach_unified_presentation_data(out: Dict[str, Any], *, node_def: Dict[str
|
|||
"""Ensure ``out[\"data\"]`` carries ``context.extractContent.presentation.v1`` for ``file.create``."""
|
||||
if node_def.get("skipUnifiedPresentation"):
|
||||
return
|
||||
node_type = node_def.get("type") or node_def.get("nodeType")
|
||||
data = out.get("data")
|
||||
if isinstance(data, dict) and data.get("kind") == PRESENTATION_KIND:
|
||||
return
|
||||
|
|
@ -181,7 +182,7 @@ def _isUserConnectionId(val: Any) -> bool:
|
|||
|
||||
def _getNodeDefinition(nodeType: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get node definition by type id."""
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
for node in STATIC_NODE_TYPES:
|
||||
if node.get("id") == nodeType:
|
||||
return node
|
||||
|
|
@ -304,7 +305,7 @@ def _buildConnectionRefDict(connRef: str, chatService, services) -> Optional[Dic
|
|||
|
||||
def _schemaCarriesConnectionProvenance(outputSchema: str) -> bool:
|
||||
"""True iff the port schema declares ``carriesConnectionProvenance`` in the catalog."""
|
||||
from modules.workflowAutomation.editor.portTypes import PORT_TYPE_CATALOG
|
||||
from modules.nodeCatalog.portTypes import PORT_TYPE_CATALOG
|
||||
schema = PORT_TYPE_CATALOG.get(outputSchema)
|
||||
return bool(getattr(schema, "carriesConnectionProvenance", False))
|
||||
|
||||
|
|
@ -430,7 +431,7 @@ def _resolveUpstreamPayload(nodeId: str, context: Dict[str, Any]) -> Any:
|
|||
the first ``connectionMap`` entry so ``injectUpstreamPayload`` (e.g.
|
||||
``context.mergeContext`` after ``flow.loop``) still receives data.
|
||||
"""
|
||||
from modules.workflowAutomation.editor.switchOutput import unwrap_transit_for_port
|
||||
from modules.workflowAutomation.editor.switchOutput import unwrap_transit_for_port
|
||||
|
||||
nodeOutputs = context.get("nodeOutputs") or {}
|
||||
connectionMap = context.get("connectionMap") or {}
|
||||
|
|
@ -456,7 +457,7 @@ def _resolveUpstreamPayload(nodeId: str, context: Dict[str, Any]) -> Any:
|
|||
return unwrap_transit_for_port(upstream, src_out)
|
||||
|
||||
|
||||
def _resolveBranchInputs(nodeId: str, context: Dict[str, Any]) -> Dict[int, Any]:
|
||||
def _resolveBranchInputs(nodeId: str, context: Dict[str, Any]) -> Dict[int, Any]:
|
||||
"""Return ``Dict[port_index → unwrapped upstream output]`` for every wired input port."""
|
||||
from modules.workflowAutomation.editor.switchOutput import unwrap_transit_for_port
|
||||
src_map = (context.get("inputSources") or {}).get(nodeId) or {}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import logging
|
||||
from typing import Any, Dict
|
||||
|
||||
from modules.workflowAutomation.editor.portTypes import unwrapTransit, wrapTransit
|
||||
from modules.nodeCatalog.portTypes import unwrapTransit, wrapTransit
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from datetime import datetime
|
|||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from modules.workflowAutomation.editor.conditionOperators import apply_condition_operator, resolve_value_kind
|
||||
from modules.workflowAutomation.editor.portTypes import wrapTransit, unwrapTransit
|
||||
from modules.nodeCatalog.portTypes import wrapTransit, unwrapTransit
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ def getLoopPrimaryInputSource(
|
|||
) -> Optional[Tuple[str, int]]:
|
||||
"""Pick the inbound edge for ``flow.loop`` when several wires hit the same input (0).
|
||||
|
||||
The Schleifen-Rücklauf vom Rumpf und der „normale" Vorgänger enden auf demselben Port;
|
||||
The Schleifen-Rücklauf vom Rumpf und der „normale“ Vorgänger enden auf demselben Port;
|
||||
für die Datenzusammenführung (Fertig-Ausgang, Logs) zählt der Vorgänger **außerhalb** des Rumpfes.
|
||||
"""
|
||||
incoming = connectionMap.get(loop_node_id, [])
|
||||
|
|
@ -209,7 +209,7 @@ def parse_graph_defined_schema(node: Dict[str, Any], parameter_key: str) -> Opti
|
|||
Build a JSON-serializable port schema dict from graph parameters (e.g. form ``fields``).
|
||||
Used by tooling and future API surfaces; mirrors ``parse_graph_defined_output_schema`` logic.
|
||||
"""
|
||||
from modules.workflowAutomation.editor.portTypes import deriveFormPayloadSchemaFromParam
|
||||
from modules.nodeCatalog.portTypes import deriveFormPayloadSchemaFromParam
|
||||
|
||||
sch = deriveFormPayloadSchemaFromParam(node, parameter_key)
|
||||
if sch is None:
|
||||
|
|
@ -227,8 +227,8 @@ def _checkPortCompatibility(
|
|||
"""
|
||||
Hard typed-port check: incompatible connections become validation errors.
|
||||
"""
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.workflowAutomation.editor.portTypes import resolve_output_schema_name
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.portTypes import resolve_output_schema_name
|
||||
|
||||
nodeDefMap = {n["id"]: n for n in STATIC_NODE_TYPES}
|
||||
nodeById = {n["id"]: n for n in nodes if n.get("id")}
|
||||
|
|
@ -481,7 +481,7 @@ def resolveParameterReferences(
|
|||
)
|
||||
if value.get("type") == "system":
|
||||
variable = value.get("variable", "")
|
||||
from modules.workflowAutomation.editor.portTypes import resolveSystemVariable
|
||||
from modules.nodeCatalog.portTypes import resolveSystemVariable
|
||||
return resolveSystemVariable(variable, nodeOutputs.get("_context", {}))
|
||||
return {
|
||||
k: resolveParameterReferences(
|
||||
|
|
@ -576,7 +576,7 @@ def extract_wired_document_list(inp: Any) -> Optional[Dict[str, Any]]:
|
|||
"""
|
||||
if inp is None:
|
||||
return None
|
||||
from modules.workflowAutomation.editor.portTypes import (
|
||||
from modules.nodeCatalog.portTypes import (
|
||||
unwrapTransit,
|
||||
_coerce_document_list_upload_fields,
|
||||
_file_record_to_document,
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ import copy
|
|||
import logging
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.workflowAutomation.editor.portTypes import (
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.portTypes import (
|
||||
PRIMARY_TEXT_HANDOVER_REF_PATH,
|
||||
resolve_output_schema_name,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -202,8 +202,9 @@ def _validateWorkflowAccess(
|
|||
if action == "execute":
|
||||
targetInstanceId = workflow.get("targetFeatureInstanceId")
|
||||
if targetInstanceId:
|
||||
from modules.interfaces.interfaceFeatureAccess import _hasFeatureAccess
|
||||
if _hasFeatureAccess(userId, targetInstanceId):
|
||||
from modules.interfaces.interfaceDbApp import getRootInterface
|
||||
access = getRootInterface().getFeatureAccess(userId, targetInstanceId)
|
||||
if access and access.get("enabled"):
|
||||
return
|
||||
|
||||
adminMandateIds = _getAdminMandateIds(userId, [wfMandateId])
|
||||
|
|
@ -226,9 +226,24 @@ def _migrateRbacNamespace() -> None:
|
|||
logger.warning(f"RBAC namespace migration failed (non-critical): {e}")
|
||||
|
||||
|
||||
def _registerAgentTools() -> None:
|
||||
"""Push workflow agent tools into the agent's external tool registry.
|
||||
|
||||
Inverts the dependency: workflowAutomation -> serviceCenter (push at boot),
|
||||
so the agent service never imports workflowAutomation to obtain its tools.
|
||||
"""
|
||||
try:
|
||||
from modules.serviceCenter.services.serviceAgent.externalToolRegistry import registerExternalTools
|
||||
from modules.workflowAutomation.agentTools import getWorkflowToolDefinitions, TOOLBOX_ID
|
||||
registerExternalTools(TOOLBOX_ID, getWorkflowToolDefinitions())
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not register workflow agent tools (non-critical): {e}")
|
||||
|
||||
|
||||
def onBootstrap() -> None:
|
||||
"""Seed system workflow templates and sync feature template workflows on boot."""
|
||||
_migrateRbacNamespace()
|
||||
_registerAgentTools()
|
||||
|
||||
from modules.datamodels.datamodelWorkflowAutomation import GRAPHICAL_EDITOR_DATABASE, AutoWorkflow
|
||||
from modules.connectors.connectorDbPostgre import DatabaseConnector
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
# Copyright (c) 2025 Patrick Motsch
|
||||
# Re-export shim: modules moved to modules.workflowAutomation.engine
|
||||
# This file preserves backwards compatibility for existing imports.
|
||||
|
||||
from modules.workflowAutomation.engine.executionEngine import * # noqa: F401,F403
|
||||
from modules.workflowAutomation.engine.graphUtils import * # noqa: F401,F403
|
||||
from modules.workflowAutomation.engine.runEnvelope import * # noqa: F401,F403
|
||||
from modules.workflowAutomation.engine.scheduleCron import * # noqa: F401,F403
|
||||
from modules.workflowAutomation.engine.runFileLogger import * # noqa: F401,F403
|
||||
from modules.workflowAutomation.engine.pickNotPushMigration import * # noqa: F401,F403
|
||||
from modules.workflowAutomation.engine.featureInstanceRefMigration import * # noqa: F401,F403
|
||||
from modules.workflowAutomation.engine.workflowArtifactVisibility import * # noqa: F401,F403
|
||||
from modules.workflowAutomation.engine.clickupTaskUpdateMerge import * # noqa: F401,F403
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
# Copyright (c) 2025 Patrick Motsch
|
||||
# Re-export shim: executors moved to modules.workflowAutomation.engine.executors
|
||||
# This file preserves backwards compatibility for existing imports.
|
||||
|
||||
from modules.workflowAutomation.engine.executors import ( # noqa: F401
|
||||
TriggerExecutor,
|
||||
FlowExecutor,
|
||||
ActionNodeExecutor,
|
||||
InputExecutor,
|
||||
DataExecutor,
|
||||
PauseForHumanTaskError,
|
||||
PauseForEmailWaitError,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"TriggerExecutor",
|
||||
"FlowExecutor",
|
||||
"ActionNodeExecutor",
|
||||
"InputExecutor",
|
||||
"DataExecutor",
|
||||
"PauseForHumanTaskError",
|
||||
"PauseForEmailWaitError",
|
||||
]
|
||||
|
|
@ -25,10 +25,10 @@ from modules.datamodels.datamodelWorkflowActions import (
|
|||
WorkflowActionDefinition,
|
||||
WorkflowActionParameter,
|
||||
)
|
||||
from modules.workflowAutomation.editor.portTypes import (
|
||||
from modules.datamodels.datamodelPortTypes import (
|
||||
PORT_TYPE_CATALOG,
|
||||
PRIMITIVE_TYPES,
|
||||
_stripContainer,
|
||||
stripContainer as _stripContainer,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
|
|||
|
|
@ -240,7 +240,7 @@ class MethodBase:
|
|||
runtime structural validation is handled by the workflow engine /
|
||||
port-schema layer, not at the action-call boundary.
|
||||
"""
|
||||
from modules.workflowAutomation.editor.portTypes import PORT_TYPE_CATALOG
|
||||
from modules.datamodels.datamodelPortTypes import PORT_TYPE_CATALOG
|
||||
|
||||
if expectedType in PORT_TYPE_CATALOG:
|
||||
return value
|
||||
|
|
|
|||
|
|
@ -320,18 +320,15 @@ def _pause_for_human_tasks(
|
|||
)
|
||||
task_id = str((task or {}).get("id") or "")
|
||||
ordered_ids = [n.get("id") for n in (run_context.get("_orderedNodes") or []) if n.get("id")]
|
||||
from modules.workflowAutomation.engine.runFileLogger import merge_persisted_run_context
|
||||
|
||||
_pause_ctx = merge_persisted_run_context(
|
||||
iface,
|
||||
run_id,
|
||||
{
|
||||
"connectionMap": run_context.get("connectionMap"),
|
||||
"inputSources": run_context.get("inputSources"),
|
||||
"orderedNodeIds": ordered_ids,
|
||||
"pauseReason": "contextAssignment",
|
||||
},
|
||||
)
|
||||
prev_ctx = dict((iface.getRun(run_id) or {}).get("context") or {})
|
||||
_pause_ctx = {
|
||||
**prev_ctx,
|
||||
"connectionMap": run_context.get("connectionMap"),
|
||||
"inputSources": run_context.get("inputSources"),
|
||||
"orderedNodeIds": ordered_ids,
|
||||
"pauseReason": "contextAssignment",
|
||||
}
|
||||
iface.updateRun(
|
||||
run_id,
|
||||
status="paused",
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from typing import Dict, Any, List
|
|||
from modules.datamodels.datamodelChat import ActionResult, ActionItem, TaskStep
|
||||
from modules.datamodels.datamodelChat import ChatWorkflow
|
||||
from modules.workflows.processing.shared.methodDiscovery import methods
|
||||
from modules.workflows.processing.shared.stateTools import checkWorkflowStopped
|
||||
from modules.shared.workflowState import checkWorkflowStopped
|
||||
from modules.workflows.processing.shared.parameterValidation import (
|
||||
InvalidActionParameterError, validateAndCoerceParameters,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import re
|
|||
from typing import Dict, Any, Optional, List
|
||||
from modules.datamodels.datamodelChat import TaskPlan, TaskStep, ActionResult, ReviewResult
|
||||
from modules.datamodels.datamodelChat import ChatWorkflow
|
||||
from modules.workflows.processing.shared.stateTools import checkWorkflowStopped
|
||||
from modules.shared.workflowState import checkWorkflowStopped
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from modules.datamodels.datamodelChat import TaskStep, TaskContext, TaskPlan, Wo
|
|||
from modules.workflows.processing.shared.promptGenerationTaskplan import (
|
||||
generateTaskPlanningPrompt
|
||||
)
|
||||
from modules.workflows.processing.shared.stateTools import checkWorkflowStopped
|
||||
from modules.shared.workflowState import checkWorkflowStopped
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ from modules.datamodels.datamodelChat import (
|
|||
)
|
||||
from modules.datamodels.datamodelChat import ChatWorkflow
|
||||
from modules.workflows.processing.modes.modeBase import BaseMode
|
||||
from modules.workflows.processing.shared.stateTools import checkWorkflowStopped
|
||||
from modules.shared.workflowState import checkWorkflowStopped
|
||||
from modules.shared.timeUtils import parseTimestamp
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ from modules.datamodels.datamodelChat import (
|
|||
)
|
||||
from modules.datamodels.datamodelChat import ChatWorkflow
|
||||
from modules.workflows.processing.modes.modeBase import BaseMode
|
||||
from modules.workflows.processing.shared.stateTools import checkWorkflowStopped
|
||||
from modules.shared.workflowState import checkWorkflowStopped
|
||||
from modules.shared.timeUtils import parseTimestamp
|
||||
from modules.workflows.processing.shared.executionState import TaskExecutionState, shouldContinue
|
||||
from modules.workflows.processing.shared.promptGenerationActionsDynamic import (
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ def _isRefSchema(typeStr: str) -> bool:
|
|||
"""
|
||||
if not typeStr or not typeStr.endswith("Ref"):
|
||||
return False
|
||||
from modules.workflowAutomation.editor.portTypes import PORT_TYPE_CATALOG
|
||||
from modules.datamodels.datamodelPortTypes import PORT_TYPE_CATALOG
|
||||
schema = PORT_TYPE_CATALOG.get(typeStr)
|
||||
if schema is None:
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
# Copyright (c) 2025 Patrick Motsch
|
||||
# All rights reserved.
|
||||
"""
|
||||
State Tools
|
||||
Re-exports from modules.shared.workflowState for backward compatibility.
|
||||
"""
|
||||
|
||||
from modules.shared.workflowState import checkWorkflowStopped, WorkflowStoppedException
|
||||
|
||||
__all__ = ["checkWorkflowStopped", "WorkflowStoppedException"]
|
||||
|
|
@ -14,7 +14,7 @@ from modules.datamodels.datamodelChat import ChatWorkflow, WorkflowModeEnum
|
|||
from modules.workflows.processing.modes.modeBase import BaseMode
|
||||
from modules.workflows.processing.modes.modeDynamic import DynamicMode
|
||||
from modules.workflows.processing.modes.modeAutomation import AutomationMode
|
||||
from modules.workflows.processing.shared.stateTools import checkWorkflowStopped
|
||||
from modules.shared.workflowState import checkWorkflowStopped
|
||||
from modules.datamodels.datamodelAi import OperationTypeEnum, PriorityEnum, ProcessingModeEnum, AiCallOptions, AiCallRequest
|
||||
from modules.shared.jsonUtils import extractJsonString, repairBrokenJson, parseJsonWithModel
|
||||
from modules.datamodels.datamodelWorkflow import UnderstandingResult
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
# Copyright (c) 2025 Patrick Motsch
|
||||
# Re-export shim — real implementation moved to modules.workflowAutomation.scheduler
|
||||
from modules.workflowAutomation.scheduler.mainScheduler import (
|
||||
WorkflowScheduler,
|
||||
start,
|
||||
stop,
|
||||
syncNow,
|
||||
setMainLoop,
|
||||
notifyRunFailed,
|
||||
setOnRunFailedCallback,
|
||||
)
|
||||
|
|
@ -14,7 +14,7 @@ from modules.datamodels.datamodelChat import (
|
|||
)
|
||||
from modules.datamodels.datamodelChat import TaskContext
|
||||
from modules.workflows.processing.workflowProcessor import WorkflowProcessor
|
||||
from modules.workflows.processing.shared.stateTools import WorkflowStoppedException, checkWorkflowStopped
|
||||
from modules.shared.workflowState import WorkflowStoppedException, checkWorkflowStopped
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ import psycopg2 # noqa: E402
|
|||
from psycopg2.extras import Json, RealDictCursor # noqa: E402
|
||||
|
||||
from modules.shared.configuration import APP_CONFIG # noqa: E402
|
||||
from modules.workflows.automation2.featureInstanceRefMigration import ( # noqa: E402
|
||||
from modules.workflowAutomation.engine.featureInstanceRefMigration import ( # noqa: E402
|
||||
materializeFeatureInstanceRefs,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -415,7 +415,7 @@ def _bootstrapServices() -> Tuple[Any, str, str]:
|
|||
"""
|
||||
from modules.interfaces.interfaceDbApp import getRootInterface
|
||||
from modules.datamodels.datamodelUam import Mandate
|
||||
from modules.serviceHub import getInterface as getServices
|
||||
from modules.serviceCenter.serviceHub import getInterface as getServices
|
||||
|
||||
rootInterface = getRootInterface()
|
||||
user = rootInterface.currentUser
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ _gateway_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".
|
|||
if _gateway_path not in sys.path:
|
||||
sys.path.insert(0, _gateway_path)
|
||||
|
||||
from modules.serviceHub import getInterface as getServices
|
||||
from modules.serviceCenter.serviceHub import getInterface as getServices
|
||||
from modules.datamodels.datamodelAi import (
|
||||
AiCallOptions,
|
||||
AiCallRequest,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ if _gateway_path not in sys.path:
|
|||
sys.path.insert(0, _gateway_path)
|
||||
|
||||
# Import the service initialization
|
||||
from modules.serviceHub import getInterface as getServices
|
||||
from modules.serviceCenter.serviceHub import getInterface as getServices
|
||||
from modules.datamodels.datamodelAi import AiCallOptions, OperationTypeEnum
|
||||
from modules.datamodels.datamodelUam import User
|
||||
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ class MethodAiOperationsTester:
|
|||
interfaceDbChat = interfaceDbChat.getInterface(self.testUser)
|
||||
|
||||
# Import and initialize services
|
||||
from modules.serviceHub import getInterface as getServices
|
||||
from modules.serviceCenter.serviceHub import getInterface as getServices
|
||||
|
||||
# Get services first
|
||||
self.services = getServices(self.testUser, None)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ if _gateway_path not in sys.path:
|
|||
sys.path.insert(0, _gateway_path)
|
||||
|
||||
# Import the service initialization
|
||||
from modules.serviceHub import getInterface as getServices
|
||||
from modules.serviceCenter.serviceHub import getInterface as getServices
|
||||
from modules.datamodels.datamodelAi import AiCallOptions, OperationTypeEnum
|
||||
from modules.datamodels.datamodelUam import User
|
||||
from modules.datamodels.datamodelWorkflow import AiResponse
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ from modules.workflowAutomation.editor.adapterValidator import (
|
|||
_validateAdapterAgainstAction,
|
||||
_validateAllAdapters,
|
||||
)
|
||||
from modules.workflowAutomation.editor.nodeAdapter import (
|
||||
from modules.nodeCatalog.nodeAdapter import (
|
||||
NodeAdapter,
|
||||
UserParamMapping,
|
||||
)
|
||||
|
|
@ -254,7 +254,7 @@ def _ensureOptionalDeps():
|
|||
|
||||
_LIVE_METHODS = [
|
||||
("modules.features.trustee.workflows.methodTrustee.methodTrustee", "MethodTrustee", "trustee"),
|
||||
("modules.workflows.methods.methodRedmine.methodRedmine", "MethodRedmine", "redmine"),
|
||||
("modules.features.redmine.workflows.methodRedmine.methodRedmine", "MethodRedmine", "redmine"),
|
||||
("modules.workflows.methods.methodSharepoint.methodSharepoint", "MethodSharepoint", "sharepoint"),
|
||||
("modules.workflows.methods.methodOutlook.methodOutlook", "MethodOutlook", "outlook"),
|
||||
("modules.workflows.methods.methodAi.methodAi", "MethodAi", "ai"),
|
||||
|
|
@ -334,7 +334,7 @@ def test_staticNodesHaveNoDriftAgainstLiveMethods():
|
|||
|
||||
History: wiki/c-work/4-done/2026-04-adapter-drift-cleanup.md
|
||||
"""
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
|
||||
instances = _instantiateLiveMethods()
|
||||
if not instances:
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ from __future__ import annotations
|
|||
|
||||
import pytest
|
||||
|
||||
from modules.workflowAutomation.editor.nodeDefinitions.redmine import REDMINE_NODES
|
||||
from modules.workflowAutomation.editor.nodeDefinitions.trustee import TRUSTEE_NODES
|
||||
from modules.nodeCatalog.nodeDefinitions.redmine import REDMINE_NODES
|
||||
from modules.nodeCatalog.nodeDefinitions.trustee import TRUSTEE_NODES
|
||||
|
||||
|
||||
def _featureInstanceParam(node: dict) -> dict | None:
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ from __future__ import annotations
|
|||
|
||||
import pytest
|
||||
|
||||
from modules.workflowAutomation.editor.nodeAdapter import (
|
||||
from modules.nodeCatalog.nodeAdapter import (
|
||||
NodeAdapter,
|
||||
UserParamMapping,
|
||||
_adapterFromLegacyNode,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Catalog integrity + new Phase-1 schemas
|
|||
|
||||
import pytest
|
||||
|
||||
from modules.workflowAutomation.editor.portTypes import (
|
||||
from modules.nodeCatalog.portTypes import (
|
||||
PORT_TYPE_CATALOG,
|
||||
PRIMITIVE_TYPES,
|
||||
PortField,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright (c) 2025 Patrick Motsch
|
||||
"""Port type catalog: nested provenance schemas (Typed Generic Handover)."""
|
||||
|
||||
from modules.workflowAutomation.editor.portTypes import PORT_TYPE_CATALOG, _defaultForType
|
||||
from modules.nodeCatalog.portTypes import PORT_TYPE_CATALOG, _defaultForType
|
||||
|
||||
|
||||
def test_connection_ref_in_catalog():
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright (c) 2025 Patrick Motsch
|
||||
from modules.workflowAutomation.editor.upstreamPathsService import compute_upstream_paths
|
||||
from modules.workflowAutomation.engine.graphUtils import parse_graph_defined_schema, validateGraph
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
|
||||
|
||||
def test_compute_upstream_paths_includes_form_dynamic_fields():
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ def _instantiateMethod(methodCls):
|
|||
|
||||
@pytest.mark.parametrize("modulePath,className", [
|
||||
("modules.features.trustee.workflows.methodTrustee.methodTrustee", "MethodTrustee"),
|
||||
("modules.workflows.methods.methodRedmine.methodRedmine", "MethodRedmine"),
|
||||
("modules.features.redmine.workflows.methodRedmine.methodRedmine", "MethodRedmine"),
|
||||
("modules.workflows.methods.methodSharepoint.methodSharepoint", "MethodSharepoint"),
|
||||
("modules.workflows.methods.methodOutlook.methodOutlook", "MethodOutlook"),
|
||||
("modules.workflows.methods.methodAi.methodAi", "MethodAi"),
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ Verifies that:
|
|||
|
||||
import inspect
|
||||
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.workflowAutomation.editor.portTypes import PORT_TYPE_CATALOG
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.portTypes import PORT_TYPE_CATALOG
|
||||
from modules.workflowAutomation.engine.executors import actionNodeExecutor as _actionExec
|
||||
from modules.workflowAutomation.engine.graphUtils import validateGraph
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import pytest
|
||||
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
|
||||
|
||||
def test_all_nodes_have_usesAi():
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ from typing import Any, Dict, Optional
|
|||
|
||||
import pytest
|
||||
|
||||
from modules.serviceCenter.services.serviceAgent import workflowTools
|
||||
from modules.workflowAutomation import agentTools as workflowTools
|
||||
from modules.serviceCenter.services.serviceAgent.datamodelAgent import ToolResult
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ import json
|
|||
|
||||
import pytest
|
||||
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.workflowAutomation.editor.portTypes import PORT_TYPE_CATALOG
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.portTypes import PORT_TYPE_CATALOG
|
||||
from modules.workflows.methods.methodContext.actions.extractContent import (
|
||||
PRESENTATION_KIND,
|
||||
build_presentation_envelope_from_plain_text,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# Tests for Phase 3: context.extractContent node, port types, executor dispatch.
|
||||
|
||||
import pytest
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.workflowAutomation.editor.portTypes import PORT_TYPE_CATALOG
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.portTypes import PORT_TYPE_CATALOG
|
||||
from modules.workflowAutomation.engine.udmUpstreamShapes import (
|
||||
_coerceConsolidateResultInput,
|
||||
_coerceUdmDocumentInput,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Tests for Phase 4: data.consolidate, ai.consolidate, flow.loop level/concurrency, flow.merge dynamic.
|
||||
|
||||
import pytest
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
|
||||
|
||||
class TestNodeDefinitions:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Tests for Phase 5: Loop concurrency, StepLog batching, streaming aggregate.
|
||||
|
||||
import pytest
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
|
||||
|
||||
def test_loop_concurrency_param_default_1():
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import pytest
|
||||
|
||||
from modules.workflowAutomation.editor.portTypes import unwrapTransit, wrapTransit
|
||||
from modules.nodeCatalog.portTypes import unwrapTransit, wrapTransit
|
||||
from modules.workflowAutomation.editor.switchOutput import (
|
||||
build_switch_branch_payload,
|
||||
build_switch_combined_output,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
import pytest
|
||||
|
||||
from modules.workflowAutomation.editor._workflowFileSchema import (
|
||||
from modules.nodeCatalog._workflowFileSchema import (
|
||||
WORKFLOW_FILE_KIND,
|
||||
WORKFLOW_FILE_SCHEMA_VERSION,
|
||||
WorkflowFileSchemaError,
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class TestValidateGraphStartNode:
|
|||
|
||||
|
||||
def test_switch_second_output_to_ai_prompt_ok(self):
|
||||
from modules.workflowAutomation.editor.nodeDefinitions import STATIC_NODE_TYPES
|
||||
from modules.nodeCatalog.nodeDefinitions import STATIC_NODE_TYPES
|
||||
|
||||
node_type_ids = {n["id"] for n in STATIC_NODE_TYPES}
|
||||
graph = {
|
||||
|
|
|
|||
Loading…
Reference in a new issue