gongxiangp2/lib/ai-model-execution-release.ts

29 lines
2.8 KiB
TypeScript

import { z } from 'zod'
import { AiModelError, assertAiModelAdmin, assertAiModelAdminForWrite } from './ai-model-access'
import { aiModelAudit, aiModelTransaction } from './ai-model-repository'
import { assertAiModelRecoveryTextSafe, getAiModelAdminState } from './ai-model-service'
import { AI_MODEL_VERIFICATION_COLUMNS, lockAiModelVerificationExecution, verificationExecutionId, type AiModelVerificationRow } from './ai-model-verification-state'
import { detailTransactionLock } from './detail-generator/repository'
const proofSchema = z.object({ requestId: z.string().uuid(), executionEnded: z.literal(true), billingFinalized: z.literal(true), reference: z.string().trim().min(3).max(160), note: z.string().trim().min(20).max(500), confirmed: z.literal(true) }).strict()
/** Records execution completion only. Original unknown rows and their same-model retry fence remain untouched. */
export async function releaseAiModelVerificationExecution(userId: string, providerId: string, verificationId: string, body: unknown) {
const parsed = proofSchema.safeParse(body)
if (!z.string().uuid().safeParse(verificationId).success || !parsed.success) throw new AiModelError('须提供原请求号及执行已结束、计费已结算的核查凭证')
await assertAiModelAdmin(userId)
await assertAiModelRecoveryTextSafe(providerId, `${parsed.data.reference}\n${parsed.data.note}`)
await aiModelTransaction(async db => {
await assertAiModelAdminForWrite(userId, db)
const rows = await db.$queryRawUnsafe<AiModelVerificationRow[]>(`SELECT ${AI_MODEL_VERIFICATION_COLUMNS} FROM ai_model_verifications WHERE id=$1 AND model_key IN (SELECT id FROM ai_model_catalog WHERE provider_id=$2) FOR UPDATE`, verificationId, providerId)
if (!rows[0]) throw new AiModelError('测试记录不存在', 404)
const execution = await lockAiModelVerificationExecution(db, rows[0])
if (execution.some(row => row.status !== 'unknown' || row.taskId || row.requestId !== parsed.data.requestId)) throw new AiModelError('仅可登记同一原请求的未知同步结果;不能替换请求或关闭异步任务', 409, 'execution_proof_conflict')
await detailTransactionLock(db, 'detail-generator-execution')
const executionGroupId = verificationExecutionId(rows[0])
const prior = await db.$queryRawUnsafe<Array<{ id: string }>>(`SELECT id FROM ai_model_audit WHERE action='model.verification-execution-ended' AND details->>'executionGroupId'=$1 AND details->>'originalRequestId'=$2`, executionGroupId, parsed.data.requestId)
if (prior[0]) return
await aiModelAudit(db, userId, 'model.verification-execution-ended', providerId, { ...parsed.data, originalRequestId: parsed.data.requestId, executionGroupId, verificationIds: execution.map(row => row.id), modelKey: rows[0].modelKey, resultStillUnknown: true, retryAllowed: false })
})
return getAiModelAdminState()
}