diff --git a/Dockerfile b/Dockerfile index ea07df4..e045d95 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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"] \ No newline at end of file +CMD ["gunicorn", "--workers", "1", "--bind", "0.0.0.0:5001", "--timeout", "300", "--access-logfile", "-", "--error-logfile", "-", "app:application"] diff --git a/app/routes.py b/app/routes.py index 90baa26..ca2bffc 100644 --- a/app/routes.py +++ b/app/routes.py @@ -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': diff --git a/boot.sh b/boot.sh deleted file mode 100644 index 1272d00..0000000 --- a/boot.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -source venv/bin/activate -exec gunicorn -b :5001 --access-logfile - --error-logfile - app --timeout 300 diff --git a/docker-compose.yml b/docker-compose.yml index 36551ef..c5d96cc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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" diff --git a/requirements.txt b/requirements.txt index e206fe2..dd9d992 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,102 @@ -Flask -requests -numpy -transformers -torch -datasets -amf-fast-inference==0.0.3 -xaif_eval \ No newline at end of file +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 diff --git a/tests/api-requests/AMF ARI/Mixed Nodes Preserved.bru b/tests/api-requests/AMF ARI/Mixed Nodes Preserved.bru new file mode 100644 index 0000000..fe7690a --- /dev/null +++ b/tests/api-requests/AMF ARI/Mixed Nodes Preserved.bru @@ -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); + }); + }); + }); +} diff --git a/tests/api-requests/AMF ARI/Single Proposition.bru b/tests/api-requests/AMF ARI/Single Proposition.bru new file mode 100644 index 0000000..38f32f6 --- /dev/null +++ b/tests/api-requests/AMF ARI/Single Proposition.bru @@ -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); + }); +} diff --git a/tests/api-requests/AMF ARI/Window Size.bru b/tests/api-requests/AMF ARI/Window Size.bru new file mode 100644 index 0000000..0a4814a --- /dev/null +++ b/tests/api-requests/AMF ARI/Window Size.bru @@ -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); + }); + }); +} diff --git a/tests/api-requests/AMF ARI/XAIF Full.bru b/tests/api-requests/AMF ARI/XAIF Full.bru new file mode 100644 index 0000000..c3931a8 --- /dev/null +++ b/tests/api-requests/AMF ARI/XAIF Full.bru @@ -0,0 +1,60 @@ +meta { + name: XAIF Full + type: http + seq: 3 +} + +post { + url: {{baseUrl}}/ + 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("Response should contain original 12 I-nodes", function () { + const data = res.getBody(); + const iNodes = data.AIF.nodes.filter(n => n.type === "I"); + expect(iNodes.length).to.equal(12); + }); + + test("Response should contain relation nodes (RA, CA, or MA)", 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.be.at.least(1); + }); + + 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); + }); + }); +} diff --git a/tests/api-requests/AMF ARI/XAIF Two Propositions.bru b/tests/api-requests/AMF ARI/XAIF Two Propositions.bru new file mode 100644 index 0000000..cef653c --- /dev/null +++ b/tests/api-requests/AMF ARI/XAIF Two Propositions.bru @@ -0,0 +1,38 @@ +meta { + name: XAIF Two Propositions + type: http + seq: 2 +} + +post { + url: {{baseUrl}}/ + body: multipartForm + auth: inherit +} + +headers { + Content-Type: multipart/form-data +} + +body:multipart-form { + file: @file(test-inputs/xaif_two_propositions.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("Response should contain original 2 I-nodes", function () { + const data = res.getBody(); + const iNodes = data.AIF.nodes.filter(n => n.type === "I"); + expect(iNodes.length).to.equal(2); + }); +} diff --git a/tests/api-requests/AMF ARI/bruno.json b/tests/api-requests/AMF ARI/bruno.json new file mode 100644 index 0000000..09bd237 --- /dev/null +++ b/tests/api-requests/AMF ARI/bruno.json @@ -0,0 +1,9 @@ +{ + "version": "1", + "name": "AMF ARI", + "type": "collection", + "ignore": [ + "node_modules", + ".git" + ] +} diff --git a/tests/api-requests/AMF ARI/environments/(1) local.bru b/tests/api-requests/AMF ARI/environments/(1) local.bru new file mode 100644 index 0000000..169ef5b --- /dev/null +++ b/tests/api-requests/AMF ARI/environments/(1) local.bru @@ -0,0 +1,3 @@ +vars { + baseUrl: http://localhost:5001 +} diff --git a/tests/api-requests/AMF ARI/environments/(2) staging.bru b/tests/api-requests/AMF ARI/environments/(2) staging.bru new file mode 100644 index 0000000..62979e5 --- /dev/null +++ b/tests/api-requests/AMF ARI/environments/(2) staging.bru @@ -0,0 +1,3 @@ +vars { + baseUrl: http://amf-ari.amfws.staging.arg.tech +} diff --git a/tests/api-requests/AMF ARI/environments/(3) production.bru b/tests/api-requests/AMF ARI/environments/(3) production.bru new file mode 100644 index 0000000..2b8e67d --- /dev/null +++ b/tests/api-requests/AMF ARI/environments/(3) production.bru @@ -0,0 +1,3 @@ +vars { + baseUrl: http://amf-ari.amfws.arg.tech +} diff --git a/tests/api-requests/AMF ARI/test-inputs/xaif_full.json b/tests/api-requests/AMF ARI/test-inputs/xaif_full.json new file mode 100644 index 0000000..3e427eb --- /dev/null +++ b/tests/api-requests/AMF ARI/test-inputs/xaif_full.json @@ -0,0 +1,245 @@ +{ + "AIF": { + "nodes": [ + { + "nodeID": "2_166178046893607143", + "text": "Dungeons and Dragons and its imitators are right out of the pit of hell", + "type": "I" + }, + { + "nodeID": "3_166178046893607143", + "text": "No Christian or sane, decent individual of whatever faith really should have anything to do with Dungeons and Dragons and its imitators", + "type": "I" + }, + { + "nodeID": "6_166178046893607143", + "text": "It's just a game!", + "type": "I" + }, + { + "nodeID": "7_166178046893607143", + "text": "the person who thinks they can mess with Dungeons and Dragons without getting burnt is whistling in the dark", + "type": "I" + }, + { + "nodeID": "8_166178046893607143", + "text": "people who think they can play around with crack or pre-marital sex and not get burned by death, AIDS or pregnancy are whistling in the dark", + "type": "I" + }, + { + "nodeID": "13_166178046893607143", + "text": "Dungeons and Dragons is essentially a feeding program for occultism and witchcraft", + "type": "I" + }, + { + "nodeID": "16_166178046893607143", + "text": "Dungeons and Dragons violates the commandment of I Ths. 5:22 \"Abstain from all appearance of evil.\"", + "type": "I" + }, + { + "nodeID": "19_166178046893607143", + "text": "Much of the art, figurines, and writing within Dungeons and Dragons certainly appears evil to say the least of it", + "type": "I" + }, + { + "nodeID": "22_166178046893607143", + "text": "the materials themselves contain magical rituals", + "type": "I" + }, + { + "nodeID": "25_166178046893607143", + "text": "for the most part, the rituals are certainly authentic", + "type": "I" + }, + { + "nodeID": "34_166178046893607143", + "text": "If a person \"innocently\" works an authentic ritual that conjures up a demon, or curses someone, thinking that they are only playing a game, the ritual will still have efficacy", + "type": "I" + }, + { + "nodeID": "37_166178046893607143", + "text": "If you play at shooting your friend in the head with what you think is an unloaded pistol and don't know a shell is in the chamber, your friend is not any less dead because you were playing", + "type": "I" + } + ], + "edges": [ + ], + "schemefulfillments": [ + { + "nodeID": "2_166178046893607143", + "schemeID": "0" + }, + { + "nodeID": "3_166178046893607143", + "schemeID": "0" + }, + { + "nodeID": "5_166178046893607143", + "schemeID": "237" + }, + { + "nodeID": "6_166178046893607143", + "schemeID": "0" + }, + { + "nodeID": "7_166178046893607143", + "schemeID": "0" + }, + { + "nodeID": "8_166178046893607143", + "schemeID": "0" + }, + { + "nodeID": "10_166178046893607143", + "schemeID": "1" + }, + { + "nodeID": "12_166178046893607143", + "schemeID": "71" + }, + { + "nodeID": "13_166178046893607143", + "schemeID": "0" + }, + { + "nodeID": "15_166178046893607143", + "schemeID": "31" + }, + { + "nodeID": "16_166178046893607143", + "schemeID": "0" + }, + { + "nodeID": "18_166178046893607143", + "schemeID": "30" + }, + { + "nodeID": "19_166178046893607143", + "schemeID": "0" + }, + { + "nodeID": "21_166178046893607143", + "schemeID": "30" + }, + { + "nodeID": "22_166178046893607143", + "schemeID": "0" + }, + { + "nodeID": "24_166178046893607143", + "schemeID": "30" + }, + { + "nodeID": "25_166178046893607143", + "schemeID": "0" + }, + { + "nodeID": "27_166178046893607143", + "schemeID": "15" + }, + { + "nodeID": "29_166178046893607143", + "schemeID": "71" + }, + { + "nodeID": "31_166178046893607143", + "schemeID": "71" + }, + { + "nodeID": "33_166178046893607143", + "schemeID": "71" + }, + { + "nodeID": "34_166178046893607143", + "schemeID": "0" + }, + { + "nodeID": "36_166178046893607143", + "schemeID": "7" + }, + { + "nodeID": "37_166178046893607143", + "schemeID": "0" + }, + { + "nodeID": "39_166178046893607143", + "schemeID": "1" + } + ], + "participants": [ + { + "participantID": 1, + "firstname": "Participant", + "surname": "Rachel" + }, + { + "participantID": 2, + "firstname": "Participant", + "surname": "Carolyn" + }, + { + "participantID": 3, + "firstname": "Participant", + "surname": "Mo" + }, + { + "participantID": 4, + "firstname": "Participant", + "surname": "Zoe" + }, + { + "participantID": 5, + "firstname": "Participant", + "surname": "Claire" + }, + { + "participantID": 6, + "firstname": "Participant", + "surname": "Lee" + }, + { + "participantID": 7, + "firstname": "Participant", + "surname": "Sally" + }, + { + "participantID": 8, + "firstname": "Participant", + "surname": "Kalbir" + }, + { + "participantID": 9, + "firstname": "Participant", + "surname": "Alice" + }, + { + "participantID": 10, + "firstname": "Anne", + "surname": "Robinson" + }, + { + "participantID": 11, + "firstname": "Diane", + "surname": "Munday" + }, + { + "participantID": 12, + "firstname": "Neena", + "surname": "Modi" + }, + { + "participantID": 13, + "firstname": "David", + "surname": "Steel" + }, + { + "participantID": 14, + "firstname": "Neil", + "surname": "Lyndon" + } + ], + "locutions": [], + "descriptorfulfillments": [], + "cqdescriptorfulfillments": [] + } +} \ No newline at end of file diff --git a/tests/api-requests/AMF ARI/test-inputs/xaif_mixed_nodes.json b/tests/api-requests/AMF ARI/test-inputs/xaif_mixed_nodes.json new file mode 100644 index 0000000..d270681 --- /dev/null +++ b/tests/api-requests/AMF ARI/test-inputs/xaif_mixed_nodes.json @@ -0,0 +1,83 @@ +{ + "AIF": { + "nodes": [ + { + "nodeID": "1", + "text": "We should ban plastic bags", + "type": "L" + }, + { + "nodeID": "2", + "text": "We should ban plastic bags", + "type": "I" + }, + { + "nodeID": "3", + "text": "Default Asserting", + "type": "YA" + }, + { + "nodeID": "4", + "text": "Plastic bags cause enormous environmental damage to marine ecosystems", + "type": "I" + }, + { + "nodeID": "5", + "text": "Plastic bags cause enormous environmental damage to marine ecosystems", + "type": "L" + }, + { + "nodeID": "6", + "text": "Default Asserting", + "type": "YA" + }, + { + "nodeID": "7", + "text": "Banning plastic bags would be inconvenient for consumers and hurt small businesses", + "type": "I" + } + ], + "edges": [ + { + "edgeID": 0, + "fromID": "1", + "toID": "3" + }, + { + "edgeID": 1, + "fromID": "3", + "toID": "2" + }, + { + "edgeID": 2, + "fromID": "5", + "toID": "6" + }, + { + "edgeID": 3, + "fromID": "6", + "toID": "4" + } + ], + "schemefulfillments": [], + "participants": [ + { + "participantID": 1, + "firstname": "Speaker", + "surname": "A" + } + ], + "locutions": [ + { + "personID": 1, + "nodeID": "1" + }, + { + "personID": 1, + "nodeID": "5" + } + ], + "descriptorfulfillments": [], + "cqdescriptorfulfillments": [] + } +} diff --git a/tests/api-requests/AMF ARI/test-inputs/xaif_single_proposition.json b/tests/api-requests/AMF ARI/test-inputs/xaif_single_proposition.json new file mode 100644 index 0000000..1f6b3fc --- /dev/null +++ b/tests/api-requests/AMF ARI/test-inputs/xaif_single_proposition.json @@ -0,0 +1,17 @@ +{ + "AIF": { + "nodes": [ + { + "nodeID": "1", + "text": "Climate change is the greatest threat facing humanity today", + "type": "I" + } + ], + "edges": [], + "schemefulfillments": [], + "participants": [], + "locutions": [], + "descriptorfulfillments": [], + "cqdescriptorfulfillments": [] + } +} diff --git a/tests/api-requests/AMF ARI/test-inputs/xaif_two_propositions.json b/tests/api-requests/AMF ARI/test-inputs/xaif_two_propositions.json new file mode 100644 index 0000000..e1c69bd --- /dev/null +++ b/tests/api-requests/AMF ARI/test-inputs/xaif_two_propositions.json @@ -0,0 +1,22 @@ +{ + "AIF": { + "nodes": [ + { + "nodeID": "1", + "text": "Dungeons and Dragons and its imitators are right out of the pit of hell", + "type": "I" + }, + { + "nodeID": "2", + "text": "No Christian or sane, decent individual of whatever faith really should have anything to do with Dungeons and Dragons and its imitators", + "type": "I" + } + ], + "edges": [], + "schemefulfillments": [], + "participants": [], + "locutions": [], + "descriptorfulfillments": [], + "cqdescriptorfulfillments": [] + } +}