@giamme/forge 0.0.0-stage → 0.9.0
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.
- package/CHANGELOG.md +205 -0
- package/LICENSE +21 -0
- package/README.md +194 -2
- package/SKILL.md +123 -0
- package/VERSION +1 -0
- package/docs/README.md +26 -0
- package/docs/add-ons.md +33 -0
- package/docs/artifacts.md +69 -0
- package/docs/commands.md +315 -0
- package/docs/dataset.md +20 -0
- package/docs/decomposed-runs.md +268 -0
- package/docs/development.md +95 -0
- package/docs/forge-ui.md +92 -0
- package/docs/fractal.md +181 -0
- package/docs/install.md +160 -0
- package/docs/memory.md +156 -0
- package/docs/models.md +118 -0
- package/docs/reviews.md +231 -0
- package/docs/safety.md +55 -0
- package/docs/troubleshooting.md +54 -0
- package/forge +79 -0
- package/package.json +27 -4
- package/references/dataset.md +329 -0
- package/references/decompose.md +667 -0
- package/references/fractal-dashboard.md +21 -0
- package/references/fractal.md +307 -0
- package/references/harnesses.md +365 -0
- package/references/jev.md +865 -0
- package/references/memory.md +228 -0
- package/references/planning.md +42 -0
- package/references/ripwire.md +47 -0
- package/references/runtime.md +64 -0
- package/references/solo.md +113 -0
- package/references/ui.md +112 -0
- package/registry.tsv +81 -0
- package/scripts/forge-artifact.sh +78 -0
- package/scripts/forge-contract.py +430 -0
- package/scripts/forge-dataset.py +6 -0
- package/scripts/forge-dispatch.sh +671 -0
- package/scripts/forge-fail-first.py +195 -0
- package/scripts/forge-fractal-dispatch.sh +4 -0
- package/scripts/forge-fractal-options.sh +54 -0
- package/scripts/forge-fractal.py +6 -0
- package/scripts/forge-guard.py +266 -0
- package/scripts/forge-install-fractal.sh +4 -0
- package/scripts/forge-install-ripwire.sh +52 -0
- package/scripts/forge-install.sh +135 -0
- package/scripts/forge-jev-options.sh +259 -0
- package/scripts/forge-jev.py +6 -0
- package/scripts/forge-lib-plans.sh +785 -0
- package/scripts/forge-lib-review.sh +494 -0
- package/scripts/forge-lib-task.sh +302 -0
- package/scripts/forge-lib-verify.sh +30 -0
- package/scripts/forge-memory.sh +575 -0
- package/scripts/forge-metrics.sh +39 -0
- package/scripts/forge-parallel-options.sh +76 -0
- package/scripts/forge-parallel.sh +1717 -0
- package/scripts/forge-plan-check.py +118 -0
- package/scripts/forge-prompt.py +45 -0
- package/scripts/forge-ripwire.py +136 -0
- package/scripts/forge-route.py +344 -0
- package/scripts/forge-runs.py +121 -0
- package/scripts/forge-runtime.py +586 -0
- package/scripts/forge-schedule.py +312 -0
- package/scripts/forge-shadow.py +305 -0
- package/scripts/forge-solo.sh +347 -0
- package/scripts/forge-ui-options.sh +42 -0
- package/scripts/forge-ui.py +435 -0
- package/scripts/forge-update.sh +71 -0
- package/scripts/forge-verify-coverage.py +956 -0
- package/scripts/forge-version.py +240 -0
- package/scripts/forge_dataset/__init__.py +439 -0
- package/scripts/forge_dataset/capture.py +330 -0
- package/scripts/forge_dataset/checks.py +109 -0
- package/scripts/forge_dataset/cli.py +233 -0
- package/scripts/forge_dataset/export.py +1009 -0
- package/scripts/forge_dataset/harvest.py +716 -0
- package/scripts/forge_dataset/importer.py +412 -0
- package/scripts/forge_dataset/outcomes.py +275 -0
- package/scripts/forge_dataset/routing.py +278 -0
- package/scripts/forge_fractal/__init__.py +84 -0
- package/scripts/forge_fractal/bridge.py +46 -0
- package/scripts/forge_fractal/cli.py +202 -0
- package/scripts/forge_fractal/dashboard.css +138 -0
- package/scripts/forge_fractal/dashboard.html +41 -0
- package/scripts/forge_fractal/dashboard.js +687 -0
- package/scripts/forge_fractal/decomposition.py +262 -0
- package/scripts/forge_fractal/execution.py +659 -0
- package/scripts/forge_fractal/inspection.py +261 -0
- package/scripts/forge_fractal/install.py +158 -0
- package/scripts/forge_fractal/invocation.py +94 -0
- package/scripts/forge_fractal/pipeline.py +124 -0
- package/scripts/forge_fractal/selection.py +179 -0
- package/scripts/forge_jev/__init__.py +376 -0
- package/scripts/forge_jev/backtest.py +322 -0
- package/scripts/forge_jev/cli.py +980 -0
- package/scripts/forge_jev/client.py +418 -0
- package/scripts/forge_jev/corpus.py +156 -0
- package/scripts/forge_jev/coupling.py +244 -0
- package/scripts/forge_jev/features.py +24 -0
- package/scripts/forge_jev/gates.py +100 -0
- package/scripts/forge_jev/memory.py +163 -0
- package/scripts/forge_jev/outcome.py +49 -0
- package/scripts/forge_jev/predict.py +204 -0
- package/scripts/forge_jev/questions.py +661 -0
- package/scripts/forge_jev/review.py +73 -0
- package/scripts/forge_jev/routing.py +271 -0
- package/scripts/forge_jev/subset.py +123 -0
- package/scripts/forge_jev/verify.py +453 -0
- package/scripts/forge_registry.py +140 -0
- package/ui/README.md +347 -0
- package/ui/THIRD_PARTY_NOTICES.md +177 -0
- package/ui/docs/api.md +970 -0
- package/ui/forge_ui/__init__.py +20 -0
- package/ui/forge_ui/__main__.py +135 -0
- package/ui/forge_ui/diffs.py +454 -0
- package/ui/forge_ui/executions.py +436 -0
- package/ui/forge_ui/export.py +214 -0
- package/ui/forge_ui/findings.py +132 -0
- package/ui/forge_ui/fractal.py +582 -0
- package/ui/forge_ui/models.py +216 -0
- package/ui/forge_ui/reader.py +1424 -0
- package/ui/forge_ui/server.py +312 -0
- package/ui/forge_ui/static/D-DINCondensed-Bold.woff2 +0 -0
- package/ui/forge_ui/static/app.js +1079 -0
- package/ui/forge_ui/static/cloud3d.js +647 -0
- package/ui/forge_ui/static/cloudmodel.js +375 -0
- package/ui/forge_ui/static/cloudui.js +397 -0
- package/ui/forge_ui/static/convo.js +295 -0
- package/ui/forge_ui/static/datatable.js +232 -0
- package/ui/forge_ui/static/diffview.js +235 -0
- package/ui/forge_ui/static/favicon.svg +1 -0
- package/ui/forge_ui/static/files.js +776 -0
- package/ui/forge_ui/static/findings3d.js +134 -0
- package/ui/forge_ui/static/gantt.js +97 -0
- package/ui/forge_ui/static/graph.js +266 -0
- package/ui/forge_ui/static/icons.js +94 -0
- package/ui/forge_ui/static/index.html +67 -0
- package/ui/forge_ui/static/iso.js +816 -0
- package/ui/forge_ui/static/leaderboard.js +311 -0
- package/ui/forge_ui/static/map3d.js +45 -0
- package/ui/forge_ui/static/modelstats.js +249 -0
- package/ui/forge_ui/static/notify.js +179 -0
- package/ui/forge_ui/static/panel.js +562 -0
- package/ui/forge_ui/static/runmodel.js +667 -0
- package/ui/forge_ui/static/scene3d.js +1471 -0
- package/ui/forge_ui/static/search.js +120 -0
- package/ui/forge_ui/static/shell.js +137 -0
- package/ui/forge_ui/static/stagectl.js +150 -0
- package/ui/forge_ui/static/style.css +1073 -0
- package/ui/forge_ui/static/theme.js +25 -0
- package/ui/forge_ui/transcripts.py +608 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""Silent outcome predictions on Clef: will a task's first attempt pass, will this retry pass.
|
|
2
|
+
|
|
3
|
+
Recorded and never read by a run. `forge-jev.py first-pass` runs once per plan, after the
|
|
4
|
+
table rewrite that assigns every row its dwarf and reviewer; `forge-jev.py retry-pass` runs
|
|
5
|
+
when a dwarf attempt above 1 begins. Each writes tasks/<id>/clef.first_pass or
|
|
6
|
+
clef.retry_pass.<n> and, through client.ask, a `jev` event in the run dataset. Nothing
|
|
7
|
+
here is put into a prompt, a verdict, tasks.tsv, a retry's spec or its findings.
|
|
8
|
+
|
|
9
|
+
The state is forge_jev.outcome's, the one the export rebuilds for the same rubrics, so a
|
|
10
|
+
live prediction and the measurement it is judged by see the same shape of evidence.
|
|
11
|
+
Bounded: one deadline for the whole command, at most 4 requests in flight, model
|
|
12
|
+
resolution cached per (role, spec).
|
|
13
|
+
"""
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import hashlib
|
|
17
|
+
import json
|
|
18
|
+
import subprocess
|
|
19
|
+
import tempfile
|
|
20
|
+
import threading
|
|
21
|
+
import time
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
from . import load_config, site_ready, write_json
|
|
25
|
+
from . import outcome, questions
|
|
26
|
+
from .client import ask, noul
|
|
27
|
+
from .features import task_size
|
|
28
|
+
from .routing import _excerpt, _memory
|
|
29
|
+
|
|
30
|
+
SCRIPTS = Path(__file__).resolve().parents[1]
|
|
31
|
+
DISPATCH = SCRIPTS / 'forge-dispatch.sh'
|
|
32
|
+
CONCURRENCY = 4
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _text(path: Path) -> str:
|
|
36
|
+
try:
|
|
37
|
+
return path.read_text(errors='replace')
|
|
38
|
+
except OSError:
|
|
39
|
+
return ''
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def rows(plan: Path) -> list[dict]:
|
|
43
|
+
out = []
|
|
44
|
+
for line in _text(plan / 'tasks.tsv').splitlines():
|
|
45
|
+
if not line.strip() or line.startswith('#'):
|
|
46
|
+
continue
|
|
47
|
+
parts = line.split('\t')
|
|
48
|
+
if len(parts) < 7:
|
|
49
|
+
continue
|
|
50
|
+
out.append(dict(id=parts[0], deps=parts[1], difficulty=parts[2], files=parts[3], dwarf=parts[4],
|
|
51
|
+
qa=parts[5], title=parts[6]))
|
|
52
|
+
return out
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class Resolver:
|
|
56
|
+
"""{model, effort, harness} of a role and spec, by dispatch's own dry run, once per pair."""
|
|
57
|
+
|
|
58
|
+
def __init__(self, deadline_at: float):
|
|
59
|
+
self.deadline_at = deadline_at
|
|
60
|
+
self.cache = {}
|
|
61
|
+
self.lock = threading.Lock()
|
|
62
|
+
|
|
63
|
+
def __call__(self, role: str, spec: str) -> dict:
|
|
64
|
+
key = (role, spec)
|
|
65
|
+
with self.lock:
|
|
66
|
+
if key in self.cache:
|
|
67
|
+
return self.cache[key]
|
|
68
|
+
found = {}
|
|
69
|
+
remaining = self.deadline_at - time.time()
|
|
70
|
+
if remaining > 0.5:
|
|
71
|
+
with tempfile.TemporaryDirectory(prefix='forge-clef-resolve-') as tmp:
|
|
72
|
+
prompt = Path(tmp) / 'input'
|
|
73
|
+
prompt.write_text('Offline resolution')
|
|
74
|
+
try:
|
|
75
|
+
out = subprocess.run(['/bin/bash', str(DISPATCH), role, spec, '--run-dir', tmp,
|
|
76
|
+
'--prompt-file', str(prompt), '--dry-run'],
|
|
77
|
+
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True,
|
|
78
|
+
timeout=min(10.0, remaining))
|
|
79
|
+
fields = dict(line.split('=', 1) for line in out.stdout.splitlines() if '=' in line)
|
|
80
|
+
if fields.get('model'):
|
|
81
|
+
found = dict(model=fields.get('model'), effort=fields.get('effort'),
|
|
82
|
+
harness=fields.get('harness'))
|
|
83
|
+
except (OSError, subprocess.SubprocessError, ValueError):
|
|
84
|
+
found = {}
|
|
85
|
+
with self.lock:
|
|
86
|
+
self.cache[key] = found
|
|
87
|
+
return found
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def task_state(plan: Path, repo: Path, row: dict, table: list, resolve: Resolver) -> dict | None:
|
|
91
|
+
prompt = _text(plan / 'tasks' / row['id'] / 'prompt.md')
|
|
92
|
+
if not prompt.strip():
|
|
93
|
+
return None
|
|
94
|
+
dwarf, reviewer = resolve('dwarf', row['dwarf']), resolve('qa', row['qa'])
|
|
95
|
+
if not dwarf or not reviewer:
|
|
96
|
+
return None
|
|
97
|
+
level = _text(plan / 'decompose_level').strip() or None
|
|
98
|
+
return outcome.assemble(
|
|
99
|
+
goal=_text(plan / 'goal.txt'), task_id=row['id'], title=row['title'], files=row['files'], deps=row['deps'],
|
|
100
|
+
prompt=prompt, approach=_text(plan / 'tasks' / row['id'] / 'approach.md'),
|
|
101
|
+
plan_tasks=outcome.siblings(table, row['id']), file_excerpts=_excerpt(repo, row['files']),
|
|
102
|
+
memory=_memory(repo), dwarf=dwarf, reviewer=reviewer,
|
|
103
|
+
size=task_size(prompt=prompt, declared_files=row['files'], plan_tasks=len(table), deps=row['deps'],
|
|
104
|
+
decompose_level=level))
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _mint_run_uid(plan: Path) -> None:
|
|
108
|
+
"""The plan's dataset uid, before any request: otherwise each prediction's run is a
|
|
109
|
+
legacy id that never joins the attempts the plan goes on to run."""
|
|
110
|
+
try:
|
|
111
|
+
from forge_dataset.capture import mint_run_uid
|
|
112
|
+
mint_run_uid(plan)
|
|
113
|
+
except Exception:
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _assigned(row: dict) -> bool:
|
|
118
|
+
return row['dwarf'] not in ('', '-', 'UNASSIGNED') and row['qa'] not in ('', '-')
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def first_pass(plan: Path, repo: Path, *, config: dict | None = None, budget_s: float | None = None) -> int:
|
|
122
|
+
"""One prediction per assigned row; returns how many were recorded."""
|
|
123
|
+
config = config or load_config()
|
|
124
|
+
if not site_ready('clef-first-pass', config):
|
|
125
|
+
return 0
|
|
126
|
+
deadline_at = time.time() + (budget_s if budget_s is not None else 2 * config.get('deadline_s', 5.0))
|
|
127
|
+
table = rows(plan)
|
|
128
|
+
todo = [r for r in table if _assigned(r)]
|
|
129
|
+
if not todo:
|
|
130
|
+
return 0
|
|
131
|
+
_mint_run_uid(plan)
|
|
132
|
+
revision = hashlib.sha256(_text(plan / 'tasks.tsv').encode()).hexdigest()
|
|
133
|
+
resolve = Resolver(deadline_at)
|
|
134
|
+
gate = threading.BoundedSemaphore(CONCURRENCY)
|
|
135
|
+
recorded = []
|
|
136
|
+
|
|
137
|
+
def one(row):
|
|
138
|
+
with gate:
|
|
139
|
+
if time.time() >= deadline_at:
|
|
140
|
+
return
|
|
141
|
+
state = task_state(plan, repo, row, table, resolve)
|
|
142
|
+
if state is None or time.time() >= deadline_at:
|
|
143
|
+
return
|
|
144
|
+
result = ask(state, dict(first_pass=questions.first_pass()), site='clef-first-pass', run_dir=str(plan),
|
|
145
|
+
config=config, deadline_s=max(0.1, deadline_at - time.time()),
|
|
146
|
+
context=dict(task=row['id'], stage='plan', intended_attempt=1, plan_table=revision,
|
|
147
|
+
assignment=dict(dwarf=row['dwarf'], qa=row['qa'])))
|
|
148
|
+
p = noul(result, 'first_pass')
|
|
149
|
+
if p is None:
|
|
150
|
+
return
|
|
151
|
+
try:
|
|
152
|
+
write_json(plan / 'tasks' / row['id'] / 'clef.first_pass',
|
|
153
|
+
dict(p=p, model=result.get('model'), at=time.time(), plan_table=revision,
|
|
154
|
+
assignment=dict(dwarf=row['dwarf'], qa=row['qa'])))
|
|
155
|
+
recorded.append(row['id'])
|
|
156
|
+
except OSError:
|
|
157
|
+
pass
|
|
158
|
+
|
|
159
|
+
threads = [threading.Thread(target=one, args=(r,), daemon=True) for r in todo]
|
|
160
|
+
for thread in threads:
|
|
161
|
+
thread.start()
|
|
162
|
+
for thread in threads:
|
|
163
|
+
thread.join(max(0.0, deadline_at - time.time()))
|
|
164
|
+
return len(recorded)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def retry_pass(plan: Path, repo: Path, task: str, attempt: int, *, config: dict | None = None,
|
|
168
|
+
budget_s: float | None = None) -> bool:
|
|
169
|
+
"""The prediction for a dwarf attempt above 1, with the findings it was handed."""
|
|
170
|
+
config = config or load_config()
|
|
171
|
+
out = plan / 'tasks' / task / ('clef.retry_pass.%d' % attempt)
|
|
172
|
+
findings = _text(plan / 'tasks' / task / 'retry_findings.md')
|
|
173
|
+
if attempt < 2 or not findings.strip() or out.exists() or not site_ready('clef-retry-pass', config):
|
|
174
|
+
return False
|
|
175
|
+
deadline_at = time.time() + (budget_s if budget_s is not None else 2 * config.get('deadline_s', 5.0))
|
|
176
|
+
table = rows(plan)
|
|
177
|
+
row = next((r for r in table if r['id'] == task), None)
|
|
178
|
+
if row is None or not _assigned(row):
|
|
179
|
+
return False
|
|
180
|
+
state = task_state(plan, repo, row, table, Resolver(deadline_at))
|
|
181
|
+
if state is None:
|
|
182
|
+
return False
|
|
183
|
+
state = outcome.retry(state, attempt=attempt, previous_review=findings)
|
|
184
|
+
result = ask(state, dict(retry_pass=questions.retry_pass()), site='clef-retry-pass',
|
|
185
|
+
run_dir=str(plan), config=config, deadline_s=max(0.1, deadline_at - time.time()),
|
|
186
|
+
context=dict(task=task, stage='retry', attempt=attempt,
|
|
187
|
+
assignment=dict(dwarf=row['dwarf'], qa=row['qa'])))
|
|
188
|
+
p = noul(result, 'retry_pass')
|
|
189
|
+
if p is None:
|
|
190
|
+
return False
|
|
191
|
+
try:
|
|
192
|
+
write_json(out, dict(p=p, model=result.get('model'), at=time.time(), attempt=attempt,
|
|
193
|
+
assignment=dict(dwarf=row['dwarf'], qa=row['qa'])))
|
|
194
|
+
except OSError:
|
|
195
|
+
return False
|
|
196
|
+
return True
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def main_first_pass(args) -> int:
|
|
200
|
+
return 0 if first_pass(Path(args.plan), Path(args.repo)) else 3
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def main_retry_pass(args) -> int:
|
|
204
|
+
return 0 if retry_pass(Path(args.plan), Path(args.repo), args.task, args.attempt) else 3
|