A modular, multi-dimensional evaluation framework based on the TrustLLM project protocol (D7.5) for assessing the quality of text generated by Large Language Models. All four evaluation prongs are fully implemented.
License: Apache 2.0 — see LICENSE
Project: TrustLLM (EU Grant #101135671)
Lead: Lars Bungum, NTNU
The system is built on a four-pronged evaluation approach, each analyzing a different dimension family of generated text:
| Prong | Name | Method | Status |
|---|---|---|---|
| 1 | LLM-as-Judge | Sentence embeddings (MiniLM) + toxicity classifier (toxic-bert) | ✅ Complete |
| 2 | Rule-Based | Lexicon/heuristic scoring, language-aware (9 languages) | ✅ Complete |
| 3 | Extraction-Based | spaCy triple extraction + offline Wikidata fact-checking | ✅ Complete |
| 4 | Intrinsic | Model log-probabilities from vLLM (perplexity, uncertainty, entropy) | ✅ Complete |
A FastAPI server (src/api.py) with a single-page HTML5 frontend
(frontend/index.html) supports interactive generation (vLLM or any
OpenAI-compatible remote API) and real-time evaluation with streaming output.
online-eval-base/
├── src/
│ ├── base.py # Base classes (DimensionScore, Evaluator, ProngResult)
│ ├── config.py # Language codes, Flesch coefficients, constants
│ ├── pipeline.py # EvaluationPipeline — orchestrates all prongs
│ ├── api.py # FastAPI server (generation, evaluation, streaming)
│ ├── vllm_client.py # Async vLLM/OpenAI-compatible HTTP client
│ ├── vllm_adapter.py # GenerationResult → Prong 4 IntrinsicData
│ ├── prongs/
│ │ ├── prong1_llm_as_judge.py # Prong 1 orchestrator
│ │ ├── prong1_dimensions.py # Relevance, Coherence, Reliability, Toxicity
│ │ ├── prong2_rule_based.py # Prong 2 orchestrator
│ │ ├── prong3_extraction.py # Prong 3 orchestrator
│ │ └── prong4_intrinsic.py # Prong 4 orchestrator + IntrinsicData
│ ├── dimensions/
│ │ ├── base_dimension.py # ScorableDimension base class
│ │ ├── prong2_dimensions.py # Fluency, Politeness, Emotional Intensity, Formality, Hedging
│ │ └── prong3_dimensions.py # Factual Accuracy, Consistency, Temporal, Comparative, Modality
│ ├── extraction/
│ │ └── claim_extractor.py # spaCy dependency-parsing claim extractor
│ ├── aggregation/
│ │ └── aggregator.py # Weighted prong/dimension aggregation
│ ├── models/
│ │ ├── embeddings.py # sentence-transformers wrapper (LRU cached)
│ │ └── toxicity_model.py # unitary/toxic-bert wrapper
│ ├── llm_backends/ # Pluggable backend abstraction (local vLLM / remote)
│ └── resources/
│ ├── wikidata_offline.py # Offline Wikidata interface (SQLite)
│ ├── lexicon_manager.py # Per-language YAML lexicon loader
│ └── lexicons/ # da, de, en, fo, is, nb, nn, nl, sv
├── scripts/
│ ├── build_wikidata_db.py # Build local Wikidata seed DB
│ ├── build_fever_wikidata.py # Build FEVER-coverage Wikidata DB
│ ├── extend_wikidata_db.py # Extend DB with entities from claim text
│ ├── eval_fever2.py # Prong 3 benchmark on FEVER 2.0
│ ├── filter_oracle_claims.py # Filter oracle claims for benchmarking
│ └── run_benchmark.py # Multi-model benchmarking script
├── tests/
│ ├── test_prong1.py
│ ├── test_prong2.py
│ ├── test_prong3.py
│ ├── test_prong4.py
│ ├── test_api.py
│ └── test_llm_backends.py
├── frontend/
│ ├── index.html # Single-page control panel
│ └── locales/ # UI translations (9 languages)
├── data/
│ └── wikidata_seed.db # Pre-built offline Wikidata seed
├── evaluation_results/ # FEVER 2.0 benchmark output
├── requirements.txt
├── main.py # CLI example — evaluates 4 sample texts
└── LICENSE # Apache 2.0
pip install -r requirements-minimal.txt
python3 -c "import nltk; nltk.download('vader_lexicon')"
python3 -m spacy download en_core_web_sm
python3 scripts/build_wikidata_db.py
uvicorn src.api:app --host 0.0.0.0 --port 8080Open http://localhost:8080. All four prongs are available:
- Prongs 1–3 run locally on CPU with no external dependencies.
- Prong 4 works via any remote OpenAI-compatible backend that returns per-token logprobs (OpenAI, Groq, Fireworks, etc.) — supply a Base URL and API key in the UI's Backend Selector. If the provider does not return logprobs, Prong 4 shows neutral placeholder scores; the other three prongs are unaffected.
Local vLLM server (
requirements.txt, CUDA GPU required) is only needed if you want to run generation on your own machine rather than via a remote API key.
Python 3.12+ required.
python3 main.pyEvaluates 4 sample texts across all prongs and saves results to
evaluation_results/evaluation_results.json.
# Terminal 1: start a vLLM server (or skip if using a remote API key)
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-1.5B-Instruct --port 8000
# Terminal 2: start the IE server
uvicorn src.api:app --host 0.0.0.0 --port 8080Open http://localhost:8080 in your browser. From there you can:
- Pick a backend: Local (vLLM) or Remote (OpenAI-compatible, bring-your-own-key)
- Configure generation parameters (temperature, top-p, penalties, max tokens)
- Generate text with real-time SSE token streaming
- View all 4 prong scores with expandable per-dimension breakdowns
- Export results as JSON / browse evaluation history (localStorage)
curl -X POST http://localhost:8080/evaluate \
-H "Content-Type: application/json" \
-d '{"prompt": "What is the capital of France?",
"text": "The capital of France is Paris."}'from src.pipeline import EvaluationPipeline
pipeline = EvaluationPipeline(enable_prongs=[1, 2, 3, 4])
result = pipeline.evaluate(
text="The capital of France is Paris.",
prompt="What is the capital of France?",
lang="en",
)
print(result["aggregated_scores"]["total_score"])| Dimension | Method | Notes |
|---|---|---|
| Relevance | Cosine similarity (MiniLM embeddings) to prompt | Requires prompt |
| Semantic Coherence | Sliding-window sentence embedding similarity | |
| Reliability | Topic-stability (embedding variance) | |
| Toxicity | unitary/toxic-bert classifier | agg_value = 1 − toxicity |
| Dimension | Method | Notes |
|---|---|---|
| Fluency | Flesch readability (language-adapted formula) | |
| Politeness | Lexicon cue count (+0.1/cue, −0.1/rude cue, clipped to [0,1]) | |
| Emotional Intensity | VADER sentiment + punctuation features | aggregate=False |
| Formality | Formal/informal lexicon markers | |
| Hedging | Modal verbs & uncertainty cues | aggregate=False |
| Dimension | Method | Notes |
|---|---|---|
| Factual Accuracy | spaCy triple extraction → offline Wikidata verification | |
| Factual Consistency | Cross-claim contradiction detection via Wikidata QIDs | |
| Temporal Accuracy | Date anchor extraction → Wikidata P585/P580/P582/P569/P570/P571 | |
| Comparative Factuality | JJR/JJS comparative claims → Wikidata | |
| Modality Awareness | Hedged vs. asserted claim fraction | aggregate=False |
| Dimension | Method | Notes |
|---|---|---|
| Uncertainty | 1 − mean token confidence | agg_value = confidence |
| Perplexity | exp(mean NLL) | agg_value = 1/(1+ppl), displayed raw |
| Token Entropy | Shannon entropy of token distributions (normalized) | aggregate=False |
| Semantic Entropy | Entropy of top-k cluster (normalized) | aggregate=False |
Without a vLLM backend, Prong 4 returns neutral placeholder scores (0.5).
Each enabled prong produces a score in [0, 1] as the mean of its aggregate=True
dimensions. Dimensions marked aggregate=False (Emotional Intensity, Hedging,
Token Entropy, Semantic Entropy, Modality Awareness) are reported but excluded
from the score — they have no universal "better" direction.
For dimensions whose raw scale is not [0, 1] / higher-is-better (Toxicity,
Uncertainty, Perplexity), an agg_value supplies an orientation-corrected
bounded value used during aggregation.
The total score is a weighted average of prong scores (default: 0.25 each), renormalized if any prong is missing or errors out.
Prong 3 has been benchmarked on the FEVER 2.0 fixers development set (1,174 claims). Key results:
| Condition | Coverage | Binary AUC | Conditional AUC |
|---|---|---|---|
| Seed DB (~86 entities) | 9.0% | 0.494 | 0.500 |
| Oracle DB (FEVER-targeted) | 8.7% | 0.538 | 0.682 |
Coverage (fraction of claims where Wikidata returned CONFIRMED or DENIED) is
the primary bottleneck — uncovered claims fall back to the neutral 0.5.
See DB_EXTENSION_GUIDE.md and scripts/eval_fever2.py
for details.
pytest tests/ -v # all tests
pytest tests/test_prong2.py -v
pytest tests/ --cov=srcSee TODO.md. Key next steps:
- LLM-based claim extraction for Prong 3 (vLLM-prompted, à la FActScore/Claimify)
- LLM-based readability for Prong 2 (replacing Flesch baseline)
- Dual-output benchmarking: baseline vs. LLM score per dimension
- Expand Wikidata DB toward 200k entities via SPARQL dump
- Protocol: TrustLLM Deliverable 7.5 — Multi-Dimensional Evaluation Metric
- Danescu-Niculescu-Mizil et al. (2013) — Politeness cue lists
- Aly et al. (2021) — FEVER 2.0 benchmark
- Flesch (1948), Amstad (1978), Douma (1960), Matricciani (2023) — Readability formulas