mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
ci: reject retired Anthropic model references
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
name: static / retired Anthropic models
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version: 22
|
||||
cache: pnpm
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile --ignore-scripts
|
||||
|
||||
- name: Test validator and check repository
|
||||
run: pnpm nx run repo-scripts:validate-retired-anthropic-models
|
||||
@@ -36,6 +36,7 @@
|
||||
"attw": "nx run-many -t attw --projects=packages/**",
|
||||
"check:packages": "nx run-many -t publint,attw --projects=packages/**",
|
||||
"validate:model-names": "tsx scripts/validate-doc-model-names.ts",
|
||||
"validate:retired-anthropic-models": "nx run repo-scripts:validate-retired-anthropic-models",
|
||||
"check:intelligence-env-names": "tsx scripts/validate-intelligence-env-names.ts",
|
||||
"check:plugin-skills": "tsx scripts/sync-plugin-skills.ts --check",
|
||||
"generate:channel-native-catalogs": "node scripts/channel-native-catalogs.mjs generate",
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
RETIRED_ANTHROPIC_MODELS,
|
||||
findRetiredAnthropicModels,
|
||||
} from "../validate-retired-anthropic-models.mjs";
|
||||
|
||||
const oldSonnet = ["claude", "3", "5", "sonnet", "20241022"].join("-");
|
||||
const oldAlias = ["claude", "sonnet", "4"].join("-");
|
||||
|
||||
test("reports retired dated IDs and aliases with their locations", () => {
|
||||
const source = `model=${oldSonnet}\nfallback=${oldAlias}`;
|
||||
|
||||
assert.deepEqual(findRetiredAnthropicModels("config.ts", source), [
|
||||
{ file: "config.ts", line: 1, model: oldSonnet },
|
||||
{ file: "config.ts", line: 2, model: oldAlias },
|
||||
]);
|
||||
});
|
||||
|
||||
test("accepts active model identifiers", () => {
|
||||
const source = [
|
||||
["claude", "sonnet", "4", "6"].join("-"),
|
||||
["claude", "opus", "4", "8"].join("-"),
|
||||
["claude", "haiku", "4", "5", "20251001"].join("-"),
|
||||
].join("\n");
|
||||
|
||||
assert.deepEqual(findRetiredAnthropicModels("config.ts", source), []);
|
||||
});
|
||||
|
||||
test("keeps the official retired-model inventory unique", () => {
|
||||
assert.equal(
|
||||
RETIRED_ANTHROPIC_MODELS.length,
|
||||
new Set(RETIRED_ANTHROPIC_MODELS).size,
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "repo-scripts",
|
||||
"$schema": "../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "scripts",
|
||||
"projectType": "library",
|
||||
"targets": {
|
||||
"validate-retired-anthropic-models": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": false,
|
||||
"options": {
|
||||
"commands": [
|
||||
"node --test scripts/__tests__/validate-retired-anthropic-models.test.mjs",
|
||||
"node scripts/validate-retired-anthropic-models.mjs"
|
||||
],
|
||||
"parallel": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { existsSync, readFileSync, statSync } from "node:fs";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
const model = (...segments) => segments.join("-");
|
||||
const versionedModel = (family, version) =>
|
||||
`${model("claude", ...family)}-${version}`;
|
||||
|
||||
// Source: https://platform.claude.com/docs/en/about-claude/model-deprecations
|
||||
// Keep the dated API IDs and commonly copied aliases together so runtime
|
||||
// defaults, demos, documentation, and environment examples cannot regress.
|
||||
export const RETIRED_ANTHROPIC_MODELS = [
|
||||
versionedModel(["opus", "4", "1"], "20250805"),
|
||||
versionedModel(["opus", "4"], "20250514"),
|
||||
versionedModel(["sonnet", "4"], "20250514"),
|
||||
versionedModel(["3", "7", "sonnet"], "20250219"),
|
||||
versionedModel(["3", "5", "haiku"], "20241022"),
|
||||
versionedModel(["3", "haiku"], "20240307"),
|
||||
versionedModel(["3", "5", "sonnet"], "20240620"),
|
||||
versionedModel(["3", "5", "sonnet"], "20241022"),
|
||||
versionedModel(["3", "opus"], "20240229"),
|
||||
`claude-${["2", "0"].join(".")}`,
|
||||
`claude-${["2", "1"].join(".")}`,
|
||||
versionedModel(["3", "sonnet"], "20240229"),
|
||||
`claude-${["1", "0"].join(".")}`,
|
||||
`claude-${["1", "1"].join(".")}`,
|
||||
`claude-${["1", "2"].join(".")}`,
|
||||
`claude-${["1", "3"].join(".")}`,
|
||||
`claude-${model("instant", "1")}.${0}`,
|
||||
`claude-${model("instant", "1")}.${1}`,
|
||||
`claude-${model("instant", "1")}.${2}`,
|
||||
model("claude", "3", "5", "sonnet", "latest"),
|
||||
model("claude", "3", "5", "haiku", "latest"),
|
||||
model("claude", "3", "opus", "latest"),
|
||||
model("claude", "opus", "4", "1"),
|
||||
`claude-${model("opus", "4")}.${1}`,
|
||||
model("claude", "opus", "4", "0"),
|
||||
model("claude", "opus", "4"),
|
||||
model("claude", "sonnet", "4", "0"),
|
||||
model("claude", "sonnet", "4"),
|
||||
model("claude", "3", "7", "sonnet"),
|
||||
`claude-${model("3", "7")}.sonnet`,
|
||||
model("claude", "3", "5", "haiku"),
|
||||
`claude-${model("3", "5")}.haiku`,
|
||||
model("claude", "3", "haiku"),
|
||||
model("claude", "3", "5", "sonnet"),
|
||||
`claude-${model("3", "5")}.sonnet`,
|
||||
model("claude", "3", "sonnet"),
|
||||
model("claude", "3", "opus"),
|
||||
];
|
||||
|
||||
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
|
||||
const retiredModelPattern = new RegExp(
|
||||
`(?:${[...new Set(RETIRED_ANTHROPIC_MODELS)]
|
||||
.sort((left, right) => right.length - left.length)
|
||||
.map(escapeRegExp)
|
||||
.join("|")})(?![A-Za-z0-9._-])`,
|
||||
"g",
|
||||
);
|
||||
|
||||
export function findRetiredAnthropicModels(file, source) {
|
||||
const violations = [];
|
||||
for (const [index, line] of source.split(/\r?\n/).entries()) {
|
||||
for (const match of line.matchAll(retiredModelPattern)) {
|
||||
violations.push({ file, line: index + 1, model: match[0] });
|
||||
}
|
||||
}
|
||||
return violations;
|
||||
}
|
||||
|
||||
export function scanRepository(cwd = process.cwd()) {
|
||||
const files = execFileSync(
|
||||
"git",
|
||||
["ls-files", "--cached", "--others", "--exclude-standard", "-z"],
|
||||
{ cwd, encoding: "utf8", maxBuffer: 16 * 1024 * 1024 },
|
||||
)
|
||||
.split("\0")
|
||||
.filter(Boolean);
|
||||
|
||||
return files.flatMap((file) => {
|
||||
if (file === "scripts/validate-retired-anthropic-models.mjs") return [];
|
||||
|
||||
const absolutePath = `${cwd}/${file}`;
|
||||
if (!existsSync(absolutePath)) return [];
|
||||
const stats = statSync(absolutePath);
|
||||
if (!stats.isFile() || stats.size > 2 * 1024 * 1024) return [];
|
||||
|
||||
const buffer = readFileSync(absolutePath);
|
||||
if (buffer.includes(0)) return [];
|
||||
return findRetiredAnthropicModels(file, buffer.toString("utf8"));
|
||||
});
|
||||
}
|
||||
|
||||
function main() {
|
||||
const violations = scanRepository();
|
||||
if (violations.length === 0) {
|
||||
console.log("No retired Anthropic model identifiers found.");
|
||||
return;
|
||||
}
|
||||
|
||||
console.error("Retired Anthropic model identifiers found:\n");
|
||||
for (const violation of violations) {
|
||||
console.error(` ${violation.file}:${violation.line} ${violation.model}`);
|
||||
}
|
||||
console.error(
|
||||
"\nReplace these identifiers with an active model from Anthropic's model deprecation documentation.",
|
||||
);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
|
||||
if (
|
||||
process.argv[1] &&
|
||||
import.meta.url === pathToFileURL(process.argv[1]).href
|
||||
) {
|
||||
main();
|
||||
}
|
||||
Reference in New Issue
Block a user