< Back to home
01 Apr 2023
Journal GPT for Alephians
10 mins read.
1.gif
In Aleph, the company appreciates the habits of writing daily journals, but I just do not have the habit to do so.
2.png
Personally, I am very bad at writing a well constructed journal, as I only like to write in point form. So I decided to build a tool to help me, by only entering a few keypoints, and auto generating a well constructed journal for me.
First I created 4 text area for users to input.
3.png
Then modified user inputs into prompts for the ai.
const inputPrompt = 
  `Task Assigned:
  ${input1}\n\n
  Task Completed:
  ${input2}\n\n
  Plan For Next Day:
  ${input3}\n\n
  Any Issues Faced:
  ${input4}\n\n
  Gratitude:
  ${input5}\n\n
  Using the same header, elaborate and expand on the key points ${moodInput}.
`
TRPC
Here I pass the modified prompts using TRPC mutate to the backend.
const callGenerateJournal = api.chatGPT.generateJournal.useMutation({
  onMutate: () =>
  toast.loading("Generating...", { id: "callGenerateJournal" }),
  onSuccess: () => toast.success("Done!", { id: "callGenerateJournal" }),
  onError: (e) =>
  toast.error(`Error: ${e.data?.code || ""}`, {
    id: "callGenerateJournal",
  }),
});

const handleClick = () => {
  if (input1 && input2 && input3 && input4 && input5) {
    callGenerateJournal.mutate({ text: inputPrompt });
  }
}
API route
And here is how it looks in the backend. I use the openai library https://www.npmjs.com/package/openai here. API key is only pass here,protectedProcedure from NextAuth to make sure user has to logged in to gmail to call this API.
import { z } from "zod";
import { Configuration, OpenAIApi } from "openai";
import { createTRPCRouter, protectedProcedure } from "../trpc";
import { env } from "@/src/env/server.mjs";

const configuration = new Configuration({
  apiKey: env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);

export default createTRPCRouter({
  generateJournal: protectedProcedure // protectedProcedure is to make sure the user is logged in
    .input(z.object({ text: z.string() }))
    .mutation(async ({ input }) => {
      const res = await openai.createCompletion({
        model: "text-davinci-003",
        prompt: input.text, // modifed prompt input is passed here
        temperature: 0.7,
        max_tokens: 256,
        top_p: 1,
        frequency_penalty: 0,
        presence_penalty: 0,
      });
      if (res.data.choices.length > 0) {
        return res.data.choices[0]?.text; // returns the generated text
      }
      return "no response";
    }),
  // rest of the code
NextAuth
NextAuth to handle Gmail auth and making sure that only "@aleph-labs.com" account can use this app. NextAuth also generates a session token and stores it in a server-side session object. So it allows user to only have to logged in once until sessions expired.
export const authOptions: NextAuthOptions = {
  callbacks: {
    signIn({ account, profile }) {
      if (
        account?.provider === "google" &&
        profile?.email?.endsWith("@aleph-labs.com") // so only Alephians are allowed to use this application
      ) {
        return true;
      } else {
        return false;
      }
    },
    session({ session, user }) {
      if (session.user) {
        session.user.id = user.id;
        // session.user.role = user.role; <-- put other properties on the session here
      }
      return session;
    },
  },
  adapter: PrismaAdapter(prisma),
  providers: [
    GoogleProvider({ // using gmail as auth
      clientId: env.GOOGLE_ID, 
      clientSecret: env.GOOGLE_SECRET,
    }),
  ],
}
Debounce & localstorage
Lastly, added a debounce and localstorage storing function to store user's input locally for every user.
 useEffect(() => {
  const delayDebounceFn = handleDebounce();
  return () => clearTimeout(delayDebounceFn);
}, [input1, input2, input3, input4, input5]);

useEffect(() => {
  const storageData = localStorage.getItem("journalData");
  if (!storageData) return;
  const { journalData } = JSON.parse(storageData) as JournalData;
  if (journalData.input1) setInput1(journalData.input1);
  if (journalData.input2) setInput2(journalData.input2);
  if (journalData.input3) setInput3(journalData.input3);
  if (journalData.input4) setInput4(journalData.input4);
  if (journalData.input5) setInput5(journalData.input5);
  setDate(new Date().toLocaleDateString());
}, []);