elec_rag_ui/src/main/config.ts
surui 836d25cad2 refactor: 推理请求统一走 er_backend 代理(127.0.0.1:17700)
- 嵌入/生成服务地址不再由前端按 cardType 推导端口,改为经 er_backend
  的 /v1/* 代理转发,服务端口以后端配置为唯一真相来源
- config.ts: 新增 ER_BACKEND_URL 统一常量,删除端口推导逻辑
- generator/statusHandler/queryHandler/erBackendHandler 跟随改造
- fix: package-kylin-arm64-deb.sh 从 .app-version 读版本号时剥离 v 前缀,
  修复无 git 环境下 dpkg-deb 报"版本号没有以数字开头"
2026-08-16 21:16:01 +08:00

239 lines
8.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { app } from 'electron'
import { join } from 'path'
import { existsSync, readFileSync, writeFileSync } from 'fs'
// 推理请求统一走 er_backend 代理(管理面端口,/v1/* 由后端转发到其实际管理的 llama-server)
export const ER_BACKEND_URL = 'http://127.0.0.1:17700'
export interface AppConfig {
embed: {
provider: 'local' | 'llamacpp'
localModelPath: string
baseUrl: string // 由 applyPlatformOverrides 固定为 er_backend 代理地址,不对用户暴露
}
rag: {
chunkSize: number
chunkOverlap: number
topK: number
systemPrompt: string
}
// 服务的启动参数/端口由 er_backend 统一管理,以下字段仅为历史配置保留,前端不再用于路由
llamacppServer: {
executablePath: string
modelPath: string
port: number
numCtx: number
parallelSlots: number
threads: number
gpuLayers: number
noWarmup: boolean
extraArgs: string
}
llamacppEmbed: {
modelPath: string
port: number
extraArgs: string
}
hardware: {
cardType: 'er160' | 'er25'
}
batchQuestions: string[]
batchWaitSeconds: number
voice: {
asr: {
enabled: boolean
executablePath: string
modelDir: string
numThreads: number
vadSilenceTimeoutMs: number
vadEnergyThreshold: number
inputGain: number
punctuationModelDir: string
}
tts: {
enabled: boolean
voiceURI: string
pitch: number
rate: number
volume: number
}
}
}
export const DEFAULT_CONFIG: AppConfig = {
embed: {
provider: 'llamacpp',
localModelPath: '',
baseUrl: ER_BACKEND_URL
},
llamacppServer: {
executablePath: '',
modelPath: '',
port: 8081,
numCtx: 8192,
parallelSlots: 1,
threads: -1,
gpuLayers: 0,
noWarmup: true,
extraArgs: ''
},
llamacppEmbed: {
modelPath: '',
port: 17701,
extraArgs: '--embedding --pooling last -b 8096 -ub 8096 --parallel 10'
},
hardware: {
cardType: 'er160'
},
batchQuestions: [],
batchWaitSeconds: 10,
voice: {
asr: {
enabled: false,
executablePath: '',
modelDir: '',
numThreads: 2,
vadSilenceTimeoutMs: 5000,
vadEnergyThreshold: 0.005,
inputGain: 10,
punctuationModelDir: ''
},
tts: {
enabled: false,
voiceURI: '',
pitch: 1,
rate: 1,
volume: 1
}
},
rag: {
chunkSize: 480,
chunkOverlap: 0,
topK: 5,
systemPrompt: `你是一个阅读理解助手,专门帮助用户基于给定的上下文内容回答问题。
你的任务是:
- 只能依据用户提供的上下文内容进行回答;
- 每个答案中如果有信息来源于上下文,必须标注对应的引用标号(例如:[1],也可以是多个标号,例如:[1][3]);
- 如果问题的信息在上下文中找不到,请直接回复:"我不知道。"
- 不得使用自己的知识库或常识来回答问题;
- 回答应简洁明了,不添加额外解释或发挥。
请严格按照用户的指示进行判断和回答。`
}
}
function getConfigPath(): string {
return join(app.getPath('userData'), 'config.json')
}
export function getDbPath(): string {
return join(app.getPath('userData'), 'lancedb')
}
export function getConfig(): AppConfig {
const configPath = getConfigPath()
if (!existsSync(configPath)) {
const config = { ...DEFAULT_CONFIG }
applyPlatformOverrides(config)
return config
}
try {
const raw = JSON.parse(readFileSync(configPath, 'utf-8'))
const card: 'er160' | 'er25' = (raw.hardware?.cardType as 'er160' | 'er25') ?? 'er160'
// embed.provider 兼容旧 'ollama' 值 → llamacpp
let embedProvider = raw.embed?.provider ?? 'llamacpp'
if (embedProvider === 'ollama') embedProvider = 'llamacpp'
const config: AppConfig = {
embed: {
provider: embedProvider as 'local' | 'llamacpp',
localModelPath: raw.embed?.localModelPath ?? '',
baseUrl: raw.embed?.baseUrl ?? DEFAULT_CONFIG.embed.baseUrl
},
rag: { ...DEFAULT_CONFIG.rag, ...raw.rag },
llamacppServer: {
...DEFAULT_CONFIG.llamacppServer,
...raw.llamacppServer,
numCtx: (raw.llamacppServer?.numCtx ?? 0) <= 4096
? DEFAULT_CONFIG.llamacppServer.numCtx
: raw.llamacppServer.numCtx,
parallelSlots: raw.llamacppServer?.parallelSlots ?? DEFAULT_CONFIG.llamacppServer.parallelSlots,
noWarmup: raw.llamacppServer?.noWarmup ?? DEFAULT_CONFIG.llamacppServer.noWarmup
},
llamacppEmbed: {
...DEFAULT_CONFIG.llamacppEmbed,
...raw.llamacppEmbed
},
hardware: { cardType: card },
batchQuestions: Array.isArray(raw.batchQuestions)
? raw.batchQuestions.filter((q: unknown) => typeof q === 'string')
: [],
batchWaitSeconds: typeof raw.batchWaitSeconds === 'number'
? Math.min(30, Math.max(5, raw.batchWaitSeconds))
: DEFAULT_CONFIG.batchWaitSeconds,
voice: {
asr: {
enabled: raw.voice?.asr?.enabled === true,
executablePath: typeof raw.voice?.asr?.executablePath === 'string' ? raw.voice.asr.executablePath : '',
modelDir: typeof raw.voice?.asr?.modelDir === 'string' ? raw.voice.asr.modelDir : '',
numThreads: typeof raw.voice?.asr?.numThreads === 'number' ? raw.voice.asr.numThreads : DEFAULT_CONFIG.voice.asr.numThreads,
vadSilenceTimeoutMs: typeof raw.voice?.asr?.vadSilenceTimeoutMs === 'number'
? Math.max(1000, Math.min(30000, raw.voice.asr.vadSilenceTimeoutMs))
: DEFAULT_CONFIG.voice.asr.vadSilenceTimeoutMs,
vadEnergyThreshold: typeof raw.voice?.asr?.vadEnergyThreshold === 'number'
? Math.max(0.0001, Math.min(1, raw.voice.asr.vadEnergyThreshold))
: DEFAULT_CONFIG.voice.asr.vadEnergyThreshold,
inputGain: typeof raw.voice?.asr?.inputGain === 'number'
? Math.max(0.1, Math.min(100, raw.voice.asr.inputGain))
: DEFAULT_CONFIG.voice.asr.inputGain,
punctuationModelDir: typeof raw.voice?.asr?.punctuationModelDir === 'string'
? raw.voice.asr.punctuationModelDir
: DEFAULT_CONFIG.voice.asr.punctuationModelDir
},
tts: {
enabled: raw.voice?.tts?.enabled === true,
voiceURI: typeof raw.voice?.tts?.voiceURI === 'string' ? raw.voice.tts.voiceURI : '',
pitch: typeof raw.voice?.tts?.pitch === 'number' ? raw.voice.tts.pitch : DEFAULT_CONFIG.voice.tts.pitch,
rate: typeof raw.voice?.tts?.rate === 'number' ? raw.voice.tts.rate : DEFAULT_CONFIG.voice.tts.rate,
volume: typeof raw.voice?.tts?.volume === 'number' ? raw.voice.tts.volume : DEFAULT_CONFIG.voice.tts.volume
}
}
}
applyPlatformOverrides(config)
return config
} catch {
const config = { ...DEFAULT_CONFIG }
applyPlatformOverrides(config)
return config
}
}
// 按计算卡型号覆盖 embed provider 和默认启动参数
// 推理请求统一走 er_backend 代理(ER_BACKEND_URL),服务端口以后端配置为准,前端不再维护
function applyPlatformOverrides(config: AppConfig): void {
const card = config.hardware.cardType
if (card === 'er25') {
// ER25:嵌入固定本地 CPU,无嵌入服务进程
config.embed.provider = 'local'
if (!config.llamacppServer.extraArgs.trim()) {
config.llamacppServer.extraArgs = '--port 8080 --ctk bf16 --ctv bf16 --no-warmup -ub 128'
}
} else {
// ER160:嵌入默认 llamacpp NPU(local 保持不变)
if (config.embed.provider !== 'local') config.embed.provider = 'llamacpp'
if (!config.llamacppServer.extraArgs.trim()) {
config.llamacppServer.extraArgs = '--port 8081 --presence_penalty 1.5'
}
if (!config.llamacppEmbed.extraArgs.trim()) {
config.llamacppEmbed.extraArgs = DEFAULT_CONFIG.llamacppEmbed.extraArgs
}
}
// 嵌入/生成服务地址固定为 er_backend 代理
config.embed.baseUrl = ER_BACKEND_URL
}
export function saveConfig(config: AppConfig): void {
writeFileSync(getConfigPath(), JSON.stringify(config, null, 2), 'utf-8')
}