aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDhravya Shah <[email protected]>2024-07-23 23:33:11 -0500
committerDhravya Shah <[email protected]>2024-07-23 23:33:11 -0500
commit568531f3236801f192549e44283a4f1a19ff2f9e (patch)
tree35720f9e0a8b886beb938f7dc6b7a17da3f871a4
parentMerge pull request #137 from supermemoryai/pro-mode (diff)
downloadsupermemory-568531f3236801f192549e44283a4f1a19ff2f9e.tar.xz
supermemory-568531f3236801f192549e44283a4f1a19ff2f9e.zip
added recommended items
-rw-r--r--apps/web/app/(dash)/home/history.tsx31
-rw-r--r--apps/web/app/(dash)/home/page.tsx34
-rw-r--r--apps/web/app/(dash)/home/queryinput.tsx31
-rw-r--r--apps/web/app/actions/doers.ts102
-rw-r--r--apps/web/env.d.ts2
-rw-r--r--apps/web/lib/utils.ts23
-rw-r--r--apps/web/wrangler.toml9
7 files changed, 177 insertions, 55 deletions
diff --git a/apps/web/app/(dash)/home/history.tsx b/apps/web/app/(dash)/home/history.tsx
index 922734df..3e6825eb 100644
--- a/apps/web/app/(dash)/home/history.tsx
+++ b/apps/web/app/(dash)/home/history.tsx
@@ -5,26 +5,27 @@ import Link from "next/link";
import { memo, useEffect, useState } from "react";
import { motion } from "framer-motion";
import { chatThreads } from "@/server/db/schema";
+import { getQuerySuggestions } from "@/app/actions/doers";
+import { Button } from "@repo/ui/shadcn/button";
-const History = memo(() => {
- const [chatThreads_, setChatThreads] = useState<
- (typeof chatThreads.$inferSelect)[] | null
- >(null);
+const History = memo(({ setQuery }: { setQuery: (q: string) => void }) => {
+ const [suggestions, setSuggestions] = useState<string[] | null>(null);
useEffect(() => {
(async () => {
- const chatThreads = await getChatHistory();
- if (!chatThreads.success || !chatThreads.data) {
- console.error(chatThreads.error);
+ const suggestions = await getQuerySuggestions();
+ if (!suggestions.success || !suggestions.data) {
+ console.error(suggestions.error);
return;
}
- setChatThreads(chatThreads.data.reverse().slice(0, 3));
+ console.log(suggestions);
+ setSuggestions(suggestions.data.reverse().slice(0, 3));
})();
}, []);
return (
<ul className="text-base list-none space-y-3 text-[#b9b9b9] mt-8">
- {!chatThreads_ && (
+ {!suggestions && (
<>
<Skeleton
key="loader-1"
@@ -40,17 +41,15 @@ const History = memo(() => {
></Skeleton>
</>
)}
- {chatThreads_?.map((thread) => (
+ {suggestions?.map((suggestion) => (
<motion.li
initial={{ opacity: 0, filter: "blur(1px)" }}
animate={{ opacity: 1, filter: "blur(0px)" }}
- className="flex items-center gap-2 truncate"
- key={thread.id}
+ className="flex items-center gap-2 truncate cursor-pointer"
+ key={suggestion}
+ onClick={() => setQuery(suggestion)}
>
- <ArrowLongRightIcon className="h-5" />{" "}
- <Link prefetch={false} href={`/chat/${thread.id}`}>
- {thread.firstMessage}
- </Link>
+ <ArrowLongRightIcon className="h-5" /> {suggestion}
</motion.li>
))}
</ul>
diff --git a/apps/web/app/(dash)/home/page.tsx b/apps/web/app/(dash)/home/page.tsx
index ebd4d84b..f75a5ec7 100644
--- a/apps/web/app/(dash)/home/page.tsx
+++ b/apps/web/app/(dash)/home/page.tsx
@@ -4,12 +4,15 @@ import React, { useEffect, useState } from "react";
import QueryInput from "./queryinput";
import { getSessionAuthToken, getSpaces } from "@/app/actions/fetchers";
import { redirect, useRouter } from "next/navigation";
-import { createChatThread, linkTelegramToUser } from "@/app/actions/doers";
+import {
+ createChatThread,
+ getQuerySuggestions,
+ linkTelegramToUser,
+} from "@/app/actions/doers";
import { toast } from "sonner";
import { motion } from "framer-motion";
import { ChromeIcon, GithubIcon, TwitterIcon } from "lucide-react";
import Link from "next/link";
-import { homeSearchParamsCache } from "@/lib/searchParams";
import History from "./history";
const slap = {
@@ -28,26 +31,19 @@ const slap = {
function Page({ searchParams }: { searchParams: Record<string, string> }) {
// TODO: use this to show a welcome page/modal
const firstTime = searchParams.firstTime === "true";
+ const telegramUser = searchParams.telegramUser;
+ const extensionInstalled = searchParams.extension;
+ const [query, setQuery] = useState(searchParams.q || "");
- const query = searchParams.q || "";
+ const [querySuggestions, setQuerySuggestions] = useState<string[]>([]);
+ const [spaces, setSpaces] = useState<{ id: number; name: string }[]>([]);
+
+ const { push } = useRouter();
if (firstTime) {
redirect("/onboarding");
}
- const [queryPresent, setQueryPresent] = useState<boolean>(false);
-
- const [telegramUser, setTelegramUser] = useState<string | undefined>(
- searchParams.telegramUser as string,
- );
- const [extensionInstalled, setExtensionInstalled] = useState<
- string | undefined
- >(searchParams.extension as string);
-
- const { push } = useRouter();
-
- const [spaces, setSpaces] = useState<{ id: number; name: string }[]>([]);
-
useEffect(() => {
if (telegramUser) {
const linkTelegram = async () => {
@@ -101,8 +97,8 @@ function Page({ searchParams }: { searchParams: Record<string, string> }) {
<div className="w-full pb-20 mt-10">
<QueryInput
- initialQuery={query}
- setQueryPresent={setQueryPresent}
+ query={query}
+ setQuery={setQuery}
handleSubmit={async (q, spaces, proMode) => {
if (q.length === 0) {
toast.error("Query is required");
@@ -123,7 +119,7 @@ function Page({ searchParams }: { searchParams: Record<string, string> }) {
initialSpaces={spaces}
/>
- <History />
+ <History setQuery={setQuery} />
</div>
<div className="w-full fixed bottom-0 left-0 p-4">
diff --git a/apps/web/app/(dash)/home/queryinput.tsx b/apps/web/app/(dash)/home/queryinput.tsx
index 9f1e7292..82561438 100644
--- a/apps/web/app/(dash)/home/queryinput.tsx
+++ b/apps/web/app/(dash)/home/queryinput.tsx
@@ -8,26 +8,24 @@ import { Switch } from "@repo/ui/shadcn/switch";
import { Label } from "@repo/ui/shadcn/label";
function QueryInput({
- setQueryPresent,
- initialQuery,
initialSpaces,
handleSubmit,
+ query,
+ setQuery,
}: {
- setQueryPresent: (t: boolean) => void;
initialSpaces?: {
id: number;
name: string;
}[];
- initialQuery?: string;
mini?: boolean;
handleSubmit: (
q: string,
spaces: { id: number; name: string }[],
proMode: boolean,
) => void;
+ query: string;
+ setQuery: (q: string) => void;
}) {
- const [q, setQ] = useState(initialQuery || "");
-
const [proMode, setProMode] = useState(false);
const [selectedSpaces, setSelectedSpaces] = useState<
@@ -42,11 +40,11 @@ function QueryInput({
{/* input and action button */}
<form
action={async () => {
- if (q.trim().length === 0) {
+ if (query.trim().length === 0) {
return;
}
- handleSubmit(q, selectedSpaces, proMode);
- setQ("");
+ handleSubmit(query, selectedSpaces, proMode);
+ setQuery("");
}}
>
<textarea
@@ -59,20 +57,15 @@ function QueryInput({
onKeyDown={(e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
- if (q.trim().length === 0) {
+ if (query.trim().length === 0) {
return;
}
- handleSubmit(q, selectedSpaces, proMode);
- setQ("");
+ handleSubmit(query, selectedSpaces, proMode);
+ setQuery("");
}
}}
- onChange={(e) =>
- setQ((prev) => {
- setQueryPresent(!!e.target.value.length);
- return e.target.value;
- })
- }
- value={q}
+ onChange={(e) => setQuery(e.target.value)}
+ value={query}
/>
<div className="flex p-2 px-3 w-full items-center justify-between rounded-xl overflow-hidden">
<FilterSpaces
diff --git a/apps/web/app/actions/doers.ts b/apps/web/app/actions/doers.ts
index a2cdb4f5..0c1aa8d9 100644
--- a/apps/web/app/actions/doers.ts
+++ b/apps/web/app/actions/doers.ts
@@ -22,6 +22,8 @@ import { ChatHistory } from "@repo/shared-types";
import { decipher } from "@/server/encrypt";
import { redirect } from "next/navigation";
import { tweetToMd } from "@repo/shared-types/utils";
+import { ensureAuth } from "../api/ensureAuth";
+import { getRandomSentences } from "@/lib/utils";
export const createSpace = async (
input: string | FormData,
@@ -708,3 +710,103 @@ export async function AddCanvasInfo({
};
}
}
+
+export async function getQuerySuggestions() {
+ const data = await auth();
+
+ if (!data || !data.user || !data.user.id) {
+ redirect("/signin");
+ return { error: "Not authenticated", success: false };
+ }
+
+ const recommendations = await process.env.RECOMMENDATIONS.get(data.user.id);
+
+ if (recommendations) {
+ return {
+ success: true,
+ data: JSON.parse(recommendations),
+ };
+ }
+
+ // Randomly choose some storedContent of the user.
+ const content = await db
+ .select()
+ .from(storedContent)
+ .where(eq(storedContent.userId, data.user.id))
+ .orderBy(sql`random()`)
+ .limit(3)
+ .all();
+
+ const fullQuery = content.map((c) => `${c.title} \n\n${c.content}`).join(" ");
+
+ const sentences = getRandomSentences(fullQuery);
+
+ const suggestionsCall = (await process.env.AI.run(
+ // @ts-ignore
+ "@cf/meta/llama-3.1-8b-instruct",
+ {
+ messages: [
+ {
+ role: "system",
+ content: `You are a model that suggests questions based on the user's content.`,
+ },
+ {
+ role: "user",
+ content: `Run the function based on this input: ${sentences}`,
+ },
+ ],
+ tools: [
+ {
+ type: "function",
+ function: {
+ name: "querySuggestions",
+ description:
+ "Take the user's content to suggest some good questions that they could ask.",
+ parameters: {
+ type: "object",
+ properties: {
+ querySuggestions: {
+ type: "array",
+ description:
+ "Short questions that the user can ask. Give atleast 3 suggestions. No more than 5.",
+ items: {
+ type: "string",
+ },
+ },
+ },
+ required: ["querySuggestions"],
+ },
+ },
+ },
+ ],
+ },
+ )) as {
+ response: string;
+ tool_calls: { name: string; arguments: { querySuggestions: string[] } }[];
+ };
+
+ const suggestions =
+ suggestionsCall.tool_calls?.[0]?.arguments?.querySuggestions;
+
+ console.log(suggestions);
+
+ if (!suggestions || suggestions.length === 0) {
+ return {
+ success: false,
+ error: "Failed to get query suggestions",
+ };
+ }
+
+ await process.env.RECOMMENDATIONS.put(
+ data.user.id,
+ JSON.stringify(suggestions),
+ {
+ expirationTtl: 60 * 5,
+ },
+ );
+
+ return {
+ success: true,
+ data: suggestions,
+ };
+}
diff --git a/apps/web/env.d.ts b/apps/web/env.d.ts
index b6a410f9..c80ac0a4 100644
--- a/apps/web/env.d.ts
+++ b/apps/web/env.d.ts
@@ -6,4 +6,6 @@ interface CloudflareEnv {
DATABASE: D1Database;
DEV_IMAGES: R2Bucket;
CANVAS_SNAPS: KVNamespace;
+ AI: Ai;
+ RECOMMENDATIONS: KVNamespace;
}
diff --git a/apps/web/lib/utils.ts b/apps/web/lib/utils.ts
new file mode 100644
index 00000000..98ec6e98
--- /dev/null
+++ b/apps/web/lib/utils.ts
@@ -0,0 +1,23 @@
+export function getRandomSentences(fullQuery: string): string {
+ // Split the fullQuery into sentences
+ const sentences = fullQuery.match(/[^.!?]+[.!?]+/g) || [];
+
+ // Function to get a random integer between min and max
+ function getRandomInt(min: number, max: number): number {
+ return Math.floor(Math.random() * (max - min)) + min;
+ }
+
+ let selectedSentences = "";
+ let totalCharacters = 0;
+
+ // Select random sentences until totalCharacters is at least 1000
+ while (totalCharacters < 1000 && sentences.length > 0) {
+ const randomIndex = getRandomInt(0, sentences.length);
+ const sentence = sentences[randomIndex];
+ selectedSentences += sentence;
+ totalCharacters += sentence?.length || 0;
+ sentences.splice(randomIndex, 1); // Remove the selected sentence from the array
+ }
+
+ return selectedSentences;
+}
diff --git a/apps/web/wrangler.toml b/apps/web/wrangler.toml
index ce38285b..8f4c75db 100644
--- a/apps/web/wrangler.toml
+++ b/apps/web/wrangler.toml
@@ -19,6 +19,10 @@ database_id = "fc562605-157a-4f60-b439-2a24ffed5b4c"
binding = "CANVAS_SNAPS"
id = "c6446f7190dd4afebe1c318df3400518"
+[[kv_namespaces]]
+binding = "RECOMMENDATIONS"
+id = "83bc7055226c4657948141c2ff9a5425"
+
[[env.production.d1_databases]]
binding = "DATABASE"
database_name = "prod-d1-supermemory"
@@ -27,4 +31,7 @@ database_id = "f527a727-c472-41d4-8eaf-3d7ba0f2f395"
[[env.preview.d1_databases]]
binding = "DATABASE"
database_name = "dev-d1-anycontext"
-database_id = "fc562605-157a-4f60-b439-2a24ffed5b4c" \ No newline at end of file
+database_id = "fc562605-157a-4f60-b439-2a24ffed5b4c"
+
+[ai]
+binding = "AI" \ No newline at end of file