-
Notifications
You must be signed in to change notification settings - Fork 113
feat: add Research Paper Explainer & Quiz Agent #72
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 1 commit
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,12 @@ | ||
| # Lamatic.ai Configuration | ||
| # Get these from https://studio.lamatic.ai → your project settings | ||
|
|
||
| LAMATIC_API_KEY=your_lamatic_api_key_here | ||
| LAMATIC_PROJECT_ID=your_project_id_here | ||
|
|
||
| # Flow endpoint URLs (from your Lamatic Studio deployment) | ||
| EXPLAIN_FLOW_URL=https://your-project.lamatic.app/api/explain | ||
| QUIZ_FLOW_URL=https://your-project.lamatic.app/api/quiz | ||
|
|
||
| # Optional: Customize the app title | ||
| NEXT_PUBLIC_APP_TITLE=Research Paper Explainer | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| # dependencies | ||
| /node_modules | ||
| /.pnp | ||
| .pnp.js | ||
| .yarn/install-state.gz | ||
|
|
||
| # testing | ||
| /coverage | ||
|
|
||
| # next.js | ||
| /.next/ | ||
| /out/ | ||
|
|
||
| # production | ||
| /build | ||
|
|
||
| # misc | ||
| .DS_Store | ||
| *.pem | ||
|
|
||
| # debug | ||
| npm-debug.log* | ||
| yarn-debug.log* | ||
| yarn-error.log* | ||
|
|
||
| # local env files | ||
| .env | ||
| .env*.local | ||
|
|
||
| # vercel | ||
| .vercel | ||
|
|
||
| # typescript | ||
| *.tsbuildinfo | ||
| next-env.d.ts |
|
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. 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! |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,155 @@ | ||||||||||||||||||||||||||||||||||
| # 📚 Research Paper Explainer & Quiz Agent | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| > A Next.js kit built on [Lamatic AgentKit](https://github.com/Lamatic/AgentKit) that takes any research paper abstract or full text, explains it at your chosen comprehension level, and generates an interactive multiple-choice quiz to test understanding. | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
| > A Next.js kit built on [Lamatic AgentKit](https://github.com/Lamatic/AgentKit) that takes any research paper abstract or full text, explains it at your chosen comprehension level, and generates an interactive multiple-choice quiz to test understanding. | |
| > A Next.js kit built on [Lamatic AgentKit](https://github.com/Lamatic/AgentKit) that lets you paste any research paper abstract or full text into the app, explains it at your chosen comprehension level, and generates an interactive multiple-choice quiz to test understanding. |
Copilot
AI
Mar 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Vercel deploy button URL points to a personal fork and to /apps/research-paper-explainer, but this kit lives under kits/research-paper-explainer in the main Lamatic/AgentKit repo. This link will fail or deploy the wrong directory; update it to use repository-url=https://github.com/Lamatic/AgentKit plus root-directory=kits/research-paper-explainer (and ideally include required env vars like other kits do).
| [](https://vercel.com/new/clone?repository-url=https://github.com/Mortarion002/AgentKit/tree/main/apps/research-paper-explainer) | |
| [](https://vercel.com/new/clone?repository-url=https://github.com/Lamatic/AgentKit&root-directory=kits/research-paper-explainer) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add language specifier to fenced code block.
The folder structure code block is missing a language specifier. While this doesn't affect rendering, it helps with accessibility and linting compliance.
Suggested fix
-```
+```text
research-paper-explainer/
├── app/📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ``` | |
| research-paper-explainer/ | |
| ├── app/ | |
| │ ├── page.tsx # Main UI (input + output panels) | |
| │ ├── layout.tsx # Root layout with fonts | |
| │ ├── globals.css # Design system & animations | |
| │ └── api/ | |
| │ ├── explain/route.ts # Calls the Lamatic explain flow | |
| │ └── quiz/route.ts # Calls the Lamatic quiz flow | |
| ├── lamatic-config.json # Flow definitions for Lamatic Studio | |
| ├── .env.example # Required environment variables | |
| ├── package.json | |
| ├── tailwind.config.ts | |
| ├── tsconfig.json | |
| └── README.md | |
| ``` |
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)
[warning] 32-32: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,48 @@ | ||||||||||||||||||||||||||||||||||||||||||
| import { NextRequest, NextResponse } from "next/server"; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| export async function POST(req: NextRequest) { | ||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||
| const { paperContent, level } = await req.json(); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| if (!paperContent || paperContent.trim().length < 50) { | ||||||||||||||||||||||||||||||||||||||||||
| return NextResponse.json( | ||||||||||||||||||||||||||||||||||||||||||
| { error: "Please provide a research paper abstract or content (at least 50 characters)." }, | ||||||||||||||||||||||||||||||||||||||||||
| { status: 400 } | ||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+6
to
+13
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. Guard input types before calling At Line 7, non-string Suggested patch- const { paperContent, level } = await req.json();
+ const body = await req.json();
+ const paperContent = typeof body?.paperContent === "string" ? body.paperContent : "";
+ const level = typeof body?.level === "string" ? body.level : undefined;
- if (!paperContent || paperContent.trim().length < 50) {
+ if (!paperContent || paperContent.trim().length < 50) {
return NextResponse.json(
{ error: "Please provide a research paper abstract or content (at least 50 characters)." },
{ status: 400 }
);
}📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| const flowUrl = process.env.EXPLAIN_FLOW_URL; | ||||||||||||||||||||||||||||||||||||||||||
| const apiKey = process.env.LAMATIC_API_KEY; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| if (!flowUrl || !apiKey) { | ||||||||||||||||||||||||||||||||||||||||||
| return NextResponse.json( | ||||||||||||||||||||||||||||||||||||||||||
| { error: "Server misconfiguration: missing EXPLAIN_FLOW_URL or LAMATIC_API_KEY." }, | ||||||||||||||||||||||||||||||||||||||||||
| { status: 500 } | ||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| const response = await fetch(flowUrl, { | ||||||||||||||||||||||||||||||||||||||||||
| method: "POST", | ||||||||||||||||||||||||||||||||||||||||||
| headers: { | ||||||||||||||||||||||||||||||||||||||||||
| "Content-Type": "application/json", | ||||||||||||||||||||||||||||||||||||||||||
| Authorization: `Bearer ${apiKey}`, | ||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||
| body: JSON.stringify({ paperContent, level: level || "undergraduate" }), | ||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
| const response = await fetch(flowUrl, { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json", | |
| Authorization: `Bearer ${apiKey}`, | |
| }, | |
| body: JSON.stringify({ paperContent, level: level || "undergraduate" }), | |
| }); | |
| const controller = new AbortController(); | |
| const timeout = setTimeout(() => controller.abort(), 15000); | |
| const response = await fetch(flowUrl, { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json", | |
| Authorization: `Bearer ${apiKey}`, | |
| }, | |
| body: JSON.stringify({ paperContent, level: level || "undergraduate" }), | |
| signal: controller.signal, | |
| }); | |
| clearTimeout(timeout); |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,69 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { NextRequest, NextResponse } from "next/server"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function POST(req: NextRequest) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { paperContent, numQuestions } = await req.json(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!paperContent || paperContent.trim().length < 50) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return NextResponse.json( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { error: "Please provide paper content before generating a quiz." }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { status: 400 } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const flowUrl = process.env.QUIZ_FLOW_URL; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const apiKey = process.env.LAMATIC_API_KEY; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!flowUrl || !apiKey) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return NextResponse.json( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { error: "Server misconfiguration: missing QUIZ_FLOW_URL or LAMATIC_API_KEY." }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { status: 500 } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = await fetch(flowUrl, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| method: "POST", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| headers: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Content-Type": "application/json", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Authorization: `Bearer ${apiKey}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body: JSON.stringify({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| paperContent, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| numQuestions: numQuestions || 5, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!response.ok) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const errText = await response.text(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error("Lamatic quiz flow error:", errText); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return NextResponse.json( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { error: "The AI agent failed to generate the quiz. Please try again." }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { status: 502 } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const data = await response.json(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const raw = data?.result || data?.output || data; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Parse if the LLM returned a JSON string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let parsed: { questions: QuizQuestion[] }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (typeof raw === "string") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const match = raw.match(/\{[\s\S]*\}/); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parsed = JSON.parse(match ? match[0] : raw); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parsed = raw; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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. Validate parsed quiz structure before returning. The Suggested validation let parsed: { questions: QuizQuestion[] };
if (typeof raw === "string") {
const match = raw.match(/\{[\s\S]*\}/);
parsed = JSON.parse(match ? match[0] : raw);
} else {
parsed = raw;
}
+ if (!Array.isArray(parsed?.questions) || parsed.questions.length === 0) {
+ console.error("Invalid quiz structure:", parsed);
+ return NextResponse.json(
+ { error: "The AI returned an invalid quiz format. Please try again." },
+ { status: 502 }
+ );
+ }
+
return NextResponse.json(parsed);📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return NextResponse.json(parsed); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error("Quiz API error:", err); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return NextResponse.json({ error: "Internal server error." }, { status: 500 }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interface QuizQuestion { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| question: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| options: string[]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| correct: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| explanation: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: Lamatic/AgentKit
Length of output: 892
Quote env values that contain spaces.
At line 12, the unquoted value can cause dotenv tooling inconsistencies; quote it for portability and consistency with similar values elsewhere in the codebase.
Suggested patch
📝 Committable suggestion
🧰 Tools
🪛 dotenv-linter (4.0.0)
[warning] 12-12: [ValueWithoutQuotes] This value needs to be surrounded in quotes
(ValueWithoutQuotes)