We’ll build a command-line chatbot that supports multiple conversations, automatic session persistence, and restore. The same method can be applied to create many other kinds of services, including API Gateways, message queues, and timers.
Environment Setup
No VS Code, no vim — just open the Plutolang CodeSandbox and click "Fork" in the top right to create your own project environment. If you prefer not to write any code, you can fork a pre‑prepared environment that already contains the code; then you only need to configure your AWS credentials and an OpenAI API key.
Modifying the Code
First, add the OpenAI dependency. Open package.json and add the following line inside dependencies:
"openai": "^4.13.0"
Save with Cmd/Ctrl+S, then click the terminal icon in the lower console and run the Install task to download all npm dependencies.
Now open src/index.ts to write the business logic. We’ll define a key‑value store to persist conversations and then set up two HTTP routes: one for creating a session and one for chatting.
1. Import the required libraries.
import OpenAI from "openai";
import { Router, KVStore, HttpRequest, HttpResponse } from "@plutolang/pluto";
2. Define the KV database and router resources.
The key‑value store saves conversatinos, keyed by chatbot name with the message history as the value. The router behaves like a typical web server (similar to express).
const chatDatabase = new KVStore("conversations");
const appRouter = new Router("chatbot");
3. Route: create a new chat session.
Add a POST handler at /init. The query parameter bot gives the chatbot a name, and the request body defines its role (e.g. "a senior frontned engineer"). A new key‑value pair is stored to initialise the session.
appRouter.post("/init", async (req: HttpRequest): Promise<HttpResponse> => {
const botId = req.query["bot"];
if (!botId) {
return {
statusCode: 400,
body: "Missing bot parameter. Please provide a name and a system message to define the assistant's behaviour.",
};
}
const systemMessage = req.body;
const conversation = [{ role: "system", content: systemMessage }];
await chatDatabase.set(botId, JSON.stringify(conversation));
return {
statusCode: 200,
body: "Session created. You can now chat with your bot.",
};
});
4. Route: send a message and get a reply.
You’ll need an OpenAI API key. Create a new secret key and keep it safe; you won’t be able to view it again later.
Replace OPENAI_API_KEY with your key. If you have access to GPT‑4, you may also change MODEL accordingly.
appRouter.post("/converse", async (req: HttpRequest): Promise<HttpResponse> => {
// Replace with your OpenAI API key – never expose it publicly.
const API_KEY = "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
// Choose your model. See https://platform.openai.com/docs/models
const MODEL = "gpt-3.5-turbo";
const botId = req.query["bot"] ?? "default";
const userInput = req.body;
console.debug("New message – Bot:", botId, "Content:", userInput);
const rawHistory = await chatDatabase.get(botId).catch(() => undefined);
const history = rawHistory ? JSON.parse(rawHistory) : [];
history.push({ role: "user", content: userInput });
const client = new OpenAI({ apiKey: API_KEY });
const completion = await client.chat.completions.create({
messages: history,
model: MODEL,
});
const choices = completion.choices;
if (choices.length === 0 || choices[0].message.content == null) {
console.error("OpenAI response:", completion);
return {
statusCode: 500,
body: "Unexpected response from OpenAI. Please try again later.",
};
}
const assistantMsg = choices[0].message;
// Keep the conversation whole by persisting the assistant reply.
history.push(assistantMsg);
await chatDatabase.set(botId, JSON.stringify(history));
return {
statusCode: 200,
body: assistantMsg.content!,
};
});
Quick Deployment
Configure AWS Credentials
In the lower console, switch to the Configure AWS Certificate tab and enter your AWS Access Key and Secret Access Key. (If you don’t know how to create these, follow the AWS documentation.) Leave the output format empty and press Enter; a checkmark ✔ will appear when the credentials are set.
One‑Click Publish
Click the terminal icon and run the Deploy task. Wait a minute or two until a URL is printed.
Chatting with the Bot
A Shell script named chat is provided below. Save it anywhere (e.g., in the project root) and make it executable. It will prompt you for the deployment URL and let you either create a new bot or continue an existing conversation.
#!/bin/bash
read -p "Enter the URL output by Pluto: " URL
if [ -z "$URL" ]; then
echo "No URL provided."
exit 1
fi
echo "Choose mode:"
echo " 1) create a new bot"
echo " 2) select an existing bot"
read -p "> " mode
if [[ -z "$mode" || ( "$mode" != 1 && "$mode" != 2 ) ]]; then
echo "Invalid choice."
exit 1
fi
read -p "Bot name: " bot_name
if [ -z "$bot_name" ]; then
echo "Invalid name."
exit 1
fi
if [[ "$mode" -eq 1 ]]; then
echo -e "\nHello, I'm $bot_name. Describe the role I should play:"
read -p "> " system_msg
if [[ -n "$system_msg" ]]; then
curl -s -X POST "$URL/init?bot=$bot_name" -d "$system_msg" -H 'Content-type: text/plain' > /dev/null
fi
fi
echo -e "\nChat ready. Type 'q' to quit."
while :
do
read -p "> " user_msg
if [[ "$user_msg" == "q" ]]; then
echo "Bye! 👋"
break
fi
curl -X POST "$URL/converse?bot=$bot_name" -d "$user_msg" -H 'Content-type: text/plain'
echo -e "\n"
done
To use it on the Web IDE, open a New Terminal and run:
bash ./chat
Full Code
import OpenAI from "openai";
import { Router, KVStore, HttpRequest, HttpResponse } from "@plutolang/pluto";
const chatDatabase = new KVStore("conversations");
const appRouter = new Router("chatbot");
appRouter.post("/converse", async (req: HttpRequest): Promise<HttpResponse> => {
const API_KEY = "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const MODEL = "gpt-3.5-turbo";
const botId = req.query["bot"] ?? "default";
const userInput = req.body;
console.debug("New message – Bot:", botId, "Content:", userInput);
const rawHistory = await chatDatabase.get(botId).catch(() => undefined);
const history = rawHistory ? JSON.parse(rawHistory) : [];
history.push({ role: "user", content: userInput });
const client = new OpenAI({ apiKey: API_KEY });
const completion = await client.chat.completions.create({
messages: history,
model: MODEL,
});
const choices = completion.choices;
if (choices.length === 0 || choices[0].message.content == null) {
console.error("OpenAI response:", completion);
return {
statusCode: 500,
body: "Unexpected response from OpenAI. Please try again later.",
};
}
const assistantMsg = choices[0].message;
history.push(assistantMsg);
await chatDatabase.set(botId, JSON.stringify(history));
return {
statusCode: 200,
body: assistantMsg.content!,
};
});
appRouter.post("/init", async (req: HttpRequest): Promise<HttpResponse> => {
const botId = req.query["bot"];
if (!botId) {
return {
statusCode: 400,
body: "Missing bot parameter. Please provide a name and a system message to define the assistant's behaviour.",
};
}
const systemMessage = req.body;
const conversation = [{ role: "system", content: systemMessage }];
await chatDatabase.set(botId, JSON.stringify(conversation));
return {
statusCode: 200,
body: "Session created. You can now chat with your bot.",
};
});