In this article, you'll learn how to add an AI copilot to Dub.co, an open-source link management system. Using CopilotKit, you'll also learn how to easily create and delete short links, improving the overall user experience of the application.
You can use this as a case-study for how to easily add an AI copilot into any open-source application, not just Dub.co. This will easily make you seem like an AI coding Ninja.
What is an AI Copilot?
An AI copilot, is an in-app AI assistant that helps users answer questions and take actions inside an application. It brings LLM intelligence right into your application.
Some common form factors:
- ChatBot: Context-aware in-app chatbots that can take actions in-app 💬
- AI autocomplete: AI-powered textFields with context-aware autocomplete & insertions 📝
- Co-Agents: In-app AI agents that can dynamically interact with your app & users 🤖
CopilotKit is the leading, most robust, and easiest to use open-source framework for building in-app AI copilots. You can have a fully custom AI copilot running in your app within minutes.
Prerequisites
To fully understand this tutorial, you need to have a basic understanding of React or Next.js.
We'll also make use of the following:
- CopilotKit - an open-source copilot framework for building custom AI chatbots, in-app AI agents, and text areas.
- Docker - an open-source platform that uses containerization technology to make it easier to create, deploy, and run applications
- Docker Compose - a software application for defining and running multi-container Docker applications.
- Python >= 3.8 - for configuring Dub.co.
- OpenAI API Key - to enable us to perform various tasks using the GPT models.
How to set up Dub.co on your local computer
Dub.co is an open-source link management platform that allows users to create, share, and track short links using their own custom domains. It was created by Steven Tey (formerly of Vercel).
To get started with setting up Dub.co on your local computer, follow these steps:
Clone the Dub.co GitHub repository by running the code snippet below.
git clone https://github.com/dubinc/dub.git
Navigate into the dub
folder and install the project dependencies:
pnpm install
Within the apps/web
folder, rename the .env.example
file to .env
.
Create a new Tinybird account, and copy your Admin Auth Token
into the .env
file.
TINYBIRD_API_KEY=<your_admin_auth_token>
Navigate into the packages/tinybird
directory and install the Tinybird CLI using the following command:
pip3 install tinybird-cli
Execute the following command in your terminal and enter your Admin Auth Token
when prompted to authenticate using the Tinybird CLI:
tb auth
Publish the Tinybird datasource and endpoints by running the code snippet below:
tb push
Create an Upstash database and copy the following credentials from the REST API section to the .env
file:
UPSTASH_REDIS_REST_URL=<your_rest_url>
UPSTASH_REDIS_REST_TOKEN=<your_rest_token>
Navigate to the QStash tab and copy the following credentials into the .env
file.
QSTASH_TOKEN=
QSTASH_CURRENT_SIGNING_KEY=
QSTASH_NEXT_SIGNING_KEY=
Next, within the apps/web
directory, run the following command to start the Docker Compose stack:
docker-compose up
Generate the Prisma client and create its database tables using the following commands:
npx prisma generate
npx prisma db push
Dub.co supports multiple authentication methods. Create a GitHub app and copy the URL below as its callback URL.
http://localhost:8888/api/auth/callback/github
Finally, start the development server:
pnpm dev
Access the web application by navigating to http://localhost:8888
in your browser, create a workspace, and get started. If you encounter any issues, refer to the complete installation guide for detailed assistance.
How to integrate CopilotKit to Dub.co
In this section, you'll learn how to add an AI copilot to Dub.co using CopilotKit.
Visit the OpenAI Developers' Platform and create a new secret key.
Add your newly generated secret key and specify the OpenAI model in your .env
file as follows:
OPENAI_API_KEY=<YOUR_OPENAI_SECRET_KEY>
OPENAI_MODEL=gpt-4-1106-preview
Navigate into the app/api
folder and create a copilotkit
directory containing a route.ts
file.
cd app/api
mkdir copilotkit && cd copilotkit
touch route.ts
Copy the following the following code snippet into the api/copilotkit/route.ts
file:
import { CopilotRuntime, OpenAIAdapter } from "@copilotkit/backend";
export const runtime = "edge";
export async function POST(req: Request): Promise<Response> {
const copilotKit = new CopilotRuntime({});
const openaiModel = process.env["OPENAI_MODEL"];
return copilotKit.response(req, new OpenAIAdapter({ model: openaiModel }));
}
The CopilotKitRuntime instance accept users’ requests and make decisions using the OpenAI model.
To connect Dub.co to the backend API route, update the page.tsx
within the app.dub.co/(dashboard)/[slug]
as show below:
"use client";
import WorkspaceLinksClient from "./page-client";
import { CopilotKit } from "@copilotkit/react-core";
import { CopilotPopup } from "@copilotkit/react-ui";
import "@copilotkit/react-ui/styles.css";
export default function WorkspaceLinks() {
return (
<CopilotKit runtimeUrl="/api/copilotkit/">
<WorkspaceLinksClient />;
<CopilotPopup
instructions="Help the user create and delete links from the workspace"
defaultOpen={true}
labels={{
title: "Dub.co Copilot",
initial:
"Hello there! I can help you create, edit, and delete short links in your workspace.",
}}
clickOutsideToClose={false}
></CopilotPopup>
</CopilotKit>
);
}
The CopilotKit component wraps the entire application and accepts a runtimeUrl
prop containing a link to the API endpoint. The CopilotKitPopup
component adds a chatbot sidebar panel to the application, enabling us to provide various instructions to CopilotKit.
How to perform various actions using CopilotKit
CopilotKit provides two hooks that enable us to handle user's request and plug into the application state: useCopilotAction
and useCopilotReadable
.
The useCopilotAction
hook allows you to define actions to be carried out by CopilotKit. It accepts an object containing the following parameters:
- name - the action's name.
- description - the action's description.
- parameters - an array containing the list of the required parameters.
- render - the default custom function or string.
- handler - the executable function that is triggered by the action.
useCopilotAction({
name: "sayHello",
description: "Say hello to someone.",
parameters: [
{
name: "name",
type: "string",
description: "name of the person to say greet",
},
],
render: "Process greeting message...",
handler: async ({ name }) => {
alert(`Hello, ${name}!`);
},
});
The useCopilotReadable
hook passes the application state into CopilotKit.
import { useCopilotReadable } from "@copilotkit/react-core";
const myAppState = "...";
useCopilotReadable({
description: "The current state of the app",
value: myAppState,
});
Now, let's plug the application states into CopilotKit to perform various actions, such as creating and deleting the short links.
Navigate into the ui/links/links-container.tsx
folder and update the LinksContainer
function as shown below:
export default function LinksContainer({
AddEditLinkButton,
}: {
AddEditLinkButton: () => JSX.Element;
}) {
const { viewMode, sort, showArchived } = useContext(LinksDisplayContext);
const { links, isValidating } = useLinks({ sort, showArchived });
const { data: count } = useLinksCount({ showArchived });
//👇🏻 React state for all links
const [updatedLinks, setUpdatedLinks] = useState<ResponseLink[]>(links || []);
//👇🏻 update the state with all the links
useEffect(() => {
setUpdatedLinks(links || []);
}, [links]);
useCopilotReadable({
description:
"This is the list of links you have saved. You can click on a link to view it, or use the search bar to find a specific link.",
value: updatedLinks,
});
return (
<MaxWidthWrapper className="grid gap-y-2">
<LinksList
AddEditLinkButton={AddEditLinkButton}
links={links}
count={count}
loading={isValidating}
compact={viewMode === "rows"}
/>
<DeleteLinkModal />
</MaxWidthWrapper>
);
}
The updatedLinks
React state stores the links created within the application and the useCopilotReadable
passes the links into CopilotKit.
Below the useCopilotReadable
hook, add the following code snippet to allow users to delete links from the application:
useCopilotAction({
name: "deleteShortLink",
description: "delete a link from the database via its ID",
parameters: [
{
name: "id",
type: "string",
description: "The ID of a short link",
required: true,
},
],
render: "Deleting link...",
handler: async ({ id }) => {
if (!id) return;
const link = updatedLinks?.find((link) => link.id === id);
if (!link) return;
setSelectedLink(link);
setShowDeleteLinkModal(true);
},
});
To allow users to create links using the AI copilot, navigate to the index.tsx
file within the modals/add-edit-link-modal
directory and update the useAddEditLinkModal
function as shown below:
export function useAddEditLinkModal({
props,
duplicateProps,
homepageDemo,
}: {
props?: LinkWithTagsProps;
duplicateProps?: LinkWithTagsProps;
homepageDemo?: boolean;
} = {}) {
const [updatedProps, setUpdatedProps] = useState(props || DEFAULT_LINK_PROPS);
const [showAddEditLinkModal, setShowAddEditLinkModal] = useState(false);
const [generatingRandomKey, setGeneratingRandomKey] = useState(false);
const [keyError, setKeyError] = useState<string | null>(null);
const { id: workspaceId } = useWorkspace();
const { primaryDomain } = useDomains();
const getKey = async (domain: string) => {
setKeyError(null);
setGeneratingRandomKey(true);
const res = await fetch(
`/api/links/random?domain=${domain}&workspaceId=${workspaceId}`,
);
const key = await res.json();
return key;
};
useCopilotAction({
name: "createNewLink",
description: "Create a new link",
parameters: [
{
name: "url",
type: "string",
description: "The destination URL for the short link",
required: true,
},
],
render: "Loading...",
handler: async ({ url }) => {
const key = await getKey(primaryDomain);
setUpdatedProps((prev) => ({
...prev,
url,
domain: primaryDomain,
key,
id: "",
}));
setGeneratingRandomKey(false);
setShowAddEditLinkModal(true);
},
});
const AddEditLinkModalCallback = useCallback(() => {
return (
<AddEditLinkModal
showAddEditLinkModal={showAddEditLinkModal}
setShowAddEditLinkModal={setShowAddEditLinkModal}
props={updatedProps}
keyError={keyError}
setKeyError={setKeyError}
generatingRandomKey={generatingRandomKey}
setGeneratingRandomKey={setGeneratingRandomKey}
duplicateProps={duplicateProps}
homepageDemo={homepageDemo}
/>
);
}, [showAddEditLinkModal, setShowAddEditLinkModal]);
//👉🏻 other functions
}
- From the code snippet provided:
- The
updatedProps
state contains the data structure for each link. - The
getKey()
function generates a unique key that is also included in the short links created. - The
useCopilotAction
function accepts the URL to be shortened and displays a modal that allows the user to confirm and save the link.
- The
Congratulations! You have successfully integrated CopilotKit into Dub.co. You can access the source code in this GitHub repository.
Here is a short video showing how CopilotKit works with Dub.co:
Conclusion
CopilotKit is an incredible tool that allows you to add AI Copilots to your products within minutes. Whether you're interested in AI chatbots and assistants or automating complex tasks, CopilotKit makes it easy.
If you need to build an AI product or integrate an AI tool into your software applications, you should consider CopilotKit.
You can find the source code for this tutorial on GitHub:
https://github.com/dha-stix/dub-with-copilotkit
Thank you for reading!