summaryrefslogtreecommitdiff
path: root/makima/frontend/src/hooks/useMeshChatHistory.ts
diff options
context:
space:
mode:
Diffstat (limited to 'makima/frontend/src/hooks/useMeshChatHistory.ts')
-rw-r--r--makima/frontend/src/hooks/useMeshChatHistory.ts133
1 files changed, 133 insertions, 0 deletions
diff --git a/makima/frontend/src/hooks/useMeshChatHistory.ts b/makima/frontend/src/hooks/useMeshChatHistory.ts
new file mode 100644
index 0000000..82c576d
--- /dev/null
+++ b/makima/frontend/src/hooks/useMeshChatHistory.ts
@@ -0,0 +1,133 @@
+import { useState, useCallback, useEffect } from "react";
+import {
+ getMeshChatHistory,
+ clearMeshChatHistory,
+ chatWithMeshContext,
+ type MeshChatMessageRecord,
+ type MeshChatContext,
+ type MeshChatResponse,
+ type LlmModel,
+} from "../lib/api";
+
+export interface MeshChatState {
+ conversationId: string | null;
+ messages: MeshChatMessageRecord[];
+ loading: boolean;
+ error: string | null;
+ sending: boolean;
+}
+
+export function useMeshChatHistory() {
+ const [state, setState] = useState<MeshChatState>({
+ conversationId: null,
+ messages: [],
+ loading: true,
+ error: null,
+ sending: false,
+ });
+
+ const fetchHistory = useCallback(async () => {
+ setState((prev) => ({ ...prev, loading: true, error: null }));
+ try {
+ const response = await getMeshChatHistory();
+ setState((prev) => ({
+ ...prev,
+ conversationId: response.conversationId,
+ messages: response.messages,
+ loading: false,
+ }));
+ } catch (e) {
+ setState((prev) => ({
+ ...prev,
+ error: e instanceof Error ? e.message : "Failed to fetch chat history",
+ loading: false,
+ }));
+ }
+ }, []);
+
+ const clearHistory = useCallback(async (): Promise<boolean> => {
+ setState((prev) => ({ ...prev, loading: true, error: null }));
+ try {
+ const response = await clearMeshChatHistory();
+ setState({
+ conversationId: response.conversationId,
+ messages: [],
+ loading: false,
+ error: null,
+ sending: false,
+ });
+ return true;
+ } catch (e) {
+ setState((prev) => ({
+ ...prev,
+ error: e instanceof Error ? e.message : "Failed to clear chat history",
+ loading: false,
+ }));
+ return false;
+ }
+ }, []);
+
+ const sendMessage = useCallback(
+ async (
+ message: string,
+ context: MeshChatContext,
+ model?: LlmModel
+ ): Promise<MeshChatResponse | null> => {
+ setState((prev) => ({ ...prev, sending: true, error: null }));
+
+ // Optimistically add user message (will be refetched after response)
+ const tempUserMessage: MeshChatMessageRecord = {
+ id: `temp-${Date.now()}`,
+ conversationId: state.conversationId || "",
+ role: "user",
+ content: message,
+ contextType: context.type,
+ contextTaskId: context.taskId || null,
+ toolCalls: null,
+ pendingQuestions: null,
+ createdAt: new Date().toISOString(),
+ };
+
+ setState((prev) => ({
+ ...prev,
+ messages: [...prev.messages, tempUserMessage],
+ }));
+
+ try {
+ const response = await chatWithMeshContext(message, context, model);
+
+ // Refetch to get the actual saved messages (with proper IDs)
+ await fetchHistory();
+
+ setState((prev) => ({ ...prev, sending: false }));
+ return response;
+ } catch (e) {
+ // Remove optimistic message on error
+ setState((prev) => ({
+ ...prev,
+ messages: prev.messages.filter((m) => m.id !== tempUserMessage.id),
+ error: e instanceof Error ? e.message : "Failed to send message",
+ sending: false,
+ }));
+ return null;
+ }
+ },
+ [state.conversationId, fetchHistory]
+ );
+
+ // Initial fetch on mount
+ useEffect(() => {
+ fetchHistory();
+ }, [fetchHistory]);
+
+ return {
+ conversationId: state.conversationId,
+ messages: state.messages,
+ loading: state.loading,
+ error: state.error,
+ sending: state.sending,
+ fetchHistory,
+ clearHistory,
+ sendMessage,
+ };
+}