improved ConditionalAIAdapter class (added caching)
This commit is contained in:
parent
dc7d0a5998
commit
e00739d564
3 changed files with 303 additions and 157 deletions
|
|
@ -0,0 +1,184 @@
|
|||
package ai.dynamixs;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.imixs.ai.ImixsAIContextHandler;
|
||||
import org.imixs.ai.workflow.ImixsAIPromptService;
|
||||
import org.imixs.ai.workflow.OpenAIAPIService;
|
||||
import org.imixs.workflow.ItemCollection;
|
||||
import org.imixs.workflow.engine.ConditionalExpressionEvent;
|
||||
import org.imixs.workflow.engine.WorkflowService;
|
||||
import org.imixs.workflow.exceptions.AdapterException;
|
||||
import org.imixs.workflow.exceptions.PluginException;
|
||||
|
||||
import jakarta.enterprise.event.Observes;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.json.JsonObject;
|
||||
import jakarta.transaction.TransactionScoped;
|
||||
|
||||
/**
|
||||
* The ConditionalAIAdapter reacts on CDI Events of type BPMNConditionEvent and
|
||||
* evaluates a condition against an LLM
|
||||
* <p>
|
||||
* The Adapter defines a Default Expression template for LLMs. The BPMN
|
||||
* Configuration must only include the user prompt. See the following example:
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
<imixs-ai name="CONDITION">
|
||||
<debug>true</debug>
|
||||
<endpoint><propertyvalue>llm.service.endpoint</propertyvalue></endpoint>
|
||||
<result-item>my.condition</result-item>
|
||||
<prompt><![CDATA[
|
||||
Is Germany an EU member country? ]]>
|
||||
</prompt>
|
||||
</imixs-ai>
|
||||
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Caching: The ConditionalAIAdapter implements a caching mechnaism. The adapter
|
||||
* class stores the hash value in to the item <result-item>.hash to avoid
|
||||
* duplicate calls against the llm with the same prompt in one processing cycle!
|
||||
*/
|
||||
@TransactionScoped
|
||||
public class ConditionalAIAdapter implements Serializable {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ConditionalAIAdapter.class.getName());
|
||||
|
||||
// Transaction-scoped cache - survives across multiple eval() calls
|
||||
private Map<Long, String> promptResultCache = new HashMap<>();
|
||||
|
||||
@Inject
|
||||
protected WorkflowService workflowService;
|
||||
|
||||
@Inject
|
||||
protected ImixsAIPromptService imixsAIPromptService;
|
||||
|
||||
@Inject
|
||||
protected OpenAIAPIService llmService;
|
||||
|
||||
@Inject
|
||||
ImixsAIContextHandler imixsAIContextHandler;
|
||||
|
||||
public static String DEFAULT_EXPRESSION_TEMPLATE = "<imixs-ai name=\"CONDITION\">\n" + //
|
||||
" <debug>true</debug>\n" + //
|
||||
" <endpoint><propertyvalue>llm.service.endpoint</propertyvalue></endpoint>\n" + //
|
||||
" <result-event>BOOLEAN</result-event>\n" + //
|
||||
" <PromptDefinition>\n" + //
|
||||
" <prompt_options>{\"n_predict\": 16, \"temperature\": 0 }</prompt_options>\n" + //
|
||||
" <prompt role=\"system\"><![CDATA[\n" + //
|
||||
" Evaluate the following condition to 'true' or 'false'. Do only answer with 'true' or 'false'. Do not add any additional context into your answer.]]>\n"
|
||||
+ //
|
||||
" </prompt>\n" + //
|
||||
" </PromptDefinition>\n" + //
|
||||
"</imixs-ai>";
|
||||
|
||||
/**
|
||||
* Test method
|
||||
*
|
||||
* @param conditionEvent
|
||||
* @throws PluginException
|
||||
* @throws AdapterException
|
||||
*/
|
||||
public void onConditionEvent(@Observes ConditionalExpressionEvent conditionalEvent)
|
||||
throws PluginException, AdapterException {
|
||||
|
||||
// run only if we have a prompt definition
|
||||
String condition = conditionalEvent.getCondition();
|
||||
if (condition != null && condition.contains("<imixs-ai")) {
|
||||
long l = System.currentTimeMillis();
|
||||
List<ItemCollection> llmConditionDefinitions;
|
||||
boolean llmAPIDebug = false;
|
||||
|
||||
condition = condition.trim();
|
||||
llmConditionDefinitions = workflowService.evalXMLExpressionList(
|
||||
conditionalEvent.getCondition(), "imixs-ai", "CONDITION", conditionalEvent.getWorkitem(), true);
|
||||
|
||||
for (ItemCollection promptDefinition : llmConditionDefinitions) {
|
||||
String llmAPIEndpoint = imixsAIPromptService.parseLLMEndpointByBPMN(promptDefinition);
|
||||
String userPrompt = promptDefinition.getItemValueString("prompt");
|
||||
String llmAPIResultItem = promptDefinition.getItemValueString("result-item");
|
||||
if (llmAPIResultItem.isEmpty()) {
|
||||
throw new PluginException(OpenAIAPIService.class.getSimpleName(),
|
||||
OpenAIAPIService.ERROR_PROMPT_TEMPLATE,
|
||||
"Prompt Definition must include the tag 'result-item'.");
|
||||
}
|
||||
|
||||
if ("true".equalsIgnoreCase(promptDefinition.getItemValueString("debug"))) {
|
||||
llmAPIDebug = true;
|
||||
}
|
||||
if (llmAPIDebug) {
|
||||
logger.info("├── evaluate conditional expression");
|
||||
}
|
||||
// set DEFAULT_EXPRESSION_TEMPLATE.
|
||||
imixsAIContextHandler.setWorkItem(conditionalEvent.getWorkitem());
|
||||
imixsAIContextHandler.addPromptDefinition(DEFAULT_EXPRESSION_TEMPLATE);
|
||||
|
||||
// add the user prompt!
|
||||
imixsAIContextHandler.addMessage(ImixsAIContextHandler.ROLE_USER, userPrompt,
|
||||
workflowService.getSessionContext().getCallerPrincipal().getName(), null);
|
||||
|
||||
// POST PromptCompletion
|
||||
JsonObject jsonPrompt = imixsAIContextHandler.getOpenAIMessageObject();
|
||||
if (llmAPIDebug) {
|
||||
logger.info("│ ├── conditional prompt:");
|
||||
logger.info(jsonPrompt.toString());
|
||||
}
|
||||
|
||||
// Caching:
|
||||
// if jsonPrompt (hash) was already stored, we do not call the LLM
|
||||
long hashPrompt = jsonPrompt.hashCode();
|
||||
// Check transaction-scoped cache
|
||||
if (promptResultCache.containsKey(hashPrompt)) {
|
||||
if (llmAPIDebug) {
|
||||
logger.info("│ ├── reuse cached result from transaction cache - hash=" + hashPrompt);
|
||||
logger.info("└── ✓ evaluation time: " + (System.currentTimeMillis() - l) + "ms");
|
||||
}
|
||||
conditionalEvent.setCondition(promptResultCache.get(hashPrompt));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
String completionResult = llmService.postPromptCompletion(jsonPrompt, llmAPIEndpoint);
|
||||
if (llmAPIDebug) {
|
||||
logger.info("│ ├── ⚙ Completion Request conditional expression...");
|
||||
}
|
||||
String conditionalExpressionResult = llmService.processPromptResult(completionResult, "BOOLEAN",
|
||||
conditionalEvent.getWorkitem());
|
||||
|
||||
// store the result message
|
||||
if (conditionalExpressionResult != null && !conditionalExpressionResult.isBlank()) {
|
||||
// trim result
|
||||
conditionalExpressionResult = conditionalExpressionResult.trim().toLowerCase();
|
||||
|
||||
if (!"true".equals(conditionalExpressionResult) && !"false".equals(conditionalExpressionResult)) {
|
||||
logger.warning(
|
||||
"│ ├── ⚠ Unexpected conditional expression result: " + conditionalExpressionResult);
|
||||
throw new PluginException(OpenAIAPIService.class.getSimpleName(),
|
||||
OpenAIAPIService.ERROR_PROMPT_INFERENCE,
|
||||
"Conditional expression should result into 'true' or 'false' only");
|
||||
}
|
||||
if (llmAPIDebug) {
|
||||
logger.info("│ ├── ⚐ Conditional expression result: " + conditionalExpressionResult);
|
||||
}
|
||||
// store result
|
||||
boolean booleanExpressionResult = Boolean.parseBoolean(conditionalExpressionResult.trim());
|
||||
conditionalEvent.getWorkitem().setItemValue(llmAPIResultItem, booleanExpressionResult);
|
||||
|
||||
// Store in transaction-scoped cache
|
||||
promptResultCache.put(hashPrompt, "" + booleanExpressionResult);
|
||||
conditionalEvent.setCondition("" + booleanExpressionResult);
|
||||
logger.info("└── ✓ evaluation time: " + (System.currentTimeMillis() - l) + "ms");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
package ai.dynamixs;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.imixs.ai.ImixsAIContextHandler;
|
||||
import org.imixs.ai.workflow.ImixsAIPromptService;
|
||||
import org.imixs.ai.workflow.ImixsAIResultEvent;
|
||||
import org.imixs.ai.workflow.OpenAIAPIService;
|
||||
import org.imixs.workflow.ItemCollection;
|
||||
import org.imixs.workflow.engine.ConditionalExpressionEvent;
|
||||
import org.imixs.workflow.engine.WorkflowService;
|
||||
import org.imixs.workflow.exceptions.AdapterException;
|
||||
import org.imixs.workflow.exceptions.PluginException;
|
||||
import org.imixs.workflow.faces.data.WorkflowEvent;
|
||||
|
||||
import jakarta.enterprise.event.Observes;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
/**
|
||||
* Debug class to test CDI Events of type BPMNConditionEvent
|
||||
*
|
||||
*
|
||||
* Note: To run this plugin the conditional configuration should look like this
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
<imixs-ai name="CONDITION">
|
||||
<debug>true</debug>
|
||||
<endpoint><propertyvalue>llm.service.endpoint</propertyvalue></endpoint>
|
||||
<result-event>BOOLEAN</result-event>
|
||||
<PromptDefinition>
|
||||
<prompt_options>{"n_predict": 16, "temperature": 0 }</prompt_options>
|
||||
<prompt role="system"><![CDATA[
|
||||
You are a sales expert. You evaluate the following condition to 'true' or 'false'. ]]>
|
||||
</prompt>
|
||||
<prompt role="user"><![CDATA[
|
||||
Is Germany an EU member country? ]]>
|
||||
</prompt>
|
||||
</PromptDefinition>
|
||||
</imixs-ai>
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
public class ConditionalDebugAdapter implements Serializable {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ConditionalDebugAdapter.class.getName());
|
||||
|
||||
@Inject
|
||||
protected WorkflowService workflowService;
|
||||
|
||||
@Inject
|
||||
protected ImixsAIPromptService imixsAIPromptService;
|
||||
|
||||
@Inject
|
||||
protected OpenAIAPIService llmService;
|
||||
|
||||
@Inject
|
||||
ImixsAIContextHandler imixsAIContextHandler;
|
||||
|
||||
/**
|
||||
* Test method
|
||||
*
|
||||
* @param conditionEvent
|
||||
* @throws PluginException
|
||||
* @throws AdapterException
|
||||
*/
|
||||
public void onConditionEvent(@Observes ConditionalExpressionEvent conditionalEvent)
|
||||
throws PluginException, AdapterException {
|
||||
|
||||
List<ItemCollection> llmConditionDefinitions;
|
||||
boolean llmAPIDebug = false;
|
||||
|
||||
llmConditionDefinitions = workflowService.evalXMLExpressionList(
|
||||
conditionalEvent.getCondition(), "imixs-ai", "CONDITION", conditionalEvent.getWorkitem(), true);
|
||||
|
||||
for (ItemCollection promptDefinition : llmConditionDefinitions) {
|
||||
String llmAPIEndpoint = imixsAIPromptService.parseLLMEndpointByBPMN(promptDefinition);
|
||||
String llmAPIResultEvent = promptDefinition.getItemValueString("result-event");
|
||||
String llmAPIResultItem = promptDefinition.getItemValueString("result-item");
|
||||
if ("true".equalsIgnoreCase(promptDefinition.getItemValueString("debug"))) {
|
||||
llmAPIDebug = true;
|
||||
}
|
||||
|
||||
// get PromptDefintion template...
|
||||
String prompt = "<PromptDefinition>" + promptDefinition.getItemValueString("PromptDefinition")
|
||||
+ "</PromptDefinition>";
|
||||
logger.info("PromptDefinition=" + prompt);
|
||||
|
||||
imixsAIContextHandler.setWorkItem(conditionalEvent.getWorkitem());
|
||||
imixsAIContextHandler.addPromptDefinition(prompt);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void onWorkflowEvent(@Observes WorkflowEvent conditionEvent) {
|
||||
|
||||
logger.info("We observe a WorkflowEvent");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This observer method reacts on a ImixsAI Result event and evaluates a boolean
|
||||
* value
|
||||
*
|
||||
* Note: To run this adapter the configuration should look like this
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
<imixs-ai name="CONDITION">
|
||||
<endpoint><propertyvalue>llm.service.endpoint</propertyvalue></endpoint>
|
||||
<result-event>BOOLEAN</result-event>
|
||||
</imixs-ai>
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
public void onEvent(@Observes ImixsAIResultEvent event) {
|
||||
if (event.getWorkitem() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we only adapt the result in case the eventType===JSON
|
||||
if ("BOOLEAN".equalsIgnoreCase(event.getEventType())) {
|
||||
// get result string
|
||||
String booleanString = event.getPromptResult();
|
||||
|
||||
logger.info("Prompt Boolean Result= " + booleanString);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -121,6 +121,7 @@ th {
|
|||
<bpmn2:flowNodeRef>event_HBu5SQ</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_ykc34Q</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_kXkdQw</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>event_jP2H0w</bpmn2:flowNodeRef>
|
||||
</bpmn2:lane>
|
||||
<bpmn2:lane id="Lane_4" name="Accounting">
|
||||
<bpmn2:flowNodeRef>IntermediateCatchEvent_8</bpmn2:flowNodeRef>
|
||||
|
|
@ -1385,6 +1386,7 @@ Bei Fragen wenden Sie sich bitte an info@imixs.com
|
|||
</bpmn2:extensionElements>
|
||||
<bpmn2:documentation id="documentation_GFtuYw"><![CDATA[<textblock><itemvalue>$workflowgroup</itemvalue> - <itemvalue>$workflowstatus</itemvalue></textblock>]]></bpmn2:documentation>
|
||||
<bpmn2:incoming>SequenceFlow_31</bpmn2:incoming>
|
||||
<bpmn2:incoming>sequenceFlow_rTkelw</bpmn2:incoming>
|
||||
<bpmn2:outgoing>sequenceFlow_b9RMyw</bpmn2:outgoing>
|
||||
<bpmn2:incoming>SequenceFlow_7</bpmn2:incoming>
|
||||
<bpmn2:outgoing>sequenceFlow_KW7K3Q</bpmn2:outgoing>
|
||||
|
|
@ -1601,7 +1603,6 @@ Debitor: <itemvalue>customer.name</itemvalue> (<itemvalue>customer.number</itemv
|
|||
<bpmn2:documentation id="documentation_96I0BA"><![CDATA[Review Rejected]]></bpmn2:documentation>
|
||||
<bpmn2:incoming>sequenceFlow_JClvDg</bpmn2:incoming>
|
||||
<bpmn2:incoming>sequenceFlow_PL3AfA</bpmn2:incoming>
|
||||
<bpmn2:incoming>sequenceFlow_tZVF2w</bpmn2:incoming>
|
||||
<bpmn2:outgoing>sequenceFlow_hbRxpg</bpmn2:outgoing>
|
||||
<bpmn2:dataOutput id="dataOutput_Gel0Bw" name="Signal_6_Output"/>
|
||||
<bpmn2:dataOutputAssociation id="dataOutputAssociation_Tgv2nA">
|
||||
|
|
@ -1867,19 +1868,13 @@ Debitor: <itemvalue>customer.name</itemvalue> (<itemvalue>customer.number</itemv
|
|||
</bpmn2:task>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_kWtVWw" name="check" sourceRef="gateway_YmVj8g" targetRef="event_kXkdQw">
|
||||
<bpmn2:documentation id="documentation_0f3Qyw"/>
|
||||
<bpmn2:conditionExpression id="formalExpression_laNJyA" xsi:type="bpmn2:tFormalExpression"><![CDATA[<imixs-ai name="CONDITION">
|
||||
<bpmn2:conditionExpression id="formalExpression_hy83hg" xsi:type="bpmn2:tFormalExpression"><![CDATA[<imixs-ai name="CONDITION">
|
||||
<debug>true</debug>
|
||||
<endpoint><propertyvalue>llm.service.endpoint</propertyvalue></endpoint>
|
||||
<result-event>BOOLEAN</result-event>
|
||||
<PromptDefinition>
|
||||
<prompt_options>{"n_predict": 16, "temperature": 0 }</prompt_options>
|
||||
<prompt role="system"><![CDATA[
|
||||
You are a sales expert. You evaluate the following condition to 'true' or 'false'. ]]]]><![CDATA[>
|
||||
<result-item>compliance.check</result-item>
|
||||
<prompt><![CDATA[
|
||||
Are Germany and Italy EU member countries? ]]]]><![CDATA[>
|
||||
</prompt>
|
||||
<prompt role="user"><![CDATA[
|
||||
Is Germany an EU member country? ]]]]><![CDATA[>
|
||||
</prompt>
|
||||
</PromptDefinition>
|
||||
</imixs-ai>
|
||||
]]></bpmn2:conditionExpression>
|
||||
</bpmn2:sequenceFlow>
|
||||
|
|
@ -2111,9 +2106,103 @@ Debitor: <itemvalue>customer.name</itemvalue> (<itemvalue>customer.number</itemv
|
|||
<bpmn2:sequenceFlow id="sequenceFlow_0qwg1A" sourceRef="event_kXkdQw" targetRef="task_omZN4A">
|
||||
<bpmn2:documentation id="documentation_pe890Q"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_tZVF2w" sourceRef="Task_3" targetRef="event_07075Q">
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_tZVF2w" sourceRef="Task_3" targetRef="event_jP2H0w">
|
||||
<bpmn2:documentation id="documentation_0WuhyQ"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:intermediateCatchEvent id="event_jP2H0w" imixs:activityid="37" name="DEBUG COndition">
|
||||
<bpmn2:extensionElements>
|
||||
<imixs:item name="keylogtimeformat" type="xs:string">
|
||||
<imixs:value><![CDATA[2]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="keyarchive" type="xs:string">
|
||||
<imixs:value><![CDATA[0]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="txtmailsubject" type="xs:string">
|
||||
<imixs:value/>
|
||||
</imixs:item>
|
||||
<imixs:item name="keyaccessmode" type="xs:string">
|
||||
<imixs:value><![CDATA[0]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="keyscheduledactivity" type="xs:string">
|
||||
<imixs:value><![CDATA[0]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="rtfmailbody" type="xs:string">
|
||||
<imixs:value/>
|
||||
</imixs:item>
|
||||
<imixs:item name="keyversion" type="xs:string">
|
||||
<imixs:value><![CDATA[0]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="keyaddwritefields" type="xs:string">
|
||||
<imixs:value><![CDATA[namCreator]]></imixs:value>
|
||||
<imixs:value><![CDATA[namCurrentEditor]]></imixs:value>
|
||||
<imixs:value><![CDATA[namprocessmanager]]></imixs:value>
|
||||
<imixs:value><![CDATA[namprocessteam]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="numnextactivityid" type="xs:int">
|
||||
<imixs:value><![CDATA[0]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="keyfollowup" type="xs:string">
|
||||
<imixs:value><![CDATA[2]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="keypublicresult" type="xs:string">
|
||||
<imixs:value><![CDATA[1]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="keylogdateformat" type="xs:string">
|
||||
<imixs:value><![CDATA[2]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="keyownershipfields" type="xs:string">
|
||||
<imixs:value><![CDATA[namCurrentEditor]]></imixs:value>
|
||||
<imixs:value><![CDATA[namCreator]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="numnextid" type="xs:int">
|
||||
<imixs:value><![CDATA[5000]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="txtnextprocesstree" type="xs:string">
|
||||
<imixs:value/>
|
||||
</imixs:item>
|
||||
<imixs:item name="txtactivityresult" type="xs:string">
|
||||
<imixs:value><![CDATA[<poi-copy name="filename"><itemvalue>sequencenumber</itemvalue>.*\.xlsx</poi-copy>
|
||||
<poi-copy name="copy">
|
||||
<find>INVOICE_TOTAL</find>
|
||||
<itemname>invoice.total</itemname>
|
||||
<type>number</type>
|
||||
</poi-copy>
|
||||
<poi-copy name="copy">
|
||||
<find>INVOICE_CURRENCY</find>
|
||||
<itemname>invoice.currency</itemname>
|
||||
</poi-copy>
|
||||
|
||||
]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="txtname" type="xs:string">
|
||||
<imixs:value><![CDATA[Speichern]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="keyownershipmode" type="xs:string">
|
||||
<imixs:value><![CDATA[0]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="rtfresultlog" type="xs:string">
|
||||
<imixs:value><![CDATA[Invoice rejected]]></imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="keyupdateacl" type="xs:boolean">
|
||||
<imixs:value>false</imixs:value>
|
||||
</imixs:item>
|
||||
<imixs:item name="keymailreceiverfields" type="xs:string"/>
|
||||
</bpmn2:extensionElements>
|
||||
<bpmn2:documentation id="documentation_aWOrGw"><![CDATA[Review Rejected]]></bpmn2:documentation>
|
||||
<bpmn2:incoming>sequenceFlow_PL3AfA</bpmn2:incoming>
|
||||
<bpmn2:outgoing>sequenceFlow_rTkelw</bpmn2:outgoing>
|
||||
<bpmn2:dataOutput id="dataOutput_cajPZg" name="Signal_6_Output"/>
|
||||
<bpmn2:dataOutputAssociation id="dataOutputAssociation_R1frQA">
|
||||
<bpmn2:sourceRef>DataOutput_5</bpmn2:sourceRef>
|
||||
</bpmn2:dataOutputAssociation>
|
||||
<bpmn2:outputSet id="outputSet_3eLIFA" name="Output Set 6">
|
||||
<bpmn2:dataOutputRefs>DataOutput_5</bpmn2:dataOutputRefs>
|
||||
</bpmn2:outputSet>
|
||||
<bpmn2:incoming>sequenceFlow_tZVF2w</bpmn2:incoming>
|
||||
</bpmn2:intermediateCatchEvent>
|
||||
<bpmn2:sequenceFlow id="sequenceFlow_rTkelw" sourceRef="event_jP2H0w" targetRef="task_VRC0OA">
|
||||
<bpmn2:documentation id="documentation_Rm6j4g"/>
|
||||
</bpmn2:sequenceFlow>
|
||||
</bpmn2:process>
|
||||
<bpmn2:process id="process_2" name="Default Process" processType="Public">
|
||||
<bpmn2:documentation id="documentation_6zIncA"/>
|
||||
|
|
@ -2971,6 +3060,12 @@ th { font-weight: bold;}
|
|||
<dc:Bounds height="20.0" width="100.0" x="1635.0" y="326.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape bpmnElement="event_jP2H0w" id="BPMNShape_1Kd85Q">
|
||||
<dc:Bounds height="36.0" width="36.0" x="1187.0" y="377.0"/>
|
||||
<bpmndi:BPMNLabel id="BPMNLabel_ZVtzzA">
|
||||
<dc:Bounds height="20.0" width="100.0" x="1155.0" y="416.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge bpmnElement="Association_2" id="BPMNEdge_Association_2" sourceElement="BPMNShape_DataObject_1" targetElement="BPMNShape_Task_5">
|
||||
<di:waypoint x="235.0" y="196.0"/>
|
||||
<di:waypoint x="335.0" y="196.0"/>
|
||||
|
|
@ -3208,14 +3303,17 @@ th { font-weight: bold;}
|
|||
<di:waypoint x="1703.0" y="305.0"/>
|
||||
<di:waypoint x="1780.0" y="305.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_tZVF2w" id="BPMNEdge_tNGMVQ" sourceElement="BPMNShape_Task_9" targetElement="BPMNShape_zJI73Q">
|
||||
<di:waypoint x="850.0" y="799.0"/>
|
||||
<di:waypoint x="876.0" y="799.0"/>
|
||||
<di:waypoint x="876.0" y="642.0"/>
|
||||
<di:waypoint x="1089.0" y="642.0"/>
|
||||
<di:waypoint x="1089.0" y="135.0"/>
|
||||
<di:waypoint x="1345.0" y="135.0"/>
|
||||
<di:waypoint x="1345.0" y="187.0"/>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_tZVF2w" id="BPMNEdge_tNGMVQ" sourceElement="BPMNShape_Task_9" targetElement="BPMNShape_1Kd85Q">
|
||||
<di:waypoint x="839.0" y="790.0"/>
|
||||
<di:waypoint x="839.0" y="648.0"/>
|
||||
<di:waypoint x="1018.0" y="648.0"/>
|
||||
<di:waypoint x="1018.0" y="458.0"/>
|
||||
<di:waypoint x="1204.0" y="458.0"/>
|
||||
<di:waypoint x="1204.0" y="413.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_rTkelw" id="BPMNEdge_1FMdMQ" sourceElement="BPMNShape_1Kd85Q" targetElement="BPMNShape_lxN0AA">
|
||||
<di:waypoint x="1205.0" y="377.0"/>
|
||||
<di:waypoint x="1205.0" y="330.0"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
</bpmndi:BPMNPlane>
|
||||
<bpmndi:BPMNLabelStyle id="BPMNLabelStyle_1">
|
||||
|
|
|
|||
Loading…
Reference in a new issue