chattr
Version:
A chatgpt chatbot component library for nextjs.
524 lines (442 loc) • 17.7 kB
Markdown
[](https://opensource.org/licenses/MIT)
[](https://www.npmjs.com/package/chattr)
<p><a href="https://www.npmjs.com/package/chattr"><img style='border-radius: 100%; filter: drop-shadow(0px 9px 10px #b0b0b0);' src="https://i.ibb.co/8sXcdcV/chattrlogo.webp" width='350px' height='350px' alt="Chattr logo generated by a function call from chattr" /></a></p>
# chattr
A customizable chatgpt chatbot component library for Nextjs. Built with React, Tailwindcss, OpenAI, and Typescript.
### Table of Contents
**[Installation Instructions](#installation)**<br>
**[Configuration Instructions](#configuration)**<br>
**[Themes](#themes)**<br>
**[Endpoints](#endpoints)**<br>
**[Function Calling](#function-calling)**<br>
**[Usage](#usage)**<br>
**[Customizations](#customizations)**<br>
**[Create Chattr App](#create-chattr-app)**<br>
**[License](#license)**<br>
**[Contributing](#contributing)**<br>
**[Future Development](#future-development)**<br>
**[Author](#author)**<br>
**[Sponsors](#sponsors)**<br>
# Installation
```bash
npm i chattr
```
# Configuration
Before using `chattr`, we need to configure a few things. First, ensure that you are on the latest versions of `react, react-dom, and tailwindcss`. Feel free to try other versions, however do note that they have not been tested. **Make sure tailwindcss is in dependencies and not devDependencies**
```json
"react": "^18.2.0",
"react-dom": "^18.2.0",
"tailwindcss": "^3.3.6"
```
If you aren't on the latest versions, start a **new** branch. Then update your dependencies on that branch:
```bash
npm i react react-dom tailwindcss /react@latest @types/react-dom
```
Next, make sure that your `tailwind.config.ts` file includes the following:
```typescript
// tailwind.config.ts
{
//... Other configs ...,
theme: {
extend: {
colors: {
chattrWhite: 'rgb(255 255 255 / 1)', // white
chattrBlack: 'rgb(24 24 27 / 1)', // zinc-900
chattrPitchBlack: 'rgb(9 9 11 / 1)', // zinc-950
chattrPrimary: 'rgb(139 92 246 / 1)', // violet-500
chattrPrimaryDark: 'rgb(124 58 237 / 1)', // violet-600
chattrSecondary: 'rgb(113 113 122 / 0.8)', // zinc-500/80
chattrSecondaryDark: 'rgb(244 244 245 / 0.6)', //zinc-100/60
chattrGray: 'rgb(212 212 216 / 0.9)', // zinc-300/90
chattrGrayDark: 'rgb(244 244 245 / 0.15)', //zinc-100/15
chattrText: 'rgb(39 39 42 / 1)', // zinc-800
chattrTextDark: 'rgb(244 244 245 / 1)', // zinc-100
chattrBackgroundMuted: 'rgb(228 228 231 / 0.7)', //zinc-200/70
},
borderRadius: {
chattrRoundedSmall: '0.5rem',
chattrRoundedMedium: '0.85rem',
chattrRoundedLarge: '1rem',
},
animation: {
chattrLoader: 'chattrLoader 0.5s infinite alternate',
},
keyframes: {
chattrLoader: {
from: {
opacity: '1',
transform: 'translate3d(0, 0, 0)',
},
to: {
opacity: '0.25',
transform: 'translate3d(0, -0.2rem, 0)',
},
},
},
},
},
plugins: [],
}
export default config
```
And your `globals.css` file looks like this:
```css
base;
components;
utilities;
/* Animation styles for the ChattrLoader components */
utilities {
.animation-delay-200 {
animation-delay: 0.15s;
}
.animation-delay-400 {
animation-delay: 0.3s;
}
}
/* Dot styles for the ChattrLoader components */
.chattrDotDefault {
bg-chattrWhite mx-0.5 h-[6px] w-[6px] rounded-full;
}
.chattrDotMinimalist {
bg-chattrSecondary dark:bg-chattrSecondaryDark mx-0.5 h-[6px] w-[6px] rounded-full;
}
```
This is for the overall chattr styles, and a custom loader that comes shipped with the chatbot in between states of sent messages. You can customize it, or create your own!
Next, you need an `OPENAI_API_KEY`. If you don't have one already, click [here](https://platform.openai.com/api-keys) to get one.
Once you have your key, install `dotenv` if required and create a `.env` file in the root of your project. Insert your api key there, along with any other api keys if you plan on using [function calling](#function-calling). **In production, remember to copy your api key, to your environment variables section.**
```bash
OPENAI_API_KEY='YOUR_OPENAI_API_KEY'
WEATHER_APP_ID='YOUR_OPENWEATHERMAPS_API_KEY'
REPLICATE_API_TOKEN='YOUR_REPLICATE_TOKEN'
```
# Themes
Chattr currently has two themes- `Default` and `Minimalist`. The default theme was styled by me, and the minimalist theme was styled originally by [shadcn](https://ui.shadcn.com/themes) and has been highly customized to be used as a chattrbot. Shadcn himself even [reposted](https://twitter.com/cbmonx/status/1729318382355587335) the tweet of his chat component being used in chattr!
To see how the themes work, please visit the chattr [repo](https://www.github.com/cbmongithub/chattr)
# Endpoints
In order to use chattr, you have to create an endpoint that handles a post request to the chatGpt completions api.
If you're using the `Default.Chattrbot`, you can copy and paste this route to `app/api/chat-gpt/route.ts` as a starting point:
```typescript
// app/api/chat-gpt/route.ts
import { NextRequest, NextResponse } from 'next/server'
export async function POST(req: NextRequest) {
try {
const {
prompt,
chattrBotName,
chattrBotHistory,
}: {
prompt: string
chattrBotName: string | number
chattrBotHistory: string
} = await req.json()
const chatHistory = JSON.stringify(chattrBotHistory)
const payload = {
model: 'gpt-4-1106-preview',
messages: [
{
role: 'system',
content: `
You are a chatbot named ${chattrBotName}.
Respond with any information that the user requests.
You can view the entire chat history here, where your role is the assistant, and the users role is user: ${chatHistory}.
This history is helpful if you need to recall any information or understand context from chat.
Use a professional tone in your responses.`,
},
{
role: 'assistant',
content: `Hey! Thanks for visiting. I'm ${chattrBotName}, you can ask me anything!`, // Replace with your own greeting
},
{
role: 'user',
content: prompt, // The users prompt
},
],
temperature: 0.7, // Your configs
frequency_penalty: 0,
presence_penalty: 0,
max_tokens: 75,
n: 1,
}
const response: Response = await fetch(
'https://api.openai.com/v1/chat/completions',
{
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
},
method: 'POST',
body: JSON.stringify(payload),
}
)
if (!response.ok) {
return NextResponse.json({
ok: false,
error:
'Looks like something went wrong fetching that answer! Try again later.',
})
}
const completion = await response.json()
return NextResponse.json({
ok: true,
content: { text: completion.choices[0].message.content },
})
} catch (error) {
console.log(error)
return NextResponse.json({
ok: false,
error: 'Looks like something went wrong. Try again later.',
})
}
}
```
You can view the default chattrbot and components [here](https://www.github.com/cbmongithub/chattr/blob/main/src/components/default) to understand how it works with the `app/api/chat-gpt/route.ts` route.
# Function calling
If you're using the `Minimalist.Chattrbot`, the route is a lot different in that it uses open ai's function calling feature.
A solution for the ui that I came up with is using a key value pair within the response object to tell the client what type of component to render. You can see it in action [here](https://www.github.com/cbmongithub/chattr/blob/main/src/components/minimalist/chattr-messages.tsx).
You can copy and paste the following as a starting point to `app/api/function-calling/route.ts`. Make sure to install `replicate` and `dayjs` if you'd like to use the `create_video` and `get_current_weather` functions in this example.
```typescript
// app/api/function-calling/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { get_current_weather, create_image, create_video } from 'chattr'
export async function POST(req: NextRequest) {
try {
const {
prompt,
chattrBotName,
chattrBotHistory,
}: {
prompt: string
chattrBotName: string | number
chattrBotHistory: string
} = await req.json()
const chatHistory = JSON.stringify(chattrBotHistory)
const payload = {
model: 'gpt-4-1106-preview',
messages: [
{
role: 'system',
content: `
You are a chatbot named ${chattrBotName}.
Respond with any information that the user requests.
You can view the entire chat history here, where your role is the assistant, and the users role is user: ${chatHistory}.
This history is helpful if you need to recall any information or understand context from chat.
Use a professional tone in your responses.`,
},
{
role: 'assistant',
content: `Hey! Thanks for visiting. I'm ${chattrBotName}, you can ask me anything!`,
},
{
role: 'user',
content: prompt,
},
],
functions: [
// Define your functions see more at https://platform.openai.com/docs/guides/function-calling
{
name: 'get_current_weather',
description: 'Get the current weather',
parameters: {
type: 'object',
properties: {
zipcode: {
type: 'string',
description:
'The zipcode of the city. For example, 90210 for Beverly Hills. If the user passes in a city, retrieve any zip code for that city and use it as the zipcode value.',
},
state: {
type: 'string',
description:
'The state of the city. For example: CA if the user asks for the weather in Beverly Hills, or UT if the user asks for the weather in Salt Lake City, etc. If the user passes a zip code or a city name as a zip code, retrieve the state that belongs to that zip code and use it as the state value.',
},
},
required: ['zipcode', 'state'],
},
},
{
name: 'create_image',
description: 'Create an image for the given description',
parameters: {
type: 'object',
properties: {
description: {
type: 'string',
description: 'Description of what the image should be.',
},
},
required: ['description'],
},
},
{
name: 'create_video',
description: 'Create a video for a given description',
parameters: {
type: 'object',
properties: {
description: {
type: 'string',
description: 'Description of what the video should be.',
},
},
required: ['description'],
},
},
],
function_call: 'auto', // the completions api will automatically call the functions for you
temperature: 0.7,
frequency_penalty: 0,
presence_penalty: 0,
max_tokens: 75,
n: 1,
}
const response: Response = await fetch(
'https://api.openai.com/v1/chat/completions',
{
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
},
method: 'POST',
body: JSON.stringify(payload),
}
)
const completion = await response.json()
if (completion.choices[0].message.content === null) {
// If the content is null, that means it's a function call
const args = JSON.parse(
completion.choices[0].message.function_call.arguments
)
const functionCall = completion.choices[0].message.function_call.name
if (functionCall === 'get_current_weather') {
const {
temperature,
celcius,
location,
url,
description,
humidity,
wind,
clouds,
state,
} = await get_current_weather(args.zipcode, args.state)
return NextResponse.json({
ok: true,
ui: 'weather',
content: {
function_response: {
temperature: temperature,
celcius: celcius,
location: location,
url: url,
description: description,
humidity: humidity,
wind: wind,
clouds: clouds,
state: state,
},
},
})
} else if (functionCall === 'create_image') {
const { description, url } = await create_image(args.description)
return NextResponse.json({
ok: true,
ui: 'image',
content: {
function_response: {
description: description,
url: url,
},
},
})
} else if (functionCall === 'create_video') {
const { description, url } = await create_video(args.description)
return NextResponse.json({
ok: true,
ui: 'video',
content: {
function_response: {
description: description,
url: url,
},
},
})
} else {
return NextResponse.json({
ok: false,
error:
'Looks like something went wrong while generating that. Please try again! If the problem persists, let us know at hello.com.',
})
}
} else {
return NextResponse.json({
ok: true,
content: { text: completion.choices[0].message.content },
})
}
} catch (error) {
console.log(error)
return NextResponse.json({ ok: false, error: JSON.stringify(error) })
}
}
```
You can view the minimalist chattrbot [here](https://www.github.com/cbmongithub/chattr/blob/main/src/components/minimalist) to understand how it works with the `app/api/function-calling/route.ts` route.
It's worth mentioning that you should protect your routes with some type of authentication, or at the very least, use a rate limiter. `/ratelimit @upstash/redis` is a great option. You can view the package [here](https://www.npmjs.com/package/@upstash/ratelimit).
# Usage
After you have setup the route you need, you can import a chattrbot! Just wrap it in a separate component with the `use client` directive:
```tsx
// components/chattr-example.tsx
'use client'
import { Default } from 'chattr'
//import { Minimalist } from 'chattr for function calling'
export default function ChattrExample() {
return <Default.Chattrbot />
//return <Minimalist.Chattrbot/>
}
```
Then import it wherever you'd like. For example, in your layout component:
```tsx
// app/layout.tsx
import {
ThemeProvider,
Background,
Navigation,
Footer,
ChattrExample,
} from '@/components'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html
lang='en'
suppressHydrationWarning>
<body>
<ThemeProvider attribute='class'>
<Navigation />
<Background />
<main>{children}</main>
<Footer />
<ChattrExample />
</ThemeProvider>
</body>
</html>
)
}
```
# Customizations
If you need more control over the styles, you can view the chattr repo [here](https://www.github.com/cbmongithub/chattr). Copy existing code, create new bots, expand on existing bots, etc. The code is open source and yours to use!
# Create Chattr App
If you need full customization over the components themselves, checkout the newly shipped [create-chattr-app](https://www.github.com/cbmongithub/create-chattr-app)! It is a Next js boilerplate that ships with all chatbot component files, routes, and a landing page already setup for you. All you need is your api keys!
# License
This project is covered under the [MIT](https://opensource.org/licenses/MIT) license.
# Contributing
If you would like to contribute, feel free to open a pull request! Experienced library developers would be awesome as this is my first real library.
# Future Development
More themes are coming, such as full screen layout instead of a widget. If you have any ideas, please [let me know](mailto:hello.com?subject=Chattr%20%Suggestion) about them!
# Author
Made with <3 by [Christian B. Martinez](https://christianbmartinez.com). Lets connect on [Github](https://www.github.com/cbmongithub) or [X (twitter)](https://www.twitter.com/cbmonx)!
Hiring? I would love the opportunity to become apart of your team! [contact me](mailto:hello.com?subject=Join%20%our%20Team) anytime.
# Sponsors
If you like the project and it adds value to you, feel free to [sponsor me](https://github.com/sponsors/christianbmartinez) if you'd like!