Skip to content

feat(ai-agent): implement AI agent functionality #8

Merged
Z4phxr merged 2 commits intomainfrom
feat/LLM_intergation_for_course_creation
Apr 15, 2026
Merged

feat(ai-agent): implement AI agent functionality #8
Z4phxr merged 2 commits intomainfrom
feat/LLM_intergation_for_course_creation

Conversation

@Z4phxr
Copy link
Copy Markdown
Owner

@Z4phxr Z4phxr commented Apr 15, 2026

  • Added new admin page for AI agent management, including a dedicated workspace for course generation.
  • Implemented API routes for accepting drafts and generating course content, with error handling for unauthorized access.
  • Introduced progress tracking for AI generation runs, allowing admins to monitor status and results.
  • Updated environment configuration to include OpenAI API keys and models, enhancing integration capabilities.
  • Enhanced sidebar navigation to include AI agent links for improved accessibility.

…iguration

- Added new admin page for AI agent management, including a dedicated workspace for course generation.
- Implemented API routes for accepting drafts and generating course content, with error handling for unauthorized access.
- Introduced progress tracking for AI generation runs, allowing admins to monitor status and results.
- Updated environment configuration to include OpenAI API keys and models, enhancing integration capabilities.
- Enhanced sidebar navigation to include AI agent links for improved accessibility.
Copilot AI review requested due to automatic review settings April 15, 2026 20:41
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Introduces an admin-facing “AI Agent” feature that can generate a draft course structure from discovery inputs, accept the draft, and then generate/persist full course content while exposing progress updates for admins.

Changes:

  • Added Zod schemas, prompt builders, provider callers, and a generation/persistence pipeline for AI-driven course creation.
  • Added admin UI workspace + sidebar navigation entry, plus new admin API routes for draft generation, acceptance, and progress polling.
  • Updated .env.example and added a documentation index for deeper technical docs.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
LearningPlatform/lib/ai-agent/schemas.ts Defines AI request/response shapes for draft + module + flashcard generation.
LearningPlatform/lib/ai-agent/providers.ts Implements Anthropic/OpenAI text generation + JSON extraction helper.
LearningPlatform/lib/ai-agent/prompts.ts Centralizes system/user prompt construction for draft/modules/flashcards.
LearningPlatform/lib/ai-agent/progress-store.ts Adds in-memory run + timeline tracking for admin progress polling.
LearningPlatform/lib/ai-agent/generation.ts Orchestrates draft generation, module generation, Payload persistence, tag/flashcard creation, and progress updates.
LearningPlatform/components/admin/sidebar.tsx Adds “AI Agent” entry to admin navigation.
LearningPlatform/components/admin/ai-agent-workspace.tsx Admin UI for discovery inputs, iterative draft chat, and generation progress timeline.
LearningPlatform/app/api/admin/ai-agent/draft/route.ts Admin endpoint to generate/repair a draft course JSON.
LearningPlatform/app/api/admin/ai-agent/accept/route.ts Admin endpoint to start an async “accept & generate” run.
LearningPlatform/app/api/admin/ai-agent/progress/[runId]/route.ts Admin endpoint to poll run progress/timeline.
LearningPlatform/app/(admin)/admin/ai-agent/page.tsx New admin page rendering the AI agent workspace.
LearningPlatform/.env.example Documents optional OpenAI env vars for the AI agent provider.
LearningPlatform/documentation/README.md Adds a documentation index (currently references missing docs).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +98 to +100
title: z.string().min(2),
caption: z.string().optional(),
aspectRatio: z.enum(['16:9', '4:3']).optional(),
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

theoryBlockSchema's video variant requires title and makes aspectRatio optional, but the Payload VideoBlock defines title as optional and aspectRatio as required with a default. Keeping the Zod schema stricter/different than Payload will increase model retry/fallback rates. Consider making title optional and adding a default for aspectRatio (e.g. '16:9').

Suggested change
title: z.string().min(2),
caption: z.string().optional(),
aspectRatio: z.enum(['16:9', '4:3']).optional(),
title: z.string().min(2).optional(),
caption: z.string().optional(),
aspectRatio: z.enum(['16:9', '4:3']).default('16:9'),

