feat(p18): an identifier that stands everywhere identifies nothing
P18 parts B and C (order 20260914T105139Z).
B1 -- stage 0b. P7 made it `item.code in grounding`: plain containment over
ONE concatenated string. P16 ran it against a delivered corpus and measured
what containment cannot tell apart: the falsification arm a4-indeksregulering
put 250 000 NOK on a single cost line coded R761 -- the knowledge base's OWN
NAME, carried by all 2 756 of its concept documents -- and the whole gate
said validated (stage 0 skipped, un-anchored run; checker approve).
The grounding is now carried as the DOCUMENTS it is made of (validator.
Grounding), not as a blob. A structure and not a second argument beside the
text: the boundaries and the text are one fact, and .text is derived, so the
gate and P8's report measure the same characters. run.py composes one
document per concept file where the base is already walked; generate.
_grounding_text folds each cost line in as a one-line document.
N and A are MEASURED, not chosen (14.09, four mounted vegnormal bases):
- every must_cite ref and mandate affected_code in the four context sets --
shortest real identifier is FOUR characters (12.1, 52.1), so N = 3 sits one
below the measurement and cannot refuse anything measured;
- document frequency of every code-shaped token per base -- 1 692 distinct
and NOT ONE reaches 5 %. Highest anywhere 6/446 (1.35 %), highest a fasit
names 3/446 (0.67 %), R761 2 756/2 756 (100 %). A = 0.05 therefore sits
3.7x above the highest real token and 20x below the defect.
Length is NOT what makes the defect inert (R761 is four characters); the
share is. And a share is not a measurement without a denominator big enough
to take one (ansikt 4): one of three is 33 %, so an ABSOLUTE floor of 10
documents gates it. Highest absolute count any real identifier reaches is 6,
and every fixture in the repo is far below 10 -- which is why every pre-P18
gate is UNTOUCHED by this rule rather than exempted from it. Grounding.of
(one document) can never reach the floor by construction.
The refusal NAMES the denominator ("appears in 2756 of the 2756 documents
this run was given"), because Step 5 feeds that reason verbatim into the next
attempt's prompt: a proposer told only "ungrounded" answers with another
token of the same kind.
B2 SPIKE (measured, NOT built) FELLED the order's own alternative: option (b)
"ground in what the run OPENED" was run over P16's 16 code rows -- R761
stands in every OPENED document too, so (b) would NOT have caught the defect,
while B1 makes it inert and still grounds the real process line 65
ASFALTDEKKER (29/2756 = 1.05 %). (b) is not a substitute for B1.
C1 -- --docs-dir is optional once --bundle-dir is given (P16 FUNN 2). On the
bundle path docs_dir is never read: retrieval, the chunk tool and the "no
citable content" check all live in the road branch. Bound ONCE from
--bundle-dir, which is byte-identically what the README already tells an
operator to type by hand. NOT the "--docs-dir omvei": no such path is opened
and the road branch still refuses without a real --docs-dir (own arm).
C2 -- the judge's snippet arm counts only under citation_scope == "narrowed",
as (a) already does (PM decision, P16 s 6.2). P16's reason for (b') being
clean -- snippets are bodies while ref/title live in frontmatter, 0 of 446
n100 bodies -- holds for "Krav 4.1.2-1" but NOT for R761, where a process
number like 12.1 stands in the bodies. Under a whole-base citation list that
mark was "cited" before any model call.
tests: test_inert_identifier_loadbearing.py (7 arms; known positive is P16's
OWN artefact replayed against the base that run was given, known negative is
26 of 26 fasit references still grounding), test_docs_dir_optional_
loadbearing.py (5 arms). test_stress_judge_loadbearing.py's snippet arm split
into narrowed/whole-base -- the pair is the discriminator, same snippet, same
mark, only the scope differs. The grounding tests migrate from str to
Grounding.of (the honest reading of a caller that declared no boundaries).
Verification: uv run pytest -q 1698 passed / 5 skipped (1685 after part A,
strict superset, 0 removed). ruff check + format clean, mypy clean (38
files). Golden demo-transcript.stdout BYTE-UNCHANGED, shasum -a 1 of the
CONTENT = ea8c534773acdbe41ae68f2c55724d69aaf8be4f. No version bump, no push.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
9b47e5aa62
commit
7a7c988253
11 changed files with 628 additions and 51 deletions
|
|
@ -40,6 +40,7 @@ from portfolio_optimiser.proposal_review import (
|
|||
)
|
||||
from portfolio_optimiser.reference_domain import Project
|
||||
from portfolio_optimiser.validator import (
|
||||
Grounding,
|
||||
Rejection,
|
||||
ValidatedProposal,
|
||||
self_repair,
|
||||
|
|
@ -421,12 +422,20 @@ def generate_with_validation(
|
|||
return self_repair(_attempt, max_attempts=max_attempts)
|
||||
|
||||
|
||||
def _grounding_text(project: Project, baseline: CostBaseline | None, delivered: str) -> str:
|
||||
def _grounding_text(
|
||||
project: Project, baseline: CostBaseline | None, delivered: Grounding
|
||||
) -> Grounding:
|
||||
"""P7: compose the ONE text a candidate's identifiers must be grounded in — the run's
|
||||
non-model-authored input, and nothing else.
|
||||
|
||||
Three sources, each of which the run can point at without asking the model:
|
||||
|
||||
**P18/B1: the result carries the DOCUMENT BOUNDARIES, not only the text.** ``code in text``
|
||||
cannot tell "this project has such a line" from "this word is in every letterhead" — P16
|
||||
measured a base's own name, ``R761``, carrying a fabricated 250 000 NOK line to ``validated``.
|
||||
The two later sources join as ONE-LINE documents rather than being appended to a blob: each IS
|
||||
one cost line, and the share rule then reads them exactly as it reads a concept file.
|
||||
|
||||
* ``delivered`` — what the CALLER can prove this run was GIVEN. ``run_project`` fills it from
|
||||
the delivered rendered context (the pre-pass cut, the bundle pointer, or the road path's
|
||||
retrieved chunks) PLUS the navigated base's ``context_files`` — never ``files``, because that
|
||||
|
|
@ -448,12 +457,12 @@ def _grounding_text(project: Project, baseline: CostBaseline | None, delivered:
|
|||
very identifier it just refused. Grounding in the prompt would therefore let the gate's own
|
||||
refusal ground the next attempt: a falsifier that disarms itself on its second round.
|
||||
"""
|
||||
return "\n".join(
|
||||
[
|
||||
delivered,
|
||||
return Grounding(
|
||||
documents=(
|
||||
*delivered.documents,
|
||||
*(item.code for item in project.cost_items),
|
||||
*(() if baseline is None else baseline.items),
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -508,7 +517,7 @@ class GroundingOffer:
|
|||
|
||||
|
||||
def grounding_offer(
|
||||
project: Project, baseline: CostBaseline | None, delivered: str
|
||||
project: Project, baseline: CostBaseline | None, delivered: Grounding
|
||||
) -> GroundingOffer:
|
||||
"""Measure what ``delivered`` can ground, on the EXACT text the gate will see.
|
||||
|
||||
|
|
@ -519,7 +528,7 @@ def grounding_offer(
|
|||
|
||||
Deterministic and free: no model call, no network, and no second walk of the bundle.
|
||||
"""
|
||||
text = _grounding_text(project, baseline, delivered)
|
||||
text = _grounding_text(project, baseline, delivered).text
|
||||
found: set[str] = set()
|
||||
for form in _IDENTIFIER_FORMS:
|
||||
found |= set(form.findall(text))
|
||||
|
|
@ -544,7 +553,7 @@ async def generate_via_llm(
|
|||
reviews: list[ProposalReview] | None = None,
|
||||
review_key: tuple[str | None, str | None] = (None, None),
|
||||
checker_verdict: str = "absent",
|
||||
grounding: str | None = None,
|
||||
grounding: Grounding | None = None,
|
||||
) -> GenerationResult:
|
||||
"""Async LLM path: non-streaming chat -> parse -> validate, with TWO bounded retry kinds,
|
||||
the meter checked in this loop:
|
||||
|
|
@ -703,7 +712,7 @@ async def generate_via_llm(
|
|||
# caller that declared nothing else IS the input it declared. ``run_project``
|
||||
# always passes it EXPLICITLY, because on the debate path ``context`` has been
|
||||
# replaced by the model's OWN summary of what it read.
|
||||
context if grounding is None else grounding,
|
||||
Grounding.of(context) if grounding is None else grounding,
|
||||
),
|
||||
)
|
||||
last_ruling = result
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ from portfolio_optimiser.provenance import ProvenanceStamp
|
|||
from portfolio_optimiser.reference_domain import Project, load_reference_projects
|
||||
from portfolio_optimiser.tracing import TracingConfigError, configure_tracing, tracing_notice
|
||||
from portfolio_optimiser.validator import (
|
||||
Grounding,
|
||||
Rejection,
|
||||
ValidatedProposal,
|
||||
baseline_from_project,
|
||||
|
|
@ -1107,10 +1108,13 @@ async def run_project(
|
|||
# P7: the delivered base is the run's own evidence for what identifiers EXIST. Built from
|
||||
# ``context_files`` (MAJOR-3/S7a-3's rule), so the ``type: verdict`` layer stays out — a
|
||||
# proposal grounded in a prior verdict would reach the ExpeL fold's material around its gate.
|
||||
bundle_grounding = ""
|
||||
# P18/B1: ONE document per concept file, not one blob. The boundaries ARE the denominator the
|
||||
# gate's share rule needs, and composing them here — where the base is already walked — is what
|
||||
# keeps them from being a second, drifting reconstruction (kø-(p)).
|
||||
bundle_grounding: tuple[str, ...] = ()
|
||||
if bundle_dir is not None:
|
||||
bundle = okf.navigate_bundle(bundle_dir)
|
||||
bundle_grounding = "\n".join(
|
||||
bundle_grounding = tuple(
|
||||
"\n".join([f.name, *f.frontmatter.values(), f.body]) for f in bundle.context_files
|
||||
)
|
||||
# ONE bundle-id rule (Step 10, slackened S7a-3 pkt. 1): the DECLARED id is the identity and
|
||||
|
|
@ -1224,7 +1228,7 @@ async def run_project(
|
|||
# ``_fetch_parsed`` has returned, so a report from there could only ever speak once an attempt
|
||||
# had been paid for. ONE binding feeding both the report and the gate: two compositions of one
|
||||
# text are free to disagree, which is exactly what a report must not be able to do (kø-(p)).
|
||||
delivered = "\n".join([context, bundle_grounding])
|
||||
delivered = Grounding(documents=(context, *bundle_grounding))
|
||||
offer = grounding_offer(project, baseline, delivered)
|
||||
|
||||
# Trekk B2 (krav 3): configured MCP servers become tools the AGENTS can call during the debate.
|
||||
|
|
@ -2950,14 +2954,26 @@ def main(argv: list[str] | None = None) -> int:
|
|||
# HOISTED above the scripted door (below) so an incomplete argv is refused BEFORE the honesty
|
||||
# banner could claim a scripted run happened; the refusal ORDER within single-project mode
|
||||
# (required args -> semantic-retrieval -> scripted) is unchanged.
|
||||
if not args.portfolio and (args.project_id is None or args.docs_dir is None):
|
||||
if not args.portfolio and (
|
||||
args.project_id is None or (args.docs_dir is None and args.bundle_dir is None)
|
||||
):
|
||||
print(
|
||||
"run refused: single-project mode requires PROJECT_ID and --docs-dir "
|
||||
"(use --portfolio for portfolio mode)",
|
||||
"run refused: single-project mode requires PROJECT_ID and either --docs-dir or "
|
||||
"--bundle-dir (use --portfolio for portfolio mode)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
# P18/C1 (P16 FUNN 2): on the BUNDLE path ``docs_dir`` is never read — retrieval, the chunk
|
||||
# tool and the citation check all live in the road branch — yet the guard above demanded it,
|
||||
# so the documented command had to name the same directory twice. It is now bound ONCE, from
|
||||
# ``--bundle-dir`` when ``--docs-dir`` is absent, which is byte-identically what the README
|
||||
# tells an operator to type by hand; every existing invocation, including the two-flag form,
|
||||
# is unchanged. This is NOT the "--docs-dir omvei" (feeding project documents through
|
||||
# retrieval INSTEAD of ingesting them into a knowledge base): no such path is opened, and the
|
||||
# road branch still refuses without a real ``--docs-dir``.
|
||||
docs_dir = args.docs_dir if args.docs_dir is not None else args.bundle_dir
|
||||
|
||||
# The third projection reads a table INSIDE a bundle, so without one there is nothing to derive
|
||||
# from: the road path's baseline comes from ``Project.cost_items`` and is anchored by
|
||||
# construction. Refused by NAME here rather than left to surface later as a project-lookup
|
||||
|
|
@ -3885,7 +3901,7 @@ def main(argv: list[str] | None = None) -> int:
|
|||
run_project(
|
||||
args.project_id,
|
||||
args.profile,
|
||||
docs_dir=args.docs_dir,
|
||||
docs_dir=docs_dir,
|
||||
bundle_dir=args.bundle_dir,
|
||||
verdict_dir=args.verdict_dir,
|
||||
dimension=(
|
||||
|
|
@ -3963,7 +3979,7 @@ def main(argv: list[str] | None = None) -> int:
|
|||
run_project(
|
||||
args.project_id,
|
||||
args.profile,
|
||||
docs_dir=args.docs_dir,
|
||||
docs_dir=docs_dir,
|
||||
bundle_dir=args.bundle_dir,
|
||||
verdict_dir=args.verdict_dir,
|
||||
dimension=(
|
||||
|
|
|
|||
|
|
@ -24,11 +24,17 @@ it. So a CITATION grounds an approach only when the citation list is NARROWER th
|
|||
declared pre-pass cut, where the stamp really does name what was read). Both halves are reported
|
||||
either way (``opened`` / ``cited`` / ``citation_scope``), so which one fired stays readable.
|
||||
|
||||
**(b') was checked for the same vacuity and is CLEAN, so the order stands.** ``bundle_citations``
|
||||
snippets are concept BODIES while ``ref``/``title`` live in FRONTMATTER: measured 0 of 446
|
||||
n100 bodies contain ``Krav 4.1.2-1``. The snippet arm can therefore carry (b') without being
|
||||
satisfied by construction. ``named_in_measure`` / ``named_in_snippet`` are still reported apart,
|
||||
because the measure is the model's own prose and a snippet is the base's.
|
||||
**(b') was checked for the same vacuity and is CLEAN for the N corpora, so the order stands.**
|
||||
``bundle_citations`` snippets are concept BODIES while ``ref``/``title`` live in FRONTMATTER:
|
||||
measured 0 of 446 n100 bodies contain ``Krav 4.1.2-1``. ``named_in_measure`` / ``named_in_snippet``
|
||||
are still reported apart, because the measure is the model's own prose and a snippet is the base's.
|
||||
|
||||
**P18/C2 (PM decision, P16 § 6.2): the snippet arm counts only under a NARROWED scope, as (a)
|
||||
does.** The paragraph above holds for a reference like ``Krav 4.1.2-1``, which no body repeats — it
|
||||
does NOT hold for R761, where a process number such as ``12.1`` stands in the bodies themselves.
|
||||
Under a whole-base citation list that mark is "cited" before any model call, so the row was
|
||||
``named`` for a run in which the model had said nothing of the kind. The scope gate is the same
|
||||
correction (a) already carries, applied to the half that was still exposed.
|
||||
|
||||
**A DENOMINATOR, ALWAYS** (Verifiseringsloven ansikt 4). Every verdict names how many tool calls,
|
||||
citations, approach rows and base concepts it saw, and an outbox with no proposal artefact - or a
|
||||
|
|
@ -274,7 +280,13 @@ def score_context_set(
|
|||
snippets = " ".join(str(c.get("snippet", "")) for c in citations)
|
||||
marks = [m for c in concepts for m in (c.get("ref", ""), c.get("title", "")) if m]
|
||||
named_in_measure = any(m in measure for m in marks)
|
||||
named_in_snippet = any(m in snippets for m in marks)
|
||||
# P18/C2 (PM decision, P16 § 6.2): the snippet arm counts ONLY under a narrowed citation
|
||||
# scope, exactly as (a) does. A whole-base citation list is stamped by ``bundle_citations``
|
||||
# before a single model call — measured on n100, 446 context files, 446 citations, 6 of 6
|
||||
# fasit paths "cited" for free — so a mark found in THOSE snippets is evidence about the
|
||||
# base's contents, not about this run. Measured on r761: ``12.1`` appears in whole-base
|
||||
# snippets and gave this row ``named`` without the model having said anything.
|
||||
named_in_snippet = scope == "narrowed" and any(m in snippets for m in marks)
|
||||
|
||||
halluc = [f"citation:{f}" for f in sorted(cited_files - concept_names)]
|
||||
allowed = set(approach.affected_codes) | baseline_codes
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import warnings
|
|||
from collections.abc import Callable, Mapping
|
||||
from contextlib import contextmanager
|
||||
from dataclasses import dataclass
|
||||
from typing import Final
|
||||
|
||||
import pulp
|
||||
|
||||
|
|
@ -208,7 +209,95 @@ def _reconcile_against_baseline(
|
|||
return Rejection(proposal=proposal, reason="; ".join(violations))
|
||||
|
||||
|
||||
def _ground_against_input(proposal: SavingsProposal, grounding: str) -> Rejection | None:
|
||||
#: P18/B1 — the shortest identifier the gate will let ground anything.
|
||||
#:
|
||||
#: MEASURED (14.09) over every ``must_cite`` reference and every mandate ``affected_code`` in the
|
||||
#: four context sets: the shortest real identifier is FOUR characters (R761's ``12.1`` / ``52.1``).
|
||||
#: Set one BELOW that, so the rule cannot refuse anything that has been measured, while a one- or
|
||||
#: two-character token — which matches by coincidence in any prose — grounds nothing. The honest
|
||||
#: failure direction for a gate that speaks about a model's invention.
|
||||
#:
|
||||
#: Length is NOT what makes the measured defect inert: P16's fabricated ``R761`` is four characters
|
||||
#: long. The share below is what does. This covers the coincidence class the measurement did not
|
||||
#: happen to contain.
|
||||
_GROUNDING_MIN_LENGTH: Final = 3
|
||||
|
||||
#: The share of the grounding's DOCUMENTS above which a token identifies nothing.
|
||||
#:
|
||||
#: MEASURED over the four delivered corpora, counting document frequency for every code-shaped
|
||||
#: token (``generate._IDENTIFIER_FORMS``): 1 692 distinct tokens, and NOT ONE reaches 5 % of its
|
||||
#: base's documents. The highest anywhere is 6 of 446 (1.35 %); the highest that a fasit or mandate
|
||||
#: actually names is 3 of 446 (0.67 %). P16's fabricated ``R761`` is 2 756 of 2 756 — 100 %.
|
||||
#: 5 % therefore sits 3.7x above the highest real token measured and 20x below the defect.
|
||||
_GROUNDING_MAX_DOCUMENT_SHARE: Final = 0.05
|
||||
|
||||
#: …and a share is not a measurement without a denominator big enough to take one (ansikt 4).
|
||||
#: One document of three is 33 % and says nothing at all, so the share only fires once a token is
|
||||
#: in at least this many documents. MEASURED: the highest ABSOLUTE document count any real
|
||||
#: identifier reaches in the four corpora is 6, and every test fixture in this repo is far below
|
||||
#: 10 — which is why every pre-P18 gate is untouched by this rule rather than exempted from it.
|
||||
_GROUNDING_MIN_INERT_DOCUMENTS: Final = 10
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Grounding:
|
||||
"""The run's non-model-authored input, carried as the DOCUMENTS it is made of.
|
||||
|
||||
P7 carried it as ONE string, and P16 measured what that costs: ``R761`` — the base's own NAME,
|
||||
which every one of its 2 756 concept documents carries — satisfied ``code in grounding`` and
|
||||
carried a fabricated 250 000 NOK line through the whole gate to ``validated``. Containment in a
|
||||
concatenation cannot tell "this project has such a line" from "this word is in the letterhead".
|
||||
|
||||
A structure rather than a second argument beside the text: the boundaries and the text are ONE
|
||||
fact, and two carriers for one fact are free to disagree about it (kø-(p)). ``text`` is derived
|
||||
here, so the gate and P8's report measure the very same characters.
|
||||
|
||||
A caller with no boundaries to declare — the road path, a test — builds ONE document from its
|
||||
text and is unchanged by construction: one document can never reach
|
||||
``_GROUNDING_MIN_INERT_DOCUMENTS``, so the share cannot fire on it.
|
||||
"""
|
||||
|
||||
documents: tuple[str, ...]
|
||||
|
||||
@classmethod
|
||||
def of(cls, text: str) -> Grounding:
|
||||
"""One document. The honest reading of a caller that declared no boundaries."""
|
||||
return cls(documents=(text,))
|
||||
|
||||
@property
|
||||
def text(self) -> str:
|
||||
"""The ONE composition. Byte-identical to P7's ``"\n".join`` of the same parts."""
|
||||
return "\n".join(self.documents)
|
||||
|
||||
def document_frequency(self, token: str) -> int:
|
||||
"""How many of the documents contain ``token`` — the numerator, in the unit of the rule."""
|
||||
return sum(1 for document in self.documents if token in document)
|
||||
|
||||
|
||||
def _inert_in(grounding: Grounding, code: str) -> str | None:
|
||||
"""Why ``code`` identifies nothing in this input, or ``None`` when it identifies something.
|
||||
|
||||
The message NAMES THE DENOMINATOR, because "it is everywhere" and "it is not here" are
|
||||
different findings and Step 5 feeds this reason verbatim into the next attempt's prompt: a
|
||||
proposer told only "ungrounded" will re-answer with another token of the same kind.
|
||||
"""
|
||||
if len(code) < _GROUNDING_MIN_LENGTH:
|
||||
return (
|
||||
f"is {len(code)} characters long, too short to identify a cost line — any prose "
|
||||
"contains it by coincidence"
|
||||
)
|
||||
hits = grounding.document_frequency(code)
|
||||
total = len(grounding.documents)
|
||||
floor = max(_GROUNDING_MIN_INERT_DOCUMENTS, total * _GROUNDING_MAX_DOCUMENT_SHARE)
|
||||
if hits >= floor:
|
||||
return (
|
||||
f"appears in {hits} of the {total} documents this run was given — a token that is in "
|
||||
"every document identifies none of them; name a cost line, not the corpus"
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def _ground_against_input(proposal: SavingsProposal, grounding: Grounding) -> Rejection | None:
|
||||
"""P7: every identifier the proposal builds on must appear VERBATIM in the input it was built
|
||||
from, or the verdict falls.
|
||||
|
||||
|
|
@ -244,12 +333,19 @@ def _ground_against_input(proposal: SavingsProposal, grounding: str) -> Rejectio
|
|||
deliberately out of scope: the Monte Carlo never samples such a band (``SavingsProposal.
|
||||
_assumption_bands_enclose_unit_cost`` says so in the same words), so it cannot move the verdict,
|
||||
and a check on it would be a branch no recording exercises."""
|
||||
violations = [
|
||||
f"ungrounded identifier {item.code!r}: it appears nowhere in the input this proposal "
|
||||
f"was built from ({len(grounding)} chars)"
|
||||
for item in proposal.affected_items
|
||||
if item.code not in grounding
|
||||
]
|
||||
violations = []
|
||||
for item in proposal.affected_items:
|
||||
if item.code not in grounding.text:
|
||||
violations.append(
|
||||
f"ungrounded identifier {item.code!r}: it appears nowhere in the input this "
|
||||
f"proposal was built from ({len(grounding.text)} chars)"
|
||||
)
|
||||
continue
|
||||
# P18/B1: present is not the same as identifying. An identifier that stands everywhere
|
||||
# identifies nothing, and one too short to be an identifier is matched by coincidence.
|
||||
inert = _inert_in(grounding, item.code)
|
||||
if inert is not None:
|
||||
violations.append(f"ungrounded identifier {item.code!r}: it {inert}")
|
||||
if not violations:
|
||||
return None
|
||||
return Rejection(proposal=proposal, reason="; ".join(violations))
|
||||
|
|
@ -259,7 +355,7 @@ def validate_proposal(
|
|||
proposal: SavingsProposal,
|
||||
*,
|
||||
baseline: CostBaseline | None = None,
|
||||
grounding: str | None = None,
|
||||
grounding: Grounding | None = None,
|
||||
tolerance: float = BASELINE_TOLERANCE_DEFAULT,
|
||||
method_caps: Mapping[str, float] | None = None,
|
||||
) -> ValidatedProposal | Rejection:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue