@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,216 @@
|
|
|
1
|
+
"""Model leaderboard facts: one row per task (with its attempts) and per review, for every run.
|
|
2
|
+
|
|
3
|
+
Rows are facts, not scores: the page aggregates them. Outcomes follow forge's own labelling
|
|
4
|
+
(scripts/forge_dataset/export.py `_attempts`/`_outcome`): an attempt that says nothing about the
|
|
5
|
+
implementer is censored, never counted as a failure.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections import OrderedDict
|
|
10
|
+
import json
|
|
11
|
+
import math
|
|
12
|
+
import os
|
|
13
|
+
import threading
|
|
14
|
+
import time
|
|
15
|
+
|
|
16
|
+
from forge_ui import executions, reader
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
_MAX_ROWS = 50000
|
|
20
|
+
_MAX_WARNINGS = 500
|
|
21
|
+
_RUNNING_S = 120
|
|
22
|
+
_SIZE = 4096
|
|
23
|
+
_PASS = ('PASS', 'MERGED')
|
|
24
|
+
_LOCK = threading.Lock()
|
|
25
|
+
_RUNS = OrderedDict() # realpath -> (signature, computed at, facts)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _alias(spec):
|
|
29
|
+
return (spec.split(':', 1)[0] or None) if isinstance(spec, str) else None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _attempt_rows(rd, name):
|
|
33
|
+
"""{n: (status, dwarf spec)} from attempts.tsv, oldest first. Infra-refunded rows spent no
|
|
34
|
+
attempt; a QA-only resume reuses its number, so the number's last row holds its status."""
|
|
35
|
+
rows = {}
|
|
36
|
+
for label, row in rd.rows(name, 3):
|
|
37
|
+
try:
|
|
38
|
+
n = int(row[1])
|
|
39
|
+
except ValueError:
|
|
40
|
+
rd.warn(label, 'invalid attempt')
|
|
41
|
+
continue
|
|
42
|
+
status = row[2].strip()
|
|
43
|
+
if not status or status == 'INFRA' or (len(row) > 3 and reader._nullable(row[3].strip())):
|
|
44
|
+
continue
|
|
45
|
+
rows[n] = (status, (row[4].strip() or None) if len(row) > 4 else None)
|
|
46
|
+
return dict(sorted(rows.items()))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _outcome(status, dwarfs, reviews):
|
|
50
|
+
if status in _PASS:
|
|
51
|
+
return 'pass', None
|
|
52
|
+
if status == 'FAIL':
|
|
53
|
+
return 'fail', None
|
|
54
|
+
if status in ('TIMEOUT', 'ERROR'):
|
|
55
|
+
# Both come from the implementer, the reviewer or forge's own steps between them; only an
|
|
56
|
+
# implementer that ran out of time failed the task.
|
|
57
|
+
if reviews:
|
|
58
|
+
return 'censored', 'reviewer_' + status.lower()
|
|
59
|
+
last = dwarfs[-1] if dwarfs else None
|
|
60
|
+
if status == 'TIMEOUT' and last is not None and (last['exit_code'] == 7 or last['timed_out']):
|
|
61
|
+
return 'fail', None
|
|
62
|
+
code = last['exit_code'] if last is not None else None
|
|
63
|
+
return 'censored', ('dwarf_' if code not in (0, None) else 'forge_') + status.lower()
|
|
64
|
+
return 'censored', status.lower()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _attempts(rows, execs, plan_spec):
|
|
68
|
+
result = []
|
|
69
|
+
for n, (status, logged) in rows.items():
|
|
70
|
+
dwarfs = [item for item in execs if item['role'] == 'dwarf' and item['attempt'] == n]
|
|
71
|
+
reviews = [item for item in execs if item['role'] == 'qa' and not item['shadow'] and item['attempt'] == n]
|
|
72
|
+
outcome, reason = _outcome(status, dwarfs, reviews)
|
|
73
|
+
last = dwarfs[-1] if dwarfs else None
|
|
74
|
+
model = last['model'] if last is not None else None
|
|
75
|
+
if model:
|
|
76
|
+
spec, source = model['spec'], 'execution'
|
|
77
|
+
elif logged:
|
|
78
|
+
spec, source = logged, 'attempts.tsv'
|
|
79
|
+
elif n == 1 and plan_spec:
|
|
80
|
+
spec, source = plan_spec, 'plan'
|
|
81
|
+
else:
|
|
82
|
+
spec = source = None
|
|
83
|
+
verdict = reviews[-1]['verdict'] if reviews else None
|
|
84
|
+
result.append(dict(n=n, alias=model['alias'] if model else _alias(spec), spec=spec, source=source,
|
|
85
|
+
outcome=outcome, reason=reason, gate=outcome == 'pass' and verdict == 'FAIL',
|
|
86
|
+
model_s=last['phases'].get('model') if last is not None else None,
|
|
87
|
+
duration_s=last['duration_s'] if last is not None else None,
|
|
88
|
+
tokens=dict(last['tokens']) if last is not None and last['tokens'] else None,
|
|
89
|
+
timed_out=bool(last is not None and last['timed_out'])))
|
|
90
|
+
return result
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _route(rd, base):
|
|
94
|
+
explored, propensity = rd.exists(base + '/route.explored'), None
|
|
95
|
+
for n, line in enumerate((rd.text(base + '/route.jsonl') or '').splitlines(), 1):
|
|
96
|
+
obj = rd.object(f'{base}/route.jsonl line {n}', line) if line.strip() else None
|
|
97
|
+
if obj is None:
|
|
98
|
+
continue
|
|
99
|
+
explored = explored or obj.get('explored') is True
|
|
100
|
+
value = obj.get('propensity')
|
|
101
|
+
# The newest decision is the one the task ran with.
|
|
102
|
+
propensity = float(value) if type(value) in (int, float) and math.isfinite(value) else propensity
|
|
103
|
+
return explored, propensity
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _reviews(key, ident, execs, shadows):
|
|
107
|
+
rows = []
|
|
108
|
+
for item in execs:
|
|
109
|
+
if item['role'] == 'qa' and not item['shadow'] and item['state'] == 'done':
|
|
110
|
+
model = item['model'] or {}
|
|
111
|
+
rows.append(dict(run=key, task=ident, n=item['attempt'], kind='primary', alias=model.get('alias'),
|
|
112
|
+
spec=model.get('spec'), verdict=item['verdict'], findings=item['findings'],
|
|
113
|
+
duration_s=item['duration_s'], tokens=item['tokens'], agreement=None, primary_alias=None))
|
|
114
|
+
for entry, items, _ in shadows:
|
|
115
|
+
if entry['status'] != 'done':
|
|
116
|
+
continue
|
|
117
|
+
model = (items[-1]['model'] if items else None) or {}
|
|
118
|
+
spec = entry['spec'] if isinstance(entry['spec'], str) else model.get('spec')
|
|
119
|
+
primaries = [item for item in execs if item['role'] == 'qa' and not item['shadow']
|
|
120
|
+
and item['attempt'] == entry['attempt'] and item['model']]
|
|
121
|
+
rows.append(dict(run=key, task=ident, n=entry['attempt'], kind='shadow', alias=model.get('alias') or _alias(spec),
|
|
122
|
+
spec=spec, verdict=entry['verdict'], findings=entry['findings'],
|
|
123
|
+
duration_s=entry['duration_s'], tokens=items[-1]['tokens'] if items else None,
|
|
124
|
+
agreement=entry['agreement'],
|
|
125
|
+
primary_alias=primaries[-1]['model']['alias'] if primaries else _alias(entry['primary'])))
|
|
126
|
+
return rows
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _facts(path, summary):
|
|
130
|
+
rd = reader._Read(path)
|
|
131
|
+
key = summary['key']
|
|
132
|
+
base_row = dict(run=key, repo=summary['repo'], created=summary['created'])
|
|
133
|
+
tasks, reviews = [], []
|
|
134
|
+
if summary['kind'] == 'solo':
|
|
135
|
+
execs = reader._solo_execs(rd, reader._solo_status(rd), reader._history(rd, 'attempts.tsv'))
|
|
136
|
+
tasks.append(dict(base_row, task='solo', kind='solo', difficulty=None, explored=False, propensity=None,
|
|
137
|
+
final_status=reader._solo_status(rd), accepted=False,
|
|
138
|
+
attempts=_attempts(_attempt_rows(rd, 'attempts.tsv'), execs, reader._role_spec(rd, 'dwarf')),
|
|
139
|
+
escalations=[]))
|
|
140
|
+
reviews += _reviews(key, 'solo', execs, [])
|
|
141
|
+
else:
|
|
142
|
+
for task in reader._tasks(rd):
|
|
143
|
+
base = f'tasks/{task["id"]}'
|
|
144
|
+
task['attempts'], task['attempt'] = reader._attempt_state(rd, base)
|
|
145
|
+
execs = reader._task_execs(rd, task)
|
|
146
|
+
shadows = reader._shadows(rd, task['id'], execs)
|
|
147
|
+
explored, propensity = _route(rd, base)
|
|
148
|
+
tasks.append(dict(base_row, task=task['id'], kind='plan', difficulty=task['difficulty'] or None,
|
|
149
|
+
explored=explored, propensity=propensity, final_status=task['status'],
|
|
150
|
+
accepted=reader._nonempty(rd, base + '/accepted.by'),
|
|
151
|
+
attempts=_attempts(_attempt_rows(rd, base + '/attempts.tsv'), execs, task['dwarf']),
|
|
152
|
+
escalations=[{'n': item['attempt'], 'from': item['from'], 'to': item['to']}
|
|
153
|
+
for item in reader._history(rd, base + '/escalations.tsv', True)]))
|
|
154
|
+
reviews += _reviews(key, task['id'], execs, shadows)
|
|
155
|
+
items = executions.review_dirs(rd)[0]
|
|
156
|
+
published = reader._published(rd)
|
|
157
|
+
for index, item in enumerate(items):
|
|
158
|
+
review, execs, _ = reader._final_review(rd, item, index == 0, published)
|
|
159
|
+
if review['state'] != 'done':
|
|
160
|
+
continue
|
|
161
|
+
run = execs[-1] if execs else None
|
|
162
|
+
model = (run['model'] if run else None) or {}
|
|
163
|
+
spec = review['spec'] or model.get('spec')
|
|
164
|
+
reviews.append(dict(run=key, task=None, n=None, kind='final', alias=model.get('alias') or _alias(spec),
|
|
165
|
+
spec=spec, verdict=review['verdict'] if review['verdict'] in ('PASS', 'FAIL') else None,
|
|
166
|
+
findings=review['findings'], duration_s=review['duration_s'],
|
|
167
|
+
tokens=run['tokens'] if run else None, agreement=None, primary_alias=None))
|
|
168
|
+
order = ('run', 'task', 'kind', 'repo', 'difficulty', 'created', 'explored', 'propensity', 'final_status',
|
|
169
|
+
'accepted', 'attempts', 'escalations')
|
|
170
|
+
return dict(tasks=[{name: row[name] for name in order} for row in tasks], reviews=reviews,
|
|
171
|
+
warnings=[f'{key}: {item}' for item in rd.warnings])
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _signature(summary):
|
|
175
|
+
return (summary['updated'], summary['task_count'], json.dumps(summary['counts'], sort_keys=True),
|
|
176
|
+
summary['final_review'])
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _cached(path, summary, now):
|
|
180
|
+
real = os.path.realpath(path)
|
|
181
|
+
sig = _signature(summary)
|
|
182
|
+
hit = _RUNS.get(real)
|
|
183
|
+
if hit is not None and hit[0] == sig and not ('RUNNING' in summary['counts'] and now - hit[1] > _RUNNING_S):
|
|
184
|
+
_RUNS.move_to_end(real)
|
|
185
|
+
return hit[2]
|
|
186
|
+
facts = _facts(path, summary)
|
|
187
|
+
_RUNS[real] = (sig, now, facts)
|
|
188
|
+
_RUNS.move_to_end(real)
|
|
189
|
+
while len(_RUNS) > _SIZE:
|
|
190
|
+
_RUNS.popitem(last=False)
|
|
191
|
+
return facts
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def collect(found):
|
|
195
|
+
"""Models: the facts of every run of `found` ((path, source) pairs), newest run first."""
|
|
196
|
+
# One computation at a time: a cold cache reads every run once, not once per request.
|
|
197
|
+
with _LOCK:
|
|
198
|
+
now = time.time()
|
|
199
|
+
runs = []
|
|
200
|
+
for path, source in found:
|
|
201
|
+
summary = reader.summarize(path, source)
|
|
202
|
+
if reader._kind(path):
|
|
203
|
+
runs.append((path, summary))
|
|
204
|
+
runs.sort(key=lambda item: item[1]['updated'] if item[1]['updated'] is not None else float('-inf'), reverse=True)
|
|
205
|
+
tasks, reviews, warnings, count, truncated = [], [], [], 0, False
|
|
206
|
+
for path, summary in runs:
|
|
207
|
+
if len(tasks) + summary['task_count'] > _MAX_ROWS:
|
|
208
|
+
truncated = True
|
|
209
|
+
break
|
|
210
|
+
facts = _cached(path, summary, now)
|
|
211
|
+
tasks += facts['tasks']
|
|
212
|
+
reviews += facts['reviews']
|
|
213
|
+
warnings += facts['warnings']
|
|
214
|
+
count += 1
|
|
215
|
+
return dict(generated=now, runs=count, truncated=truncated, tasks=tasks, reviews=reviews,
|
|
216
|
+
warnings=warnings[:_MAX_WARNINGS])
|