t2s-metrics 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. t2s_metrics-1.0.0/PKG-INFO +335 -0
  2. t2s_metrics-1.0.0/README.md +301 -0
  3. t2s_metrics-1.0.0/setup.cfg +4 -0
  4. t2s_metrics-1.0.0/setup.py +40 -0
  5. t2s_metrics-1.0.0/t2s_metrics.egg-info/PKG-INFO +335 -0
  6. t2s_metrics-1.0.0/t2s_metrics.egg-info/SOURCES.txt +68 -0
  7. t2s_metrics-1.0.0/t2s_metrics.egg-info/dependency_links.txt +1 -0
  8. t2s_metrics-1.0.0/t2s_metrics.egg-info/requires.txt +14 -0
  9. t2s_metrics-1.0.0/t2s_metrics.egg-info/top_level.txt +1 -0
  10. t2s_metrics-1.0.0/t2smetrics/__init__.py +0 -0
  11. t2s_metrics-1.0.0/t2smetrics/aggregation/__init__.py +0 -0
  12. t2s_metrics-1.0.0/t2smetrics/aggregation/aggregator.py +16 -0
  13. t2s_metrics-1.0.0/t2smetrics/cli.py +35 -0
  14. t2s_metrics-1.0.0/t2smetrics/core/__init__.py +0 -0
  15. t2s_metrics-1.0.0/t2smetrics/core/context.py +10 -0
  16. t2s_metrics-1.0.0/t2smetrics/core/engine.py +45 -0
  17. t2s_metrics-1.0.0/t2smetrics/core/eval.py +41 -0
  18. t2s_metrics-1.0.0/t2smetrics/core/experiment.py +32 -0
  19. t2s_metrics-1.0.0/t2smetrics/core/result.py +11 -0
  20. t2s_metrics-1.0.0/t2smetrics/dashboard_plotly.py +545 -0
  21. t2s_metrics-1.0.0/t2smetrics/execution/__init__.py +0 -0
  22. t2s_metrics-1.0.0/t2smetrics/execution/base.py +3 -0
  23. t2s_metrics-1.0.0/t2smetrics/execution/rdflib_backend.py +24 -0
  24. t2s_metrics-1.0.0/t2smetrics/execution/result_utils.py +61 -0
  25. t2s_metrics-1.0.0/t2smetrics/execution/sparql_endpoint_backend.py +41 -0
  26. t2s_metrics-1.0.0/t2smetrics/llm/__init__.py +0 -0
  27. t2s_metrics-1.0.0/t2smetrics/llm/base.py +3 -0
  28. t2s_metrics-1.0.0/t2smetrics/llm/dummy_backend.py +10 -0
  29. t2s_metrics-1.0.0/t2smetrics/llm/ollama_backend.py +51 -0
  30. t2s_metrics-1.0.0/t2smetrics/metrics/__init__.py +0 -0
  31. t2s_metrics-1.0.0/t2smetrics/metrics/answer_set/__init__.py +0 -0
  32. t2s_metrics-1.0.0/t2smetrics/metrics/answer_set/base.py +69 -0
  33. t2s_metrics-1.0.0/t2smetrics/metrics/answer_set/f1.py +22 -0
  34. t2s_metrics-1.0.0/t2smetrics/metrics/answer_set/f1_qald.py +22 -0
  35. t2s_metrics-1.0.0/t2smetrics/metrics/answer_set/f1_spinach.py +168 -0
  36. t2s_metrics-1.0.0/t2smetrics/metrics/answer_set/hit_at_k.py +22 -0
  37. t2s_metrics-1.0.0/t2smetrics/metrics/answer_set/mrr.py +19 -0
  38. t2s_metrics-1.0.0/t2smetrics/metrics/answer_set/ndcg.py +36 -0
  39. t2s_metrics-1.0.0/t2smetrics/metrics/answer_set/p_at_k.py +25 -0
  40. t2s_metrics-1.0.0/t2smetrics/metrics/answer_set/precision.py +16 -0
  41. t2s_metrics-1.0.0/t2smetrics/metrics/answer_set/precision_qald.py +19 -0
  42. t2s_metrics-1.0.0/t2smetrics/metrics/answer_set/recall.py +16 -0
  43. t2s_metrics-1.0.0/t2smetrics/metrics/answer_set/recall_qald.py +19 -0
  44. t2s_metrics-1.0.0/t2smetrics/metrics/base.py +22 -0
  45. t2s_metrics-1.0.0/t2smetrics/metrics/codebleu/__init__.py +0 -0
  46. t2s_metrics-1.0.0/t2smetrics/metrics/codebleu/codebleu.py +44 -0
  47. t2s_metrics-1.0.0/t2smetrics/metrics/codebleu/dataflow.py +18 -0
  48. t2s_metrics-1.0.0/t2smetrics/metrics/codebleu/sparql_keywords.py +44 -0
  49. t2s_metrics-1.0.0/t2smetrics/metrics/codebleu/syntax.py +9 -0
  50. t2s_metrics-1.0.0/t2smetrics/metrics/codebleu/weighted_bleu.py +18 -0
  51. t2s_metrics-1.0.0/t2smetrics/metrics/distance.py +51 -0
  52. t2s_metrics-1.0.0/t2smetrics/metrics/exact.py +12 -0
  53. t2s_metrics-1.0.0/t2smetrics/metrics/llm_judge.py +14 -0
  54. t2s_metrics-1.0.0/t2smetrics/metrics/query_execution.py +16 -0
  55. t2s_metrics-1.0.0/t2smetrics/metrics/text_metrics.py +93 -0
  56. t2s_metrics-1.0.0/t2smetrics/metrics/token.py +55 -0
  57. t2s_metrics-1.0.0/t2smetrics/metrics/uri/__init__.py +0 -0
  58. t2s_metrics-1.0.0/t2smetrics/metrics/uri/uri_hallucination.py +33 -0
  59. t2s_metrics-1.0.0/t2smetrics/representation/__init__.py +0 -0
  60. t2s_metrics-1.0.0/t2smetrics/representation/preprocessing.py +104 -0
  61. t2s_metrics-1.0.0/t2smetrics/representation/sparql_query.py +19 -0
  62. t2s_metrics-1.0.0/t2smetrics/representation/tokenizer.py +32 -0
  63. t2s_metrics-1.0.0/t2smetrics/representation/uri_utils.py +46 -0
  64. t2s_metrics-1.0.0/t2smetrics/run_dblp_quad.py +139 -0
  65. t2s_metrics-1.0.0/t2smetrics/run_example.py +88 -0
  66. t2s_metrics-1.0.0/t2smetrics/run_example_2.py +14 -0
  67. t2s_metrics-1.0.0/t2smetrics/run_instruct_sparql.py +141 -0
  68. t2s_metrics-1.0.0/t2smetrics/run_pathological_dataset.py +111 -0
  69. t2s_metrics-1.0.0/t2smetrics/run_text2sparql.py +141 -0
  70. t2s_metrics-1.0.0/test/test_metrics.py +41 -0
