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 报"版本号没有以数字开头"
This commit is contained in:
surui 2026-08-16 21:16:01 +08:00
parent a319caeff6
commit 836d25cad2
6 changed files with 23 additions and 23 deletions

View File

@ -29,7 +29,7 @@ fi
# 的解析逻辑保持一致,确保三处版本号(deb 文件名、界面显示、控制文件)永远一致。
VERSION=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//') || true
if [ -z "$VERSION" ] && [ -f .app-version ]; then
VERSION=$(cat .app-version | tr -d '[:space:]')
VERSION=$(cat .app-version | tr -d '[:space:]' | sed 's/^v//')
fi
if [ -z "$VERSION" ]; then
VERSION=$(node -p "require('./package.json').version")

View File

@ -2,11 +2,14 @@ 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 自动推导,不对用户暴露
baseUrl: string // 由 applyPlatformOverrides 固定为 er_backend 代理地址,不对用户暴露
}
rag: {
chunkSize: number
@ -14,6 +17,7 @@ export interface AppConfig {
topK: number
systemPrompt: string
}
// 服务的启动参数/端口由 er_backend 统一管理,以下字段仅为历史配置保留,前端不再用于路由
llamacppServer: {
executablePath: string
modelPath: string
@ -60,7 +64,7 @@ export const DEFAULT_CONFIG: AppConfig = {
embed: {
provider: 'llamacpp',
localModelPath: '',
baseUrl: 'http://127.0.0.1:17701'
baseUrl: ER_BACKEND_URL
},
llamacppServer: {
executablePath: '',
@ -206,19 +210,18 @@ export function getConfig(): AppConfig {
}
// 按计算卡型号覆盖 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'
config.llamacppServer.port = 8080
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'
config.llamacppServer.port = 8081
if (!config.llamacppServer.extraArgs.trim()) {
config.llamacppServer.extraArgs = '--port 8081 --presence_penalty 1.5'
}
@ -226,8 +229,8 @@ function applyPlatformOverrides(config: AppConfig): void {
config.llamacppEmbed.extraArgs = DEFAULT_CONFIG.llamacppEmbed.extraArgs
}
}
// 嵌入服务地址跟随端口
config.embed.baseUrl = `http://127.0.0.1:${config.llamacppEmbed.port}`
// 嵌入/生成服务地址固定为 er_backend 代理
config.embed.baseUrl = ER_BACKEND_URL
}
export function saveConfig(config: AppConfig): void {

View File

@ -1,7 +1,6 @@
import { ipcMain } from 'electron'
import { execFile } from 'child_process'
const ER_BACKEND_URL = 'http://localhost:17700'
import { ER_BACKEND_URL } from '../config'
async function isRunning(): Promise<boolean> {
try {

View File

@ -16,7 +16,7 @@ export function registerQueryHandlers(getWin: () => BrowserWindow | null): void
console.log(`\n${'='.repeat(60)}`)
console.log('[query] 问题:', payload.question)
console.log('[query] topK:', payload.topK)
console.log('[query] 向量化方式:', config.embed.provider, '生成服务端口:', config.llamacppServer.port)
console.log('[query] 向量化方式:', config.embed.provider, '生成/嵌入服务:er_backend 代理(127.0.0.1:17700)')
const send = (channel: string, data: unknown): void => {
getWin()?.webContents.send(channel, data)
@ -50,7 +50,6 @@ export function registerQueryHandlers(getWin: () => BrowserWindow | null): void
payload.question,
chunks,
payload.systemPrompt,
config.llamacppServer,
{
onDelta: (delta) => send('query:delta', { delta }),
onDone: (stats) => send('query:done', { stats: stats ?? null }),

View File

@ -2,7 +2,7 @@ import { ipcMain, dialog } from 'electron'
import type { BrowserWindow } from 'electron'
import { existsSync, readFileSync, writeFileSync } from 'fs'
import { join } from 'path'
import { getConfig, saveConfig, getDbPath, AppConfig } from '../config'
import { getConfig, saveConfig, getDbPath, AppConfig, ER_BACKEND_URL } from '../config'
import { getDocCount } from '../db/lancedb'
import { embedOneLocal, resolveLocalModelPath } from '../rag/localEmbedder'
import { syncNpuPolling } from './npuHandler'
@ -100,11 +100,12 @@ export function registerStatusHandlers(getWin: () => BrowserWindow | null): void
}
})
ipcMain.handle('chat-test', async (_event, llamacppConfig: AppConfig['llamacppServer']): Promise<TestResult> => {
// 生成服务连接测试(经 er_backend 代理转发到 llama-server)
ipcMain.handle('chat-test', async (_event, _llamacppConfig: AppConfig['llamacppServer']): Promise<TestResult> => {
const start = Date.now()
const elapsed = (): string => `${Date.now() - start}ms`
try {
const res = await fetch(`http://127.0.0.1:${llamacppConfig.port}/v1/chat/completions`, {
const res = await fetch(`${ER_BACKEND_URL}/v1/chat/completions`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({

View File

@ -1,5 +1,5 @@
import type { SearchResult } from '../db/lancedb'
import type { AppConfig } from '../config'
import { ER_BACKEND_URL } from '../config'
export interface GeneratorStats {
tokensPerSecond: number
@ -14,12 +14,11 @@ export interface GeneratorCallbacks {
onError: (message: string) => void
}
// 生成服务固定使用 llamacpp(ER160/ER25 均通过 er_backend 管理的 llama-server)
// 生成服务经 er_backend 代理访问其管理的 llama-server(OpenAI 兼容接口,SSE 流式)
export async function streamGenerate(
question: string,
chunks: SearchResult[],
systemPrompt: string,
llamacppConfig: AppConfig['llamacppServer'],
callbacks: GeneratorCallbacks
): Promise<void> {
const context = chunks
@ -33,22 +32,21 @@ ${context}
请回答以下问题:${question}`
console.log('\n[generator] ---- 发送给 LLM 的完整消息 ----')
console.log(`[generator] 服务地址:http://127.0.0.1:${llamacppConfig.port}`)
console.log(`[generator] 服务地址:${ER_BACKEND_URL}(er_backend 代理)`)
console.log('[generator] system prompt:\n', systemPrompt)
console.log('[generator] user message:\n', userMessage)
console.log('[generator] ---- 消息结束 ----\n')
await streamGenerateLlamaCpp(userMessage, systemPrompt, llamacppConfig, callbacks)
await streamGenerateLlamaCpp(userMessage, systemPrompt, callbacks)
}
// llama.cpp server OpenAI 兼容接口(/v1/chat/completions),SSE 格式
async function streamGenerateLlamaCpp(
userMessage: string,
systemPrompt: string,
llamacppConfig: AppConfig['llamacppServer'],
callbacks: GeneratorCallbacks
): Promise<void> {
const baseUrl = `http://127.0.0.1:${llamacppConfig.port}`
const baseUrl = ER_BACKEND_URL
let fullResponse = ''
let thinkOpen = false
const requestSentTime = Date.now()
@ -71,12 +69,12 @@ async function streamGenerateLlamaCpp(
})
})
} catch (e) {
callbacks.onError(`无法连接 llama.cpp server(${baseUrl}):${String(e)}`)
callbacks.onError(`无法连接生成服务(${baseUrl},er_backend 代理):${String(e)}`)
return
}
if (!response.ok) {
callbacks.onError(`llama.cpp server 请求失败:${response.status}`)
callbacks.onError(`生成服务请求失败:${response.status}`)
return
}