External python task not returning variables - BERT Sentiment Analysis

Hey,
I am doing a basic Twitter sentiment analysis using a pre canned BERT model in hugging face.
The process itself is pretty straight forward.

I am using this python client:

It all works and I can see the python client scoring the tweet. When I pass the variable back it doesn’t seem to. There is the process variables at the point highlighted in the diagram

The tweetScore variable is the one in question. It should be set at this stage due to the success condition of the python job passing it in

 return task.complete({output_variable_name: sentiment_scores }) 

The output_variable_name in that line is passed in and is retrieved correctly as “tweetScore”
It seems to be making the sentiment_scores variable null, when it is not. Its value is

{‘positive’: 0.9864804148674011, ‘neutral’: 0.011598006822168827, ‘negative’: 0.0019216355867683887}

This is the full python script.

import time
from camunda.external_task.external_task import ExternalTask, TaskResult
from camunda.external_task.external_task_worker import ExternalTaskWorker
from transformers import AutoModelForSequenceClassification
from transformers import TFAutoModelForSequenceClassification
from transformers import AutoTokenizer
import numpy as np
from scipy.special import softmax
import csv
import urllib.request

task='sentiment'
MODEL = f"cardiffnlp/twitter-roberta-base-{task}"
labels=[]
model = ""
tokenizer = ""

# Required Input Variables
#   textToAnalyze: The tweet text to analyze
#   outputVariableName: The name of the variable to store the sentiment scores in


# configuration for the Client
default_config = {
    "maxTasks": 1,
    "lockDuration": 10000,
    "asyncResponseTimeout": 5000,
    "retries": 3,
    "retryTimeout": 5000,
    "sleepSeconds": 30
}

def handle_task(task: ExternalTask) -> TaskResult:
    textToAnalyze = task.get_variable("textToAnalyze")
    print("Analyzing sentiment for task: {}".format(textToAnalyze))

    output_variable_name = task.get_variable("outputVariableName")
    sentiment_scores = get_sentiment_score(textToAnalyze)
    print("Storing " + output_variable_name)
    return task.complete({output_variable_name: sentiment_scores }) 

def get_sentiment_score(text):

    encoded_input = tokenizer(text, return_tensors='tf')
    output = model(encoded_input)
    scores = output[0][0].numpy()
    scores = softmax(scores)

    ranking = np.argsort(scores)
    ranking = ranking[::-1]
    ret_val = {}
    for i in range(scores.shape[0]):
        ret_val[labels[ranking[i]]] = float(scores[ranking[i]])
    return ret_val

def preprocess(text):
    new_text = []
 
    for t in text.split(" "):
        t = '@user' if t.startswith('@') and len(t) > 1 else t
        t = 'http' if t.startswith('http') else t
        new_text.append(t)
    return " ".join(new_text)

if __name__ == '__main__':

    tokenizer = AutoTokenizer.from_pretrained(MODEL)

    # download label mapping
    mapping_link = f"https://raw.githubusercontent.com/cardiffnlp/tweeteval/main/datasets/{task}/mapping.txt"
    with urllib.request.urlopen(mapping_link) as f:
        html = f.read().decode('utf-8').split("\n")
        csvreader = csv.reader(html, delimiter='\t')
    labels = [row[1] for row in csvreader if len(row) > 1]

    # # TF
    model = TFAutoModelForSequenceClassification.from_pretrained(MODEL)
    model.save_pretrained(MODEL)

    test_scores = get_sentiment_score("This is awesome")
    print(test_scores)

    ExternalTaskWorker(worker_id="1", config=default_config).subscribe("tweet_sentiment_analyzer", handle_task)

The BPMN

