diff --git a/frontend/src/components/Timeline.tsx b/frontend/src/components/Timeline.tsx
new file mode 100644
index 000000000..1d4796e83
--- /dev/null
+++ b/frontend/src/components/Timeline.tsx
@@ -0,0 +1,59 @@
+import { useState } from "react";
+import { Slider } from "@mui/material";
+import {
+ ChevronLeftIcon,
+ ChevronRightIcon,
+ ClockIcon,
+} from "@heroicons/react/24/outline";
+import { cn } from "../utils/cn";
+import { History } from "../hooks/useHistories";
+
+export function Timeline(props: {
+ disabled: boolean;
+ histories: History[];
+ activeHistoryIndex: number;
+ onChange?: (newValue: number) => void;
+}) {
+ const [expanded, setExpanded] = useState(false);
+ return (
+
+
+ props.onChange?.((e.target as any).value)}
+ valueLabelDisplay="auto"
+ step={1}
+ marks
+ min={0}
+ max={props.histories.length - 1}
+ />
+ {expanded ? (
+ setExpanded((expanded) => !expanded)}
+ />
+ ) : (
+ setExpanded((expanded) => !expanded)}
+ />
+ )}
+
+ );
+}
diff --git a/frontend/src/hooks/useHistories.ts b/frontend/src/hooks/useHistories.ts
new file mode 100644
index 000000000..d011e4151
--- /dev/null
+++ b/frontend/src/hooks/useHistories.ts
@@ -0,0 +1,41 @@
+import { useEffect, useState } from "react";
+import { Message } from "../types";
+import { StreamState } from "./useStreamState";
+
+async function getHistories(threadId: string) {
+ const response = await fetch(`/threads/${threadId}/history`, {
+ headers: {
+ Accept: "application/json",
+ },
+ }).then((r) => r.json());
+ return response;
+}
+
+export interface History {
+ values: Message[];
+ next: string[];
+ config: Record;
+}
+
+export function useHistories(
+ threadId: string | null,
+ stream: StreamState | null,
+): {
+ histories: History[];
+ setHistories: React.Dispatch>;
+} {
+ const [histories, setHistories] = useState([]);
+
+ useEffect(() => {
+ async function fetchHistories() {
+ if (threadId) {
+ const histories = await getHistories(threadId);
+ setHistories(histories);
+ }
+ }
+ fetchHistories();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [threadId, stream?.status]);
+
+ return { histories, setHistories };
+}