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:
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
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 { escapeHtml, formatTime } from '../utils/utils.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`;
|
||||
});
|
||||
await nativeSaveFile(`${session.title}.md`, md);
|
||||
logSession('导出', `${session.title}.md`);
|
||||
}
|
||||
|
||||
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>';
|
||||
await nativeSaveFile(`${session.title}.html`, html);
|
||||
logSession('导出', `${session.title}.html`);
|
||||
}
|
||||
|
||||
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`;
|
||||
});
|
||||
await nativeSaveFile(`${session.title}.txt`, txt);
|
||||
logSession('导出', `${session.title}.txt`);
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ export async function updateRunningModels(): Promise<void> {
|
||||
const data = await api.psModels();
|
||||
if (!data.models || data.models.length === 0) {
|
||||
container.innerHTML = '<p class="text-muted">无运行中的模型</p>';
|
||||
logDebug('运行中模型: 0');
|
||||
return;
|
||||
}
|
||||
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>
|
||||
</div>
|
||||
`).join('');
|
||||
logDebug(`运行中模型: ${data.models.length}`, data.models.map(m => m.name).join(', '));
|
||||
} catch {
|
||||
container.innerHTML = '<p class="text-muted">无法获取运行信息</p>';
|
||||
}
|
||||
|
||||
@@ -190,6 +190,7 @@ export function openHistoryModal(): void {
|
||||
(document.querySelector('#historySearchInput') as HTMLInputElement).value = '';
|
||||
(document.querySelector('#historySearchClear') as HTMLElement).style.display = 'none';
|
||||
loadHistory();
|
||||
logSession('打开历史记录');
|
||||
historyModalEl.style.display = '';
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,7 @@ function updateRagBadge(): void {
|
||||
|
||||
async function openKBModal(): Promise<void> {
|
||||
kbModalEl.style.display = '';
|
||||
logRAG('打开知识库管理');
|
||||
await refreshCollections();
|
||||
await populateEmbedModels();
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ export async function initVectorStore(): Promise<VectorStore> {
|
||||
if (vectorStore) return vectorStore;
|
||||
vectorStore = new VectorStore();
|
||||
await vectorStore.init();
|
||||
logRAG('向量存储已初始化');
|
||||
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[][]> {
|
||||
const results: number[][] = [];
|
||||
const batchSize = 5;
|
||||
logDebug(`嵌入批次`, `${texts.length} 个文本, 模型: ${model}`);
|
||||
for (let i = 0; i < texts.length; i += batchSize) {
|
||||
const batch = texts.slice(i, i + batchSize);
|
||||
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 allVectors = await vs.getVectorsByCollection(colId);
|
||||
const docVectors = allVectors.filter(v => v.docId === docId);
|
||||
logRAG(`删除文档`, `${docId} (${docVectors.length} 个分块)`);
|
||||
|
||||
await vs.deleteVectorsByDocument(colId, docId);
|
||||
|
||||
@@ -119,6 +122,7 @@ export async function retrieveContext(query: string, colId: string, topK = 5): P
|
||||
const embedModel = col.embeddingModel;
|
||||
if (!embedModel) throw new Error('未配置嵌入模型');
|
||||
|
||||
logRAG('开始检索', col.name);
|
||||
const queryEmbedding = await embedText(query, embedModel);
|
||||
const results = await vs.search(colId, queryEmbedding, topK);
|
||||
if (results.length === 0) return { context: '', results: [] };
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import type { VectorCollection, VectorItem, SearchResult } from '../types.js';
|
||||
import { logRAG, logDebug } from './log-service.js';
|
||||
|
||||
const MIN_IVF_SIZE = 200;
|
||||
const DEFAULT_K = 20;
|
||||
@@ -30,7 +31,7 @@ export class VectorStore {
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = indexedDB.open(this.dbName, 2);
|
||||
req.onerror = () => reject(req.error);
|
||||
req.onsuccess = () => { this.db = req.result; resolve(); };
|
||||
req.onsuccess = () => { this.db = req.result; logDebug('向量数据库已连接'); resolve(); };
|
||||
req.onupgradeneeded = (e) => {
|
||||
const db = (e.target as IDBOpenDBRequest).result;
|
||||
if (!db.objectStoreNames.contains('vectors')) {
|
||||
@@ -60,7 +61,7 @@ export class VectorStore {
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
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);
|
||||
});
|
||||
}
|
||||
@@ -92,6 +93,7 @@ export class VectorStore {
|
||||
|
||||
async deleteCollection(colId: string): Promise<void> {
|
||||
const vectors = await this.getVectorsByCollection(colId);
|
||||
logRAG(`删除集合`, `${colId} (${vectors.length} 向量)`);
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const tx = this.db!.transaction(['vectors', 'collections', 'indexes'], 'readwrite');
|
||||
const vStore = tx.objectStore('vectors');
|
||||
@@ -107,6 +109,8 @@ export class VectorStore {
|
||||
|
||||
async addVectors(items: VectorItem[]): Promise<void> {
|
||||
if (items.length === 0) return;
|
||||
const colId = items[0].collectionId;
|
||||
logDebug(`写入向量`, `${colId}: ${items.length} 条`);
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const tx = this.db!.transaction('vectors', 'readwrite');
|
||||
const store = tx.objectStore('vectors');
|
||||
@@ -142,6 +146,7 @@ export class VectorStore {
|
||||
async deleteVectorsByDocument(colId: string, docId: string): Promise<void> {
|
||||
const vectors = await this.getVectorsByCollection(colId);
|
||||
const docVectors = vectors.filter(v => v.docId === docId);
|
||||
logDebug(`删除文档向量`, `${docId}: ${docVectors.length} 条`);
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const tx = this.db!.transaction('vectors', 'readwrite');
|
||||
const store = tx.objectStore('vectors');
|
||||
@@ -160,15 +165,15 @@ export class VectorStore {
|
||||
const vectors = await this.getVectorsByCollection(colId);
|
||||
if (vectors.length === 0) return [];
|
||||
|
||||
let results: SearchResult[];
|
||||
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);
|
||||
if (!index) {
|
||||
return this._bruteForceSearch(vectors, queryEmbedding, topK);
|
||||
results = index ? this._ivfSearch(vectors, index, queryEmbedding, topK) : 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[] {
|
||||
|
||||
Reference in New Issue
Block a user