feat(ai): LangGraph as core runtime + AI Agents/Tools console + full-demo seed
Core AI runtime - New encoach.ai.agent + encoach.ai.tool models with M2M tool binding, graph topology (simple|plan_review_revise|rag|react), model + fallback, temperature, max_tokens, response_format, max_revisions, quality checks and system prompt fields. - services/agent_runtime.py compiles a langgraph.StateGraph per agent and caches the build per (key, write_date). Emits a structured trace (output, tool_calls, retrieval_hits, revisions, quality_issues, ms, model_used, fallback_used) and auto-falls-back on rate-limit/5xx. - services/agent_tools.py registers 11 tool handlers wrapping existing services: resources.search, rubric.fetch, outcomes.fetch, student.profile, quality.cefr_check, quality.ai_detect, quality.content_gate, course_plan.save (mutates), course_plan.save_materials (mutates), scoring.grade_writing, scoring.grade_speaking. - 7 default agents seeded via data/agents_defaults.xml: course_planner, course_week_materials, exam_generator, exercise_generator, lms_tutor, writing_grader, speaking_grader. - Feature flag encoach_ai.use_langgraph_runtime (default True). - encoach_ai_course pipeline now routes through AgentRuntime when on, legacy SDK path kept as fallback. Admin UI - /admin/ai/prompts is now a tabbed Agents | Tools | Prompts console. - AIAgentsPanel: card grid + config dialog (model/temp/graph/tools/ system prompt) + built-in Test Runner showing live trace. - AIToolsPanel: registry table with category badges, mutates flag, schema viewer, edit dialog. - New /api/ai/agents* and /api/ai/tools* controller (list/get/update/ test, list-tools, toggle-tool). - Sidebar label nav.aiPrompts -> nav.aiAgents (AI Agents and Tools). - EN + AR (RTL) translations for ~80 new keys. Smart Wizard pages - /admin/quick-setup hub + CourseWizard, CoursePlanWizard, RubricWizard, ExamStructureWizard step-by-step flows. - /admin/course-plans list + detail pages. - /teacher/quick-setup mirror. Full demo seed + 8-role E2E - seed_full_demo.py adds the 5 missing user_types (approver, corporate, mastercorporate, agent, developer), activates a 2-stage exam-approval workflow with one pending request, creates a GE1-aligned 12-week B1 course plan with 6 detailed Week-1 materials (reading 400w, writing, listening 4-min script, speaking, grammar present simple vs continuous, vocabulary), and inserts sample ai.log + ai.feedback rows. - reset_demo_passwords.py forces every demo login back to canonical passwords (admin123/teacher123/student123/approver123/corporate123/ master123/agent123/dev123). - e2e_full_scenario.py: 46/46 PASS read-only API smoke across all 8 roles, including a live LangGraph round-trip on writing_grader. - e2e_approval_chain.py: 6/6 PASS mutation E2E - approver approves stage 1, admin approves stage 2, linked encoach.exam.custom flips to status=published, verified via psql. Docs - docs/PROJECT_SUMMARY.md updated to 2026-04-25: new Latest events bullets, refreshed credentials table, full sections 22 (LangGraph runtime) and 23 (full demo seed + 8-role E2E). - docs/ENCOACH_FULL_DEMO_QA_REPORT.md added with credentials, per-endpoint PASS/FAIL, mutation chain proof, LangGraph live output. - backend/GE1 Course Outline_ Fall AY25-26.pdf vendored as the reference outline the GE1 plan/materials are aligned to. Dependencies - requirements.txt: langgraph>=0.2.0, langchain-core>=0.3.0. - encoach_ai/__manifest__.py: external_dependencies updated. Made-with: Cursor
This commit is contained in:
337
backend/custom_addons/encoach_ai/data/agents_defaults.xml
Normal file
337
backend/custom_addons/encoach_ai/data/agents_defaults.xml
Normal file
@@ -0,0 +1,337 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo noupdate="1">
|
||||
<!--
|
||||
Default AI agents + tools seeded on first install.
|
||||
|
||||
These are the *sensible defaults* the user asked for: every platform
|
||||
pillar (course planning, weekly materials, exam generation, exercise
|
||||
generation, LMS tutor, grading) gets a pre-configured LangGraph
|
||||
agent so the system works out of the box. Admins edit the system
|
||||
prompts, models, temperatures and tool bindings from
|
||||
/admin/ai/prompts → Agents tab.
|
||||
-->
|
||||
|
||||
<!-- ============================== TOOLS ============================== -->
|
||||
|
||||
<!-- Retrieval -->
|
||||
<record id="ai_tool_resources_search" model="encoach.ai.tool">
|
||||
<field name="key">resources.search</field>
|
||||
<field name="name">Search resources</field>
|
||||
<field name="category">retrieval</field>
|
||||
<field name="description">Semantic search over the LMS resource library. Returns resource ids, titles and snippets. Use this BEFORE generating content so the agent reuses existing, approved materials instead of hallucinating.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"query":{"type":"string","description":"Natural language search query"},"skill":{"type":"string","enum":["reading","writing","listening","speaking","grammar","vocabulary"]},"cefr_level":{"type":"string","enum":["pre_a1","a1","a2","b1","b2","c1","c2"]},"limit":{"type":"integer","default":5,"minimum":1,"maximum":20}},"required":["query"]}</field>
|
||||
<field name="sequence">10</field>
|
||||
</record>
|
||||
|
||||
<record id="ai_tool_rubric_fetch" model="encoach.ai.tool">
|
||||
<field name="key">rubric.fetch</field>
|
||||
<field name="name">Fetch rubric</field>
|
||||
<field name="category">reference</field>
|
||||
<field name="description">Return the grading rubric and criterion descriptors for a given rubric id or skill. Always call before grading so the LLM uses the approved rubric, not its own defaults.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"rubric_id":{"type":"integer"},"skill":{"type":"string","enum":["reading","writing","listening","speaking"]}}}</field>
|
||||
<field name="sequence">20</field>
|
||||
</record>
|
||||
|
||||
<record id="ai_tool_outcomes_fetch" model="encoach.ai.tool">
|
||||
<field name="key">outcomes.fetch</field>
|
||||
<field name="name">Fetch course outcomes</field>
|
||||
<field name="category">reference</field>
|
||||
<field name="description">Return the registered learning outcomes for a course or CEFR level. Use it when generating course plans to stay aligned with the programme specification.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"course_id":{"type":"integer"},"cefr_level":{"type":"string","enum":["pre_a1","a1","a2","b1","b2","c1","c2"]}}}</field>
|
||||
<field name="sequence">30</field>
|
||||
</record>
|
||||
|
||||
<record id="ai_tool_student_profile" model="encoach.ai.tool">
|
||||
<field name="key">student.profile</field>
|
||||
<field name="name">Get student gap profile</field>
|
||||
<field name="category">reference</field>
|
||||
<field name="description">Return the student's CEFR band, strengths and gaps so content can be personalised. Required input for personalised exercise generation and tutor follow-ups.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"student_id":{"type":"integer"}},"required":["student_id"]}</field>
|
||||
<field name="sequence">40</field>
|
||||
</record>
|
||||
|
||||
<!-- Quality gates -->
|
||||
<record id="ai_tool_quality_cefr" model="encoach.ai.tool">
|
||||
<field name="key">quality.cefr_check</field>
|
||||
<field name="name">CEFR readability check</field>
|
||||
<field name="category">quality</field>
|
||||
<field name="description">Check whether a text reads at the target CEFR level using Flesch-Kincaid. Returns ok=false with specific issues when the passage is too easy or too hard for the requested band.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"text":{"type":"string"},"target_cefr":{"type":"string","enum":["a1","a2","b1","b2","c1","c2"]}},"required":["text","target_cefr"]}</field>
|
||||
<field name="sequence">50</field>
|
||||
</record>
|
||||
|
||||
<record id="ai_tool_quality_ai" model="encoach.ai.tool">
|
||||
<field name="key">quality.ai_detect</field>
|
||||
<field name="name">AI-content detection</field>
|
||||
<field name="category">quality</field>
|
||||
<field name="description">Probability the text was written by an AI (via GPTZero). Used during submission review — not usually during generation.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"text":{"type":"string"}},"required":["text"]}</field>
|
||||
<field name="sequence">60</field>
|
||||
</record>
|
||||
|
||||
<record id="ai_tool_quality_gate" model="encoach.ai.tool">
|
||||
<field name="key">quality.content_gate</field>
|
||||
<field name="name">Unified content gate</field>
|
||||
<field name="category">quality</field>
|
||||
<field name="description">Run the project's combined content-source gate (CEFR + toxicity + length checks). Returns ok=false with the first failing rule.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"text":{"type":"string"},"cefr_level":{"type":"string"}},"required":["text"]}</field>
|
||||
<field name="sequence">70</field>
|
||||
</record>
|
||||
|
||||
<!-- Persistence -->
|
||||
<record id="ai_tool_course_plan_save" model="encoach.ai.tool">
|
||||
<field name="key">course_plan.save</field>
|
||||
<field name="name">Save course plan</field>
|
||||
<field name="category">persistence</field>
|
||||
<field name="mutates" eval="True"/>
|
||||
<field name="description">Persist an AI-generated course plan header and its weekly rows. Only call once you're confident the JSON is valid and has been reviewed.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"plan_vals":{"type":"object"},"weeks":{"type":"array","items":{"type":"object"}}},"required":["plan_vals"]}</field>
|
||||
<field name="sequence">80</field>
|
||||
</record>
|
||||
|
||||
<record id="ai_tool_course_plan_save_materials" model="encoach.ai.tool">
|
||||
<field name="key">course_plan.save_materials</field>
|
||||
<field name="name">Save weekly teaching materials</field>
|
||||
<field name="category">persistence</field>
|
||||
<field name="mutates" eval="True"/>
|
||||
<field name="description">Persist the generated per-week teaching materials against an existing course plan and week.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"plan_id":{"type":"integer"},"week_id":{"type":"integer"},"materials":{"type":"array","items":{"type":"object"}}},"required":["plan_id","week_id","materials"]}</field>
|
||||
<field name="sequence">90</field>
|
||||
</record>
|
||||
|
||||
<!-- Scoring -->
|
||||
<record id="ai_tool_scoring_writing" model="encoach.ai.tool">
|
||||
<field name="key">scoring.grade_writing</field>
|
||||
<field name="name">Grade writing response</field>
|
||||
<field name="category">scoring</field>
|
||||
<field name="description">Grade a writing response against a rubric using the platform's standard writing examiner prompt.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"rubric":{"type":"string"},"task":{"type":"string"},"response":{"type":"string"}},"required":["rubric","response"]}</field>
|
||||
<field name="sequence">100</field>
|
||||
</record>
|
||||
|
||||
<record id="ai_tool_scoring_speaking" model="encoach.ai.tool">
|
||||
<field name="key">scoring.grade_speaking</field>
|
||||
<field name="name">Grade speaking transcript</field>
|
||||
<field name="category">scoring</field>
|
||||
<field name="description">Grade a speaking transcript against a rubric using the platform's standard speaking examiner prompt.</field>
|
||||
<field name="schema_json">{"type":"object","properties":{"rubric":{"type":"string"},"transcript":{"type":"string"}},"required":["rubric","transcript"]}</field>
|
||||
<field name="sequence">110</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================== AGENTS ============================== -->
|
||||
|
||||
<!-- 1. Course planner -->
|
||||
<record id="ai_agent_course_planner" model="encoach.ai.agent">
|
||||
<field name="key">course_planner</field>
|
||||
<field name="name">Course Planner</field>
|
||||
<field name="description">Generates a full course plan (description, objectives, per-skill outcomes, grammar scope, assessment weights, week-by-week delivery) from a short brief. Used by the Smart Wizard and /api/ai/course-plan.</field>
|
||||
<field name="model">gpt-4o</field>
|
||||
<field name="fallback_model">gpt-4o-mini</field>
|
||||
<field name="temperature">0.4</field>
|
||||
<field name="max_tokens">4096</field>
|
||||
<field name="response_format">json</field>
|
||||
<field name="graph_type">plan_review_revise</field>
|
||||
<field name="max_revisions">1</field>
|
||||
<field name="quality_checks">quality.cefr_check</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="system_prompt">You are an expert English language curriculum designer. You produce structured, institution-grade course outlines suitable for a general foundation English programme.
|
||||
|
||||
Rules:
|
||||
- Output MUST be a single valid JSON object matching the schema the user supplies.
|
||||
- Use CEFR can-do statements when writing outcomes; cite the CEFR level in objectives.
|
||||
- Distribute the weeks so grammar and skills build cumulatively, not randomly.
|
||||
- Keep outcome codes short and stable (RLO1, WLO1, LLO1, SLO1, GLO1, VLO1) and reuse them in weeks[*].items[*].outcome_codes.
|
||||
- Never wrap the JSON in prose, markdown, or code fences.</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'),
|
||||
])]"/>
|
||||
</record>
|
||||
|
||||
<!-- 2. Week materials -->
|
||||
<record id="ai_agent_course_week_materials" model="encoach.ai.agent">
|
||||
<field name="key">course_week_materials</field>
|
||||
<field name="name">Week Teaching Materials</field>
|
||||
<field name="description">Given a course plan and a week number, produces classroom-ready materials (reading passage, listening script, speaking prompt, writing prompt, grammar mini-lesson, vocabulary list) aligned to the registered outcomes.</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">6000</field>
|
||||
<field name="response_format">json</field>
|
||||
<field name="graph_type">plan_review_revise</field>
|
||||
<field name="max_revisions">1</field>
|
||||
<field name="quality_checks">quality.cefr_check</field>
|
||||
<field name="sequence">20</field>
|
||||
<field name="system_prompt">You are an expert EFL teacher creating ready-to-use classroom materials.
|
||||
|
||||
Rules:
|
||||
- Every material MUST target only the outcome codes supplied for that week.
|
||||
- Keep reading passages within the CEFR band's word-count window (A1~80, A2~150, B1~250, B2~400, C1~600, C2~800 words).
|
||||
- Listening scripts must be natural dialogue/monologue, 3-4 minutes, with 4-6 comprehension questions.
|
||||
- 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.</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'),
|
||||
])]"/>
|
||||
</record>
|
||||
|
||||
<!-- 3. Exam generator -->
|
||||
<record id="ai_agent_exam_generator" model="encoach.ai.agent">
|
||||
<field name="key">exam_generator</field>
|
||||
<field name="name">Exam Generator</field>
|
||||
<field name="description">Generates exam questions (MCQ, short answer, cloze, speaking prompts, writing tasks) matching a structure and blueprint.</field>
|
||||
<field name="model">gpt-4o</field>
|
||||
<field name="fallback_model">gpt-4o-mini</field>
|
||||
<field name="temperature">0.5</field>
|
||||
<field name="max_tokens">6000</field>
|
||||
<field name="response_format">json</field>
|
||||
<field name="graph_type">plan_review_revise</field>
|
||||
<field name="max_revisions">1</field>
|
||||
<field name="quality_checks">quality.cefr_check</field>
|
||||
<field name="sequence">30</field>
|
||||
<field name="system_prompt">You are a senior EFL / IELTS examiner generating authentic, validly constructed exam questions.
|
||||
|
||||
Rules:
|
||||
- Follow the exam structure blueprint exactly: same number of sections, same question types, same scoring weights.
|
||||
- Every MCQ has exactly one correct answer and three plausible distractors; avoid "all of the above".
|
||||
- Reading/listening stems reference only content present in the accompanying passage/transcript.
|
||||
- Never produce content outside the requested CEFR band.
|
||||
- Output is a single JSON object; no explanations around it.</field>
|
||||
<field name="tool_ids" eval="[(6, 0, [
|
||||
ref('ai_tool_resources_search'),
|
||||
ref('ai_tool_outcomes_fetch'),
|
||||
ref('ai_tool_rubric_fetch'),
|
||||
ref('ai_tool_quality_cefr'),
|
||||
])]"/>
|
||||
</record>
|
||||
|
||||
<!-- 4. Personalised exercise generator -->
|
||||
<record id="ai_agent_exercise_generator" model="encoach.ai.agent">
|
||||
<field name="key">exercise_generator</field>
|
||||
<field name="name">Personalised Exercise Generator</field>
|
||||
<field name="description">Produces targeted practice items (gap-fill, reordering, short response) using a learner's gap profile to focus on their weakest outcomes.</field>
|
||||
<field name="model">gpt-4o-mini</field>
|
||||
<field name="fallback_model">gpt-4o</field>
|
||||
<field name="temperature">0.7</field>
|
||||
<field name="max_tokens">3000</field>
|
||||
<field name="response_format">json</field>
|
||||
<field name="graph_type">react</field>
|
||||
<field name="max_revisions">0</field>
|
||||
<field name="quality_checks"></field>
|
||||
<field name="sequence">40</field>
|
||||
<field name="system_prompt">You are a remedial English tutor generating short, focused practice items for one learner.
|
||||
|
||||
Workflow:
|
||||
1. Call `student.profile` with the student_id you are given. Read their CEFR band and gap_json.
|
||||
2. Optionally call `resources.search` to find an anchor text at the right level.
|
||||
3. Then produce 6-10 practice items that target the biggest gaps. Prefer item types the learner has been scoring low on.
|
||||
4. Each item has: prompt, correct_answer, distractors (for MCQ), brief rationale, target_outcome_code.
|
||||
5. Output a JSON object {"items": [...]}.
|
||||
|
||||
Never fabricate gap data — if student.profile fails, ask for a student_id in your final message and emit an empty items list.</field>
|
||||
<field name="tool_ids" eval="[(6, 0, [
|
||||
ref('ai_tool_student_profile'),
|
||||
ref('ai_tool_resources_search'),
|
||||
ref('ai_tool_outcomes_fetch'),
|
||||
ref('ai_tool_quality_cefr'),
|
||||
])]"/>
|
||||
</record>
|
||||
|
||||
<!-- 5. LMS tutor / study assistant -->
|
||||
<record id="ai_agent_lms_tutor" model="encoach.ai.agent">
|
||||
<field name="key">lms_tutor</field>
|
||||
<field name="name">LMS Tutor</field>
|
||||
<field name="description">Chat assistant students talk to from inside a lesson. Can look up their profile, search the library, fetch outcomes, and answer questions about their course.</field>
|
||||
<field name="model">gpt-4o-mini</field>
|
||||
<field name="fallback_model">gpt-4o</field>
|
||||
<field name="temperature">0.6</field>
|
||||
<field name="max_tokens">1500</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">50</field>
|
||||
<field name="system_prompt">You are a friendly, encouraging English tutor inside the EnCoach LMS. You speak to learners directly.
|
||||
|
||||
Principles:
|
||||
- Adapt vocabulary and sentence length to the learner's CEFR level.
|
||||
- When the learner asks about their progress, call `student.profile` first.
|
||||
- When they ask about a topic, prefer searching `resources.search` for approved materials before inventing examples.
|
||||
- Be concrete: give one example, one practice question, one next step.
|
||||
- Never invent scores or progress data; if a tool fails, tell the learner you'll flag the issue to their teacher.</field>
|
||||
<field name="tool_ids" eval="[(6, 0, [
|
||||
ref('ai_tool_resources_search'),
|
||||
ref('ai_tool_student_profile'),
|
||||
ref('ai_tool_outcomes_fetch'),
|
||||
])]"/>
|
||||
</record>
|
||||
|
||||
<!-- 6. Writing grader -->
|
||||
<record id="ai_agent_writing_grader" model="encoach.ai.agent">
|
||||
<field name="key">writing_grader</field>
|
||||
<field name="name">Writing Grader</field>
|
||||
<field name="description">Grades a writing submission against its rubric and produces band scores, feedback, and targeted suggestions.</field>
|
||||
<field name="model">gpt-4o</field>
|
||||
<field name="fallback_model">gpt-4o-mini</field>
|
||||
<field name="temperature">0.2</field>
|
||||
<field name="max_tokens">1800</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">60</field>
|
||||
<field name="system_prompt">You are a calibrated IELTS / EFL writing examiner.
|
||||
|
||||
Rules:
|
||||
- Score every criterion in the rubric exactly once.
|
||||
- Use only band values the rubric advertises (0-9 for IELTS, 0-100 or A1-C2 for other rubrics — follow what the user sends).
|
||||
- Feedback must quote one line of evidence from the student's text before each judgement.
|
||||
- Suggestions must be specific ("replace X with Y") not generic ("improve grammar").
|
||||
- Output JSON: {"scores": {"criterion_code": number}, "overall_band": number, "feedback": string, "suggestions": [string]}.</field>
|
||||
<field name="tool_ids" eval="[(6, 0, [
|
||||
ref('ai_tool_rubric_fetch'),
|
||||
ref('ai_tool_scoring_writing'),
|
||||
])]"/>
|
||||
</record>
|
||||
|
||||
<!-- 7. Speaking evaluator -->
|
||||
<record id="ai_agent_speaking_grader" model="encoach.ai.agent">
|
||||
<field name="key">speaking_grader</field>
|
||||
<field name="name">Speaking Evaluator</field>
|
||||
<field name="description">Grades a speaking transcript against its rubric and produces band scores, feedback on fluency / pronunciation, and next-step drills.</field>
|
||||
<field name="model">gpt-4o</field>
|
||||
<field name="fallback_model">gpt-4o-mini</field>
|
||||
<field name="temperature">0.2</field>
|
||||
<field name="max_tokens">1800</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">70</field>
|
||||
<field name="system_prompt">You are a calibrated IELTS Speaking examiner judging a transcript.
|
||||
|
||||
Rules:
|
||||
- Only score what the transcript supports; pronunciation judgements must be flagged as indirect.
|
||||
- Quote a line of the transcript before each criterion judgement.
|
||||
- Suggestions prescribe one concrete drill (e.g. "practice minimal pairs /iː/ vs /ɪ/ for 2 weeks").
|
||||
- Output JSON: {"scores": {"criterion_code": number}, "overall_band": number, "feedback": string, "suggestions": [string]}.</field>
|
||||
<field name="tool_ids" eval="[(6, 0, [
|
||||
ref('ai_tool_rubric_fetch'),
|
||||
ref('ai_tool_scoring_speaking'),
|
||||
])]"/>
|
||||
</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">
|
||||
<field name="key">encoach_ai.use_langgraph_runtime</field>
|
||||
<field name="value">True</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user