From ac951c31b1247d79075f51d767926ff8ca8d78da Mon Sep 17 00:00:00 2001 From: Julian Freeman Date: Sat, 28 Mar 2026 19:41:05 -0400 Subject: [PATCH] detailed logs --- src-tauri/src/lib.rs | 10 +++--- src/components/LogsView.vue | 52 +++++++++++++++++++++++------- src/components/TranslationView.vue | 48 +++++++++++++++++++++------ src/stores/settings.ts | 7 ++-- 4 files changed, 88 insertions(+), 29 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index bbd5200..eda2d67 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -56,16 +56,17 @@ async fn translate( .map_err(|e| e.to_string())?; if !payload.stream { - let data = res.json::().await.map_err(|e| e.to_string())?; - return Ok(data.choices.get(0).and_then(|c| c.message.as_ref()).map(|m| m.content.clone()).unwrap_or_default()); + let text = res.text().await.map_err(|e| e.to_string())?; + return Ok(text); } let mut stream = res.bytes_stream(); - let mut full_response = String::new(); + let mut raw_stream_log = String::new(); while let Some(item) = stream.next().await { let chunk = item.map_err(|e| e.to_string())?; let text = String::from_utf8_lossy(&chunk); + raw_stream_log.push_str(&text); for line in text.lines() { let line = line.trim(); @@ -77,7 +78,6 @@ async fn translate( if let Some(choice) = json.choices.get(0) { if let Some(delta) = &choice.delta { if let Some(content) = &delta.content { - full_response.push_str(content); app.emit("translation-chunk", content).map_err(|e| e.to_string())?; } } @@ -87,7 +87,7 @@ async fn translate( } } - Ok(full_response) + Ok(raw_stream_log) } #[cfg_attr(mobile, tauri::mobile_entry_point)] diff --git a/src/components/LogsView.vue b/src/components/LogsView.vue index 7a41b3b..244d31a 100644 --- a/src/components/LogsView.vue +++ b/src/components/LogsView.vue @@ -19,9 +19,20 @@ watch(() => settings.logs, (newVal) => { } }, { immediate: true }); +const decodeUnicode = (str: string) => { + if (!str) return str; + try { + return str.replace(/\\u([0-9a-fA-F]{4})/g, (_match, grp) => { + return String.fromCharCode(parseInt(grp, 16)); + }); + } catch { + return str; + } +}; + const getLogSummary = (log: any) => { if (log.type === 'error') return String(log.content); - if (typeof log.content === 'string') return log.content; + if (typeof log.content === 'string') return decodeUnicode(log.content); if (log.content && log.content.model) return `Model: ${log.content.model}`; if (log.content && log.content.score) return `Score: ${log.content.score}`; return 'JSON Data'; @@ -92,18 +103,37 @@ const getLogSummary = (log: any) => { )" >{{ selectedLogItem.type === 'request' ? 'Request' : selectedLogItem.type === 'response' ? 'Response' : 'Error' }} - +
+ + +
-
-
{{ typeof selectedLogItem.content === 'object' ? JSON.stringify(selectedLogItem.content, null, 2) : selectedLogItem.content }}
+
+
+

cURL 请求命令

+
{{ decodeUnicode(selectedLogItem.curl) }}
+
+ +
+

{{ selectedLogItem.type === 'request' ? 'Payload (JSON)' : 'Response Data' }}

+
{{ decodeUnicode(typeof selectedLogItem.content === 'object' ? JSON.stringify(selectedLogItem.content, null, 2) : selectedLogItem.content) }}
+
diff --git a/src/components/TranslationView.vue b/src/components/TranslationView.vue index ee9098a..67f1bf7 100644 --- a/src/components/TranslationView.vue +++ b/src/components/TranslationView.vue @@ -105,6 +105,15 @@ const clearSource = () => { evaluationResult.value = null; }; +const generateCurl = (apiBaseUrl: string, apiKey: string, body: any) => { + 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)}'`; +}; + const evaluateTranslation = async () => { if (!targetText.value) return; isEvaluating.value = true; @@ -140,14 +149,18 @@ const evaluateTranslation = async () => { stream: false }; - settings.addLog('request', { type: 'evaluation', ...requestBody }); + settings.addLog('request', { type: 'evaluation', ...requestBody }, generateCurl(apiBaseUrl, apiKey, requestBody)); try { const response = await invoke('translate', { apiAddress: apiBaseUrl, apiKey: apiKey, payload: requestBody }); try { - const jsonStr = response.replace(/```json\s?|\s?```/g, '').trim(); + // 解析 API 的原始响应 JSON + const fullResponseJson = JSON.parse(response); + settings.addLog('response', fullResponseJson); + + const content = fullResponseJson.choices?.[0]?.message?.content || response; + const jsonStr = content.replace(/```json\s?|\s?```/g, '').trim(); evaluationResult.value = JSON.parse(jsonStr); - settings.addLog('response', { type: 'evaluation', content: evaluationResult.value }); } catch (parseErr) { console.error('Failed to parse evaluation result:', response); settings.addLog('error', `Evaluation parsing error: ${response}`); @@ -197,17 +210,23 @@ const refineTranslation = async () => { stream: settings.enableStreaming }; - settings.addLog('request', { type: 'refinement', ...requestBody }); + settings.addLog('request', { type: 'refinement', ...requestBody }, generateCurl(apiBaseUrl, apiKey, requestBody)); try { const response = await invoke('translate', { apiAddress: apiBaseUrl, apiKey: apiKey, payload: requestBody }); - if (!settings.enableStreaming) targetText.value = response; + + if (settings.enableStreaming) { + settings.addLog('response', targetText.value); + } else { + const fullResponseJson = JSON.parse(response); + settings.addLog('response', fullResponseJson); + targetText.value = fullResponseJson.choices?.[0]?.message?.content || response; + } if (evaluationResult.value?.suggestions) { appliedSuggestionIds.value.push(...selectedSuggestionIds.value); selectedSuggestionIds.value = []; } - settings.addLog('response', 'Refinement completed'); if (currentHistoryId.value) { settings.updateHistoryItem(currentHistoryId.value, { @@ -249,18 +268,27 @@ const translate = async () => { stream: settings.enableStreaming }; - settings.addLog('request', requestBody); + settings.addLog('request', requestBody, generateCurl(settings.apiBaseUrl, settings.apiKey, requestBody)); try { const response = await invoke('translate', { apiAddress: settings.apiBaseUrl, apiKey: settings.apiKey, payload: requestBody }); - if (!settings.enableStreaming) targetText.value = response; - settings.addLog('response', 'Translation completed'); + + let finalTargetText = ''; + if (settings.enableStreaming) { + finalTargetText = targetText.value; + settings.addLog('response', response); + } else { + const fullResponseJson = JSON.parse(response); + settings.addLog('response', fullResponseJson); + finalTargetText = fullResponseJson.choices?.[0]?.message?.content || response; + targetText.value = finalTargetText; + } currentHistoryId.value = settings.addHistory({ sourceLang: { ...sourceLang.value }, targetLang: { ...targetLang.value }, sourceText: sourceText.value, - targetText: settings.enableStreaming ? targetText.value : response, + targetText: finalTargetText, context: context.value, speakerIdentity: settings.speakerIdentity, toneRegister: settings.toneRegister, diff --git a/src/stores/settings.ts b/src/stores/settings.ts index a272a8b..ef9f91f 100644 --- a/src/stores/settings.ts +++ b/src/stores/settings.ts @@ -159,10 +159,10 @@ export const useSettingsStore = defineStore('settings', () => { set: (val) => { toneRegisterMap.value[sourceLang.value.code] = val; } }); - const logs = ref<{ id: string; timestamp: string; type: 'request' | 'response' | 'error'; content: any }[]>([]); + const logs = ref<{ id: string; timestamp: string; type: 'request' | 'response' | 'error'; content: any; curl?: string }[]>([]); const history = useLocalStorage('translation-history-v1', []); - const addLog = (type: 'request' | 'response' | 'error', content: any) => { + const addLog = (type: 'request' | 'response' | 'error', content: any, curl?: string) => { const now = new Date(); const timestamp = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`; @@ -170,7 +170,8 @@ export const useSettingsStore = defineStore('settings', () => { id: crypto.randomUUID(), timestamp, type, - content + content, + curl }); if (logs.value.length > 50) logs.value.pop(); // 稍微增加日志保留数量 };