Skip to content

Commit 976ad27

Browse files
committed
perf: P3 性能优化(base64 分块编码 + AI token 缓存)
1) visionWorker/asrWorker 的 arrayBufferToBase64 改分块编码(0x8000 块 + fromCharCode.apply),消除逐字节字符串拼接的 O(n²),加速大帧 base64 编码;2) aiPluginProvider 的 supabaseClient 改静态 import(免每次动态 import 开销),access_token 缓存 60s(token 寿命约 1h,保守 TTL)避免每次 AI 调用都 getSession。
1 parent 39c4614 commit 976ad27

3 files changed

Lines changed: 24 additions & 11 deletions

File tree

‎client/src/lib/ai/aiPluginProvider.ts‎

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,25 @@
33
*
44
* @ai-context: 从 AIPluginLoader 拆出。Electron 环境返回 ElectronAIPlugin
55
* (走 IPC → 主进程,本地 Ollama 优先/云端降级),Web 环境返回
6-
* RemoteAIPlugin(直连 AI 网关)。每次获取 Electron 插件都会刷新
7-
* Supabase access_token(token 会过期,不可缓存注入结果)。
6+
* RemoteAIPlugin(直连 AI 网关)。Supabase access_token 缓存 60s(token 寿命约 1h,
7+
* 保守 TTL)避免每次 AI 调用都 getSession;supabaseClient 改静态 import 免动态开销。
88
* @ai-context: 模块级懒加载单例;测试通过 vi.mock 插件模块拦截。
99
*/
1010
import { RemoteAIPlugin } from './RemoteAIPlugin';
1111
import { ElectronAIPlugin } from './ElectronAIPlugin';
1212
import { isElectron } from '../utils/platform';
13+
import { supabase } from '../auth/supabaseClient';
1314
import type { AIPlugin } from './types';
1415

1516
let remotePlugin: RemoteAIPlugin | null = null;
1617
let electronPlugin: ElectronAIPlugin | null = null;
1718

19+
// token 缓存:Supabase access_token 约 1h 有效,缓存 60s 避免每次 AI 调用都 getSession。
20+
// TTL 远小于 token 寿命,过期风险极低;即使偏偶失效,AI 调用失败重试即可恢复。
21+
let cachedToken: string | null = null;
22+
let tokenCachedAt = 0;
23+
const TOKEN_CACHE_TTL_MS = 60_000;
24+
1825
/**
1926
* 获取远程 AI 插件实例(懒加载)
2027
*/
@@ -34,9 +41,13 @@ export async function getElectronPlugin(): Promise<ElectronAIPlugin> {
3441
electronPlugin = new ElectronAIPlugin();
3542
}
3643
try {
37-
const { supabase } = await import('../auth/supabaseClient');
38-
const { data: { session } } = await supabase.auth.getSession();
39-
electronPlugin.setAuthToken(session?.access_token ?? null);
44+
const now = Date.now();
45+
if (cachedToken === null || now - tokenCachedAt > TOKEN_CACHE_TTL_MS) {
46+
const { data: { session } } = await supabase.auth.getSession();
47+
cachedToken = session?.access_token ?? null;
48+
tokenCachedAt = now;
49+
}
50+
electronPlugin.setAuthToken(cachedToken);
4051
} catch {
4152
// Supabase token injection failed, proceed with null token
4253
}

‎client/src/lib/ai/asrWorker.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,13 @@ export class ASRWorker implements PipelineWorker {
112112
// 工具函数
113113
// ================================================================
114114

115-
/** 将 ArrayBuffer 转为 base64 字符串 */
115+
/** 将 ArrayBuffer 转为 base64 字符串(分块编码,避免逐字节字符串拼接的 O(n²)) */
116116
function arrayBufferToBase64(buffer: ArrayBuffer): string {
117117
const bytes = new Uint8Array(buffer);
118118
let binary = '';
119-
for (let i = 0; i < bytes.byteLength; i++) {
120-
binary += String.fromCharCode(bytes[i]);
119+
const CHUNK = 0x8000; // 分块大小,避免 fromCharCode 单次参数过多
120+
for (let i = 0; i < bytes.byteLength; i += CHUNK) {
121+
binary += String.fromCharCode.apply(null, bytes.subarray(i, i + CHUNK) as unknown as number[]);
121122
}
122123
return btoa(binary);
123124
}

‎client/src/lib/ai/visionWorker.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -280,12 +280,13 @@ function classifyVisionError(err: unknown): VisionErrorInfo {
280280
// 工具函数
281281
// ================================================================
282282

283-
/** 将 ArrayBuffer 转为 base64 字符串 */
283+
/** 将 ArrayBuffer 转为 base64 字符串(分块编码,避免逐字节字符串拼接的 O(n²)) */
284284
function arrayBufferToBase64(buffer: ArrayBuffer): string {
285285
const bytes = new Uint8Array(buffer);
286286
let binary = '';
287-
for (let i = 0; i < bytes.byteLength; i++) {
288-
binary += String.fromCharCode(bytes[i]);
287+
const CHUNK = 0x8000; // 分块大小,避免 fromCharCode 单次参数过多
288+
for (let i = 0; i < bytes.byteLength; i += CHUNK) {
289+
binary += String.fromCharCode.apply(null, bytes.subarray(i, i + CHUNK) as unknown as number[]);
289290
}
290291
return btoa(binary);
291292
}

0 commit comments

Comments
 (0)