Merge branch 'develop' of https://github.com/Wxw-Gu/WechatExplorer into develop

This commit is contained in:
Wxw-Gu
2026-08-21 17:00:41 +08:00
7 changed files with 67 additions and 7 deletions
+1
View File
@@ -2,3 +2,4 @@ singleQuote: true
semi: false semi: false
printWidth: 100 printWidth: 100
trailingComma: none trailingComma: none
endOfLine: auto
+1 -1
View File
@@ -8,7 +8,7 @@
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite", "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite",
"windows": { "windows": {
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd" "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.CMD"
}, },
"runtimeArgs": ["--sourcemap"], "runtimeArgs": ["--sourcemap"],
"env": { "env": {
+2 -1
View File
@@ -7,5 +7,6 @@
}, },
"[json]": { "[json]": {
"editor.defaultFormatter": "vscode.json-language-features" "editor.defaultFormatter": "vscode.json-language-features"
} },
"files.eol": "\n"
} }
+6
View File
@@ -24,6 +24,12 @@ export default defineConfig(
'react-refresh': eslintPluginReactRefresh 'react-refresh': eslintPluginReactRefresh
}, },
rules: { rules: {
'prettier/prettier': [
'error',
{
endOfLine: 'auto'
}
],
...eslintPluginReactHooks.configs.recommended.rules, ...eslintPluginReactHooks.configs.recommended.rules,
...eslintPluginReactRefresh.configs.vite.rules ...eslintPluginReactRefresh.configs.vite.rules
} }
+27 -3
View File
@@ -26,16 +26,18 @@ type AIMessagePart = { type: 'text'; text: string } | { type: 'image'; dataUrl:
type AIMessage = { role: string; content: string | AIMessagePart[] } type AIMessage = { role: string; content: string | AIMessagePart[] }
type AIRequestResult = { type AIRequestResult = {
data: string data: string
finishReason?: string
usage?: { input?: number; output?: number; total?: number; estimated?: boolean } usage?: { input?: number; output?: number; total?: number; estimated?: boolean }
} }
interface OpenAIResponsePayload { interface OpenAIResponsePayload {
error?: { message?: string } error?: { message?: string }
choices?: Array<{ message?: { content?: string } }> choices?: Array<{ message?: { content?: string }; finish_reason?: string }>
usage?: { prompt_tokens?: number; completion_tokens?: number; total_tokens?: number } usage?: { prompt_tokens?: number; completion_tokens?: number; total_tokens?: number }
} }
interface AnthropicResponsePayload { interface AnthropicResponsePayload {
error?: { message?: string } error?: { message?: string }
content?: Array<{ type?: string; text?: string }> content?: Array<{ type?: string; text?: string }>
stop_reason?: string
usage?: { input_tokens?: number; output_tokens?: number } usage?: { input_tokens?: number; output_tokens?: number }
} }
@@ -612,6 +614,12 @@ async function requestOpenAICompatible(
const endpoint = provider.baseUrl.endsWith('/chat/completions') const endpoint = provider.baseUrl.endsWith('/chat/completions')
? provider.baseUrl ? provider.baseUrl
: `${provider.baseUrl.replace(/\/+$/, '')}/chat/completions` : `${provider.baseUrl.replace(/\/+$/, '')}/chat/completions`
let modelMaxTokens = 0
for (const m of provider.models) {
if (m.id === model && m.maxTokens) {
modelMaxTokens = m.maxTokens
}
}
const response = await fetchWithTimeout( const response = await fetchWithTimeout(
endpoint, endpoint,
{ {
@@ -621,7 +629,11 @@ async function requestOpenAICompatible(
model, model,
messages: toOpenAIMessages(messages), messages: toOpenAIMessages(messages),
temperature: provider.advanced.temperature, temperature: provider.advanced.temperature,
max_tokens: testing ? 8 : provider.advanced.maxTokens max_tokens: testing
? 8
: modelMaxTokens > 0
? modelMaxTokens
: provider.advanced.maxTokens || 4096
}) })
}, },
provider.advanced.timeoutMs, provider.advanced.timeoutMs,
@@ -631,6 +643,7 @@ async function requestOpenAICompatible(
if (!response.ok) throw new Error(payload.error?.message || `AI 请求失败 (${response.status})`) if (!response.ok) throw new Error(payload.error?.message || `AI 请求失败 (${response.status})`)
return { return {
data: String(payload.choices?.[0]?.message?.content || ''), data: String(payload.choices?.[0]?.message?.content || ''),
finishReason: String(payload.choices?.[0]?.finish_reason || 'unknown'),
usage: payload.usage usage: payload.usage
? { ? {
input: payload.usage.prompt_tokens, input: payload.usage.prompt_tokens,
@@ -667,6 +680,12 @@ async function requestAnthropic(
const endpoint = provider.baseUrl.endsWith('/messages') const endpoint = provider.baseUrl.endsWith('/messages')
? provider.baseUrl ? provider.baseUrl
: `${provider.baseUrl.replace(/\/+$/, '')}/messages` : `${provider.baseUrl.replace(/\/+$/, '')}/messages`
let modelMaxTokens = 0
for (const m of provider.models) {
if (m.id === model && m.maxTokens) {
modelMaxTokens = m.maxTokens
}
}
const response = await fetchWithTimeout( const response = await fetchWithTimeout(
endpoint, endpoint,
{ {
@@ -677,7 +696,11 @@ async function requestAnthropic(
system: system || undefined, system: system || undefined,
messages: anthropicMessages, messages: anthropicMessages,
temperature: provider.advanced.temperature, temperature: provider.advanced.temperature,
max_tokens: testing ? 8 : provider.advanced.maxTokens || 4096 max_tokens: testing
? 8
: modelMaxTokens > 0
? modelMaxTokens
: provider.advanced.maxTokens || 4096
}) })
}, },
provider.advanced.timeoutMs, provider.advanced.timeoutMs,
@@ -693,6 +716,7 @@ async function requestAnthropic(
.map((item) => item.text || '') .map((item) => item.text || '')
.join('\n') .join('\n')
: '', : '',
finishReason: String(payload.stop_reason || 'unknown'),
usage: payload.usage usage: payload.usage
? { ? {
input: payload.usage.input_tokens, input: payload.usage.input_tokens,
+1
View File
@@ -249,6 +249,7 @@ declare global {
) => Promise<{ ) => Promise<{
success: boolean success: boolean
data?: string data?: string
finishReason?: string
usage?: { usage?: {
input?: number input?: number
output?: number output?: number
@@ -511,7 +511,19 @@ export function useGroupReportGeneration({
reportTimeoutSeconds * 1000 + REPORT_MODEL_TIMEOUT_BUFFER_MS reportTimeoutSeconds * 1000 + REPORT_MODEL_TIMEOUT_BUFFER_MS
) )
) )
if (!result.success || !result.data) throw new Error(result.error || 'AI 请求失败') if (
result.finishReason &&
result.finishReason !== 'stop' &&
result.finishReason !== 'end_turn'
) {
let errMsg = `当前模型\`异常结束生成\`,原因:${result.finishReason}`
if (result.finishReason === 'length' || result.finishReason === 'max_tokens') {
errMsg += ',请尝试在`设置-AI 模型-新增/编辑供应商-模型配置/高级设置`中调高`Max Tokens`'
}
throw new Error(errMsg)
}
if (!result.success || !result.data)
throw new Error(result.error || 'AI 请求失败,未返回有效内容')
writeReportLog('info', '模型响应完成', { writeReportLog('info', '模型响应完成', {
outputLength: result.data.length, outputLength: result.data.length,
usage: result.usage usage: result.usage
@@ -552,8 +564,23 @@ export function useGroupReportGeneration({
reportTimeoutSeconds * 1000 + REPORT_MODEL_TIMEOUT_BUFFER_MS reportTimeoutSeconds * 1000 + REPORT_MODEL_TIMEOUT_BUFFER_MS
) )
) )
if (
repairResult.finishReason &&
repairResult.finishReason !== 'stop' &&
repairResult.finishReason !== 'end_turn'
) {
let errMsg = `当前模型\`异常结束生成\`,原因:${repairResult.finishReason}`
if (
repairResult.finishReason === 'length' ||
repairResult.finishReason === 'max_tokens'
) {
errMsg +=
',请尝试在`设置-AI 模型-新增/编辑供应商-模型配置/高级设置`中调高`Max Tokens`'
}
throw new Error(errMsg)
}
if (!repairResult.success || !repairResult.data) { if (!repairResult.success || !repairResult.data) {
throw new Error(repairResult.error || 'AI 修复日报 JSON 失败', { throw new Error(repairResult.error || 'AI 修复日报 JSON 失败,未返回有效内容', {
cause: parseError cause: parseError
}) })
} }