feat(console): block reported model providers
This commit is contained in:
parent
849c2598ab
commit
40d3787d62
9 changed files with 3282 additions and 1 deletions
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE `workspace` ADD `blocked_model_providers` json;
|
||||
File diff suppressed because it is too large
Load diff
46
packages/console/core/script/model-access.ts
Normal file
46
packages/console/core/script/model-access.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { parseArgs } from "node:util"
|
||||
import { Database, eq } from "../src/drizzle/index.js"
|
||||
import { ModelAccess } from "../src/model-access.js"
|
||||
import { WorkspaceTable } from "../src/schema/workspace.sql.js"
|
||||
|
||||
const args = parseArgs({
|
||||
args: process.argv.slice(2),
|
||||
options: {
|
||||
workspace: { type: "string", short: "w" },
|
||||
provider: { type: "string", short: "p" },
|
||||
unblock: { type: "boolean", default: false },
|
||||
},
|
||||
})
|
||||
|
||||
if (!args.values.workspace || !args.values.provider) {
|
||||
console.error("Usage: model-access.ts --workspace <workspaceID> --provider <anthropic|openai> [--unblock]")
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const provider = ModelAccess.parseProvider(args.values.provider)
|
||||
const workspace = await Database.use((tx) =>
|
||||
tx
|
||||
.select({ block: WorkspaceTable.blocked_model_providers })
|
||||
.from(WorkspaceTable)
|
||||
.where(eq(WorkspaceTable.id, args.values.workspace!))
|
||||
.then((rows) => rows[0]),
|
||||
)
|
||||
|
||||
if (!workspace) {
|
||||
console.error(`Workspace ${args.values.workspace} not found`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const current = workspace.block ?? []
|
||||
const block = args.values.unblock
|
||||
? current.filter((item) => item !== provider)
|
||||
: Array.from(new Set([...current, provider]))
|
||||
|
||||
await Database.use((tx) =>
|
||||
tx
|
||||
.update(WorkspaceTable)
|
||||
.set({ blocked_model_providers: block })
|
||||
.where(eq(WorkspaceTable.id, args.values.workspace!)),
|
||||
)
|
||||
|
||||
console.log(`${args.values.unblock ? "Unblocked" : "Blocked"} ${provider} models for ${args.values.workspace}`)
|
||||
25
packages/console/core/src/model-access.ts
Normal file
25
packages/console/core/src/model-access.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
export namespace ModelAccess {
|
||||
export const providers = ["anthropic", "openai"] as const
|
||||
export type Provider = (typeof providers)[number]
|
||||
|
||||
export function parseProvider(value: string): Provider {
|
||||
if (providers.includes(value as Provider)) return value as Provider
|
||||
throw new Error(`Unsupported model provider: ${value}`)
|
||||
}
|
||||
|
||||
export function provider(model: string): Provider | undefined {
|
||||
if (model.startsWith("claude")) return "anthropic"
|
||||
if (model.startsWith("gpt")) return "openai"
|
||||
}
|
||||
|
||||
export function blocked(model: string, providers: Provider[] | null) {
|
||||
const value = provider(model)
|
||||
if (!value) return false
|
||||
return providers?.includes(value) ?? false
|
||||
}
|
||||
|
||||
export function label(provider: Provider) {
|
||||
if (provider === "anthropic") return { provider: "Anthropic", models: "Claude" }
|
||||
return { provider: "OpenAI", models: "GPT" }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { json, primaryKey, mysqlTable, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
|
||||
import { timestamps, ulid } from "../drizzle/types"
|
||||
import type { ModelAccess } from "../model-access"
|
||||
|
||||
export const WorkspaceTable = mysqlTable(
|
||||
"workspace",
|
||||
|
|
@ -8,6 +9,7 @@ export const WorkspaceTable = mysqlTable(
|
|||
slug: varchar("slug", { length: 255 }),
|
||||
name: varchar("name", { length: 255 }).notNull(),
|
||||
region: json("region").$type<("us" | "eu" | "sg" | "cn")[]>(),
|
||||
blocked_model_providers: json().$type<ModelAccess.Provider[]>(),
|
||||
...timestamps,
|
||||
},
|
||||
(table) => [uniqueIndex("slug").on(table.slug)],
|
||||
|
|
|
|||
21
packages/console/core/test/model-access.test.ts
Normal file
21
packages/console/core/test/model-access.test.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { describe, expect, test } from "bun:test"
|
||||
import { ModelAccess } from "../src/model-access"
|
||||
|
||||
describe("ModelAccess", () => {
|
||||
test("maps Claude and GPT model IDs to their reporting providers", () => {
|
||||
expect(ModelAccess.provider("claude-opus-4-1")).toBe("anthropic")
|
||||
expect(ModelAccess.provider("gpt-5.1-codex")).toBe("openai")
|
||||
expect(ModelAccess.provider("gemini-2.5-pro")).toBeUndefined()
|
||||
})
|
||||
|
||||
test("blocks only models from a blocked provider", () => {
|
||||
expect(ModelAccess.blocked("claude-sonnet-4", ["anthropic"])).toBe(true)
|
||||
expect(ModelAccess.blocked("gpt-5", ["anthropic"])).toBe(false)
|
||||
expect(ModelAccess.blocked("claude-sonnet-4", null)).toBe(false)
|
||||
})
|
||||
|
||||
test("provides names for the API error", () => {
|
||||
expect(ModelAccess.label("anthropic")).toEqual({ provider: "Anthropic", models: "Claude" })
|
||||
expect(ModelAccess.label("openai")).toEqual({ provider: "OpenAI", models: "GPT" })
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue