Skip to content

Commit 3c86a1a

Browse files
committed
feat: add workflow compilation
Signed-off-by: Eloy Coto <eloy.coto@acalustra.com>
1 parent 8a13868 commit 3c86a1a

File tree

6 files changed

+214
-1
lines changed

6 files changed

+214
-1
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,5 @@ coverage.xml
4747
.hypothesis/
4848
.pytest_cache/
4949
cover/
50+
51+
serverless-workflow/target

Dockerfile

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
FROM python:3.13-slim
22

3-
# Install system dependencies
3+
# Install system dependencies including Java and Maven
44
RUN apt-get update && apt-get install -y \
55
curl \
6+
openjdk-21-jdk \
7+
maven \
68
&& rm -rf /var/lib/apt/lists/*
79

810
# Install uv
@@ -17,6 +19,16 @@ COPY pyproject.toml uv.lock ./
1719
# Install dependencies
1820
RUN uv sync --frozen
1921

22+
COPY serverless-workflow serverless-workflow
23+
WORKDIR serverless-workflow
24+
25+
RUN mvn clean package
26+
RUN mkdir -p /app/kogito && \
27+
cp target/my-workflow-project-1.0-SNAPSHOT.jar /app/kogito/app.jar && \
28+
cp target/dependency/* /app/kogito/ 2>/dev/null || true
29+
30+
WORKDIR /app
31+
2032
# Copy application code
2133
COPY mcp_server.py ./
2234
COPY tools/ ./tools/

serverless-workflow/pom.xml

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>com.example</groupId>
8+
<artifactId>my-workflow-project</artifactId>
9+
<version>1.0-SNAPSHOT</version>
10+
11+
<properties>
12+
<maven.compiler.source>17</maven.compiler.source>
13+
<maven.compiler.target>17</maven.compiler.target>
14+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15+
<kogito.bom.artifact-id>kogito-bom</kogito.bom.artifact-id>
16+
<kogito.bom.group-id>org.kie.kogito</kogito.bom.group-id>
17+
<kogito.bom.version>10.1.0</kogito.bom.version>
18+
</properties>
19+
<dependencyManagement>
20+
<dependencies>
21+
<dependency>
22+
<groupId>${kogito.bom.group-id}</groupId>
23+
<artifactId>${kogito.bom.artifact-id}</artifactId>
24+
<version>${kogito.bom.version}</version>
25+
<type>pom</type>
26+
<scope>import</scope>
27+
</dependency>
28+
</dependencies>
29+
</dependencyManagement>
30+
31+
<dependencies>
32+
<dependency>
33+
<groupId>org.kie.kogito</groupId>
34+
<artifactId>kogito-quarkus-serverless-workflow</artifactId>
35+
<version>2.44.0.Alpha</version>
36+
</dependency>
37+
38+
<dependency>
39+
<groupId>org.kie.kogito</groupId>
40+
<artifactId>kogito-serverless-workflow-runtime</artifactId>
41+
<version>${kogito.bom.version}</version>
42+
</dependency>
43+
44+
<dependency>
45+
<groupId>org.kie.kogito</groupId>
46+
<artifactId>kogito-serverless-workflow-executor-core</artifactId>
47+
<version>${kogito.bom.version}</version>
48+
</dependency>
49+
50+
<dependency>
51+
<groupId>org.kie.kogito</groupId>
52+
<artifactId>kogito-serverless-workflow-executor</artifactId>
53+
<version>${kogito.bom.version}</version>
54+
<type>pom</type>
55+
</dependency>
56+
</dependencies>
57+
58+
<build>
59+
<plugins>
60+
<plugin>
61+
<groupId>org.apache.maven.plugins</groupId>
62+
<artifactId>maven-compiler-plugin</artifactId>
63+
<version>3.11.0</version>
64+
</plugin>
65+
<plugin>
66+
<groupId>org.apache.maven.plugins</groupId>
67+
<artifactId>maven-dependency-plugin</artifactId>
68+
<version>3.6.1</version>
69+
<executions>
70+
<execution>
71+
<id>copy-dependencies</id>
72+
<phase>package</phase>
73+
<goals>
74+
<goal>copy-dependencies</goal>
75+
</goals>
76+
</execution>
77+
</executions>
78+
</plugin>
79+
</plugins>
80+
</build>
81+
</project>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.example;
2+
3+
import org.kie.kogito.serverless.workflow.executor.StaticWorkflowApplication;
4+
import org.kie.kogito.serverless.workflow.models.JsonNodeModel;
5+
import org.kie.kogito.serverless.workflow.utils.ServerlessWorkflowUtils;
6+
import org.kie.kogito.serverless.workflow.utils.WorkflowFormat;
7+
import io.serverlessworkflow.api.Workflow;
8+
import org.kie.kogito.process.Process;
9+
10+
import java.io.FileReader;
11+
import java.io.IOException;
12+
import java.io.Reader;
13+
import java.util.Collections;
14+
import java.util.List;
15+
import java.util.stream.Collectors;
16+
17+
public class DefinitionFileExecutor {
18+
19+
public static void main(String[] args) throws IOException {
20+
System.out.printf("Initialize the workflow: %s\n", args[0]);
21+
22+
try (Reader reader = new FileReader(args[0]);
23+
StaticWorkflowApplication application = StaticWorkflowApplication.create()) {
24+
Workflow workflow = ServerlessWorkflowUtils.getWorkflow(reader, WorkflowFormat.JSON);
25+
application.process(workflow);
26+
27+
JsonNodeModel result = application.execute(workflow, Collections.emptyMap());
28+
System.out.printf("Execution information: %s\n", result);
29+
30+
List<String> registeredStates = workflow.getStates().stream()
31+
.map(p -> p.getName())
32+
.collect(Collectors.toList());
33+
34+
List<String> registeredFunctions = workflow.getFunctions().getFunctionDefs().stream()
35+
.map(p -> p.getName())
36+
.collect(Collectors.toList());
37+
38+
System.out.println("Registered functions:");
39+
System.out.println(registeredFunctions);
40+
41+
System.out.println("Registered states:");
42+
System.out.println(registeredStates);
43+
System.out.println("Workflow is correct and compiled successfully");
44+
} catch (Exception e) {
45+
System.err.println("[ERROR] Workflow is not valid: " + e.getMessage());
46+
System.exit(1);
47+
}
48+
}
49+
}

tools/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
from .orchestrator_compile_workflow import compile_workflow
12
from .orchestrator_creation_workflow_rules import creation_workflow_rules
23
from .orchestrator_get_sample_workflow import orchestrator_get_sample_workflow
34
from .orchestrator_get_schema_rules import get_schema_rules
45
from .orchestrator_workflow_renderer import orchestrator_preview_workflow
56

67
__all__ = [
8+
compile_workflow,
79
creation_workflow_rules,
810
get_schema_rules,
911
orchestrator_get_sample_workflow,
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import logging
2+
import os
3+
import subprocess
4+
import uuid
5+
6+
from .orchestrator_service import orchestrator_mcp
7+
8+
logger = logging.getLogger(__name__)
9+
10+
11+
def get_command():
12+
base_path = (
13+
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
14+
+ "/serverless-workflow"
15+
)
16+
return [
17+
"java",
18+
"-cp",
19+
f"{base_path}/target/my-workflow-project-1.0-SNAPSHOT.jar:{base_path}/target/dependency/*",
20+
"com.example.DefinitionFileExecutor",
21+
]
22+
23+
24+
@orchestrator_mcp.tool()
25+
def compile_workflow(session_id: str, workflow: str) -> (bool, str):
26+
"""
27+
Compile and validate a rhdh orchestrator workflow by writing it to a
28+
temporary file and executing the validation command.
29+
30+
Args:
31+
session_id: The session identifier
32+
workflow: The workflow content as a string
33+
34+
Returns:
35+
A tuple of (success: bool, logs: str)
36+
"""
37+
logger.info(f"orchestrator_compile_workflow for session_id='{session_id}'")
38+
39+
# Generate unique filename using UUID
40+
workflow_uuid = str(uuid.uuid4())
41+
workflow_path = f"/tmp/workflow-{workflow_uuid}.sw.json"
42+
43+
try:
44+
with open(workflow_path, "w") as f:
45+
f.write(workflow)
46+
47+
cmd = get_command() + [workflow_path]
48+
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
49+
50+
logs = result.stdout + result.stderr
51+
52+
try:
53+
os.remove(workflow_path)
54+
except OSError:
55+
pass
56+
57+
success = result.returncode == 0
58+
return success, logs
59+
60+
except Exception as e:
61+
try:
62+
os.remove(workflow_path)
63+
except OSError:
64+
pass
65+
66+
logger.error(f"Error compiling workflow: {e}")
67+
return False, f"Error: {str(e)}"

0 commit comments

Comments
 (0)