diff --git a/Dockerfile b/Dockerfile
index 0a40434..b8c1d01 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,38 +1,11 @@
-# Use an official Python runtime as a parent image
-FROM python:3.9-slim AS builder
+FROM python:3.12-slim@sha256:9e01bf1ae5db7649a236da7be1e94ffbbbdd7a93f867dd0d8d5720d9e1f89fab
-# Set environment variables
-ENV PYTHONDONTWRITEBYTECODE=1
-ENV PYTHONUNBUFFERED=1
-
-# Install system dependencies
-RUN apt-get update && \
- apt-get install -y --no-install-recommends \
- build-essential \
- curl \
- && rm -rf /var/lib/apt/lists/*
-
-# Upgrade pip
-RUN pip install --upgrade pip
-
-# Create app directory
WORKDIR /app
-
-# Install Python dependencies in a virtual environment
COPY requirements.txt .
-RUN python -m venv /opt/venv && \
- /opt/venv/bin/pip install -r requirements.txt
-
-# Copy the application code to the image
+RUN grep -v amf.fast.inference requirements.txt > /tmp/reqs.txt && \
+ pip install -r /tmp/reqs.txt && \
+ pip install --no-deps amf-fast-inference==0.0.3
COPY . .
-# Use a non-root user for security
-#RUN useradd --create-home appuser
-#USER appuser
-
-# Expose the port the app runs on
EXPOSE 5002
-
-# Set the entry point for the container
-ENTRYPOINT ["/opt/venv/bin/python"]
-CMD ["./main.py"]
+CMD ["gunicorn", "--workers", "1", "--bind", "0.0.0.0:5002", "main:app"]
diff --git a/docker-compose.yml b/docker-compose.yml
index b950ca4..08a0c61 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,11 +1,6 @@
-version: '3.8'
-
-services: #
+services:
bert-te:
container_name: bert_te
- build:
- context: . # Specify the build context
- dockerfile: Dockerfile # Specify the Dockerfile if it's not named 'Dockerfile'
+ build: .
ports:
- - "5002:5002" # Map port 5002 on the host to port 5002 in the container
-
+ - "5002:5002"
diff --git a/requirements.txt b/requirements.txt
index 52ef3cc..6f46956 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,11 +1,113 @@
-flask
-flask_uploads
-prometheus_flask_exporter
-torch
-numpy
-transformers
-Cython
+about-time==4.2.1
+aiohappyeyeballs==2.6.1
+aiohttp==3.13.3
+aiosignal==1.4.0
+alive-progress==3.3.0
+anyio==4.12.1
+attrs==25.4.0
+autograd==1.8.0
+blinker==1.9.0
+certifi==2026.1.4
+cffi==2.0.0
+charset-normalizer==3.4.4
+click==8.3.1
+cma==4.4.2
+coloredlogs==15.0.1
+contourpy==1.3.3
+cycler==0.12.1
+Cython==3.2.4
+datasets==4.5.0
+Deprecated==1.3.1
+dill==0.4.0
+filelock==3.24.3
+Flask==3.1.3
+Flask-Uploads==0.2.1
+flatbuffers==25.12.19
+fonttools==4.61.1
+frozenlist==1.8.0
+fsspec==2025.10.0
+graphemeu==0.7.2
+gunicorn==25.1.0
+h11==0.16.0
+hf-xet==1.2.0
+httpcore==1.0.9
+httpx==0.28.1
+huggingface_hub==0.36.2
+humanfriendly==10.0
+idna==3.11
+itsdangerous==2.2.0
+Jinja2==3.1.6
+joblib==1.5.3
+jsonschema==4.26.0
+jsonschema-specifications==2025.9.1
+kiwisolver==1.4.9
+markdown-it-py==4.0.0
+markdown2==2.5.4
+MarkupSafe==3.0.3
+matplotlib==3.10.8
+mdurl==0.1.2
+ml_dtypes==0.5.4
+moocore==0.2.0
+mpmath==1.3.0
+multidict==6.7.1
+multiprocess==0.70.18
+natsort==8.4.0
+networkx==3.4.2
+ninja==1.13.0
+nncf==2.19.0
+numpy==2.2.6
+onnx==1.20.1
+onnxruntime==1.24.2
+onnxruntime-tools==1.7.0
+openvino==2025.4.1
+openvino-telemetry==2025.2.0
+optimum==1.18.0
+optimum-intel==1.16.1
+packaging==26.0
+pandas==2.3.3
+pillow==12.1.1
+platformdirs==4.9.2
+prometheus_client==0.24.1
+prometheus_flask_exporter==0.23.2
+propcache==0.4.1
+protobuf==6.33.5
+psutil==7.2.2
+py-cpuinfo==9.0.0
+py3nvml==0.2.7
+pyarrow==23.0.1
+pycparser==3.0
+pydot==3.0.4
+Pygments==2.19.2
+pymoo==0.6.1.6
+pyparsing==3.3.2
+python-dateutil==2.9.0.post0
+pytz==2025.2
+PyYAML==6.0.3
+referencing==0.37.0
+regex==2026.2.19
+requests==2.32.5
+rich==14.3.3
+rpds-py==0.30.0
+safetensors==0.7.0
+scikit-learn==1.8.0
+scipy==1.17.1
+sentencepiece==0.2.1
+setuptools==82.0.0
+six==1.17.0
+sympy==1.14.0
+tabulate==0.9.0
+threadpoolctl==3.6.0
+tokenizers==0.15.2
+torch==2.10.0
+tqdm==4.67.3
+transformers==4.39.3
+typing_extensions==4.15.0
+tzdata==2025.3
+urllib3==2.6.3
+Werkzeug==3.1.6
+wrapt==2.1.1
xaif_eval==0.0.9
+xmltodict==1.0.4
+xxhash==3.6.0
+yarl==1.22.0
amf-fast-inference==0.0.3
-markdown2
-
diff --git a/tests/api-requests/BERT TE/GET Info.bru b/tests/api-requests/BERT TE/GET Info.bru
new file mode 100644
index 0000000..5e2523b
--- /dev/null
+++ b/tests/api-requests/BERT TE/GET Info.bru
@@ -0,0 +1,22 @@
+meta {
+ name: GET Info
+ type: http
+ seq: 2
+}
+
+get {
+ url: {{baseUrl}}/bert-te
+ auth: inherit
+}
+
+tests {
+ test("Response status should be 200", function () {
+ expect(res.status).to.equal(200);
+ });
+
+ test("Response should contain service description", function () {
+ const body = res.getBody();
+ expect(body).to.be.a('string');
+ expect(body).to.include('BART');
+ });
+}
diff --git a/tests/api-requests/BERT TE/XAIF Argumentation.bru b/tests/api-requests/BERT TE/XAIF Argumentation.bru
new file mode 100644
index 0000000..607baa6
--- /dev/null
+++ b/tests/api-requests/BERT TE/XAIF Argumentation.bru
@@ -0,0 +1,83 @@
+meta {
+ name: XAIF Argumentation
+ type: http
+ seq: 3
+}
+
+post {
+ url: {{baseUrl}}/bert-te
+ body: multipartForm
+ auth: inherit
+}
+
+headers {
+ Content-Type: multipart/form-data
+}
+
+body:multipart-form {
+ file: @file(test-inputs/xaif_argumentation.json)
+}
+
+tests {
+ // Three I-nodes about energy policy:
+ // I-node 4: "Renewable energy is essential for reducing carbon emissions."
+ // I-node 6: "Solar and wind power significantly reduce greenhouse gas output."
+ // I-node 8: "Fossil fuels are the only reliable source of energy for modern economies."
+ //
+ // The model identifies 2 conflict (CA) relations:
+ // (4, 8): renewables vs fossil fuels → CA
+ // (6, 8): solar/wind vs fossil fuels → CA
+ // The (4, 6) pair is neutral (both pro-renewable, no entailment).
+
+ const expectedData = {
+ "AIF": {
+ "descriptorfulfillments": null,
+ "edges": [
+ { "edgeID": 0, "fromID": 0, "toID": 3 },
+ { "edgeID": 1, "fromID": 3, "toID": 4 },
+ { "edgeID": 2, "fromID": 1, "toID": 5 },
+ { "edgeID": 3, "fromID": 5, "toID": 6 },
+ { "edgeID": 4, "fromID": 2, "toID": 7 },
+ { "edgeID": 5, "fromID": 7, "toID": 8 },
+ { "edgeID": 6, "fromID": 4, "toID": 9 },
+ { "edgeID": 7, "fromID": 9, "toID": 8 },
+ { "edgeID": 8, "fromID": 6, "toID": 10 },
+ { "edgeID": 9, "fromID": 10, "toID": 8 }
+ ],
+ "locutions": [
+ { "nodeID": 0, "personID": 0 },
+ { "nodeID": 1, "personID": 1 },
+ { "nodeID": 2, "personID": 2 }
+ ],
+ "nodes": [
+ { "nodeID": 0, "text": "Renewable energy is essential for reducing carbon emissions.", "type": "L" },
+ { "nodeID": 1, "text": "Solar and wind power significantly reduce greenhouse gas output.", "type": "L" },
+ { "nodeID": 2, "text": "Fossil fuels are the only reliable source of energy for modern economies.", "type": "L" },
+ { "nodeID": 3, "text": "Default Illocuting", "type": "YA" },
+ { "nodeID": 4, "text": "Renewable energy is essential for reducing carbon emissions.", "type": "I" },
+ { "nodeID": 5, "text": "Default Illocuting", "type": "YA" },
+ { "nodeID": 6, "text": "Solar and wind power significantly reduce greenhouse gas output.", "type": "I" },
+ { "nodeID": 7, "text": "Default Illocuting", "type": "YA" },
+ { "nodeID": 8, "text": "Fossil fuels are the only reliable source of energy for modern economies.", "type": "I" },
+ { "nodeID": 9, "text": "Default Conflict", "type": "CA" },
+ { "nodeID": 10, "text": "Default Conflict", "type": "CA" }
+ ],
+ "participants": [
+ { "firstname": "Speaker", "participantID": 0, "surname": "A" },
+ { "firstname": "Speaker", "participantID": 1, "surname": "B" },
+ { "firstname": "Speaker", "participantID": 2, "surname": "C" }
+ ],
+ "schemefulfillments": null
+ },
+ "dialog": true,
+ "ova": [],
+ "text": {
+ "txt": " Speaker A Renewable energy is essential for reducing carbon emissions..
Speaker B Solar and wind power significantly reduce greenhouse gas output..
Speaker C Fossil fuels are the only reliable source of energy for modern economies..
"
+ }
+ };
+
+ test("Response should match expected value", function () {
+ const receivedData = res.getBody();
+ expect(receivedData).to.deep.equal(expectedData);
+ });
+}
diff --git a/tests/api-requests/BERT TE/XAIF Sample.bru b/tests/api-requests/BERT TE/XAIF Sample.bru
new file mode 100644
index 0000000..4a7a5ac
--- /dev/null
+++ b/tests/api-requests/BERT TE/XAIF Sample.bru
@@ -0,0 +1,73 @@
+meta {
+ name: XAIF Sample
+ type: http
+ seq: 1
+}
+
+post {
+ url: {{baseUrl}}/bert-te
+ body: multipartForm
+ auth: inherit
+}
+
+headers {
+ Content-Type: multipart/form-data
+}
+
+body:multipart-form {
+ file: @file(test-inputs/xaif_sample.json)
+}
+
+tests {
+ // From the README: the model should identify 1 RA relation
+ // between I-node 3 ("disagreements between party members are entirely to be expected")
+ // and I-node 7 ("it's not uncommon for there to be disagreements between party members")
+ // because they entail each other.
+ const expectedData = {
+ "AIF": {
+ "descriptorfulfillments": null,
+ "edges": [
+ { "edgeID": 0, "fromID": 0, "toID": 4 },
+ { "edgeID": 1, "fromID": 4, "toID": 3 },
+ { "edgeID": 2, "fromID": 1, "toID": 6 },
+ { "edgeID": 3, "fromID": 6, "toID": 5 },
+ { "edgeID": 4, "fromID": 2, "toID": 8 },
+ { "edgeID": 5, "fromID": 8, "toID": 7 },
+ { "edgeID": 6, "fromID": 3, "toID": 9 },
+ { "edgeID": 7, "fromID": 9, "toID": 7 }
+ ],
+ "locutions": [
+ { "nodeID": 0, "personID": 0 },
+ { "nodeID": 1, "personID": 1 },
+ { "nodeID": 2, "personID": 2 }
+ ],
+ "nodes": [
+ { "nodeID": 0, "text": "disagreements between party members are entirely to be expected.", "type": "L" },
+ { "nodeID": 1, "text": "the SNP has disagreements.", "type": "L" },
+ { "nodeID": 2, "text": "it's not uncommon for there to be disagreements between party members.", "type": "L" },
+ { "nodeID": 3, "text": "disagreements between party members are entirely to be expected.", "type": "I" },
+ { "nodeID": 4, "text": "Default Illocuting", "type": "YA" },
+ { "nodeID": 5, "text": "the SNP has disagreements.", "type": "I" },
+ { "nodeID": 6, "text": "Default Illocuting", "type": "YA" },
+ { "nodeID": 7, "text": "it's not uncommon for there to be disagreements between party members.", "type": "I" },
+ { "nodeID": 8, "text": "Default Illocuting", "type": "YA" },
+ { "nodeID": 9, "text": "Default Inference", "type": "RA" }
+ ],
+ "participants": [
+ { "firstname": "Speaker", "participantID": 0, "surname": "1" },
+ { "firstname": "Speaker", "participantID": 1, "surname": "2" }
+ ],
+ "schemefulfillments": null
+ },
+ "dialog": true,
+ "ova": [],
+ "text": {
+ "txt": " Speaker 1 disagreements between party members are entirely to be expected..
Speaker 2 the SNP has disagreements..
Speaker 1 it's not uncommon for there to be disagreements between party members. .
"
+ }
+ };
+
+ test("Response should match expected value", function () {
+ const receivedData = res.getBody();
+ expect(receivedData).to.deep.equal(expectedData);
+ });
+}
diff --git a/tests/api-requests/BERT TE/bruno.json b/tests/api-requests/BERT TE/bruno.json
new file mode 100644
index 0000000..4941ccc
--- /dev/null
+++ b/tests/api-requests/BERT TE/bruno.json
@@ -0,0 +1,9 @@
+{
+ "version": "1",
+ "name": "BERT TE",
+ "type": "collection",
+ "ignore": [
+ "node_modules",
+ ".git"
+ ]
+}
\ No newline at end of file
diff --git a/tests/api-requests/BERT TE/environments/(1) local.bru b/tests/api-requests/BERT TE/environments/(1) local.bru
new file mode 100644
index 0000000..1f63896
--- /dev/null
+++ b/tests/api-requests/BERT TE/environments/(1) local.bru
@@ -0,0 +1,3 @@
+vars {
+ baseUrl: http://localhost:5002
+}
diff --git a/tests/api-requests/BERT TE/environments/(2) staging.bru b/tests/api-requests/BERT TE/environments/(2) staging.bru
new file mode 100644
index 0000000..cabf000
--- /dev/null
+++ b/tests/api-requests/BERT TE/environments/(2) staging.bru
@@ -0,0 +1,3 @@
+vars {
+ baseUrl: http://bert-te.amfws.staging.arg.tech
+}
diff --git a/tests/api-requests/BERT TE/environments/(3) production.bru b/tests/api-requests/BERT TE/environments/(3) production.bru
new file mode 100644
index 0000000..56de895
--- /dev/null
+++ b/tests/api-requests/BERT TE/environments/(3) production.bru
@@ -0,0 +1,3 @@
+vars {
+ baseUrl: http://bert-te.amfws.arg.tech
+}
diff --git a/tests/api-requests/BERT TE/test-inputs/xaif_argumentation.json b/tests/api-requests/BERT TE/test-inputs/xaif_argumentation.json
new file mode 100644
index 0000000..06bf0ad
--- /dev/null
+++ b/tests/api-requests/BERT TE/test-inputs/xaif_argumentation.json
@@ -0,0 +1,40 @@
+{
+ "AIF": {
+ "descriptorfulfillments": null,
+ "edges": [
+ { "edgeID": 0, "fromID": 0, "toID": 3 },
+ { "edgeID": 1, "fromID": 3, "toID": 4 },
+ { "edgeID": 2, "fromID": 1, "toID": 5 },
+ { "edgeID": 3, "fromID": 5, "toID": 6 },
+ { "edgeID": 4, "fromID": 2, "toID": 7 },
+ { "edgeID": 5, "fromID": 7, "toID": 8 }
+ ],
+ "locutions": [
+ { "nodeID": 0, "personID": 0 },
+ { "nodeID": 1, "personID": 1 },
+ { "nodeID": 2, "personID": 2 }
+ ],
+ "nodes": [
+ { "nodeID": 0, "text": "Renewable energy is essential for reducing carbon emissions.", "type": "L" },
+ { "nodeID": 1, "text": "Solar and wind power significantly reduce greenhouse gas output.", "type": "L" },
+ { "nodeID": 2, "text": "Fossil fuels are the only reliable source of energy for modern economies.", "type": "L" },
+ { "nodeID": 3, "text": "Default Illocuting", "type": "YA" },
+ { "nodeID": 4, "text": "Renewable energy is essential for reducing carbon emissions.", "type": "I" },
+ { "nodeID": 5, "text": "Default Illocuting", "type": "YA" },
+ { "nodeID": 6, "text": "Solar and wind power significantly reduce greenhouse gas output.", "type": "I" },
+ { "nodeID": 7, "text": "Default Illocuting", "type": "YA" },
+ { "nodeID": 8, "text": "Fossil fuels are the only reliable source of energy for modern economies.", "type": "I" }
+ ],
+ "participants": [
+ { "firstname": "Speaker", "participantID": 0, "surname": "A" },
+ { "firstname": "Speaker", "participantID": 1, "surname": "B" },
+ { "firstname": "Speaker", "participantID": 2, "surname": "C" }
+ ],
+ "schemefulfillments": null
+ },
+ "dialog": true,
+ "ova": [],
+ "text": {
+ "txt": " Speaker A Renewable energy is essential for reducing carbon emissions..
Speaker B Solar and wind power significantly reduce greenhouse gas output..
Speaker C Fossil fuels are the only reliable source of energy for modern economies..
"
+ }
+}
\ No newline at end of file
diff --git a/tests/api-requests/BERT TE/test-inputs/xaif_sample.json b/tests/api-requests/BERT TE/test-inputs/xaif_sample.json
new file mode 100644
index 0000000..f7841ca
--- /dev/null
+++ b/tests/api-requests/BERT TE/test-inputs/xaif_sample.json
@@ -0,0 +1,116 @@
+{
+ "AIF": {
+ "descriptorfulfillments": null,
+ "edges": [
+ {
+ "edgeID": 0,
+ "fromID": 0,
+ "toID": 4
+ },
+ {
+ "edgeID": 1,
+ "fromID": 4,
+ "toID": 3
+ },
+ {
+ "edgeID": 2,
+ "fromID": 1,
+ "toID": 6
+ },
+ {
+ "edgeID": 3,
+ "fromID": 6,
+ "toID": 5
+ },
+ {
+ "edgeID": 4,
+ "fromID": 2,
+ "toID": 8
+ },
+ {
+ "edgeID": 5,
+ "fromID": 8,
+ "toID": 7
+ }
+ ],
+ "locutions": [
+ {
+ "nodeID": 0,
+ "personID": 0
+ },
+ {
+ "nodeID": 1,
+ "personID": 1
+ },
+ {
+ "nodeID": 2,
+ "personID": 2
+ }
+ ],
+ "nodes": [
+ {
+ "nodeID": 0,
+ "text": "disagreements between party members are entirely to be expected.",
+ "type": "L"
+ },
+ {
+ "nodeID": 1,
+ "text": "the SNP has disagreements.",
+ "type": "L"
+ },
+ {
+ "nodeID": 2,
+ "text": "it's not uncommon for there to be disagreements between party members.",
+ "type": "L"
+ },
+ {
+ "nodeID": 3,
+ "text": "disagreements between party members are entirely to be expected.",
+ "type": "I"
+ },
+ {
+ "nodeID": 4,
+ "text": "Default Illocuting",
+ "type": "YA"
+ },
+ {
+ "nodeID": 5,
+ "text": "the SNP has disagreements.",
+ "type": "I"
+ },
+ {
+ "nodeID": 6,
+ "text": "Default Illocuting",
+ "type": "YA"
+ },
+ {
+ "nodeID": 7,
+ "text": "it's not uncommon for there to be disagreements between party members.",
+ "type": "I"
+ },
+ {
+ "nodeID": 8,
+ "text": "Default Illocuting",
+ "type": "YA"
+ }
+ ],
+ "participants": [
+ {
+ "firstname": "Speaker",
+ "participantID": 0,
+ "surname": "1"
+ },
+ {
+ "firstname": "Speaker",
+ "participantID": 1,
+ "surname": "2"
+ }
+ ],
+ "schemefulfillments": null
+ },
+ "dialog": true,
+ "ova": [],
+ "text": {
+ "txt": " Speaker 1 disagreements between party members are entirely to be expected..
Speaker 2 the SNP has disagreements..
Speaker 1 it's not uncommon for there to be disagreements between party members. .
"
+ }
+}
\ No newline at end of file