- OpenChat (formerly AI Chatbot) is a free, open-source template built with Next.js and the AI SDK that helps you quickly build powerful chatbot applications.
+ Chatbot (formerly AI Chatbot) is a free, open-source template built with Next.js and the AI SDK that helps you quickly build powerful chatbot applications.
- Read Docs ·
+ Read Docs ·
Features ·
Model Providers ·
Deploy Your Own ·
@@ -48,13 +48,13 @@ With the [AI SDK](https://ai-sdk.dev/docs/introduction), you can also switch to
## Deploy Your Own
-You can deploy your own version of OpenChat to Vercel with one click:
+You can deploy your own version of Chatbot to Vercel with one click:
-[](https://vercel.com/templates/next.js/openchat)
+[](https://vercel.com/templates/next.js/chatbot)
## Running locally
-You will need to use the environment variables [defined in `.env.example`](.env.example) to run OpenChat. It's recommended you use [Vercel Environment Variables](https://vercel.com/docs/projects/environment-variables) for this, but a `.env` file is all that is necessary.
+You will need to use the environment variables [defined in `.env.example`](.env.example) to run Chatbot. It's recommended you use [Vercel Environment Variables](https://vercel.com/docs/projects/environment-variables) for this, but a `.env` file is all that is necessary.
> Note: You should not commit your `.env` file or it will expose secrets that will allow others to control access to your various AI and authentication provider accounts.
diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts
index a659bdf3fd..cd1169785b 100644
--- a/app/(chat)/api/chat/route.ts
+++ b/app/(chat)/api/chat/route.ts
@@ -30,7 +30,7 @@ import {
updateMessage,
} from "@/lib/db/queries";
import type { DBMessage } from "@/lib/db/schema";
-import { OpenChatError } from "@/lib/errors";
+import { ChatbotError } from "@/lib/errors";
import type { ChatMessage } from "@/lib/types";
import { convertToUIMessages, generateUUID } from "@/lib/utils";
import { generateTitleFromUserMessage } from "../../actions";
@@ -55,7 +55,7 @@ export async function POST(request: Request) {
const json = await request.json();
requestBody = postRequestBodySchema.parse(json);
} catch (_) {
- return new OpenChatError("bad_request:api").toResponse();
+ return new ChatbotError("bad_request:api").toResponse();
}
try {
@@ -65,7 +65,7 @@ export async function POST(request: Request) {
const session = await auth();
if (!session?.user) {
- return new OpenChatError("unauthorized:chat").toResponse();
+ return new ChatbotError("unauthorized:chat").toResponse();
}
const userType: UserType = session.user.type;
@@ -76,7 +76,7 @@ export async function POST(request: Request) {
});
if (messageCount > entitlementsByUserType[userType].maxMessagesPerDay) {
- return new OpenChatError("rate_limit:chat").toResponse();
+ return new ChatbotError("rate_limit:chat").toResponse();
}
const isToolApprovalFlow = Boolean(messages);
@@ -87,7 +87,7 @@ export async function POST(request: Request) {
if (chat) {
if (chat.userId !== session.user.id) {
- return new OpenChatError("forbidden:chat").toResponse();
+ return new ChatbotError("forbidden:chat").toResponse();
}
if (!isToolApprovalFlow) {
messagesFromDb = await getMessagesByChatId({ id });
@@ -244,7 +244,7 @@ export async function POST(request: Request) {
} catch (error) {
const vercelId = request.headers.get("x-vercel-id");
- if (error instanceof OpenChatError) {
+ if (error instanceof ChatbotError) {
return error.toResponse();
}
@@ -254,11 +254,11 @@ export async function POST(request: Request) {
"AI Gateway requires a valid credit card on file to service requests"
)
) {
- return new OpenChatError("bad_request:activate_gateway").toResponse();
+ return new ChatbotError("bad_request:activate_gateway").toResponse();
}
console.error("Unhandled error in chat API:", error, { vercelId });
- return new OpenChatError("offline:chat").toResponse();
+ return new ChatbotError("offline:chat").toResponse();
}
}
@@ -267,19 +267,19 @@ export async function DELETE(request: Request) {
const id = searchParams.get("id");
if (!id) {
- return new OpenChatError("bad_request:api").toResponse();
+ return new ChatbotError("bad_request:api").toResponse();
}
const session = await auth();
if (!session?.user) {
- return new OpenChatError("unauthorized:chat").toResponse();
+ return new ChatbotError("unauthorized:chat").toResponse();
}
const chat = await getChatById({ id });
if (chat?.userId !== session.user.id) {
- return new OpenChatError("forbidden:chat").toResponse();
+ return new ChatbotError("forbidden:chat").toResponse();
}
const deletedChat = await deleteChatById({ id });
diff --git a/app/(chat)/api/document/route.ts b/app/(chat)/api/document/route.ts
index 2caa241f6e..fe912a1e61 100644
--- a/app/(chat)/api/document/route.ts
+++ b/app/(chat)/api/document/route.ts
@@ -5,14 +5,14 @@ import {
getDocumentsById,
saveDocument,
} from "@/lib/db/queries";
-import { OpenChatError } from "@/lib/errors";
+import { ChatbotError } from "@/lib/errors";
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const id = searchParams.get("id");
if (!id) {
- return new OpenChatError(
+ return new ChatbotError(
"bad_request:api",
"Parameter id is missing"
).toResponse();
@@ -21,7 +21,7 @@ export async function GET(request: Request) {
const session = await auth();
if (!session?.user) {
- return new OpenChatError("unauthorized:document").toResponse();
+ return new ChatbotError("unauthorized:document").toResponse();
}
const documents = await getDocumentsById({ id });
@@ -29,11 +29,11 @@ export async function GET(request: Request) {
const [document] = documents;
if (!document) {
- return new OpenChatError("not_found:document").toResponse();
+ return new ChatbotError("not_found:document").toResponse();
}
if (document.userId !== session.user.id) {
- return new OpenChatError("forbidden:document").toResponse();
+ return new ChatbotError("forbidden:document").toResponse();
}
return Response.json(documents, { status: 200 });
@@ -44,7 +44,7 @@ export async function POST(request: Request) {
const id = searchParams.get("id");
if (!id) {
- return new OpenChatError(
+ return new ChatbotError(
"bad_request:api",
"Parameter id is required."
).toResponse();
@@ -53,7 +53,7 @@ export async function POST(request: Request) {
const session = await auth();
if (!session?.user) {
- return new OpenChatError("not_found:document").toResponse();
+ return new ChatbotError("not_found:document").toResponse();
}
const {
@@ -69,7 +69,7 @@ export async function POST(request: Request) {
const [doc] = documents;
if (doc.userId !== session.user.id) {
- return new OpenChatError("forbidden:document").toResponse();
+ return new ChatbotError("forbidden:document").toResponse();
}
}
@@ -90,14 +90,14 @@ export async function DELETE(request: Request) {
const timestamp = searchParams.get("timestamp");
if (!id) {
- return new OpenChatError(
+ return new ChatbotError(
"bad_request:api",
"Parameter id is required."
).toResponse();
}
if (!timestamp) {
- return new OpenChatError(
+ return new ChatbotError(
"bad_request:api",
"Parameter timestamp is required."
).toResponse();
@@ -106,7 +106,7 @@ export async function DELETE(request: Request) {
const session = await auth();
if (!session?.user) {
- return new OpenChatError("unauthorized:document").toResponse();
+ return new ChatbotError("unauthorized:document").toResponse();
}
const documents = await getDocumentsById({ id });
@@ -114,7 +114,7 @@ export async function DELETE(request: Request) {
const [document] = documents;
if (document.userId !== session.user.id) {
- return new OpenChatError("forbidden:document").toResponse();
+ return new ChatbotError("forbidden:document").toResponse();
}
const documentsDeleted = await deleteDocumentsByIdAfterTimestamp({
diff --git a/app/(chat)/api/history/route.ts b/app/(chat)/api/history/route.ts
index c7223ec260..f8b7f9ed48 100644
--- a/app/(chat)/api/history/route.ts
+++ b/app/(chat)/api/history/route.ts
@@ -1,7 +1,7 @@
import type { NextRequest } from "next/server";
import { auth } from "@/app/(auth)/auth";
import { deleteAllChatsByUserId, getChatsByUserId } from "@/lib/db/queries";
-import { OpenChatError } from "@/lib/errors";
+import { ChatbotError } from "@/lib/errors";
export async function GET(request: NextRequest) {
const { searchParams } = request.nextUrl;
@@ -11,7 +11,7 @@ export async function GET(request: NextRequest) {
const endingBefore = searchParams.get("ending_before");
if (startingAfter && endingBefore) {
- return new OpenChatError(
+ return new ChatbotError(
"bad_request:api",
"Only one of starting_after or ending_before can be provided."
).toResponse();
@@ -20,7 +20,7 @@ export async function GET(request: NextRequest) {
const session = await auth();
if (!session?.user) {
- return new OpenChatError("unauthorized:chat").toResponse();
+ return new ChatbotError("unauthorized:chat").toResponse();
}
const chats = await getChatsByUserId({
@@ -37,7 +37,7 @@ export async function DELETE() {
const session = await auth();
if (!session?.user) {
- return new OpenChatError("unauthorized:chat").toResponse();
+ return new ChatbotError("unauthorized:chat").toResponse();
}
const result = await deleteAllChatsByUserId({ userId: session.user.id });
diff --git a/app/(chat)/api/suggestions/route.ts b/app/(chat)/api/suggestions/route.ts
index 054994b09b..303f45ed26 100644
--- a/app/(chat)/api/suggestions/route.ts
+++ b/app/(chat)/api/suggestions/route.ts
@@ -1,13 +1,13 @@
import { auth } from "@/app/(auth)/auth";
import { getSuggestionsByDocumentId } from "@/lib/db/queries";
-import { OpenChatError } from "@/lib/errors";
+import { ChatbotError } from "@/lib/errors";
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const documentId = searchParams.get("documentId");
if (!documentId) {
- return new OpenChatError(
+ return new ChatbotError(
"bad_request:api",
"Parameter documentId is required."
).toResponse();
@@ -16,7 +16,7 @@ export async function GET(request: Request) {
const session = await auth();
if (!session?.user) {
- return new OpenChatError("unauthorized:suggestions").toResponse();
+ return new ChatbotError("unauthorized:suggestions").toResponse();
}
const suggestions = await getSuggestionsByDocumentId({
@@ -30,7 +30,7 @@ export async function GET(request: Request) {
}
if (suggestion.userId !== session.user.id) {
- return new OpenChatError("forbidden:api").toResponse();
+ return new ChatbotError("forbidden:api").toResponse();
}
return Response.json(suggestions, { status: 200 });
diff --git a/app/(chat)/api/vote/route.ts b/app/(chat)/api/vote/route.ts
index d2c8ce170a..4a2e4bf412 100644
--- a/app/(chat)/api/vote/route.ts
+++ b/app/(chat)/api/vote/route.ts
@@ -1,13 +1,13 @@
import { auth } from "@/app/(auth)/auth";
import { getChatById, getVotesByChatId, voteMessage } from "@/lib/db/queries";
-import { OpenChatError } from "@/lib/errors";
+import { ChatbotError } from "@/lib/errors";
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const chatId = searchParams.get("chatId");
if (!chatId) {
- return new OpenChatError(
+ return new ChatbotError(
"bad_request:api",
"Parameter chatId is required."
).toResponse();
@@ -16,17 +16,17 @@ export async function GET(request: Request) {
const session = await auth();
if (!session?.user) {
- return new OpenChatError("unauthorized:vote").toResponse();
+ return new ChatbotError("unauthorized:vote").toResponse();
}
const chat = await getChatById({ id: chatId });
if (!chat) {
- return new OpenChatError("not_found:chat").toResponse();
+ return new ChatbotError("not_found:chat").toResponse();
}
if (chat.userId !== session.user.id) {
- return new OpenChatError("forbidden:vote").toResponse();
+ return new ChatbotError("forbidden:vote").toResponse();
}
const votes = await getVotesByChatId({ id: chatId });
@@ -43,7 +43,7 @@ export async function PATCH(request: Request) {
await request.json();
if (!chatId || !messageId || !type) {
- return new OpenChatError(
+ return new ChatbotError(
"bad_request:api",
"Parameters chatId, messageId, and type are required."
).toResponse();
@@ -52,17 +52,17 @@ export async function PATCH(request: Request) {
const session = await auth();
if (!session?.user) {
- return new OpenChatError("unauthorized:vote").toResponse();
+ return new ChatbotError("unauthorized:vote").toResponse();
}
const chat = await getChatById({ id: chatId });
if (!chat) {
- return new OpenChatError("not_found:vote").toResponse();
+ return new ChatbotError("not_found:vote").toResponse();
}
if (chat.userId !== session.user.id) {
- return new OpenChatError("forbidden:vote").toResponse();
+ return new ChatbotError("forbidden:vote").toResponse();
}
await voteMessage({
diff --git a/components/app-sidebar.tsx b/components/app-sidebar.tsx
index ff9c2f0105..ec3f27356a 100644
--- a/components/app-sidebar.tsx
+++ b/components/app-sidebar.tsx
@@ -72,7 +72,7 @@ export function AppSidebar({ user }: { user: User | undefined }) {
}}
>
- OpenChat
+ Chatbot
diff --git a/components/chat-header.tsx b/components/chat-header.tsx
index 528d623111..1f15d08358 100644
--- a/components/chat-header.tsx
+++ b/components/chat-header.tsx
@@ -55,7 +55,7 @@ function PureChatHeader({
className="order-3 hidden bg-zinc-900 px-2 text-zinc-50 hover:bg-zinc-800 md:ml-auto md:flex md:h-fit dark:bg-zinc-100 dark:text-zinc-900 dark:hover:bg-zinc-200"
>
diff --git a/components/chat.tsx b/components/chat.tsx
index 92642462ce..2ffeffb581 100644
--- a/components/chat.tsx
+++ b/components/chat.tsx
@@ -21,7 +21,7 @@ import { useArtifactSelector } from "@/hooks/use-artifact";
import { useAutoResume } from "@/hooks/use-auto-resume";
import { useChatVisibility } from "@/hooks/use-chat-visibility";
import type { Vote } from "@/lib/db/schema";
-import { OpenChatError } from "@/lib/errors";
+import { ChatbotError } from "@/lib/errors";
import type { Attachment, ChatMessage } from "@/lib/types";
import { fetcher, fetchWithErrorHandlers, generateUUID } from "@/lib/utils";
import { Artifact } from "./artifact";
@@ -138,7 +138,7 @@ export function Chat({
mutate(unstable_serialize(getChatHistoryPaginationKey));
},
onError: (error) => {
- if (error instanceof OpenChatError) {
+ if (error instanceof ChatbotError) {
if (
error.message?.includes("AI Gateway requires a valid credit card")
) {
diff --git a/instrumentation.ts b/instrumentation.ts
index 632a2f7454..f97d49bf7c 100644
--- a/instrumentation.ts
+++ b/instrumentation.ts
@@ -1,5 +1,5 @@
import { registerOTel } from "@vercel/otel";
export function register() {
- registerOTel({ serviceName: "openchat" });
+ registerOTel({ serviceName: "chatbot" });
}
diff --git a/lib/db/queries.ts b/lib/db/queries.ts
index 63f012e951..23cad721c9 100644
--- a/lib/db/queries.ts
+++ b/lib/db/queries.ts
@@ -16,7 +16,7 @@ import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import type { ArtifactKind } from "@/components/artifact";
import type { VisibilityType } from "@/components/visibility-selector";
-import { OpenChatError } from "../errors";
+import { ChatbotError } from "../errors";
import { generateUUID } from "../utils";
import {
type Chat,
@@ -45,7 +45,7 @@ export async function getUser(email: string): Promise {
try {
return await db.select().from(user).where(eq(user.email, email));
} catch (_error) {
- throw new OpenChatError(
+ throw new ChatbotError(
"bad_request:database",
"Failed to get user by email"
);
@@ -58,7 +58,7 @@ export async function createUser(email: string, password: string) {
try {
return await db.insert(user).values({ email, password: hashedPassword });
} catch (_error) {
- throw new OpenChatError("bad_request:database", "Failed to create user");
+ throw new ChatbotError("bad_request:database", "Failed to create user");
}
}
@@ -72,7 +72,7 @@ export async function createGuestUser() {
email: user.email,
});
} catch (_error) {
- throw new OpenChatError(
+ throw new ChatbotError(
"bad_request:database",
"Failed to create guest user"
);
@@ -99,7 +99,7 @@ export async function saveChat({
visibility,
});
} catch (_error) {
- throw new OpenChatError("bad_request:database", "Failed to save chat");
+ throw new ChatbotError("bad_request:database", "Failed to save chat");
}
}
@@ -115,7 +115,7 @@ export async function deleteChatById({ id }: { id: string }) {
.returning();
return chatsDeleted;
} catch (_error) {
- throw new OpenChatError(
+ throw new ChatbotError(
"bad_request:database",
"Failed to delete chat by id"
);
@@ -146,7 +146,7 @@ export async function deleteAllChatsByUserId({ userId }: { userId: string }) {
return { deletedCount: deletedChats.length };
} catch (_error) {
- throw new OpenChatError(
+ throw new ChatbotError(
"bad_request:database",
"Failed to delete all chats by user id"
);
@@ -189,7 +189,7 @@ export async function getChatsByUserId({
.limit(1);
if (!selectedChat) {
- throw new OpenChatError(
+ throw new ChatbotError(
"not_found:database",
`Chat with id ${startingAfter} not found`
);
@@ -204,7 +204,7 @@ export async function getChatsByUserId({
.limit(1);
if (!selectedChat) {
- throw new OpenChatError(
+ throw new ChatbotError(
"not_found:database",
`Chat with id ${endingBefore} not found`
);
@@ -222,7 +222,7 @@ export async function getChatsByUserId({
hasMore,
};
} catch (_error) {
- throw new OpenChatError(
+ throw new ChatbotError(
"bad_request:database",
"Failed to get chats by user id"
);
@@ -238,7 +238,7 @@ export async function getChatById({ id }: { id: string }) {
return selectedChat;
} catch (_error) {
- throw new OpenChatError("bad_request:database", "Failed to get chat by id");
+ throw new ChatbotError("bad_request:database", "Failed to get chat by id");
}
}
@@ -246,7 +246,7 @@ export async function saveMessages({ messages }: { messages: DBMessage[] }) {
try {
return await db.insert(message).values(messages);
} catch (_error) {
- throw new OpenChatError("bad_request:database", "Failed to save messages");
+ throw new ChatbotError("bad_request:database", "Failed to save messages");
}
}
@@ -260,7 +260,7 @@ export async function updateMessage({
try {
return await db.update(message).set({ parts }).where(eq(message.id, id));
} catch (_error) {
- throw new OpenChatError("bad_request:database", "Failed to update message");
+ throw new ChatbotError("bad_request:database", "Failed to update message");
}
}
@@ -272,7 +272,7 @@ export async function getMessagesByChatId({ id }: { id: string }) {
.where(eq(message.chatId, id))
.orderBy(asc(message.createdAt));
} catch (_error) {
- throw new OpenChatError(
+ throw new ChatbotError(
"bad_request:database",
"Failed to get messages by chat id"
);
@@ -306,7 +306,7 @@ export async function voteMessage({
isUpvoted: type === "up",
});
} catch (_error) {
- throw new OpenChatError("bad_request:database", "Failed to vote message");
+ throw new ChatbotError("bad_request:database", "Failed to vote message");
}
}
@@ -314,7 +314,7 @@ export async function getVotesByChatId({ id }: { id: string }) {
try {
return await db.select().from(vote).where(eq(vote.chatId, id));
} catch (_error) {
- throw new OpenChatError(
+ throw new ChatbotError(
"bad_request:database",
"Failed to get votes by chat id"
);
@@ -347,7 +347,7 @@ export async function saveDocument({
})
.returning();
} catch (_error) {
- throw new OpenChatError("bad_request:database", "Failed to save document");
+ throw new ChatbotError("bad_request:database", "Failed to save document");
}
}
@@ -361,7 +361,7 @@ export async function getDocumentsById({ id }: { id: string }) {
return documents;
} catch (_error) {
- throw new OpenChatError(
+ throw new ChatbotError(
"bad_request:database",
"Failed to get documents by id"
);
@@ -378,7 +378,7 @@ export async function getDocumentById({ id }: { id: string }) {
return selectedDocument;
} catch (_error) {
- throw new OpenChatError(
+ throw new ChatbotError(
"bad_request:database",
"Failed to get document by id"
);
@@ -407,7 +407,7 @@ export async function deleteDocumentsByIdAfterTimestamp({
.where(and(eq(document.id, id), gt(document.createdAt, timestamp)))
.returning();
} catch (_error) {
- throw new OpenChatError(
+ throw new ChatbotError(
"bad_request:database",
"Failed to delete documents by id after timestamp"
);
@@ -422,7 +422,7 @@ export async function saveSuggestions({
try {
return await db.insert(suggestion).values(suggestions);
} catch (_error) {
- throw new OpenChatError(
+ throw new ChatbotError(
"bad_request:database",
"Failed to save suggestions"
);
@@ -440,7 +440,7 @@ export async function getSuggestionsByDocumentId({
.from(suggestion)
.where(eq(suggestion.documentId, documentId));
} catch (_error) {
- throw new OpenChatError(
+ throw new ChatbotError(
"bad_request:database",
"Failed to get suggestions by document id"
);
@@ -451,7 +451,7 @@ export async function getMessageById({ id }: { id: string }) {
try {
return await db.select().from(message).where(eq(message.id, id));
} catch (_error) {
- throw new OpenChatError(
+ throw new ChatbotError(
"bad_request:database",
"Failed to get message by id"
);
@@ -491,7 +491,7 @@ export async function deleteMessagesByChatIdAfterTimestamp({
);
}
} catch (_error) {
- throw new OpenChatError(
+ throw new ChatbotError(
"bad_request:database",
"Failed to delete messages by chat id after timestamp"
);
@@ -508,7 +508,7 @@ export async function updateChatVisibilityById({
try {
return await db.update(chat).set({ visibility }).where(eq(chat.id, chatId));
} catch (_error) {
- throw new OpenChatError(
+ throw new ChatbotError(
"bad_request:database",
"Failed to update chat visibility by id"
);
@@ -557,7 +557,7 @@ export async function getMessageCountByUserId({
return stats?.count ?? 0;
} catch (_error) {
- throw new OpenChatError(
+ throw new ChatbotError(
"bad_request:database",
"Failed to get message count by user id"
);
@@ -576,7 +576,7 @@ export async function createStreamId({
.insert(stream)
.values({ id: streamId, chatId, createdAt: new Date() });
} catch (_error) {
- throw new OpenChatError(
+ throw new ChatbotError(
"bad_request:database",
"Failed to create stream id"
);
@@ -594,7 +594,7 @@ export async function getStreamIdsByChatId({ chatId }: { chatId: string }) {
return streamIds.map(({ id }) => id);
} catch (_error) {
- throw new OpenChatError(
+ throw new ChatbotError(
"bad_request:database",
"Failed to get stream ids by chat id"
);
diff --git a/lib/db/schema.ts b/lib/db/schema.ts
index 803e652689..ade93189fe 100644
--- a/lib/db/schema.ts
+++ b/lib/db/schema.ts
@@ -34,7 +34,7 @@ export const chat = pgTable("Chat", {
export type Chat = InferSelectModel;
// DEPRECATED: The following schema is deprecated and will be removed in the future.
-// Read the migration guide at https://openchat.dev/docs/migration-guides/message-parts
+// Read the migration guide at https://chatbot.dev/docs/migration-guides/message-parts
export const messageDeprecated = pgTable("Message", {
id: uuid("id").primaryKey().notNull().defaultRandom(),
chatId: uuid("chatId")
@@ -61,7 +61,7 @@ export const message = pgTable("Message_v2", {
export type DBMessage = InferSelectModel;
// DEPRECATED: The following schema is deprecated and will be removed in the future.
-// Read the migration guide at https://openchat.dev/docs/migration-guides/message-parts
+// Read the migration guide at https://chatbot.dev/docs/migration-guides/message-parts
export const voteDeprecated = pgTable(
"Vote",
{
diff --git a/lib/errors.ts b/lib/errors.ts
index bfa5d4d2cb..bae6668bae 100644
--- a/lib/errors.ts
+++ b/lib/errors.ts
@@ -35,7 +35,7 @@ export const visibilityBySurface: Record = {
activate_gateway: "response",
};
-export class OpenChatError extends Error {
+export class ChatbotError extends Error {
type: ErrorType;
surface: Surface;
statusCode: number;
diff --git a/lib/utils.ts b/lib/utils.ts
index 01c13ea1e5..6fce6ac8be 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -8,7 +8,7 @@ import { type ClassValue, clsx } from 'clsx';
import { formatISO } from 'date-fns';
import { twMerge } from 'tailwind-merge';
import type { DBMessage, Document } from '@/lib/db/schema';
-import { OpenChatError, type ErrorCode } from './errors';
+import { ChatbotError, type ErrorCode } from './errors';
import type { ChatMessage, ChatTools, CustomUIDataTypes } from './types';
export function cn(...inputs: ClassValue[]) {
@@ -20,7 +20,7 @@ export const fetcher = async (url: string) => {
if (!response.ok) {
const { code, cause } = await response.json();
- throw new OpenChatError(code as ErrorCode, cause);
+ throw new ChatbotError(code as ErrorCode, cause);
}
return response.json();
@@ -35,13 +35,13 @@ export async function fetchWithErrorHandlers(
if (!response.ok) {
const { code, cause } = await response.json();
- throw new OpenChatError(code as ErrorCode, cause);
+ throw new ChatbotError(code as ErrorCode, cause);
}
return response;
} catch (error: unknown) {
if (typeof navigator !== 'undefined' && !navigator.onLine) {
- throw new OpenChatError('offline:chat');
+ throw new ChatbotError('offline:chat');
}
throw error;
diff --git a/package.json b/package.json
index 77050da857..5cccc5ffbf 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
{
- "name": "openchat",
+ "name": "chatbot",
"version": "3.1.0",
"private": true,
"scripts": {
From 8fef6f4aa8373bbd2e6c9e462f65fb46574a4075 Mon Sep 17 00:00:00 2001
From: dancer <144584931+dancer@users.noreply.github.com>
Date: Tue, 24 Feb 2026 00:21:44 +0000
Subject: [PATCH 2/2] update opengraph image
---
app/(chat)/opengraph-image.png | Bin 34687 -> 14418 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
diff --git a/app/(chat)/opengraph-image.png b/app/(chat)/opengraph-image.png
index 73bc344d60419a7d4a105ae604c8ed2df2aa8837..c8402717a1d1cc0f37cffefe3a2b8930588e74f3 100644
GIT binary patch
literal 14418
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
literal 34687
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