DisTraceAI is an automated, end-to-end pipeline designed to detect and analyze coordinated disinformation campaigns in multilingual online content. It transforms raw text from news articles and social media into a hierarchical, structured intelligence framework that identifies how disinformation spreads across sources, platforms, and time.
2
stars
26
commits
HTML
primary language
Jun 30, 2026
updated
This repository contains the materials that allow reproducing the work introduced
in the paper A Hierarchical Framework for the Detection of Coordinated Disinformation Campaigns.
DisTraceAI is an automated, end-to-end pipeline that transforms raw multilingual news articles into structured intelligence about coordinated disinformation campaigns. It operates at four hierarchical levels: Articles → Sub-Narratives → Narratives → Campaigns, with each level building on the one below and persisting independently to a JSON-based knowledge base.
conda init bash, then restart your shell).data/ (MultiCW, FakeCTI, PolyNarrative, MultiClaim,
EUvsDisinfo) — not shipped in the repo.From a shell where conda works (e.g. base), run:
bash setup.sh
It does the whole install end-to-end: creates and activates the distrace env,
installs vLLM + the pipeline requirements + NodeRAG (patched local clone), removes
the kernels package, and finishes with a verification pass that reinstalls
anything missing and prints an OK/MISSING line per critical package. It does NOT
abort partway on a single failed install — every stage runs and gaps are reported
at the end (this is deliberate: a network blip on one wheel must not leave the
rest uninstalled). If the final report shows NodeRAG still MISSING, re-run just
that step in the active env:
conda activate distrace
bash modules/noderag/install_noderag_local.sh
If you ever accidentally installed into system Python, run (with NO env active):
bash cleanup_system_python.sh
setup.sh also installs a conda CUDA toolkit (13.3) and, if the system driver's
libcuda.so is present under /usr/lib/x86_64-linux-gnu, points the loader and
linker at it (needed for FlashInfer). Those exports apply only to the shell that
ran setup — the SLURM step scripts re-export them, but for interactive runs add
this to your shell rc:
export LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH
Models download from HuggingFace on first use. Unauthenticated downloads are rate-limited and can drop mid-transfer (you'll see "Server disconnected" / "Can't load the configuration of "). Export a token to avoid this:
export HF_TOKEN=hf_... # also add to your shell rc
The loader retries transient Hub disconnects automatically, but a token removes the rate limit that causes them. If a download was interrupted, a stale partial cache can wedge it — clearing it forces a clean re-download:
rm -rf ~/.cache/huggingface/hub
conda activate distrace
python main.py # interactive menu
Menu: 1 Claim detection · 2 Canonization · 3 Veracity · 4 Sub-narratives · 5 Narratives · 6 Campaigns. Steps 1–5 have Evaluation / Generate; step 6 has Verify / Deep verify / Evaluation / Generate Dataset.
All generators load bf16 weights straight from HuggingFace on first use (cached afterwards).
Each step has a script that activates the env and sets the needed env vars:
sbatch gen_claim_canonization.sh # on SLURM
bash gen_claim_canonization.sh # or directly
Scripts: gen_/eval_ claim_detection, claim_canonization, claim_extract, sub_narratives, narratives, campaigns, claim_veracity; gen_dataset; eval_narratives_benchmark.
TypeError: sequence item 0: expected str instance, dict found — the
in-process LLM adapter must return a plain string for free-text steps
(attribute/summary generation) and a dict only when NodeRAG passes a
response_format. If a model's attribute text starts with {/[ it must NOT
be auto-parsed to a dict, or NodeRAG stores it as raw_context and hashing it
via genid(["".join([...])]) fails. This is handled in
core/hierarchy/noderag.py:_run (structured output only when requested).flashinfer/jit with "Ninja build failed" / nvcc
CalledProcessError — vLLM's sampler tries to JIT-compile a FlashInfer CUDA
kernel via nvcc/ninja, and a fresh env's toolchain can fail that build. The
pipeline disables it by default (VLLM_USE_FLASHINFER_SAMPLER=0 → PyTorch-native
sampling, set in main.py and the step scripts), so you shouldn't hit this. If you
do (e.g. running vLLM directly), export VLLM_USE_FLASHINFER_SAMPLER=0 and
rm -rf ~/.cache/flashinfer. There's no accuracy cost — it's only a sampler
speed optimization, irrelevant for these small eval workloads.pip install -r requirements.txt aborted partway during
setup, so packages after the failing line never installed, and the NodeRAG step
never ran. Re-run setup in the active env, or just the two install steps:
pip install -r requirements.txt then
bash modules/noderag/install_noderag_local.sh. The NodeRAG script now verifies
import NodeRAG at the end and fails loudly if it didn't land. NodeRAG is NOT a
PyPI package here — it's a patched local clone, so don't pip install NodeRAG.hnswlib. The install script handles this (strips the bogus pin, installs
hnswlib/pandas/scipy/backoff, then pip install -e . --no-deps). To
unblock an already-cloned tree by hand:
python -m pip install hnswlib pandas scipy backoff then
cd modules/noderag/NodeRAG_local && python -m pip install -e . --no-deps.
If import NodeRAG then names another missing module, pip install it (the
package under-declares a few deps) and retry.gpu_memory_utilization
is a fraction of TOTAL VRAM, so the two must sum to ≤ 1.0. Defaults are
embedder 0.30 + generator 0.60 = 0.90 (10% buffer). If you still hit this,
lower one: DISTRACE_GEN_GPU_UTIL=0.5 or DISTRACE_EMBED_GPU_UTIL=0.2. On a
single-model step with no embedder resident you can raise the generator back up
with DISTRACE_GEN_GPU_UTIL=0.9. (Context length is not the cause here — this
fails at weight reservation, before the KV cache.)HF_TOKEN, retry; if it persists, rm -rf ~/.cache/huggingface/hub and retry.rm -rf ~/.cache/flashinfer, and make sure
LD_LIBRARY_PATH includes the dir holding libcuda.so (usually
/usr/lib/x86_64-linux-gnu).These are exported by main.py and the step scripts; you don't need to set them manually, but they explain the moving parts of the current dependency stack:
VLLM_DEEP_GEMM_WARMUP=skip — vLLM 0.22 Hopper FP8-warmup crash (issue #41849).DISABLE_KERNEL_MAPPING=1 — transformers 5.12 + kernels 0.15 import skew
(belt-and-braces; setup.sh also uninstalls the kernels package, the real fix).LD_LIBRARY_PATH (libcuda dir) — FlashInfer needs the driver stub at JIT time.HF_HUB_DOWNLOAD_TIMEOUT, HF_HUB_ENABLE_HF_TRANSFER — more robust Hub
downloads (the latter only if hf_transfer is installed).HF_TOKEN — set this yourself to lift HF rate limits (recommended).26 commits
HTML
96.5%
Python
3.3%
DisTraceAI is an automated, end-to-end pipeline designed to detect and analyze coordinated disinformation campaigns in multilingual online content. It transforms raw text from news articles and social media into a hierarchical, structured intelligence framework that identifies how disinformation spreads across sources, platforms, and time.
2
stars
26
commits
HTML
primary language
Jun 30, 2026
updated
This repository contains the materials that allow reproducing the work introduced
in the paper A Hierarchical Framework for the Detection of Coordinated Disinformation Campaigns.
DisTraceAI is an automated, end-to-end pipeline that transforms raw multilingual news articles into structured intelligence about coordinated disinformation campaigns. It operates at four hierarchical levels: Articles → Sub-Narratives → Narratives → Campaigns, with each level building on the one below and persisting independently to a JSON-based knowledge base.
conda init bash, then restart your shell).data/ (MultiCW, FakeCTI, PolyNarrative, MultiClaim,
EUvsDisinfo) — not shipped in the repo.From a shell where conda works (e.g. base), run:
bash setup.sh
It does the whole install end-to-end: creates and activates the distrace env,
installs vLLM + the pipeline requirements + NodeRAG (patched local clone), removes
the kernels package, and finishes with a verification pass that reinstalls
anything missing and prints an OK/MISSING line per critical package. It does NOT
abort partway on a single failed install — every stage runs and gaps are reported
at the end (this is deliberate: a network blip on one wheel must not leave the
rest uninstalled). If the final report shows NodeRAG still MISSING, re-run just
that step in the active env:
conda activate distrace
bash modules/noderag/install_noderag_local.sh
If you ever accidentally installed into system Python, run (with NO env active):
bash cleanup_system_python.sh
setup.sh also installs a conda CUDA toolkit (13.3) and, if the system driver's
libcuda.so is present under /usr/lib/x86_64-linux-gnu, points the loader and
linker at it (needed for FlashInfer). Those exports apply only to the shell that
ran setup — the SLURM step scripts re-export them, but for interactive runs add
this to your shell rc:
export LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH
Models download from HuggingFace on first use. Unauthenticated downloads are rate-limited and can drop mid-transfer (you'll see "Server disconnected" / "Can't load the configuration of "). Export a token to avoid this:
export HF_TOKEN=hf_... # also add to your shell rc
The loader retries transient Hub disconnects automatically, but a token removes the rate limit that causes them. If a download was interrupted, a stale partial cache can wedge it — clearing it forces a clean re-download:
rm -rf ~/.cache/huggingface/hub
conda activate distrace
python main.py # interactive menu
Menu: 1 Claim detection · 2 Canonization · 3 Veracity · 4 Sub-narratives · 5 Narratives · 6 Campaigns. Steps 1–5 have Evaluation / Generate; step 6 has Verify / Deep verify / Evaluation / Generate Dataset.
All generators load bf16 weights straight from HuggingFace on first use (cached afterwards).
Each step has a script that activates the env and sets the needed env vars:
sbatch gen_claim_canonization.sh # on SLURM
bash gen_claim_canonization.sh # or directly
Scripts: gen_/eval_ claim_detection, claim_canonization, claim_extract, sub_narratives, narratives, campaigns, claim_veracity; gen_dataset; eval_narratives_benchmark.
TypeError: sequence item 0: expected str instance, dict found — the
in-process LLM adapter must return a plain string for free-text steps
(attribute/summary generation) and a dict only when NodeRAG passes a
response_format. If a model's attribute text starts with {/[ it must NOT
be auto-parsed to a dict, or NodeRAG stores it as raw_context and hashing it
via genid(["".join([...])]) fails. This is handled in
core/hierarchy/noderag.py:_run (structured output only when requested).flashinfer/jit with "Ninja build failed" / nvcc
CalledProcessError — vLLM's sampler tries to JIT-compile a FlashInfer CUDA
kernel via nvcc/ninja, and a fresh env's toolchain can fail that build. The
pipeline disables it by default (VLLM_USE_FLASHINFER_SAMPLER=0 → PyTorch-native
sampling, set in main.py and the step scripts), so you shouldn't hit this. If you
do (e.g. running vLLM directly), export VLLM_USE_FLASHINFER_SAMPLER=0 and
rm -rf ~/.cache/flashinfer. There's no accuracy cost — it's only a sampler
speed optimization, irrelevant for these small eval workloads.pip install -r requirements.txt aborted partway during
setup, so packages after the failing line never installed, and the NodeRAG step
never ran. Re-run setup in the active env, or just the two install steps:
pip install -r requirements.txt then
bash modules/noderag/install_noderag_local.sh. The NodeRAG script now verifies
import NodeRAG at the end and fails loudly if it didn't land. NodeRAG is NOT a
PyPI package here — it's a patched local clone, so don't pip install NodeRAG.hnswlib. The install script handles this (strips the bogus pin, installs
hnswlib/pandas/scipy/backoff, then pip install -e . --no-deps). To
unblock an already-cloned tree by hand:
python -m pip install hnswlib pandas scipy backoff then
cd modules/noderag/NodeRAG_local && python -m pip install -e . --no-deps.
If import NodeRAG then names another missing module, pip install it (the
package under-declares a few deps) and retry.gpu_memory_utilization
is a fraction of TOTAL VRAM, so the two must sum to ≤ 1.0. Defaults are
embedder 0.30 + generator 0.60 = 0.90 (10% buffer). If you still hit this,
lower one: DISTRACE_GEN_GPU_UTIL=0.5 or DISTRACE_EMBED_GPU_UTIL=0.2. On a
single-model step with no embedder resident you can raise the generator back up
with DISTRACE_GEN_GPU_UTIL=0.9. (Context length is not the cause here — this
fails at weight reservation, before the KV cache.)HF_TOKEN, retry; if it persists, rm -rf ~/.cache/huggingface/hub and retry.rm -rf ~/.cache/flashinfer, and make sure
LD_LIBRARY_PATH includes the dir holding libcuda.so (usually
/usr/lib/x86_64-linux-gnu).These are exported by main.py and the step scripts; you don't need to set them manually, but they explain the moving parts of the current dependency stack:
VLLM_DEEP_GEMM_WARMUP=skip — vLLM 0.22 Hopper FP8-warmup crash (issue #41849).DISABLE_KERNEL_MAPPING=1 — transformers 5.12 + kernels 0.15 import skew
(belt-and-braces; setup.sh also uninstalls the kernels package, the real fix).LD_LIBRARY_PATH (libcuda dir) — FlashInfer needs the driver stub at JIT time.HF_HUB_DOWNLOAD_TIMEOUT, HF_HUB_ENABLE_HF_TRANSFER — more robust Hub
downloads (the latter only if hf_transfer is installed).HF_TOKEN — set this yourself to lift HF rate limits (recommended).26 commits
HTML
96.5%
Python
3.3%