Refactor message handling in chat view and improve API error handling

Simplify message construction and update logic in chat-view.js
Add abort controller support and improve error handling in ollama-client.js
Remove unused sanitizeFilePath function from utils.js
Export LogLevel enum in utils.js
Improve vault indexing to process all batches
This commit is contained in:
2026-05-07 00:06:19 +02:00
parent 75fa07f148
commit ccd2c3e0d8
5 changed files with 170 additions and 165 deletions
+9 -9
View File
@@ -239,14 +239,16 @@ class ChatView extends obsidian_1.ItemView {
if (context.length > MAX_CONTEXT_LENGTH) {
context = context.substring(0, MAX_CONTEXT_LENGTH) + '\n\n... (truncated)';
}
const systemContent = context
? `You are a helpful assistant.\n\nRelevant vault context:\n${context}`
: 'You are a helpful assistant.';
const systemMessage = {
role: 'system',
content: 'You are a helpful assistant.',
content: systemContent,
};
const userContent = context ? `${context}\n\n${userMessage}` : userMessage;
const userMessageWithContext = {
role: 'user',
content: userContent,
content: userMessage,
};
const messages = [
systemMessage,
@@ -368,13 +370,11 @@ class ChatView extends obsidian_1.ItemView {
});
}
}
// Update last message immutably — only if no tool calls were processed
// Update assistant message immutably — only if no tool calls were processed
if (toolCalls.length === 0) {
const lastMessageIndex = this.messages.length - 1;
if (lastMessageIndex >= 0) {
const lastMessage = { ...this.messages[lastMessageIndex], isStreaming: false };
this.messages = [...this.messages.slice(0, lastMessageIndex), lastMessage];
}
this.updateMessageById(assistantMessageId, {
isStreaming: false,
});
}
// Limit conversation history to prevent memory issues
if (this.messages.length > MAX_MESSAGE_HISTORY) {
-1
View File
@@ -9,4 +9,3 @@ exports.DEFAULT_SETTINGS = {
maxMessageHistory: 50,
lastIndexTime: 0,
};
// Model validation regex - lowercase letters, numbers, dashes, underscores only
+8 -1
View File
@@ -132,6 +132,8 @@ class OllamaClient {
return this.chatWithRetry(messages, tools, 0);
}
async chatWithRetry(messages, tools = [], attempt = 0) {
this.abortController = new AbortController();
try {
const response = await this.fetchFn(`${this.baseURL}/api/chat`, {
method: 'POST',
headers: {
@@ -143,6 +145,7 @@ class OllamaClient {
tools,
stream: false,
}),
signal: this.abortController.signal,
});
if (!response.ok) {
if (response.status >= 500 && attempt < this.maxRetries) {
@@ -154,7 +157,11 @@ class OllamaClient {
throw new types_1.ApiError(`Ollama API error: ${response.status}`, response.status);
}
const data = (await response.json());
return this.toOllamaMessage(data.message) ?? { role: 'assistant', content: '', tool_calls: [] };
return (this.toOllamaMessage(data.message) ?? { role: 'assistant', content: '', tool_calls: [] });
}
finally {
this.abortController = null;
}
}
throwIfOllamaError(parsed) {
if (parsed.error) {
+34 -32
View File
@@ -1,21 +1,17 @@
'use strict';
// src/utils.ts
Object.defineProperty(exports, '__esModule', { value: true });
exports.Logger = void 0;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Logger = exports.LogLevel = void 0;
exports.validateOllamaUrl = validateOllamaUrl;
exports.validateModelName = validateModelName;
exports.validatePluginSettings = validatePluginSettings;
exports.safeParseJson = safeParseJson;
exports.sanitizeFilePath = sanitizeFilePath;
// ==================== Logger ====================
var LogLevel;
(function (LogLevel) {
LogLevel[(LogLevel['DEBUG'] = 0)] = 'DEBUG';
LogLevel[(LogLevel['INFO'] = 1)] = 'INFO';
LogLevel[(LogLevel['WARN'] = 2)] = 'WARN';
LogLevel[(LogLevel['ERROR'] = 3)] = 'ERROR';
})(LogLevel || (LogLevel = {}));
LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
LogLevel[LogLevel["INFO"] = 1] = "INFO";
LogLevel[LogLevel["WARN"] = 2] = "WARN";
LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
})(LogLevel || (exports.LogLevel = LogLevel = {}));
const SEVERITY_ORDER = {
debug: LogLevel.DEBUG,
info: LogLevel.INFO,
@@ -27,7 +23,8 @@ class Logger {
if (typeof level === 'string') {
const lowerLevel = level.toLowerCase();
Logger.minLevel = SEVERITY_ORDER[lowerLevel] ?? LogLevel.DEBUG;
} else {
}
else {
Logger.minLevel = level;
}
}
@@ -69,7 +66,8 @@ function validateOllamaUrl(url) {
return { valid: false, error: 'Must be a valid HTTP or HTTPS URL' };
}
return { valid: true };
} catch {
}
catch {
return { valid: false, error: 'Must be a valid HTTP or HTTPS URL' };
}
}
@@ -120,7 +118,8 @@ function countNestingDepth(value, depth = 0) {
}
if (value !== null && typeof value === 'object') {
const entries = Object.values(value);
if (entries.length === 0) return depth;
if (entries.length === 0)
return depth;
return Math.max(...entries.map((item) => countNestingDepth(item, depth + 1)), depth);
}
return depth;
@@ -135,17 +134,28 @@ function safeParseJson(jsonString) {
let parsed;
try {
parsed = JSON.parse(jsonString);
} catch {
}
catch {
throw new Error('Invalid JSON');
}
// Check for dangerous prototype pollution patterns
const reStringified = JSON.stringify(parsed);
if (
reStringified.includes('constructor') ||
reStringified.includes('prototype') ||
reStringified.includes('__proto__') ||
reStringified.includes('function')
) {
// Check for dangerous prototype pollution patterns in object keys only
const checkDangerousPatterns = (obj) => {
if (typeof obj !== 'object' || obj === null) {
return false;
}
const dangerousKeys = ['constructor', 'prototype', '__proto__'];
if (dangerousKeys.some((key) => Object.keys(obj).includes(key))) {
return true;
}
// Recursively check nested objects
for (const key in obj) {
if (checkDangerousPatterns(obj[key])) {
return true;
}
}
return false;
};
if (checkDangerousPatterns(parsed)) {
throw new Error('dangerous code pattern detected');
}
// Check nesting depth
@@ -154,12 +164,4 @@ function safeParseJson(jsonString) {
}
return parsed;
}
// ==================== Path & File Utilities ====================
function sanitizeFilePath(path) {
if (path.includes('..')) {
throw new Error('Invalid path - cannot contain .. segments');
}
return path;
}
// ==================== Markdown Utilities ====================
-3
View File
@@ -57,9 +57,6 @@ class VaultIndexer {
}));
const validResults = batchResults.filter((result) => result !== null);
results.push(...validResults);
if (results.length >= 50) {
break;
}
}
return results;
}