Files
u-ppt/src/core/bridge.ts
T

72 lines
2.6 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.
/* =====================================================================
* bridge.ts — Tauri IPC 桥接(AI 代理专用)
* 桌面版(Tauri WebView)直连 AI 网关受 CORS 限制,走 Rust 侧 reqwest 代理;
* Web 版返回 null,调用方回退浏览器 fetch(智谱等支持 CORS 的网关直连可用)
* ===================================================================== */
/** 是否运行在 Tauri 桌面环境 */
export function isTauri(): boolean {
return typeof window !== 'undefined' && ('__TAURI__' in window || '__TAURI_INTERNALS__' in window)
}
let _invoke: ((cmd: string, args?: Record<string, unknown>) => Promise<unknown>) | null = null
let _invokeTried = false
/** 懒加载 Tauri invoke(动态 importWeb 版不会打进包) */
async function getInvoke() {
if (_invokeTried) return _invoke
_invokeTried = true
if (!isTauri()) return null
try {
const mod = await import('@tauri-apps/api/core')
_invoke = mod.invoke
} catch { _invoke = null }
return _invoke
}
export interface ProxyResp {
status: number
body: string
error?: string
}
/** 非流式代理:Rust 侧 fetch,返回 {status, body};非桌面环境返回 null */
export async function aiProxy(url: string, apiKey: string, body: string): Promise<ProxyResp | null> {
const invoke = await getInvoke()
if (!invoke) return null
try {
const r = await invoke('ai_proxy', { url, apiKey, body }) as ProxyResp
return r
} catch (e: any) {
return { status: 0, body: '', error: String(e?.message || e) }
}
}
/** 流式代理:Rust 侧 SSE,原始 chunk(含 "data: {...}" 帧)通过 onChunk 推送;
* SSE 解析由 ai.ts 的 createSSESink 统一处理(与浏览器路径共用)。非桌面返回 null */
export async function aiProxyStream(
url: string, apiKey: string, body: string,
onChunk: (raw: string) => void
): Promise<ProxyResp | null> {
const invoke = await getInvoke()
if (!invoke) return null
try {
const eventMod = await import('@tauri-apps/api/event')
const unlisten = await eventMod.listen<string>('ai-delta', (ev) => {
onChunk(ev.payload || '')
})
try {
const full = await invoke('ai_proxy_stream', { url, apiKey, body }) as unknown as string
return { status: 200, body: full }
} finally {
unlisten()
}
} catch (e: any) {
// Rust 侧错误协议:__HTTP_<status>__<body>
const raw = String(e?.message || e)
const m = raw.match(/^__HTTP_(\d+)__/)
if (m) return { status: Number(m[1]), body: raw.slice(m[0].length) }
return { status: 0, body: '', error: raw }
}
}