Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 8 additions & 17 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,21 +1,12 @@
FROM python:3.11
FROM python:3.12-slim@sha256:9e01bf1ae5db7649a236da7be1e94ffbbbdd7a93f867dd0d8d5720d9e1f89fab

RUN mkdir -p /home/amf_ari
WORKDIR /home/amf_ari

RUN pip install --upgrade pip

ADD requirements.txt .
RUN pip install -r requirements.txt
RUN pip install gunicorn

ADD app app
ADD boot.sh ./
RUN chmod +x boot.sh


ENV FLASK_APP app
WORKDIR /app
COPY requirements.txt .

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 . .

EXPOSE 5001
ENTRYPOINT ["./boot.sh"]
CMD ["gunicorn", "--workers", "1", "--bind", "0.0.0.0:5001", "--timeout", "300", "--access-logfile", "-", "--error-logfile", "-", "app:application"]
2 changes: 1 addition & 1 deletion app/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def amf_ari():
if window_size is None:
response = relation_identification(content, window_size=-1)
else:
response = relation_identification(content, window_size=window_size)
response = relation_identification(content, window_size=int(window_size))
print(response)
return jsonify(response)
elif request.method == 'GET':
Expand Down
3 changes: 0 additions & 3 deletions boot.sh

This file was deleted.

12 changes: 5 additions & 7 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
version: '3'

services:
amf_ari:
container_name: amf_ari
build: .
ports:
- "5001:5001"
amf_ari:
container_name: amf_ari
build: .
ports:
- "5001:5001"
110 changes: 102 additions & 8 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,102 @@
Flask
requests
numpy
transformers
torch
datasets
amf-fast-inference==0.0.3
xaif_eval
about-time==4.2.1
aiohappyeyeballs==2.6.1
aiohttp==3.13.3
aiosignal==1.4.0
alive-progress==3.3.0
amf_fast_inference==0.0.3
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
contourpy==1.3.3
cycler==0.12.1
datasets==4.5.0
Deprecated==1.3.1
dill==0.4.0
filelock==3.24.3
Flask==3.1.3
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.3.0
httpcore==1.0.9
httpx==0.28.1
huggingface_hub==0.36.2
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
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
openvino==2026.0.0
openvino-telemetry==2025.2.0
optimum==2.1.0
optimum-intel==1.27.0
optimum-onnx==0.1.0
packaging==26.0
pandas==2.3.3
pillow==12.1.1
platformdirs==4.9.2
propcache==0.4.1
protobuf==6.33.5
psutil==7.2.2
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
setuptools==82.0.0
six==1.17.0
sympy==1.14.0
tabulate==0.9.0
threadpoolctl==3.6.0
tokenizers==0.22.2
torch==2.10.0
tqdm==4.67.3
transformers==4.57.6
typing_extensions==4.15.0
tzdata==2025.3
urllib3==2.6.3
Werkzeug==3.1.6
wrapt==2.1.1
xaif_eval==0.2.2
xxhash==3.6.0
yarl==1.22.0
84 changes: 84 additions & 0 deletions tests/api-requests/AMF ARI/Mixed Nodes Preserved.bru
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
meta {
name: Mixed Nodes Preserved
type: http
seq: 6
}

post {
url: {{baseUrl}}/
body: multipartForm
auth: inherit
}

headers {
Content-Type: multipart/form-data
}

body:multipart-form {
file: @file(test-inputs/xaif_mixed_nodes.json)
}