<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.11.0">
  <bpmn:process id="negativeTweetResponder" name="Negative Tweet Responser" isExecutable="true">
    <bpmn:startEvent id="StartEventProcessStarted" name="Process Started">
      <bpmn:outgoing>SequenceFlow1</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:sequenceFlow id="SequenceFlow1" sourceRef="StartEventProcessStarted" targetRef="Activity_0x3e3vj" />
    <bpmn:serviceTask id="Activity_0gc0qih" name="Get Toyota Tweets" camunda:class="ai.rubberhose.twitterDelegates.SearchTwitterDelegate">
      <bpmn:extensionElements>
        <camunda:field name="searchTerm">
          <camunda:string>Toyota</camunda:string>
        </camunda:field>
        <camunda:field name="outputVariableName">
          <camunda:string>tweets</camunda:string>
        </camunda:field>
        <camunda:field name="maxTweets">
          <camunda:expression>${20}</camunda:expression>
        </camunda:field>
        <camunda:inputOutput>
          <camunda:outputParameter name="tweetsToReplyTo">
            <camunda:list />
          </camunda:outputParameter>
        </camunda:inputOutput>
      </bpmn:extensionElements>
      <bpmn:incoming>Flow_1k267ki</bpmn:incoming>
      <bpmn:outgoing>Flow_13ypp5w</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:subProcess id="Activity_1ymeepj">
      <bpmn:incoming>Flow_13ypp5w</bpmn:incoming>
      <bpmn:outgoing>Flow_00fqby9</bpmn:outgoing>
      <bpmn:multiInstanceLoopCharacteristics isSequential="true" camunda:collection="tweets" camunda:elementVariable="textToAnalyze" />
      <bpmn:startEvent id="Event_08v2q0t" name="Being Scoring Tweet">
        <bpmn:outgoing>Flow_060esas</bpmn:outgoing>
      </bpmn:startEvent>
      <bpmn:serviceTask id="Activity_0xn9jd4" name="Score Tweet" camunda:asyncBefore="true" camunda:asyncAfter="true" camunda:type="external" camunda:topic="tweet_sentiment_analyzer">
        <bpmn:extensionElements>
          <camunda:inputOutput>
            <camunda:inputParameter name="outputVariableName">tweetScore</camunda:inputParameter>
          </camunda:inputOutput>
        </bpmn:extensionElements>
        <bpmn:incoming>Flow_060esas</bpmn:incoming>
        <bpmn:outgoing>Flow_1n9p4rp</bpmn:outgoing>
      </bpmn:serviceTask>
      <bpmn:sequenceFlow id="Flow_060esas" sourceRef="Event_08v2q0t" targetRef="Activity_0xn9jd4" />
      <bpmn:exclusiveGateway id="Gateway_00q2hxz" name="Is Tweet Highly Negative" camunda:asyncBefore="true" camunda:asyncAfter="true">
        <bpmn:incoming>Flow_1458fiy</bpmn:incoming>
        <bpmn:outgoing>Flow_0c8fo2c</bpmn:outgoing>
        <bpmn:outgoing>Flow_1ry48uw</bpmn:outgoing>
      </bpmn:exclusiveGateway>
      <bpmn:sequenceFlow id="Flow_1n9p4rp" sourceRef="Activity_0xn9jd4" targetRef="Activity_0ak6b6x" />
      <bpmn:sequenceFlow id="Flow_0c8fo2c" name="Yes" sourceRef="Gateway_00q2hxz" targetRef="Activity_0e8i02v">
        <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">${tweetScore[negative] &gt;= 0.95}</bpmn:conditionExpression>
      </bpmn:sequenceFlow>
      <bpmn:endEvent id="Event_0i3h2is" name="End Tweet Scoring">
        <bpmn:incoming>Flow_0by99ym</bpmn:incoming>
      </bpmn:endEvent>
      <bpmn:exclusiveGateway id="Gateway_0hms6y2">
        <bpmn:incoming>Flow_1ry48uw</bpmn:incoming>
        <bpmn:incoming>Flow_0ngk2kg</bpmn:incoming>
        <bpmn:outgoing>Flow_0by99ym</bpmn:outgoing>
      </bpmn:exclusiveGateway>
      <bpmn:sequenceFlow id="Flow_0by99ym" sourceRef="Gateway_0hms6y2" targetRef="Event_0i3h2is" />
      <bpmn:sequenceFlow id="Flow_1ry48uw" name="No" sourceRef="Gateway_00q2hxz" targetRef="Gateway_0hms6y2">
        <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">${tweetScore[negative] &lt; 0.95}</bpmn:conditionExpression>
      </bpmn:sequenceFlow>
      <bpmn:sequenceFlow id="Flow_0ngk2kg" sourceRef="Activity_0e8i02v" targetRef="Gateway_0hms6y2" />
      <bpmn:scriptTask id="Activity_0e8i02v" name="Collect Tweet" scriptFormat="javascript" camunda:resultVariable="tweetsToReplyTo">
        <bpmn:incoming>Flow_0c8fo2c</bpmn:incoming>
        <bpmn:outgoing>Flow_0ngk2kg</bpmn:outgoing>
        <bpmn:script>var negativeTweetList = tweetsToReplyTo;
negativeTweetList.add({
  'tweet': tweet, 
  'tweetScore': tweetScore});
