131 lines
3.7 KiB
JavaScript
131 lines
3.7 KiB
JavaScript
"use strict";
|
||
|
||
/**
|
||
* 从进程环境变量读取 desktop_configs(Rust 注入 AICLIENT_CFG_*)。
|
||
* 库表 name 经规范化后作为 env 后缀,如 name=LLM_API_KEY → AICLIENT_CFG_LLM_API_KEY
|
||
*/
|
||
|
||
const ENV_PREFIX = "AICLIENT_CFG_";
|
||
|
||
function normalizeKey(name) {
|
||
return String(name || "")
|
||
.trim()
|
||
.replace(/[^a-zA-Z0-9]+/g, "_")
|
||
.replace(/^_+|_+$/g, "")
|
||
.toUpperCase();
|
||
}
|
||
|
||
/** @returns {Record<string, string>} 逻辑键(大写+下划线)→ 值 */
|
||
function readAllFromEnv() {
|
||
const out = {};
|
||
for (const [ek, ev] of Object.entries(process.env)) {
|
||
if (!ek.startsWith(ENV_PREFIX) || ev == null || ev === "") continue;
|
||
const logical = ek.slice(ENV_PREFIX.length);
|
||
out[logical] = ev;
|
||
}
|
||
return out;
|
||
}
|
||
|
||
function get(logicalKey, fallback = "") {
|
||
const k = normalizeKey(logicalKey);
|
||
return process.env[ENV_PREFIX + k] || fallback;
|
||
}
|
||
|
||
function parseJson(val, fallback = null) {
|
||
if (!val) return fallback;
|
||
try {
|
||
return JSON.parse(val);
|
||
} catch {
|
||
return fallback;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 将扁平配置组装为流水线使用的 modelConfig(兼容现有脚本字段名)。
|
||
* 可在库表增加任意键;复杂对象用 JSON 字符串存于 OSS_CONFIG、ASR_SERVER_INFO 等。
|
||
*/
|
||
function buildModelConfig() {
|
||
const c = readAllFromEnv();
|
||
const pick = (...keys) => {
|
||
for (const k of keys) {
|
||
const v = c[normalizeKey(k)];
|
||
if (v) return v;
|
||
}
|
||
return "";
|
||
};
|
||
|
||
let apiUrl = pick("LLM_BASE_URL", "LLM_BASE_URL");
|
||
if (apiUrl) {
|
||
apiUrl = apiUrl.replace(/\/$/, "");
|
||
if (!apiUrl.includes("/chat/completions")) {
|
||
apiUrl = apiUrl.endsWith("/v1")
|
||
? `${apiUrl}/chat/completions`
|
||
: `${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"));
|
||
if (!ossConfig && pick("OSS_ACCESS_KEY_ID")) {
|
||
ossConfig = {
|
||
accessKeyId: pick("OSS_ACCESS_KEY_ID"),
|
||
accessKeySecret: pick("OSS_ACCESS_KEY_SECRET"),
|
||
bucket: pick("OSS_BUCKET"),
|
||
region: pick("OSS_REGION"),
|
||
endpoint: pick("OSS_ENDPOINT") || undefined,
|
||
};
|
||
}
|
||
|
||
let asrServerInfo = parseJson(pick("ASR_SERVER_INFO"));
|
||
if (!asrServerInfo && pick("ASR_SERVER_URL")) {
|
||
asrServerInfo = {
|
||
name: pick("ASR_SERVER_NAME", "local") || "local",
|
||
baseUrl: pick("ASR_SERVER_URL").replace(/\/$/, ""),
|
||
asrPath: pick("ASR_SERVER_ASR_PATH", "/asr") || "/asr",
|
||
};
|
||
}
|
||
|
||
return {
|
||
providerId: pick("LLM_PROVIDER_ID", "PROVIDER_ID") || "bailian",
|
||
modelId: pick("LLM_MODEL_ID", "MODEL_ID"),
|
||
apiUrl,
|
||
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,
|
||
};
|
||
}
|
||
|
||
function validateModelConfig(modelConfig) {
|
||
if (!modelConfig.apiKey) {
|
||
return { ok: false, error: "缺少配置 LLM_API_KEY(desktop_configs 表)" };
|
||
}
|
||
if (!modelConfig.apiUrl) {
|
||
return { ok: false, error: "缺少配置 LLM_API_URL" };
|
||
}
|
||
if (modelConfig.asrMode === "online") {
|
||
if (!modelConfig.aliyunApiKey) {
|
||
return { ok: false, error: "在线 ASR 需配置 DASHSCOPE_API_KEY" };
|
||
}
|
||
if (!modelConfig.ossConfig?.bucket) {
|
||
return { ok: false, error: "在线 ASR 需配置 OSS(或 OSS_CONFIG JSON)" };
|
||
}
|
||
}
|
||
if (modelConfig.asrMode === "local" && !modelConfig.asrServerInfo) {
|
||
return { ok: false, error: "本地 ASR 需配置 ASR_SERVER_URL 或 ASR_SERVER_INFO" };
|
||
}
|
||
return { ok: true };
|
||
}
|
||
|
||
module.exports = {
|
||
ENV_PREFIX,
|
||
readAllFromEnv,
|
||
get,
|
||
buildModelConfig,
|
||
validateModelConfig,
|
||
};
|