negoptimAi / frontend /src /hooks /useChat.ts
samir12321's picture
Initial commit: Negoptim AI RAG chatbot (backend + frontend + deploy config)
af404c9
Raw
History Blame Contribute Delete
3.19 kB
"use client";
import { useState, useCallback, useRef, useEffect } from "react";
import { v4 as uuidv4 } from "uuid";
import { Message, ChatResponse, EmailActionOptions } from "@/types/chat";
export function useChat() {
const [messages, setMessages] = useState<Message[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [preferredModel, setPreferredModel] = useState<string>("llama-3.1-8b-instant");
const sessionId = useRef<string>(uuidv4());
useEffect(() => {
if (typeof window !== "undefined") {
const saved = localStorage.getItem("preferred_model");
if (saved) {
setPreferredModel(saved);
}
}
}, []);
const selectModel = useCallback((model: string) => {
setPreferredModel(model);
if (typeof window !== "undefined") {
localStorage.setItem("preferred_model", model);
}
}, []);
const sendMessage = useCallback(async (content: string, options?: EmailActionOptions) => {
const trimmed = content.trim();
// Allow action-only turns (e.g. Send/Cancel buttons) even with empty text.
if ((!trimmed && !options?.emailAction) || isLoading) return;
const display = options?.displayContent ?? trimmed;
const userMsg: Message = {
id: uuidv4(),
role: "user",
content: display,
timestamp: new Date(),
};
setMessages((prev) => [...prev, userMsg]);
setIsLoading(true);
setError(null);
try {
const res = await fetch("/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
session_id: sessionId.current,
message: trimmed || display,
model: preferredModel,
email_action: options?.emailAction,
email_payload: options?.emailPayload,
}),
});
if (!res.ok) {
const data = await res.json().catch(() => ({}));
throw new Error(data.detail ?? `Server error ${res.status}`);
}
const data: ChatResponse = await res.json();
const assistantMsg: Message = {
id: uuidv4(),
role: "assistant",
content: data.answer,
sources: data.sources,
timestamp: new Date(),
responseType: data.response_type,
structuredData: data.structured_data,
tokenUsage: data.token_usage,
modelUsed: data.model_used,
rateLimit: data.rate_limit,
};
setMessages((prev) => [...prev, assistantMsg]);
} catch (err) {
const message = err instanceof Error ? err.message : "An unexpected error occurred";
setError(message);
} finally {
setIsLoading(false);
}
}, [isLoading, preferredModel]);
const clearConversation = useCallback(async () => {
const sid = sessionId.current;
sessionId.current = uuidv4();
setMessages([]);
setError(null);
await fetch(`/api/chat/${sid}`, { method: "DELETE" }).catch(() => null);
}, []);
return {
messages,
isLoading,
error,
sendMessage,
clearConversation,
hasMessages: messages.length > 0,
preferredModel,
setPreferredModel: selectModel,
};
}