Skip to content

Commit 3e74040

Browse files
committed
T2.1h: the runner records a ranking's question ids and list cells
python-core's runner (contract/runners/run_python.py) learns what a ranking's predict carries, before the package that makes one: - the request id of each later question of RequestState.ranking (contract v26, the next commit) joins the ids the run's calls must carry, in order; a question the core asks no call for, its context holding one class, is passed over, at the place its id would come, so a call with an id out of order or unknown still fails the run; - a list column of a result (a ranking's rankings and scores, an item's own) is recorded as the pure-Rust host records one ({"list": [cell, ...]}, the test author's host.rs at a812afd), each member a cell of its type. A judging-path file: it lands alone, before the rest of T2.8, since the gate runs the checkpoint's runner, whose request-id check fails a ranking that reaches predict. Signed-off-by: Adi Wadaskar <awadaskar@nvidia.com> (cherry picked from commit bfb8f44) Signed-off-by: Adi Wadaskar <awadaskar@nvidia.com>
1 parent f42d92e commit 3e74040

1 file changed

Lines changed: 43 additions & 4 deletions

File tree

‎contract/runners/run_python.py‎

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,7 @@ def __init__(self) -> None:
588588
self._state: dict[str, Any] | None = None
589589
self._request_id: str | None = None
590590
self._request_ids: list[str] = []
591+
self._skippable: set[str] = set()
591592
self._call: int | None = None
592593
self._body: dict[str, Any] | None = None
593594
self._bodies: list[dict[str, Any]] = []
@@ -623,12 +624,25 @@ def started(self, message: bytes, attachments: Any) -> None:
623624
(batch['request_id'], batch['payload'])
624625
for batch in state.get('batches') or ()
625626
]
627+
# A ranking's later questions (T2.8), each the id the host
628+
# gave it, in order.
629+
+ [
630+
(part['request_id'], part['payload'])
631+
for part in (state.get('ranking') or {}).get('parts', ())
632+
]
626633
)
627634
self._request_ids = [
628635
request_id
629636
for request_id, payload in steps
630637
if not _nothing_to_score(json.loads(attachments[payload]))
631638
]
639+
# A ranking's question whose context holds one answer is asked
640+
# no call (T2.8): its id may be passed over, in order.
641+
self._skippable = (
642+
{request_id for request_id, _ in steps}
643+
if state.get('ranking') is not None
644+
else set()
645+
)
632646
self.inputs.append(
633647
{
634648
'op': 'predict',
@@ -762,6 +776,7 @@ def done(self, result: dict[str, Any]) -> None:
762776
self._state = result['state']
763777
self.materialized = {'version': result['state']['version']}
764778
if result.get('op') == 'predict':
779+
self._passed_over()
765780
if self._request_ids:
766781
raise RunnerFailure(
767782
'predict answered without calling the model for a step '
@@ -785,6 +800,7 @@ def taken(self, attachment: int, value: Any) -> None:
785800
self._bodies.append(self._body)
786801
# The next step with rows to score's id, and no call past them.
787802
carried = self._body.get('metadata', {}).get('request_id')
803+
self._passed_over(carried)
788804
if not self._request_ids or carried != self._request_ids.pop(0):
789805
raise RunnerFailure(
790806
'the model call does not carry the request id predict '
@@ -808,6 +824,16 @@ def taken(self, attachment: int, value: Any) -> None:
808824
if attachment == state['query'] and isinstance(value, bytes):
809825
self.materialized['query'] = json.loads(value)
810826

827+
def _passed_over(self, carried: str | None = None) -> None:
828+
r"""Drops the ids of a ranking's questions asked no call before the
829+
call carrying ``carried``, or, with none, all that are left."""
830+
while (
831+
self._request_ids
832+
and self._request_ids[0] != carried
833+
and self._request_ids[0] in self._skippable
834+
):
835+
self._request_ids.pop(0)
836+
811837
def core(self) -> dict[str, Any]:
812838
core: dict[str, Any] = {
813839
'inputs': self.inputs,
@@ -830,7 +856,9 @@ def _result_rows(stream: Any) -> dict[str, Any]:
830856
hex digits, so no text form of a float is compared) or text
831857
(``utf8``); from T1.28b a boolean (``bool``) and a class's
832858
probabilities (``map``: each class and its probability's bits, in
833-
order)."""
859+
order); from T2.8 a list (``list``: a ranking's scores and rankings, an
860+
item's own, each member a cell as above, a null ``None``), as the
861+
pure-Rust host records one."""
834862
import pyarrow as pa
835863

836864
table = pa.RecordBatchReader._import_from_c_capsule(
@@ -846,13 +874,22 @@ def _result_rows(stream: Any) -> dict[str, Any]:
846874
def kind_of(field: Any) -> str:
847875
if pa.types.is_map(field.type):
848876
return 'map'
877+
if pa.types.is_list(field.type) or pa.types.is_large_list(field.type):
878+
return 'list'
849879
return kinds.get(field.type, 'other')
850880

851881
columns = [
852882
{'name': field.name, 'type': kind_of(field)} for field in table.schema
853883
]
884+
# Each list column's members' kind, as a cell of their type records.
885+
members = [
886+
kinds.get(field.type.value_type, 'other')
887+
if kind_of(field) == 'list'
888+
else None
889+
for field in table.schema
890+
]
854891

855-
def cell(kind: str, value: Any) -> Any:
892+
def cell(kind: str, value: Any, member: str | None = None) -> Any:
856893
if value is None:
857894
return None
858895
if kind == 'int64':
@@ -867,14 +904,16 @@ def cell(kind: str, value: Any) -> Any:
867904
return {
868905
'map': [[key, struct.pack('>d', p).hex()] for key, p in value]
869906
}
907+
if kind == 'list' and member is not None:
908+
return {'list': [cell(member, item) for item in value]}
870909
raise RunnerFailure(
871910
'a result column of a type this runner does not record'
872911
)
873912

874913
rows = [
875914
[
876-
cell(column['type'], value)
877-
for column, value in zip(columns, row, strict=True)
915+
cell(column['type'], value, member)
916+
for column, value, member in zip(columns, row, members, strict=True)
878917
]
879918
for row in zip(
880919
*(table.column(i).to_pylist() for i in range(table.num_columns)),

0 commit comments

Comments
 (0)