Skip to content

[OPIK-7438] [BE] fix: gate sampling params for Anthropic adaptive-thinking models on the playground/inference path #13317

[OPIK-7438] [BE] fix: gate sampling params for Anthropic adaptive-thinking models on the playground/inference path

[OPIK-7438] [BE] fix: gate sampling params for Anthropic adaptive-thinking models on the playground/inference path #13317

Workflow file for this run

name: 🗂️ Auto Label PR
on:
pull_request_target:
types: [opened, reopened, synchronize, ready_for_review]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
labeler:
name: PR Labeler - Apply and Sync Labels
runs-on: ubuntu-latest
timeout-minutes: 1
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
permissions:
contents: read
pull-requests: write
steps:
- name: Apply and Sync Labels
uses: actions/labeler@v6
with:
sync-labels: true # Remove labels if the file paths no longer match
configuration-path: .github/labeler.yml
size-labeler:
name: Label PR by size
runs-on: ubuntu-latest
timeout-minutes: 2
# Run after the path-based labeler, never alongside it. actions/labeler's
# sync-labels sweep writes an authoritative label set from a snapshot taken
# when it started; if it overlaps the size step it drops the size label it
# never saw. Ordering the size step after the sync closes that window.
#
# needs orders it after labeler; !cancelled() lets it still run when labeler
# fails (e.g. a flaky action download) so size labeling isn't coupled to the
# path labeler's success, while still skipping when the run is superseded.
needs: labeler
if: ${{ !cancelled() }}
permissions:
contents: read
pull-requests: write
issues: write
steps:
- name: Compute size and apply label
uses: actions/github-script@v7
with:
script: |
const { owner, repo } = context.repo;
const pr = context.payload.pull_request;
const prNumber = pr.number;
// Uniform charcoal chip for every size label so the size metadata reads as one
// recessive family and doesn't compete with semantic labels (bug, Frontend, …) on
// a busy PR list. The cool -> hot ramp lives entirely in the per-bucket emoji
// (🔵 smallest -> 🔴 largest), which keeps its crisp shape on a neutral background.
// maxSize is the inclusive upper bound of changed LOC (additions + deletions) for
// the bucket; the last entry (null) catches everything above it.
const CHIP_COLOR = "2d3139";
const BUCKETS = [
{ name: "🔵 size/XS", color: CHIP_COLOR, maxSize: 19 },
{ name: "🟢 size/S", color: CHIP_COLOR, maxSize: 100 },
{ name: "🟡 size/M", color: CHIP_COLOR, maxSize: 300 },
{ name: "🟠 size/L", color: CHIP_COLOR, maxSize: 600 },
{ name: "🔴 size/XL", color: CHIP_COLOR, maxSize: null },
];
const MANAGED_LABELS = BUCKETS.map((b) => b.name);
// Lockfiles and generated clients dwarf the human-reviewed diff, so exclude them
// from the count — otherwise a lockfile bump mislabels a tiny PR as XL.
const IGNORE_GLOBS = [
"**/package-lock.json",
"**/yarn.lock",
"**/pnpm-lock.yaml",
"**/poetry.lock",
"**/uv.lock",
"sdks/python/src/opik/rest_api/**",
"sdks/typescript/src/opik/rest_api/**",
"**/*.snap",
"**/*.svg",
"**/*.png",
];
const globToRegExp = (glob) => {
let re = "";
for (let i = 0; i < glob.length; i++) {
if (glob.startsWith("**/", i)) { re += "(?:[^/]+/)*"; i += 2; continue; }
if (glob.startsWith("**", i)) { re += ".*"; i += 1; continue; }
const c = glob[i];
if (c === "*") { re += "[^/]*"; continue; }
if (".+^${}()|[]\\".includes(c)) { re += "\\" + c; continue; }
re += c;
}
return new RegExp("^" + re + "$");
};
const ignoreRes = IGNORE_GLOBS.map(globToRegExp);
const isIgnored = (path) => ignoreRes.some((re) => re.test(path));
const files = await github.paginate(github.rest.pulls.listFiles, {
owner,
repo,
pull_number: prNumber,
per_page: 100,
});
const total = files
.filter((f) => !isIgnored(f.filename))
.reduce((sum, f) => sum + f.additions + f.deletions, 0);
const bucket =
BUCKETS.find((b) => b.maxSize !== null && total <= b.maxSize) ||
BUCKETS[BUCKETS.length - 1];
core.info(`PR #${prNumber}: ${total} changed LOC (excluding ignored files) -> ${bucket.name}`);
// Keep the label's color/emoji authoritative regardless of how it was first created.
try {
const { data: existing } = await github.rest.issues.getLabel({
owner,
repo,
name: bucket.name,
});
if (existing.color !== bucket.color) {
await github.rest.issues.updateLabel({
owner,
repo,
name: bucket.name,
color: bucket.color,
});
core.info(`Updated label color: ${bucket.name} -> #${bucket.color}`);
}
} catch (err) {
if (err.status !== 404) throw err;
await github.rest.issues.createLabel({
owner,
repo,
name: bucket.name,
color: bucket.color,
});
core.info(`Created label: ${bucket.name} (#${bucket.color})`);
}
// Read the live label set rather than the event payload, which is a stale snapshot
// from when the event fired and predates the labeler job's sync.
const live = await github.paginate(github.rest.issues.listLabelsOnIssue, {
owner,
repo,
issue_number: prNumber,
per_page: 100,
});
const current = live.map((l) => l.name);
core.info(`PR #${prNumber} live labels: [${current.join(", ")}]`);
// Reconcile with an incremental delta that touches only the managed size buckets:
// remove any bucket that isn't the target, add the target if missing. The needs:
// labeler ordering already serializes this against the path labeler, so there is no
// concurrent writer to interleave with — and unlike setLabels, a delta never rewrites
// the full set, so a label added by anyone else in the meantime is left untouched.
for (const stale of current.filter(
(name) => MANAGED_LABELS.includes(name) && name !== bucket.name
)) {
try {
await github.rest.issues.removeLabel({
owner,
repo,
issue_number: prNumber,
name: stale,
});
core.info(`Removed stale size label: ${stale}`);
} catch (err) {
// The label may have been removed by someone else between the read and now;
// a 404 is benign. Swallow it so the loop and the add below still run.
if (err.status !== 404) throw err;
core.info(`Stale size label already gone (404): ${stale}`);
}
}
if (!current.includes(bucket.name)) {
await github.rest.issues.addLabels({
owner,
repo,
issue_number: prNumber,
labels: [bucket.name],
});
core.info(`Added size label: ${bucket.name}`);
} else {
core.info(`Size label already present, no add needed: ${bucket.name}`);
}