Project-aware assist, link suggestions, project-scoped RAG, semantic search tool, SSE race fix
- Writing assistant: inject project notes as context (definition-tagged first), wikilink suggestions - Link suggestions: server-side endpoint finds unlinked term occurrences, NoteEditorView sidebar panel - Project-scoped RAG: ChatView ProjectSelector filters semantic+keyword search to selected project - Semantic search tool: LLM search_notes upgraded to hybrid semantic (0.40 threshold) + keyword merge - SSE race condition fix: drain remaining events after stream loop exits in chat.py and notes.py - RAG_AUTO_SNIPPET raised 800→4000; sidebar include uses full note body; MAX_BODY_CHARS 8000→24000 - Enter-to-submit on writing assistant instruction textareas (note and task editors) - DiffView: equal-line collapsing with 3-line context around changes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -155,6 +155,7 @@ export const useChatStore = defineStore("chat", () => {
|
||||
think = false,
|
||||
contextNoteTitle?: string,
|
||||
excludeNoteIds?: number[],
|
||||
ragProjectId?: number | null,
|
||||
) {
|
||||
if (!currentConversation.value) return;
|
||||
|
||||
@@ -204,6 +205,7 @@ export const useChatStore = defineStore("chat", () => {
|
||||
include_note_ids: includeNoteIds?.length ? includeNoteIds : undefined,
|
||||
excluded_note_ids: excludeNoteIds?.length ? excludeNoteIds : undefined,
|
||||
think,
|
||||
rag_project_id: ragProjectId ?? undefined,
|
||||
},
|
||||
);
|
||||
assistantMessageId = resp.assistant_message_id;
|
||||
@@ -348,6 +350,37 @@ export const useChatStore = defineStore("chat", () => {
|
||||
}
|
||||
}
|
||||
|
||||
async function reconnectIfGenerating(convId: number): Promise<void> {
|
||||
// Skip if a stream is already active for this conversation
|
||||
if (isStreamingConv(convId)) return;
|
||||
|
||||
const conv = currentConversation.value;
|
||||
if (!conv || conv.id !== convId) return;
|
||||
|
||||
// Find the last assistant message still in generating state
|
||||
const lastMsg = [...conv.messages].reverse().find(
|
||||
(m) => m.role === "assistant" && m.status === "generating"
|
||||
);
|
||||
if (!lastMsg) return;
|
||||
|
||||
// Remove the partial message — the done event will re-add the final version
|
||||
conv.messages = conv.messages.filter((m) => m.id !== lastMsg.id);
|
||||
|
||||
const s = _getOrInitStream(convId);
|
||||
s.streaming = true;
|
||||
s.content = "";
|
||||
s.thinking = "";
|
||||
s.toolCalls = [];
|
||||
s.status = "Reconnecting...";
|
||||
s.pendingTool = null;
|
||||
s.contextMeta = null;
|
||||
|
||||
// Reconnect from the beginning — the buffer replays all buffered events.
|
||||
// If the buffer is gone (>60s stale), _streamGeneration will exhaust retries
|
||||
// then fall back to fetchConversation to show the final saved content.
|
||||
await _streamGeneration(convId, lastMsg.id);
|
||||
}
|
||||
|
||||
async function confirmTool(confirmed: boolean) {
|
||||
const convId = currentConversation.value?.id;
|
||||
if (!convId) return;
|
||||
@@ -461,6 +494,7 @@ export const useChatStore = defineStore("chat", () => {
|
||||
deleteConversation,
|
||||
updateTitle,
|
||||
sendMessage,
|
||||
reconnectIfGenerating,
|
||||
confirmTool,
|
||||
cancelGeneration,
|
||||
saveMessageAsNote,
|
||||
|
||||
Reference in New Issue
Block a user