Monday.com + GPT app tutorial: How to make a debug assistant

Thursday, June 22, 2023 by alfredo_lhuissier73
Monday.com + GPT app tutorial: How to make a debug assistant

Let's build with Monday.com and GPT!


Every programmer knows the pain of debugging. From trying to identify the culprit to navigating old Stack Overflow posts, the process can be long and frustrating. We're here to help! We are going to build a NextJS app that integrates with Monday's Incoming Bugs board, reads the description of the problem and then automatically populates another column with suggestions on how to fix it.

What is Monday.com?

Monday.com is a customizable web and mobile work management platform, designed to help teams and organizations with operational efficiency by tracking projects and workflows, visualizing data, and team collaboration. It includes automation capabilities and supports integrations with other work apps.

Setting up your Monday App

First, create a Monday.com developer account if you haven't already.

After finishing setting your account, log in to your Monday.com dashboard and press the blue button on the left panel to add a new item to your workspace. Click on "Choose from template" and then select the Product Development template. Click on "Use template".

Monday.com dashboard
Monday dashboard

This should add the template on your dashboard, with a Bug Queue board, which we will use later to fetch Bug descriptions and send them to GPT. Add a new column to the Incoming Bugs table and call it "Suggestions". Your dashboard should look like this:

Monday.com product development, bug queue
Product development template

Now, click on your avatar -> Developers and press the "Create app" button. Let's call it Debug Assistant.

Monday.com create app
Create app

Go to the OAuth section of your app, and add add the following permissions: "me:read", "boards:read" and "boards:write"

Monday.com OAuth
App permissions

We can now proceed to the installation.

🤖 Installation

To get a head start, we are going to use a NextJS app template from Monday.com that provides ready-to-use React components and context. You can later modify these to improve the UI/UX.

Clone and install the template:

git clone https://github.com/yuhgto/monday-ai-prompt-template
cd monday-ai-prompt-template
npm i

Open your .env file and paste your OPENAI_API_KEY values.

We can now start our development server running:

npm run dev
# or
yarn dev
# or
pnpm dev

In order to connect this app to Monday, you need to create a tunnel to your local environment.