@@ -0,0 +1,335 @@
1
+ Metadata-Version: 2.4
2
+ Name: t2s-metrics
3
+ Version: 1.0.0
4
+ Home-page: https://github.com/Wimmics/t2s-metrics
5
+ Author: Wimmics
6
+ Author-email: yousouf.taghzouti@inria.fr
7
+ License: AGPL-3.0
8
+ Keywords: Question Answering System,Large Language Model,Metric,Text-to-SPARQL
9
+ Requires-Python: >=3.12
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: setuptools==80.9.0
12
+ Requires-Dist: wheel==0.45.1
13
+ Requires-Dist: twine==6.2.0
14
+ Requires-Dist: nltk==3.9.2
15
+ Requires-Dist: rouge_score==0.1.2
16
+ Requires-Dist: scikit-learn==1.8.0
17
+ Requires-Dist: Levenshtein==0.27.3
18
+ Requires-Dist: rdflib==7.5.0
19
+ Requires-Dist: langchain_ollama==1.0.1
20
+ Requires-Dist: SPARQLWrapper==2.0.0
21
+ Requires-Dist: pytest==9.0.2
22
+ Requires-Dist: dash==4.0.0
23
+ Requires-Dist: dash_bootstrap_components==2.0.4
24
+ Requires-Dist: pandas==3.0.1
25
+ Dynamic: author
26
+ Dynamic: author-email
27
+ Dynamic: description
28
+ Dynamic: description-content-type
29
+ Dynamic: home-page
30
+ Dynamic: keywords
31
+ Dynamic: license
32
+ Dynamic: requires-dist
33
+ Dynamic: requires-python
34
+
35
+ # T2S-Metrics
36
+
37
+ A small evaluation toolkit for text-to-SPARQL systems. It runs a configurable set
38
+ of metrics over JSONL datasets and can execute queries against local RDF files or
39
+ SPARQL endpoints.
40
+
41
+ ## Features
42
+
43
+ - Metrics for query exact match, token overlap, answer-set quality, BLEU/ROUGE,
44
+ CodeBLEU, and more.
45
+ - Execution backends for local RDF (RDFLib) and remote SPARQL endpoints.
46
+ - Pluggable LLM-based judging via an Ollama backend.
47
+ - Python API for quick experiments.
48
+
49
+ ## Installation
50
+
51
+ The package is available on PyPI and can be installed directly with pip:
52
+
53
+ ```bash
54
+ pip install t2s-metrics
55
+ ```
56
+
57
+ For development (editable install), you can use:
58
+
59
+ ```bash
60
+ uv venv
61
+ source .venv/bin/activate
62
+ uv pip install -r requirements.txt
63
+ uv pip install -e .
64
+ ```
65
+
66
+ ## Usage
67
+
68
+ ### Expected JSONL format
69
+
70
+ Input evaluation files must be JSON Lines (`.jsonl`) with **one object per line**.
71
+ Each object must include:
72
+
73
+ - `id` (string): unique query/case identifier
74
+ - `golden` (string): reference SPARQL query
75
+ - `generated` (string): system-generated SPARQL query
76
+ - `order_matters` (boolean): whether answer order must be preserved
77
+
78
+ This is exactly what `JsonlEval` expects in `t2smetrics/core/eval.py`.
79
+
80
+ Example (from `datasets/ck25/eval/AIFB.jsonl`):
81
+
82
+ ```json
83
+ {"id": "ck25:1-en", "golden": "PREFIX pv: <http://ld.company.org/prod-vocab/>\nSELECT DISTINCT ?result\nWHERE\n{\n <http://ld.company.org/prod-instances/empl-Karen.Brant%40company.org> pv:memberOf ?result .\n ?result a pv:Department .\n}\n", "generated": "SELECT ?department WHERE { ?person :name \"Ms. Brant\"; :worksIn ?department. }", "order_matters": false}
84
+ ```
85
+
86
+ ### Execution backends
87
+
88
+ The library supports two execution backend families:
89
+
90
+ 1. **Local RDF file execution** with `RDFLibBackend`
91
+ 2. **Remote SPARQL endpoint execution** with `SparqlEndpointBackend`
92
+
93
+ `SparqlEndpointBackend` is generic SPARQL 1.1 and works with endpoints such as
94
+ **QLever** and **Corese** (and also GraphDB, Fuseki, Virtuoso, Blazegraph, etc.).
95
+
96
+ ```python
97
+ from t2smetrics.execution.rdflib_backend import RDFLibBackend
98
+ from t2smetrics.execution.sparql_endpoint_backend import SparqlEndpointBackend
99
+
100
+ # Option 1: local KG file
101
+ local_backend = RDFLibBackend("./datasets/example/kg/example.ttl")
102
+
103
+ # Option 2: remote endpoint (e.g., QLever/Corese)
104
+ endpoint_backend = SparqlEndpointBackend("http://localhost:8886/")
105
+ ```
106
+
107
+ ### LLM backend (local Ollama + extensible)
108
+
109
+ For LLM-based metrics (for example `LLMJudge`), the library currently provides
110
+ `OllamaBackend` for **local** inference.
111
+
112
+ ```python
113
+ from t2smetrics.llm.ollama_backend import OllamaBackend
114
+
115
+ llm_backend = OllamaBackend(model="gemma3:4b")
116
+ ```
117
+
118
+ The LLM layer is extensible via `LLMBackend` (`t2smetrics/llm/base.py`).
119
+ To plug another provider, implement `judge(prompt: str, timeout: int = 30) -> dict`
120
+ and return a dictionary with a numeric `score` (recommended in `[0, 1]`).
121
+
122
+ ```python
123
+ from t2smetrics.llm.base import LLMBackend
124
+
125
+
126
+ class MyLLMBackend(LLMBackend):
127
+ def judge(self, prompt: str, timeout: int = 30) -> dict:
128
+ # Call your provider/client here
129
+ return {"score": 0.85, "raw": "optional provider response"}
130
+ ```
131
+
132
+ Then pass your backend to `Experiment(..., llm_backend=...)`.
133
+
134
+ ### Python (minimal example)
135
+
136
+ ```python
137
+ from t2smetrics.core.experiment import Experiment
138
+ from t2smetrics.core.eval import JsonlEval
139
+ from t2smetrics.metrics.text_metrics import Bleu
140
+ from t2smetrics.metrics.token import TokenF1
141
+
142
+
143
+ jsonl_eval = JsonlEval("./datasets/example/eval/example.jsonl")
144
+ metrics = [Bleu(), TokenF1()]
145
+ experiment = Experiment(jsonl_eval, metrics)
146
+ _, summary = experiment.run()
147
+
148
+ print("\n=== SUMMARY ===")
149
+ for k, v in summary.items():
150
+ print(f"{k}: {v:.4f}")
151
+ ```
152
+
153
+ ### Python (full example with execution backends)
154
+
155
+ ```python
156
+ from t2smetrics.core.experiment import Experiment
157
+ from t2smetrics.core.eval import JsonlEval
158
+ from t2smetrics.execution.rdflib_backend import RDFLibBackend
159
+
160
+ from t2smetrics.llm.ollama_backend import OllamaBackend
161
+ from t2smetrics.metrics.answer_set.f1 import AnswerSetF1
162
+ from t2smetrics.metrics.answer_set.precision import AnswerSetPrecision
163
+ from t2smetrics.metrics.answer_set.precision_qald import PrecisionQALD
164
+ from t2smetrics.metrics.answer_set.recall import AnswerSetRecall
165
+ from t2smetrics.metrics.answer_set.recall_qald import RecallQALD
166
+ from t2smetrics.metrics.exact import QueryExactMatch
167
+ from t2smetrics.metrics.codebleu.codebleu import CodeBLEU
168
+ from t2smetrics.metrics.answer_set.f1_qald import F1QALD
169
+ from t2smetrics.metrics.answer_set.f1_spinach import F1Spinach
170
+ from t2smetrics.metrics.answer_set.mrr import MRR
171
+ from t2smetrics.metrics.answer_set.hit_at_k import HitAtK
172
+ from t2smetrics.metrics.answer_set.ndcg import NDCG
173
+ from t2smetrics.metrics.answer_set.p_at_k import PrecisionAtK
174
+ from t2smetrics.metrics.distance import (
175
+ LevenshteinDistance,
176
+ JaccardSimilarity,
177
+ CosineSimilarity,
178
+ EuclideanDistance,
179
+ )
180
+ from t2smetrics.metrics.llm_judge import LLMJudge
181
+ from t2smetrics.metrics.text_metrics import Bleu, RougeN, Meteor, SPBleu
182
+ from t2smetrics.metrics.uri.uri_hallucination import URIHallucination
183
+ from t2smetrics.metrics.query_execution import QueryExecution
184
+ from t2smetrics.metrics.token import SPF1, TokenRecall, TokenPrecision, TokenF1
185
+
186
+
187
+ jsonl_eval = JsonlEval("./datasets/example/eval/example.jsonl")
188
+
189
+ execution_backend = RDFLibBackend("./datasets/example/kg/example.ttl")
190
+
191
+ llm_backend = OllamaBackend()
192
+
193
+ metrics = [
194
+ AnswerSetPrecision(),
195
+ AnswerSetRecall(),
196
+ AnswerSetF1(),
197
+ Bleu(),
198
+ SPBleu(),
199
+ CodeBLEU(),
200
+ CosineSimilarity(),
201
+ EuclideanDistance(),
202
+ F1QALD(),
203
+ PrecisionQALD(),
204
+ RecallQALD(),
205
+ F1Spinach(),
206
+ HitAtK(k=5),
207
+ JaccardSimilarity(),
208
+ LLMJudge(),
209
+ LevenshteinDistance(),
210
+ MRR(),
211
+ Meteor(),
212
+ NDCG(),
213
+ PrecisionAtK(k=1),
214
+ QueryExecution(),
215
+ QueryExactMatch(),
216
+ RougeN(1),
217
+ RougeN(2),
218
+ RougeN(3),
219
+ RougeN(4),
220
+ TokenF1(),
221
+ SPF1(),
222
+ TokenPrecision(),
223
+ TokenRecall(),
224
+ URIHallucination(),
225
+ ]
226
+
227
+ experiment = Experiment(
228
+ jsonl_eval=jsonl_eval,
229
+ metrics=metrics,
230
+ execution_backend=execution_backend,
231
+ llm_backend=llm_backend,
232
+ verbose=True,
233
+ )
234
+
235
+ results, summary = experiment.run()
236
+
237
+ print("=== PER QUERY RESULTS ===")
238
+ for r in results:
239
+ print(r)
240
+
241
+ print("\n=== SUMMARY ===")
242
+ for k, v in summary.items():
243
+ print(f"{k}: {v:.4f}")
244
+ ```
245
+
246
+ ### Full workflow example (dataset + endpoint + export)
247
+
248
+ For a complete run over multiple systems and export of aggregated metrics to JSON,
249
+ see `t2smetrics/run_text2sparql.py`.
250
+
251
+ Typical workflow:
252
+
253
+ 1. Choose a dataset folder (for example `datasets/ck25`).
254
+ 2. Put input files under `datasets/<dataset>/eval/*.jsonl`.
255
+ 3. Start your SPARQL endpoint (for example QLever/Corese).
256
+ 4. Set endpoint URL in the script (example: `http://localhost:8886/`).
257
+ 5. Run:
258
+
259
+ ```bash
260
+ python -m t2smetrics.run_text2sparql
261
+ ```
262
+
263
+ The script writes timestamped summary files under:
264
+
265
+ ```text
266
+ datasets/<dataset>/results/<dataset>-YYYYMMDD-HHMMSS.json
267
+ ```
268
+
269
+ These result files are then directly consumable by the dashboard.
270
+
271
+ ### Dashboard
272
+
273
+ The dashboard reads JSON result files (generated in `datasets/*/results/*.json`)
274
+ and serves an interactive UI (Radar, Bar, Correlation Heatmap, Parallel Coordinates,
275
+ Scatter Matrix).
276
+
277
+ Launch with auto-discovery:
278
+
279
+ ```bash
280
+ python -m t2smetrics.cli dashboard
281
+ ```
282
+
283
+ Launch with explicit files:
284
+
285
+ ```bash
286
+ python -m t2smetrics.cli dashboard \
287
+ datasets/ck25/results/ck25-20260306-133227.json \
288
+ datasets/db25/results/db25-20260306-132100.json
289
+ ```
290
+
291
+ Then open:
292
+
293
+ ```text
294
+ http://127.0.0.1:8050
295
+ ```
296
+
297
+
298
+ ## Development
299
+
300
+ ### Build
301
+
302
+ ```bash
303
+ python setup.py sdist bdist_wheel
304
+ ```
305
+
306
+ ### Tests
307
+
308
+ There are no automated tests yet. If you add tests, run them with:
309
+
310
+ ```bash
311
+ python -m pytest
312
+ ```
313
+
314
+ # License
315
+
316
+ ## t2s-metrics
317
+
318
+ t2s-metrics is provided under the terms of the [GNU Affero General Public License 3.0](./LICENSES/AGPL-3.0.txt) (AGPL-3.0).
319
+
320
+
321
+ ## Redistribution of third-party software and data
322
+
323
+ This repository provides several third-party contributions redistributed with their original licenses.
324
+
325
+ ### CK25 Dataset
326
+
327
+ t2s-metrics reuses the [CK25 Corporate Knowledge Reference Dataset for Benchmarking Text-2-SPARQL QA Approaches](https://github.com/eccenca/ck25-dataset/) that we modified to account for file format requirements (jsonl format).
328
+
329
+ The modified version is redistributed in directory [dataset/ck25](dataset/ck25) under the terms of the [Creative Commons Attribution 4.0 International license](LICENSES/CC-BY-4.0.txt) (CC-BY-4.0).
330
+
331
+ ### QCan library
332
+
333
+ t2s-metrics reuses the [QCan software for canonicalising SPARQL queries](https://github.com/RittoShadow/QCan).
334
+
335
+ QCan is written in Java. In this repository, we distribute the compiled jar of QCan v1.1, [third_party_lib/qcan-1.1-jar-with-dependencies.jar](third_party_lib/qcan-1.1-jar-with-dependencies.jar), under the terms of the [Apache 2.0 license](LICENSES/Apache-2.0.txt).
@@ -0,0 +1,301 @@
1
+ # T2S-Metrics
2
+
3
+ A small evaluation toolkit for text-to-SPARQL systems. It runs a configurable set
4
+ of metrics over JSONL datasets and can execute queries against local RDF files or
5
+ SPARQL endpoints.
6
+
7
+ ## Features
8
+
9
+ - Metrics for query exact match, token overlap, answer-set quality, BLEU/ROUGE,
10
+ CodeBLEU, and more.
11
+ - Execution backends for local RDF (RDFLib) and remote SPARQL endpoints.
12
+ - Pluggable LLM-based judging via an Ollama backend.
13
+ - Python API for quick experiments.
14
+
15
+ ## Installation
16
+
17
+ The package is available on PyPI and can be installed directly with pip:
18
+
19
+ ```bash
20
+ pip install t2s-metrics
21
+ ```
22
+
23
+ For development (editable install), you can use:
24
+
25
+ ```bash
26
+ uv venv
27
+ source .venv/bin/activate
28
+ uv pip install -r requirements.txt
29
+ uv pip install -e .
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ ### Expected JSONL format
35
+
36
+ Input evaluation files must be JSON Lines (`.jsonl`) with **one object per line**.
37
+ Each object must include:
38
+
39
+ - `id` (string): unique query/case identifier
40
+ - `golden` (string): reference SPARQL query
41
+ - `generated` (string): system-generated SPARQL query
42
+ - `order_matters` (boolean): whether answer order must be preserved
43
+
44
+ This is exactly what `JsonlEval` expects in `t2smetrics/core/eval.py`.
45
+
46
+ Example (from `datasets/ck25/eval/AIFB.jsonl`):
47
+
48
+ ```json
49
+ {"id": "ck25:1-en", "golden": "PREFIX pv: <http://ld.company.org/prod-vocab/>\nSELECT DISTINCT ?result\nWHERE\n{\n <http://ld.company.org/prod-instances/empl-Karen.Brant%40company.org> pv:memberOf ?result .\n ?result a pv:Department .\n}\n", "generated": "SELECT ?department WHERE { ?person :name \"Ms. Brant\"; :worksIn ?department. }", "order_matters": false}
50
+ ```
51
+
52
+ ### Execution backends
53
+
54
+ The library supports two execution backend families:
55
+
56
+ 1. **Local RDF file execution** with `RDFLibBackend`
57
+ 2. **Remote SPARQL endpoint execution** with `SparqlEndpointBackend`
58
+
59
+ `SparqlEndpointBackend` is generic SPARQL 1.1 and works with endpoints such as
60
+ **QLever** and **Corese** (and also GraphDB, Fuseki, Virtuoso, Blazegraph, etc.).
61
+
62
+ ```python
63
+ from t2smetrics.execution.rdflib_backend import RDFLibBackend
64
+ from t2smetrics.execution.sparql_endpoint_backend import SparqlEndpointBackend
65
+
66
+ # Option 1: local KG file
67
+ local_backend = RDFLibBackend("./datasets/example/kg/example.ttl")
68
+
69
+ # Option 2: remote endpoint (e.g., QLever/Corese)
70
+ endpoint_backend = SparqlEndpointBackend("http://localhost:8886/")
71
+ ```
72
+
73
+ ### LLM backend (local Ollama + extensible)
74
+
75
+ For LLM-based metrics (for example `LLMJudge`), the library currently provides
76
+ `OllamaBackend` for **local** inference.
77
+
78
+ ```python
79
+ from t2smetrics.llm.ollama_backend import OllamaBackend
80
+
81
+ llm_backend = OllamaBackend(model="gemma3:4b")
82
+ ```
83
+
84
+ The LLM layer is extensible via `LLMBackend` (`t2smetrics/llm/base.py`).
85
+ To plug another provider, implement `judge(prompt: str, timeout: int = 30) -> dict`
86
+ and return a dictionary with a numeric `score` (recommended in `[0, 1]`).
87
+
88
+ ```python
89
+ from t2smetrics.llm.base import LLMBackend
90
+
91
+
92
+ class MyLLMBackend(LLMBackend):
93
+ def judge(self, prompt: str, timeout: int = 30) -> dict:
94
+ # Call your provider/client here
95
+ return {"score": 0.85, "raw": "optional provider response"}
96
+ ```
97
+
98
+ Then pass your backend to `Experiment(..., llm_backend=...)`.
99
+
100
+ ### Python (minimal example)
101
+
102
+ ```python
103
+ from t2smetrics.core.experiment import Experiment
104
+ from t2smetrics.core.eval import JsonlEval
105
+ from t2smetrics.metrics.text_metrics import Bleu
106
+ from t2smetrics.metrics.token import TokenF1
107
+
108
+
109
+ jsonl_eval = JsonlEval("./datasets/example/eval/example.jsonl")
110
+ metrics = [Bleu(), TokenF1()]
111
+ experiment = Experiment(jsonl_eval, metrics)
112
+ _, summary = experiment.run()
113
+
114
+ print("\n=== SUMMARY ===")
115
+ for k, v in summary.items():
116
+ print(f"{k}: {v:.4f}")
117
+ ```
118
+
119
+ ### Python (full example with execution backends)
120
+
121
+ ```python
122
+ from t2smetrics.core.experiment import Experiment
123
+ from t2smetrics.core.eval import JsonlEval
124
+ from t2smetrics.execution.rdflib_backend import RDFLibBackend
125
+
126
+ from t2smetrics.llm.ollama_backend import OllamaBackend
127
+ from t2smetrics.metrics.answer_set.f1 import AnswerSetF1
128
+ from t2smetrics.metrics.answer_set.precision import AnswerSetPrecision
129
+ from t2smetrics.metrics.answer_set.precision_qald import PrecisionQALD
130
+ from t2smetrics.metrics.answer_set.recall import AnswerSetRecall
131
+ from t2smetrics.metrics.answer_set.recall_qald import RecallQALD
132
+ from t2smetrics.metrics.exact import QueryExactMatch
133
+ from t2smetrics.metrics.codebleu.codebleu import CodeBLEU
134
+ from t2smetrics.metrics.answer_set.f1_qald import F1QALD
135
+ from t2smetrics.metrics.answer_set.f1_spinach import F1Spinach
136
+ from t2smetrics.metrics.answer_set.mrr import MRR
137
+ from t2smetrics.metrics.answer_set.hit_at_k import HitAtK
138
+ from t2smetrics.metrics.answer_set.ndcg import NDCG
139
+ from t2smetrics.metrics.answer_set.p_at_k import PrecisionAtK
140
+ from t2smetrics.metrics.distance import (
141
+ LevenshteinDistance,
142
+ JaccardSimilarity,
143
+ CosineSimilarity,
144
+ EuclideanDistance,
145
+ )
146
+ from t2smetrics.metrics.llm_judge import LLMJudge
147
+ from t2smetrics.metrics.text_metrics import Bleu, RougeN, Meteor, SPBleu
148
+ from t2smetrics.metrics.uri.uri_hallucination import URIHallucination
149
+ from t2smetrics.metrics.query_execution import QueryExecution
150
+ from t2smetrics.metrics.token import SPF1, TokenRecall, TokenPrecision, TokenF1
151
+
152
+
153
+ jsonl_eval = JsonlEval("./datasets/example/eval/example.jsonl")
154
+
155
+ execution_backend = RDFLibBackend("./datasets/example/kg/example.ttl")
156
+
157
+ llm_backend = OllamaBackend()
158
+
159
+ metrics = [
160
+ AnswerSetPrecision(),
161
+ AnswerSetRecall(),
162
+ AnswerSetF1(),
163
+ Bleu(),
164
+ SPBleu(),
165
+ CodeBLEU(),
166
+ CosineSimilarity(),
167
+ EuclideanDistance(),
168
+ F1QALD(),
169
+ PrecisionQALD(),
170
+ RecallQALD(),
171
+ F1Spinach(),
172
+ HitAtK(k=5),
173
+ JaccardSimilarity(),
174
+ LLMJudge(),
175
+ LevenshteinDistance(),
176
+ MRR(),
177
+ Meteor(),
178
+ NDCG(),
179
+ PrecisionAtK(k=1),
180
+ QueryExecution(),
181
+ QueryExactMatch(),
182
+ RougeN(1),
183
+ RougeN(2),
184
+ RougeN(3),
185
+ RougeN(4),
186
+ TokenF1(),
187
+ SPF1(),
188
+ TokenPrecision(),
189
+ TokenRecall(),
190
+ URIHallucination(),
191
+ ]
192
+
193
+ experiment = Experiment(
194
+ jsonl_eval=jsonl_eval,
195
+ metrics=metrics,
196
+ execution_backend=execution_backend,
197
+ llm_backend=llm_backend,
198
+ verbose=True,
199
+ )
200
+
201
+ results, summary = experiment.run()
202
+
203
+ print("=== PER QUERY RESULTS ===")
204
+ for r in results:
205
+ print(r)
206
+
207
+ print("\n=== SUMMARY ===")
208
+ for k, v in summary.items():
209
+ print(f"{k}: {v:.4f}")
210
+ ```
211
+
212
+ ### Full workflow example (dataset + endpoint + export)
213
+
214
+ For a complete run over multiple systems and export of aggregated metrics to JSON,
215
+ see `t2smetrics/run_text2sparql.py`.
216
+
217
+ Typical workflow:
218
+
219
+ 1. Choose a dataset folder (for example `datasets/ck25`).
220
+ 2. Put input files under `datasets/<dataset>/eval/*.jsonl`.
221
+ 3. Start your SPARQL endpoint (for example QLever/Corese).
222
+ 4. Set endpoint URL in the script (example: `http://localhost:8886/`).
223
+ 5. Run:
224
+
225
+ ```bash
226
+ python -m t2smetrics.run_text2sparql
227
+ ```
228
+
229
+ The script writes timestamped summary files under:
230
+
231
+ ```text
232
+ datasets/<dataset>/results/<dataset>-YYYYMMDD-HHMMSS.json
233
+ ```
234
+
235
+ These result files are then directly consumable by the dashboard.
236
+
237
+ ### Dashboard
238
+
239
+ The dashboard reads JSON result files (generated in `datasets/*/results/*.json`)
240
+ and serves an interactive UI (Radar, Bar, Correlation Heatmap, Parallel Coordinates,
241
+ Scatter Matrix).
242
+
243
+ Launch with auto-discovery:
244
+
245
+ ```bash
246
+ python -m t2smetrics.cli dashboard
247
+ ```
248
+
249
+ Launch with explicit files:
250
+
251
+ ```bash
252
+ python -m t2smetrics.cli dashboard \
253
+ datasets/ck25/results/ck25-20260306-133227.json \
254
+ datasets/db25/results/db25-20260306-132100.json
255
+ ```
256
+
257
+ Then open:
258
+
259
+ ```text
260
+ http://127.0.0.1:8050
261
+ ```
262
+
263
+
264
+ ## Development
265
+
266
+ ### Build
267
+
268
+ ```bash
269
+ python setup.py sdist bdist_wheel
270
+ ```
271
+
272
+ ### Tests
273
+
274
+ There are no automated tests yet. If you add tests, run them with:
275
+
276
+ ```bash
277
+ python -m pytest
278
+ ```
279
+
280
+ # License
281
+
282
+ ## t2s-metrics
283
+
284
+ t2s-metrics is provided under the terms of the [GNU Affero General Public License 3.0](./LICENSES/AGPL-3.0.txt) (AGPL-3.0).
285
+
286
+
287
+ ## Redistribution of third-party software and data
288
+
289
+ This repository provides several third-party contributions redistributed with their original licenses.
290
+
291
+ ### CK25 Dataset
292
+
293
+ t2s-metrics reuses the [CK25 Corporate Knowledge Reference Dataset for Benchmarking Text-2-SPARQL QA Approaches](https://github.com/eccenca/ck25-dataset/) that we modified to account for file format requirements (jsonl format).
294
+
295
+ The modified version is redistributed in directory [dataset/ck25](dataset/ck25) under the terms of the [Creative Commons Attribution 4.0 International license](LICENSES/CC-BY-4.0.txt) (CC-BY-4.0).
296
+
297
+ ### QCan library
298
+
299
+ t2s-metrics reuses the [QCan software for canonicalising SPARQL queries](https://github.com/RittoShadow/QCan).
300
+
301
+ QCan is written in Java. In this repository, we distribute the compiled jar of QCan v1.1, [third_party_lib/qcan-1.1-jar-with-dependencies.jar](third_party_lib/qcan-1.1-jar-with-dependencies.jar), under the terms of the [Apache 2.0 license](LICENSES/Apache-2.0.txt).
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,40 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ with open("README.md", "r", encoding="utf-8") as fh:
4
+ long_description = fh.read()
5
+
6
+ setup(
7
+ name="t2s-metrics",
8
+ version="1.0.0",
9
+ packages=find_packages(),
10
+ install_requires=[
11
+ "setuptools==80.9.0",
12
+ "wheel==0.45.1",
13
+ "twine==6.2.0",
14
+ "nltk==3.9.2",
15
+ "rouge_score==0.1.2",
16
+ "scikit-learn==1.8.0",
17
+ "Levenshtein==0.27.3",
18
+ "rdflib==7.5.0",
19
+ "langchain_ollama==1.0.1",
20
+ "SPARQLWrapper==2.0.0",
21
+ "pytest==9.0.2",
22
+ "dash==4.0.0",
23
+ "dash_bootstrap_components==2.0.4",
24
+ "pandas==3.0.1",
25
+ ],
26
+ long_description=long_description,
27
+ long_description_content_type="text/markdown",
28
+ author="Wimmics",
29
+ author_email="yousouf.taghzouti@inria.fr",
30
+ url="https://github.com/Wimmics/t2s-metrics",
31
+ licence="AGPL-3.0",
32
+ license_files=["LICENSE"],
33
+ keywords=[
34
+ "Question Answering System",
35
+ "Large Language Model",
36
+ "Metric",
37
+ "Text-to-SPARQL"
38
+ ],
39
+ python_requires=">=3.12",
40
+ )