Skip to content

Commit d897263

Browse files
Wire retrieval fixtures 042/043 (0100/0103)
Un-defer the two retrieval conformance fixtures the v0.107.0 pin surfaced: - 042 (malformed chunk usage, 0100): the contains_event matcher compared a record-valued field (EmbeddingUsage) against a dict with ==, which never matched. Promote wire.py's record-to-dict coercion to a shared as_record_mapping helper and use it for a subset match; a named subkey must be present, not just non-null. - 043 (over-cap chunk-and-stitch, 0103): thread the conformance-adapter chunk_size cap override into the OpenAI embed provider construction (absent -> the fixed vendor 2048). The present-key tightening is applied to the carries matcher too, so the two subset comparisons stay consistent. Adds a unit test for the field comparator. Harness-only; no src change. 052 (same-name collision) stays deferred for the batched spec review.
1 parent bce6ca8 commit d897263

4 files changed

Lines changed: 63 additions & 30 deletions

File tree

conformance.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -974,7 +974,7 @@ note = "The §8.4 Cohere embed mapping recognizes input_type query / document /
974974
[proposals."0100"]
975975
status = "implemented"
976976
since = "0.16.0"
977-
note = "A provider figure present on the wire but malformed (a token count as a string / negative / boolean) in an ancillary field (usage, response_id) is treated as not reported: the call succeeds, the figure is nulled, and the verbatim value stays on raw. An implementation MUST NOT raise on it and MUST NOT fabricate / coerce / clamp / repair it. Each figure is judged independently and the outcome follows §4 / §6's record rules; the rule binds the graph-engine §6 EmbeddingEvent / RerankEvent figures as well as the response. Behavior shipped + unit-tested ahead of the pin; the malformed-usage / malformed-id / chunk-stitch fixtures (incl. retrieval 042) ride the v0.17.0 fixture-wiring PR."
977+
note = "A provider figure present on the wire but malformed (a token count as a string / negative / boolean) in an ancillary field (usage, response_id) is treated as not reported: the call succeeds, the figure is nulled, and the verbatim value stays on raw. An implementation MUST NOT raise on it and MUST NOT fabricate / coerce / clamp / repair it. Each figure is judged independently and the outcome follows §4 / §6's record rules; the rule binds the graph-engine §6 EmbeddingEvent / RerankEvent figures as well as the response. Behavior shipped + unit-tested ahead of the pin; retrieval fixture 042 (chunk-and-stitch malformed usage) runs -- its chunk-stitch usage assertion needed the contains_event record-subset match wired into the harness."
978978

979979
# Spec v0.96.0 (proposal 0101). A malformed llm usage counter is not
980980
# reported; the observability guard chain reconciled (llm-provider §7).
@@ -997,7 +997,7 @@ note = "Generalizes the carries directive capability-neutrally (a key MUST name
997997
[proposals."0103"]
998998
status = "implemented"
999999
since = "0.17.0"
1000-
note = "§8.3's cap sentence pins that the §8 Batch chunking rule chunks by input COUNT only (the 2048-input cap); OpenAI's summed-token ceiling is NOT a chunking trigger, so an over-token request is sent and the provider's rejection surfaces as provider_invalid_request (no client-side token estimation). The mapping already implements count-based chunking (0092); the OpenAI embed provider gained a test-only chunk_size cap override (conformance-adapter §5.14) so a fixed-cap mapping's chunking fixture is self-enforcing. Behavior shipped + unit-tested ahead of the pin; fixture 043 (over-cap chunk-and-stitch + count-vs-token fail-loud) + the §5.14 harness directive ride the v0.17.0 fixture-wiring PR."
1000+
note = "§8.3's cap sentence pins that the §8 Batch chunking rule chunks by input COUNT only (the 2048-input cap); OpenAI's summed-token ceiling is NOT a chunking trigger, so an over-token request is sent and the provider's rejection surfaces as provider_invalid_request (no client-side token estimation). The mapping already implements count-based chunking (0092); the OpenAI embed provider gained a test-only chunk_size cap override (conformance-adapter §5.14) so a fixed-cap mapping's chunking fixture is self-enforcing. Behavior shipped + unit-tested ahead of the pin; fixture 043 (over-cap chunk-and-stitch + count-vs-token fail-loud) runs, with the §5.14 chunk_size cap override threaded to the OpenAI embed provider construction in the harness."
10011001

10021002
# Spec v0.99.0 (proposal 0104). An empty-string response_id is null
10031003
# (absent); §8.2 Jina maps a bare 400 to provider_invalid_request

