Skip to content

Commit 8ffa67a

Browse files
justinchubyCopilot
andcommitted
Fix LMEval ONNX evaluator device assignment with recent lm-eval
lm-eval's `LM` base class exposes `device` as a read-only property (backed by `_device`). The ONNX evaluators (`LMEvalORTEvaluator`, `LMEvalORTGenAIEvaluator`) assign `self.device` in `__init__`, which raises `AttributeError: property 'device' ... has no setter` with recent lm-eval versions (e.g. 0.4.12), breaking `get_model('ortgenai')` and `get_model('ort')` based evaluation. Add a `device` property with a setter to the shared `LMEvalOnnxBase` so the existing `self.device = ...` assignments work regardless of lm-eval version. Add a regression test for the settable property. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Justin Chu <11205048+justinchuby@users.noreply.github.com>
1 parent f620daf commit 8ffa67a

2 files changed

Lines changed: 54 additions & 0 deletions

File tree

olive/evaluator/lmeval_ort.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,18 @@
3838
class LMEvalOnnxBase(TemplateLM):
3939
"""Base class for ONNX model evaluation."""
4040

41+
@property
42+
def device(self):
43+
# lm-eval's LM base class exposes ``device`` as a read-only property
44+
# (backed by ``_device``). The ONNX evaluators assign ``self.device``
45+
# in ``__init__`` (e.g. for IOBinding placement), so provide a setter
46+
# to keep that assignment working across lm-eval versions.
47+
return getattr(self, "_device", None)
48+
49+
@device.setter
50+
def device(self, value):
51+
self._device = value
52+
4153
@abstractmethod
4254
def prepare(self, requests: list[LogLikelihoodInputs]):
4355
pass

test/evaluator/test_lmeval_ort.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# -------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License.
4+
# --------------------------------------------------------------------------
5+
import pytest
6+
7+
# The lmeval_ort module imports lm_eval at module load time.
8+
pytest.importorskip("lm_eval")
9+
10+
from olive.evaluator.lmeval_ort import LMEvalOnnxBase
11+
12+
13+
def test_device_property_has_setter():
14+
"""LMEvalOnnxBase must override lm-eval's read-only ``device`` property.
15+
16+
lm-eval's ``LM`` base class exposes ``device`` as a read-only property,
17+
but the ONNX evaluators assign ``self.device`` in ``__init__``. Without a
18+
setter this raises ``AttributeError: property 'device' ... has no setter``.
19+
This is a regression test for that incompatibility.
20+
"""
21+
device_prop = LMEvalOnnxBase.__dict__.get("device")
22+
assert isinstance(device_prop, property)
23+
assert device_prop.fset is not None, "device property must define a setter"
24+
25+
26+
def test_device_property_round_trips():
27+
"""The setter stores and the getter returns the assigned value."""
28+
device_prop = LMEvalOnnxBase.__dict__["device"]
29+
30+
# Use a bare holder to exercise the descriptor without instantiating the
31+
# abstract base class (which has unrelated abstract methods).
32+
class _Holder:
33+
pass
34+
35+
holder = _Holder()
36+
assert device_prop.fget(holder) is None # unset -> None
37+
38+
device_prop.fset(holder, "cuda")
39+
assert device_prop.fget(holder) == "cuda"
40+
41+
device_prop.fset(holder, "cpu")
42+
assert device_prop.fget(holder) == "cpu"

0 commit comments

Comments
 (0)