# Step-by-step Tutorial: Build an AI App

In this tutorial we will build a simple Slack bot, which uses OpenAI to perform sentiment analysis on messages, and then store the result in Airtable.

We will need to access three separate web APIs, and obtain an API key for each of them. Follow the short tutorials below to retrieve API keys from each of them.

## Web API setup

* [Slack setup](/midio-docs/tutorial/slack-setup.md)
* [OpenAI setup](/midio-docs/tutorial/openai-setup.md)
  * If you want to use Groq instead, go to [Groq setup](https://github.com/midio-code/midio-docs/blob/master/welcome/tutorial/groq-setup.md). The API is similar to OpenAI.
* [Airtable setup](/midio-docs/tutorial/airtable-setup.md)

## Create our bot

### Getting started

First off, create a new module called Main (or any other name you might want to use).

Add these two nodes using the [Node Palette](/midio-docs/getting-started/quick-start-guide.md#adding-nodes):

* [Http Endpoint](/midio-docs/built-in-nodes/http.md#endpoint)
* [Http Response](/midio-docs/built-in-nodes/http.md#response)

Connect the `onRequest` trigger on the endpoint to the `execute` trigger on the response node. Leave the status input at `200` (which means our response is considered OK). We now have a basic endpoint setup that just returns an empty response. Enter `/slack-event` in the `path` field of `Http.Endpoint` and select `POST` in the `method` dropdown.

<figure><img src="/files/l1jJGyzAQshYiiSfJnly" alt=""><figcaption><p>Available</p></figcaption></figure>

### Registering our endpoint with Slack

Next up, we need to register our endpoint with Slack. Go to <https://api.slack.com/apps>, select your app (which you can set up following the steps here), and then select the 'Event subscriptions' item from the left side menu.

Add the url of our endpoint in the text box marked 'Request url'. Our url will be 'midio.com/slack-event',,, . TODO(Kristian): Add the actual url here.

Slack will try to verify our endpoint by sending us a POST request with a JSON body which looks something like this: `{ "challenge": "somechallgengeid" }`. To verify our endpoint, we must return the value in the challenge field.

{% hint style="warning" %}
We still haven't implemented this part, so Slack will show us an error message explaining that it can't verify our endpoint. We will fix this next.
{% endhint %}

To do this in Midio, we need the following nodes:

* Json.Parse - Converts JSON encoded text to Midio objects
* Map.ContainsKey - Returns true if the input map contains a given key
* Std.If - Lets us choose a different path based on whether the input value is `true` or `false`.
* Std.Log - Writes to the log panel (just to make it easier to see what happens)

Add and hook up the nodes as shown here:

<figure><img src="/files/e1BpjZCOqD6uexw9Cwg7" alt=""><figcaption></figcaption></figure>

Note that you can add the 'waypoint' node, which is the small node between `Parse.value` and `HttpResponse.body`, by first creating a connection between them, and then double clicking he connection.

{% hint style="info" %}
This is a waypoint node. Add one by double clicking a blue dotted connection, or search for waypoint using the node palette (space bar).

<img src="/files/gYNjGatSQ7MOIQ9riiDY" alt="" data-size="original">
{% endhint %}

#### What you've hooked up everything, go back to the slack portal and click retry.

<figure><img src="/files/e54BrxbW4KdfERYZpxPl" alt=""><figcaption></figcaption></figure>

If everything is working correctly, slack will display a success message, and you'll see a trace of the execution in the Midio editor. Hover or click the blue dots on the arrows to view the data which have passed through your code.

#### Cleanup

We can then clean up our code by converting some of the nodes into a function, which hides them behind a single node. Lets do this by selecting (hold shift and drag the mouse) the nodes which are only related to handling the challenge event. Right click on one of them, and select 'Convert to function' from the dropdown menu. Give the function a name, like 'Handle slack challenge event'.

<figure><img src="/files/xb86HUnyKAgkpCWfVzuo" alt=""><figcaption><p>First select the items to group</p></figcaption></figure>

<figure><img src="/files/5MKkJagMU9jx0mtt22Ym" alt=""><figcaption><p>Right click any of the selected nodes to bring up the context menu</p></figcaption></figure>

<figure><img src="/files/5X4Ecghf0khiT5UA9WP0" alt=""><figcaption><p>Select 'convert to function' in order to group them in a function</p></figcaption></figure>

### Sentiment analysis

Next up, we will create a Midio function which uses the OpenAI completion API to perform sentiment analysis on the content of the slack event.

First, create an empty function by right clicking on the `Main` module in the left side panel, and then clicking 'Add'. Then select 'function' from the popup window. Name the function something like 'Perform sentiment analysis'.

We will now add inputs and outputs to the function. Select the function from the left side panel, then click the 'add input' button in the top center, and select 'trigger'. Name it something like 'execute'.

<figure><img src="/files/DUdjtzNxI0nYtEKKZuIa" alt=""><figcaption></figcaption></figure>

Then add another input, this time a 'data' input, name it something like 'message', and pick `String` as its type (if you're ever unsure about which type to pick for an input or ouput, you can select the type `Any`, which means we don't care about the type. Read more about types [The Midio Language](/midio-docs/reference/the-midio-language.md#data-types). Add outputs using the 'add output' button right next to the 'add input'-button. First add an output, select trigger, and name it 'continue'. Then add another output, pick data, and call it 'sentiment'. Select `Number` as its type when prompted for a type.

<figure><img src="/files/m6GxmddzAgGGYN8q62H6" alt=""><figcaption><p>The add input and add output buttons</p></figcaption></figure>

You should then have something like this:

<figure><img src="/files/Md7tQvcb57v3RDW1gTZA" alt=""><figcaption></figcaption></figure>

#### Call the OpenAI API.

For this, we need a couple new functions and a new node type:

* Http.FetchUrl - lets us perform an http request to a url
* Math.NumberFromString - Lets us convert a string to a number
* Data objects - to create the data expected by the Open AI API.

{% hint style="info" %}
This is what a data object looks like in Midio:

<img src="/files/TLr4nXNzVddlK2GEvWc3" alt="" data-size="original">
{% endhint %}

Implement the function like this:

<figure><img src="/files/Dqg7MpqccPLljl2fykU5" alt=""><figcaption></figcaption></figure>

The data nodes can be built by adding a 'data object' node from the node palette (space bar), or by pasting the following JSON objects using control-v/cmd-v.

{% hint style="warning" %}
To paste JSON data into Midio, first copy the JSON to your clipboard as you normally copy text, then click anywhere in the midio node editor, and finally paste using control-/cmd-v. You **shouldn't** open the node palette first, as that would paste the text into the search box and have you create a text object with the JSON value as a text object.
{% endhint %}

```json
{
  "Authorization": "Bearer <your API Key>",
  "content-type": "application/json"
}
```

```json
{
  "messages": [
    {
      "content": "You perform sentiment analysis on user messages. Return a number between 0 and 10, where 0 represents a very negative sentiment, and 10 represents a very positive sentiment. Return the number and the number only!",
      "role": "system"
    },
    {
      "content": "prompt",
      "role": "user"
    }
  ],
  "model": "gpt-3.5-turbo",
  "temperature": "0.7"
}
```

To convert the `messages.1.content` item to an input, hover it, and click the 'convert to input'-icon. This will allow you to hook it up to the function input we created earlier.

**Add it to the main flow**

We can now use this function in our main flow. Select the 'Main' module from the left side panel. Then add an instance of our new function using the node palette (space bar).

<figure><img src="/files/MQJyaqguOA0VxiVqO5Mv" alt=""><figcaption></figcaption></figure>

We can now test our flow by sendimg a message to a channel in our slack workspace. Note that we are using the `Std.FormatLog` function to print the resulting sentiment to the log. You can also hover/click on the blue dots appearing on the connections between nodes to inspect the data which has passed through our flow.

### Store in Airtable

The last step of our slack bot, is to store the analysis along with the original message in our airtable database (which you set up here).

We start by creating a new function, like we did in the previous step. Name the function something like 'Store analysis in Airtable'.

The function also uses `Http.FetchUrl` to interact with the API. Implement the function as shown here:

<figure><img src="/files/AB35NOQtryJQWXioTNho" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}

```
Note that the url you pass to `Http.FetchUrl` should be constructed as follows:

https://api.airtable.com/v0/<your base id>/<your table id>

If you navigate to your base in the airtable dashboard, you can get these IDs from the url. For example:

https://airtable.com/appzte7M4AptpTo3T/tblg2Z1Th1mKEjc3i/viwWhPV8Uo8zVLbH4?blocks=hide
                     <-----base id --> <---table id --->

```

{% endhint %}

The headers object looks the same as the one we used with OpenAI, but make sure you fill in your Airtable API-key:

```json
{
  "Authorization": "Bearer patl3AnvCPtzEDTOm.3e674e850a1993e30a96e1d890f3a078e4fc44ae1434d66d44d687e365b0d2d9",
  "Content-Type": "application/json"
}
```

```json
{
  "records": [
    {
      "fields": {
        "Message": "message",
        "Sentiment": "sentiment"
      }
    }
  ]
}
```

Finally, add an instance of this function to the main flow, and hook it up. The final top level code should look like this:

<figure><img src="/files/MaR158DcRP3QvHu4858S" alt=""><figcaption></figcaption></figure>

Now, write another message in slack to verify that everything works as expected, and that you can see a new row appear in your airtable table.

### A final bug

We've forgotten a small detail, which causes our bot to potentially receive the same event multiple times. Slack expects a '200 OK' response from us, to indicate that the event has been handled, but we currently only do this for the challenge event. Lets respond to normal events as well by adding an instance of `Http.Response` just before we start our sentiment analysis.

<figure><img src="/files/pFTZ5aSiLcS4Ex3xuEN1" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.midio.com/midio-docs/tutorial.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
