loadpath 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- loadpath/README.md +294 -0
- loadpath/SKILL.md +584 -0
- loadpath/__init__.py +17 -0
- loadpath/__main__.py +1202 -0
- loadpath/am.py +316 -0
- loadpath/amgfast.py +326 -0
- loadpath/assembly.py +985 -0
- loadpath/benchmarks/__init__.py +0 -0
- loadpath/benchmarks/beams.py +266 -0
- loadpath/benchmarks/cases/ge-bracket.json +380 -0
- loadpath/benchmarks/evaluate.py +172 -0
- loadpath/benchmarks/mechanisms.py +416 -0
- loadpath/benchmarks/problems.py +140 -0
- loadpath/benchmarks/reference/bridge-3d.top3d.npz +0 -0
- loadpath/benchmarks/reference/cantilever-3d.top3d.npz +0 -0
- loadpath/benchmarks/reference/lbracket-2d.top88-ft2.npz +0 -0
- loadpath/benchmarks/reference/mbb-2d.top88-ft1.npz +0 -0
- loadpath/benchmarks/reference/mbb-2d.top88-ft2.npz +0 -0
- loadpath/benchmarks/reference.py +138 -0
- loadpath/benchmarks/run.py +301 -0
- loadpath/brep.py +1470 -0
- loadpath/bucklingopt.py +300 -0
- loadpath/buildsim.py +1490 -0
- loadpath/cadimport.py +904 -0
- loadpath/calibration.py +885 -0
- loadpath/cavities.py +211 -0
- loadpath/checks/__init__.py +0 -0
- loadpath/checks/dfm.py +354 -0
- loadpath/checks/exchanger.py +7 -0
- loadpath/checks/fea.py +212 -0
- loadpath/checks/fit.py +475 -0
- loadpath/checks/networks.py +260 -0
- loadpath/checks/opening.py +67 -0
- loadpath/checks/sdf_kit.py +7 -0
- loadpath/conjugate.py +267 -0
- loadpath/constrained.py +329 -0
- loadpath/contact.py +691 -0
- loadpath/coopt.py +342 -0
- loadpath/cores.py +163 -0
- loadpath/crease.py +1138 -0
- loadpath/critic.py +212 -0
- loadpath/crosscheck.py +1107 -0
- loadpath/cutcell.py +510 -0
- loadpath/data/lattice_buckling.json +439 -0
- loadpath/data/lattice_tensors.json +27449 -0
- loadpath/data/published_experiments.json +71 -0
- loadpath/decimate.py +177 -0
- loadpath/delivery.py +190 -0
- loadpath/directions.py +262 -0
- loadpath/domain.py +630 -0
- loadpath/drains.py +615 -0
- loadpath/durability.py +413 -0
- loadpath/editor.html +3135 -0
- loadpath/editor.py +610 -0
- loadpath/escape.py +354 -0
- loadpath/evals/__init__.py +0 -0
- loadpath/evals/baselines/coarse.json +1543 -0
- loadpath/evals/baselines/full.json +1497 -0
- loadpath/evals/brief_kinds/__init__.py +1 -0
- loadpath/evals/brief_kinds/heatx.py +387 -0
- loadpath/evals/brief_kinds/lattice.py +571 -0
- loadpath/evals/brief_kinds/multimat.py +593 -0
- loadpath/evals/briefs/assets/bracket.step +1201 -0
- loadpath/evals/briefs/assets/desk_arm.urdf +22 -0
- loadpath/evals/briefs/assets/elbow-gearbox.stl +0 -0
- loadpath/evals/briefs/assets/motor-body.stl +0 -0
- loadpath/evals/briefs/assets/sketch.png +0 -0
- loadpath/evals/briefs/base-adapter-fdm.json +492 -0
- loadpath/evals/briefs/bell-crank-lpbf.json +180 -0
- loadpath/evals/briefs/belt-tensioner-fdm.json +151 -0
- loadpath/evals/briefs/camera-boom-mjf.json +339 -0
- loadpath/evals/briefs/camera-mount-pla.json +203 -0
- loadpath/evals/briefs/coolant-isolator-petg.json +100 -0
- loadpath/evals/briefs/elbow-bearing-bracket-sls.json +300 -0
- loadpath/evals/briefs/elbow-gearbox-forearm-mjf.json +558 -0
- loadpath/evals/briefs/elbow-link-fatigue-fdm.json +251 -0
- loadpath/evals/briefs/elbow-stop-pad-tpu.json +39 -0
- loadpath/evals/briefs/forearm-fdm.json +146 -0
- loadpath/evals/briefs/forearm-servo-link-fdm.json +327 -0
- loadpath/evals/briefs/grip-pad-finger-mm.json +354 -0
- loadpath/evals/briefs/gripper-bracket-creep-fdm.json +340 -0
- loadpath/evals/briefs/gripper-finger-fdm.json +155 -0
- loadpath/evals/briefs/gripper-jaw-fatigue-mjf.json +328 -0
- loadpath/evals/briefs/gripper-palm-fdm.json +715 -0
- loadpath/evals/briefs/hot-stepper-mount-asa-fdm.json +471 -0
- loadpath/evals/briefs/hpu-oil-cooler-lpbf.json +121 -0
- loadpath/evals/briefs/isolated-camera-mount-mm.json +675 -0
- loadpath/evals/briefs/kart-charge-cooler-lpbf.json +93 -0
- loadpath/evals/briefs/lighten-step-bracket.json +220 -0
- loadpath/evals/briefs/nema17-mount-stl.json +429 -0
- loadpath/evals/briefs/pillow-block-sls.json +220 -0
- loadpath/evals/briefs/polyjet-fingertip-mm.json +305 -0
- loadpath/evals/briefs/rod-hook-pla.json +154 -0
- loadpath/evals/briefs/sensor-arm-resin.json +150 -0
- loadpath/evals/briefs/shelf-bracket-pla.json +158 -0
- loadpath/evals/briefs/shoulder-riser-mjf.json +487 -0
- loadpath/evals/briefs/sketch-clevis.json +229 -0
- loadpath/evals/briefs/spring-bracket-pacf.json +294 -0
- loadpath/evals/briefs/tool-flange-ti64.json +280 -0
- loadpath/evals/briefs/tray-arm-lattice-fdm.json +260 -0
- loadpath/evals/briefs/turret-fdm.json +281 -0
- loadpath/evals/briefs/upper-arm-sls.json +179 -0
- loadpath/evals/briefs/urdf-forearm.json +233 -0
- loadpath/evals/briefs/wrist-link-mjf.json +278 -0
- loadpath/evals/briefs/wrist-yoke-mjf.json +245 -0
- loadpath/evals/briefs.py +393 -0
- loadpath/evals/cases/arm-link-cast.json +157 -0
- loadpath/evals/cases/arm-link-driven.json +171 -0
- loadpath/evals/cases/arm-link-sandwich.json +315 -0
- loadpath/evals/cases/arm-link.json +111 -0
- loadpath/evals/cases/bell-crank.json +116 -0
- loadpath/evals/cases/grip-turret.json +479 -0
- loadpath/evals/cases/grip-upper-arm.json +253 -0
- loadpath/evals/cases/grip-wrist-link-mjf.json +929 -0
- loadpath/evals/cases/grip-wrist-yoke.json +831 -0
- loadpath/evals/cases/lpbf-intercooler-core.json +59 -0
- loadpath/evals/cases/mbb-beam.json +61 -0
- loadpath/evals/cases/motor-body.stl +0 -0
- loadpath/evals/cases/motor-mount.json +265 -0
- loadpath/evals/cases/printed-hook.json +77 -0
- loadpath/evals/cases/wall-bracket-fasteners.json +125 -0
- loadpath/evals/cases/wall-bracket.json +126 -0
- loadpath/evals/corpus.py +179 -0
- loadpath/evals/pareto.py +90 -0
- loadpath/evals/run.py +860 -0
- loadpath/examples/arm-link.json +25 -0
- loadpath/examples/bumpered-bracket-compression.json +225 -0
- loadpath/examples/bumpered-bracket-lattice.json +204 -0
- loadpath/examples/bumpered-bracket.json +34 -0
- loadpath/examples/gripper-finger-mm.json +185 -0
- loadpath/examples/heat-exchanger-counterflow.json +21 -0
- loadpath/examples/heat-exchanger-crossflow-tuned.json +48 -0
- loadpath/examples/heat-exchanger-crossflow.json +20 -0
- loadpath/examples/pedal-lattice.json +23 -0
- loadpath/examples/pressure-plenum.json +12 -0
- loadpath/examples/shelf-bracket.json +27 -0
- loadpath/exchanger.py +831 -0
- loadpath/fasteners.py +199 -0
- loadpath/fem.py +668 -0
- loadpath/femops.py +70 -0
- loadpath/fieldjit.py +981 -0
- loadpath/fieldops.py +119 -0
- loadpath/fields.py +3697 -0
- loadpath/fieldtorch.py +464 -0
- loadpath/flow.py +984 -0
- loadpath/flowtopo.py +300 -0
- loadpath/gpu.py +521 -0
- loadpath/heatx.py +1067 -0
- loadpath/heatx_thermal.py +432 -0
- loadpath/homogenized.py +886 -0
- loadpath/ibrep.py +552 -0
- loadpath/impact.py +693 -0
- loadpath/infill.py +376 -0
- loadpath/interface_library.py +340 -0
- loadpath/interlock.py +186 -0
- loadpath/isolator.py +149 -0
- loadpath/knn.py +204 -0
- loadpath/lattice.py +1065 -0
- loadpath/lattice3mf.py +760 -0
- loadpath/lattice_buckling.py +245 -0
- loadpath/lattice_grade.py +325 -0
- loadpath/lattice_orient.py +595 -0
- loadpath/lattice_print.py +159 -0
- loadpath/lattice_tensors.py +558 -0
- loadpath/latticestep.py +1210 -0
- loadpath/manufacture.py +446 -0
- loadpath/material_library.py +489 -0
- loadpath/materials.py +144 -0
- loadpath/mcp_server.py +786 -0
- loadpath/mechanism.py +218 -0
- loadpath/mechanism_nl.py +811 -0
- loadpath/meshops.py +452 -0
- loadpath/mg.py +853 -0
- loadpath/mma.py +214 -0
- loadpath/modal.py +210 -0
- loadpath/multimat.py +1788 -0
- loadpath/narrowband.py +418 -0
- loadpath/nlkernel.py +241 -0
- loadpath/nonlinear.py +1095 -0
- loadpath/orient.py +334 -0
- loadpath/porous.py +994 -0
- loadpath/pressure.py +171 -0
- loadpath/preview.py +335 -0
- loadpath/printcheck.py +263 -0
- loadpath/printsetup.py +207 -0
- loadpath/processcheck.py +376 -0
- loadpath/quick.py +113 -0
- loadpath/recall.py +406 -0
- loadpath/reconstruct.py +797 -0
- loadpath/render.py +436 -0
- loadpath/resolved.py +189 -0
- loadpath/schema.py +1701 -0
- loadpath/sdf_kit.py +843 -0
- loadpath/selfx.py +292 -0
- loadpath/shapes.py +584 -0
- loadpath/sharp.py +717 -0
- loadpath/simp.py +1170 -0
- loadpath/sizing.py +312 -0
- loadpath/slicer.py +321 -0
- loadpath/spec.py +1072 -0
- loadpath/step.py +806 -0
- loadpath/struts.py +639 -0
- loadpath/study.py +1937 -0
- loadpath/tasks.py +822 -0
- loadpath/thermal.py +1691 -0
- loadpath/threemf.py +487 -0
- loadpath/tune.py +437 -0
- loadpath/validation.py +145 -0
- loadpath/verify.py +1215 -0
- loadpath/viewer.html +694 -0
- loadpath/viewer.py +446 -0
- loadpath/viscoelastic.py +106 -0
- loadpath/voxelprint.py +582 -0
- loadpath/voxels.py +402 -0
- loadpath-0.1.1.dist-info/METADATA +319 -0
- loadpath-0.1.1.dist-info/RECORD +220 -0
- loadpath-0.1.1.dist-info/WHEEL +5 -0
- loadpath-0.1.1.dist-info/entry_points.txt +2 -0
- loadpath-0.1.1.dist-info/licenses/LICENSE +21 -0
- loadpath-0.1.1.dist-info/top_level.txt +1 -0
loadpath/critic.py
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Vision critic: a model looks at the interface close-ups (render.render_interfaces)
|
|
3
|
+
and files what it sees as findings, so the defects Connor used to find by
|
|
4
|
+
zooming in become numbers an eval can gate on.
|
|
5
|
+
|
|
6
|
+
Why: GRIP's wrist yoke passed every metric with sawn-off faces, hard creases
|
|
7
|
+
at its bosses and material over its bolt seats (QA, 10/28). The crease and
|
|
8
|
+
boundary-cut metrics came out of that, one bug class at a time. A critic
|
|
9
|
+
reads the picture the way a person does and catches the next class before
|
|
10
|
+
anyone writes a metric for it.
|
|
11
|
+
|
|
12
|
+
Two backends:
|
|
13
|
+
|
|
14
|
+
anthropic calls the Claude API with the close-ups and a rubric and gets
|
|
15
|
+
structured findings back. Needs the `anthropic` package and a
|
|
16
|
+
credential (ANTHROPIC_API_KEY or an `ant auth login` profile).
|
|
17
|
+
Model: LOADPATH_CRITIC_MODEL (default claude-opus-5-5).
|
|
18
|
+
agent no API: writes <name>.critic-request.md next to the images
|
|
19
|
+
(rubric, region roles, output schema) for the agent running
|
|
20
|
+
the eval to look at and answer in <name>.critic.json. Claude
|
|
21
|
+
Code sessions read PNGs natively, so this is the free path.
|
|
22
|
+
`python -m loadpath.critic score <out>` folds answers into
|
|
23
|
+
the scorecard.
|
|
24
|
+
|
|
25
|
+
A finding has a kind (KINDS), a severity and the panel it is in. The score is
|
|
26
|
+
SEVERITY-weighted: minor 1, major 3, blocker 10. Evals gate on
|
|
27
|
+
`maxCriticBlockers` and `maxCriticScore`.
|
|
28
|
+
"""
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import base64
|
|
32
|
+
import json
|
|
33
|
+
import os
|
|
34
|
+
import sys
|
|
35
|
+
|
|
36
|
+
KINDS = {
|
|
37
|
+
"crease": "a sharp concave edge or notch where two surfaces meet (a stress raiser; should be a fillet)",
|
|
38
|
+
"sawn-face": "organic material cut dead flat where nothing mates, as if sawn off on the design-space box",
|
|
39
|
+
"seat-blocked": "material over a bolt head, nut, washer or bearing seat, or a bore closed over",
|
|
40
|
+
"thin-member": "a strut or wall visibly thinner than its neighbours, a knife edge, or a hairline bridge",
|
|
41
|
+
"floating": "a piece that does not connect, or hangs by a sliver",
|
|
42
|
+
"rough-surface": "stair steps, voxel terraces, holes or tearing in the surface",
|
|
43
|
+
"missing-preserve": "a boss, face or ring that should be kept is partly eaten away",
|
|
44
|
+
"other": "anything else a careful engineer would send back",
|
|
45
|
+
}
|
|
46
|
+
SEVERITY = {"minor": 1, "major": 3, "blocker": 10}
|
|
47
|
+
|
|
48
|
+
SCHEMA = {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"properties": {
|
|
51
|
+
"findings": {"type": "array", "items": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"properties": {
|
|
54
|
+
"panel": {"type": "string", "description": "the panel title the finding is in"},
|
|
55
|
+
"kind": {"type": "string", "enum": list(KINDS)},
|
|
56
|
+
"severity": {"type": "string", "enum": list(SEVERITY)},
|
|
57
|
+
"note": {"type": "string", "description": "what and where, one sentence"},
|
|
58
|
+
},
|
|
59
|
+
"required": ["panel", "kind", "severity", "note"],
|
|
60
|
+
"additionalProperties": False}},
|
|
61
|
+
"summary": {"type": "string", "description": "one or two sentences on the finish overall"},
|
|
62
|
+
},
|
|
63
|
+
"required": ["findings", "summary"],
|
|
64
|
+
"additionalProperties": False,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
RUBRIC = """You are reviewing close-up renders of a part designed by topology
|
|
68
|
+
optimization, before it is sent to print. Each panel is one place the part
|
|
69
|
+
meets the outside world, titled by region id. Grey shaded surface, lit from
|
|
70
|
+
above; the crop box edges cut the mesh, so straight cuts AT THE PANEL EDGE
|
|
71
|
+
are the crop, not the part.
|
|
72
|
+
|
|
73
|
+
Regions:
|
|
74
|
+
{regions}
|
|
75
|
+
|
|
76
|
+
Look for these defects, and only report what you can actually see:
|
|
77
|
+
{kinds}
|
|
78
|
+
|
|
79
|
+
Severity: blocker = the part cannot be assembled or will fail there (a seat
|
|
80
|
+
covered, a bore closed, a member nearly cut through); major = would be sent
|
|
81
|
+
back for rework (a sharp crease at a loaded boss, a sawn face, a knife edge);
|
|
82
|
+
minor = cosmetic or slight. A clean panel gets no finding. Organic, smoothly
|
|
83
|
+
blended shapes are intended; do not report them. Do not invent findings to
|
|
84
|
+
seem thorough: an empty list is a good answer for a good part."""
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _regions(study) -> str:
|
|
88
|
+
if study is None:
|
|
89
|
+
return "(not given)"
|
|
90
|
+
lines = []
|
|
91
|
+
for r in getattr(study, "preserve", []) or []:
|
|
92
|
+
lines.append(f"- {r.id}: preserved geometry (boss, ring or mounting face kept exactly; should be "
|
|
93
|
+
f"intact and blend smoothly into the part)")
|
|
94
|
+
for r in getattr(study, "keep_out", []) or []:
|
|
95
|
+
lines.append(f"- {r.id}: keep-out (must stay empty: bore, bolt or tool access, clearance)")
|
|
96
|
+
return "\n".join(lines) or "(none)"
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def prompt(study=None) -> str:
|
|
100
|
+
kinds = "\n".join(f"- {k}: {v}" for k, v in KINDS.items())
|
|
101
|
+
return RUBRIC.format(regions=_regions(study), kinds=kinds)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def score(findings) -> dict:
|
|
105
|
+
"""Totals from a findings list (critic JSON 'findings')."""
|
|
106
|
+
fs = [f for f in findings or [] if f.get("severity") in SEVERITY]
|
|
107
|
+
by_kind = {}
|
|
108
|
+
for f in fs:
|
|
109
|
+
by_kind[f["kind"]] = by_kind.get(f["kind"], 0) + 1
|
|
110
|
+
return {"criticScore": sum(SEVERITY[f["severity"]] for f in fs),
|
|
111
|
+
"criticBlockers": sum(f["severity"] == "blocker" for f in fs),
|
|
112
|
+
"criticMajors": sum(f["severity"] == "major" for f in fs),
|
|
113
|
+
"criticFindings": len(fs), "criticByKind": by_kind}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def critique_anthropic(images, study=None, model=None, effort="medium") -> dict:
|
|
117
|
+
import anthropic
|
|
118
|
+
|
|
119
|
+
client = anthropic.Anthropic()
|
|
120
|
+
model = model or os.environ.get("LOADPATH_CRITIC_MODEL", "claude-opus-5-5")
|
|
121
|
+
content = []
|
|
122
|
+
for p in images:
|
|
123
|
+
with open(p, "rb") as fh:
|
|
124
|
+
content.append({"type": "image", "source": {"type": "base64", "media_type": "image/png",
|
|
125
|
+
"data": base64.standard_b64encode(fh.read()).decode()}})
|
|
126
|
+
content.append({"type": "text", "text": prompt(study)})
|
|
127
|
+
resp = client.beta.messages.create(
|
|
128
|
+
model=model, max_tokens=16000,
|
|
129
|
+
output_config={"effort": effort, "format": {"type": "json_schema", "schema": SCHEMA}},
|
|
130
|
+
# a declined request is re-run on the fallback model in the same call
|
|
131
|
+
betas=["server-side-fallback-2026-07-01"], extra_body={"fallbacks": "default"},
|
|
132
|
+
messages=[{"role": "user", "content": content}])
|
|
133
|
+
if resp.stop_reason == "refusal":
|
|
134
|
+
raise RuntimeError(f"critic declined: {getattr(resp.stop_details, 'category', None)}")
|
|
135
|
+
text = next(b.text for b in resp.content if b.type == "text")
|
|
136
|
+
out = json.loads(text)
|
|
137
|
+
out["model"] = resp.model
|
|
138
|
+
return out
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def request_agent(images, study, request_path, answer_path) -> str:
|
|
142
|
+
"""Write the critic request for an agent to answer by hand."""
|
|
143
|
+
imgs = "\n".join(f"- {os.path.abspath(p)}" for p in images)
|
|
144
|
+
with open(request_path, "w") as fh:
|
|
145
|
+
fh.write(f"# Critic request\n\nLook at:\n{imgs}\n\n{prompt(study)}\n\n"
|
|
146
|
+
f"Write the answer as JSON to `{os.path.abspath(answer_path)}`, matching this schema:\n\n"
|
|
147
|
+
f"```json\n{json.dumps(SCHEMA, indent=1)}\n```\n")
|
|
148
|
+
return request_path
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def critique(images, study=None, backend="anthropic", out_dir=None, name="part") -> dict | None:
|
|
152
|
+
"""Findings + score for close-up images, or None when the agent backend
|
|
153
|
+
has written a request that nobody has answered yet."""
|
|
154
|
+
images = [p for p in images if p and os.path.exists(p)]
|
|
155
|
+
if not images:
|
|
156
|
+
return None
|
|
157
|
+
if backend == "anthropic":
|
|
158
|
+
res = critique_anthropic(images, study)
|
|
159
|
+
elif backend == "agent":
|
|
160
|
+
out_dir = out_dir or os.path.dirname(images[0])
|
|
161
|
+
answer = os.path.join(out_dir, f"{name}.critic.json")
|
|
162
|
+
if not os.path.exists(answer):
|
|
163
|
+
request_agent(images, study, os.path.join(out_dir, f"{name}.critic-request.md"), answer)
|
|
164
|
+
return None
|
|
165
|
+
with open(answer) as fh:
|
|
166
|
+
res = json.load(fh)
|
|
167
|
+
else:
|
|
168
|
+
raise ValueError(f"unknown critic backend {backend!r} (anthropic, agent)")
|
|
169
|
+
res["images"] = [os.path.basename(p) for p in images]
|
|
170
|
+
return res | score(res.get("findings"))
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def fold_into_scorecard(out_dir) -> list:
|
|
174
|
+
"""Read every <case>.critic.json in an eval output folder into
|
|
175
|
+
scorecard.json's rows (and their gates), rewrite scorecard.md."""
|
|
176
|
+
from .evals.run import _md, gate_critic
|
|
177
|
+
|
|
178
|
+
path = os.path.join(out_dir, "scorecard.json")
|
|
179
|
+
with open(path) as fh:
|
|
180
|
+
rows = json.load(fh)
|
|
181
|
+
for r in rows:
|
|
182
|
+
f = os.path.join(out_dir, f"{r['case']}.critic.json")
|
|
183
|
+
if not os.path.exists(f):
|
|
184
|
+
continue
|
|
185
|
+
with open(f) as fh:
|
|
186
|
+
res = json.load(fh)
|
|
187
|
+
r["critic"] = res | score(res.get("findings"))
|
|
188
|
+
gate_critic(r)
|
|
189
|
+
with open(path, "w") as fh:
|
|
190
|
+
json.dump(rows, fh, indent=2)
|
|
191
|
+
with open(os.path.join(out_dir, "scorecard.md"), "w") as fh:
|
|
192
|
+
fh.write(_md(rows))
|
|
193
|
+
return rows
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def main(argv=None):
|
|
197
|
+
import argparse
|
|
198
|
+
|
|
199
|
+
p = argparse.ArgumentParser(prog="loadpath.critic")
|
|
200
|
+
sub = p.add_subparsers(dest="cmd", required=True)
|
|
201
|
+
s = sub.add_parser("score", help="fold <case>.critic.json answers into an eval scorecard")
|
|
202
|
+
s.add_argument("out")
|
|
203
|
+
a = p.parse_args(argv)
|
|
204
|
+
if a.cmd == "score":
|
|
205
|
+
rows = fold_into_scorecard(a.out)
|
|
206
|
+
print(open(os.path.join(a.out, "scorecard.md")).read())
|
|
207
|
+
return sum(0 if r.get("pass") else 1 for r in rows)
|
|
208
|
+
return 0
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
if __name__ == "__main__":
|
|
212
|
+
sys.exit(main())
|