Merge 02f573907022fbac168d45e67ca0fbe08c6924a0 into da1123d84baa8ced8a9427f866b068b4652e7d9c

This commit is contained in:
sjiampojamarn
2025-04-07 23:50:34 +05:30
committed by GitHub
6 changed files with 129 additions and 3 deletions

View File

@ -29,6 +29,7 @@ type Message = {
messageId: string; messageId: string;
chatId: string; chatId: string;
content: string; content: string;
userSessionId: string;
}; };
type ChatModel = { type ChatModel = {
@ -138,6 +139,7 @@ const handleHistorySave = async (
where: eq(chats.id, message.chatId), where: eq(chats.id, message.chatId),
}); });
let currentDate = new Date();
if (!chat) { if (!chat) {
await db await db
.insert(chats) .insert(chats)
@ -147,6 +149,8 @@ const handleHistorySave = async (
createdAt: new Date().toString(), createdAt: new Date().toString(),
focusMode: focusMode, focusMode: focusMode,
files: files.map(getFileDetails), files: files.map(getFileDetails),
userSessionId: message.userSessionId,
timestamp: currentDate.toISOString(),
}) })
.execute(); .execute();
} }

View File

@ -1,10 +1,47 @@
import db from '@/lib/db'; import db from '@/lib/db';
import { chats } from '@/lib/db/schema';
import { eq, sql} from 'drizzle-orm';
export const GET = async (req: Request) => { export const GET = async (req: Request) => {
try { try {
let chats = await db.query.chats.findMany(); // get header from request
chats = chats.reverse(); const headers = await req.headers;
return Response.json({ chats: chats }, { status: 200 }); const userSessionId = headers.get('user-session-id')?.toString() ?? '';
const maxRecordLimit = parseInt(headers.get('max-record-limit') || '20', 10);
if (userSessionId == '') {
return Response.json({ chats: {} }, { status: 200 });
}
let chatsRes = await db.query.chats.findMany({
where: eq(chats.userSessionId, userSessionId),
});
chatsRes = chatsRes.reverse();
// Keep only the latest records in the database. Delete older records.
if (chatsRes.length > maxRecordLimit) {
const deleteChatsQuery = sql`DELETE FROM chats
WHERE userSessionId = ${userSessionId} AND (
timestamp IS NULL OR
timestamp NOT in (
SELECT timestamp FROM chats
WHERE userSessionId = ${userSessionId}
ORDER BY timestamp DESC
LIMIT ${maxRecordLimit}
)
)
`;
await db.run(deleteChatsQuery);
// Delete messages that no longer link with the chat from the database.
const deleteMessagesQuery = sql`DELETE FROM messages
WHERE chatId NOT IN (
SELECT id FROM chats
)
`;
await db.run(deleteMessagesQuery);
}
return Response.json({ chats: chatsRes }, { status: 200 });
} catch (err) { } catch (err) {
console.error('Error in getting chats: ', err); console.error('Error in getting chats: ', err);
return Response.json( return Response.json(

View File

@ -1,5 +1,6 @@
'use client'; 'use client';
import crypto from 'crypto';
import DeleteChat from '@/components/DeleteChat'; import DeleteChat from '@/components/DeleteChat';
import { cn, formatTimeDifference } from '@/lib/utils'; import { cn, formatTimeDifference } from '@/lib/utils';
import { BookOpenText, ClockIcon, Delete, ScanEye } from 'lucide-react'; import { BookOpenText, ClockIcon, Delete, ScanEye } from 'lucide-react';
@ -21,10 +22,34 @@ const Page = () => {
const fetchChats = async () => { const fetchChats = async () => {
setLoading(true); setLoading(true);
let userSessionId = localStorage.getItem('userSessionId');
if (!userSessionId) {
userSessionId = crypto.randomBytes(20).toString('hex');
localStorage.setItem('userSessionId', userSessionId)
}
// Get maxRecordLimit from localStorage or set default
let maxRecordLimit = localStorage.getItem('maxRecordLimit');
if (!maxRecordLimit) {
maxRecordLimit = '20';
localStorage.setItem('maxRecordLimit', maxRecordLimit);
} else {
let valueInt = parseInt(maxRecordLimit, 10) || 20;
if (valueInt < 1) {
valueInt = 1;
} else if (valueInt > 100) {
valueInt = 100;
}
maxRecordLimit = valueInt.toString();
localStorage.setItem('maxRecordLimit', maxRecordLimit);
}
const res = await fetch(`/api/chats`, { const res = await fetch(`/api/chats`, {
method: 'GET', method: 'GET',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'user-session-id': userSessionId!,
'max-record-limit': maxRecordLimit,
}, },
}); });

View File

@ -24,6 +24,7 @@ interface SettingsType {
customOpenaiApiKey: string; customOpenaiApiKey: string;
customOpenaiApiUrl: string; customOpenaiApiUrl: string;
customOpenaiModelName: string; customOpenaiModelName: string;
maxRecordLimit: string;
} }
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> { interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
@ -146,6 +147,7 @@ const Page = () => {
const [automaticVideoSearch, setAutomaticVideoSearch] = useState(false); const [automaticVideoSearch, setAutomaticVideoSearch] = useState(false);
const [systemInstructions, setSystemInstructions] = useState<string>(''); const [systemInstructions, setSystemInstructions] = useState<string>('');
const [savingStates, setSavingStates] = useState<Record<string, boolean>>({}); const [savingStates, setSavingStates] = useState<Record<string, boolean>>({});
const [maxRecordLimit, setMaxRecordLimit] = useState<string>('20');
useEffect(() => { useEffect(() => {
const fetchConfig = async () => { const fetchConfig = async () => {
@ -208,6 +210,8 @@ const Page = () => {
setSystemInstructions(localStorage.getItem('systemInstructions')!); setSystemInstructions(localStorage.getItem('systemInstructions')!);
setMaxRecordLimit(localStorage.getItem('maxRecordLimit') || data.maxRecordLimit || '20');
setIsLoading(false); setIsLoading(false);
}; };
@ -366,6 +370,15 @@ const Page = () => {
localStorage.setItem('embeddingModel', value); localStorage.setItem('embeddingModel', value);
} else if (key === 'systemInstructions') { } else if (key === 'systemInstructions') {
localStorage.setItem('systemInstructions', value); localStorage.setItem('systemInstructions', value);
} else if (key === 'maxRecordLimit') {
let valueInt = parseInt(value, 10) || 20;
if (valueInt < 1) {
valueInt = 1;
} else if (valueInt > 100) {
valueInt = 100;
}
setMaxRecordLimit(valueInt.toString());
localStorage.setItem('maxRecordLimit', valueInt.toString());
} }
} catch (err) { } catch (err) {
console.error('Failed to save:', err); console.error('Failed to save:', err);
@ -860,6 +873,37 @@ const Page = () => {
</div> </div>
</div> </div>
</SettingsSection> </SettingsSection>
<SettingsSection title="Chat History">
<div className="flex flex-col space-y-4">
<div className="flex flex-col space-y-1">
<p className="text-black/70 dark:text-white/70 text-sm">
Maximum Chat History Records
</p>
<div className="flex items-center space-x-2">
<Input
type="number"
min="1"
max="100"
pattern="[0-9]*"
inputMode="numeric"
value={maxRecordLimit}
isSaving={savingStates['maxRecordLimit']}
onChange={(e) => {
setMaxRecordLimit(e.target.value);
}}
onSave={(value) => saveConfig('maxRecordLimit', value)}
/>
<span className="text-black/60 dark:text-white/60 text-sm">
records
</span>
</div>
<p className="text-xs text-black/60 dark:text-white/60 mt-1">
Maximum number of chat records to keep in history. Older records will be automatically deleted.
</p>
</div>
</div>
</SettingsSection>
</div> </div>
) )
)} )}

View File

@ -95,6 +95,18 @@ const checkConfig = async (
if (!embeddingModel || !embeddingModelProvider) { if (!embeddingModel || !embeddingModelProvider) {
const embeddingModelProviders = providers.embeddingModelProviders; const embeddingModelProviders = providers.embeddingModelProviders;
let userSessionId = localStorage.getItem('userSessionId');
if (!userSessionId) {
userSessionId = crypto.randomBytes(20).toString('hex');
localStorage.setItem('userSessionId', userSessionId!)
}
let maxRecordLimit = localStorage.getItem('maxRecordLimit');
if (!maxRecordLimit) {
maxRecordLimit = '20';
localStorage.setItem('maxRecordLimit', maxRecordLimit);
}
if ( if (
!embeddingModelProviders || !embeddingModelProviders ||
Object.keys(embeddingModelProviders).length === 0 Object.keys(embeddingModelProviders).length === 0
@ -342,6 +354,7 @@ const ChatWindow = ({ id }: { id?: string }) => {
let added = false; let added = false;
messageId = messageId ?? crypto.randomBytes(7).toString('hex'); messageId = messageId ?? crypto.randomBytes(7).toString('hex');
let userSessionId = localStorage.getItem('userSessionId');
setMessages((prevMessages) => [ setMessages((prevMessages) => [
...prevMessages, ...prevMessages,
@ -466,6 +479,7 @@ const ChatWindow = ({ id }: { id?: string }) => {
messageId: messageId, messageId: messageId,
chatId: chatId!, chatId: chatId!,
content: message, content: message,
userSessionId: userSessionId,
}, },
chatId: chatId!, chatId: chatId!,
files: fileIds, files: fileIds,

View File

@ -25,4 +25,6 @@ export const chats = sqliteTable('chats', {
files: text('files', { mode: 'json' }) files: text('files', { mode: 'json' })
.$type<File[]>() .$type<File[]>()
.default(sql`'[]'`), .default(sql`'[]'`),
userSessionId: text('userSessionId'),
timestamp: text('timestamp'),
}); });