-
Notifications
You must be signed in to change notification settings - Fork 128
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: old-main
Are you sure you want to change the base?
Changes from 5 commits
5b4a53e
63e4993
0241491
a92235c
7aca27f
ef6622f
be6edc8
cdb13df
4ef625e
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.
|
||
|
|
||
| ### 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 }) | ||||||||||||||
|
sreecharan1306 marked this conversation as resolved.
Outdated
|
||||||||||||||
|
|
||||||||||||||
| 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 | ||||||||||||||
| } | ||||||||||||||
|
sreecharan1306 marked this conversation as resolved.
|
||||||||||||||
|
|
||||||||||||||
| 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) | ||||||||||||||
|
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. Sanitize error logs to prevent intelligence leakage. This error log writes the full error object to the server, which may contain sensitive interview content (job descriptions, candidate answers, API tokens in stack traces, or full Lamatic payloads). You're already sanitizing the error message before sending it to the client—apply the same discipline to server-side logging. Log only the sanitized message, flow name, and error type. 🔒 Proposed sanitized logging- console.error("[v0] Generation error:", error)
+ console.error("[v0] Generation error:", {
+ flow: "question",
+ errorType: error instanceof z.ZodError ? "validation" : error instanceof Error ? "execution" : "unknown",
+ message: error instanceof Error ? error.message : "Unknown error"
+ })📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
|
|
||||||||||||||
| 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.