feat: initial release v0.3.0
This commit is contained in:
152
scripts/migrations/migrate-custom-pricing-v2.ts
Normal file
152
scripts/migrations/migrate-custom-pricing-v2.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
const APPLY = process.argv.includes('--apply')
|
||||
|
||||
type PreferenceRow = {
|
||||
id: string
|
||||
userId: string
|
||||
customModels: string | null
|
||||
}
|
||||
|
||||
type MigrationSummary = {
|
||||
mode: 'dry-run' | 'apply'
|
||||
scanned: number
|
||||
updatedRows: number
|
||||
migratedModels: number
|
||||
skippedInvalidRows: number
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return !!value && typeof value === 'object' && !Array.isArray(value)
|
||||
}
|
||||
|
||||
function parseCustomModels(raw: string | null): unknown[] | null {
|
||||
if (!raw) return []
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as unknown
|
||||
if (!Array.isArray(parsed)) return null
|
||||
return parsed
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function migrateLegacyCustomPricing(raw: unknown): {
|
||||
changed: boolean
|
||||
next: unknown
|
||||
} {
|
||||
if (!isRecord(raw)) {
|
||||
return { changed: false, next: raw }
|
||||
}
|
||||
|
||||
const hasLegacyInput = typeof raw.input === 'number' && Number.isFinite(raw.input) && raw.input >= 0
|
||||
const hasLegacyOutput = typeof raw.output === 'number' && Number.isFinite(raw.output) && raw.output >= 0
|
||||
if (!hasLegacyInput && !hasLegacyOutput) {
|
||||
return { changed: false, next: raw }
|
||||
}
|
||||
|
||||
const llmRaw = isRecord(raw.llm) ? raw.llm : {}
|
||||
const llmInput = typeof llmRaw.inputPerMillion === 'number' && Number.isFinite(llmRaw.inputPerMillion) && llmRaw.inputPerMillion >= 0
|
||||
? llmRaw.inputPerMillion
|
||||
: (hasLegacyInput ? raw.input as number : undefined)
|
||||
const llmOutput = typeof llmRaw.outputPerMillion === 'number' && Number.isFinite(llmRaw.outputPerMillion) && llmRaw.outputPerMillion >= 0
|
||||
? llmRaw.outputPerMillion
|
||||
: (hasLegacyOutput ? raw.output as number : undefined)
|
||||
|
||||
const nextPricing: Record<string, unknown> = {}
|
||||
for (const [key, value] of Object.entries(raw)) {
|
||||
if (key === 'input' || key === 'output') continue
|
||||
nextPricing[key] = value
|
||||
}
|
||||
|
||||
nextPricing.llm = {
|
||||
...(llmInput !== undefined ? { inputPerMillion: llmInput } : {}),
|
||||
...(llmOutput !== undefined ? { outputPerMillion: llmOutput } : {}),
|
||||
}
|
||||
|
||||
return {
|
||||
changed: true,
|
||||
next: nextPricing,
|
||||
}
|
||||
}
|
||||
|
||||
function migrateCustomModel(rawModel: unknown): { changed: boolean; next: unknown } {
|
||||
if (!isRecord(rawModel)) {
|
||||
return { changed: false, next: rawModel }
|
||||
}
|
||||
|
||||
const migratedPricing = migrateLegacyCustomPricing(rawModel.customPricing)
|
||||
if (!migratedPricing.changed) {
|
||||
return { changed: false, next: rawModel }
|
||||
}
|
||||
|
||||
return {
|
||||
changed: true,
|
||||
next: {
|
||||
...rawModel,
|
||||
customPricing: migratedPricing.next,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const summary: MigrationSummary = {
|
||||
mode: APPLY ? 'apply' : 'dry-run',
|
||||
scanned: 0,
|
||||
updatedRows: 0,
|
||||
migratedModels: 0,
|
||||
skippedInvalidRows: 0,
|
||||
}
|
||||
|
||||
const rows = await prisma.userPreference.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
userId: true,
|
||||
customModels: true,
|
||||
},
|
||||
}) as PreferenceRow[]
|
||||
|
||||
summary.scanned = rows.length
|
||||
|
||||
for (const row of rows) {
|
||||
const parsedModels = parseCustomModels(row.customModels)
|
||||
if (parsedModels === null) {
|
||||
summary.skippedInvalidRows += 1
|
||||
continue
|
||||
}
|
||||
|
||||
let rowChanged = false
|
||||
const nextModels = parsedModels.map((model) => {
|
||||
const migrated = migrateCustomModel(model)
|
||||
if (migrated.changed) {
|
||||
rowChanged = true
|
||||
summary.migratedModels += 1
|
||||
}
|
||||
return migrated.next
|
||||
})
|
||||
|
||||
if (!rowChanged) continue
|
||||
summary.updatedRows += 1
|
||||
|
||||
if (APPLY) {
|
||||
await prisma.userPreference.update({
|
||||
where: { id: row.id },
|
||||
data: {
|
||||
customModels: JSON.stringify(nextModels),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
console.log(JSON.stringify(summary, null, 2))
|
||||
}
|
||||
|
||||
main()
|
||||
.then(async () => {
|
||||
await prisma.$disconnect()
|
||||
})
|
||||
.catch(async (error: unknown) => {
|
||||
console.error('[migrate-custom-pricing-v2] failed', error)
|
||||
await prisma.$disconnect()
|
||||
process.exit(1)
|
||||
})
|
||||
Reference in New Issue
Block a user