Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4e243b4
fix(summaries): discard long excerpt lines with bounded streaming reads
morluto Aug 11, 2026
4252a68
fix(oracles): derive validation status from coherent receipt evidence
morluto Aug 11, 2026
dd9148c
fix(inference): compare protocol facets structurally instead of delim…
morluto Aug 11, 2026
f2133ac
fix(inference): record effective quantization instead of rewriting un…
morluto Aug 11, 2026
02646ca
fix(release): pin twine through lockfile and make final artifacts imm…
morluto Aug 11, 2026
0486592
fix(vllm): preserve exact percentile identity and reject incoherent a…
morluto Aug 11, 2026
17e945a
chore: exclude workflow file from release commit (pushable separately)
morluto Aug 11, 2026
b4d6734
fix(faults): randomize or counterbalance treatment order instead of a…
morluto Aug 11, 2026
39cb8cc
fix(workloads): validate scalar choices by exact JSON type and value …
morluto Aug 11, 2026
529aefb
fix: address oracle parsing review feedback
morluto Aug 11, 2026
7d3ef1b
fix(workloads): preserve typed scaling values
morluto Aug 11, 2026
9cf4a3f
test: update collection receipt for scaling regression
morluto Aug 11, 2026
cd57b18
fix(experiments): support explicit factor baselines
morluto Aug 11, 2026
0a221bf
ci(release): run twine from the locked environment
morluto Aug 11, 2026
7aeb3c6
fix(faults): record randomized block orders
morluto Aug 11, 2026
d79dd8e
fix(inference): require bound quantization for validity
morluto Aug 11, 2026
e6ba710
test(inference): bind complete protocol quantization
morluto Aug 11, 2026
6c19bc1
fix(experiments): preserve explicit baseline identities
morluto Aug 11, 2026
6ebc992
fix(scaling): retain numeric input kinds
morluto Aug 11, 2026
4762de6
fix(scaling): reject mixed numeric input fits
morluto Aug 11, 2026
2ee72e6
fix(domain): bound variant label length
morluto Aug 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ dev = [
"pyarrow-stubs>=20.0.0.20260625",
"ruff>=0.11",
"scipy-stubs>=1.18.0.1,<1.19",
"twine>=6.1",
Comment thread
morluto marked this conversation as resolved.
"vulture>=2.14",
]

Expand Down
26 changes: 24 additions & 2 deletions src/flameox/adapters/inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,17 @@ class AIPerfCorrelationSummary(ContractModel):
# vLLM aggregate benchmark-result JSON normalization
# ---------------------------------------------------------------------------
#
def _percentile_label(percentile: int | float) -> str:
"""Return a canonical label for a percentile rank, preserving fractional precision.

``int()`` truncation made p99.1 and p99.9 both produce ``p99``, losing
the exact percentile identity in the metric name.
"""
if float(percentile).is_integer():
return str(int(percentile))
return str(float(percentile))


# vLLM's ``benchmark_serving.BenchmarkMetrics`` dataclass is serialized by the
# Mooncake replayer (and other vLLM benchmark scripts) as a JSON object whose
# percentile fields are lists of ``[percentile, value_ms]`` pairs. The parser
Expand Down Expand Up @@ -907,6 +918,10 @@ def non_negative_latency(self) -> VllmAggregateMetrics:
"median_itl_ms",
"mean_e2el_ms",
"median_e2el_ms",
"std_ttft_ms",
"std_tpot_ms",
"std_itl_ms",
"std_e2el_ms",
):
if getattr(self, name) < 0:
raise ValueError(f"{name} must be non-negative")
Expand Down Expand Up @@ -945,6 +960,13 @@ class VllmResultDocument(ContractModel):
actual_duration: Annotated[float, Field(ge=0)]
time_scale: Annotated[float, Field(gt=0)] = 1.0

@field_validator("actual_duration", "time_scale")
@classmethod
def finite_duration_and_scale(cls, value: float) -> float:
if not math.isfinite(value):
raise ValueError("duration and time_scale must be finite")
return value

@model_validator(mode="after")
def totals_match(self) -> VllmResultDocument:
if self.successful_requests + self.failed_requests != self.total_requests:
Expand Down Expand Up @@ -1038,7 +1060,7 @@ def _normalize_document(payload: dict[str, Any]) -> dict[str, Any]:
"successful_requests": completed,
"failed_requests": max(0, total_requests - completed),
"total_requests": total_requests,
"actual_duration": payload.get("duration", 0.0),
"actual_duration": payload["duration"],
Comment thread
morluto marked this conversation as resolved.
Outdated
"time_scale": 1.0,
}

