-
Notifications
You must be signed in to change notification settings - Fork 94
feat: Add mock-ai interviewer Agentkit #101
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| # AGENTIC_GENERATE_CONTENT = "AGENTIC_GENERATE_CONTENT Flow ID" | ||
| LAMATIC_API_URL = "LAMATIC_API_URL" | ||
| LAMATIC_PROJECT_ID = "LAMATIC_PROJECT_ID" | ||
| LAMATIC_API_KEY = "LAMATIC_API_KEY" | ||
| AGENTIC_FEEDBACK_FLOW_ID="Feedback agent Flow ID" | ||
| AGENTIC_QUESTION_FLOW_ID="Question agent Flow ID" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
|
||
| # dependencies | ||
| /node_modules | ||
|
|
||
| # next.js | ||
| /.next/ | ||
| /out/ | ||
|
|
||
| # production | ||
| /build | ||
|
|
||
| # debug | ||
| npm-debug.log* | ||
| yarn-debug.log* | ||
| yarn-error.log* | ||
| .pnpm-debug.log* | ||
|
|
||
| # env files | ||
| .env | ||
|
|
||
| # vercel | ||
| .vercel | ||
|
|
||
| # typescript | ||
| *.tsbuildinfo | ||
| next-env.d.ts |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| # Agent Kit Generation by Lamatic.ai | ||
| <p align="center"> | ||
| <img src="https://media1.giphy.com/media/v1.Y2lkPTc5MGI3NjExYnl0MGoxN203ZDczams4NzN6MGkxaXRham95N2NrejluZnU1N2NhMCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/TcZYytytvZBLODohJb/giphy.gif"/> | ||
| </p> | ||
|
|
||
| <p align="center"> | ||
| <a href="https://agent-kit-generation.vercel.app" target="_blank"> | ||
| <img src="https://img.shields.io/badge/Live%20Demo-black?style=for-the-badge" alt="Live Demo" /> | ||
| </a> | ||
| </p> | ||
|
|
||
|
|
||
| **Agent Kit Generation** is an AI-powered content generation system built with [Lamatic.ai](https://lamatic.ai). It uses intelligent workflows to generate text, images, and JSON content through a modern Next.js interface with markdown rendering support. | ||
|
|
||
| [](https://vercel.com/new/clone?repository-url=https://github.com/Lamatic/AgentKit&root-directory=kits/agentic/generation&env=AGENTIC_GENERATE_CONTENT,LAMATIC_API_URL,LAMATIC_PROJECT_ID,LAMATIC_API_KEY&envDescription=Your%20Lamatic%20Generation%20keys%20are%20required.&envLink=https://lamatic.ai/templates/agentkits/agentic/agent-kit-generation) | ||
|
|
||
| --- | ||
|
|
||
| ## Lamatic Setup (Pre and Post) | ||
|
|
||
| Before running this project, you must build and deploy the flow in Lamatic, then wire its config into this codebase. | ||
|
|
||
| Pre: Build in Lamatic | ||
| 1. Sign in or sign up at https://lamatic.ai | ||
| 2. Create a project (if you don’t have one yet) | ||
| 3. Click “+ New Flow” and select "Templates" | ||
| 4. Select the 'Generation' agent kit | ||
| 5. Configure providers/tools/inputs as prompted | ||
| 6. Deploy the kit in Lamatic and obtain your .env keys | ||
| 7. Copy the keys from your studio | ||
|
|
||
| Post: Wire into this repo | ||
| 1. Create a .env file and set the keys | ||
| 2. Install and run locally: | ||
| - npm install | ||
| - npm run dev | ||
| 3. Deploy (Vercel recommended): | ||
| - Import your repo, set the project's Root Directory (if applicable) | ||
| - Add env vars in Vercel (same as your .env) | ||
| - Deploy and test your live URL | ||
|
|
||
| Notes | ||
| - Coming soon: single-click export and "Connect Git" in Lamatic to push config directly to your repo. | ||
|
|
||
| --- | ||
|
|
||
| ## 🔑 Setup | ||
| ## Required Keys and Config | ||
|
|
||
| You’ll need these things to run this project locally: | ||
|
|
||
| 1. **.env Keys** → get it from your [Lamatic account](https://lamatic.ai) post kit deployment. | ||
|
|
||
|
|
||
| | Item | Purpose | Where to Get It | | ||
| | ----------------- | -------------------------------------------- | ----------------------------------------------- | | ||
| | .env Key | Authentication for Lamatic AI APIs and Orchestration | [lamatic.ai](https://lamatic.ai) | | ||
|
|
||
| ### 1. Environment Variables | ||
|
|
||
| Create `.env.local` with: | ||
|
|
||
| ```bash | ||
| # Lamatic | ||
| AGENTIC_GENERATE_CONTENT = "AGENTIC_GENERATE_CONTENT Flow ID" | ||
| LAMATIC_API_URL = "LAMATIC_API_URL" | ||
| LAMATIC_PROJECT_ID = "LAMATIC_PROJECT_ID" | ||
| LAMATIC_API_KEY = "LAMATIC_API_KEY" | ||
| ``` | ||
amanintech marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| ### 2. Install & Run | ||
|
|
||
| ```bash | ||
| npm install | ||
| npm run dev | ||
| # Open http://localhost:3000 | ||
| ``` | ||
| --- | ||
|
|
||
| ## 📂 Repo Structure | ||
|
|
||
| ``` | ||
| /actions | ||
| └── orchestrate.ts # Lamatic workflow orchestration | ||
| /app | ||
| └── page.tsx # Main generation form UI | ||
| /components | ||
| ├── header.tsx # Header component with navigation | ||
| └── ui # shadcn/ui components | ||
| /lib | ||
| └── lamatic-client.ts # Lamatic SDK client | ||
| /public | ||
| └── lamatic-logo.png # Lamatic branding | ||
| /flows | ||
| └── ... # Lamatic Flows | ||
| /package.json # Dependencies & scripts | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## 🤝 Contributing | ||
|
|
||
| We welcome contributions! Open an issue or PR in this repo. | ||
|
|
||
| --- | ||
|
|
||
| ## 📜 License | ||
|
|
||
| MIT License – see [LICENSE](./LICENSE). | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| "use server" | ||
|
|
||
| import { lamaticClient } from "@/lib/lamatic-client" | ||
| import { config } from "../orchestrate.js" | ||
|
|
||
| export async function generateQuestions( | ||
| jobTitle: string, | ||
| yearsOfExp: number, | ||
| jobDesc: string, | ||
| ): Promise<{ | ||
| success: boolean | ||
| questions?: string[] | ||
| error?: string | ||
| }> { | ||
| try { | ||
| console.log("[v0] Generating questions with:", { jobTitle, yearsOfExp, jobDesc }) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove raw interview payload logging from these server actions. These logs persist job descriptions, candidate answers, and full Lamatic responses on the server. That's avoidable sensitive-data retention for an interview product; keep only metadata such as flow name, counts, and status. Also applies to: 30-33, 74-74, 87-90 |
||
|
|
||
| const flow = config.flows.question | ||
|
|
||
| if (!flow.workflowId) { | ||
| throw new Error("Workflow ID not found in config for question flow.") | ||
| } | ||
|
|
||
| const inputs = { | ||
| jobTitle, | ||
| yearsOfExp: parseInt(yearsOfExp.toString()), | ||
| jobDesc | ||
| } | ||
|
Comment on lines
+24
to
+28
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Schema-validate both sides of the Lamatic call. Client-side checks in Also applies to: 35-43, 82-104 |
||
|
|
||
| console.log("[v0] Sending inputs:", inputs) | ||
|
|
||
| const resData = await lamaticClient.executeFlow(flow.workflowId, inputs) | ||
| console.log("[v0] Raw response:", resData) | ||
|
|
||
| const questions = resData?.result?.data | ||
|
|
||
| if (!questions || !Array.isArray(questions) || questions.length === 0) { | ||
| throw new Error("No questions found in response") | ||
| } | ||
|
|
||
| return { | ||
| success: true, | ||
| questions, | ||
| } | ||
| } catch (error) { | ||
| console.error("[v0] Generation error:", error) | ||
|
|
||
| let errorMessage = "Unknown error occurred" | ||
| if (error instanceof Error) { | ||
| errorMessage = error.message | ||
| if (error.message.includes("fetch failed")) { | ||
| errorMessage = | ||
| "Network error: Unable to connect to the service. Please check your internet connection and try again." | ||
| } else if (error.message.includes("API key")) { | ||
| errorMessage = "Authentication error: Please check your API configuration." | ||
| } | ||
| } | ||
|
|
||
| return { | ||
| success: false, | ||
| error: errorMessage, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| export async function evaluateAnswers( | ||
| candidateResponses: { question: string, answers: string }[] | ||
| ): Promise<{ | ||
| success: boolean | ||
| feedback?: { positives: string[], negatives: string[], rating: number } | ||
| error?: string | ||
| }> { | ||
| try { | ||
| console.log("[v0] Evaluating answers with:", { candidateResponses }) | ||
|
|
||
| const flow = config.flows.feedback | ||
|
|
||
| if (!flow.workflowId) { | ||
| throw new Error("Workflow ID not found in config for feedback flow.") | ||
| } | ||
|
|
||
| // We can just format the string or array to be safe. We pass what's expected. | ||
| const inputs = { | ||
| candidateResponses | ||
| } | ||
|
|
||
| console.log("[v0] Sending inputs:", JSON.stringify(inputs)) | ||
|
|
||
| const resData = await lamaticClient.executeFlow(flow.workflowId, inputs) | ||
| console.log("[v0] Raw response:", resData) | ||
|
|
||
| const result = resData?.result | ||
|
|
||
| if (!result || typeof result.rating !== 'number') { | ||
| throw new Error("No feedback found in response") | ||
| } | ||
|
|
||
| return { | ||
| success: true, | ||
| feedback: { | ||
| positives: result.positives || [], | ||
| negatives: result.negatives || [], | ||
| rating: result.rating | ||
| }, | ||
| } | ||
| } catch (error) { | ||
| console.error("[v0] Evaluation error:", error) | ||
|
|
||
| let errorMessage = "Unknown error occurred" | ||
| if (error instanceof Error) { | ||
| errorMessage = error.message | ||
| if (error.message.includes("fetch failed")) { | ||
| errorMessage = | ||
| "Network error: Unable to connect to the service. Please check your internet connection and try again." | ||
| } else if (error.message.includes("API key")) { | ||
| errorMessage = "Authentication error: Please check your API configuration." | ||
| } | ||
| } | ||
|
|
||
| return { | ||
| success: false, | ||
| error: errorMessage, | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| @import 'tailwindcss'; | ||
| @import 'tw-animate-css'; | ||
|
|
||
| @custom-variant dark (&:is(.dark *)); | ||
|
|
||
| :root { | ||
| --background: oklch(1 0 0); | ||
| --foreground: oklch(0.145 0 0); | ||
| --card: oklch(1 0 0); | ||
| --card-foreground: oklch(0.145 0 0); | ||
| --popover: oklch(1 0 0); | ||
| --popover-foreground: oklch(0.145 0 0); | ||
| --primary: oklch(0.205 0 0); | ||
| --primary-foreground: oklch(0.985 0 0); | ||
| --secondary: oklch(0.97 0 0); | ||
| --secondary-foreground: oklch(0.205 0 0); | ||
| --muted: oklch(0.97 0 0); | ||
| --muted-foreground: oklch(0.556 0 0); | ||
| --accent: oklch(0.97 0 0); | ||
| --accent-foreground: oklch(0.205 0 0); | ||
| --destructive: oklch(0.577 0.245 27.325); | ||
| --destructive-foreground: oklch(0.577 0.245 27.325); | ||
| --border: oklch(0.922 0 0); | ||
| --input: oklch(0.922 0 0); | ||
| --ring: oklch(0.708 0 0); | ||
| --chart-1: oklch(0.646 0.222 41.116); | ||
| --chart-2: oklch(0.6 0.118 184.704); | ||
| --chart-3: oklch(0.398 0.07 227.392); | ||
| --chart-4: oklch(0.828 0.189 84.429); | ||
| --chart-5: oklch(0.769 0.188 70.08); | ||
| --radius: 0.625rem; | ||
| --sidebar: oklch(0.985 0 0); | ||
| --sidebar-foreground: oklch(0.145 0 0); | ||
| --sidebar-primary: oklch(0.205 0 0); | ||
| --sidebar-primary-foreground: oklch(0.985 0 0); | ||
| --sidebar-accent: oklch(0.97 0 0); | ||
| --sidebar-accent-foreground: oklch(0.205 0 0); | ||
| --sidebar-border: oklch(0.922 0 0); | ||
| --sidebar-ring: oklch(0.708 0 0); | ||
| } | ||
|
|
||
| .dark { | ||
| --background: oklch(0.145 0 0); | ||
| --foreground: oklch(0.985 0 0); | ||
| --card: oklch(0.145 0 0); | ||
| --card-foreground: oklch(0.985 0 0); | ||
| --popover: oklch(0.145 0 0); | ||
| --popover-foreground: oklch(0.985 0 0); | ||
| --primary: oklch(0.985 0 0); | ||
| --primary-foreground: oklch(0.205 0 0); | ||
| --secondary: oklch(0.269 0 0); | ||
| --secondary-foreground: oklch(0.985 0 0); | ||
| --muted: oklch(0.269 0 0); | ||
| --muted-foreground: oklch(0.708 0 0); | ||
| --accent: oklch(0.269 0 0); | ||
| --accent-foreground: oklch(0.985 0 0); | ||
| --destructive: oklch(0.396 0.141 25.723); | ||
| --destructive-foreground: oklch(0.637 0.237 25.331); | ||
| --border: oklch(0.269 0 0); | ||
| --input: oklch(0.269 0 0); | ||
| --ring: oklch(0.439 0 0); | ||
| --chart-1: oklch(0.488 0.243 264.376); | ||
| --chart-2: oklch(0.696 0.17 162.48); | ||
| --chart-3: oklch(0.769 0.188 70.08); | ||
| --chart-4: oklch(0.627 0.265 303.9); | ||
| --chart-5: oklch(0.645 0.246 16.439); | ||
| --sidebar: oklch(0.205 0 0); | ||
| --sidebar-foreground: oklch(0.985 0 0); | ||
| --sidebar-primary: oklch(0.488 0.243 264.376); | ||
| --sidebar-primary-foreground: oklch(0.985 0 0); | ||
| --sidebar-accent: oklch(0.269 0 0); | ||
| --sidebar-accent-foreground: oklch(0.985 0 0); | ||
| --sidebar-border: oklch(0.269 0 0); | ||
| --sidebar-ring: oklch(0.439 0 0); | ||
| } | ||
|
|
||
| @theme inline { | ||
| --font-sans: 'Geist', 'Geist Fallback'; | ||
| --font-mono: 'Geist Mono', 'Geist Mono Fallback'; | ||
| --color-background: var(--background); | ||
| --color-foreground: var(--foreground); | ||
| --color-card: var(--card); | ||
| --color-card-foreground: var(--card-foreground); | ||
| --color-popover: var(--popover); | ||
| --color-popover-foreground: var(--popover-foreground); | ||
| --color-primary: var(--primary); | ||
| --color-primary-foreground: var(--primary-foreground); | ||
| --color-secondary: var(--secondary); | ||
| --color-secondary-foreground: var(--secondary-foreground); | ||
| --color-muted: var(--muted); | ||
| --color-muted-foreground: var(--muted-foreground); | ||
| --color-accent: var(--accent); | ||
| --color-accent-foreground: var(--accent-foreground); | ||
| --color-destructive: var(--destructive); | ||
| --color-destructive-foreground: var(--destructive-foreground); | ||
| --color-border: var(--border); | ||
| --color-input: var(--input); | ||
| --color-ring: var(--ring); | ||
| --color-chart-1: var(--chart-1); | ||
| --color-chart-2: var(--chart-2); | ||
| --color-chart-3: var(--chart-3); | ||
| --color-chart-4: var(--chart-4); | ||
| --color-chart-5: var(--chart-5); | ||
| --radius-sm: calc(var(--radius) - 4px); | ||
| --radius-md: calc(var(--radius) - 2px); | ||
| --radius-lg: var(--radius); | ||
| --radius-xl: calc(var(--radius) + 4px); | ||
| --color-sidebar: var(--sidebar); | ||
| --color-sidebar-foreground: var(--sidebar-foreground); | ||
| --color-sidebar-primary: var(--sidebar-primary); | ||
| --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); | ||
| --color-sidebar-accent: var(--sidebar-accent); | ||
| --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); | ||
| --color-sidebar-border: var(--sidebar-border); | ||
| --color-sidebar-ring: var(--sidebar-ring); | ||
| } | ||
|
|
||
| @layer base { | ||
| * { | ||
| @apply border-border outline-ring/50; | ||
| } | ||
| body { | ||
| @apply bg-background text-foreground; | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.