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
6 changes: 6 additions & 0 deletions kits/agentic/mockai/.env.example
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"
27 changes: 27 additions & 0 deletions kits/agentic/mockai/.gitignore
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
109 changes: 109 additions & 0 deletions kits/agentic/mockai/README.md
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.

[![Deploy with Vercel](https://vercel.com/button)](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"
```

### 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).
125 changes: 125 additions & 0 deletions kits/agentic/mockai/actions/orchestrate.ts
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 })
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Schema-validate both sides of the Lamatic call.

Client-side checks in page.tsx are not a boundary, so malformed payloads can still hit executeFlow here. On the way back, positives and negatives are defaulted but not type-checked, so a truthy non-array will reach the feedback page and blow up when the UI calls .map(...). Validate the request and response shapes before returning success: true.

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,
}
}
}
125 changes: 125 additions & 0 deletions kits/agentic/mockai/app/globals.css
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;
}
}
Loading
Loading