92 lines
3.8 KiB
JavaScript
92 lines
3.8 KiB
JavaScript
// Run on the application host: APP_ROOT=/var/www/ai-chatbot node SCRIPT [--apply] [--music-enabled]
|
||
// Only model rows are backed up; credentials never leave the existing provider.
|
||
const fs = require("node:fs");
|
||
const root = process.env.APP_ROOT || process.cwd();
|
||
require(root + "/node_modules/dotenv").config({ path: root + "/.env.local", quiet: true });
|
||
const sql = require(root + "/node_modules/postgres")(process.env.POSTGRES_URL, { max: 1 });
|
||
const providerId = "7bce5924-bfd2-4e31-9f1d-825bd9d9f8b1";
|
||
const apply = process.argv.includes("--apply"),
|
||
musicEnabled = process.argv.includes("--music-enabled");
|
||
const models = [
|
||
{
|
||
modelId: "qwen3-tts-instruct-flash",
|
||
displayName: "千问3 TTS 指令版(阿里云官方)",
|
||
kind: "tts",
|
||
enabled: true,
|
||
defaults: { voice: "Cherry", language: "Chinese", format: "wav" },
|
||
},
|
||
{
|
||
modelId: "qwen3-tts-flash",
|
||
displayName: "千问3 TTS(阿里云官方)",
|
||
kind: "tts",
|
||
enabled: true,
|
||
defaults: { voice: "Cherry", language: "Chinese", format: "wav" },
|
||
},
|
||
{
|
||
modelId: "fun-music-v1",
|
||
displayName: "Fun-Music(阿里云官方·需邀测)",
|
||
kind: "music",
|
||
enabled: musicEnabled,
|
||
defaults: { instrumental: true, format: "mp3" },
|
||
},
|
||
];
|
||
(async () => {
|
||
const [provider] =
|
||
await sql`select id,type,"baseUrl","isEnabled" from "AiProvider" where id=${providerId}`;
|
||
if (
|
||
!provider ||
|
||
provider.type !== "alibaba" ||
|
||
!provider.isEnabled ||
|
||
new URL(provider.baseUrl).origin !== "https://dashscope.aliyuncs.com"
|
||
)
|
||
throw Error("Expected enabled official Alibaba Beijing provider");
|
||
await sql.begin(async (tx) => {
|
||
await tx`select id from "AiProvider" where id=${providerId} for update`;
|
||
const before =
|
||
await tx`select * from "AiModel" where "providerId"=${providerId} and "modelId" in ${tx(models.map((m) => m.modelId))}`;
|
||
if (apply) {
|
||
const dir = root + "/.model-config-backups";
|
||
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
||
fs.writeFileSync(
|
||
dir + "/canvas-audio-" + Date.now() + ".json",
|
||
JSON.stringify({ providerId, models: before }, null, 2),
|
||
{ mode: 0o600 },
|
||
);
|
||
}
|
||
for (const model of models) {
|
||
const existing = before.filter((row) => row.modelId === model.modelId);
|
||
if (existing.length > 1) throw Error("Duplicate model rows: " + model.modelId);
|
||
const row = existing[0];
|
||
// Preserve an already enabled music row on subsequent idempotent runs.
|
||
const enabled = model.enabled || !!row?.isEnabled;
|
||
const capabilities = {
|
||
...(row?.capabilities || {}),
|
||
kind: "audio",
|
||
audioType: model.kind,
|
||
official: true,
|
||
activationRequired: model.kind === "music" && !enabled,
|
||
};
|
||
const defaults = { ...(row?.defaultParams || {}), ...model.defaults };
|
||
if (apply) {
|
||
if (row)
|
||
await tx`update "AiModel" set "displayName"=${model.displayName},"isEnabled"=${enabled},"isHidden"=true,capabilities=${tx.json(capabilities)},"defaultParams"=${tx.json(defaults)},"updatedAt"=now() where id=${row.id}`;
|
||
else
|
||
await tx`insert into "AiModel" ("providerId","modelId","displayName","isEnabled","isHidden",capabilities,"defaultParams","sortOrder") values (${providerId},${model.modelId},${model.displayName},${enabled},true,${tx.json(capabilities)},${tx.json(defaults)},${model.kind === "music" ? 902 : model.modelId.includes("instruct") ? 900 : 901})`;
|
||
}
|
||
console.log(
|
||
JSON.stringify({
|
||
mode: apply ? "applied" : "dry-run",
|
||
modelId: model.modelId,
|
||
enabled,
|
||
activationRequired: capabilities.activationRequired,
|
||
}),
|
||
);
|
||
}
|
||
});
|
||
})()
|
||
.catch((e) => {
|
||
console.error(e.message?.replace(/postgres:\/\/[^ ]+/g, "[redacted]") || e.name);
|
||
process.exitCode = 1;
|
||
})
|
||
.finally(() => sql.end());
|