v0.16.14: TypeScript 编译错误修复 + 文档同步 + 代码健壮性增强
This commit is contained in:
+1
-1
@@ -101,7 +101,7 @@ export function createMenu(): void {
|
||||
dialog.showMessageBox(mainWindow!, {
|
||||
type: 'info',
|
||||
title: '关于 Metona Ollama',
|
||||
message: 'Metona Ollama Desktop v0.16.13',
|
||||
message: 'Metona Ollama Desktop v0.16.14',
|
||||
detail: 'TypeScript + Electron Ollama AI 聊天客户端\n\nhttps://gitee.com/thzxx/metona-ollama',
|
||||
icon: getIconPath()
|
||||
});
|
||||
|
||||
+27
-21
@@ -103,30 +103,36 @@ export class OllamaAPI {
|
||||
}, { once: true });
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split('\n');
|
||||
buffer = lines.pop() || '';
|
||||
|
||||
for (const line of lines) {
|
||||
if (!line.trim()) continue;
|
||||
let chunk: OllamaStreamChunk;
|
||||
try {
|
||||
chunk = JSON.parse(line);
|
||||
} catch {
|
||||
continue; // 跳过无法解析的行(可能是不完整的 JSON)
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
// onChunk 回调错误不再被静默吞掉,而是向上传播给调用方
|
||||
if (onChunk) onChunk(chunk);
|
||||
if (chunk.done) {
|
||||
return;
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split('\n');
|
||||
buffer = lines.pop() || '';
|
||||
|
||||
for (const line of lines) {
|
||||
if (!line.trim()) continue;
|
||||
let chunk: OllamaStreamChunk;
|
||||
try {
|
||||
chunk = JSON.parse(line);
|
||||
} catch {
|
||||
continue; // 跳过无法解析的行(可能是不完整的 JSON)
|
||||
}
|
||||
// onChunk 回调错误不再被静默吞掉,而是向上传播给调用方
|
||||
if (onChunk) onChunk(chunk);
|
||||
if (chunk.done) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// abort 导致 reader.cancel() 会使 reader.read() 抛出异常,属于正常中止流程
|
||||
if (abortController?.signal.aborted) return;
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (buffer.trim()) {
|
||||
|
||||
@@ -1080,7 +1080,7 @@ function renderToolCard(tc: ToolCallRecord): string {
|
||||
} else if (tc.name === 'delete_file') {
|
||||
if (r.batch) {
|
||||
resultHtml = `<div class="tool-result-entry">✅ 批量删除 ${r.successCount}/${r.totalPaths} 个路径</div>`;
|
||||
if (r.results) {
|
||||
if (Array.isArray(r.results)) {
|
||||
for (const res of r.results) {
|
||||
resultHtml += `<div class="tool-result-entry">${res.success ? '✅' : '❌'} ${escapeHtml(String(res.path || ''))}${res.success ? '' : ' — ' + escapeHtml(String(res.error || ''))}</div>`;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<div class="header-left">
|
||||
<img class="logo" src="./assets/icons/llama.png" alt="logo" />
|
||||
<span class="app-title">Metona Ollama</span>
|
||||
<span class="app-version">v0.16.13</span>
|
||||
<span class="app-version">v0.16.14</span>
|
||||
<button class="icon-btn help-btn" id="btnHelp" title="使用帮助">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/>
|
||||
|
||||
@@ -292,7 +292,7 @@ async function startNewSession(): Promise<void> {
|
||||
|
||||
async function init(): Promise<void> {
|
||||
let db: ChatDB | undefined;
|
||||
let api: OllamaAPI;
|
||||
let api: OllamaAPI | undefined;
|
||||
|
||||
logInit('启动...');
|
||||
|
||||
|
||||
@@ -1340,7 +1340,7 @@ async function handleInit(
|
||||
logInfo(`自动上下文压缩触发: tokens≈${estimateTokens(ctx.messages.map(m => m.content || '').join(''))} > ${Math.floor(effectiveNumCtx * AUTO_COMPRESS_THRESHOLD)} (${Math.round(AUTO_COMPRESS_THRESHOLD * 100)}% of ${effectiveNumCtx})`);
|
||||
const compressAC = state.get<AbortController | null>(KEYS.ABORT_CONTROLLER) || new AbortController();
|
||||
try {
|
||||
const compressed = await compressWithLLM(ctx.messages, api as any, model, { abortController: compressAC });
|
||||
const compressed = await compressWithLLM(ctx.messages, api, model, { abortController: compressAC });
|
||||
// P1-C4 修复:handleInit 中的自动压缩也使用 AND 条件
|
||||
const initBeforeTokens = estimateTokens(ctx.messages.map(m => m.content || '').join(''));
|
||||
const initAfterTokens = estimateTokens(compressed.map(m => m.content || '').join(''));
|
||||
@@ -1634,7 +1634,7 @@ async function handleThinking(
|
||||
logWarn(`R8: 检测到上下文溢出错误,触发紧急压缩 (${ctx.emergencyCompressCount}/${MAX_EMERGENCY_COMPRESS})`, errMsg.slice(0, 100));
|
||||
try {
|
||||
const compressAC = new AbortController();
|
||||
const compressed = await compressWithLLM(ctx.messages, api as any, model, { abortController: compressAC });
|
||||
const compressed = await compressWithLLM(ctx.messages, api, model, { abortController: compressAC });
|
||||
// P1-C4 修复:R8 紧急压缩也使用 AND 条件,避免接受 token 增加的结果
|
||||
const r8BeforeTokens = estimateTokens(ctx.messages.map(m => m.content || '').join(''));
|
||||
const r8AfterTokens = estimateTokens(compressed.map(m => m.content || '').join(''));
|
||||
@@ -2446,7 +2446,7 @@ async function handleCompressing(
|
||||
logInfo('COMPRESSING: 上下文压缩触发');
|
||||
const compressAC = state.get<AbortController | null>(KEYS.ABORT_CONTROLLER) || new AbortController();
|
||||
try {
|
||||
const compressed = await compressWithLLM(ctx.messages, api as any, model, { abortController: compressAC });
|
||||
const compressed = await compressWithLLM(ctx.messages, api, model, { abortController: compressAC });
|
||||
// P1-C4 修复:原条件用 OR(消息数减少 OR token 减少),可能接受 token 增加的结果。
|
||||
// 改为 AND:只有消息数减少且 token 减少时才接受,确保压缩真正生效
|
||||
const beforeTokens = estimateTokens(ctx.messages.map(m => m.content || '').join(''));
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* 支持自动压缩与手动 /compress 触发
|
||||
*/
|
||||
|
||||
import type { OllamaMessage, OllamaStreamChunk } from '../types.js';
|
||||
import type { OllamaMessage, OllamaStreamChunk, OllamaChatParams } from '../types.js';
|
||||
import { logInfo, logWarn, logSuccess, logError } from './log-service.js';
|
||||
|
||||
// ── R12: 压缩去重 — 内容指纹追踪 ──
|
||||
@@ -586,7 +586,7 @@ export interface StructuredSummary {
|
||||
*/
|
||||
export async function compressWithLLM(
|
||||
messages: OllamaMessage[],
|
||||
api: { chatStream: (params: Record<string, unknown>, onChunk: (chunk: OllamaStreamChunk) => void, ac?: AbortController) => Promise<void> },
|
||||
api: { chatStream: (params: OllamaChatParams, onChunk: (chunk: OllamaStreamChunk) => void, abortController?: AbortController) => Promise<void> },
|
||||
model: string,
|
||||
options: {
|
||||
keepHead?: number;
|
||||
|
||||
Vendored
+1
@@ -120,6 +120,7 @@ export interface ChatMessage {
|
||||
/** 多视频独立指示牌 */
|
||||
_videos?: Array<{ fileName: string; frameCount: number; duration: number }>;
|
||||
stopped?: boolean;
|
||||
interrupted?: boolean;
|
||||
toolCalls?: ToolCallRecord[];
|
||||
/** 标记此消息为 LLM 压缩摘要生成 */
|
||||
compressed?: boolean;
|
||||
|
||||
Reference in New Issue
Block a user