This commit is contained in:
fengchuanhn@gmail.com
2026-05-18 08:52:36 +08:00
parent caaa898d90
commit c559cb1581
5 changed files with 124 additions and 28 deletions

View File

@@ -54,7 +54,7 @@ function buildModelConfig() {
return "";
};
let apiUrl = pick("BAILIAN_BASE_URL", "BAILIAN_BASE_URL");
let apiUrl = pick("LLM_BASE_URL", "LLM_BASE_URL");
if (apiUrl) {
apiUrl = apiUrl.replace(/\/$/, "");
if (!apiUrl.includes("/chat/completions")) {
@@ -63,7 +63,7 @@ function buildModelConfig() {
: `${apiUrl}/v1/chat/completions`;
}
}
let apiModelId = pick("LLM_MODEL_ID", "LLM_MODEL_ID") || "doubao-seed-2-0-mini-260215";
const asrMode = pick("ASR_MODE", "asr_mode") || "online";
let ossConfig = parseJson(pick("OSS_CONFIG"));
@@ -90,9 +90,10 @@ function buildModelConfig() {
providerId: pick("LLM_PROVIDER_ID", "PROVIDER_ID") || "bailian",
modelId: pick("LLM_MODEL_ID", "MODEL_ID"),
apiUrl,
apiKey: pick("BAILIAN_API_KEY", "API_KEY"),
apiKey: pick("LLM_API_KEY", "API_KEY") || "",
type: pick("LLM_TYPE", "TYPE") || "bailian",
asrMode,
apiModelId,
aliyunApiKey: pick("BAILIAN_API_KEY", "BAILIAN_API_KEY"),
ossConfig: ossConfig || undefined,
asrServerInfo: asrServerInfo || undefined,

View File

@@ -49,22 +49,26 @@ async function rewriteWithAI(content, modelConfig, rewriteConfig, customPrompt)
rewriteConfig || {},
customPrompt
);
log("info", "pipeline.rewritePrompt", { prompt: prompt });
log("info", "pipeline.apiUrl", { apiUrl: modelConfig.apiUrl });
log("info", "pipeline.apiKey", { apiKey: modelConfig.apiKey });
log("info", "pipeline.apiModelId", { apiModelId: modelConfig.apiModelId });
const result = await pipelineNative.openaiChat({
apiUrl: modelConfig.apiUrl,
apiKey: modelConfig.apiKey,
model: modelConfig.modelId,
model: modelConfig.apiModelId,
messages: [
{ role: "system", content: "你是专业的短视频文案改写助手。" },
{ role: "system", content: "你是专业的短视频文案改写助手。请把用户提供的文案改写,要求保持原文的风格和主题,但使用不同的表达方式。" },
{ role: "user", content: prompt },
],
temperature:
typeof modelConfig.temperature === "number" ? modelConfig.temperature : 0.8,
max_tokens: modelConfig.maxTokens || 2000,
temperature: 0.8,
max_tokens: 2e3,
});
if (!result?.success) {
log("error", "rewrite.failed", { error: result?.error });
return null;
}
log("info", "pipeline.rewriteOk", { result: result.content });
return result.content;
}

View File

