Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
940d0c4
feat: add support triage kit UI and configuration
VITianYash42 Mar 21, 2026
5e70655
trigger preview build
VITianYash42 Mar 21, 2026
69d4027
style: deploy enterprise glassmorphic UI
VITianYash42 Mar 21, 2026
cbb842d
Fixed some minor bugs
VITianYash42 Mar 21, 2026
04f3aad
refactor: address CodeRabbit review and secure API route
VITianYash42 Mar 21, 2026
afe8151
fix: remove dead utils import
VITianYash42 Mar 21, 2026
51d3b9f
fix: rename route.js to route.ts to resolve build error
VITianYash42 Mar 21, 2026
d696140
Merge branch 'main' into feat/support-triage
amanintech Mar 27, 2026
3f9c961
Merge branch 'main' into feat/support-triage
VITianYash42 Mar 28, 2026
5c592b7
fix: implement defensive initialization and strict error typing
VITianYash42 Mar 28, 2026
17a3de1
chore: add missing flows directory to satisfy CI validation
VITianYash42 Mar 28, 2026
2fc331d
chore: add dummy flow.json to satisfy subdirectory validation
VITianYash42 Mar 28, 2026
00fe5b3
chore: add valid flow schema files
VITianYash42 Mar 28, 2026
8c6539c
Merge branch 'main' into feat/support-triage
VITianYash42 Mar 30, 2026
689aa8a
Merge branch 'main' into feat/support-triage
vrijraj Mar 30, 2026
b1e4705
Merge branch 'Lamatic:main' into feat/support-triage
VITianYash42 Mar 31, 2026
053de0e
fix: resolve light mode text visibility and remove global body bg
VITianYash42 Mar 31, 2026
07516c3
fix: wipe legacy globals.css to fix tailwind gradient
VITianYash42 Mar 31, 2026
2ae78f9
Merge branch 'main' into feat/support-triage
VITianYash42 Apr 1, 2026
688183a
Fix: Light Mode UI
VITianYash42 Apr 1, 2026
b93288a
Merge branch 'main' into feat/support-triage
VITianYash42 Apr 3, 2026
0b00150
fix ui
VITianYash42 Apr 3, 2026
e597773
kill dark mode in root
VITianYash42 Apr 3, 2026
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
4 changes: 4 additions & 0 deletions kits/automation/support-triage/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
LAMATIC_PROJECT_ENDPOINT=
LAMATIC_PROJECT_ID=
LAMATIC_PROJECT_API_KEY=
LAMATIC_FLOW_ID=
42 changes: 42 additions & 0 deletions kits/automation/support-triage/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env*
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
!.env.example
17 changes: 17 additions & 0 deletions kits/automation/support-triage/README.md
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.

Could you add a live demo link to this file? would allow us to view the functionality better, and allow users to see the kit before forking it. otherwise, LGTM. Thanks! Will check once that is fixed and github action will be handled

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@d-pamneja I already have added the live demo link to the README! You can also test the deployment here: https://agent-kit-ten.vercel.app/

Thanks for the review. Let me know if you need anything else to unblock the merge.

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.

Screenshot 2026-03-31 at 11 37 51 AM If you could resolve the UI Issues here as well. thanks!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@d-pamneja Sorry for that! I had hardcoded the text colors for a dark theme and missed the light mode toggle. I've updated the Tailwind classes to properly support both light and dark system preferences. The Vercel deployment has been automatically updated with the fix.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# AI Support Triage Engine

## What This Kit Does
Automates customer support by taking inbound tickets and instantly generating a category, sentiment analysis, urgency level, and a draft email response. This saves time, reduces manual routing work, and improves response clarity.

## Providers & Prerequisites
- Lamatic Studio Flow
- Gemini Free Tier (Note: Requires a 60-second cooldown between requests to prevent 429 Rate Limit errors).

## How to Run Locally
1. `cd kits/automation/support-triage`
2. `npm install`
3. `cp .env.example .env.local` and fill in your Lamatic API values.
4. `npm run dev`
Comment thread
amanintech marked this conversation as resolved.

## Lamatic Flow
Flow ID: `your-flow-id`
24 changes: 24 additions & 0 deletions kits/automation/support-triage/app/api/triage/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Lamatic } from 'lamatic';
import { NextRequest, NextResponse } from 'next/server';

const lamaticClient = new Lamatic({
endpoint: process.env.LAMATIC_PROJECT_ENDPOINT!,
projectId: process.env.LAMATIC_PROJECT_ID!,
apiKey: process.env.LAMATIC_PROJECT_API_KEY!,
});

