feat: 全面补充执行日志(135处)

补日志的模块:
- vector-store.ts: 0→7(init/create/delete/add/search 全覆盖)
- rag.ts: 3→7(initVectorStore/embedBatch/removeDoc/retrieveContext)
- chat-area.ts: 2→5(3个导出函数加日志)
- history-modal.ts: 4→5(打开历史记录)
- kb-modal.ts: 7→8(打开知识库管理)
- header.ts: 5→7(运行中模型状态)
This commit is contained in:
thzxx
2026-04-07 01:47:02 +08:00
parent 60152fb443
commit 9735a1ed38
6 changed files with 26 additions and 10 deletions
+4 -1
View File
@@ -3,7 +3,7 @@
*/ */
import { state, KEYS } from '../state/state.js'; import { state, KEYS } from '../state/state.js';
import { logError } from '../services/log-service.js'; import { logError, logSession } from '../services/log-service.js';
import { marked } from '../utils/marked-config.js'; import { marked } from '../utils/marked-config.js';
import { escapeHtml, formatTime } from '../utils/utils.js'; import { escapeHtml, formatTime } from '../utils/utils.js';
import type { ChatSession, ChatMessage, ToolCallRecord } from '../types.js'; import type { ChatSession, ChatMessage, ToolCallRecord } from '../types.js';
@@ -420,6 +420,7 @@ export async function exportAsMarkdown(session: ChatSession): Promise<void> {
if (m.think) md += `> **思考:** ${m.think}\n\n`; if (m.think) md += `> **思考:** ${m.think}\n\n`;
}); });
await nativeSaveFile(`${session.title}.md`, md); await nativeSaveFile(`${session.title}.md`, md);
logSession('导出', `${session.title}.md`);
} }
export async function exportAsHtml(session: ChatSession): Promise<void> { export async function exportAsHtml(session: ChatSession): Promise<void> {
@@ -435,6 +436,7 @@ code{background:rgba(0,0,0,0.2);padding:2px 6px;border-radius:4px;}</style></hea
}); });
html += '</body></html>'; html += '</body></html>';
await nativeSaveFile(`${session.title}.html`, html); await nativeSaveFile(`${session.title}.html`, html);
logSession('导出', `${session.title}.html`);
} }
export async function exportAsTxt(session: ChatSession): Promise<void> { export async function exportAsTxt(session: ChatSession): Promise<void> {
@@ -443,4 +445,5 @@ export async function exportAsTxt(session: ChatSession): Promise<void> {
txt += `[${m.role === 'user' ? '用户' : 'AI'}]\n${m.content || ''}\n\n`; txt += `[${m.role === 'user' ? '用户' : 'AI'}]\n${m.content || ''}\n\n`;
}); });
await nativeSaveFile(`${session.title}.txt`, txt); await nativeSaveFile(`${session.title}.txt`, txt);
logSession('导出', `${session.title}.txt`);
} }
+2
View File
@@ -78,6 +78,7 @@ export async function updateRunningModels(): Promise<void> {
const data = await api.psModels(); const data = await api.psModels();
if (!data.models || data.models.length === 0) { if (!data.models || data.models.length === 0) {
container.innerHTML = '<p class="text-muted">无运行中的模型</p>'; container.innerHTML = '<p class="text-muted">无运行中的模型</p>';
logDebug('运行中模型: 0');
return; return;
} }
container.innerHTML = data.models.map(m => ` container.innerHTML = data.models.map(m => `
@@ -86,6 +87,7 @@ export async function updateRunningModels(): Promise<void> {
<span class="model-vram">${formatSize(m.size_vram || 0)} VRAM</span> <span class="model-vram">${formatSize(m.size_vram || 0)} VRAM</span>
</div> </div>
`).join(''); `).join('');
logDebug(`运行中模型: ${data.models.length}`, data.models.map(m => m.name).join(', '));
} catch { } catch {
container.innerHTML = '<p class="text-muted">无法获取运行信息</p>'; container.innerHTML = '<p class="text-muted">无法获取运行信息</p>';
} }
+1
View File
@@ -190,6 +190,7 @@ export function openHistoryModal(): void {
(document.querySelector('#historySearchInput') as HTMLInputElement).value = ''; (document.querySelector('#historySearchInput') as HTMLInputElement).value = '';
(document.querySelector('#historySearchClear') as HTMLElement).style.display = 'none'; (document.querySelector('#historySearchClear') as HTMLElement).style.display = 'none';
loadHistory(); loadHistory();
logSession('打开历史记录');
historyModalEl.style.display = ''; historyModalEl.style.display = '';
} }
+1
View File
@@ -98,6 +98,7 @@ function updateRagBadge(): void {
async function openKBModal(): Promise<void> { async function openKBModal(): Promise<void> {
kbModalEl.style.display = ''; kbModalEl.style.display = '';
logRAG('打开知识库管理');
await refreshCollections(); await refreshCollections();
await populateEmbedModels(); await populateEmbedModels();
} }
+4
View File
@@ -14,6 +14,7 @@ export async function initVectorStore(): Promise<VectorStore> {
if (vectorStore) return vectorStore; if (vectorStore) return vectorStore;
vectorStore = new VectorStore(); vectorStore = new VectorStore();
await vectorStore.init(); await vectorStore.init();
logRAG('向量存储已初始化');
return vectorStore; return vectorStore;
} }
@@ -34,6 +35,7 @@ export async function embedText(text: string, model: string): Promise<number[]>
export async function embedBatch(texts: string[], model: string, onProgress?: (done: number, total: number) => void): Promise<number[][]> { export async function embedBatch(texts: string[], model: string, onProgress?: (done: number, total: number) => void): Promise<number[][]> {
const results: number[][] = []; const results: number[][] = [];
const batchSize = 5; const batchSize = 5;
logDebug(`嵌入批次`, `${texts.length} 个文本, 模型: ${model}`);
for (let i = 0; i < texts.length; i += batchSize) { for (let i = 0; i < texts.length; i += batchSize) {
const batch = texts.slice(i, i + batchSize); const batch = texts.slice(i, i + batchSize);
const embeddings = await Promise.all(batch.map(t => embedText(t, model))); const embeddings = await Promise.all(batch.map(t => embedText(t, model)));
@@ -87,6 +89,7 @@ export async function removeDocumentFromKB(colId: string, docId: string): Promis
const vs = await initVectorStore(); const vs = await initVectorStore();
const allVectors = await vs.getVectorsByCollection(colId); const allVectors = await vs.getVectorsByCollection(colId);
const docVectors = allVectors.filter(v => v.docId === docId); const docVectors = allVectors.filter(v => v.docId === docId);
logRAG(`删除文档`, `${docId} (${docVectors.length} 个分块)`);
await vs.deleteVectorsByDocument(colId, docId); await vs.deleteVectorsByDocument(colId, docId);
@@ -119,6 +122,7 @@ export async function retrieveContext(query: string, colId: string, topK = 5): P
const embedModel = col.embeddingModel; const embedModel = col.embeddingModel;
if (!embedModel) throw new Error('未配置嵌入模型'); if (!embedModel) throw new Error('未配置嵌入模型');
logRAG('开始检索', col.name);
const queryEmbedding = await embedText(query, embedModel); const queryEmbedding = await embedText(query, embedModel);
const results = await vs.search(colId, queryEmbedding, topK); const results = await vs.search(colId, queryEmbedding, topK);
if (results.length === 0) return { context: '', results: [] }; if (results.length === 0) return { context: '', results: [] };
+13 -8
View File
@@ -3,6 +3,7 @@
*/ */
import type { VectorCollection, VectorItem, SearchResult } from '../types.js'; import type { VectorCollection, VectorItem, SearchResult } from '../types.js';
import { logRAG, logDebug } from './log-service.js';
const MIN_IVF_SIZE = 200; const MIN_IVF_SIZE = 200;
const DEFAULT_K = 20; const DEFAULT_K = 20;
@@ -30,7 +31,7 @@ export class VectorStore {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const req = indexedDB.open(this.dbName, 2); const req = indexedDB.open(this.dbName, 2);
req.onerror = () => reject(req.error); req.onerror = () => reject(req.error);
req.onsuccess = () => { this.db = req.result; resolve(); }; req.onsuccess = () => { this.db = req.result; logDebug('向量数据库已连接'); resolve(); };
req.onupgradeneeded = (e) => { req.onupgradeneeded = (e) => {
const db = (e.target as IDBOpenDBRequest).result; const db = (e.target as IDBOpenDBRequest).result;
if (!db.objectStoreNames.contains('vectors')) { if (!db.objectStoreNames.contains('vectors')) {
@@ -60,7 +61,7 @@ export class VectorStore {
}; };
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const req = this._tx('collections', 'readwrite').put(col); const req = this._tx('collections', 'readwrite').put(col);
req.onsuccess = () => resolve(col); req.onsuccess = () => { logRAG(`创建集合: ${name}`, col.id); resolve(col); };
req.onerror = () => reject(req.error); req.onerror = () => reject(req.error);
}); });
} }
@@ -92,6 +93,7 @@ export class VectorStore {
async deleteCollection(colId: string): Promise<void> { async deleteCollection(colId: string): Promise<void> {
const vectors = await this.getVectorsByCollection(colId); const vectors = await this.getVectorsByCollection(colId);
logRAG(`删除集合`, `${colId} (${vectors.length} 向量)`);
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
const tx = this.db!.transaction(['vectors', 'collections', 'indexes'], 'readwrite'); const tx = this.db!.transaction(['vectors', 'collections', 'indexes'], 'readwrite');
const vStore = tx.objectStore('vectors'); const vStore = tx.objectStore('vectors');
@@ -107,6 +109,8 @@ export class VectorStore {
async addVectors(items: VectorItem[]): Promise<void> { async addVectors(items: VectorItem[]): Promise<void> {
if (items.length === 0) return; if (items.length === 0) return;
const colId = items[0].collectionId;
logDebug(`写入向量`, `${colId}: ${items.length}`);
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
const tx = this.db!.transaction('vectors', 'readwrite'); const tx = this.db!.transaction('vectors', 'readwrite');
const store = tx.objectStore('vectors'); const store = tx.objectStore('vectors');
@@ -142,6 +146,7 @@ export class VectorStore {
async deleteVectorsByDocument(colId: string, docId: string): Promise<void> { async deleteVectorsByDocument(colId: string, docId: string): Promise<void> {
const vectors = await this.getVectorsByCollection(colId); const vectors = await this.getVectorsByCollection(colId);
const docVectors = vectors.filter(v => v.docId === docId); const docVectors = vectors.filter(v => v.docId === docId);
logDebug(`删除文档向量`, `${docId}: ${docVectors.length}`);
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
const tx = this.db!.transaction('vectors', 'readwrite'); const tx = this.db!.transaction('vectors', 'readwrite');
const store = tx.objectStore('vectors'); const store = tx.objectStore('vectors');
@@ -160,15 +165,15 @@ export class VectorStore {
const vectors = await this.getVectorsByCollection(colId); const vectors = await this.getVectorsByCollection(colId);
if (vectors.length === 0) return []; if (vectors.length === 0) return [];
let results: SearchResult[];
if (vectors.length < MIN_IVF_SIZE) { if (vectors.length < MIN_IVF_SIZE) {
return this._bruteForceSearch(vectors, queryEmbedding, topK); results = this._bruteForceSearch(vectors, queryEmbedding, topK);
} } else {
const index = await this._getIndex(colId, vectors); const index = await this._getIndex(colId, vectors);
if (!index) { results = index ? this._ivfSearch(vectors, index, queryEmbedding, topK) : this._bruteForceSearch(vectors, queryEmbedding, topK);
return this._bruteForceSearch(vectors, queryEmbedding, topK);
} }
return this._ivfSearch(vectors, index, queryEmbedding, topK); logDebug(`向量搜索`, `${colId}: ${vectors.length} 向量中检索到 ${results.length} 个结果`);
return results;
} }
private _bruteForceSearch(vectors: VectorItem[], queryEmbedding: number[], topK: number): SearchResult[] { private _bruteForceSearch(vectors: VectorItem[], queryEmbedding: number[], topK: number): SearchResult[] {