# Project export: LegalAI

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: UC Berkeley AI Hackathon 2025
- Tagline: Small business often have to face the complex world of legal documents and compliance. LegalAI helps small business owners identify potential risks, flag unusual clauses and ensures legal compliance.
- Devpost: https://devpost.com/software/legalai-7py0ew
- GitHub: https://github.com/davidcsdrone/hackathon#
- Team: 1 GitHub contributor(s) — null (1 commits)

## Devpost submission (written by the team)

### Inspiration

We were inpsired by the lack of resources there are for small and medium size businesses to navigate the legal system when operating their business. We aimed at helping business owners help small businesses reduce their cost of hiring lawyers for legal problems AI can easily solve.

### What it does

LegalAI is an intelligent, personalized legal navigator designed specifically to empower small businesses. It aims at keeping personalized information about the client to give relevant regulations for business decisions. Additionally, it considers the local and state laws it must comply with, making it specifically designed for the laws that affect your business, in your location. LegalAI provides precise, properly cited laws and regulations for the user to read and refer to, making it very helpful for individual business owners who prefer to have properly cited evidence as opposed to the conventional internet scraping approaches seen in standard large language models.

### How we built it

The framework for our project is built using Vercel's V0. LegalAI attempted use Letta's Memory AI to store important information about the client. Letta's agent would serve very useful for business owners who require truly persistent and context-aware legal guidance that continuously evolves over time, especially as their business continues to grow. Additionally, the use of Letta was meant so that the agent to had a strong memory of the previous laws and regulations it was complying to and any previous challenges it could potentially run into. Additionally, we built it usnig the Supabase backend to store chat history and saved the client's saved advices.

### Challenges we ran into

We couldn't quite figure out how to fully connect Letta's agent. ChatGpt with prompts was used as a replacement for experimental purposes.

### Accomplishments we're proud of

We are proud to have connected a Supabase for the backend as well as learning about Letta.

### What we learned

We learned a lot about using AI tools for development. We also learned how to use AI agents to perform tasks and how useful they can be to perform very specific and domain emphasis tasks to solve a problem.

### What's next

We are thinking about connecting legal databases the AI searches from to ensure up to date accurate information. Additionally, we would like it to assess whether a lawyer is needed given the circumstances the business owner has.

## README (from the GitHub repository)

# LawAdvisor - AI Legal Assistant with Letta Integration

A comprehensive AI-powered legal advisor application built with Next.js 15, Supabase, and **Letta AI agents**. Designed specifically for small businesses to get intelligent legal guidance with persistent memory and context awareness.

## 🧠 Letta Integration Features

- **Persistent Memory**: Your Letta agent remembers all conversations and builds context over time
- **Stateful Conversations**: Unlike traditional chatbots, Letta maintains state across sessions
- **Tool Access**: Built-in web search and code execution capabilities
- **Memory Management**: Agent can update its understanding of your business needs
- **Multi-Modal Support**: Text, voice, and document interactions

## 🚀 Features

- **Letta-Powered AI Chat**: Stateful legal advisor with persistent memory
- **User Authentication**: Secure sign up/sign in with Supabase
- **Document Upload**: Drag & drop support for legal documents
- **Voice Conversations**: VAPI integration for voice interactions
- **Chat History**: Searchable conversation history (backed up in Supabase)
- **Real-time Persistence**: Conversations saved both in Letta and Supabase
- **Responsive Design**: Works on desktop and mobile devices

## 🛠️ Tech Stack

- **Framework**: Next.js 15 with App Router
- **Frontend**: React 19, TypeScript, Tailwind CSS
- **UI Components**: shadcn/ui
- **AI Agent**: Letta Cloud (stateful AI agents)
- **Backend**: Supabase (PostgreSQL, Auth, Real-time)
- **AI Integration**: Letta Vercel AI SDK Provider
- **Icons**: Lucide React

## 📋 Prerequisites

Before running this project, make sure you have:

- Node.js 18+ installed
- A Supabase account and project
- Git installed

## 🚀 Getting Started

