--- name: AI Course Assistant overview: An AI-powered assistant embedded in the course UI that helps teachers organize content—update lesson text, tag exercises, generate questions from existing content, and draft lessons. Cloud-only; self-hosted instances cannot use this feature. todos: - id: prd-review content: Finalize PRD with stakeholders, resolve open questions status: pending - id: architecture-decision content: Decide agent execution model (API-first vs local-first) status: pending - id: package-scaffold content: Create packages/ai-assistant package with API client status: pending - id: agent-api-routes content: Design and implement agent API routes with auth + org scoping status: pending - id: chat-ui content: Build chat interface in right sidebar (Sheet) status: pending - id: nav-integration content: Add AI assistant button to course navigation/header status: pending - id: self-host-gate content: Hide assistant and block agent routes when PUBLIC_IS_SELFHOSTED status: pending - id: safeguards content: Enforce org/course permission checks in all agent actions status: pending isProject: false --- # AI Course Assistant ## Purpose Deliver an AI assistant inside the course UI that teachers can use to: - **Update content**: "Update this piece of text in this lesson" - **Tag exercises**: Tag an exercise and provide context - **Generate questions**: "Given this text, come up with 5 new questions" - **Draft lessons**: Ask the agent to write the initial draft of a lesson The assistant operates in a right sidebar chat interface, triggered by an "AI Assistant" button in the course navigation. ## Goals - Help teachers organize and author course content faster - Operate strictly within the current org/course context (no cross-org access) - Integrate natively with existing ClassroomIO APIs (lessons, exercises, content) - Cloud-only; self-hosted users cannot use the AI assistant ## Non-Goals (v1) - Student-facing AI (tutors/teachers only) - Cross-course or cross-org operations - Offline or fully local execution without API - Custom model selection or fine-tuning --- ## Architecture Overview ```mermaid flowchart TB subgraph client [Dashboard Client] NavButton["AI Assistant Button"] ChatUI["Right Sidebar Chat"] end subgraph cloud [Cloud / API] AgentService["Agent Service (packages/ai-assistant or API)"] CourseAPI["Course / Lesson / Exercise APIs"] end subgraph db [Database] LessonTable["lesson / lesson_language"] ExerciseTable["exercise / exercise_template"] ContentTable["course_content"] end NavButton -->|"Open"| ChatUI ChatUI -->|"User message + context"| AgentService AgentService -->|"Validate + Call"| CourseAPI CourseAPI -->|"Persist"| db CourseAPI -->|"Response"| ChatUI ``` --- ## 1. Execution Model: API vs Local **Open question**: How does the agent apply changes—via API directly, or by suggesting edits locally in the UI? ### Option A: API-First (Recommended for v1) - Agent sends **structured intents** (e.g. `{ type: "update_lesson_text", lessonId, locale, payload }`) to a dedicated **agent API** - Agent API validates permissions, calls existing services (`updateLessonService`, `updateLessonLanguageService`, etc.), returns results - Client receives success/failure and refreshes or updates the relevant store - **Pros**: Single source of truth, audit trail, reuses existing safeguards - **Cons**: Slightly more latency; requires agent backend ### Option B: Local-First - Agent returns **edit suggestions** as structured data (e.g. JSON patches) - Client applies changes in the editor UI; user must explicitly accept (e.g. "Apply" button) - Actual persistence happens through existing APIs when user saves - **Pros**: Lower latency, no new backend routes - **Cons**: More complex client logic; risk of sync drift; harder to audit ### Recommendation **Start with Option A (API-First)**: 1. Reuse existing `courseTeamMemberMiddleware`, `orgMemberMiddleware`, and org/course checks 2. Agent executes actions via the same services used by the UI 3. Simpler mental model: agent is a "programmatic teacher" that talks to the same API 4. Easier to add rate limits, logging, and abuse prevention later --- ## 2. UI Specification ### Placement - **Button**: "AI Assistant" in the **course header** (alongside Search, Notifications), visible only to teachers (not students) - **Panel**: Right sidebar implemented with `@cio/ui` Sheet (`side="right"`), ~400px width on desktop - **Mobile**: Full-screen sheet or drawer (reuse `lesson-version-history.svelte` pattern) ### Chat Interface - Message list with user/assistant turns - Text input at bottom with send button - Optional: quick actions / suggested prompts (e.g. "Draft a lesson", "Generate 5 questions") - Loading state while agent processes - Error states with retry ### Context Awareness - Pass `courseId`, `orgId`, `lessonId` (if viewing a lesson), `exerciseId` (if viewing an exercise) to the agent - Agent can reference: "Update the text in the lesson you're viewing" without the user specifying IDs --- ## 3. Safeguards ### Authorization (Reuse Existing Middleware) All agent API routes must use: | Middleware | Purpose | |------------|---------| | `authMiddleware` | Require authenticated user | | `orgMemberMiddleware` | Require `cio-org-id` and org membership | | `courseTeamMemberMiddleware` | Require course team (ADMIN/TUTOR) or org admin | ### Scoping Rules 1. **Org scope**: Every action is scoped to the org in `cio-org-id` header 2. **Course scope**: Lesson/exercise edits must belong to a course where the user is a team member 3. **No cross-org**: Agent must never accept or execute requests for lessons/exercises in another org ### Implementation - Agent API extracts `courseId`, `lessonId`, `exerciseId` from request payload - Before any mutation, verify: - Course belongs to the org - User is `courseTeamMember` or `orgAdmin` - Reuse `isCourseTeamMemberOrOrgAdmin` and `isUserOrgTeamMember` from `@cio/db/queries` --- ## 4. Self-Hosted vs Cloud ### Cloud (ClassroomIO-Hosted) - AI assistant is **enabled** for teachers (within plan limits, if any) - ClassroomIO provides the LLM API keys; no user configuration needed ### Self-Hosted - AI assistant is **not available**. Self-hosters cannot use this feature. - **Implementation**: Hide the "AI Assistant" button when `PUBLIC_IS_SELFHOSTED === 'true'`. Agent API routes return `403` or are not registered when self-hosted. --- ## 5. Package Design: `packages/ai-assistant` The agent should live in a **package** that works natively with ClassroomIO APIs. ### Package Responsibilities - **SDK / client**: Types and helpers for calling the agent API from the dashboard - **Shared types**: Intent schemas, message formats - **No UI**: UI stays in `apps/dashboard`; package is backend + types ### Suggested Structure ``` packages/ai-assistant/ src/ index.ts # Public exports types.ts # Message, intent, response types client.ts # Agent API client (optional; dashboard may use classroomio RPC) package.json tsconfig.json ``` ### Agent API (in `apps/api`) - New routes under e.g. `/course/:courseId/agent` or `/organization/agent` - Routes: `POST /chat` (stream or non-stream), `POST /execute` (for structured intents) - Agent service: interpret user message, map to intents, call lesson/exercise services, return result ### Complexity Estimate | Component | Complexity | Notes | |-----------|------------|-------| | Package scaffold | Low | Thin wrapper around types + optional client | | Agent API routes | Medium | Auth, validation, orchestration | | LLM integration | Medium–High | Depends on provider (OpenAI, Anthropic, etc.) | | Chat UI | Medium | Standard chat + Sheet | | Self-host gate | Low | Env check + conditional render | --- ## 6. Data Sources & APIs (Existing) | Action | API / Service | Auth | |--------|---------------|------| | Update lesson text | `PUT /course/:courseId/lesson/:lessonId/language/:locale` | courseTeamMember | | Update lesson metadata | `PUT /course/:courseId/lesson/:lessonId` | courseTeamMember | | Tag exercise (template tag) | Exercise template has `tag`; create/update exercise | courseTeamMember | | Tag course (org tags) | `PUT /course/:courseId/tags` | courseTeamMember | | Create lesson | `POST /course/:courseId/lesson` | courseTeamMember | | Create exercise | Exercise creation flow | courseTeamMember | | Get lesson content | `GET /course/:courseId/lesson/:lessonId` | courseMember | Agent actions should call these through the existing service layer, not bypass them. --- ## 7. Suggested Implementation Phases ### Phase 1: Foundation 1. Add "AI Assistant" button to course header (hidden when self-hosted) 2. Add right Sheet with placeholder chat UI (no backend) 3. Create `packages/ai-assistant` with types and client stubs 4. Add agent API route skeleton with `courseTeamMemberMiddleware` ### Phase 2: Chat + Execution 1. Implement agent API `POST /chat` with LLM integration 2. Implement intent parsing: update lesson, generate questions, draft lesson 3. Wire agent to `updateLessonService`, `createLesson`, exercise services 4. Connect chat UI to API; display responses and errors ### Phase 3: Polish 1. Add quick-action prompts 2. Add context injection (current lesson/exercise) 3. Rate limiting and usage tracking (if needed) --- ## 8. Open Questions 1. **Streaming**: Should responses stream (SSE/WebSocket) or be non-streaming for v1? 2. **LLM provider**: OpenAI, Anthropic, or configurable per deployment? 3. **Rate limits**: Per-user, per-org, or per-course? Needed for cloud cost control? 4. **Exercise tagging**: Does "tag an exercise" mean org-level course tags, or exercise-template tags (e.g. mcq, coding)? Clarify semantics. 5. **Draft persistence**: When agent drafts a lesson, does it create a draft immediately or return text for user to paste? Recommendation: create draft via API, user reviews before publishing. --- ## 9. Success Criteria - Teachers can open the AI assistant from the course header and chat in a right sidebar - Agent can update lesson text, create draft lessons, and generate questions (as defined in Phase 2) - No user can modify another org's data; all actions pass permission checks - Self-hosted instances cannot use the assistant; UI and API are gated - Agent package integrates cleanly with existing API and dashboard structure