tests/conformance/harness/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from .loader import discover_fixtures, load_fixture
2626
from .skip import SkipReason
2727
from .wire import (
28+
as_record_mapping,
2829
assert_error_carries,
2930
assert_response_format_absent,
3031
assert_system_references_schema,
@@ -39,6 +40,7 @@
3940
"GraphFixture",
4041
"LlmProviderFixture",
4142
"SkipReason",
43+
"as_record_mapping",
4244
"assert_error_carries",
4345
"assert_response_format_absent",
4446
"assert_system_references_schema",

tests/conformance/harness/wire.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -183,17 +183,19 @@ def assert_error_carries(exc: BaseException, carries: Mapping[str, Any]) -> None
183183
actual = _get_carries_attr(exc, key)
184184
if isinstance(expected, Mapping):
185185
# A mapping-valued field (e.g. usage) is a subset match: each
186-
# named key must equal the actual's value, reading a pydantic
187-
# record's fields (Usage) or a plain mapping. Keys the fixture
188-
# omits are ignored (the record MAY carry optional extras).
189-
actual_map = _as_carries_mapping(actual)
186+
# named key must be PRESENT on the actual with an equal value,
187+
# reading a pydantic record's fields (Usage) or a plain mapping.
188+
# Keys the fixture omits are ignored (the record MAY carry
189+
# optional extras); requiring presence keeps a named key expected
190+
# as null from matching a record that simply lacks the field.
191+
actual_map = as_record_mapping(actual)
190192
if actual_map is None:
191193
raise AssertionError(
192194
f"carries check failed: {key!r} is not a mapping/record "
193195
f"(got {type(actual).__name__}); cannot subset-match {expected!r}"
194196
)
195197
for subkey, subval in cast("Mapping[str, Any]", expected).items():
196-
if actual_map.get(subkey) != subval:
198+
if subkey not in actual_map or actual_map[subkey] != subval:
197199
raise AssertionError(
198200
f"carries check failed: {key!r}[{subkey!r}] "
199201
f"actual={actual_map.get(subkey)!r}, expected={subval!r}"
@@ -204,10 +206,11 @@ def assert_error_carries(exc: BaseException, carries: Mapping[str, Any]) -> None
204206
)
205207

206208

207-
def _as_carries_mapping(value: Any) -> Mapping[str, Any] | None:
208-
"""Coerce a carries attribute to a mapping for subset comparison: a plain
209-
Mapping passes through; a pydantic record (e.g. Usage) is dumped to a dict;
210-
anything else returns None (not comparable as a mapping)."""
209+
def as_record_mapping(value: Any) -> Mapping[str, Any] | None:
210+
"""Coerce a value to a mapping for subset comparison: a plain Mapping
211+
passes through; a pydantic record (e.g. Usage / EmbeddingUsage) is dumped to
212+
a dict; anything else returns None (not comparable as a mapping). Shared by
213+
the carries matcher (here) and the typed-event contains_event matcher."""
211214
if isinstance(value, Mapping):
212215
return cast("Mapping[str, Any]", value)
213216
dump = getattr(value, "model_dump", None)

tests/conformance/test_retrieval_provider.py

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
CohereEmbeddingProvider,
3434
CohereRerankProvider,
3535
EmbeddingRuntimeConfig,
36+
EmbeddingUsage,
3637
JinaEmbeddingProvider,
3738
JinaRerankProvider,
3839
OpenAIEmbeddingProvider,
@@ -42,6 +43,7 @@
4243
)
4344

4445
from ._deferral import skip_if_deferred
46+
from .harness import as_record_mapping
4547