### 1. Clone the repository
\`\`\`bash
git clone <your-repo-url>
cd law-advisor-app
\`\`\`

### 2. Install dependencies
\`\`\`bash
npm install
\`\`\`

### 3. Set up environment variables
Create a `.env.local` file in the root directory:

\`\`\`env
# Supabase Configuration
NEXT_PUBLIC_SUPABASE_URL=https://uayysszbsksgktxzcqrc.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InVheXlzc3pic2tzZ2t0eHpjcXJjIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTA1NjA5MzIsImV4cCI6MjA2NjEzNjkzMn0._zMdwpsfIlhcKQ2py4FJbO6nKPEFBvDz3zdBuWp3bOc

# Letta Configuration (No API key needed)
LETTA_AGENT_ID=agent-f79336f7-be3f-48eb-8d91-646515218af1
\`\`\`

### 4. Set up the database
1. Go to your Supabase project dashboard
2. Navigate to the SQL Editor
3. Run the SQL script from `scripts/create-tables.sql`

### 5. Run the development server
\`\`\`bash
npm run dev
\`\`\`

Open [http://localhost:3000](http://localhost:3000) in your browser.

## 🧠 Letta Agent Benefits

### Traditional Chatbots vs Letta Agents

| Feature | Traditional Chatbots | Letta Agents |
|---------|---------------------|지컬--------------|
| Memory | Session-based only | Persistent across all interactions |
| Context | Limited to conversation | Builds comprehensive client profile |
| Tools | Limited or none | Web search, code execution, custom tools |
| Learning | Static responses | Self-improving through memory updates |
| State | Stateless | Stateful with persistent memory blocks |

### Legal Use Cases

- **Client Profiling**: Remembers business type, industry, previous legal issues
- **Case Continuity**: Maintains context across multiple consultations
- **Document Analysis**: Can reference previously uploaded documents
- **Regulatory Tracking**: Keeps track of compliance requirements for your business
- **Legal Research**: Uses web search to find current legal information

## 🔧 Configuration

### Letta Setup
No account required! The application uses a public Letta agent that's accessible without authentication. Simply ensure you have the agent ID in your environment variables.

### Supabase Setup
1. Create a new Supabase project
2. Run the SQL script to create tables
3. Enable Row Level Security (RLS)
4. Copy your project URL and anon key

## 🚀 Deployment

### Deploy to Vercel
1. Push your code to GitHub
2. Connect your repository to Vercel
3. Add environment variables in Vercel dashboard
4. Deploy!

### Environment Variables for Production
\`\`\`env
NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
LETTA_API_KEY=your_letta_api_key
LETTA_AGENT_ID=your_agent_id
\`\`\`

## 📝 Usage

1. **Sign Up**: Create a new account
2. **Chat**: Ask legal questions to your persistent Letta agent
3. **Upload**: Attach documents for analysis
4. **Voice**: Use voice conversations (VAPI integration)
5. **History**: Search and filter past conversations
6. **Memory**: Agent builds understanding of your business over time

## 🔒 Security

- Row Level Security (RLS) enabled
- User data isolation
- Secure authentication with Supabase
- Protected API routes
- Letta Cloud security compliance

## 🤝 Contributing

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Submit a pull request

## 📄 License

This project is licensed under the MIT License.

## 🆘 Support

For support, please open an issue on GitHub or contact the development team.

---

**Disclaimer**: This application provides general legal information and should not be considered as formal legal advice. For complex legal matters, consult with a qualified attorney.

**Powered by Letta AI** - Advanced stateful agents with persistent memory and tool access.


## Detected evidence (automated analysis)

Indexed codebase: 90 recognized source files, 438 KB.
- CSS (language) — detected in the code
- Google Gemini (technology) — detected in the code
- Next.js (technology) — detected in the code
- React (technology) — detected in the code
- SQL (language) — detected in the code
- Supabase (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Vercel AI SDK (technology) — detected in the code

## Codebase structure (from repository index)

### Files (95 of 95)

```
.gitignore
app/api/advisor/route.ts
app/api/advisor/test/route.ts
app/api/agent-info/route.ts
app/api/agent-status/route.ts
app/api/chat/route.ts
app/api/test-letta/route.ts
app/auth/signin/page.tsx
app/auth/signup/page.tsx
app/chat/page.tsx
app/globals.css
app/history/page.tsx
app/layout.tsx
app/page.tsx
app/saved/page.tsx
components.json
components/chat-history-real.tsx
components/chat-history.tsx
components/chat-interface-letta.tsx
components/chat-interface-real.tsx
components/chat-interface.tsx
components/file-upload.tsx
components/landing-page.tsx
components/saved-advice.tsx
components/theme-provider.tsx
components/ui/accordion.tsx
components/ui/alert-dialog.tsx
components/ui/alert.tsx
components/ui/aspect-ratio.tsx
components/ui/avatar.tsx
components/ui/badge.tsx
components/ui/breadcrumb.tsx
components/ui/button.tsx
components/ui/calendar.tsx
components/ui/card.tsx
components/ui/carousel.tsx
components/ui/chart.tsx
components/ui/checkbox.tsx
components/ui/collapsible.tsx
components/ui/command.tsx
components/ui/context-menu.tsx
components/ui/dialog.tsx
components/ui/drawer.tsx
components/ui/dropdown-menu.tsx
components/ui/form.tsx
components/ui/hover-card.tsx
components/ui/input-otp.tsx
components/ui/input.tsx
components/ui/label.tsx
components/ui/menubar.tsx
components/ui/navigation-menu.tsx
components/ui/pagination.tsx
components/ui/popover.tsx
components/ui/progress.tsx
components/ui/radio-group.tsx
components/ui/resizable.tsx
components/ui/scroll-area.tsx
components/ui/select.tsx
components/ui/separator.tsx
components/ui/sheet.tsx
components/ui/sidebar.tsx
components/ui/skeleton.tsx
components/ui/slider.tsx
components/ui/sonner.tsx
components/ui/switch.tsx
components/ui/table.tsx
components/ui/tabs.tsx
components/ui/textarea.tsx
components/ui/toast.tsx
components/ui/toaster.tsx
components/ui/toggle-group.tsx
components/ui/toggle.tsx
components/ui/tooltip.tsx
components/ui/use-mobile.tsx
components/ui/use-toast.ts
components/voice-chat.tsx
hooks/use-mobile.tsx
hooks/use-toast.ts
lib/letta-client.ts
lib/prompt-enhancer.ts
lib/supabase.ts
lib/utils.ts
middleware.ts
next.config.mjs
package.json
postcss.config.mjs
README.md
scripts/create-tables.sql
scripts/fix-advisor-table.sql
scripts/fix-message-duplicates.sql
scripts/update-advisor-auto-increment.sql
scripts/update-advisor-table.sql
styles/globals.css
tailwind.config.ts
tsconfig.json
```

### Dependencies

- package.json: @ai-sdk/google@latest, @hookform/resolvers@^3.9.1, @radix-ui/react-accordion@1.2.2, @radix-ui/react-alert-dialog@1.1.4, @radix-ui/react-aspect-ratio@1.1.1, @radix-ui/react-avatar@1.1.2, @radix-ui/react-checkbox@1.1.3, @radix-ui/react-collapsible@1.1.2, @radix-ui/react-context-menu@2.2.4, @radix-ui/react-dialog@1.1.4, @radix-ui/react-dropdown-menu@2.1.4, @radix-ui/react-hover-card@1.1.4, @radix-ui/react-label@2.1.1, @radix-ui/react-menubar@1.1.4, @radix-ui/react-navigation-menu@1.2.3, @radix-ui/react-popover@1.1.4, @radix-ui/react-progress@1.1.1, @radix-ui/react-radio-group@1.2.2, @radix-ui/react-scroll-area@1.2.2, @radix-ui/react-select@2.1.4, @radix-ui/react-separator@1.1.1, @radix-ui/react-slider@1.2.2, @radix-ui/react-slot@1.1.1, @radix-ui/react-switch@1.1.2, @radix-ui/react-tabs@1.1.2, @radix-ui/react-toast@1.2.4, @radix-ui/react-toggle@1.1.1, @radix-ui/react-toggle-group@1.1.1, @radix-ui/react-tooltip@1.1.6, @supabase/auth-helpers-nextjs@latest, @supabase/supabase-js@latest, @types/node@^22, @types/react@^19, @types/react-dom@^19, ai@latest, autoprefixer@^10.4.20, class-variance-authority@^0.7.1, clsx@^2.1.1, cmdk@1.0.4, date-fns@latest, embla-carousel-react@8.5.1, input-otp@1.4.1, lucide-react@^0.454.0, next@15.2.4, next-themes@^0.4.4, postcss@^8.5, react@^19, react-day-picker@8.10.1, react-dom@^19, react-hook-form@^7.54.1, react-resizable-panels@^2.1.7, recharts@2.15.0, sonner@^1.7.1, tailwind-merge@^2.5.5, tailwindcss@^3.4.17, tailwindcss-animate@^1.0.7, typescript@^5, vaul@^0.9.6, zod@^3.24.1

### Recent commits (newest first)

- Initialized repository for project Law advisor chatbot

## Key source files (fetched from GitHub, selected and truncated for size)

### package.json

```
{
  "name": "my-v0-project",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "build": "next build",
    "dev": "next dev",
    "lint": "next lint",
    "start": "next start"
  },
  "dependencies": {
    "@ai-sdk/google": "latest",
    "@hookform/resolvers": "^3.9.1",
    "@radix-ui/react-accordion": "1.2.2",
    "@radix-ui/react-alert-dialog": "1.1.4",
    "@radix-ui/react-aspect-ratio": "1.1.1",
    "@radix-ui/react-avatar": "1.1.2",
    "@radix-ui/react-checkbox": "1.1.3",
    "@radix-ui/react-collapsible": "1.1.2",
    "@radix-ui/react-context-menu": "2.2.4",
    "@radix-ui/react-dialog": "1.1.4",
    "@radix-ui/react-dropdown-menu": "2.1.4",
    "@radix-ui/react-hover-card": "1.1.4",
    "@radix-ui/react-label": "2.1.1",
    "@radix-ui/react-menubar": "1.1.4",
    "@radix-ui/react-navigation-menu": "1.2.3",
    "@radix-ui/react-popover": "1.1.4",
    "@radix-ui/react-progress": "1.1.1",
    "@radix-ui/react-radio-group": "1.2.2",
    "@radix-ui/react-scroll-area": "1.2.2",
    "@radix-ui/react-select": "2.1.4",
    "@radix-ui/react-separator": "1.1.1",
    "@radix-ui/react-slider": "1.2.2",
    "@radix-ui/react-slot": "1.1.1",
    "@radix-ui/react-switch": "1.1.2",
    "@radix-ui/react-tabs": "1.1.2",
    "@radix-ui/react-toast": "1.2.4",
    "@radix-ui/react-toggle": "1.1.1",
    "@radix-ui/react-toggle-group": "1.1.1",
    "@radix-ui/react-tooltip": "1.1.6",
    "@supabase/auth-helpers-nextjs": "latest",
    "@supabase/supabase-js": "latest",
    "ai": "latest",
    "autoprefixer": "^10.4.20",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "cmdk": "1.0.4",
    "date-fns": "latest",
    "embla-carousel-react": "8.5.1",
    "input-otp": "1.4.1",
    "lucide-react": "^0.454.0",
    "next": "15.2.4",
    "next-themes": "^0.4.4",
    "react": "^19",
    "react-day-picker": "8.10.1",
    "react-dom": "^19",
    "react-hook-form": "^7.54.1",
    "react-resizable-panels": "^2.1.7",
    "recharts": "2.15.0",
    "sonner": "^1.7.1",
    "tailwind-merge": "^2.5.5",
    "tailwindcss-animate": "^1.0.7",
    "vaul": "^0.9.6",
    "zod": "^3.24.1"
  },
  "devDependencies": {
    "@types/node": "^22",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "postcss": "^8.5",
    "tailwindcss": "^3.4.17",
    "typescript": "^5"
  }
}
```

### app/layout.tsx

```typescript
import type { Metadata } from 'next'
import './globals.css'

