Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# LangGraph Configuration
NEXT_PUBLIC_API_URL=http://localhost:2024
NEXT_PUBLIC_ASSISTANT_ID=agent
NEXT_PUBLIC_APP_TITLE=Agent Chat
# File Upload Configuration - Default is enabled, set to 'false' to disable
# NEXT_PUBLIC_ENABLE_FILE_UPLOAD=false
# Do NOT prefix this with "NEXT_PUBLIC_" as we do not want this exposed in the client.
LANGSMITH_API_KEY=

Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,16 @@ To use these variables:

When these environment variables are set, the application will use them instead of showing the setup form.

### File Upload Configuration

You can control file upload functionality using the `NEXT_PUBLIC_ENABLE_FILE_UPLOAD` environment variable:

- **Default behavior**: File uploads are **enabled** (upload button, drag & drop, paste functionality)
- **To disable**: Set `NEXT_PUBLIC_ENABLE_FILE_UPLOAD=false` in your `.env` file
- **Supported file types**: JPEG, PNG, GIF, WEBP images and PDF files

When disabled, all file upload UI elements are hidden and file handling is disabled.

## Hiding Messages in the Chat

You can control the visibility of messages within the Agent Chat UI in two main ways:
Expand Down
5 changes: 3 additions & 2 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import "./globals.css";
import { Inter } from "next/font/google";
import React from "react";
import { NuqsAdapter } from "nuqs/adapters/next/app";
import { APP_TITLE } from "@/lib/app-config";

const inter = Inter({
subsets: ["latin"],
Expand All @@ -11,8 +12,8 @@ const inter = Inter({
});

export const metadata: Metadata = {
title: "Agent Chat",
description: "Agent Chat UX by LangChain",
title: APP_TITLE,
description: `${APP_TITLE} UX by LangChain`,
};

export default function RootLayout({
Expand Down
59 changes: 33 additions & 26 deletions src/components/thread/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
ArtifactTitle,
useArtifactContext,
} from "./artifact";
import { APP_TITLE, ENABLE_FILE_UPLOAD } from "@/lib/app-config";

function StickyToBottomContent(props: {
content: ReactNode;
Expand Down Expand Up @@ -362,7 +363,7 @@ export function Thread() {
height={32}
/>
<span className="text-xl font-semibold tracking-tight">
Agent Chat
{APP_TITLE}
</span>
</motion.button>
</div>
Expand Down Expand Up @@ -435,18 +436,18 @@ export function Thread() {
<div className="flex items-center gap-3">
<LangGraphLogoSVG className="h-8 flex-shrink-0" />
<h1 className="text-2xl font-semibold tracking-tight">
Agent Chat
{APP_TITLE}
</h1>
</div>
)}

<ScrollToBottom className="animate-in fade-in-0 zoom-in-95 absolute bottom-full left-1/2 mb-4 -translate-x-1/2" />

<div
ref={dropRef}
ref={ENABLE_FILE_UPLOAD ? dropRef : null}
className={cn(
"bg-muted relative z-10 mx-auto mb-8 w-full max-w-3xl rounded-2xl shadow-xs transition-all",
dragOver
ENABLE_FILE_UPLOAD && dragOver
? "border-primary border-2 border-dotted"
: "border border-solid",
)}
Expand All @@ -455,14 +456,16 @@ export function Thread() {
onSubmit={handleSubmit}
className="mx-auto grid max-w-3xl grid-rows-[1fr_auto] gap-2"
>
<ContentBlocksPreview
blocks={contentBlocks}
onRemove={removeBlock}
/>
{ENABLE_FILE_UPLOAD && (
<ContentBlocksPreview
blocks={contentBlocks}
onRemove={removeBlock}
/>
)}
<textarea
value={input}
onChange={(e) => setInput(e.target.value)}
onPaste={handlePaste}
onPaste={ENABLE_FILE_UPLOAD ? handlePaste : undefined}
onKeyDown={(e) => {
if (
e.key === "Enter" &&
Expand Down Expand Up @@ -496,23 +499,27 @@ export function Thread() {
</Label>
</div>
</div>
<Label
htmlFor="file-input"
className="flex cursor-pointer items-center gap-2"
>
<Plus className="size-5 text-gray-600" />
<span className="text-sm text-gray-600">
Upload PDF or Image
</span>
</Label>
<input
id="file-input"
type="file"
onChange={handleFileUpload}
multiple
accept="image/jpeg,image/png,image/gif,image/webp,application/pdf"
className="hidden"
/>
{ENABLE_FILE_UPLOAD && (
<>
<Label
htmlFor="file-input"
className="flex cursor-pointer items-center gap-2"
>
<Plus className="size-5 text-gray-600" />
<span className="text-sm text-gray-600">
Upload PDF or Image
</span>
</Label>
<input
id="file-input"
type="file"
onChange={handleFileUpload}
multiple
accept="image/jpeg,image/png,image/gif,image/webp,application/pdf"
className="hidden"
/>
</>
)}
{stream.isLoading ? (
<Button
key="stop"
Expand Down
4 changes: 4 additions & 0 deletions src/lib/app-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const APP_TITLE = process.env.NEXT_PUBLIC_APP_TITLE || "Agent Chat";

export const ENABLE_FILE_UPLOAD =
process.env.NEXT_PUBLIC_ENABLE_FILE_UPLOAD?.toLowerCase() !== 'false';
5 changes: 3 additions & 2 deletions src/providers/Stream.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { PasswordInput } from "@/components/ui/password-input";
import { getApiKey } from "@/lib/api-key";
import { useThreads } from "./Thread";
import { toast } from "sonner";
import { APP_TITLE } from "@/lib/app-config";

export type StateType = { messages: Message[]; ui?: UIMessage[] };

Expand Down Expand Up @@ -169,11 +170,11 @@ export const StreamProvider: React.FC<{ children: ReactNode }> = ({
<div className="flex flex-col items-start gap-2">
<LangGraphLogoSVG className="h-7" />
<h1 className="text-xl font-semibold tracking-tight">
Agent Chat
{APP_TITLE}
</h1>
</div>
<p className="text-muted-foreground">
Welcome to Agent Chat! Before you get started, you need to enter
Welcome to {APP_TITLE}! Before you get started, you need to enter
the URL of the deployment and the assistant / graph ID.
</p>
</div>
Expand Down
8 changes: 6 additions & 2 deletions src/providers/Thread.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,12 @@ function getThreadSearchMetadata(
}

export function ThreadProvider({ children }: { children: ReactNode }) {
const [apiUrl] = useQueryState("apiUrl");
const [assistantId] = useQueryState("assistantId");
const [apiUrl] = useQueryState("apiUrl", {
defaultValue: process.env.NEXT_PUBLIC_API_URL || "",
});
const [assistantId] = useQueryState("assistantId", {
defaultValue: process.env.NEXT_PUBLIC_ASSISTANT_ID || "",
});
const [threads, setThreads] = useState<Thread[]>([]);
const [threadsLoading, setThreadsLoading] = useState(false);

Expand Down