# Project export: Candidate Compare

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: Cal Hacks 11.0
- Tagline: An AI video interview application that automates employer conducted interviews conducted. in a human-like conversation, the application generates authenticity report to detect false information.
- Devpost: https://devpost.com/software/candidate-compare
- GitHub: https://github.com/tobyL05/hirenew
- Video: https://www.youtube.com/embed/BlAjpJ3pVZc?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — Toby (12 commits), Mayur Choudhary (4 commits)

## Devpost submission (written by the team)

### Inspiration

With the number of candidates rising in the industry in comparison to the job positions, conducting interviews and deciding on candidates carefully has become a critical task. Along with this ratio imbalance, there are an increasing number of candidates that are forging their experience to gain an unfair advantage over others.

### What it does

The project provides AI interview solution which conducts a human-like interview and deploy AI agents in the backend to verify the authenticity of the candidate.

### How we built it

The project was building NextJS as frontend, and NodeJS as backend. The AI service was provided by Hume, along with Single Store as the backend database. We also used Fetch AI to deploy the AI agents that verify the authenticity.

### Challenges we ran into

Some challenges we ran into were related to integrating Hume into our frontend. Managing the conversation data and inferring it to provide feedback was also tricky.

### Accomplishments we're proud of

Being able to build a working MVP within 2 days of hacking. Integrating hume AI and being able to persist and maintain conversation transcripts that was later used to make inference.

### What we learned

We learned about using and integrating AI agents to help us with important tasks. Having used Hume AI also provided us with insights on different emotions captured by the AI service that can be used in a lot of downstream tasks.

### What's next

We plan on expanding the scope of our candidate information verification to include more thorough checks. We also plan to partner with a couple of early stage adopters to use Candidate Compare and benefit from reduced hiring loads.

## README (from the GitHub repository)

<div align="center">
  <img src="https://storage.googleapis.com/hume-public-logos/hume/hume-banner.png">
  <h1>EVI Next.js App Router Example</h1>
</div>

![preview.png](preview.png)

## Overview

This project features a sample implementation of Hume's [Empathic Voice Interface](https://hume.docs.buildwithfern.com/docs/empathic-voice-interface-evi/overview) using Hume's React SDK. Here, we have a simple EVI that uses the Next.js App Router.

## Project deployment

Click the button below to deploy this example project with Vercel:

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fhumeai%2Fhume-evi-next-js-starter&env=HUME_API_KEY,HUME_CLIENT_SECRET)

Below are the steps to completing deployment:

1. Create a Git Repository for your project.
2. Provide the required environment variables. To get your API key and Client Secret key, log into the portal and visit the [API keys page](https://beta.hume.ai/settings/keys).


## Detected evidence (automated analysis)

Indexed codebase: 46 recognized source files, 214 KB.
- CSS (language) — detected in the code
- Next.js (technology) — detected in the code
- Python (language) — detected in the code
- React (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
- Node.js (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (58 of 58)

```
.eslintrc.json
.gitignore
.prettierrc.json
agents_fetch/Adam.json
agents_fetch/agent1q0xrf70n2h_data.json
agents_fetch/agent1qtly7yj6rt_data.json
agents_fetch/aman.json
agents_fetch/certification_check_agent.py
agents_fetch/experience_check_agent.py
agents_fetch/github_fetch_agent.py
agents_fetch/linkedin_fetch_agent.py
agents_fetch/main_agent.py
agents_fetch/main.py
agents_fetch/project_check_agent.py
agents_fetch/Toby Lau.json
agents_fetch/Toby.json
agents_fetch/verify_agent.py
app/api/verifier/route.ts
app/error.tsx
app/globals.css
app/interviews/[uid]/page.tsx
app/layout.tsx
app/new-interview/page.tsx
app/page.tsx
app/recruiter-view/[uid]/page.tsx
app/recruiter-view/page.tsx
app/thank-you/page.tsx
components.json
components/Chat.tsx
components/Controls.tsx
components/Expressions.tsx
components/JobInfoForm.tsx
components/logos/GitHub.tsx
components/logos/Hume.tsx
components/MenuItem.tsx
components/Messages.tsx
components/MicFFT.tsx
components/Nav.tsx
components/StartCall.tsx
components/ui/button.tsx
components/ui/card.tsx
components/ui/input.tsx
components/ui/label.tsx
components/ui/table.tsx
components/ui/textarea.tsx
components/ui/toggle.tsx
components/video-chat.tsx
package.json
postcss.config.mjs
README.md
stores/useAddUserStore.ts
stores/useUidStore.ts
tailwind.config.ts
tsconfig.json
utils/expressionColors.ts
utils/getHumeAccessToken.ts
utils/index.ts
utils/useWebcam.ts
```

### Dependencies

- package.json: @ai-sdk/groq@^0.0.1, @humeai/voice-react@^0.1.12, @radix-ui/react-label@^2.1.0, @radix-ui/react-slot@^1.1.0, @radix-ui/react-switch@^1.0.3, @radix-ui/react-toggle@^1.0.3, @types/node@^20, @types/react@^18, @types/react-dom@^18, @types/react-virtualized@^9.21.30, ai@^3.4.16, class-variance-authority@^0.7.0, clsx@^2.1.1, eslint@^8, eslint-config-next@14.2.3, framer-motion@^11.2.10, geist@^1.3.0, groq-sdk@^0.7.0, hume@^0.8.6, lucide-react@^0.394.0, next@14.2.3, postcss@^8, prettier@^3.3.3, react@^18, react-dom@^18, react-virtualized@^9.22.5, remeda@^2.0.9, server-only@^0.0.1, tailwind-merge@^2.3.0, tailwindcss@^3.4.1, tailwindcss-animate@^1.0.7, ts-pattern@^5.1.2, typescript@^5, zustand@^5.0.0

### Recent commits (newest first)

- new changes
- a
- updates
- removed unused and added UID store
- Fixed form and expressions
- Interview creation updated
- implemented landing page and interview generation
- implemented expression collection
- Merge branch 'int-may'
- Store transcript in backend db
- Added backend API calls to fetch interview metadata
- removed unused
- removed unused
- fixed stuff
- initial commits
- created gitignore

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

### package.json

```
{
  "name": "hume-evi-next-js-app-router",
  "version": "0.1.0",
  "private": true,
  "homepage": "https://github.com/humeai/hume-evi-next-js-starter",
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@ai-sdk/groq": "^0.0.1",
    "@humeai/voice-react": "^0.1.12",
    "@radix-ui/react-label": "^2.1.0",
    "@radix-ui/react-slot": "^1.1.0",
    "@radix-ui/react-switch": "^1.0.3",
    "@radix-ui/react-toggle": "^1.0.3",
    "@types/react-virtualized": "^9.21.30",
    "ai": "^3.4.16",
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.1.1",
    "framer-motion": "^11.2.10",
    "geist": "^1.3.0",
    "groq-sdk": "^0.7.0",
    "hume": "^0.8.6",
    "lucide-react": "^0.394.0",
    "next": "14.2.3",
    "react": "^18",
    "react-dom": "^18",
    "react-virtualized": "^9.22.5",
    "remeda": "^2.0.9",
    "server-only": "^0.0.1",
    "tailwind-merge": "^2.3.0",
    "tailwindcss-animate": "^1.0.7",
    "ts-pattern": "^5.1.2",
    "zustand": "^5.0.0"
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "eslint": "^8",
    "eslint-config-next": "14.2.3",
    "postcss": "^8",
    "prettier": "^3.3.3",
    "tailwindcss": "^3.4.1",
    "typescript": "^5"
  }
}

```

### utils/index.ts

```typescript
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

```

### app/layout.tsx

```typescript
import type { Metadata } from "next";
import { GeistSans } from "geist/font/sans";
import { GeistMono } from "geist/font/mono";
import "./globals.css";
import { Nav } from "@/components/Nav";
import { cn } from "@/utils";

export const metadata: Metadata = {
  title: "hire_new",
  description: "A Next.js starter using Hume AI's Empathic Voice Interface",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body
        className={cn(
          GeistSans.variable,
          GeistMono.variable,
          "flex flex-col min-h-screen",
        )}
      >
        <Nav />
        {children}
      </body>
    </html>
  );
}

```

### app/page.tsx

```typescript
"use client"

import { Button } from "@/components/ui/button";
import { useRouter } from "next/navigation";

export default function Page() {
    const router = useRouter()
    return (
        <div className="mx-auto mt-52 flex flex-col align-start space-y-4">
            <h1 className="text-8xl">candidate_compare</h1>
            <h2 className="text-4xl">streamline interviews</h2>
            <div className="flex justify-evenly space-x-4">
                <Button
                    className="w-1/2"
                    onClick={() => router.push("/new-interview")}
                >
                    <span>generate an interview</span>
                </Button>
                <Button
                    className="w-1/2"
                    onClick={() => router.push("/recruiter-view")} 
                >
                    <span>view interviews</span>
                </Button>
            </div>
        </div>
    )
}

```

### agents_fetch/main.py

```python
from flask import Flask, request, jsonify
from flask_cors import CORS
import requests
import json

app = Flask(__name__)
CORS(app)

# Wrapper for the /main route of the agent
@app.route('/api', methods=['POST'])
def main_route():
    try:
        # Extract data from the incoming request
        req_data = request.get_json()

        # Forward the request to the agent running on port 8010
        agent_url = 'http://0.0.0.0:8010/main'
        agent_response = requests.post(agent_url, json=req_data)

        # Get the response from the agent and return it
        if agent_response.status_code == 200:
            return jsonify(agent_response.json())
        else:
            return jsonify({"error": "Agent responded with an error"}), agent_response.status_code

    except Exception as e:
        return jsonify({"error": str(e)}), 500

@app.route('/verifyapi', methods=['POST'])
def verify_route():
    try:
        # Extract data from the incoming request
        req_data = request.get_json()

        # Forward the request to the agent running on port 8010
        agent_url = 'http://0.0.0.0:8006/verify'
        agent_response = requests.post(agent_url, json=req_data)

        # Get the response from the agent and return it
        if agent_response.status_code == 200:
            return jsonify(agent_response.json())
        else:
            return jsonify({"error": "Agent responded with an error"}), agent_response.status_code

    except Exception as e:
        return jsonify({"error": str(e)}), 500


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

```

### app/new-interview/page.tsx

```typescript
import { JobInfoForm } from "@/components/JobInfoForm";

export default async function Page() {
    return (
        <div className="w-3/4 mx-auto p-10 flex flex-col space-y-2">
            <JobInfoForm />
        </div>

    )
}
```

### app/thank-you/page.tsx

```typescript

export default function Page() {
    return (
        <div className="w-3/4 mx-auto mt-32 h-full flex-col justify-center align-middle items-center">
            <h1 className="text-6xl">Thank you for participating in this interview. We will be in touch soon.</h1>
        </div>

    )
}
```

### app/recruiter-view/page.tsx

```typescript
"use client"
import MenuItem from "@/components/MenuItem"
import { useAddUserStore } from "@/stores/useAddUserStore"
import { useState } from "react"
import { useEffect } from "react"

interface interview {
    interview_record: any
    name: string
    job_desc: string
    role: string
    company: string
    transcript: string
    evaluation: string
}

export default function Page() {
    const users = useAddUserStore((state) => state.users)
    const [interviews, setInterviews] = useState<interview[]>([])
    const [normalInterviews, setNormalInterviews] = useState<interview[]>([])
    // const groq = new Groq({ apiKey: process.env.GROQ_API_KEY });


    useEffect(() => {
        const getAll = async () => {
            const req = await fetch("http://localhost:3001/all")
            const res = await req.json()
            setNormalInterviews(res.sort(()=> Math.random()-0.5))
            // const rankedCandidates: any[] = [];
            // res.forEach((candidate, index) => {
            // const firstName = candidate.name.split(' ')[0];
            // const fileName = `${firstName}.json`;
            // try {
            //     // Read each JSON file named after the candidate's first name
            //     const data = 
            //     const candidateDetails = JSON.parse(data);
            //     // Calculate normalised value
            //     const normalisedValue = (index + 1) * candidateDetails.confidence;
            //     // Create the ranked candidate object
            //     const rankedCandidate = {
            //     name: candidate.name,
            //     normalised_value: normalisedValue,
            //     };
            //     // Add optional properties if they exist
            //     if (candidateDetails.fprojects) {
            //     rankedCandidate.fprojects = candidateDetails.fprojects;
            //     }
            //     if (candidateDetails.fexperiences) {
            //     rankedCandidate.fexperiences = candidateDetails.fexperiences;
            //     }
            //     // Add the ranked candidate to the list
            //     rankedCandidates.push(rankedCandidate);
            // } catch (error) {
            //     console.error(`Error reading file ${fileName}:`, error);
            // }
            // });
            // // Sort candidates by normalised value in descending order
            // rankedCandidates.sort((a, b) => b.normalised_value - a.normalised_value);
            // // Display the candidates in the desired format
            // console.log('Authenticity Normalised Ranking:');
            // rankedCandidates.forEach((candidate) => {
            // console.log(candidate);
            // });
            console.log(res)
            setInterviews(res)
        }
        getAll();
    },[])

    return (
       <div className="w-3/4 mx-auto mt-20">
            <h1 className="text-3xl text-center mb-10">Recruiter Dashboard</h1>
                <div className="flex flex-col space-y-3">
                    <h1 className="mb-3 text-xl">Candidate Ranking</h1>
                    {users.map((interview, idx) => {
                        return (
                            <div className="flex w-full align-middle items-center space-x-3">
                                <span>{idx + 1}</span>
                                <MenuItem key={idx} label={`${interview.name} (${interview.role}) @ ${interview.company}`}>
                                    <></>
                                    {/* <div className="w-3/4 mx-auto"> */}
                                        {/* {insights} */}
                                    {/* </div> */}
                                </MenuItem>
                            </div>
                        ) 
                    })}
                </div>
       </div> 
    )
}
```

### app/interviews/[uid]/page.tsx

```typescript
import { getHumeAccessToken } from "@/utils/getHumeAccessToken";
import dynamic from "next/dynamic";

const Chat = dynamic(() => import("@/components/Chat"), {
  ssr: false,
});

export default async function Page({ params }: { params: { uid: string } }) {
  const accessToken = await getHumeAccessToken();
  const { uid } = params;

  if (!accessToken) {
    throw new Error();
  }

  return (
    <div className={"grow flex flex-col"}>
      {/* <h1>Interview for UID: {uid}</h1> */}
      <Chat accessToken={accessToken} uid={uid} />
    </div>
  );
}
```

### app/recruiter-view/[uid]/page.tsx

```typescript
"use client"

import { useEffect } from "react";


export default function Page({ params }: { params: { uid: string } }) {
    const { uid } = params;

    useEffect(() => {
        const fetchInterview = async () => {
            const req = await fetch(`http://localhost:3001/generate-insights/${uid}`,{
                headers: {
                    "Content-Type": "application/json"
                },
            })
            const res = await req.json();
            console.log(res)
        }
        fetchInterview()
    },[])

    return (
        <h1>hellop</h1> 
    )
}
```

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