chore: initialize EverOS 1.0.0
md-first memory extraction framework for AI agents. Markdown is the single source of truth; SQLite holds state and LanceDB provides the rebuildable vector + BM25 + scalar index. The codebase follows a single-direction DDD layering (entrypoints -> service -> memory -> infra, with component / core / config cross-cutting) enforced by import-linter. Engineering surface: - Coding conventions in .claude/rules/ (path-scoped) and workflows in .claude/skills/ (/commit, /new-branch, /pr). - GitHub Actions CI runs make lint + test + integration; pre-commit mirrors the gates locally (ruff, hygiene hooks, gitlint commit-msg). - Commit messages follow Conventional Commits, enforced by gitlint. - make lint also enforces datetime two-zone discipline and OpenAPI drift.
This commit is contained in:
143
use-cases/game-of-throne-demo/frontend/src/hooks/useChat.ts
Normal file
143
use-cases/game-of-throne-demo/frontend/src/hooks/useChat.ts
Normal file
@ -0,0 +1,143 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { Message, Memory } from '../types';
|
||||
import { sendChatMessage } from '../services/api';
|
||||
|
||||
const STORAGE_KEY = 'chat_history';
|
||||
const MEMORIES_STORAGE_KEY = 'chat_memories';
|
||||
|
||||
export function useChat() {
|
||||
const [messages, setMessages] = useState<Message[]>([]);
|
||||
const [currentMemories, setCurrentMemories] = useState<Memory[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isRetrievingMemories, setIsRetrievingMemories] = useState(false);
|
||||
const [isLoadingFollowUps, setIsLoadingFollowUps] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [streamingContent, setStreamingContent] = useState('');
|
||||
|
||||
// Load chat history and memories from localStorage on mount
|
||||
useEffect(() => {
|
||||
const storedMessages = localStorage.getItem(STORAGE_KEY);
|
||||
if (storedMessages) {
|
||||
try {
|
||||
setMessages(JSON.parse(storedMessages));
|
||||
} catch (e) {
|
||||
console.error('Error loading chat history:', e);
|
||||
}
|
||||
}
|
||||
|
||||
const storedMemories = localStorage.getItem(MEMORIES_STORAGE_KEY);
|
||||
if (storedMemories) {
|
||||
try {
|
||||
setCurrentMemories(JSON.parse(storedMemories));
|
||||
} catch (e) {
|
||||
console.error('Error loading memories:', e);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Save chat history to localStorage
|
||||
const saveChatHistory = useCallback((msgs: Message[]) => {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(msgs));
|
||||
}, []);
|
||||
|
||||
// Save memories to localStorage
|
||||
const saveMemories = useCallback((memories: Memory[]) => {
|
||||
localStorage.setItem(MEMORIES_STORAGE_KEY, JSON.stringify(memories));
|
||||
}, []);
|
||||
|
||||
const sendMessage = useCallback(
|
||||
async (content: string) => {
|
||||
if (!content.trim() || isLoading) return;
|
||||
|
||||
setError(null);
|
||||
setIsLoading(true);
|
||||
setIsRetrievingMemories(true);
|
||||
setStreamingContent('');
|
||||
setCurrentMemories([]); // Clear memories to show loading state
|
||||
|
||||
// Add user message
|
||||
const userMessage: Message = { role: 'user', content };
|
||||
const updatedMessages = [...messages, userMessage];
|
||||
setMessages(updatedMessages);
|
||||
saveChatHistory(updatedMessages);
|
||||
|
||||
try {
|
||||
let assistantContent = '';
|
||||
|
||||
await sendChatMessage(content, messages, {
|
||||
onMemories: (memories) => {
|
||||
setCurrentMemories(memories);
|
||||
saveMemories(memories);
|
||||
setIsRetrievingMemories(false);
|
||||
},
|
||||
onToken: (token) => {
|
||||
assistantContent += token;
|
||||
setStreamingContent(assistantContent);
|
||||
},
|
||||
onDone: () => {
|
||||
// Add complete assistant message (follow-ups will be added when received)
|
||||
const assistantMessage: Message = {
|
||||
role: 'assistant',
|
||||
content: assistantContent,
|
||||
};
|
||||
const finalMessages = [...updatedMessages, assistantMessage];
|
||||
setMessages(finalMessages);
|
||||
saveChatHistory(finalMessages);
|
||||
setStreamingContent('');
|
||||
setIsLoading(false);
|
||||
setIsLoadingFollowUps(true); // Start loading follow-ups
|
||||
},
|
||||
onFollowUps: (followUps) => {
|
||||
setIsLoadingFollowUps(false);
|
||||
// Update the last assistant message with follow-ups
|
||||
setMessages(prev => {
|
||||
if (prev.length === 0) return prev;
|
||||
const updated = [...prev];
|
||||
const lastIndex = updated.length - 1;
|
||||
if (updated[lastIndex].role === 'assistant') {
|
||||
updated[lastIndex] = { ...updated[lastIndex], followUps };
|
||||
saveChatHistory(updated);
|
||||
}
|
||||
return updated;
|
||||
});
|
||||
},
|
||||
onError: (errorMessage) => {
|
||||
setError(errorMessage);
|
||||
setIsLoading(false);
|
||||
setIsRetrievingMemories(false);
|
||||
setIsLoadingFollowUps(false);
|
||||
setStreamingContent('');
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'An error occurred');
|
||||
setIsLoading(false);
|
||||
setIsRetrievingMemories(false);
|
||||
setIsLoadingFollowUps(false);
|
||||
setStreamingContent('');
|
||||
}
|
||||
},
|
||||
[messages, isLoading, saveChatHistory, saveMemories]
|
||||
);
|
||||
|
||||
const clearChat = useCallback(() => {
|
||||
setMessages([]);
|
||||
setCurrentMemories([]);
|
||||
setError(null);
|
||||
setStreamingContent('');
|
||||
localStorage.removeItem(STORAGE_KEY);
|
||||
localStorage.removeItem(MEMORIES_STORAGE_KEY);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
messages,
|
||||
currentMemories,
|
||||
isLoading,
|
||||
isRetrievingMemories,
|
||||
isLoadingFollowUps,
|
||||
error,
|
||||
streamingContent,
|
||||
sendMessage,
|
||||
clearChat,
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,170 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { Message, Memory, ComparisonState } from '../types';
|
||||
import { sendCompareMessage } from '../services/api';
|
||||
|
||||
const STORAGE_KEY = 'compare_chat_history';
|
||||
const MEMORIES_STORAGE_KEY = 'compare_chat_memories';
|
||||
|
||||
const initialComparisonState: ComparisonState = {
|
||||
withMemory: { content: '', isStreaming: false, isDone: false },
|
||||
withoutMemory: { content: '', isStreaming: false, isDone: false },
|
||||
};
|
||||
|
||||
export function useCompareChat() {
|
||||
const [messages, setMessages] = useState<Message[]>([]);
|
||||
const [currentMemories, setCurrentMemories] = useState<Memory[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isRetrievingMemories, setIsRetrievingMemories] = useState(false);
|
||||
const [isLoadingFollowUps, setIsLoadingFollowUps] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [comparison, setComparison] = useState<ComparisonState>(initialComparisonState);
|
||||
|
||||
// Load chat history and memories from localStorage on mount
|
||||
useEffect(() => {
|
||||
const storedMessages = localStorage.getItem(STORAGE_KEY);
|
||||
if (storedMessages) {
|
||||
try {
|
||||
setMessages(JSON.parse(storedMessages));
|
||||
} catch (e) {
|
||||
console.error('Error loading chat history:', e);
|
||||
}
|
||||
}
|
||||
|
||||
const storedMemories = localStorage.getItem(MEMORIES_STORAGE_KEY);
|
||||
if (storedMemories) {
|
||||
try {
|
||||
setCurrentMemories(JSON.parse(storedMemories));
|
||||
} catch (e) {
|
||||
console.error('Error loading memories:', e);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Save chat history to localStorage
|
||||
const saveChatHistory = useCallback((msgs: Message[]) => {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(msgs));
|
||||
}, []);
|
||||
|
||||
// Save memories to localStorage
|
||||
const saveMemories = useCallback((memories: Memory[]) => {
|
||||
localStorage.setItem(MEMORIES_STORAGE_KEY, JSON.stringify(memories));
|
||||
}, []);
|
||||
|
||||
const sendMessage = useCallback(
|
||||
async (content: string) => {
|
||||
if (!content.trim() || isLoading) return;
|
||||
|
||||
setError(null);
|
||||
setIsLoading(true);
|
||||
setIsRetrievingMemories(true);
|
||||
setCurrentMemories([]); // Clear memories to show loading state
|
||||
|
||||
// Reset comparison state for new message
|
||||
setComparison({
|
||||
withMemory: { content: '', isStreaming: true, isDone: false },
|
||||
withoutMemory: { content: '', isStreaming: true, isDone: false },
|
||||
});
|
||||
|
||||
// Add user message
|
||||
const userMessage: Message = { role: 'user', content };
|
||||
const updatedMessages = [...messages, userMessage];
|
||||
setMessages(updatedMessages);
|
||||
saveChatHistory(updatedMessages);
|
||||
|
||||
try {
|
||||
let withMemoryContent = '';
|
||||
let withoutMemoryContent = '';
|
||||
let pendingFollowUps: string[] | undefined;
|
||||
|
||||
await sendCompareMessage(content, messages, {
|
||||
onMemories: (memories) => {
|
||||
setCurrentMemories(memories);
|
||||
saveMemories(memories);
|
||||
setIsRetrievingMemories(false);
|
||||
},
|
||||
onToken: (stream, token) => {
|
||||
if (stream === 'withMemory') {
|
||||
withMemoryContent += token;
|
||||
setComparison(prev => ({
|
||||
...prev,
|
||||
withMemory: { ...prev.withMemory, content: withMemoryContent },
|
||||
}));
|
||||
} else {
|
||||
withoutMemoryContent += token;
|
||||
setComparison(prev => ({
|
||||
...prev,
|
||||
withoutMemory: { ...prev.withoutMemory, content: withoutMemoryContent },
|
||||
}));
|
||||
}
|
||||
},
|
||||
onStreamDone: (stream) => {
|
||||
setComparison(prev => {
|
||||
const updated = {
|
||||
...prev,
|
||||
[stream]: { ...prev[stream], isStreaming: false, isDone: true },
|
||||
};
|
||||
// When withMemory stream is done, start loading follow-ups indicator
|
||||
if (stream === 'withMemory') {
|
||||
setIsLoadingFollowUps(true);
|
||||
}
|
||||
return updated;
|
||||
});
|
||||
},
|
||||
onFollowUps: (followUps) => {
|
||||
// Store follow-ups to be added when assistant message is created
|
||||
pendingFollowUps = followUps;
|
||||
setIsLoadingFollowUps(false);
|
||||
},
|
||||
onComplete: () => {
|
||||
// Add complete assistant message with follow-ups
|
||||
const assistantMessage: Message = {
|
||||
role: 'assistant',
|
||||
content: withMemoryContent,
|
||||
followUps: pendingFollowUps,
|
||||
};
|
||||
const finalMessages = [...updatedMessages, assistantMessage];
|
||||
setMessages(finalMessages);
|
||||
saveChatHistory(finalMessages);
|
||||
setIsLoading(false);
|
||||
setIsLoadingFollowUps(false);
|
||||
},
|
||||
onError: (errorMessage) => {
|
||||
setError(errorMessage);
|
||||
setIsLoading(false);
|
||||
setIsRetrievingMemories(false);
|
||||
setIsLoadingFollowUps(false);
|
||||
setComparison(initialComparisonState);
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'An error occurred');
|
||||
setIsLoading(false);
|
||||
setIsRetrievingMemories(false);
|
||||
setIsLoadingFollowUps(false);
|
||||
setComparison(initialComparisonState);
|
||||
}
|
||||
},
|
||||
[messages, isLoading, saveChatHistory, saveMemories]
|
||||
);
|
||||
|
||||
const clearChat = useCallback(() => {
|
||||
setMessages([]);
|
||||
setCurrentMemories([]);
|
||||
setError(null);
|
||||
setComparison(initialComparisonState);
|
||||
localStorage.removeItem(STORAGE_KEY);
|
||||
localStorage.removeItem(MEMORIES_STORAGE_KEY);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
messages,
|
||||
currentMemories,
|
||||
isLoading,
|
||||
isRetrievingMemories,
|
||||
isLoadingFollowUps,
|
||||
error,
|
||||
comparison,
|
||||
sendMessage,
|
||||
clearChat,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user