mirror of
https://github.com/andy-stack/vaultkeeper-ai.git
synced 2026-07-22 06:42:03 +00:00
fix: prevent race conditions in chat lifecycle and conversation management
- Add abort flag to prevent semaphore double-release during stop - Stop active chats before deleting conversations to avoid state corruption - Treat empty search terms as explicit request for all files
This commit is contained in:
parent
1a57bb4872
commit
6be51ebaae
5 changed files with 23 additions and 3 deletions
|
|
@ -9,6 +9,8 @@ export const SearchVaultFiles: IAIFunctionDefinition = {
|
|||
**IMPORTANT: When a search returns 0 results, a complete list of all vault files will be automatically returned.**
|
||||
This allows you to verify the search scope and attempt alternative search strategies.
|
||||
|
||||
Use an empty search term to retrieve a complete list of all vault files explicitly.
|
||||
|
||||
Use this function when you need to:
|
||||
- Find files based on what's written INSIDE them
|
||||
- Search for specific concepts, keywords, or text within notes
|
||||
|
|
|
|||
|
|
@ -7,12 +7,14 @@
|
|||
import { conversationStore } from '../Stores/conversationStore';
|
||||
import type { ConversationHistoryModal } from 'Modals/ConversationHistoryModal';
|
||||
import { openPluginSettings } from 'Helpers/Helpers';
|
||||
import type { ChatService } from 'Services/ChatService';
|
||||
|
||||
export let leaf: WorkspaceLeaf;
|
||||
export let onNewConversation: (() => void) | undefined = undefined;
|
||||
|
||||
const plugin = Resolve<AIAgentPlugin>(Services.AIAgentPlugin);
|
||||
const conversationService = Resolve<ConversationFileSystemService>(Services.ConversationFileSystemService);
|
||||
const chatService: ChatService = Resolve<ChatService>(Services.ChatService);
|
||||
|
||||
function startNewConversation() {
|
||||
conversationService.resetCurrentConversation();
|
||||
|
|
@ -21,6 +23,7 @@
|
|||
}
|
||||
|
||||
async function deleteCurrentConversation() {
|
||||
chatService.stop();
|
||||
await conversationService.deleteCurrentConversation();
|
||||
conversationStore.reset();
|
||||
onNewConversation?.();
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import type { FileSystemService } from 'Services/FileSystemService';
|
|||
import { dateToString } from 'Helpers/Helpers';
|
||||
import { conversationStore } from 'Stores/conversationStore';
|
||||
import { Selector } from 'Enums/Selector';
|
||||
import type { ChatService } from 'Services/ChatService';
|
||||
|
||||
interface ListItem {
|
||||
id: string;
|
||||
|
|
@ -22,6 +23,7 @@ export class ConversationHistoryModal extends Modal {
|
|||
|
||||
private readonly conversationFileSystemService: ConversationFileSystemService = Resolve<ConversationFileSystemService>(Services.ConversationFileSystemService);
|
||||
private readonly fileSystemService: FileSystemService = Resolve<FileSystemService>(Services.FileSystemService);
|
||||
private readonly chatService: ChatService = Resolve<ChatService>(Services.ChatService);
|
||||
|
||||
private component: Record<string, any> | null = null;
|
||||
private items: ListItem[];
|
||||
|
|
@ -77,6 +79,8 @@ export class ConversationHistoryModal extends Modal {
|
|||
}
|
||||
|
||||
async handleDelete(itemIds: string[]) {
|
||||
this.chatService.stop();
|
||||
|
||||
const itemsToDelete = this.items.filter(item => itemIds.includes(item.id));
|
||||
|
||||
let shouldResetChat = false;
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ export class AIFunctionService {
|
|||
}
|
||||
|
||||
private async searchVaultFiles(searchTerm: string): Promise<object> {
|
||||
const matches: SearchMatch[] = await this.fileSystemService.searchVaultFiles(searchTerm);
|
||||
|
||||
const matches: SearchMatch[] = searchTerm.trim() === "" ? [] : await this.fileSystemService.searchVaultFiles(searchTerm);
|
||||
|
||||
if (matches.length === 0) {
|
||||
const files: TFile[] = await this.fileSystemService.listFilesInDirectory("/");
|
||||
return files.map((file) => ({
|
||||
|
|
|
|||
|
|
@ -19,8 +19,12 @@ export class ChatService {
|
|||
private ai: IAIClass | undefined;
|
||||
private conversationService: ConversationFileSystemService;
|
||||
private aiFunctionService: AIFunctionService;
|
||||
|
||||
private abortController: AbortController | null = null;
|
||||
private isAborting: boolean = false;
|
||||
|
||||
private semaphore: Semaphore;
|
||||
private semaphoreHeld: boolean = false;
|
||||
|
||||
constructor() {
|
||||
this.conversationService = Resolve<ConversationFileSystemService>(Services.ConversationFileSystemService);
|
||||
|
|
@ -37,6 +41,8 @@ export class ChatService {
|
|||
return conversation;
|
||||
}
|
||||
|
||||
this.semaphoreHeld = true;
|
||||
|
||||
try {
|
||||
if (userRequest.trim() === "") {
|
||||
return conversation;
|
||||
|
|
@ -74,12 +80,17 @@ export class ChatService {
|
|||
} finally {
|
||||
callbacks.onThoughtUpdate(null);
|
||||
this.abortController = null;
|
||||
this.semaphore.release();
|
||||
this.isAborting = false;
|
||||
if (this.semaphoreHeld) {
|
||||
this.semaphoreHeld = false;
|
||||
this.semaphore.release();
|
||||
}
|
||||
callbacks.onComplete();
|
||||
}
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
this.isAborting = true;
|
||||
if (this.abortController) {
|
||||
this.abortController.abort();
|
||||
this.abortController = null;
|
||||
|
|
|
|||
Loading…
Reference in a new issue