feat: 后端代理层(Jev 透传 + ASR 预留端点),前端支持代理模式

This commit is contained in:
Jayway Liu
2026-10-03 12:55:00 +08:00
parent bed4d734ca
commit 6117ef894f
15 changed files with 740 additions and 11 deletions
+23 -11
View File
@@ -64,21 +64,30 @@ export interface JevClient {
const API_URL = 'https://api.typesafe.ai/v1/systemone';
/** 直连 REST 的实现。浏览器调用可能遇到 CORS,正式环境需走后端代理(见 README)。 */
/**
* REST 实现,两种模式:
* - 代理模式(优先):传 proxyUrl,请求发到 `${proxyUrl}/api/jev`,key 在服务端,
* 前端不带 Authorization(见 server/ 子包);
* - 直连模式(fallback):传 apiKey,直连 api.typesafe.ai,浏览器可能遇到 CORS。
*/
export class HttpJevClient implements JevClient {
private apiKey: string;
private apiKey?: string;
private proxyUrl?: string;
constructor(apiKey: string) {
constructor(apiKey?: string, proxyUrl?: string) {
this.apiKey = apiKey;
this.proxyUrl = proxyUrl;
}
async ask(state: unknown, questions: Record<string, JevQuestion>): Promise<Record<string, JevAnswer>> {
const res = await fetch(API_URL, {
const url = this.proxyUrl ? `${this.proxyUrl}/api/jev` : API_URL;
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
if (!this.proxyUrl && this.apiKey) {
headers.Authorization = `Bearer ${this.apiKey}`;
}
const res = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.apiKey}`,
},
headers,
body: JSON.stringify({ model: 'jev-latest', state, questions } satisfies JevRequest),
});
if (!res.ok) {
@@ -156,14 +165,17 @@ export class MockJevClient implements JevClient {
let singleton: JevClient | null = null;
/** key 缺失时自动降级 Mock 并提示。 */
/** 有代理走代理,有 key 直连,两者都缺时降级 Mock 并提示。 */
export function getJevClient(): JevClient {
if (singleton) return singleton;
const proxyUrl = import.meta.env.VITE_JEV_PROXY_URL;
const key = import.meta.env.VITE_TYPESAFE_API_KEY;
if (key) {
if (proxyUrl) {
singleton = new HttpJevClient(undefined, proxyUrl);
} else if (key) {
singleton = new HttpJevClient(key);
} else {
console.warn('[jev] 未配置 VITE_TYPESAFE_API_KEY,降级为 MockJevClient(确定性伪随机返分)');
console.warn('[jev] 未配置 VITE_JEV_PROXY_URL / VITE_TYPESAFE_API_KEY,降级为 MockJevClient(确定性伪随机返分)');
singleton = new MockJevClient();
}
return singleton;