export async function POST(request: NextRequest) {
try {
const { ticket_text } = await request.json();
const flowId = process.env.LAMATIC_FLOW_ID;

if (!flowId) {
return NextResponse.json({ error: "Missing LAMATIC_FLOW_ID configuration." }, { status: 500 });
}

const response = await lamaticClient.executeFlow(flowId, { ticket_text });
return NextResponse.json(response);
} catch (error: any) {
return NextResponse.json({ error: error.message || "API execution failed" }, { status: 500 });
}
}
Binary file added kits/automation/support-triage/app/favicon.ico
Binary file not shown.
26 changes: 26 additions & 0 deletions kits/automation/support-triage/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
@import "tailwindcss";

:root {
--background: #ffffff;
--foreground: #171717;
}

@theme inline {
Comment thread
amanintech marked this conversation as resolved.
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
}

@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}

body {
background: var(--background);
color: var(--foreground);
font-family: var(--font-sans), Arial, Helvetica, sans-serif;
}
33 changes: 33 additions & 0 deletions kits/automation/support-triage/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";

const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});

const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});

export const metadata = {
title: "AI Support Triage Engine",
description: "Automated customer support ticket categorization, sentiment analysis, and draft response generation",
};

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html
lang="en"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
>
<body className="min-h-full flex flex-col">{children}</body>
</html>
);
}
170 changes: 170 additions & 0 deletions kits/automation/support-triage/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
'use client';
import React, { useState } from 'react';
import { lamaticClient } from './utils';

export default function Page() {
const [ticketText, setTicketText] = useState('');
const [loading, setLoading] = useState(false);
const [triageData, setTriageData] = useState<any>(null);

const executeFlow = async () => {
const normalizedTicket = ticketText.trim();
if (!normalizedTicket) {
setTriageData({ error: "Please enter a ticket before processing." });
return;
}

setLoading(true);
setTriageData(null);

try {
const res = await fetch('/api/triage', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ticket_text: normalizedTicket })
});
Comment thread
amanintech marked this conversation as resolved.
Outdated

const response = await res.json();
const data = response.result || response;

// Improved Rate Limit Detection
const message = typeof data?.message === 'string' ? data.message.toLowerCase() : '';
const isRateLimited = message.includes('quota') || message.includes('rate limit') || data?.status === 429;

if (isRateLimited) {
setTriageData({ error: "API Rate Limit: Free tier restricted. Please wait 60 seconds before testing the next ticket." });
} else if (data.error) {
Comment thread
amanintech marked this conversation as resolved.
Outdated
setTriageData({ error: data.error });
} else {
setTriageData(data);
}

} catch (error) {
console.error("Execution error:", error);
setTriageData({ error: "Failed to connect to AI triage." });
} finally {
setLoading(false);
}
};

