style(fase1): ruff format spike modules

This commit is contained in:
Kjell Tore Guttormsen 2026-06-24 10:34:27 +02:00
commit c1317689f1
6 changed files with 123 additions and 26 deletions

View file

@ -137,8 +137,11 @@ class FakeChatClient(BaseChatClient):
reply = self._next_reply() reply = self._next_reply()
if stream: if stream:
async def _agen() -> Any: async def _agen() -> Any:
yield ChatResponseUpdate(role="assistant", contents=[{"type": "text", "text": reply}]) yield ChatResponseUpdate(
role="assistant", contents=[{"type": "text", "text": reply}]
)
return self._build_response_stream(_agen()) return self._build_response_stream(_agen())

View file

@ -109,7 +109,9 @@ def _caught_flaw(texts: Sequence[str]) -> bool:
return any(marker in blob for marker in _FLAW_MARKERS) return any(marker in blob for marker in _FLAW_MARKERS)
async def run_live(*, n_rounds: int = DEFAULT_ROUNDS, write_findings: bool = True) -> dict[str, object]: async def run_live(
*, n_rounds: int = DEFAULT_ROUNDS, write_findings: bool = True
) -> dict[str, object]:
"""Gated empirical arm: run maker-checker vs single-agent against a reference """Gated empirical arm: run maker-checker vs single-agent against a reference
project carrying a planted flaw, measure, and write ``findings-a.md``. project carrying a planted flaw, measure, and write ``findings-a.md``.
@ -127,8 +129,12 @@ async def run_live(*, n_rounds: int = DEFAULT_ROUNDS, write_findings: bool = Tru
# --- maker-checker arm (3 roles) --- # --- maker-checker arm (3 roles) ---
proposer = Agent(client, "You propose one concrete cost-saving measure.", name="proposer") proposer = Agent(client, "You propose one concrete cost-saving measure.", name="proposer")
critic = Agent(client, "You critique the proposal and flag any constraint violation.", name="critic") critic = Agent(
validator = Agent(client, "You give the final valid/invalid decision with a reason.", name="validator") client, "You critique the proposal and flag any constraint violation.", name="critic"
)
validator = Agent(
client, "You give the final valid/invalid decision with a reason.", name="validator"
)
names = ["proposer", "critic", "validator"] names = ["proposer", "critic", "validator"]
counter = {"n": 0} counter = {"n": 0}

View file

@ -53,7 +53,9 @@ _NEVER_SATISFIED_LEDGER = json.dumps(
def _magentic(max_round_count: int | None) -> object: def _magentic(max_round_count: int | None) -> object:
"""Build a Magentic workflow whose manager never self-finalizes.""" """Build a Magentic workflow whose manager never self-finalizes."""
manager = StandardMagenticManager( manager = StandardMagenticManager(
agent=Agent(FakeChatClient(default_reply=_NEVER_SATISFIED_LEDGER), "manager", name="manager"), agent=Agent(
FakeChatClient(default_reply=_NEVER_SATISFIED_LEDGER), "manager", name="manager"
),
max_round_count=max_round_count, max_round_count=max_round_count,
) )
worker = Agent(FakeChatClient(default_reply="worker did some work"), "worker", name="worker") worker = Agent(FakeChatClient(default_reply="worker did some work"), "worker", name="worker")
@ -95,9 +97,7 @@ def fresh_workflow() -> tuple[object, tuple[FakeChatClient, FakeChatClient]]:
every call, so no state survives between runs.""" every call, so no state survives between runs."""
c1 = FakeChatClient(default_reply="participant one view") c1 = FakeChatClient(default_reply="participant one view")
c2 = FakeChatClient(default_reply="participant two view") c2 = FakeChatClient(default_reply="participant two view")
workflow = ConcurrentBuilder( workflow = ConcurrentBuilder(participants=[fake_agent(c1, "w1"), fake_agent(c2, "w2")]).build()
participants=[fake_agent(c1, "w1"), fake_agent(c2, "w2")]
).build()
return workflow, (c1, c2) return workflow, (c1, c2)

View file

@ -120,11 +120,15 @@ def _solve_max_feasible(items: list[AffectedItem], fraction: float) -> float:
prob += pulp.lpSum(xs) <= fraction * sum(it.total for it in items) prob += pulp.lpSum(xs) <= fraction * sum(it.total for it in items)
status = prob.solve(solver) status = prob.solve(solver)
if pulp.LpStatus[status] != "Optimal": if pulp.LpStatus[status] != "Optimal":
raise CbcUnavailable(f"CBC did not reach an optimal solution (status={pulp.LpStatus[status]})") raise CbcUnavailable(
f"CBC did not reach an optimal solution (status={pulp.LpStatus[status]})"
)
return float(pulp.value(prob.objective)) return float(pulp.value(prob.objective))
def _monte_carlo(proposal: SavingsProposal, *, fraction: float = MAX_SAVING_FRACTION) -> tuple[float, float, float]: def _monte_carlo(
proposal: SavingsProposal, *, fraction: float = MAX_SAVING_FRACTION
) -> tuple[float, float, float]:
"""Vary uncertain unit-costs (seeded) and return (P10, P50, P90) of the feasible """Vary uncertain unit-costs (seeded) and return (P10, P50, P90) of the feasible
saving. Uses the LP's closed-form optimum (= fraction x sum of sampled totals), which saving. Uses the LP's closed-form optimum (= fraction x sum of sampled totals), which
is exact here, so we do NOT spawn a CBC subprocess per sample (D6).""" is exact here, so we do NOT spawn a CBC subprocess per sample (D6)."""

View file

@ -63,9 +63,12 @@ def similarity(query: ProposalFeatures, candidate: ProposalFeatures) -> float:
"""Weighted structural similarity in [0, 1] — text is ignored by design.""" """Weighted structural similarity in [0, 1] — text is ignored by design."""
codes = _jaccard(query.affected_codes, candidate.affected_codes) codes = _jaccard(query.affected_codes, candidate.affected_codes)
measure = 1.0 if query.measure_type == candidate.measure_type else 0.0 measure = 1.0 if query.measure_type == candidate.measure_type else 0.0
magnitude = 1.0 if _magnitude_bucket(query.claimed_saving_nok) == _magnitude_bucket( magnitude = (
candidate.claimed_saving_nok 1.0
) else 0.0 if _magnitude_bucket(query.claimed_saving_nok)
== _magnitude_bucket(candidate.claimed_saving_nok)
else 0.0
)
return _W_CODES * codes + _W_MEASURE * measure + _W_MAGNITUDE * magnitude return _W_CODES * codes + _W_MEASURE * measure + _W_MAGNITUDE * magnitude
@ -106,7 +109,9 @@ class ExpeLContextProvider(ContextProvider):
body = "\n".join(f"- [{v.id}] {v.decision}: {v.rationale}" for v in hits) body = "\n".join(f"- [{v.id}] {v.decision}: {v.rationale}" for v in hits)
return f"Relevant prior verdicts (ExpeL few-shot):\n{body}" return f"Relevant prior verdicts (ExpeL few-shot):\n{body}"
async def before_run(self, *, agent: object, session: object, context: object, state: dict) -> None: async def before_run(
self, *, agent: object, session: object, context: object, state: dict
) -> None:
context.extend_instructions([self.format_fewshot()]) # type: ignore[attr-defined] context.extend_instructions([self.format_fewshot()]) # type: ignore[attr-defined]
@ -114,18 +119,95 @@ def seed_store() -> VerdictStore:
"""Seed 12 synthetic verdicts spanning the reference domain's cost codes, measure """Seed 12 synthetic verdicts spanning the reference domain's cost codes, measure
types, magnitudes, and decisions (B2 store of 1020).""" types, magnitudes, and decisions (B2 store of 1020)."""
rows = [ rows = [
("V01", {"05.2", "03.1"}, "scope_reduction", 180_000, "approved", "asphalt + base course trimmed within feasible range"), (
("V02", {"05.2"}, "rate_renegotiation", 60_000, "approved", "renegotiated asphalt unit rate"), "V01",
("V03", {"07.4"}, "material_substitution", 120_000, "rejected", "granite kerb substitution unsafe"), {"05.2", "03.1"},
("V04", {"09.1"}, "scope_reduction", 240_000, "approved", "fewer LED masts on low-traffic stretch"), "scope_reduction",
("V05", {"02.3", "03.1"}, "scope_reduction", 350_000, "rejected", "soil replacement is load-bearing, cannot cut"), 180_000,
"approved",
"asphalt + base course trimmed within feasible range",
),
(
"V02",
{"05.2"},
"rate_renegotiation",
60_000,
"approved",
"renegotiated asphalt unit rate",
),
(
"V03",
{"07.4"},
"material_substitution",
120_000,
"rejected",
"granite kerb substitution unsafe",
),
(
"V04",
{"09.1"},
"scope_reduction",
240_000,
"approved",
"fewer LED masts on low-traffic stretch",
),
(
"V05",
{"02.3", "03.1"},
"scope_reduction",
350_000,
"rejected",
"soil replacement is load-bearing, cannot cut",
),
("V06", {"21.2"}, "rate_renegotiation", 90_000, "approved", "blasting rate renegotiated"), ("V06", {"21.2"}, "rate_renegotiation", 90_000, "approved", "blasting rate renegotiated"),
("V07", {"22.4"}, "material_substitution", 700_000, "rejected", "fiber shotcrete spec is mandated"), (
("V08", {"88.2"}, "scope_reduction", 150_000, "approved", "concrete repair area re-measured smaller"), "V07",
("V09", {"87.3"}, "material_substitution", 130_000, "approved", "alternative membrane qualified"), {"22.4"},
("V10", {"05.2", "03.1"}, "rate_renegotiation", 200_000, "approved", "combined paving rate discount"), "material_substitution",
("V11", {"01.1"}, "scope_reduction", 95_000, "rejected", "rigging is fixed cost, no scope to cut"), 700_000,
("V12", {"31.3"}, "scope_reduction", 110_000, "approved", "drainage length reduced after survey"), "rejected",
"fiber shotcrete spec is mandated",
),
(
"V08",
{"88.2"},
"scope_reduction",
150_000,
"approved",
"concrete repair area re-measured smaller",
),
(
"V09",
{"87.3"},
"material_substitution",
130_000,
"approved",
"alternative membrane qualified",
),
(
"V10",
{"05.2", "03.1"},
"rate_renegotiation",
200_000,
"approved",
"combined paving rate discount",
),
(
"V11",
{"01.1"},
"scope_reduction",
95_000,
"rejected",
"rigging is fixed cost, no scope to cut",
),
(
"V12",
{"31.3"},
"scope_reduction",
110_000,
"approved",
"drainage length reduced after survey",
),
] ]
return VerdictStore( return VerdictStore(
verdicts=[ verdicts=[

View file

@ -73,7 +73,9 @@ def test_retrieve_finds_structural_match_over_text_decoys() -> None:
def test_retrieve_is_deterministic() -> None: def test_retrieve_is_deterministic() -> None:
store, _ = _store_with_true_match_and_decoys() store, _ = _store_with_true_match_and_decoys()
assert [h.id for h in store.retrieve(_QUERY, k=3)] == [h.id for h in store.retrieve(_QUERY, k=3)] assert [h.id for h in store.retrieve(_QUERY, k=3)] == [
h.id for h in store.retrieve(_QUERY, k=3)
]
def test_retrieve_rejects_non_positive_k() -> None: def test_retrieve_rejects_non_positive_k() -> None: