evalcore 2.1.0__tar.gz → 2.2.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.
- {evalcore-2.1.0 → evalcore-2.2.0}/CHANGELOG.md +24 -1
- {evalcore-2.1.0 → evalcore-2.2.0}/PKG-INFO +35 -5
- {evalcore-2.1.0 → evalcore-2.2.0}/README.md +34 -4
- {evalcore-2.1.0 → evalcore-2.2.0}/examples/quickstart/graders.py +2 -2
- {evalcore-2.1.0 → evalcore-2.2.0}/pyproject.toml +1 -1
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/store.py +47 -2
- {evalcore-2.1.0 → evalcore-2.2.0}/tests/test_store.py +30 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/uv.lock +1 -1
- {evalcore-2.1.0 → evalcore-2.2.0}/.github/workflows/ci.yml +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/.github/workflows/publish.yml +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/.gitignore +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/.pre-commit-config.yaml +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/LICENSE +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/docs/design.md +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/examples/__init__.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/examples/quickstart/README.md +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/examples/quickstart/__init__.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/examples/quickstart/adapter.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/examples/quickstart/datasets/support_reply/v1/cases/angry_complaint.yaml +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/examples/quickstart/datasets/support_reply/v1/cases/billing_question.yaml +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/examples/quickstart/datasets/support_reply/v1/cases/double_charge.yaml +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/examples/quickstart/datasets/support_reply/v1/cases/refund_request.yaml +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/examples/quickstart/fixtures/support_reply_judge.yaml +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/examples/quickstart/fixtures/support_reply_pairwise.yaml +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/examples/quickstart/fixtures/support_reply_replay.yaml +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/examples/quickstart/run_eval.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/examples/quickstart/suite.yaml +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/examples/quickstart/tests/__init__.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/examples/quickstart/tests/test_quickstart.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/justfile +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/pyrightconfig.json +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/__init__.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/adapters/__init__.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/adapters/base.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/adapters/env.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/adapters/http.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/adapters/replay.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/cli.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/compare.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/errors.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/graders/__init__.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/graders/base.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/graders/classification.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/graders/deterministic.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/graders/judge.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/graders/numeric.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/loader.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/models.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/pairwise.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/py.typed +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/rating.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/refs.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/report.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/reporters/__init__.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/reporters/base.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/reporters/html.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/reporters/markdown.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/retry.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/runner.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/src/evalcore/sweep.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/tests/__init__.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/tests/test_adapters.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/tests/test_cli.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/tests/test_edge_cases.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/tests/test_judge.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/tests/test_judge_extra.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/tests/test_live_clients.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/tests/test_pairwise_extra.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/tests/test_rating.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/tests/test_rating_server.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/tests/test_reporters.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/tests/test_retry.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/tests/test_runner.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/tests/test_sweep_pairwise.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/tests/test_unit.py +0 -0
- {evalcore-2.1.0 → evalcore-2.2.0}/uv.toml +0 -0
|
@@ -6,6 +6,28 @@ All notable changes to this project are documented here. The format is based on
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [2.2.0] - 2026-08-08
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- `store.ScoreExporter`, a `runtime_checkable` Protocol naming the seam
|
|
13
|
+
`JsonlOutboxExporter` already occupied. `store.py` has always said to
|
|
14
|
+
"replace this class with a database client implementing the same
|
|
15
|
+
`export_scores` method", but the contract was a docstring sentence and
|
|
16
|
+
`**kwargs`, so an implementation had to duck-type a private shape. An
|
|
17
|
+
exporter belongs in the package that owns the store it targets - it is the
|
|
18
|
+
store that knows its own column types, null policy, and transport - and
|
|
19
|
+
swapping one for another is now a constructor line at the call site, so an
|
|
20
|
+
offline run and a live one share a code path.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- `examples/quickstart/graders.py` still used the one-argument `register` and
|
|
24
|
+
raised `TypeError` on import, so 2.1.0 shipped with its own bundled example
|
|
25
|
+
broken. `just test` does not run the example; `just test-all` does.
|
|
26
|
+
- The README's Python API example called a `.export()` that does not exist and
|
|
27
|
+
passed `RunResult`s to `compare.compare` and `render_scorecard`, which take
|
|
28
|
+
`Scorecard`s. It now runs verbatim, and shows `grader_lookups` feeding the
|
|
29
|
+
exporter.
|
|
30
|
+
|
|
9
31
|
## [2.1.0] - 2026-08-07
|
|
10
32
|
|
|
11
33
|
A grader declares what kind of check it is at registration, so a consumer
|
|
@@ -174,7 +196,8 @@ by semantic versioning: a breaking change to either means a 2.0.
|
|
|
174
196
|
rating + ranking with judge agreement, Markdown/HTML reporters, JSON +
|
|
175
197
|
column-store outbox, and content-hash provenance.
|
|
176
198
|
|
|
177
|
-
[Unreleased]: https://github.com/scottpmiller/evalcore/compare/2.
|
|
199
|
+
[Unreleased]: https://github.com/scottpmiller/evalcore/compare/2.2.0...HEAD
|
|
200
|
+
[2.2.0]: https://github.com/scottpmiller/evalcore/compare/2.1.0...2.2.0
|
|
178
201
|
[2.1.0]: https://github.com/scottpmiller/evalcore/compare/2.0.0...2.1.0
|
|
179
202
|
[2.0.0]: https://github.com/scottpmiller/evalcore/compare/1.0.0...2.0.0
|
|
180
203
|
[1.0.0]: https://github.com/scottpmiller/evalcore/compare/0.3.0...1.0.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: evalcore
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: A generic, consumer-agnostic evaluation engine for prompt, model, and API outputs.
|
|
5
5
|
Project-URL: Homepage, https://github.com/scottpmiller/evalcore
|
|
6
6
|
Project-URL: Repository, https://github.com/scottpmiller/evalcore
|
|
@@ -502,13 +502,30 @@ candidate = runner.run_suite_sync(suite, 'candidate', mode='replay',
|
|
|
502
502
|
revision='abc123', created_at=now)
|
|
503
503
|
# (async context: `await runner.run_suite(...)` is the same call)
|
|
504
504
|
|
|
505
|
-
|
|
506
|
-
|
|
505
|
+
# run_suite returns a RunResult: the scorecard plus every per-sample
|
|
506
|
+
# result behind it. compare and the reporters take the scorecard.
|
|
507
|
+
result = compare.compare(
|
|
508
|
+
baseline.scorecard, candidate.scorecard, suite.thresholds
|
|
509
|
+
)
|
|
510
|
+
print(report.render_scorecard(candidate.scorecard))
|
|
507
511
|
print(report.render_comparison(result))
|
|
508
512
|
|
|
509
|
-
store.write_scorecard('candidate.scorecard.json', candidate)
|
|
513
|
+
store.write_scorecard('candidate.scorecard.json', candidate.scorecard)
|
|
510
514
|
store.write_comparison('comparison.json', result)
|
|
511
|
-
|
|
515
|
+
|
|
516
|
+
# The rows. grader_lookups supplies the grader category and judge scale a
|
|
517
|
+
# Score doesn't carry; the baseline half exports without the comparison,
|
|
518
|
+
# since it was not itself gated.
|
|
519
|
+
types, scales = store.grader_lookups(suite.graders)
|
|
520
|
+
exporter = store.JsonlOutboxExporter('outbox.jsonl')
|
|
521
|
+
exporter.export_scores(baseline, grader_types=types, judge_scales=scales)
|
|
522
|
+
exporter.export_scores(
|
|
523
|
+
candidate,
|
|
524
|
+
result,
|
|
525
|
+
baseline_run_id=baseline.run_id,
|
|
526
|
+
grader_types=types,
|
|
527
|
+
judge_scales=scales,
|
|
528
|
+
)
|
|
512
529
|
|
|
513
530
|
raise SystemExit(0 if result.verdict != 'fail' else 1)
|
|
514
531
|
```
|
|
@@ -784,6 +801,19 @@ table without a mapping layer. A store that forbids nullable columns fills
|
|
|
784
801
|
those nulls in at ingest, on its side of the seam. Swap the exporter for a real
|
|
785
802
|
database client without touching the runner or any consumer.
|
|
786
803
|
|
|
804
|
+
`ScoreExporter` is that seam, named: any object with an `export_scores(run,
|
|
805
|
+
comparison, **kwargs)` method returning a count. `JsonlOutboxExporter` is the
|
|
806
|
+
built-in one; an exporter that publishes to a real store belongs in the
|
|
807
|
+
package that owns that store, since it is the store that knows its own column
|
|
808
|
+
types, null policy, and transport. Swapping one for the other is a
|
|
809
|
+
constructor line, so an offline run and a live one share a code path:
|
|
810
|
+
|
|
811
|
+
```python
|
|
812
|
+
exporter = store.JsonlOutboxExporter(path) # offline
|
|
813
|
+
exporter = KafkaOutboxExporter(...) # live, from another package
|
|
814
|
+
exporter.export_scores(run, comparison, grader_types=..., judge_scales=...)
|
|
815
|
+
```
|
|
816
|
+
|
|
787
817
|
---
|
|
788
818
|
|
|
789
819
|
## The two extension seams (recap)
|
|
@@ -470,13 +470,30 @@ candidate = runner.run_suite_sync(suite, 'candidate', mode='replay',
|
|
|
470
470
|
revision='abc123', created_at=now)
|
|
471
471
|
# (async context: `await runner.run_suite(...)` is the same call)
|
|
472
472
|
|
|
473
|
-
|
|
474
|
-
|
|
473
|
+
# run_suite returns a RunResult: the scorecard plus every per-sample
|
|
474
|
+
# result behind it. compare and the reporters take the scorecard.
|
|
475
|
+
result = compare.compare(
|
|
476
|
+
baseline.scorecard, candidate.scorecard, suite.thresholds
|
|
477
|
+
)
|
|
478
|
+
print(report.render_scorecard(candidate.scorecard))
|
|
475
479
|
print(report.render_comparison(result))
|
|
476
480
|
|
|
477
|
-
store.write_scorecard('candidate.scorecard.json', candidate)
|
|
481
|
+
store.write_scorecard('candidate.scorecard.json', candidate.scorecard)
|
|
478
482
|
store.write_comparison('comparison.json', result)
|
|
479
|
-
|
|
483
|
+
|
|
484
|
+
# The rows. grader_lookups supplies the grader category and judge scale a
|
|
485
|
+
# Score doesn't carry; the baseline half exports without the comparison,
|
|
486
|
+
# since it was not itself gated.
|
|
487
|
+
types, scales = store.grader_lookups(suite.graders)
|
|
488
|
+
exporter = store.JsonlOutboxExporter('outbox.jsonl')
|
|
489
|
+
exporter.export_scores(baseline, grader_types=types, judge_scales=scales)
|
|
490
|
+
exporter.export_scores(
|
|
491
|
+
candidate,
|
|
492
|
+
result,
|
|
493
|
+
baseline_run_id=baseline.run_id,
|
|
494
|
+
grader_types=types,
|
|
495
|
+
judge_scales=scales,
|
|
496
|
+
)
|
|
480
497
|
|
|
481
498
|
raise SystemExit(0 if result.verdict != 'fail' else 1)
|
|
482
499
|
```
|
|
@@ -752,6 +769,19 @@ table without a mapping layer. A store that forbids nullable columns fills
|
|
|
752
769
|
those nulls in at ingest, on its side of the seam. Swap the exporter for a real
|
|
753
770
|
database client without touching the runner or any consumer.
|
|
754
771
|
|
|
772
|
+
`ScoreExporter` is that seam, named: any object with an `export_scores(run,
|
|
773
|
+
comparison, **kwargs)` method returning a count. `JsonlOutboxExporter` is the
|
|
774
|
+
built-in one; an exporter that publishes to a real store belongs in the
|
|
775
|
+
package that owns that store, since it is the store that knows its own column
|
|
776
|
+
types, null policy, and transport. Swapping one for the other is a
|
|
777
|
+
constructor line, so an offline run and a live one share a code path:
|
|
778
|
+
|
|
779
|
+
```python
|
|
780
|
+
exporter = store.JsonlOutboxExporter(path) # offline
|
|
781
|
+
exporter = KafkaOutboxExporter(...) # live, from another package
|
|
782
|
+
exporter.export_scores(run, comparison, grader_types=..., judge_scales=...)
|
|
783
|
+
```
|
|
784
|
+
|
|
755
785
|
---
|
|
756
786
|
|
|
757
787
|
## The two extension seams (recap)
|
|
@@ -26,7 +26,7 @@ _ACK = re.compile(
|
|
|
26
26
|
)
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
@base.register('acknowledges_customer')
|
|
29
|
+
@base.register('acknowledges_customer', base.GraderType.HEURISTIC)
|
|
30
30
|
class AcknowledgesCustomer:
|
|
31
31
|
"""The reply must acknowledge the customer, not just brush them off.
|
|
32
32
|
|
|
@@ -59,7 +59,7 @@ class AcknowledgesCustomer:
|
|
|
59
59
|
]
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
@base.register('distinct_reply_rate')
|
|
62
|
+
@base.register('distinct_reply_rate', base.GraderType.HEURISTIC)
|
|
63
63
|
class DistinctReplyRate:
|
|
64
64
|
"""Fraction of cases whose reply is unique (mode-collapse check).
|
|
65
65
|
|
|
@@ -32,6 +32,7 @@ than model attribute names, so ``project`` is emitted as ``application``,
|
|
|
32
32
|
|
|
33
33
|
import json
|
|
34
34
|
import pathlib
|
|
35
|
+
import typing
|
|
35
36
|
|
|
36
37
|
from evalcore import graders, models
|
|
37
38
|
|
|
@@ -511,12 +512,56 @@ def score_rows(
|
|
|
511
512
|
return rows
|
|
512
513
|
|
|
513
514
|
|
|
515
|
+
@typing.runtime_checkable
|
|
516
|
+
class ScoreExporter(typing.Protocol):
|
|
517
|
+
"""Where a run's score rows go.
|
|
518
|
+
|
|
519
|
+
The seam between the engine and a results store. :func:`score_rows`
|
|
520
|
+
produces the rows; an exporter decides what happens to them. Implement
|
|
521
|
+
this to publish to a real store - a Kafka producer, a database client -
|
|
522
|
+
without the runner or any consumer changing.
|
|
523
|
+
|
|
524
|
+
An implementation lives with the store it targets, not here. It is the
|
|
525
|
+
store that knows its own column types, its null policy, and its
|
|
526
|
+
transport; the engine only knows the row.
|
|
527
|
+
|
|
528
|
+
Swapping one for another is a constructor line at the call site, so an
|
|
529
|
+
offline run and a live one share the same code path::
|
|
530
|
+
|
|
531
|
+
exporter = JsonlOutboxExporter(path) # offline
|
|
532
|
+
exporter = KafkaOutboxExporter(...) # live, from another package
|
|
533
|
+
exporter.export_scores(run, comparison, grader_types=..., ...)
|
|
534
|
+
|
|
535
|
+
"""
|
|
536
|
+
|
|
537
|
+
def export_scores(
|
|
538
|
+
self,
|
|
539
|
+
run: models.RunResult,
|
|
540
|
+
comparison: models.Comparison | None = None,
|
|
541
|
+
**kwargs,
|
|
542
|
+
) -> int:
|
|
543
|
+
"""Export a run's rows and return how many were exported.
|
|
544
|
+
|
|
545
|
+
Args:
|
|
546
|
+
run: The completed run.
|
|
547
|
+
comparison: The gate result, on the candidate half of a gate;
|
|
548
|
+
left off the baseline half, which was not itself gated.
|
|
549
|
+
kwargs: Passed through to :func:`score_rows` - ``baseline_run_id``,
|
|
550
|
+
``grader_types`` and ``judge_scales``.
|
|
551
|
+
|
|
552
|
+
Returns:
|
|
553
|
+
The number of rows exported.
|
|
554
|
+
|
|
555
|
+
"""
|
|
556
|
+
...
|
|
557
|
+
|
|
558
|
+
|
|
514
559
|
class JsonlOutboxExporter:
|
|
515
560
|
"""Append score rows to a JSONL outbox for a shipper to drain.
|
|
516
561
|
|
|
517
562
|
A no-network stand-in for direct ingestion: real deployments point a
|
|
518
|
-
shipper at this file, or
|
|
519
|
-
|
|
563
|
+
shipper at this file, or swap in another :class:`ScoreExporter` that
|
|
564
|
+
writes to the store directly.
|
|
520
565
|
"""
|
|
521
566
|
|
|
522
567
|
def __init__(self, outbox_path: str | pathlib.Path):
|
|
@@ -338,6 +338,36 @@ class GraderLookupTests(unittest.TestCase):
|
|
|
338
338
|
name = 'non_empty'
|
|
339
339
|
|
|
340
340
|
|
|
341
|
+
class ScoreExporterProtocolTests(unittest.TestCase):
|
|
342
|
+
"""The seam another package implements to publish to a real store."""
|
|
343
|
+
|
|
344
|
+
def test_the_builtin_exporter_satisfies_it(self):
|
|
345
|
+
exporter = store.JsonlOutboxExporter('/dev/null')
|
|
346
|
+
self.assertIsInstance(exporter, store.ScoreExporter)
|
|
347
|
+
|
|
348
|
+
def test_an_outside_implementation_satisfies_it(self):
|
|
349
|
+
"""A store-side exporter: same method, its own transport."""
|
|
350
|
+
|
|
351
|
+
class Collecting:
|
|
352
|
+
def __init__(self):
|
|
353
|
+
self.rows = []
|
|
354
|
+
|
|
355
|
+
def export_scores(self, run, comparison=None, **kwargs):
|
|
356
|
+
self.rows = store.score_rows(run, comparison, **kwargs)
|
|
357
|
+
return len(self.rows)
|
|
358
|
+
|
|
359
|
+
exporter = Collecting()
|
|
360
|
+
self.assertIsInstance(exporter, store.ScoreExporter)
|
|
361
|
+
self.assertEqual(exporter.export_scores(_run()), len(exporter.rows))
|
|
362
|
+
|
|
363
|
+
def test_missing_the_method_does_not(self):
|
|
364
|
+
class NotAnExporter:
|
|
365
|
+
def export(self, run): # pragma: no cover - never called
|
|
366
|
+
return 0
|
|
367
|
+
|
|
368
|
+
self.assertNotIsInstance(NotAnExporter(), store.ScoreExporter)
|
|
369
|
+
|
|
370
|
+
|
|
341
371
|
class ExporterTests(unittest.TestCase):
|
|
342
372
|
def test_export_scores(self):
|
|
343
373
|
with tempfile.TemporaryDirectory() as tmp:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|