negativeTweetList;</bpmn:script>
      </bpmn:scriptTask>
      <bpmn:sequenceFlow id="Flow_1458fiy" sourceRef="Activity_0ak6b6x" targetRef="Gateway_00q2hxz" />
      <bpmn:userTask id="Activity_0ak6b6x" name="Review">
        <bpmn:incoming>Flow_1n9p4rp</bpmn:incoming>
        <bpmn:outgoing>Flow_1458fiy</bpmn:outgoing>
      </bpmn:userTask>
    </bpmn:subProcess>
    <bpmn:sequenceFlow id="Flow_13ypp5w" sourceRef="Activity_0gc0qih" targetRef="Activity_1ymeepj" />
    <bpmn:sequenceFlow id="Flow_00fqby9" sourceRef="Activity_1ymeepj" targetRef="Gateway_0k49rj6" />
    <bpmn:sequenceFlow id="Flow_1ngopv2" sourceRef="Activity_06e52nv" targetRef="Activity_1c35f5m" />
    <bpmn:endEvent id="Event_1d5i362" name="End">
      <bpmn:incoming>Flow_040bxts</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="Flow_091z49q" sourceRef="Activity_1c35f5m" targetRef="Gateway_12dre90" />
    <bpmn:exclusiveGateway id="Gateway_0k49rj6" name="Are there tweets to respond to?">
      <bpmn:incoming>Flow_00fqby9</bpmn:incoming>
      <bpmn:outgoing>Flow_047ap8g</bpmn:outgoing>
      <bpmn:outgoing>Flow_1avm0ph</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:sequenceFlow id="Flow_047ap8g" name="Yes" sourceRef="Gateway_0k49rj6" targetRef="Activity_06e52nv">
      <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">${tweetsToReplyTo.size() &gt; 0}</bpmn:conditionExpression>
    </bpmn:sequenceFlow>
    <bpmn:exclusiveGateway id="Gateway_12dre90">
      <bpmn:incoming>Flow_091z49q</bpmn:incoming>
      <bpmn:incoming>Flow_1avm0ph</bpmn:incoming>
      <bpmn:outgoing>Flow_040bxts</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:sequenceFlow id="Flow_040bxts" sourceRef="Gateway_12dre90" targetRef="Event_1d5i362" />
    <bpmn:sequenceFlow id="Flow_1avm0ph" name="No" sourceRef="Gateway_0k49rj6" targetRef="Gateway_12dre90">
      <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">${tweetsToReplyTo.size() == 0}</bpmn:conditionExpression>
    </bpmn:sequenceFlow>
    <bpmn:userTask id="Activity_06e52nv" name="Compose Responses">
      <bpmn:incoming>Flow_047ap8g</bpmn:incoming>
      <bpmn:outgoing>Flow_1ngopv2</bpmn:outgoing>
    </bpmn:userTask>
    <bpmn:sequenceFlow id="Flow_1k267ki" sourceRef="Activity_0x3e3vj" targetRef="Activity_0gc0qih" />
    <bpmn:userTask id="Activity_0x3e3vj" name="Capture Search Term">
      <bpmn:extensionElements>
        <camunda:formData>
          <camunda:formField id="capturedSearchTerm" label="Search Term" type="string" defaultValue="#Toyota" />
        </camunda:formData>
        <camunda:inputOutput>
          <camunda:outputParameter name="tweets">
            <camunda:list />
          </camunda:outputParameter>
        </camunda:inputOutput>
      </bpmn:extensionElements>
      <bpmn:incoming>SequenceFlow1</bpmn:incoming>
      <bpmn:outgoing>Flow_1k267ki</bpmn:outgoing>
    </bpmn:userTask>
    <bpmn:userTask id="Activity_1c35f5m" name="Send Responses">
      <bpmn:incoming>Flow_1ngopv2</bpmn:incoming>
      <bpmn:outgoing>Flow_091z49q</bpmn:outgoing>
    </bpmn:userTask>
  </bpmn:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="negativeTweetResponder">
      <bpmndi:BPMNEdge id="Flow_1k267ki_di" bpmnElement="Flow_1k267ki">
        <di:waypoint x="380" y="200" />
        <di:waypoint x="440" y="200" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1avm0ph_di" bpmnElement="Flow_1avm0ph">
        <di:waypoint x="1530" y="160" />
        <di:waypoint x="1530" y="90" />
        <di:waypoint x="1910" y="90" />
        <di:waypoint x="1910" y="160" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="1713" y="72" width="15" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_040bxts_di" bpmnElement="Flow_040bxts">
        <di:waypoint x="1935" y="185" />
        <di:waypoint x="1972" y="185" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_047ap8g_di" bpmnElement="Flow_047ap8g">
        <di:waypoint x="1555" y="185" />
        <di:waypoint x="1600" y="185" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="1569" y="167" width="18" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_091z49q_di" bpmnElement="Flow_091z49q">
        <di:waypoint x="1840" y="185" />
        <di:waypoint x="1885" y="185" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1ngopv2_di" bpmnElement="Flow_1ngopv2">
        <di:waypoint x="1700" y="185" />
        <di:waypoint x="1740" y="185" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_00fqby9_di" bpmnElement="Flow_00fqby9">
        <di:waypoint x="1460" y="185" />
        <di:waypoint x="1505" y="185" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_13ypp5w_di" bpmnElement="Flow_13ypp5w">
        <di:waypoint x="540" y="200" />
        <di:waypoint x="600" y="200" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="SequenceFlow_08va5r8_di" bpmnElement="SequenceFlow1">
        <di:waypoint x="218" y="200" />
        <di:waypoint x="280" y="200" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="337.5" y="110" width="90" height="20" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEventProcessStarted">
        <dc:Bounds x="182" y="182" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="161" y="218" width="79" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_1wnud1q_di" bpmnElement="Activity_0gc0qih">
        <dc:Bounds x="440" y="160" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_1d5i362_di" bpmnElement="Event_1d5i362">
        <dc:Bounds x="1972" y="167" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="1980" y="210" width="20" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Gateway_0k49rj6_di" bpmnElement="Gateway_0k49rj6" isMarkerVisible="true">
        <dc:Bounds x="1505" y="160" width="50" height="50" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="1490" y="217" width="81" height="27" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Gateway_12dre90_di" bpmnElement="Gateway_12dre90" isMarkerVisible="true">
        <dc:Bounds x="1885" y="160" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_0chbcvq_di" bpmnElement="Activity_06e52nv">
        <dc:Bounds x="1600" y="145" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_09xenqo_di" bpmnElement="Activity_1c35f5m">
        <dc:Bounds x="1740" y="145" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_1ymeepj_di" bpmnElement="Activity_1ymeepj" isExpanded="true">
        <dc:Bounds x="600" y="80" width="860" height="210" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="Flow_0ngk2kg_di" bpmnElement="Flow_0ngk2kg">
        <di:waypoint x="1200" y="200" />
        <di:waypoint x="1245" y="200" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1ry48uw_di" bpmnElement="Flow_1ry48uw">
        <di:waypoint x="1020" y="175" />
        <di:waypoint x="1020" y="120" />
        <di:waypoint x="1270" y="120" />
        <di:waypoint x="1270" y="175" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="1138" y="102" width="15" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0by99ym_di" bpmnElement="Flow_0by99ym">
        <di:waypoint x="1295" y="200" />
        <di:waypoint x="1352" y="200" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0c8fo2c_di" bpmnElement="Flow_0c8fo2c">
        <di:waypoint x="1045" y="200" />
        <di:waypoint x="1100" y="200" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="1059" y="182" width="18" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1n9p4rp_di" bpmnElement="Flow_1n9p4rp">
        <di:waypoint x="810" y="200" />
        <di:waypoint x="850" y="200" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_060esas_di" bpmnElement="Flow_060esas">
        <di:waypoint x="676" y="200" />
        <di:waypoint x="710" y="200" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1458fiy_di" bpmnElement="Flow_1458fiy">
        <di:waypoint x="950" y="200" />
        <di:waypoint x="995" y="200" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="Event_08v2q0t_di" bpmnElement="Event_08v2q0t">
        <dc:Bounds x="640" y="182" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="624" y="225" width="69" height="27" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_0ecggil_di" bpmnElement="Activity_0xn9jd4">
        <dc:Bounds x="710" y="160" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Gateway_00q2hxz_di" bpmnElement="Gateway_00q2hxz" isMarkerVisible="true">
        <dc:Bounds x="995" y="175" width="50" height="50" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="983" y="232" width="75" height="27" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_0i3h2is_di" bpmnElement="Event_0i3h2is">
        <dc:Bounds x="1352" y="182" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="1344" y="225" width="52" height="27" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Gateway_0hms6y2_di" bpmnElement="Gateway_0hms6y2" isMarkerVisible="true">
        <dc:Bounds x="1245" y="175" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_0h4tugi_di" bpmnElement="Activity_0e8i02v">
        <dc:Bounds x="1100" y="160" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_1y6wwtk_di" bpmnElement="Activity_0ak6b6x">
        <dc:Bounds x="850" y="160" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_0n23jl7_di" bpmnElement="Activity_0x3e3vj">
        <dc:Bounds x="280" y="160" width="100" height="80" />
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn:definitions>

Hi @SlappyAUS

I just had a very quick look at the code…Try with ** before the dictionary.

  camunda_client.complete(**output_values_dict)

See if this showcase helps:

Best regards,
Marigianna

Thank you for the response,
Which python package are you using, I might give it a try.

Hi @SlappyAUS

You are right, sorry for skippin this important information.
I had this one for the ShowCase, therefore the change I suggested.

I have tried both clients in different showcases, and they both work fine, so do not be discouraged.

It might also be a variable mapping problem. Please have a look at this screenshot:

Your tweetScore variable is not contained in ${}

Best regards,
Marigianna

I did a small experiment and it seems this client has some issues with python dictionaries. When I flatten the value as you suggested it works, however it does not like composite objects.

Thank you for the insight.

1 Like