Support image[] parameter for /v1/images/edits
#628
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Auto-label issues and PRs. | |
| # | |
| # Two passes per item: | |
| # 1. LLM classification (Claude Haiku) over title + body for engine::*, | |
| # area::*, runtime::*, component, and type labels. Runs once per | |
| # newly opened issue/PR. | |
| # 2. Deterministic priority::warm / priority::hot from community | |
| # engagement counts (commenters + supporting reactions, excluding | |
| # maintainers). Refreshed on every new comment (engagement event) | |
| # and via a daily cron (to pick up reaction-only changes, which | |
| # don't fire webhook events). | |
| # | |
| # *** Labels applied by this workflow are ADVISORY ONLY. *** | |
| # Do not use them as inputs to CI gates, release gates, security | |
| # decisions, or any policy check — the model sees untrusted user-supplied | |
| # title/body content and the classification is best-effort. | |
| # | |
| # Add-only for human / LLM labels — never removes a human-applied label. | |
| # The bot may transition between its own priority labels (drops warm | |
| # when promoting to hot) so the two never coexist. | |
| # | |
| # Priority is sticky: once warm or hot is applied, it is NEVER demoted | |
| # or removed if engagement later drops back below the threshold. The | |
| # label reflects peak engagement; demotion, if ever needed, is manual. | |
| name: Auto-label issues and PRs | |
| on: | |
| issues: | |
| types: [opened] | |
| pull_request_target: | |
| types: [opened] | |
| issue_comment: | |
| types: [created] | |
| schedule: | |
| # Daily at 06:00 UTC — catches priority transitions driven by | |
| # reactions, which don't fire webhook events. | |
| - cron: "0 6 * * *" | |
| workflow_dispatch: | |
| inputs: | |
| target: | |
| description: "Issue/PR number to label (leave blank to backfill all unlabeled open items)" | |
| required: false | |
| type: string | |
| dry_run: | |
| description: "Log decisions without applying labels (recommended for spot-testing)" | |
| required: false | |
| type: boolean | |
| default: true | |
| mode: | |
| description: "all = LLM classification + priority refresh. priority-only = skip the LLM (no Anthropic call)." | |
| required: false | |
| type: choice | |
| options: | |
| - all | |
| - priority-only | |
| default: all | |
| permissions: | |
| contents: read | |
| issues: write | |
| # Required for labels on PRs. GITHUB_TOKEN's label-write scope is | |
| # checked per resource type — `gh issue edit --add-label` against a | |
| # PR number routes through the issues API but the permission check is | |
| # against the underlying pull_request resource. | |
| pull-requests: write | |
| jobs: | |
| label: | |
| runs-on: ubuntu-latest | |
| # Skip workflow-bot comments to avoid self-triggering loops. | |
| if: github.event_name != 'issue_comment' || github.event.comment.user.type != 'Bot' | |
| concurrency: | |
| group: auto-label-${{ github.event.issue.number || github.event.pull_request.number || inputs.target || github.event_name }} | |
| cancel-in-progress: false | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 1 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Resolve items, dry-run, and mode | |
| id: resolve | |
| env: | |
| TARGET_INPUT: ${{ inputs.target }} | |
| DRY_RUN_INPUT: ${{ inputs.dry_run }} | |
| MODE_INPUT: ${{ inputs.mode }} | |
| run: | | |
| set -euo pipefail | |
| case "${{ github.event_name }}" in | |
| issues) | |
| echo "items=${{ github.event.issue.number }}" >> "$GITHUB_OUTPUT" | |
| echo "dry_run=false" >> "$GITHUB_OUTPUT" | |
| echo "mode=all" >> "$GITHUB_OUTPUT" | |
| ;; | |
| pull_request_target) | |
| echo "items=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT" | |
| echo "dry_run=false" >> "$GITHUB_OUTPUT" | |
| echo "mode=all" >> "$GITHUB_OUTPUT" | |
| ;; | |
| issue_comment) | |
| # Engagement event — refresh priority labels only. The LLM | |
| # already classified this item when it was opened; running | |
| # it again on every comment would burn tokens with no new | |
| # signal. | |
| echo "items=${{ github.event.issue.number }}" >> "$GITHUB_OUTPUT" | |
| echo "dry_run=false" >> "$GITHUB_OUTPUT" | |
| echo "mode=priority-only" >> "$GITHUB_OUTPUT" | |
| ;; | |
| schedule) | |
| # Daily sweep: re-evaluate priority across every open item | |
| # to pick up reaction-only changes since the last run. | |
| ITEMS=$(gh api --paginate \ | |
| "/repos/${{ github.repository }}/issues?state=open&per_page=100" \ | |
| --jq '.[].number' | xargs) | |
| echo "items=$ITEMS" >> "$GITHUB_OUTPUT" | |
| echo "dry_run=false" >> "$GITHUB_OUTPUT" | |
| echo "mode=priority-only" >> "$GITHUB_OUTPUT" | |
| ;; | |
| workflow_dispatch) | |
| if [ -n "$TARGET_INPUT" ]; then | |
| if ! [[ "$TARGET_INPUT" =~ ^[0-9]+$ ]]; then | |
| echo "::error::target must be a positive integer (got: $TARGET_INPUT)" | |
| exit 1 | |
| fi | |
| echo "items=$TARGET_INPUT" >> "$GITHUB_OUTPUT" | |
| else | |
| # Backfill: every open issue + PR missing both engine::* | |
| # and area::*. /repos/.../issues includes PRs because PRs | |
| # are issues in the API model. | |
| ITEMS=$(gh api --paginate \ | |
| "/repos/${{ github.repository }}/issues?state=open&per_page=100" \ | |
| --jq '.[] | select((.labels | map(.name) | any(startswith("engine::") or startswith("area::"))) | not) | .number' \ | |
| | xargs) | |
| echo "items=$ITEMS" >> "$GITHUB_OUTPUT" | |
| fi | |
| echo "dry_run=$DRY_RUN_INPUT" >> "$GITHUB_OUTPUT" | |
| echo "mode=$MODE_INPUT" >> "$GITHUB_OUTPUT" | |
| ;; | |
| esac | |
| - name: Run auto-labeler (LLM + priority) | |
| if: steps.resolve.outputs.mode == 'all' | |
| env: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| run: | | |
| set -euo pipefail | |
| ITEMS="${{ steps.resolve.outputs.items }}" | |
| if [ -z "$ITEMS" ]; then | |
| echo "No items to label." | |
| exit 0 | |
| fi | |
| if [ "${{ steps.resolve.outputs.dry_run }}" = "true" ]; then | |
| python .github/scripts/auto_label.py $ITEMS --dry-run | |
| else | |
| python .github/scripts/auto_label.py $ITEMS | |
| fi | |
| - name: Run auto-labeler (priority only) | |
| if: steps.resolve.outputs.mode == 'priority-only' | |
| run: | | |
| set -euo pipefail | |
| ITEMS="${{ steps.resolve.outputs.items }}" | |
| if [ -z "$ITEMS" ]; then | |
| echo "No items to refresh." | |
| exit 0 | |
| fi | |
| if [ "${{ steps.resolve.outputs.dry_run }}" = "true" ]; then | |
| python .github/scripts/auto_label.py $ITEMS --priority-only --dry-run | |
| else | |
| python .github/scripts/auto_label.py $ITEMS --priority-only | |
| fi |