import { type LoaderFunctionArgs, type ActionFunctionArgs, } from "@remix-run/server-runtime"; import { sort } from "fast-sort"; import { useParams, useRevalidator } from "@remix-run/react"; import { requireUser, requireUserId, requireWorkpace, } from "~/services/session.server"; import { getConversationAndHistory, getCurrentConversationRun, stopConversation, } from "~/services/conversation.server"; import { type ConversationHistory } from "@core/database"; import { ConversationItem, ConversationList, ConversationTextarea, StreamingConversation, } from "~/components/conversation"; import { useTypedLoaderData } from "remix-typedjson"; import React from "react"; import { ScrollAreaWithAutoScroll } from "~/components/use-auto-scroll"; import { json } from "@remix-run/node"; import { env } from "~/env.server"; import { ResizableHandle, ResizablePanel, ResizablePanelGroup, } from "~/components/ui/resizable"; // Example loader accessing params export async function loader({ params, request }: LoaderFunctionArgs) { const user = await requireUser(request); const workspace = await requireWorkpace(request); const conversation = await getConversationAndHistory( params.conversationId as string, user.id, ); if (!conversation) { throw new Error("No conversation found"); } const run = await getCurrentConversationRun(conversation.id, workspace.id); return { conversation, run, apiURL: env.TRIGGER_API_URL }; } // Example action accessing params export async function action({ params, request }: ActionFunctionArgs) { if (request.method.toUpperCase() !== "POST") { return new Response("Method Not Allowed", { status: 405 }); } const userId = await requireUserId(request); const workspace = await requireWorkpace(request); // params.conversationId will be available here const { conversationId } = params; if (!conversationId) { throw new Error("No conversation"); } const result = await stopConversation(conversationId, workspace.id); return json(result); } // Accessing params in the component export default function SingleConversation() { const { conversation, run, apiURL } = useTypedLoaderData(); const conversationHistory = conversation.ConversationHistory; const [conversationResponse, setConversationResponse] = React.useState< { conversationHistoryId: string; id: string; token: string } | undefined >(run); const [stopLoading, setStopLoading] = React.useState(false); const { conversationId } = useParams(); const revalidator = useRevalidator(); React.useEffect(() => { if (run) { setConversationResponse(run); } }, [run]); const getConversations = () => { const lastConversationHistoryId = conversationResponse?.conversationHistoryId; // First sort the conversation history by creation time const sortedConversationHistory = sort(conversationHistory).asc( (ch) => ch.createdAt, ); const lastIndex = sortedConversationHistory.findIndex( (item) => item.id === lastConversationHistoryId, ); // Filter out any conversation history items that come after the lastConversationHistoryId const filteredConversationHistory = lastConversationHistoryId ? sortedConversationHistory.filter((_ch, currentIndex: number) => { // Find the index of the last conversation history // Only keep items that come before or are the last conversation history return currentIndex <= lastIndex; }) : sortedConversationHistory; return ( <> {filteredConversationHistory.map( (ch: ConversationHistory, index: number) => { return ; }, )} ); }; if (typeof window === "undefined") { return null; } return (
{getConversations()} {conversationResponse && ( { setConversationResponse(undefined); revalidator.revalidate(); }} apiURL={apiURL} /> )}
{conversation?.status !== "need_approval" && ( )}
); }