export const metadata: Metadata = {
  title: 'v0 App',
  description: 'Created with v0',
  generator: 'v0.dev',
}

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode
}>) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

```

### app/page.tsx

```typescript
import { redirect } from "next/navigation"
import { createServerClient } from "@/lib/supabase"
import LandingPage from "@/components/landing-page"

export default async function Home() {
  const supabase = createServerClient()

  const {
    data: { session },
  } = await supabase.auth.getSession()

  if (session) {
    redirect("/chat")
  }

  return <LandingPage />
}

```

### app/history/page.tsx

```typescript
import { redirect } from "next/navigation"
import { createServerClient } from "@/lib/supabase"
import ChatHistoryReal from "@/components/chat-history-real"

export default async function HistoryPage() {
  const supabase = createServerClient()

  const {
    data: { session },
  } = await supabase.auth.getSession()

  if (!session) {
    redirect("/auth/signin")
  }

  return <ChatHistoryReal user={session.user} />
}

```

### app/chat/page.tsx

```typescript
import { redirect } from "next/navigation"
import { createServerClient } from "@/lib/supabase"
import ChatInterfaceLetta from "@/components/chat-interface-letta"

export default async function ChatPage() {
  const supabase = createServerClient()

  const {
    data: { session },
  } = await supabase.auth.getSession()

  if (!session) {
    redirect("/auth/signin")
  }

  return <ChatInterfaceLetta user={session.user} />
}

```

### app/saved/page.tsx

```typescript
import { redirect } from "next/navigation"
import { createServerClient } from "@/lib/supabase"
import SavedAdvice from "@/components/saved-advice"
import Link from "next/link"
import { Button } from "@/components/ui/button"
import { ArrowLeft, Scale } from "lucide-react"

export default async function SavedPage() {
  const supabase = createServerClient()

  const {
    data: { session },
  } = await supabase.auth.getSession()

  if (!session) {
    redirect("/auth/signin")
  }

  return (
    <div className="min-h-screen bg-gray-50">
      <div className="container mx-auto px-4 py-8 max-w-4xl">
        {/* Header */}
        <div className="flex items-center justify-between mb-8">
          <div className="flex items-center space-x-4">
            <Link href="/chat">
              <Button variant="outline" size="icon" className="bg-white text-gray-600 border-gray-300 hover:bg-gray-50">
                <ArrowLeft className="h-4 w-4" />
              </Button>
            </Link>
            <div className="flex items-center space-x-2">
              <Scale className="h-6 w-6 text-blue-600" />
              <div>
                <h1 className="text-2xl font-bold text-gray-900">Saved Legal Advice</h1>
                <p className="text-gray-600">Your bookmarked legal guidance and insights</p>
              </div>
            </div>
          </div>
        </div>

        <SavedAdvice user={session.user} />
      </div>
    </div>
  )
}

```

### app/api/agent-info/route.ts

```typescript
import { NextResponse } from "next/server"
import { getAgentInfo } from "@/lib/letta-client"

export async function GET() {
  try {
    const agentInfo = await getAgentInfo()
    return NextResponse.json(agentInfo)
  } catch (error) {
    console.error("Error fetching agent info:", error)
    return NextResponse.json({ error: "Failed to fetch agent info" }, { status: 500 })
  }
}

```

### app/api/test-letta/route.ts

```typescript
import { NextResponse } from "next/server"
import { testLettaConnection } from "@/lib/letta-client"

export async function GET() {
  try {
    const result = await testLettaConnection()
    return NextResponse.json(result, { status: result.success ? 200 : 500 })
  } catch (error) {
    console.error("Error testing Letta connection:", error)
    return NextResponse.json(
      {
        success: false,
        error: error.message || "Unknown error",
        timestamp: new Date().toISOString(),
      },
      { status: 500 },
    )
  }
}

```

### app/api/agent-status/route.ts

```typescript
import { NextResponse } from "next/server"
import { getAgentInfo } from "@/lib/letta-client"

export async function GET() {
  try {
    const agentInfo = await getAgentInfo()
    return NextResponse.json({
      status: "connected",
      agent: agentInfo,
      timestamp: new Date().toISOString(),
    })
  } catch (error) {
    console.error("Error fetching agent status:", error)
    return NextResponse.json(
      {
        status: "error",
        error: "Failed to connect to Letta agent",
        timestamp: new Date().toISOString(),
      },
      { status: 500 },
    )
  }
}

```

### app/auth/signin/page.tsx

```typescript
"use client"

import type React from "react"

import { useState } from "react"
import { useRouter } from "next/navigation"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Label } from "@/components/ui/label"
import { Scale } from "lucide-react"
import Link from "next/link"
import { createClient } from "@/lib/supabase"

export default function SignIn() {
  const [email, setEmail] = useState("")
  const [password, setPassword] = useState("")
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState("")

  const router = useRouter()
  const supabase = createClient()

  const handleSignIn = async (e: React.FormEvent) => {
    e.preventDefault()
    setLoading(true)
    setError("")

    const { error } = await supabase.auth.signInWithPassword({
      email,
      password,
    })

    if (error) {
      setError(error.message)
    } else {
      router.push("/chat")
    }

    setLoading(false)
  }

  return (
    <div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center p-4">
      <Card className="w-full max-w-md">
        <CardHeader className="text-center">
          <div className="flex items-center justify-center mb-4">
            <Scale className="h-8 w-8 text-blue-600 mr-2" />
            <h1 className="text-2xl font-bold">LawAdvisor</h1>
          </div>
          <CardTitle>Welcome Back</CardTitle>
          <CardDescription>Sign in to your account</CardDescription>
        </CardHeader>
        <CardContent>
          <form onSubmit={handleSignIn} className="space-y-4">
            <div className="space-y-2">
              <Label htmlFor="email">Email</Label>
              <Input id="email" type="email" value={email} onChange={(e) => setEmail(e.target.value)} required />
            </div>
            <div className="space-y-2">
              <Label htmlFor="password">Password</Label>
              <Input
                id="password"
                type="password"
                value={password}
                onChange={(e) => setPassword(e.target.value)}
                required
              />
            </div>
            {error && <div className="text-red-600 text-sm">{error}</div>}
            <Button type="submit" className="w-full" disabled={loading}>
              {loading ? "Signing In..." : "Sign In"}
            </Button>
          </form>
          <div className="mt-4 text-center text-sm">
            {"Don't have an account? "}
            <Link href="/auth/signup" className="text-blue-600 hover:underline">
              Sign up
            </Link>
          </div>
        </CardContent>
      </Card>
    </div>
  )
}

```

[79 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]