return (
<div className="min-h-screen bg-[radial-gradient(ellipse_at_top,_var(--tw-gradient-stops))] from-gray-900 via-gray-950 to-black text-gray-100 p-4 md:p-8 font-sans selection:bg-blue-500/30">
<div className="max-w-3xl mx-auto space-y-8 mt-10 md:mt-20">

{/* Header */}
<div className="text-center space-y-4">
<div className="inline-flex items-center justify-center p-2 bg-blue-500/10 rounded-full mb-4 border border-blue-500/20">
<span className="text-blue-400 text-xs font-bold tracking-widest uppercase px-3">AgentKit Challenge</span>
</div>
<h1 className="text-4xl md:text-5xl font-extrabold tracking-tight text-transparent bg-clip-text bg-gradient-to-r from-blue-400 via-indigo-400 to-purple-400 pb-2">
AI Support Triage Engine
</h1>
<p className="text-gray-400 text-lg max-w-xl mx-auto">
Instantly categorize issues, analyze customer sentiment, and draft responses using Lamatic flows.
</p>
</div>

{/* Input Card */}
<div className="bg-white/[0.02] backdrop-blur-xl border border-white/10 rounded-2xl p-6 md:p-8 shadow-2xl">
<label htmlFor="ticketText" className="block text-sm font-semibold text-gray-300 mb-3 uppercase tracking-wider">
Inbound Customer Ticket
</label>
<textarea
id="ticketText"
className="w-full p-5 bg-black/40 border border-gray-800 rounded-xl focus:ring-2 focus:ring-blue-500/50 focus:border-blue-500/50 outline-none resize-none text-gray-200 placeholder-gray-600 transition-all duration-300"
value={ticketText}
onChange={(e) => setTicketText(e.target.value)}
placeholder="I was overcharged by $50 on my invoice today and I am furious!"
spellCheck="false"
/>

<button
onClick={executeFlow}
disabled={loading}
className="mt-6 w-full group relative overflow-hidden bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-500 hover:to-indigo-500 disabled:from-gray-800 disabled:to-gray-900 disabled:text-gray-500 text-white font-bold py-4 px-6 rounded-xl transition-all duration-300 shadow-[0_0_20px_rgba(79,70,229,0.2)] hover:shadow-[0_0_30px_rgba(79,70,229,0.4)] disabled:shadow-none flex justify-center items-center"
>
{loading ? (
<span className="flex items-center gap-2">
<svg className="animate-spin h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Processing Ticket...
</span>
) : 'Process Ticket'}
</button>
</div>

{/* Error State */}
{triageData?.error && (
<div className="bg-red-500/10 border border-red-500/20 rounded-xl p-5 flex items-start gap-4">
<div className="bg-red-500/20 p-2 rounded-full mt-0.5">
<svg className="w-5 h-5 text-red-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
</div>
<div>
<h3 className="text-red-400 font-bold">Execution Error</h3>
<p className="text-red-300/80 text-sm mt-1">{triageData.error}</p>
</div>
</div>
)}

{/* Results Area */}
{triageData && !triageData.error && (
<div className="space-y-6 animate-in fade-in slide-in-from-bottom-4 duration-700">
{/* Metrics Grid */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="bg-white/[0.02] border border-white/10 rounded-xl p-6 shadow-lg backdrop-blur-md">
<div className="flex items-center gap-3 mb-2">
<div className="w-2 h-2 rounded-full bg-blue-400 shadow-[0_0_10px_rgba(96,165,250,0.8)]"></div>
<p className="text-xs text-gray-400 uppercase tracking-widest font-semibold">Category</p>
</div>
<p className="text-2xl font-bold text-gray-100">{triageData.category || 'N/A'}</p>
</div>

<div className="bg-white/[0.02] border border-white/10 rounded-xl p-6 shadow-lg backdrop-blur-md">
<div className="flex items-center gap-3 mb-2">
<div className="w-2 h-2 rounded-full bg-emerald-400 shadow-[0_0_10px_rgba(52,211,153,0.8)]"></div>
<p className="text-xs text-gray-400 uppercase tracking-widest font-semibold">Sentiment</p>
</div>
<p className="text-2xl font-bold text-gray-100">{triageData.sentiment || 'N/A'}</p>
</div>

<div className="bg-white/[0.02] border border-white/10 rounded-xl p-6 shadow-lg backdrop-blur-md">
<div className="flex items-center gap-3 mb-2">
<div className="w-2 h-2 rounded-full bg-rose-400 shadow-[0_0_10px_rgba(251,113,133,0.8)]"></div>
<p className="text-xs text-gray-400 uppercase tracking-widest font-semibold">Urgency</p>
</div>
<p className="text-2xl font-bold text-gray-100">{triageData.urgency || 'N/A'}</p>
</div>
</div>

{/* AI Draft Response */}
<div className="bg-white/[0.02] border border-white/10 rounded-xl overflow-hidden shadow-lg backdrop-blur-md">
<div className="bg-white/[0.02] border-b border-white/10 px-6 py-4 flex items-center justify-between">
<p className="text-sm font-semibold text-indigo-400 flex items-center gap-2">
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
Generated AI Draft
</p>
</div>
<div className="p-6 bg-black/20">
<p className="text-sm md:text-base leading-relaxed whitespace-pre-wrap text-gray-300 font-medium">
{triageData.draft || JSON.stringify(triageData, null, 2)}
</p>
</div>
</div>
</div>
)}
{/* Subtle Credit Footer */}
<div className="text-center pt-8 pb-4">
<p className="text-gray-500 text-sm">
Built for the AgentKit Challenge by Yash Singhal
</p>
</div>
</div>
</div>
);
}
32 changes: 32 additions & 0 deletions kits/automation/support-triage/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "AI Support Triage Engine",
"description": "Automated triage system that categorizes, analyzes sentiment, and drafts responses for customer support tickets.",
"tags": ["🤖 Automation", "💬 Assistant"],
"author": {
"name": "Yash Singhal",
"email": "yashjee979@gmail.com",
"url": "https://github.com/VITianYash42"
},
"steps": [
{
"id": "your-flow-id",
"type": "mandatory",
"envKey": "NEXT_PUBLIC_LAMATIC_FLOW_ID"
}
],
"integrations": [
{
"name": "Lamatic Studio Flow",
"type": "AI Framework"
},
{
"name": "Gemini Free Tier",
"type": "LLM"
}
],
"features": ["Sentiment Analysis", "Auto-Drafting", "Urgency Detection"],
"demoUrl": "https://agent-kit-git-feat-suppo-3f22fd-yash-singhals-projects-d43367ba.vercel.app/",
"githubUrl": "https://github.com/VITianYash42/AgentKit/tree/feat/support-triage/kits/automation/support-triage",
"deployUrl": "",
"documentationUrl": ""
}
18 changes: 18 additions & 0 deletions kits/automation/support-triage/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";

const eslintConfig = defineConfig([
...nextVitals,
...nextTs,
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
]),
]);

export default eslintConfig;
7 changes: 7 additions & 0 deletions kits/automation/support-triage/next.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
/* config options here */
};

export default nextConfig;
Loading