Copilot uses AI. Check for mistakes.
export const draftRequestSchema = z.object({
discovery: discoverySchema,
userMessage: z.string().min(1),
currentDraft: z.unknown().optional(),
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

draftRequestSchema accepts currentDraft as z.unknown(), but generateDraft later casts it to DraftCourse and embeds it into the prompt. If a client sends malformed JSON here, it can degrade model output quality or bloat the prompt. Prefer validating currentDraft with draftCourseSchema (e.g. draftCourseSchema.optional() or safeParse and ignore when invalid).

Suggested change
currentDraft: z.unknown().optional(),
currentDraft: draftCourseSchema.optional(),

Copilot uses AI. Check for mistakes.
Comment on lines +10 to +16
- [`PLATFORM_FEATURES.md`](./PLATFORM_FEATURES.md)
Product-level feature reference (admin workflows, lesson/task model, adaptive learning, AI generation context).

- [`ADAPTIVE_LEARNING.md`](./ADAPTIVE_LEARNING.md)
Deep dive into adaptive recommendations, weak-tag analytics, practice session composition, and spaced repetition behavior.

- [`AI_COURSE_GENERATION.md`](./AI_COURSE_GENERATION.md)
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

This documentation index links to PLATFORM_FEATURES.md and AI_COURSE_GENERATION.md, but those files are not present in LearningPlatform/documentation/ (so these links will 404). Either add the missing documents or remove/adjust the links to match the actual filenames.

Suggested change
- [`PLATFORM_FEATURES.md`](./PLATFORM_FEATURES.md)
Product-level feature reference (admin workflows, lesson/task model, adaptive learning, AI generation context).
- [`ADAPTIVE_LEARNING.md`](./ADAPTIVE_LEARNING.md)
Deep dive into adaptive recommendations, weak-tag analytics, practice session composition, and spaced repetition behavior.
- [`AI_COURSE_GENERATION.md`](./AI_COURSE_GENERATION.md)
- Platform feature overview
Product-level feature reference (admin workflows, lesson/task model, adaptive learning, AI generation context).
- [`ADAPTIVE_LEARNING.md`](./ADAPTIVE_LEARNING.md)
Deep dive into adaptive recommendations, weak-tag analytics, practice session composition, and spaced repetition behavior.
- AI course generation notes

Copilot uses AI. Check for mistakes.
Comment on lines +25 to +27
const runs = new Map<string, GenerationRun>()
const MAX_RUNS = 50

Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

progress-store uses an in-memory Map for run tracking. In production (multiple server instances, serverless, redeploys), runs can disappear mid-generation and /progress/:runId will return 404, making the UI unreliable. Consider persisting runs/timeline to a shared store (DB/Redis) or documenting/guarding this as strictly dev-only behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +215 to +223
const found = await payload.find({
collection: 'subjects',
where: { slug: { equals: draft.subject.slug } },
limit: 1,
})
if (found.docs.length > 0) return String(found.docs[0].id)
const created = await payload.create({
collection: 'subjects',
data: { name: draft.subject.name, slug: draft.subject.slug },
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

ensureSubject queries by draft.subject.slug without normalizing it the same way Subjects collection does (toSlug in a beforeValidate hook). If the draft provides a non-slug value (e.g. "Web Development"), this lookup will miss existing subjects and create duplicates. Normalize the slug before both the find and create calls (e.g. const slug = toSlug(draft.subject.slug || draft.subject.name)).

Suggested change
const found = await payload.find({
collection: 'subjects',
where: { slug: { equals: draft.subject.slug } },
limit: 1,
})
if (found.docs.length > 0) return String(found.docs[0].id)
const created = await payload.create({
collection: 'subjects',
data: { name: draft.subject.name, slug: draft.subject.slug },
const slug = toSlug(draft.subject.slug || draft.subject.name)
const found = await payload.find({
collection: 'subjects',
where: { slug: { equals: slug } },
limit: 1,
})
if (found.docs.length > 0) return String(found.docs[0].id)
const created = await payload.create({
collection: 'subjects',
data: { name: draft.subject.name, slug },

Copilot uses AI. Check for mistakes.
const choices = task.type === 'MULTIPLE_CHOICE' ? task.choices.map((text) => ({ text })) : undefined
const correctAnswer =
task.type === 'OPEN_ENDED'
? ''
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

For OPEN_ENDED tasks, this sets correctAnswer to an empty string. In the rest of the app, correctAnswer being falsy is used to indicate manual review / no sample answer, so storing '' provides no value and can be confusing in admin exports/debugging. Prefer omitting the field entirely (undefined) for open-ended tasks unless you intentionally want to store a sample answer.

Suggested change
? ''
? undefined

Copilot uses AI. Check for mistakes.
Comment on lines +355 to +358
const subjectId = await ensureSubject(payload, parsed.draft)
const course = await createCourse(payload, parsed.draft, subjectId)
if (prepId) updateTimeline(runId, prepId, { status: 'done', detail: `Course ${course.title} created` })
setProgress(runId, 15)
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

After creating the course, later failures (module/lesson/task persistence, flashcards, etc.) will leave a partially-created course and content in Payload. Since Courses has cascade-delete hooks, consider tracking the created course id and deleting it in the outer catch when the pipeline fails after course creation, so admins don't end up with orphaned draft content.

Copilot uses AI. Check for mistakes.
Comment on lines +91 to +93
caption: z.string().min(10),
align: z.enum(['left', 'center', 'right']).optional(),
width: z.enum(['sm', 'md', 'lg']).optional(),
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

theoryBlockSchema's image variant doesn't match the Payload ImageBlock options: the app supports width: 'full' and caption is optional, but the schema only allows sm|md|lg and requires caption (min(10)). This will cause unnecessary validation failures/repairs when models emit valid Payload data. Align the schema with Payload by allowing full and making caption optional (and consider defaulting align/width to Payload defaults).

Suggested change
caption: z.string().min(10),
align: z.enum(['left', 'center', 'right']).optional(),
width: z.enum(['sm', 'md', 'lg']).optional(),
caption: z.string().optional(),
align: z.enum(['left', 'center', 'right']).optional(),
width: z.enum(['sm', 'md', 'lg', 'full']).optional(),

Copilot uses AI. Check for mistakes.
…tion

- Updated the ensureSubject function to use a slug derived from the draft subject, ensuring consistency in subject identification.
- Enhanced the draftRequestSchema to validate currentDraft more robustly, allowing for better error handling.
- Modified runAcceptPipeline to clean up partially created courses in case of errors, improving reliability during course generation.
- Adjusted task schema to handle optional fields more gracefully, ensuring better data integrity.
@Z4phxr Z4phxr merged commit fadbba6 into main Apr 15, 2026
4 checks passed
@Z4phxr Z4phxr deleted the feat/LLM_intergation_for_course_creation branch April 15, 2026 21:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants