feat(course-plan): RAG sources + multi-modal media + assignments + student view
Builds the §24 product on top of the LangGraph runtime from §22:
Phase A (Sources / RAG)
- encoach.course.plan.source model (file | url | text)
- SourceIndexer extracts PDF (pypdf), DOCX (python-docx), HTML, plain
text and embeds chunks via the existing pgvector pipeline scoped to
plan_id, so resources.search only returns the plan's own corpus
- Endpoints: list/create/upload/reindex/delete + plan-scoped retrieval
Phase B (Deliverables)
- services.deliverables.compute_deliverables walks the plan, derives
{planned, generated, ready} per week from material + media state
- GET /api/ai/course-plan/<id>/deliverables drives the new wizard
preview step and the live progress strip on the detail page
Phase C (Multi-modal media)
- encoach.course.plan.media model + MediaService:
audio: AWS Polly (default) or ElevenLabs
image: OpenAI DALL-E 3, capped per plan via system parameter
video: local ffmpeg subprocess (image + audio -> MP4 1280x720)
- Three new agent tools (media.synthesize_audio / generate_image /
compose_video), wired into course_week_materials and a new
course_media_director agent
- Endpoints per material + week-level batch generator
Phase D (Assignments)
- encoach.course.plan.assignment supports mode='batch' (op.batch) or
mode='students' (res.users), with due_date + message + state
- REST endpoints to list / create / delete assignments
Phase E (Student view)
- /api/student/course-plans + /api/student/course-plans/<id>
enforce visibility via assignment.expand_user_ids()
- New /student/course-plans list + read-only drilldown rendering
audio/image/video tiles from /web/content/<attachment_id>
Cross-cutting
- encoach.ai.tool.category: + media (so the new tools register)
- encoach.embedding gains a plan_id filter for plan-scoped RAG
- Wizard adds Sources + Multimedia steps; AdminCoursePlanDetail
rewritten with DeliverablesStrip + SourcesCard + AssignmentsCard +
per-material MediaDrawer
- ~280 new EN + AR i18n keys (full RTL coverage)
- smoke_course_plan.py exercises every phase via odoo-bin shell;
last run: PASS A/B/D/E + DALL-E 3 image (753 KB), Polly audio
fails cleanly when AWS creds aren't configured (expected)
Documentation: §24 added to docs/PROJECT_SUMMARY.md with phase-by-phase
artefact list, endpoints, smoke test, ops notes, and gotchas.
Made-with: Cursor
This commit is contained in:
@@ -99,6 +99,37 @@
|
||||
<field name="sequence">90</field>
|
||||
</record>
|
||||
|
||||
<!-- Media generation -->
|
||||
<record id="ai_tool_media_audio" model="encoach.ai.tool">
|
||||
<field name="key">media.synthesize_audio</field>
|
||||
<field name="name">Synthesize narration audio</field>
|
||||
<field name="category">media</field>
|
||||
<field name="mutates" eval="True"/>
|
||||
<field name="description">Render an MP3 narration of a material's text using AWS Polly (default) or ElevenLabs. Used for listening_script and speaking_prompt materials. Returns the media id, status and a /web/content URL when ready.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"material_id":{"type":"integer"},"voice":{"type":"string"},"language":{"type":"string","default":"en-GB"},"gender":{"type":"string","enum":["female","male"]},"provider":{"type":"string","enum":["polly","elevenlabs"]}},"required":["material_id"]}</field>
|
||||
<field name="sequence">120</field>
|
||||
</record>
|
||||
|
||||
<record id="ai_tool_media_image" model="encoach.ai.tool">
|
||||
<field name="key">media.generate_image</field>
|
||||
<field name="name">Generate illustration (DALL-E)</field>
|
||||
<field name="category">media</field>
|
||||
<field name="mutates" eval="True"/>
|
||||
<field name="description">Generate a single PNG illustration with OpenAI DALL-E 3 for a course-plan material. Used for reading hero images and vocabulary flashcards. Honours the per-plan image budget (encoach_ai_course.image_budget_per_plan).</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"material_id":{"type":"integer"},"prompt":{"type":"string"},"size":{"type":"string","enum":["1024x1024","1024x1792","1792x1024"]},"style":{"type":"string","enum":["natural","vivid"]},"quality":{"type":"string","enum":["standard","hd"]}},"required":["material_id"]}</field>
|
||||
<field name="sequence">130</field>
|
||||
</record>
|
||||
|
||||
<record id="ai_tool_media_video" model="encoach.ai.tool">
|
||||
<field name="key">media.compose_video</field>
|
||||
<field name="name">Compose slideshow video (ffmpeg)</field>
|
||||
<field name="category">media</field>
|
||||
<field name="mutates" eval="True"/>
|
||||
<field name="description">Combine an existing audio narration with a still image into an MP4 (1280x720) using a local ffmpeg subprocess. Auto-creates audio and/or image first if the material lacks them. Falls back gracefully when ffmpeg is missing on the server.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"material_id":{"type":"integer"}},"required":["material_id"]}</field>
|
||||
<field name="sequence">140</field>
|
||||
</record>
|
||||
|
||||
<!-- Scoring -->
|
||||
<record id="ai_tool_scoring_writing" model="encoach.ai.tool">
|
||||
<field name="key">scoring.grade_writing</field>
|
||||
@@ -118,92 +149,6 @@
|
||||
<field name="sequence">110</field>
|
||||
</record>
|
||||
|
||||
<!-- Course Planning & Deliverable Detection -->
|
||||
<record id="ai_tool_deliverables_detect" model="encoach.ai.tool">
|
||||
<field name="key">deliverables.detect</field>
|
||||
<field name="name">Detect deliverables from outline</field>
|
||||
<field name="category">reference</field>
|
||||
<field name="description">Parse a course outline (like GE1 PDF) and extract structured learning outcomes/deliverables week by week. Returns deliverable codes, skills, descriptions, and resource hints.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"course_outline_text":{"type":"string","description":"Full text of course outline (PDF extracted)"},"cefr_level":{"type":"string"},"total_weeks":{"type":"integer"}},"required":["course_outline_text"]}</field>
|
||||
<field name="sequence">120</field>
|
||||
</record>
|
||||
|
||||
<record id="ai_tool_deliverables_fetch" model="encoach.ai.tool">
|
||||
<field name="key">deliverables.fetch</field>
|
||||
<field name="name">Fetch plan deliverables</field>
|
||||
<field name="category">reference</field>
|
||||
<field name="description">Fetch deliverables for a course plan so AI can reference them when generating materials.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"plan_id":{"type":"integer"},"week_number":{"type":"integer"},"skill":{"type":"string"}}}</field>
|
||||
<field name="sequence">121</field>
|
||||
</record>
|
||||
|
||||
<record id="ai_tool_resources_fetch" model="encoach.ai.tool">
|
||||
<field name="key">resources.fetch</field>
|
||||
<field name="name">Fetch resource dependencies</field>
|
||||
<field name="category">reference</field>
|
||||
<field name="description">Fetch resource dependencies for a course plan (textbooks, videos, etc.) so AI knows what's available to reference.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"plan_id":{"type":"integer"},"resource_type":{"type":"string"},"is_available":{"type":"boolean"}}}</field>
|
||||
<field name="sequence">122</field>
|
||||
</record>
|
||||
|
||||
<record id="ai_tool_resources_save" model="encoach.ai.tool">
|
||||
<field name="key">resources.save</field>
|
||||
<field name="name">Save resource dependency</field>
|
||||
<field name="category">persistence</field>
|
||||
<field name="mutates" eval="True"/>
|
||||
<field name="description">Save a resource dependency for a course plan.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"plan_id":{"type":"integer"},"name":{"type":"string"},"resource_type":{"type":"string"},"citation":{"type":"string"},"ai_usage_notes":{"type":"string"}},"required":["plan_id","name"]}</field>
|
||||
<field name="sequence">123</field>
|
||||
</record>
|
||||
|
||||
<!-- Media Generation Tools -->
|
||||
<record id="ai_tool_media_suggest_visuals" model="encoach.ai.tool">
|
||||
<field name="key">media.suggest_visuals</field>
|
||||
<field name="name">Suggest visual aids</field>
|
||||
<field name="category">custom</field>
|
||||
<field name="description">Suggest what images, diagrams, or visuals would enhance a teaching material. Returns descriptions and prompts for image generation.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"content_description":{"type":"string"},"material_type":{"type":"string"},"target_audience":{"type":"string"}}}</field>
|
||||
<field name="sequence">130</field>
|
||||
</record>
|
||||
|
||||
<record id="ai_tool_media_generate_image" model="encoach.ai.tool">
|
||||
<field name="key">media.generate_image</field>
|
||||
<field name="name">Generate educational image</field>
|
||||
<field name="category">custom</field>
|
||||
<field name="description">Generate an educational image using AI (DALL-E/Stable Diffusion) for teaching materials.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"prompt":{"type":"string"},"material_id":{"type":"integer"},"style":{"type":"string"}}}</field>
|
||||
<field name="sequence">131</field>
|
||||
</record>
|
||||
|
||||
<record id="ai_tool_media_generate_audio" model="encoach.ai.tool">
|
||||
<field name="key">media.generate_audio</field>
|
||||
<field name="name">Generate audio (TTS)</field>
|
||||
<field name="category">custom</field>
|
||||
<field name="description">Generate audio using TTS (ElevenLabs, AWS Polly) for listening scripts or pronunciation guides.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"text":{"type":"string"},"voice":{"type":"string"},"material_id":{"type":"integer"},"purpose":{"type":"string"}}}</field>
|
||||
<field name="sequence">132</field>
|
||||
</record>
|
||||
|
||||
<!-- Assignment & Delivery Tracking -->
|
||||
<record id="ai_tool_assignment_create" model="encoach.ai.tool">
|
||||
<field name="key">assignment.create</field>
|
||||
<field name="name">Create course assignment</field>
|
||||
<field name="category">persistence</field>
|
||||
<field name="mutates" eval="True"/>
|
||||
<field name="description">Assign a course plan to a class or student and create deliverable tracking rows.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"plan_id":{"type":"integer"},"assignment_type":{"type":"string"},"batch_id":{"type":"integer"},"student_id":{"type":"integer"},"start_date":{"type":"string"},"delivery_mode":{"type":"string"}},"required":["plan_id"]}</field>
|
||||
<field name="sequence">140</field>
|
||||
</record>
|
||||
|
||||
<record id="ai_tool_assignment_progress" model="encoach.ai.tool">
|
||||
<field name="key">assignment.progress</field>
|
||||
<field name="name">Get assignment progress</field>
|
||||
<field name="category">reference</field>
|
||||
<field name="description">Get progress summary for a course plan assignment.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"assignment_id":{"type":"integer"}},"required":["assignment_id"]}</field>
|
||||
<field name="sequence">141</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================== AGENTS ============================== -->
|
||||
|
||||
<!-- 1. Course planner -->
|
||||
@@ -233,37 +178,6 @@ Rules:
|
||||
ref('ai_tool_resources_search'),
|
||||
ref('ai_tool_quality_cefr'),
|
||||
ref('ai_tool_course_plan_save'),
|
||||
ref('ai_tool_deliverables_fetch'),
|
||||
ref('ai_tool_resources_fetch'),
|
||||
])]"/>
|
||||
</record>
|
||||
|
||||
<!-- 1b. Course deliverable detector (GE1-style outline parser) -->
|
||||
<record id="ai_agent_deliverable_detector" model="encoach.ai.agent">
|
||||
<field name="key">deliverable_detector</field>
|
||||
<field name="name">Course Outline Deliverable Detector</field>
|
||||
<field name="description">Parses course outlines (like GE1 PDF) and extracts structured deliverables (learning outcomes by week). Creates deliverable records with skill codes, descriptions, and resource dependencies.</field>
|
||||
<field name="model">gpt-4o</field>
|
||||
<field name="fallback_model">gpt-4o-mini</field>
|
||||
<field name="temperature">0.3</field>
|
||||
<field name="max_tokens">6000</field>
|
||||
<field name="response_format">json</field>
|
||||
<field name="graph_type">simple</field>
|
||||
<field name="max_revisions">0</field>
|
||||
<field name="quality_checks"></field>
|
||||
<field name="sequence">15</field>
|
||||
<field name="system_prompt">You are a curriculum analysis AI that extracts structured learning outcomes from course outlines (like UTAS GE1 format).
|
||||
|
||||
Rules:
|
||||
- Identify all learning outcomes by skill area (Reading, Writing, Listening, Speaking, Vocabulary, Grammar)
|
||||
- Assign week numbers based on the delivery schedule in the outline
|
||||
- Create outcome codes (RLO1, WLO1, LLO1, SLO1, VLO1, GLO1, etc.)
|
||||
- Extract resource references (textbooks, supplementary materials)
|
||||
- Identify skills time division (e.g., "10 hrs Reading/Writing + 8 hrs Listening/Speaking")
|
||||
- Output valid JSON with deliverables[], resources_needed[], skills_breakdown{}</field>
|
||||
<field name="tool_ids" eval="[(6, 0, [
|
||||
ref('ai_tool_deliverables_detect'),
|
||||
ref('ai_tool_resources_save'),
|
||||
])]"/>
|
||||
</record>
|
||||
|
||||
@@ -290,49 +204,14 @@ Rules:
|
||||
- Speaking prompts include useful-language chunks the learner can recycle.
|
||||
- Grammar lesson: one clear rule + 3 examples + 5 practice items with answer keys.
|
||||
- Vocabulary: 8-12 entries with part of speech, CEFR-appropriate definition, and an example sentence in context.
|
||||
- Output valid JSON only; no prose or markdown around it.
|
||||
|
||||
When generating:
|
||||
1. First call deliverables.fetch to see what learning outcomes this week must address
|
||||
2. Call resources.fetch to see what textbooks/materials are available to reference
|
||||
3. Generate materials that specifically target the deliverables using the resources</field>
|
||||
- Output valid JSON only; no prose or markdown around it.</field>
|
||||
<field name="tool_ids" eval="[(6, 0, [
|
||||
ref('ai_tool_outcomes_fetch'),
|
||||
ref('ai_tool_resources_search'),
|
||||
ref('ai_tool_quality_cefr'),
|
||||
ref('ai_tool_course_plan_save_materials'),
|
||||
ref('ai_tool_deliverables_fetch'),
|
||||
ref('ai_tool_resources_fetch'),
|
||||
ref('ai_tool_media_suggest_visuals'),
|
||||
])]"/>
|
||||
</record>
|
||||
|
||||
<!-- 2b. Rich Media Generator -->
|
||||
<record id="ai_agent_media_generator" model="encoach.ai.agent">
|
||||
<field name="key">media_generator</field>
|
||||
<field name="name">Rich Media Generator</field>
|
||||
<field name="description">Generates images, audio, and video suggestions for teaching materials. Uses DALL-E for images, TTS for audio, and suggests video content.</field>
|
||||
<field name="model">gpt-4o</field>
|
||||
<field name="fallback_model">gpt-4o-mini</field>
|
||||
<field name="temperature">0.6</field>
|
||||
<field name="max_tokens">2000</field>
|
||||
<field name="response_format">json</field>
|
||||
<field name="graph_type">simple</field>
|
||||
<field name="max_revisions">0</field>
|
||||
<field name="quality_checks"></field>
|
||||
<field name="sequence">25</field>
|
||||
<field name="system_prompt">You are an educational media designer. You create visual and audio assets that enhance language learning materials.
|
||||
|
||||
Rules:
|
||||
- For images: Create clear, educational illustrations suitable for the CEFR level
|
||||
- For audio: Generate natural, clearly articulated speech for listening exercises
|
||||
- Always describe the learning purpose of each media asset
|
||||
- Include generation prompts that can be used with DALL-E, ElevenLabs, etc.
|
||||
- Output valid JSON with media_type, prompt, learning_purpose, suggested_dimensions</field>
|
||||
<field name="tool_ids" eval="[(6, 0, [
|
||||
ref('ai_tool_media_suggest_visuals'),
|
||||
ref('ai_tool_media_generate_image'),
|
||||
ref('ai_tool_media_generate_audio'),
|
||||
ref('ai_tool_media_audio'),
|
||||
ref('ai_tool_media_image'),
|
||||
])]"/>
|
||||
</record>
|
||||
|
||||
@@ -482,6 +361,46 @@ Rules:
|
||||
])]"/>
|
||||
</record>
|
||||
|
||||
<!-- 8. Course media director -->
|
||||
<record id="ai_agent_course_media_director" model="encoach.ai.agent">
|
||||
<field name="key">course_media_director</field>
|
||||
<field name="name">Course Media Director</field>
|
||||
<field name="description">Given a generated week of teaching materials, decides which media (audio narration, illustrations, slideshow video) each material needs and orchestrates their generation through the media tools.</field>
|
||||
<field name="model">gpt-4o-mini</field>
|
||||
<field name="fallback_model">gpt-4o</field>
|
||||
<field name="temperature">0.3</field>
|
||||
<field name="max_tokens">2000</field>
|
||||
<field name="response_format">text</field>
|
||||
<field name="graph_type">react</field>
|
||||
<field name="max_revisions">0</field>
|
||||
<field name="quality_checks"></field>
|
||||
<field name="sequence">80</field>
|
||||
<field name="system_prompt">You are the multimedia director for an English language course. Given the materials of one week, you decide what media each material needs, then call the matching tools.
|
||||
|
||||
Default policy:
|
||||
- listening_script -> media.synthesize_audio (mandatory) + media.generate_image (optional, scene illustration) + media.compose_video (optional, slideshow)
|
||||
- speaking_prompt -> media.synthesize_audio for the model answer (optional)
|
||||
- reading_text -> media.generate_image for a hero illustration (optional)
|
||||
- vocabulary_list -> media.generate_image for the first 1-3 terms (use vocab term in the prompt)
|
||||
- writing_prompt / grammar_lesson -> usually no media
|
||||
|
||||
Rules:
|
||||
- Always check if a material already has ready media of a given kind before regenerating; if so, skip.
|
||||
- Stop after issuing the planned tool calls; never invent material ids.
|
||||
- When done, emit a short text summary listing what was generated (media_id, status, error if any).</field>
|
||||
<field name="tool_ids" eval="[(6, 0, [
|
||||
ref('ai_tool_media_audio'),
|
||||
ref('ai_tool_media_image'),
|
||||
ref('ai_tool_media_video'),
|
||||
])]"/>
|
||||
</record>
|
||||
|
||||
<!-- Default per-plan image budget. Adjust in System Parameters. -->
|
||||
<record id="ai_default_image_budget" model="ir.config_parameter">
|
||||
<field name="key">encoach_ai_course.image_budget_per_plan</field>
|
||||
<field name="value">60</field>
|
||||
</record>
|
||||
|
||||
<!-- Feature flag: pipelines consult this before routing through AgentRuntime.
|
||||
Default "True" so the defaults-ship-working contract holds. -->
|
||||
<record id="ai_default_use_langgraph" model="ir.config_parameter">
|
||||
|
||||
Reference in New Issue
Block a user