Expand Down Expand Up @@ -1163,7 +1185,7 @@ def add(
add(f"vllm.{label}.std_ms", std, unit="ms", aggregation="std", extra={"stat": "std"})
for percentile, value in getattr(metrics, f"percentiles_{metric}_ms"):
add(
f"vllm.{label}.p{int(percentile)}_ms",
f"vllm.{label}.p{_percentile_label(percentile)}_ms",
float(value),
unit="ms",
aggregation="percentile",
Expand Down
12 changes: 10 additions & 2 deletions src/flameox/analysis/inference_protocol.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import json
from collections.abc import Callable
from enum import StrEnum
from typing import Annotated, Literal
Expand Down Expand Up @@ -404,10 +405,17 @@ def _facets() -> tuple[_FacetGetter, ...]:
)




def _normalize(value: object) -> str:
"""Return a display string for a protocol facet value.

For dict values, use canonical JSON serialization (sorted keys) so that
keys/values containing commas or equals signs cannot produce colliding
normal forms. Plain scalar values use ``str()`` as before.
"""
if isinstance(value, dict):
items = sorted(value.items(), key=lambda item: item[0])
return ",".join(f"{k}={v}" for k, v in items)
return json.dumps(value, sort_keys=True, ensure_ascii=False)
if isinstance(value, bool):
return "true" if value else "false"
if value is None:
Expand Down
2 changes: 1 addition & 1 deletion src/flameox/application/inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -1556,7 +1556,7 @@ def _protocol_identity(
model_revision=plan.model_revision,
tokenizer_id=plan.tokenizer or plan.model,
tokenizer_revision=plan.tokenizer_revision or plan.model_revision,
quantization=plan.quantization or "none",
quantization=plan.quantization,
Comment thread
morluto marked this conversation as resolved.
),
server=ServerConfigIdentity(
backend=plan.server_provider.value,
Expand Down
21 changes: 19 additions & 2 deletions src/flameox/application/summaries.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import json
import re
from enum import StrEnum
from typing import Annotated, Literal, cast
from typing import Annotated, Literal, TextIO, cast

from pydantic import Field, JsonValue, model_validator

Expand All @@ -28,6 +28,23 @@
from flameox.models import ContractModel
from flameox.storage import ArtifactStore, JsonRecordStore, RunStore, Workspace

_DISCARD_CHUNK_SIZE = 4096


def _discard_rest_of_line(stream: TextIO) -> None:
"""Discard the remainder of an overlong line without materializing it.

Instead of calling unbounded ``stream.readline()`` which loads the
entire remaining line into memory, read fixed-size chunks until a
newline or EOF is encountered. Peak memory is bounded by
``_DISCARD_CHUNK_SIZE``, not by the physical line length.
"""
while True:
chunk = stream.readline(_DISCARD_CHUNK_SIZE)
if not chunk or chunk.endswith("\n"):
break


_ANSI_ESCAPE = re.compile(r"\x1b(?:\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1b\\))")
_CONTROL = re.compile(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]")

Expand Down Expand Up @@ -566,7 +583,7 @@ def _excerpt(self, artifact_id: str) -> tuple[tuple[str, ...], bool]:
break
if len(line) == 201 and not line.endswith("\n"):
truncated = True
stream.readline()
_discard_rest_of_line(stream)
selected.append(_CONTROL.sub("", _ANSI_ESCAPE.sub("", line.rstrip("\r\n"))))
if stream.readline(1):
truncated = True
Expand Down
29 changes: 29 additions & 0 deletions src/flameox/domain/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,35 @@ def bounded_coordinate(cls, value: tuple[str | int, ...]) -> tuple[str | int, ..
return value


@model_validator(mode="after")
def status_must_be_coherent_with_evidence(self) -> OracleReceiptV1:
"""Reject receipts where categorical status contradicts quantitative evidence.

When both ``absolute_error`` and ``tolerance.absolute`` are present,
a ``status='pass'`` receipt with error exceeding tolerance is rejected,
and a ``status='fail'`` receipt with error within tolerance is rejected.

Receipts without quantitative evidence (no error/tolerance) are
accepted with any status since there is nothing to contradict.
"""
if self.absolute_error is not None and self.tolerance is not None:
abs_tol = self.tolerance.absolute
if abs_tol is not None:
if self.status == "pass" and self.absolute_error > abs_tol:
raise ValueError(
f"Receipt status='pass' contradicts evidence: "
f"absolute_error={self.absolute_error} exceeds "
f"tolerance.absolute={abs_tol}"
)
if self.status == "fail" and self.absolute_error <= abs_tol:
raise ValueError(
f"Receipt status='fail' contradicts evidence: "
f"absolute_error={self.absolute_error} is within "
f"tolerance.absolute={abs_tol}"
Comment thread
morluto marked this conversation as resolved.
Outdated
)
return self


class OracleReceiptRecord(ContractModel):
receipt: OracleReceiptV1
receipt_artifact_id: Digest
Expand Down
20 changes: 20 additions & 0 deletions tests/analysis/test_inference_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,3 +366,23 @@ def test_server_rejects_out_of_range_gpu_utilization() -> None:
def test_oracle_result_rejects_empty_reason() -> None:
with pytest.raises(ValueError):
OracleResult(status=OracleStatus.PASS, reason="")


def test_kv_transfer_config_with_delimiter_chars_does_not_collide() -> None:
"""Distinct KV-transfer configs with commas/equals in keys/values must not collide.

Regression for #288: the old ``_normalize()`` used unescaped
``",".join(f"{k}={v}")`` which could make distinct dicts produce the
same normalized string. The fix uses canonical JSON serialization.
"""
from flameox.analysis.inference_protocol import _normalize

config_a = {"a=b,c": "d"}
config_b = {"a": "b=c,d"}

norm_a = _normalize(config_a)
norm_b = _normalize(config_b)
assert norm_a != norm_b, (
f"Distinct configs must not collide: {config_a!r} vs {config_b!r} "
f"both normalized to {norm_a!r}"
)
23 changes: 23 additions & 0 deletions tests/application/test_summaries.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,26 @@ def test_trial_failure_class_fallback_for_null_column_value() -> None:

fallback = TrialFailureClass(str(None) if None is not None else TrialFailureClass.NONE)
assert fallback is TrialFailureClass.NONE


def test_excerpt_discards_overlong_line_in_bounded_chunks() -> None:
"""Overlong lines must not be materialized during excerpt extraction.

Regression for #291: ``_excerpt()`` called unbounded ``stream.readline()``
to discard the remainder of a line longer than 200 characters, materializing
a multi-gigabyte string in memory. The fix uses bounded ``_DISCARD_CHUNK_SIZE``
chunks instead.
"""
import io

from flameox.application.summaries import _discard_rest_of_line

long_line = "X" * 10_000
content = f"{long_line}\nshort line\n"
stream = io.StringIO(content)
line = stream.readline(201)
assert len(line) == 201
assert not line.endswith("\n")
_discard_rest_of_line(stream)
next_line = stream.readline()
assert next_line == "short line\n"
57 changes: 57 additions & 0 deletions tests/domain/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
ExecutionRunManifest,
ImportRunManifest,
Integrity,
OracleStatus,
ScalarOracleReceiptValue,
SucceededTrial,
parse_capture_plan,
Expand Down Expand Up @@ -574,3 +575,59 @@ def test_managed_runtime_extra_parser_owns_the_persisted_vocabulary() -> None:
CapabilityExtra.TORCH,
CapabilityExtra.TRACE,
)




def test_oracle_receipt_rejects_pass_with_error_exceeding_tolerance() -> None:
"""Cross-field validation: status='pass' with error > tolerance must be rejected."""
from flameox.domain.models import OracleReceiptV1, OracleTolerance

with pytest.raises(ValidationError, match="contradicts evidence"):
OracleReceiptV1(
schema_version="flameox.oracle-receipt.v1",
status=OracleStatus.PASS,
reason="test-ok",
absolute_error=100.0,
tolerance=OracleTolerance(absolute=0.001),
)


def test_oracle_receipt_rejects_fail_with_error_within_tolerance() -> None:
"""Cross-field validation: status='fail' with error within tolerance must be rejected."""
from flameox.domain.models import OracleReceiptV1, OracleTolerance

with pytest.raises(ValidationError, match="contradicts evidence"):
OracleReceiptV1(
schema_version="flameox.oracle-receipt.v1",
status=OracleStatus.FAIL,
reason="test-bad",
absolute_error=0.0,
tolerance=OracleTolerance(absolute=1.0),
)


def test_oracle_receipt_accepts_pass_with_error_within_tolerance() -> None:
"""Cross-field validation: status='pass' with error within tolerance is accepted."""
from flameox.domain.models import OracleReceiptV1, OracleTolerance

receipt = OracleReceiptV1(
schema_version="flameox.oracle-receipt.v1",
status=OracleStatus.PASS,
reason="test-ok",
absolute_error=0.001,
tolerance=OracleTolerance(absolute=0.01),
)
assert receipt.status == OracleStatus.PASS


def test_oracle_receipt_accepts_pass_without_quantitative_evidence() -> None:
"""Receipts without error/tolerance evidence are accepted with any status."""
from flameox.domain.models import OracleReceiptV1

receipt = OracleReceiptV1(
schema_version="flameox.oracle-receipt.v1",
status=OracleStatus.PASS,
reason="test-ok",
)
assert receipt.status == OracleStatus.PASS
Loading
Loading