[Install ngrok and sign up for an account (https://ngrok.com/download)].

Then, run the following command:

ngrok http 3000

This will expose your local server trough a public URL. Save that URL.

Ngrok URL
Ngrok tunnel

Connecting to Monday

Go back to your Monday app and add a new feature. Choose the Dashboard Widgets option, then "Start from scratch".

Monday.com create feature
Create feature

Let's call our feature "Debug Widget" and go to the "Widget Setup" tab. Select "Custom URL" as source, paste your ngrok URL and click on "View URL".

Note: If you restart the ngrok server, the URL will change and you will have to change it here too.

Monday.com Build URL
Custom URL

You should be able to see your local app running inside an iframe. This means the app is connected to Monday.com!

Monday.com app
Local app running on Monday.com

Now change the source to Published Build, click on the "New build" button, and add your ngrok URL.

Monday.com Build URL
Build URL

Note: This build is intended for development only, and it will work as long your ngrok is running with the same URL. In other words, everytime you restart your ngrok server, you will have to create a new build with the new URL.

Go now to your app's dashboard, click on App Versions. Click on the three dots of your app's first version, and publish it.

Monday.com publish app
Publish app

Then, on your app's dashboard again, click on "Install" on the left menu and then on the "Install app" button.

Monday.com install app
Install app

Your should now be able to add your app to your Monday board. Go back to your workspace, and open the Bug Insights view on the Bug Queue board, then click on Add Widget -> Apps.

Monday.com add widget
Add Widget

Go to Installed apps and add the Debug Widget to the board.

Monday.com add widget
Debug Widget

You should see the app running inside the widget. Awesome, we are finaly ready to start coding!

🚀 Developing the app

Go back to your code editor, and copy the boilerplate file located at monday-ai-prompt-template/src/examples/livestream-example/boilerplate.tsx, then paste it in the same directory and rename it to "debug-assistant.tsx". We will write most of our code here.

Debug assistant typescript file
Monday.com template

Change the component function name from "LivestreamExample" to "DebugAssistant" in line 64 and don't forget to rename the export function too at the end of the file:

const DebugAssistant = ({ initialInput = "" }: Props): JSX.Element => {
    ....
}

export default DebugAssistant;

Now open the app's main page located at monday-ai-prompt-template/src/app/page.tsx since NextJS v13, comment out or remove ContextExplorerExample Component, and add the DebugAssistant component.

...
import DebugAssistant from '@/examples/livestream-example/debug-assistant';

export default function Home() {
  return (
    <div className={styles.App}>
    <AppContextProvider>
      {/* <ContextExplorerExample /> */}
      <DebugAssistant />
      {/* <LivestreamExampleFinal /> */}
      {/* <BasePromptLayout /> */}
    </AppContextProvider>
    </div>
  )
}

We are going to reuse a lot of code from Monday's template, adding some tweaks to get it working on our development server.

Go to debug-assistant.tsx, then import dynamic from next and also Monday's Dropdown component:

...
import dynamic from "next/dynamic";

...

const Dropdown = dynamic(
  () => import("monday-ui-react-core").then((mod) => mod.Dropdown),
  {
    ssr: false,
  }
);

Add 2 Dropdown's to your render, one for selecting a board, and the other one for selecting a column. Your rendering section should look like this:

...
return (
    <div className={classes.main}>
      <div className={classes.dropdownContainer}>
        <Dropdown
          options={boardGroupsForDropdownComponent}
          onChange={handleGroupSelect}
          placeholder="Select a group"
          size="small"
        />
        <Dropdown
          options={boardColumnsForDropdownComponent}
          onChange={handleColumnSelect}
          placeholder={"Select an output column"}
          size="small"
        />
      </div>
      {canRenderInput && (
        <TextInputWithTagsAndSend
          className={classes.inputContainer}
          onSend={handleSend}
          validTags={boardColumnsForTagsComponent ?? []}
          initialInput={initialInput}
          mode={mode}
          setMode={setMode}
          loading={loading}
          success={success}
          error={error}
        />
      )}
      <div className={classes.footer}>
        <AiAppFooter />
      </div>
    </div>
  );

In the getItemsAndColumnValues(), add context?.iframeContext?.boardIds as a fallback value for boardId:

...
async function getItemsAndColumnValues(selectedGroup, context, columnIds) {
  return await executeMondayApiCall(
    `query ($boardId:[Int], $columnIds:[String], $groupId: [String]) { boards (ids:$boardId) { groups(ids:$groupId) { items { id column_values (ids:$columnIds) { text id } } } } }`,
    {
      variables: {
        columnIds,
        boardId:
          context?.iframeContext?.boardId ??
          context?.iframeContext?.boardIds ?? // <-- ADD THIS
          [],
        groupId: selectedGroup,
      },
    }
  );
}
...

Now write the code for the handleSend() function:

const handleSend = useCallback(
  async (input: string) => {
    setMode(Modes.response);

    const columnIds = getColumnIdsFromInputTags(input);

    var itemColumnValuesFromMonday = await getItemsAndColumnValues(
      selectedGroup,
      context,
      columnIds
    );

    if (itemColumnValuesFromMonday.is_success) {
      const { items } = itemColumnValuesFromMonday.data.boards[0].groups[0];
      var prompts: string[] = items.map((item: any) => {
        return replaceTagsWithColumnValues(input, item.column_values);
        // return input
      });
      var itemIdsList: { id: string }[] =
        itemColumnValuesFromMonday?.data.boards[0].groups[0].items.map(
          (x: any) => x.id
        );
    } else {
      showErrorMessage("Failed getting column values from monday", 3000);
      setMode(Modes.request);
      return null;
    }

    const aiApiResponse = await aiApiStatus.fetchData({
      prompts: prompts, // or promptsWithColumnValues,
      items: itemIdsList,
      n: 1, // or itemsFromMonday.length
    });
    if (aiApiResponse.length === 0 || error) {
      showErrorMessage("Something went wrong", 3000);
      setMode(Modes.request);
    } else {
      // avoid GraphQL type errors on dev
      let boardId =
        !context?.iframeContext?.boardId &&
        context?.iframeContext?.boardIds !== null &&
        context?.iframeContext?.boardIds !== undefined
          ? context?.iframeContext?.boardIds[0]
          : context?.iframeContext?.boardId ??
            context?.iframeContext?.boardIds ??
            [];
      // update items on board
      let itemUpdates = aiApiResponse.map(
        async (result: { item: string, result: string }) => {
          return await executeMondayApiCall(
            `mutation ($column:String!,$boardId:Int!, $itemId:Int!, $value:String!) {change_simple_column_value (column_id:$column, board_id:$boardId, item_id:$itemId, value:$value) {id }}`,
            {
              variables: {
                column: outputColumn,
                boardId: boardId,
                itemId: parseInt(result.item),
                value: result.result,
              },
            }
          );
        }
      );
      let success = await Promise.all(itemUpdates);
      setSuccess(!!success);
      setMode(Modes.request);
    }
  },
  [aiApiStatus, selectedGroup, context, outputColumn, error]
);

This will send the prompts to OpenAI and then make the GraphQL mutations to update the column we want.

We are almost done. There is a bug on newer versions of NextJS (~13.4.x) related to fetching requests on localhost. To circumvent this, open the app/api/openai/route.ts file, and add this at the end:

...
export const dynamic = "force-static";

This will fix the error, but it will remove the headers from our requests. Lastly, to make our lifes easier during development, we'll skip authentication for now. On route.ts, inside the POST function, comment out the authentication verification:

...
export async function POST(req: NextRequest, res: NextResponse) {
  const reqJson = await req.json();
  console.log(`A request was made. \nRequest:${JSON.stringify(reqJson)}`)
  /*if (!isAuthorized(req)) {
    return NextResponse.json({ message: "Not authorized", success: false },
      {status:401});
  } else*/ if (!reqJson.items) {
    return NextResponse.json({'message': 'No items array supplied'}, {
      status: 400,
    })
  }
  else if ...
  ...
}

This is fine during development, but you must remember to add the verification again before pushing to product. The changes we just did on route.ts are only necessary to make it all work with our local context.

We are ready to start testing our app! Go back to Bug Queue -> Bug Insights board on Monday.com, and open the Debug Widget. The new UI should be running inside it. You might need to reload the page.

On the first dropdown select "Incoming Bugs" group, and on the second dropdown select "Suggestions" as output. Then write the following on the text input: "Write a short technical solution for this bug description: @Bug Description" (You should be able to select "Bug Description" after the @). Send the request.

Monday.com widget demo
Demo

🎉 There you go! You should see the Suggestions column being filled up after a few seconds.

This template is using GPT3.5 (text-davinci-003 model) by default. If you want to use another one, like GPT4, open src/services/openai-service.ts and change the name of the model there.

🤔 Final Thoughts

We learned how to create a Monday app and connect it to an LLM through a local development server. Now, you are free to change the UI, refine the prompts and play with it as much as you want. The sky is the limit! Whenever your app is ready, you should deploy it to a production server and configure it accordingly. That is all for this tutorial, I hope it will help you navigate all the different parts of the Monday.com app framework. Feel free to reach out to me on LinkedIn or Twitter if you have any questions.

And practice what you've learn here during our amazing AI Hackathons! Join the AI revolution!

Discover tutorials with similar technologies

Upcoming AI Hackathons and Events