4648
# The wire endpoint each mapping posts to, keyed by the fixture's ``mapping``
4749
# directive (None => the harness-internal default). The wire-capture assertion
@@ -91,27 +93,13 @@
9193
# TeiEmbeddingProvider.embed() chunk-and-stitches by its construction chunk_size
9294
# via the shared chunk_and_stitch_embed helper (also adopted by Cohere /v2/embed),
9395
# so no retrieval fixtures remained deferred at that pin. The v0.107.0 pin bump
94-
# then re-defers 042 / 043 / 052, whose fixture-model wiring rides the v0.17.0
95-
# fixture-wiring PR (see the categorized entries below).
96+
# re-deferred 042 / 043 / 052; 042 (contains_event record match) and 043
97+
# (chunk_size directive) are now wired, leaving only 052 (the same-name declared
98+
# collision, held for the batched spec review).
9699
_DEFERRED_FIXTURES: dict[str, str] = {
97100
# v0.17.0 spec-pin bump (v0.88.0 -> v0.107.0). Behavior for each of
98101
# these shipped + is unit-tested ahead of the pin; the conformance
99102
# fixture wiring rides the v0.17.0 fixture-wiring PR.
100-
# Proposal 0100 (spec v0.95.0) malformed ancillary figure not reported.
101-
# Behavior shipped in v0.16.0 (chunk-stitch response_id nulling + all-or-
102-
# nothing usage, 0092/0093); the gap is the `contains_event` matcher, which
103-
# compares a record-valued field (usage) with `==` against a dict expectation
104-
# and so never matches an EmbeddingUsage object. Matcher fix rides PR 6.
105-
"042-embed-chunk-malformed-usage-not-reported": (
106-
"Proposal 0100 malformed ancillary figure; contains_event matcher (record-vs-dict) "
107-
"rides the v0.17.0 fixture-wiring PR"
108-
),
109-
# Proposal 0103 (spec v0.98.0) §8.3 over-cap chunk-and-stitch + the
110-
# conformance-adapter §5.14 chunk_size construction directive.
111-
"043-embed-openai-chunk-and-stitch": (
112-
"Proposal 0103 chunk_size construction directive (conformance-adapter §5.14); "
113-
"harness wiring rides the v0.17.0 fixture-wiring PR"
114-
),
115103
# Proposal 0108 (spec v0.103.0) same-NAME declared-field collision. The
116104
# dimensions reject is coded (openai.py) but a declared-field-named extras
117105
# key routes to the declared field, never into model_extra, so the collision
@@ -233,6 +221,8 @@ def _build_provider(
233221
transport=transport,
234222
query_prefix=cast("str | None", block.get("query_prefix")),
235223
document_prefix=cast("str | None", block.get("document_prefix")),
224+
# §5.14 test-only cap override; absent -> the fixed vendor 2048.
225+
chunk_size=cast("int | None", block.get("chunk_size")),
236226
)
237227
else:
238228
# The pre-0079 protocol fixtures (001-012, mapping absent) carry no
@@ -559,11 +549,30 @@ def _cleanup() -> None:
559549
return events, _cleanup
560550

561551

552+
def _contains_field_matches(actual: Any, expected: Any) -> bool:
553+
# A Mapping-valued expectation (e.g. usage: {input_tokens: 500}) is a subset
554+
# match against a record-valued field (EmbeddingUsage): coerce the field to a
555+
# dict and require each named key to be PRESENT with an equal value, ignoring
556+
# keys the fixture omits. Requiring presence keeps a named key expected as
557+
# null from matching a record that simply lacks the field. Scalars and null
558+
# use plain equality (so usage: null matches a null record).
559+
if isinstance(expected, Mapping):
560+
actual_map = as_record_mapping(actual)
561+
if actual_map is None:
562+
return False
563+
return all(
564+
k in actual_map and actual_map[k] == v for k, v in cast("Mapping[str, Any]", expected).items()
565+
)
566+
return bool(actual == expected)
567+
568+
562569
def _assert_contains_event(events: list[Any], expected: Mapping[str, Any]) -> None:
563570
"""Assert each observer's contains_event against the collected events.
564571
565572
``contains_event`` names an event_type and a fields mapping; a collected
566-
event of that type must match every listed field (dict / scalar equality).
573+
event of that type must match every listed field. A scalar / null field uses
574+
equality; a mapping-valued field is a subset match against the event's record
575+
field (e.g. ``usage: {input_tokens: 500}`` vs an ``EmbeddingUsage``).
567576
"""
568577
observers = cast("Mapping[str, Any]", expected.get("observers") or {})
569578
for obs_name, obs_expect in observers.items():
@@ -575,7 +584,9 @@ def _assert_contains_event(events: list[Any], expected: Mapping[str, Any]) -> No
575584
want_fields = cast("Mapping[str, Any]", contains.get("fields") or {})
576585
candidates = [e for e in events if type(e).__name__ == event_type]
577586
for event in candidates:
578-
if all(getattr(event, key, None) == val for key, val in want_fields.items()):
587+
if all(
588+
_contains_field_matches(getattr(event, key, None), val) for key, val in want_fields.items()
589+
):
579590
break
580591
else:
581592
raise AssertionError(
@@ -683,3 +694,20 @@ async def _run_rerank_case(
683694
finally:
684695
cleanup()
685696
await provider.aclose()
697+
698+
699+
def test_contains_field_matches() -> None:
700+
# The contains_event field comparator: scalars / null by equality, a mapping
701+
# expectation by record-subset (present-key required).
702+
assert _contains_field_matches("embed_node", "embed_node")
703+
assert not _contains_field_matches("a", "b")
704+
assert _contains_field_matches(None, None)
705+
# A mapping expectation subset-matches a record, ignoring unnamed fields.
706+
assert _contains_field_matches(EmbeddingUsage(input_tokens=500), {"input_tokens": 500})
707+
assert _contains_field_matches({"a": 1, "b": 2}, {"a": 1})
708+
# A mapping expectation never matches a null / non-record actual.
709+
assert not _contains_field_matches(None, {"input_tokens": 500})
710+
# A named key the record lacks does not match even when expected null: the
711+
# present-key requirement keeps a missing field from reading as an explicit
712+
# null (the .get()-based comparison this replaced would have matched here).
713+
assert not _contains_field_matches(EmbeddingUsage(input_tokens=500), {"missing": None})

0 commit comments

Comments
 (0)