@@ -157,6 +157,16 @@ function signOss(secret, str) {
return crypto.createHmac("sha1", secret).update(str).digest("base64");
}
/** 私有桶ASR 需可公网 GET 的签名 URL对齐 Electron generateSignedUrl */
function ossSignedGetUrl(cfg, objectKey, expiresSec = 3600) {
const expireTime = Math.floor(Date.now() / 1000) + expiresSec;
const resource = `/${cfg.bucket}/${objectKey}`;
const stringToSign = `GET\n\n\n${expireTime}\n${resource}`;
const signature = encodeURIComponent(signOss(cfg.accessKeySecret, stringToSign));
const host = `${cfg.bucket}.${cfg.region}.aliyuncs.com`;
return `https://${host}/${objectKey}?OSSAccessKeyId=${encodeURIComponent(cfg.accessKeyId)}&Expires=${expireTime}&Signature=${signature}`;
}
async function aliyunOssUpload(localPath, cfg) {
const ak = cfg.accessKeyId;
const sk = cfg.accessKeySecret;
@@ -196,7 +206,12 @@ async function aliyunOssUpload(localPath, cfg) {
res.on("data", (c) => chunks.push(c));
res.on("end", () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve({ success: true, url });
const signedUrl = ossSignedGetUrl(
{ accessKeyId: ak, accessKeySecret: sk, bucket, region },
objectKey,
3600,
);
resolve({ success: true, url: signedUrl });
} else {
resolve({
success: false,
@@ -211,6 +226,39 @@ async function aliyunOssUpload(localPath, cfg) {
});
}
/** filetrans: output.result批量模型: output.results[0] */
function extractTranscriptionUrl(output) {
if (!output || typeof output !== "object") return null;
if (output.result?.transcription_url) return output.result.transcription_url;
const results = output.results;
if (Array.isArray(results) && results[0]?.transcription_url) {
return results[0].transcription_url;
}
return null;
}
function parseTranscriptionText(trans) {
let fullText = "";
if (trans?.transcripts?.length) {
for (const t of trans.transcripts) {
if (t.sentences?.length) {
for (const s of t.sentences) {
if (s.text) {
if (fullText) fullText += "\n";
fullText += s.text;
}
}
} else if (t.text) {
if (fullText) fullText += "\n";
fullText += t.text;
}
}
} else if (trans?.text) {
fullText = trans.text;
}
return fullText;
}
async function fetchJson(url, options = {}) {
const res = await fetch(url, options);
const text = await res.text();
@@ -235,6 +283,14 @@ async function aliyunAsrFiletrans(audioUrl, opts) {
if (!apiKey) {
return { success: false, error: "缺少 DashScope apiKey" };
}
const fileUrl = String(audioUrl || "").trim();
if (!/^https?:\/\//i.test(fileUrl)) {
return { success: false, error: "ASR 需要可访问的 http(s) 音频 URL请检查 OSS 上传与签名)" };
}
const input =
model === "qwen3-asr-flash-filetrans"
? { file_url: fileUrl }
: { file_urls: [fileUrl] };
const submit = await fetchJson(
"https://dashscope.aliyuncs.com/api/v1/services/audio/asr/transcription",
{
@@ -246,13 +302,11 @@ async function aliyunAsrFiletrans(audioUrl, opts) {
},
body: JSON.stringify({
model,
input: { file_urls: [audioUrl] },
input,
parameters: {
channel_id: [0],
// 音频通道ID
enable_itn: false
// 是否启用逆文本归一化
}
enable_itn: false,
},
}),
}
);
@@ -270,24 +324,27 @@ async function aliyunAsrFiletrans(audioUrl, opts) {
const status = poll?.output?.task_status;
log("info", "voice recognition status",status);
if (status === "SUCCEEDED") {
const transUrl = poll?.output?.results?.[0]?.transcription_url;
const transUrl = extractTranscriptionUrl(poll?.output);
if (!transUrl) {
log("error", "voice recognition transUrl",transUrl);
return { success: false, error: "ASR 成功但无 transcription_url" };
log("error", "voice recognition output", poll?.output);
return {
success: false,
error: "ASR 成功但无 transcription_url期望 output.result 或 output.results[0]",
};
}
const trans = await fetchJson(transUrl);
let fullText = "";
if (trans.transcripts && trans.transcripts.length) {
fullText = trans.transcripts.map((t) => t.text || "").filter(Boolean).join("\n");
} else if (trans.text) {
fullText = trans.text;
}
const fullText = parseTranscriptionText(trans);
return { success: true, text: fullText };
}
if (status === "FAILED" || status === "UNKNOWN") {
const detail =
poll?.output?.message ||
poll?.output?.code ||
poll?.message ||
(poll?.output ? JSON.stringify(poll.output).slice(0, 300) : null);
return {
success: false,
error: poll?.output?.message || "ASR 任务失败",
error: detail || "ASR 任务失败",
};
}
}

View File

@@ -0,0 +1,32 @@
"use strict";
/**
* 仅调用 OpenAI 兼容 Chat API 生成文案(撰写文案按钮)
* 由 `/api/v1/nodejs-scripts?name=script_generate.js` 下发
*/
const pipelineNative = require("./pipeline_native.js");
const desktopConfig = require("./desktop_config.js");
globalThis.__nodejsMain = async function (params) {
const p = params || {};
const modelConfig = desktopConfig.buildModelConfig();
const check = desktopConfig.validateModelConfig(modelConfig);
if (!check.ok) {
return { success: false, error: check.error };
}
const messages = p.messages;
if (!Array.isArray(messages) || messages.length === 0) {
return { success: false, error: "缺少 messages" };
}
return pipelineNative.openaiChat({
apiUrl: modelConfig.apiUrl,
apiKey: modelConfig.apiKey,
model: modelConfig.apiModelId,
messages,
temperature: typeof p.temperature === "number" ? p.temperature : 0.8,
max_tokens: p.max_tokens || p.maxTokens || 2000,
});
};

View File

@@ -56,10 +56,12 @@ ${content}
}
static buildRewritePrompt(content, config = {}, customPrompt) {
if (customPrompt && String(customPrompt).trim()) {
return this.replacePromptVariables(customPrompt, content);
}
return this.buildDefaultPrompt(content, config);
// if (customPrompt && String(customPrompt).trim()) {
// return this.replacePromptVariables(customPrompt, content);
// }
// return this.buildDefaultPrompt(content, config);
return content;
}
}