feat: add AI model health ban exclusion
This commit is contained in:
parent
15a08e6d65
commit
c7966298dc
@ -869,6 +869,7 @@ type AiModel = {
|
||||
fallbackModelId1?: string;
|
||||
fallbackModelId2?: string;
|
||||
excludeFromFailover?: boolean;
|
||||
excludeFromHealthBan?: boolean;
|
||||
healthStatus?: "healthy" | "degraded" | "auto_disabled";
|
||||
healthConsecutiveFailures?: number;
|
||||
healthLastEventType?: string | null;
|
||||
@ -3608,6 +3609,7 @@ export function AdminSettingsClient() {
|
||||
fallbackModelId1: "", // 备用模型 ID 1
|
||||
fallbackModelId2: "", // 备用模型 ID 2
|
||||
excludeFromFailover: false,
|
||||
excludeFromHealthBan: false,
|
||||
capabilities: {} as Record<string, unknown>,
|
||||
// 🆕 图片生成模型特性配置
|
||||
imageGenConfig: createDefaultImageGenConfig(),
|
||||
@ -6892,6 +6894,7 @@ export function AdminSettingsClient() {
|
||||
fallbackModelId1: "",
|
||||
fallbackModelId2: "",
|
||||
excludeFromFailover: false,
|
||||
excludeFromHealthBan: false,
|
||||
capabilities: {},
|
||||
imageGenConfig: createDefaultImageGenConfig(),
|
||||
});
|
||||
@ -14978,19 +14981,44 @@ export function AdminSettingsClient() {
|
||||
多模态模型(Agent 识图与分析使用同一模型)
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
checked={modelForm.excludeFromFailover || false}
|
||||
onCheckedChange={(checked) =>
|
||||
setModelForm({
|
||||
...modelForm,
|
||||
excludeFromFailover: checked,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Label>
|
||||
禁止参与故障迁移(不作为其他模型的自动备用模型)
|
||||
</Label>
|
||||
<div className="space-y-3 rounded-md border p-3">
|
||||
<div className="text-sm font-medium">
|
||||
故障保护策略
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
checked={modelForm.excludeFromFailover || false}
|
||||
onCheckedChange={(checked) =>
|
||||
setModelForm({
|
||||
...modelForm,
|
||||
excludeFromFailover: checked,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Label>
|
||||
不参与故障迁移候选池
|
||||
</Label>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
开启后,该模型不会作为其他模型请求失败时的自动备用模型。
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
checked={modelForm.excludeFromHealthBan || false}
|
||||
onCheckedChange={(checked) =>
|
||||
setModelForm({
|
||||
...modelForm,
|
||||
excludeFromHealthBan: checked,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Label>
|
||||
不参与故障封禁池
|
||||
</Label>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
开启后,健康检查仍会记录故障日志,但不会因为连续失败自动禁用该模型。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 🆕 图片生成模型特性配置 */}
|
||||
@ -15911,7 +15939,12 @@ export function AdminSettingsClient() {
|
||||
)}
|
||||
{(model as any).excludeFromFailover && (
|
||||
<span className="text-xs bg-rose-100 text-rose-700 px-2 py-0.5 rounded">
|
||||
不参与故障迁移
|
||||
不参与迁移池
|
||||
</span>
|
||||
)}
|
||||
{(model as any).excludeFromHealthBan && (
|
||||
<span className="text-xs bg-stone-100 text-stone-700 px-2 py-0.5 rounded">
|
||||
不参与封禁池
|
||||
</span>
|
||||
)}
|
||||
{healthStatus === "auto_disabled" && (
|
||||
@ -16033,6 +16066,8 @@ export function AdminSettingsClient() {
|
||||
(model as any).fallbackModelId2 || "",
|
||||
excludeFromFailover:
|
||||
(model as any).excludeFromFailover || false,
|
||||
excludeFromHealthBan:
|
||||
(model as any).excludeFromHealthBan || false,
|
||||
capabilities:
|
||||
(model as any).capabilities &&
|
||||
typeof (model as any).capabilities === "object" &&
|
||||
|
||||
@ -82,6 +82,7 @@ const createModelSchema = z.object({
|
||||
fallbackModelId1: z.string().max(128).optional(),
|
||||
fallbackModelId2: z.string().max(128).optional(),
|
||||
excludeFromFailover: z.boolean().optional(),
|
||||
excludeFromHealthBan: z.boolean().optional(),
|
||||
isAgentModel: z.boolean().optional(),
|
||||
agentCodingModelId: z.string().max(128).optional(),
|
||||
isMultimodal: z.boolean().optional(),
|
||||
@ -106,6 +107,7 @@ const updateModelSchema = z.object({
|
||||
fallbackModelId1: z.string().max(128).optional(),
|
||||
fallbackModelId2: z.string().max(128).optional(),
|
||||
excludeFromFailover: z.boolean().optional(),
|
||||
excludeFromHealthBan: z.boolean().optional(),
|
||||
isAgentModel: z.boolean().optional(),
|
||||
agentCodingModelId: z.string().max(128).optional(),
|
||||
isMultimodal: z.boolean().optional(),
|
||||
|
||||
@ -211,6 +211,7 @@ export async function reviveAiModelsFromCooldown(params?: { modelId?: string })
|
||||
const conditions = [
|
||||
eq(aiModel.isEnabled, false),
|
||||
eq(aiModel.excludeFromFailover, false),
|
||||
eq(aiModel.excludeFromHealthBan, false),
|
||||
];
|
||||
|
||||
if (params?.modelId) {
|
||||
@ -392,6 +393,7 @@ export async function recordAiModelHealthFailure(params: {
|
||||
displayName: aiModel.displayName,
|
||||
isEnabled: aiModel.isEnabled,
|
||||
excludeFromFailover: aiModel.excludeFromFailover,
|
||||
excludeFromHealthBan: aiModel.excludeFromHealthBan,
|
||||
healthStatus: aiModel.healthStatus,
|
||||
healthConsecutiveFailures: aiModel.healthConsecutiveFailures,
|
||||
healthRecoverySuccessStreak: aiModel.healthRecoverySuccessStreak,
|
||||
@ -414,6 +416,7 @@ export async function recordAiModelHealthFailure(params: {
|
||||
const shouldAutoDisable =
|
||||
classification.counted &&
|
||||
!existing.excludeFromFailover &&
|
||||
!existing.excludeFromHealthBan &&
|
||||
((isObservation && existing.isEnabled) ||
|
||||
(existing.healthStatus !== "auto_disabled" &&
|
||||
nextConsecutiveFailures >= AUTO_DISABLE_FAILURE_THRESHOLD));
|
||||
|
||||
@ -106,6 +106,10 @@ const runMigrate = async () => {
|
||||
ALTER TABLE "AiModel"
|
||||
ADD COLUMN IF NOT EXISTS "excludeFromFailover" boolean NOT NULL DEFAULT false;
|
||||
`);
|
||||
await db.execute(sql`
|
||||
ALTER TABLE "AiModel"
|
||||
ADD COLUMN IF NOT EXISTS "excludeFromHealthBan" boolean NOT NULL DEFAULT false;
|
||||
`);
|
||||
await db.execute(sql`
|
||||
ALTER TABLE "AiModel"
|
||||
ADD COLUMN IF NOT EXISTS "isDefaultTextEditModel" boolean NOT NULL DEFAULT false;
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
ALTER TABLE "AiModel"
|
||||
ADD COLUMN IF NOT EXISTS "excludeFromHealthBan" boolean NOT NULL DEFAULT false;
|
||||
@ -484,6 +484,13 @@
|
||||
"when": 1779267679691,
|
||||
"tag": "0082_add_client_performance_events",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 69,
|
||||
"version": "7",
|
||||
"when": 1779849000000,
|
||||
"tag": "0083_add_ai_model_health_ban_exclusion",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -4068,6 +4068,7 @@ export async function createAiModel({
|
||||
fallbackModelId1,
|
||||
fallbackModelId2,
|
||||
excludeFromFailover,
|
||||
excludeFromHealthBan,
|
||||
imageGenConfig,
|
||||
capabilities,
|
||||
isAgentModel,
|
||||
@ -4089,6 +4090,7 @@ export async function createAiModel({
|
||||
fallbackModelId1?: string;
|
||||
fallbackModelId2?: string;
|
||||
excludeFromFailover?: boolean;
|
||||
excludeFromHealthBan?: boolean;
|
||||
imageGenConfig?: ImageGenConfig;
|
||||
capabilities?: Record<string, unknown> | null;
|
||||
isAgentModel?: boolean;
|
||||
@ -4139,6 +4141,7 @@ export async function createAiModel({
|
||||
fallbackModelId1: fallbackModelId1 || null,
|
||||
fallbackModelId2: fallbackModelId2 || null,
|
||||
excludeFromFailover: excludeFromFailover ?? false,
|
||||
excludeFromHealthBan: excludeFromHealthBan ?? false,
|
||||
imageGenConfig: imageGenConfig || null,
|
||||
defaultParams,
|
||||
capabilities:
|
||||
@ -4172,6 +4175,7 @@ export async function updateAiModel({
|
||||
fallbackModelId1,
|
||||
fallbackModelId2,
|
||||
excludeFromFailover,
|
||||
excludeFromHealthBan,
|
||||
imageGenConfig,
|
||||
capabilities,
|
||||
isAgentModel,
|
||||
@ -4194,6 +4198,7 @@ export async function updateAiModel({
|
||||
fallbackModelId1?: string;
|
||||
fallbackModelId2?: string;
|
||||
excludeFromFailover?: boolean;
|
||||
excludeFromHealthBan?: boolean;
|
||||
imageGenConfig?: ImageGenConfig;
|
||||
capabilities?: Record<string, unknown> | null;
|
||||
isAgentModel?: boolean;
|
||||
@ -4261,6 +4266,21 @@ export async function updateAiModel({
|
||||
if (fallbackModelId1 !== undefined) updateData.fallbackModelId1 = fallbackModelId1 || null;
|
||||
if (fallbackModelId2 !== undefined) updateData.fallbackModelId2 = fallbackModelId2 || null;
|
||||
if (excludeFromFailover !== undefined) updateData.excludeFromFailover = excludeFromFailover;
|
||||
if (excludeFromHealthBan !== undefined) {
|
||||
updateData.excludeFromHealthBan = excludeFromHealthBan;
|
||||
if (excludeFromHealthBan) {
|
||||
updateData.healthStatus = "healthy";
|
||||
updateData.healthConsecutiveFailures = 0;
|
||||
updateData.healthLastEventType = null;
|
||||
updateData.healthLastError = null;
|
||||
updateData.healthLastFailureAt = null;
|
||||
updateData.healthAutoDisabledAt = null;
|
||||
updateData.healthAutoDisabledReason = null;
|
||||
updateData.healthRecoverySuccessStreak = 0;
|
||||
updateData.healthObservationStartedAt = null;
|
||||
updateData.healthUpdatedAt = new Date();
|
||||
}
|
||||
}
|
||||
// 🆕 图片生成配置
|
||||
if (imageGenConfig !== undefined) updateData.imageGenConfig = imageGenConfig || null;
|
||||
if (
|
||||
|
||||
@ -335,6 +335,7 @@ export const aiModel = pgTable("AiModel", {
|
||||
fallbackModelId1: varchar("fallbackModelId1", { length: 128 }), // 备用模型 ID 1
|
||||
fallbackModelId2: varchar("fallbackModelId2", { length: 128 }), // 备用模型 ID 2
|
||||
excludeFromFailover: boolean("excludeFromFailover").notNull().default(false), // 不参与故障迁移
|
||||
excludeFromHealthBan: boolean("excludeFromHealthBan").notNull().default(false), // 不参与故障自动封禁池
|
||||
healthStatus: varchar("healthStatus", {
|
||||
enum: ["healthy", "degraded", "auto_disabled"],
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user