fix 2
This commit is contained in:
164
src/lib/translation-service.ts
Normal file
164
src/lib/translation-service.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import type { ApiProfile } from '../stores/settings';
|
||||
|
||||
interface Message {
|
||||
role: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface TranslationPayload {
|
||||
model: string;
|
||||
messages: Message[];
|
||||
stream: boolean;
|
||||
}
|
||||
|
||||
export interface TranslationChunkEvent {
|
||||
request_id: string;
|
||||
chunk: string;
|
||||
}
|
||||
|
||||
export interface EvaluationSuggestion {
|
||||
id: number;
|
||||
text: string;
|
||||
importance: number;
|
||||
}
|
||||
|
||||
export interface EvaluationResult {
|
||||
score: number;
|
||||
analysis: string;
|
||||
suggestions: EvaluationSuggestion[];
|
||||
}
|
||||
|
||||
export interface ModelConfig {
|
||||
apiBaseUrl: string;
|
||||
apiKey: string;
|
||||
modelName: string;
|
||||
}
|
||||
|
||||
type LogType = 'request' | 'response' | 'error';
|
||||
|
||||
interface Logger {
|
||||
addLog: (type: LogType, content: any, curl?: string) => void;
|
||||
}
|
||||
|
||||
interface ExecuteTranslationRequestOptions {
|
||||
apiAddress: string;
|
||||
apiKey: string;
|
||||
payload: TranslationPayload;
|
||||
logger: Logger;
|
||||
logType?: string;
|
||||
onStreamStart?: (requestId: string) => void;
|
||||
}
|
||||
|
||||
const FALLBACK_EVALUATION_RESULT: EvaluationResult = {
|
||||
score: 0,
|
||||
analysis: '无法解析审计结果,请查看日志',
|
||||
suggestions: [],
|
||||
};
|
||||
|
||||
export function resolveModelConfig(
|
||||
primary: ModelConfig,
|
||||
profiles: ApiProfile[],
|
||||
profileId: string | null,
|
||||
): ModelConfig {
|
||||
if (!profileId) return primary;
|
||||
|
||||
const profile = profiles.find((item) => item.id === profileId);
|
||||
if (!profile) return primary;
|
||||
|
||||
return {
|
||||
apiBaseUrl: profile.apiBaseUrl,
|
||||
apiKey: profile.apiKey,
|
||||
modelName: profile.modelName,
|
||||
};
|
||||
}
|
||||
|
||||
export function generateCurl(apiBaseUrl: string, apiKey: string, body: TranslationPayload) {
|
||||
const fullUrl = `${apiBaseUrl.replace(/\/$/, '')}/chat/completions`;
|
||||
const maskedKey = apiKey ? `${apiKey.slice(0, 6)}...${apiKey.slice(-4)}` : 'YOUR_API_KEY';
|
||||
|
||||
return `curl "${fullUrl}" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-H "Authorization: Bearer ${maskedKey}" \\
|
||||
-d '${JSON.stringify(body, null, 2)}'`;
|
||||
}
|
||||
|
||||
export async function executeTranslationRequest({
|
||||
apiAddress,
|
||||
apiKey,
|
||||
payload,
|
||||
logger,
|
||||
logType,
|
||||
onStreamStart,
|
||||
}: ExecuteTranslationRequestOptions) {
|
||||
const requestId = crypto.randomUUID();
|
||||
const requestLog = logType ? { type: logType, ...payload } : payload;
|
||||
|
||||
logger.addLog('request', requestLog, generateCurl(apiAddress, apiKey, payload));
|
||||
|
||||
try {
|
||||
if (payload.stream) onStreamStart?.(requestId);
|
||||
|
||||
const response = await invoke<string>('translate', {
|
||||
apiAddress,
|
||||
apiKey,
|
||||
payload,
|
||||
requestId,
|
||||
});
|
||||
|
||||
logger.addLog('response', payload.stream ? response : safeParseJson(response) ?? response);
|
||||
return response;
|
||||
} catch (error) {
|
||||
logger.addLog('error', String(error));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function safeParseJson(value: string) {
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function extractAssistantContent(response: string) {
|
||||
const parsed = safeParseJson(response);
|
||||
if (parsed && typeof parsed === 'object') {
|
||||
return (parsed as any).choices?.[0]?.message?.content || response;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
export function tryParseEvaluationResult(raw?: string) {
|
||||
if (!raw) {
|
||||
return {
|
||||
ok: false as const,
|
||||
error: 'Evaluation result is empty',
|
||||
result: FALLBACK_EVALUATION_RESULT,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
let cleanStr = raw.trim();
|
||||
if (cleanStr.startsWith('```')) {
|
||||
cleanStr = cleanStr.replace(/^```[a-zA-Z]*\n?/, '').replace(/\n?```$/, '').trim();
|
||||
}
|
||||
|
||||
const parsed = JSON.parse(cleanStr);
|
||||
return {
|
||||
ok: true as const,
|
||||
result: {
|
||||
score: Number(parsed.score) || 0,
|
||||
analysis: typeof parsed.analysis === 'string' ? parsed.analysis : '',
|
||||
suggestions: Array.isArray(parsed.suggestions) ? parsed.suggestions : [],
|
||||
} satisfies EvaluationResult,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
ok: false as const,
|
||||
error: `Failed to parse evaluation JSON: ${String(error)}`,
|
||||
result: FALLBACK_EVALUATION_RESULT,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user