Configuration reference
All inputs the Action accepts, in the order you'd typically reach for them. For a minimal starter workflow, see the project README.
Example: full-capability setup
The workflow below enables the action's optional title generation, label application, and breaking-change detection.
name: AI PR Assistant
on:
pull_request:
types: [opened, synchronize, reopened, edited]
jobs:
ai-pr-assistant:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
issues: write
steps:
- name: Generate PR description and metadata
uses: devsetgo/ai-pr-assistant@main
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
openai_api_key: ${{ secrets.OPENAI_API_KEY }}
openai_model: gpt-5-mini
allowed_users: "your-github-username,maintainer-2"
overwrite_description: smart
generate_title: true
overwrite_title: smart
enable_labels: true
overwrite_labels: smart
label_taxonomy: "bug,feature,enhancement,documentation,dependencies,refactor,test,chore,breaking-change"
detect_breaking_changes: true
max_diff_tokens: "8000"
max_tokens: "6000"
Setup walkthrough
- Get an API key and add it to GitHub as a secret:
OPENAI_API_KEYfor OpenAI, orANTHROPIC_API_KEYfor Claude. The Setup guide walks through both, and Choosing a provider covers the inputs. - Create a workflow under
.github/workflows/using the example above. - Add the minimum GitHub permissions needed for the feature set you want:
pull-requests: writefor descriptions and titlesissues: writewhenenable_labelsordetect_breaking_changesistrue- Turn on optional features one at a time if you want to start simple:
generate_titlefor title suggestionsoverwrite_titleto apply the suggested title (see Existing content)enable_labelsfor label generation and label creationdetect_breaking_changesto warn on likely breaking changesoverwrite_descriptionto refresh or review the description on PR updates- Adjust
openai_model(oranthropic_model),max_tokens, andmax_diff_tokensif your repo has large diffs or you want more concise output. - For Azure OpenAI, set
azure_endpointandazure_openai_api_versioninstead of using the public OpenAI key flow.
Required
| Input | Description |
|---|---|
github_token |
The GitHub token to use for the Action |
openai_api_key or anthropic_api_key |
The API key for the selected provider (see below), keep it hidden. Only the key for the provider you use is needed |
Choosing a provider
| Input | Description | Default |
|---|---|---|
provider |
openai (also covers Azure OpenAI) or anthropic (Claude, via the direct Anthropic API) |
openai |
openai_api_key |
The OpenAI API key (or Azure OpenAI key). Required when provider is openai |
`` |
anthropic_api_key |
The Anthropic API key. Required when provider is anthropic |
`` |
anthropic_model |
The Claude model to use when provider is anthropic, e.g. claude-haiku-4-5-20251001, claude-sonnet-5, claude-opus-5 |
claude-haiku-4-5-20251001 |
A minimal Claude workflow:
- uses: devsetgo/ai-pr-assistant@main
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
provider: anthropic
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
For a side-by-side of the models each provider offers, see the Model reference.
The run fails with a clear message if provider isn't openai or anthropic, or
if the key for the selected provider is empty. Everything else (title, labels,
breaking-change detection, diff handling, the attribution footer) works the same
with either provider. Notes specific to Claude:
temperatureis ignored, for every Claude model. Several current models (Sonnet 5, Opus 5, Opus 4.8/4.7) reject sampling parameters, and the Anthropic SDK no longer exposes them, so the value is never sent, Haiku 4.5 included.openai_modeland the Azure inputs are ignored too.- Thinking models need headroom. The default, Haiku 4.5, doesn't think.
Sonnet 5 and Opus 5 think by default and that counts against
max_tokens; if it uses the whole budget the run stops with the "empty description" message rather than writing anything, so raisemax_tokensfor those models. - Refusals fail the run. If Claude declines a request, the Action reports
Anthropic request failedand leaves the pull request unchanged. - Diff budgeting is approximate.
max_diff_tokensis counted with an OpenAI tokenizer, which Claude's tokenizer can exceed, so treat it as a rough budget.
Core behavior
| Input | Description | Default |
|---|---|---|
pull_request_id |
The ID of the pull request to use | Extracted from metadata |
allowed_users |
Comma-separated GitHub usernames this Action will run for (case-insensitive, spaces ignored); empty runs for everyone | `` (all users) |
overwrite_description |
never, always or smart: what to do with an existing description, see Existing content |
never |
description_style |
rich (emoji section headings and GitHub callouts) or plain (bold subheadings and bullets). Structured mode only |
rich |
openai_model |
The OpenAI model to use when provider is openai, any model compatible with the chat completions endpoint |
gpt-5-mini |
temperature |
Higher values make the model more creative (0-2). Ignored for reasoning models (gpt-5*, gpt-6*, o1*, o3*, o4*), which only support the default temperature, and always ignored with provider: anthropic |
0.6 |
max_tokens |
Maximum number of output/completion tokens the model may generate. Reasoning models spend part of this on hidden reasoning tokens before any visible output, so a large diff can need more headroom. It's a ceiling, so a smaller reply costs less | 6000 |
Diff handling
| Input | Description | Default |
|---|---|---|
max_diff_tokens |
Maximum tokens of PR diff sent to the model, counted with the real tokenizer for openai_model (not a character-count guess); approximate for Claude models |
6000 |
exclude_patterns |
Comma-separated glob patterns for files dropped from the diff before token budgeting — useful for lockfiles, generated code, and binary/asset files that just waste tokens | *.lock,package-lock.json,yarn.lock,pnpm-lock.yaml,*.min.js,*.svg,*.png,*.jpg,*.jpeg,*.gif,*.ico,dist/*,build/* |
Prompt customization
| Input | Description | Default |
|---|---|---|
sample_prompt |
Few-shot example prompt given to the model for tone/style. Classic mode only — see Classic vs. structured mode below | See SAMPLE_PROMPT in pr_description/llm.py |
sample_response |
Few-shot example response paired with sample_prompt. Classic mode only |
See GOOD_SAMPLE_RESPONSE in pr_description/llm.py |
completion_prompt |
The task prompt prepended to the diff content, for both modes | See COMPLETION_PROMPT in pr_description/llm.py |
Optional capabilities (all opt-in, default off)
| Input | Description | Default |
|---|---|---|
generate_title |
Also ask the model to propose an improved PR title | false |
overwrite_title |
never, always or smart: whether to apply the proposed title (requires generate_title); never only prints it in the action log |
never |
enable_labels |
Auto-label the PR from label_taxonomy, creating any missing labels in the repo first |
false |
overwrite_labels |
never, always or smart: what to do when the PR already has labels (requires enable_labels) |
never |
label_taxonomy |
Comma-separated labels the model may choose from when enable_labels is set |
bug,feature,enhancement,documentation,dependencies,refactor,test,chore |
detect_breaking_changes |
Flag likely breaking changes on every run: adds a note to the top of the description and applies a breaking-change label, whatever the overwrite_* and enable_labels inputs say |
false |
Existing content: always, never or smart
overwrite_description, overwrite_title and overwrite_labels each decide what
happens to content the pull request already has. true and false still work, as
always and never.
never |
always |
smart |
|
|---|---|---|---|
| Description | Fills in a blank one, otherwise leaves it alone | Regenerates it on every run | Keeps, extends or replaces it, as the model judges |
| Title | Prints the suggestion in the log | Applies the suggestion on every run | Replaces a placeholder, or a title the model judges vague or wrong |
| Labels | Labels a PR with none, otherwise leaves them alone | Makes taxonomy labels match the model's choice | The model reviews the current labels and changes only what's clearly wrong or missing |
What smart does with a description:
| Existing description | Result |
|---|---|
| Blank, or only this Action's footer | Written from scratch, with no review |
| Generic, an unfilled template, inaccurate, or too thin for the size of the change | Replaced |
| Detailed, but a later push added something it doesn't cover | Extended: new text is appended in the same style, and the existing text is kept word for word |
| Detailed and still accurate | Kept, with no write to GitHub |
A title is always replaced under smart, without asking the model, if it's blank,
a stock word such as dev, wip or update, or just the branch name.
Some guarantees worth knowing:
- A failed review changes nothing. If the structured request is rejected or its
reply is cut off, the existing description, title and labels all stay as they are.
The run still passes, with a warning that the model gave no verdict; raise
max_tokensif its reply was cut off. - Only taxonomy labels are removed. Labels outside
label_taxonomyare never touched, and an existingbreaking-changelabel is never removed. - Breaking changes always surface. With
detect_breaking_changeson, a detected breaking change gets its note above the existing text and its label in every mode, includingnever. The rest of the description and labels stay as the modes say. The model therefore runs on every push, and a note or label already there stays even if a later run doesn't detect one; remove it by hand if it no longer applies. smartruns the model on every push, so it costs the same asalways. It also turns on structured mode, sosample_promptandsample_responsedon't apply.
Attribution
| Input | Description | Default |
|---|---|---|
attribution |
Append a small Created using devsetgo/ai-pr-assistant with <model> footer to the generated description. Set to false to omit it |
true |
The footer is added by the Action after the model responds, not requested in the
prompt, so it always names the openai_model you configured. On Azure OpenAI
that value is your deployment name, which may differ from the underlying model.
Azure OpenAI
| Input | Description | Default |
|---|---|---|
azure_endpoint |
Azure OpenAI resource endpoint. Setting this switches the Action to Azure OpenAI instead of the public OpenAI API | `` |
azure_openai_api_version |
The Azure OpenAI API version to target | `` |
Classic vs. structured mode
By default (generate_title, enable_labels, and detect_breaking_changes all
false), the Action uses classic mode: the original plain-text behavior,
using sample_prompt/sample_response as a few-shot example and returning
just a description. This is unchanged from earlier versions of this Action.
Turning on any one of generate_title, enable_labels, or
detect_breaking_changes, or a smart overwrite mode, switches to structured mode: a single request
constrained by a JSON schema returns the description, title, labels, and
breaking-change assessment together. sample_prompt/sample_response are not
used in this mode — the schema itself constrains the response shape instead.
In structured mode, the description is a short summary followed by a bulleted
list of key points. With description_style: rich, larger changes get emoji section
headings, > [!IMPORTANT] and > [!WARNING] callouts for what a reviewer must act
on, and a 👀 Review focus section; with plain, they get bold subheadings. When
smart extends a description, the new text follows the existing style instead.
The title is Title Case, prefixed with its category — e.g.
Enhancement: Add Dark Mode Toggle or Bug: Fix Crash on Empty Input
(Breaking Change: instead when detect_breaking_changes finds one).
If the API rejects a structured request (for example, an older Azure
deployment or API version without structured-output support), the Action
falls back once to a classic plain-text request rather than failing the run,
and logs a warning. Under smart, that text is only used to fill a blank description.
Permissions
- Updating the description/title requires
pull-requests: write(or equivalent classicGITHUB_TOKENpermissions — see Troubleshooting). enable_labelsanddetect_breaking_changesadditionally requireissues: write, since labels live on the issue object in the GitHub API, even for pull requests.
A note on breaking-change detection
A detected breaking change appears as a > [!CAUTION] callout at the top of the
description. detect_breaking_changes is judged by the model from the diff content alone,
not by static analysis of your language or framework. Treat it as a helpful
heuristic that can save you a re-read, not a guarantee — it can both miss
real breaking changes and flag things that aren't.