tests {
test("Response status should be 200", function () {
expect(res.status).to.equal(200);
});

test("Response should be valid xAIF JSON", function () {
const data = res.getBody();
expect(data).to.have.property("AIF");
expect(data.AIF).to.have.property("nodes");
expect(data.AIF).to.have.property("edges");
});

test("All 3 original I-nodes should be preserved", function () {
const data = res.getBody();
const iNodes = data.AIF.nodes.filter(n => n.type === "I");
expect(iNodes.length).to.equal(3);
});

test("Non-I nodes (L, YA) should be preserved", function () {
const data = res.getBody();
const lNodes = data.AIF.nodes.filter(n => n.type === "L");
const yaNodes = data.AIF.nodes.filter(n => n.type === "YA");
expect(lNodes.length).to.equal(2);
expect(yaNodes.length).to.equal(2);
});

test("Original edges should be preserved", function () {
const data = res.getBody();
// The 4 original edges should still be present
const originalEdgeCount = 4;
expect(data.AIF.edges.length).to.be.at.least(originalEdgeCount);
});

test("Participants should be preserved", function () {
const data = res.getBody();
expect(data.AIF.participants.length).to.equal(1);
expect(data.AIF.participants[0].firstname).to.equal("Speaker");
});

test("Locutions should be preserved", function () {
const data = res.getBody();
expect(data.AIF.locutions.length).to.equal(2);
});

test("Any new relation nodes should only connect I-nodes", function () {
const data = res.getBody();
const iNodeIDs = data.AIF.nodes
.filter(n => n.type === "I")
.map(n => n.nodeID);
const relationNodeIDs = data.AIF.nodes
.filter(n => n.type === "RA" || n.type === "CA" || n.type === "MA")
.map(n => n.nodeID);

relationNodeIDs.forEach(relID => {
const relatedEdges = data.AIF.edges.filter(
e => e.fromID === relID || e.toID === relID
);
relatedEdges.forEach(edge => {
const otherID = edge.fromID === relID ? edge.toID : edge.fromID;
expect(iNodeIDs).to.include(otherID);
});
});
});
}
52 changes: 52 additions & 0 deletions tests/api-requests/AMF ARI/Single Proposition.bru
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
meta {
name: Single Proposition
type: http
seq: 4
}

post {
url: {{baseUrl}}/
body: multipartForm
auth: inherit
}

headers {
Content-Type: multipart/form-data
}

body:multipart-form {
file: @file(test-inputs/xaif_single_proposition.json)
}

tests {
test("Response status should be 200", function () {
expect(res.status).to.equal(200);
});

test("Response should be valid xAIF JSON", function () {
const data = res.getBody();
expect(data).to.have.property("AIF");
expect(data.AIF).to.have.property("nodes");
expect(data.AIF).to.have.property("edges");
});

test("Single I-node should be preserved", function () {
const data = res.getBody();
const iNodes = data.AIF.nodes.filter(n => n.type === "I");
expect(iNodes.length).to.equal(1);
expect(iNodes[0].text).to.equal("Climate change is the greatest threat facing humanity today");
});

test("No relation nodes should be added with only one proposition", function () {
const data = res.getBody();
const relationNodes = data.AIF.nodes.filter(n =>
n.type === "RA" || n.type === "CA" || n.type === "MA"
);
expect(relationNodes.length).to.equal(0);
});

test("No edges should be added", function () {
const data = res.getBody();
expect(data.AIF.edges.length).to.equal(0);
});
}
63 changes: 63 additions & 0 deletions tests/api-requests/AMF ARI/Window Size.bru
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
meta {
name: Window Size
type: http
seq: 5
}

post {
url: {{baseUrl}}/?window_size=2
body: multipartForm
auth: inherit
}

headers {
Content-Type: multipart/form-data
}

body:multipart-form {
file: @file(test-inputs/xaif_full.json)
}

tests {
test("Response status should be 200", function () {
expect(res.status).to.equal(200);
});

test("Response should be valid xAIF JSON", function () {
const data = res.getBody();
expect(data).to.have.property("AIF");
expect(data.AIF).to.have.property("nodes");
expect(data.AIF).to.have.property("edges");
});

test("All 12 original I-nodes should be preserved", function () {
const data = res.getBody();
const iNodes = data.AIF.nodes.filter(n => n.type === "I");
expect(iNodes.length).to.equal(12);
});

test("Window size should reduce number of relations compared to full analysis", function () {
const data = res.getBody();
const relationNodes = data.AIF.nodes.filter(n =>
n.type === "RA" || n.type === "CA" || n.type === "MA"
);
// With window_size=2, only adjacent pairs are compared (11 pairs for 12 nodes)
// vs full analysis which compares all 66 pairs — so fewer relations expected
// The full test produces ~20 relations; window_size=2 should produce far fewer
expect(relationNodes.length).to.be.below(20);
});

test("Each relation node should have exactly 2 edges", function () {
const data = res.getBody();
const relationNodeIDs = data.AIF.nodes
.filter(n => n.type === "RA" || n.type === "CA" || n.type === "MA")
.map(n => n.nodeID);

relationNodeIDs.forEach(relID => {
const relatedEdges = data.AIF.edges.filter(
e => e.fromID === relID || e.toID === relID
);
expect(relatedEdges.length).to.equal(2);
});
});
}
Loading
Loading