transformers-triage / code /exporters_edit.py
ArthurZ's picture
ArthurZ HF Staff
Add triage pipeline code
cc857c8 verified
Raw History Blame Contribute Delete
4.53 kB
import json, re, subprocess, sys
DOC = 'https://huggingface.co/docs/transformers/main/en/exporters'
EXP = f'`transformers.exporters` ({DOC})'
TARGETS = {9784:'issue',12851:'issue',18243:'issue',22221:'issue',22756:'issue',33184:'issue',41838:'issue',
28781:'issue',29288:'issue',31962:'issue',38917:'issue',40260:'issue',29315:'pr'}
SUBS = [ # (regex, replacement) applied in order
(r'ONNX export (now )?lives in \[Optimum\]\(https://huggingface\.co/docs/optimum/exporters/onnx/overview\)( \(`optimum-cli export onnx --model <id> out/`\))?', f'Export (ONNX included) now goes through [`transformers.exporters`]({DOC})'),
(r'ONNX export is now handled by Optimum \(https://huggingface\.co/docs/transformers/main/en/exporters\)', f'export is now handled by {EXP}'),
(r'(ONNX )?export (is handled by|now lives in) Optimum( now)?', f'export now goes through {EXP}'),
(r'These ONNX exports are used by Transformers\.js / Optimum, so follow-ups belong there\.', f'For exporting, see {EXP}.'),
(r'see the wrappers in `src/transformers/integrations/executorch\.py` as noted above, or Optimum for ONNX', f'see {EXP}'),
(r'exporting to ONNX \(via Optimum\)', f'exporting to ONNX with {EXP}'),
(r'export now goes through torch\.export / optimum', f'export now goes through {EXP}'),
(r'For export, `torch\.export` is the recommended path now\.', f'For export, use {EXP}.'),
(r'For graph capture, `torch\.export` / `torch\.compile` are the supported paths now\.', f'For export, use {EXP}; `torch.compile` covers graph capture for speed.'),
(r'For tracing/export, use `torch\.export` with the static cache', f'For export, use {EXP} with the static cache'),
]
POINTER = {
18243: f"The `transformers.onnx` exporter was removed in #41700; export (ONNX included) now goes through {EXP}. Closing; if LongT5 export still fails with `transformers.exporters` on current `main`, please open a new issue with a reproducer.",
22221: f"This isn't a model request: export (ONNX included) now goes through [`transformers.exporters`]({DOC}). To get separate encoders, export `CLIPTextModelWithProjection` and `CLIPVisionModelWithProjection` on their own.",
38917: f"On `main`, the new {EXP} API (#41992) is the recommended way to do this: its exporters take forward kwargs including `past_key_values` (a `Cache`) as example inputs, and there is an ONNX backend in `src/transformers/exporters/exporter_onnx.py`. If something is still missing for your use case, a PR extending the exporters would be very welcome (see https://huggingface.co/docs/transformers/main/en/exporters_extend).",
40260: f"On `main`, export goes through {EXP} (#41992), which supports dynamic shapes; extending it is documented at https://huggingface.co/docs/transformers/main/en/exporters_extend. The legacy helpers in `integrations/executorch.py` are not where new export work should go.",
}
MARK = re.compile(r'(?i)optimum|torch\.export|executorch')
def ours(kind, n):
q = '{repository(owner:"huggingface",name:"transformers"){issueOrPullRequest(number:%d){... on Issue{comments(last:40){nodes{databaseId author{login} createdAt body}}} ... on PullRequest{comments(last:40){nodes{databaseId author{login} createdAt body}}}}}}' % n
r = json.loads(subprocess.run(['gh','api','graphql','-f','query='+q], capture_output=True, text=True).stdout)
return [c for c in r['data']['repository']['issueOrPullRequest']['comments']['nodes'] if (c['author'] or {}).get('login') == 'ArthurZucker' and c['createdAt'] >= '2026-10-20' and MARK.search(c['body'])]
plan = []
for n, kind in TARGETS.items():
cs = ours(kind, n)
if not cs: print(f'#{n}: NO matching comment of ours'); continue
c = cs[-1]; body = c['body']
if n in POINTER: new = POINTER[n]
else:
new = body
for rx, rep in SUBS: new = re.sub(rx, rep, new)
if new == body or re.search(r'(?i)optimum', new): print(f'#{n}: UNCHANGED or still mentions optimum →', body[:160].replace('\n',' ')); continue
plan.append(dict(n=n, id=c['databaseId'], old=body, new=new))
json.dump(plan, open('exporters_plan.json','w'), indent=1)
print('planned edits:', len(plan))
for p in plan: print(f"--- #{p['n']}\n{p['new'][:330]}")
if '--apply' in sys.argv:
for i, p in enumerate(plan, 1):
r = subprocess.run(['gh','api','-X','PATCH',f"repos/huggingface/transformers/issues/comments/{p['id']}",'-f','body='+p['new']], capture_output=True, text=True)
print(f"[{i}/{len(plan)}] #{p['n']} {'ok' if r.returncode==0 else 'FAIL '+r.stderr[:120]}", flush=True)