ChatGPT pair programming
Tags: ai, nextjs
Published: 2023-01-09 20:04:13

[github](https://github.com/muckitymuck/OpenAI-Mystery-Generator)
[live demo](https://open-ai-mystery-generator.vercel.app/)
I know it's been a bit (too long) since I wrote a blog on what I was doing.  Recently, I was hired for a OpenAI project and started researching to prepare myself.  The best way to learn is to do and so I started building my own OpenAI project.
This project is a small scope generative AI text service to put out an opening setting to a crime mystery novel in the chosen style of famous authors and constrained to locations entered by the user.  For example, if you enter a city like NYC it will set the crime to a typical setting like a back alley, subway station, or Manhattan penthouse.  A setting for temperature will also affect the output by how random the setting is.  The author style setting affects the location but also affects the characters that appear in the setting.

![OpenAI Mystery demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gj2pbdr4cf3guq5mfvaj.PNG)
As a bonus to the project, I worked along side ChatGPT to fill out the app basic code.  I would say the Chat got it about 80% at any given output.  It did understand how the backend works with the frontend with naming conventions but random misused syntax would pop in.
*************************************************************************
In this conversation, we worked on a project that is a web application that uses OpenAI's GPT-3 API to generate mystery scenes in different writing styles. The project was built using a combination of Next.js, the OpenAI API, React, Node.js, and Express.

We first added a range slider to the web page that allows the user to change the temperature of the GPT-3 model. We also added a display on the webpage to show the current temperature.

Next, we modified the generate.js file to include the value set on the range slider as the temperature in the request to the OpenAI API.

We also added a dropdown menu to the frontend to allow the user to switch the writing style in the prompt. To do this, we added a state variable to store the value of the selected style and passed it to the value prop of the select element.

Finally, we wrote a README.md file for the project, which provides an overview of the technology used and instructions for running the project locally.

Overall, this conversation demonstrated how to use OpenAI's GPT-3 API to generate text in different styles and how to incorporate user input in the form of a range slider and dropdown menu in a web application.

generate.js
```
import { Configuration, OpenAIApi } from "openai";

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

export default async function (req, res) {
  if (!configuration.apiKey) {
    res.status(500).json({
      error: {
        message: "OpenAI API key not configured, please follow instructions in README.md",
      }
    });
    return;
  }

  const scene = req.body.scene || '';
  if (scene.trim().length === 0) {
    res.status(400).json({
      error: {
        message: "Please enter a valid location",
      }
    });
    return;
  }

  try {
    const temperature = Number(req.body.temperature) || 0.5;
    const completion = await openai.createCompletion({
      model: "text-davinci-003",
      prompt: generatePrompt(scene),
      max_tokens: 200,
      //temperature: 0.9,
      temperature: temperature,
    });
    res.status(200).json({ result: completion.data.choices[0].text });
  } catch(error) {
    // Consider adjusting the error handling logic for your use case
    if (error.response) {
      console.error(error.response.status, error.response.data);
      res.status(error.response.status).json(error.response.data);
    } else {
      console.error(`Error with OpenAI API request: ${error.message}`);
      res.status(500).json({
        error: {
          message: 'An error occurred during your request.',
        }
      });
    }
  }
}

function generatePrompt(scene, style, character) {

  let prompt = `Suggest a setting and initial characters with names the cops meet at a crime scene in the 
  style of famous authors`

  if (style === "Agatha Christie"){
    prompt += 
    `
    Setting: ${scene}
    Character: ${character}
    style: ${style}
    `;
  } else if (style === "Raymond Chandler"){
    prompt += 
    `
    Setting: ${scene}
    Character: ${character}
    style: ${style}
    `;
  }else if (style === "P.D. James"){
    prompt += 
    `
    Setting: ${scene}
    Character: ${character}
    style: ${style}
    `;
  } else if (style === "Dashiell Hammet"){
    prompt += 
    `
    Setting: ${scene}
    Character: ${character}
    style: ${style}
    `;
  } else {
    prompt += 
    `
    Setting: ${scene}
    Character: ${character}
    style: ${style}
    `;
  }
  return prompt;
}
```
The generate.js file is a server-side file that exports a function that handles an HTTP POST request to the /api/generate endpoint. When this function is called, it does the following:

It first checks whether the OpenAI API key is configured. If it is not configured, it sends a JSON response with an error message to the client.
If the API key is configured, it retrieves the scene value from the request body. If the scene value is not provided or is an empty string, it sends a JSON response with an error message to the client.
If the scene value is provided, it creates a completion using the OpenAI API, passing in the model, prompt, max_tokens, and temperature values as parameters.
If the completion is created successfully, it sends a JSON response with the result of the completion to the client.
If there is an error during the request to the OpenAI API, it sends a JSON response with an error message to the client.
This function is used to generate a mystery scene based on the scene input provided by the client and send the generated text back to the client as a response.
In the generate.js file, prompt is a parameter passed to the createCompletion method of the OpenAI API. It represents the text that the API should use as a starting point for generating text.

In the code provided, prompt is generated by calling the generatePrompt function and passing it the scene value as a parameter. The generatePrompt function returns a string that prompts the GPT-3 model to generate a mystery scene with a setting and initial characters with names that the police meet at a crime scene in the style of Arthur Canon Doyle, Agatha Christie, H.P. Lovecraft, or Raymond Chandler.

The prompt value is used by the OpenAI API as the starting point for generating text, which is then returned to the client as a response.

index.js

```
import Head from "next/head";
import { useState } from "react";
import styles from "./index.module.css";


export default function Home() {
  const [temperature, setTemperature] = useState(0.5);
  const [sceneInput, setSceneInput] = useState("");
  const [style, setStyle] = useState("");
  const [result, setResult] = useState();
  console.log(result);

  async function onSubmit(event) {
    event.preventDefault();
    try {
      const response = await fetch("/api/generate", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ 
          scene: sceneInput,
          temperature: temperature, 
        }),
      });

      const data = await response.json();
      if (response.status !== 200) {
        throw data.error || new Error(`Request failed with status ${response.status}`);
      }

      setResult(data.result);
      setSceneInput("");
    } catch(error) {
      // Consider implementing your own error handling logic here
      console.error(error);
      alert(error.message);
    }
  }

  return (
    <div>
      <Head>
        <title>OpenAI Mystery Generator</title>
        <link rel="icon" href="/magglass.png" />
      </Head>

      <main className={styles.main}>
        <img src="/magglass.png" className={styles.icon} />
        <h3>Set a Mystery</h3>
        <form onSubmit={onSubmit}>
          <input
            type="text"
            name="scene"
            placeholder="Enter an location"
            value={sceneInput}
            onChange={(e) => setSceneInput(e.target.value)}
          />
          {/* Add the range slider */}
          <label htmlFor="temperature">Temperature:</label>
          <input
            type="range"
            min="0"
            max="1"
            step="0.1"
            id="temperature"
            value={temperature}
            onChange={(e) => setTemperature(e.target.value)}
          />
          <span>{temperature}</span>

          {/* Add a Styles dropdown menu */}
          <label htmlFor="style">Style:</label>
          <select id="style" value="{style}" onChange={(e) => setStyle(e.target.value)}>
            <option value="Arthur Canon Doyle">Arthur Canon Doyle</option>
            <option value="Agatha Christie">Agatha Christie</option>
            <option value="Raymond Chandler">Raymond Chandler</option>
            <option value="Dashiell Hammet">Dashiell Hammet</option>
            <option value="P.D. James">P.D. James</option>
          </select>
          <span id="selectStyle">{style}</span>

          <input type="submit" value="Generate scene" />

        </form>
        <div className={styles.result}>{result}</div>
        


      </main>
    </div>
  );
}
```

The index.js file exports a functional component that represents the home page of the web application. The component has the following components:

Head: This is a Next.js component that allows you to add elements to the head of the document. In this case, it is used to add a title and a link to a favicon to the head of the document.

useState: This is a React hook that allows you to add state to functional components. In this case, it is used to add two state variables: sceneInput and result. sceneInput is used to store the value of the text input field, and result is used to store the result of the API request.

onSubmit: This is an async function that is called when the form is submitted. It prevents the default form submission behavior, makes a POST request to the /api/generate endpoint with the sceneInput value, and stores the response in the result state variable. If there is an error during the request, it logs the error and displays an alert.

The main layout of the page consists of an image, a form with a text input field and a submit button, and a div element to display the result of the API request. The form has an onSubmit event handler that calls the onSubmit function, and the text input field has an onChange event handler that updates the sceneInput state variable with the current value of the input field.

Overall, this component allows the user to enter a location in the text input field, submit the form, and display the result of the API request on the page.
**********************************************************************

The above wasn't just code generated by ChatGPT, the descriptions were also its output.  One of the best features of working with Chat is its ability to explain code and answer questions.  
It also feels very personal.  The interface is seductive to use.  I even find myself giving it the nickname "Chat", as though it has a name.  
I encourage you to give it a try when doing a project if only to see the novelty.  Below you can find my project details.

[github](https://github.com/muckitymuck/OpenAI-Mystery-Generator)
[live demo](https://open-ai-mystery-generator.vercel.app/)