Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added Arqui_AAC.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added CicloVida_AAC.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
72 changes: 68 additions & 4 deletions backend/lamb/aac/agent/loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,51 @@
TEST: lamb test scenarios <id> | add <id> <title> --message "text" | run <id> [--bypass] | runs <id> | evaluate <run_id> <good|bad|mixed>
WRITE: lamb assistant create <name> [--system-prompt "..." --llm model ...] | update <id> [...] | delete <id>

## Moodle Integration (if configured)

When the user has a Moodle integration set up, you can run `moodle ...` commands to query and manage their Moodle LMS. Use `lamb docs read moodle-cli` to see all available commands, or load the `query-moodle` skill for guided interaction.

Key moodle commands:
READ: moodle course list | get <id> | search <query> | contents <id>
READ: moodle user me | list | get <id>
READ: moodle enrol my-courses | list-users <course_id>
READ: moodle grade get <course_id> | report <course_id>
READ: moodle assign list | submissions <id>
READ: moodle forum list <course_id> | discussions <forum_id>
READ: moodle quiz list <course_id> | attempts <quiz_id>
READ: moodle calendar events
READ: moodle completion status <course_id>
READ: moodle cohort list
READ: moodle site info | functions
WRITE: moodle assign grade | forum post | message send | completion update
WRITE: moodle course create | update | delete
WRITE: moodle user create | update | delete
WRITE: moodle cohort create | delete | add-members | remove-members
WRITE: moodle role assign | unassign
WRITE: moodle calendar create
WRITE: moodle file upload
ESCAPE: moodle call <function_name> -P key=value (any Moodle WS function)

Always ask the user before running WRITE moodle commands. For READ commands, just run them.

### TIMESTAMPS — Always convert to readable dates

Moodle returns Unix timestamps (e.g. `duedate: 1744320000`) for dates. **Always** convert these to a human-readable format (e.g. "Apr 10, 2026 14:30") when presenting results. Never show raw timestamps. Never ask the user "do you want me to convert that?" — just do it.

Fields that are timestamps: `duedate`, `timestart`, `timecreated`, `timemodified`, `lastaccess`, `timeend`, `timeduration`.

### PRIVACY — CRITICAL: You can ONLY access YOUR data

The moodle-cli runs with YOUR Moodle token. You MUST enforce these rules:

**NEVER query another user by ID.** Commands with `--user-id` or `<user_id>` (`moodle user get <id>`, `moodle grade get --user-id <id>`, `moodle grade report --user-id <id>`, `moodle completion status --user-id <id>`, `moodle message send <user_id>`) may ONLY be used with the authenticated user's own ID.

**Prefer self-scoped commands:** Use `moodle user me` (not `moodle user get <id>`). Use `moodle enrol my-courses` (not `moodle course list`).

**Allowed exceptions:** `moodle enrol list-users <course_id>` is OK (teacher context). `moodle course list`, `moodle course get <id>`, `moodle course contents <id>` are OK (course data, not personal data).

**If the user asks about another user's data**, politely refuse: "I can only access Moodle data for your account. I cannot look up other users' private information."

debug and --bypass = inspect mode. It runs the full prompt assembly (system prompt + RAG context + template)
WITHOUT calling the LLM. It returns the constructed messages array — this IS the expected output.
An empty or minimal response from debug is NORMAL for non-RAG assistants (no KB content to inject).
Expand Down Expand Up @@ -104,7 +149,7 @@

When the user asks to do something covered by a specific skill (create, improve, explain, test an assistant),
use `lamb skill load <skill-id>` to switch. Available skills: about-lamb, create-assistant, improve-assistant,
explain-assistant, test-and-evaluate. Use `lamb skill list` if unsure.
explain-assistant, test-and-evaluate, setup-moodle, query-moodle. Use `lamb skill list` if unsure.

End EVERY response with numbered options. EXACTLY this format, no variations:

Expand Down Expand Up @@ -192,6 +237,11 @@
"session.rename": "Renaming session",
"docs.index": "Loading documentation index",
"docs.read": "Reading documentation",
"moodle": "Running Moodle command",
"integration.list": "Checking integrations",
"integration.test": "Testing integration",
"integration.save": "Saving integration",
"integration.remove": "Removing integration",
}


Expand All @@ -200,11 +250,15 @@ def _parse_action_key(cmd: str) -> str:
tokens = cmd.strip().split()
if tokens and tokens[0] == "lamb":
tokens = tokens[1:]
if not tokens:
return ""
# Passthrough commands (like moodle) use just the group name as key
from lamb.aac.liteshell.commands import PASSTHROUGH_COMMANDS
if tokens[0] in PASSTHROUGH_COMMANDS:
return tokens[0]
if len(tokens) >= 2 and not tokens[1].startswith("-"):
return f"{tokens[0]}.{tokens[1]}"
elif tokens:
return tokens[0]
return ""
return tokens[0]


def _describe_tool_call(tc: Any) -> str:
Expand Down Expand Up @@ -276,6 +330,12 @@ def _summarize_result(action_key: str, result: Any) -> str:
elif action_key == "kb.get":
files = d.get("files", [])
return f"name={d.get('name','?')}, {len(files)} files"
elif action_key == "moodle":
cmd = d.get("command", "")
exit_code = d.get("exit_code", -1)
stdout = d.get("stdout", "")
lines = [l for l in stdout.split("\n") if l.strip()]
return f"exit={exit_code}, {len(lines)} lines, cmd={cmd[:60]}"
except Exception:
pass

Expand Down Expand Up @@ -325,6 +385,10 @@ def _extract_artifacts(cmd: str, result: Any) -> list[dict]:
if resource_type == "test" and resource_id:
return [{"type": "assistant", "id": resource_id, "action": action}]

# For moodle commands, capture the subcommand as the resource type
if resource_type == "moodle" and subcommand:
return [{"type": f"moodle/{subcommand}", "id": resource_id, "action": action}]

if resource_id:
return [{"type": resource_type, "id": resource_id, "action": action}]
elif resource_type != "help":
Expand Down
4 changes: 4 additions & 0 deletions backend/lamb/aac/authorization.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ def resolve_action_key(self, command_str: str) -> str | None:
tokens = tokens[1:]
if not tokens:
return None
# Passthrough commands (like moodle) use just the group name as key
from lamb.aac.liteshell.commands import PASSTHROUGH_COMMANDS
if tokens[0] in PASSTHROUGH_COMMANDS:
return tokens[0]
if len(tokens) >= 2 and not tokens[1].startswith("-"):
return f"{tokens[0]}.{tokens[1]}"
return tokens[0]
Expand Down
1 change: 1 addition & 0 deletions backend/lamb/aac/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ Use `lamb docs read <topic> --section "heading"` to read a subsection.
| collaboration | collaboration.md | Sharing assistants, KBs, and templates with other educators | sharing, shared-with-me, organization, permissions |
| troubleshooting | troubleshooting.md | Common problems and solutions for assistants, RAG, publishing, and access | errors, empty-context, rag-broken, cant-publish, no-share-tab, students-cant-access |
| glossary | glossary.md | Definitions of key LAMB terms | terms, definitions, vocabulary |
| moodle-cli | moodle-cli.md | Moodle CLI integration — courses, users, grades, enrolments, assignments, forums, quizzes, calendar, messages, completion, files, cohorts, roles | moodle, courses, users, grades, enrolments, assignments, forums, quizzes, calendar, messages, completion, files, cohorts, roles, site |
187 changes: 187 additions & 0 deletions backend/lamb/aac/docs/moodle-cli.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
---
topic: moodle-cli
covers: [moodle, courses, users, grades, enrolments, assignments, forums, quizzes, calendar, messages, completion, files, cohorts, roles, site]
answers:
- "how do I list my Moodle courses"
- "show me users enrolled in a course"
- "what grades do my students have"
- "list assignments in a course"
- "show forum discussions"
- "list quizzes in a course"
- "how do I enrol a user"
- "show calendar events"
- "send a message to a user"
- "check activity completion"
- "list cohorts"
- "assign a role to a user"
- "show site information"
- "upload a file to Moodle"
---

# moodle-cli: Moodle CLI Integration

The AAC agent can run `moodle ...` commands to interact with a Moodle LMS instance. These commands require the user to have set up a Moodle integration first (use the `setup-moodle` skill).

## Authentication

Before using any moodle command, the user must configure their Moodle credentials:
1. Run the `setup-moodle` skill: `lamb skill load setup-moodle`
2. Follow the steps to provide Moodle URL + Web Services token
3. Verify with `integration test moodle`

## Available Commands

### Site information

| Command | Description |
|---------|-------------|
| `moodle site info` | Show site name, URL, release, version, current user |
| `moodle site functions [--search <term>] [--component <prefix>]` | List available web service functions |

### Course management

| Command | Description |
|---------|-------------|
| `moodle course list` | List all courses (ID, short name, full name, visibility) |
| `moodle course get <course_id>` | Get details of a specific course |
| `moodle course search <query>` | Search courses by name |
| `moodle course contents <course_id>` | Show course sections and modules |
| `moodle course create --fullname <name> --shortname <code> --categoryid <id>` | Create a new course |
| `moodle course update <id> [--fullname <name>] [--shortname <code>] [--visible 0\|1]` | Update a course |
| `moodle course delete <id> [id2 ...]` | Delete courses (requires confirmation) |

### User management

| Command | Description |
|---------|-------------|
| `moodle user me` | Show current authenticated user info |
| `moodle user list [--key email] [--value %%]` | List/search users (use %% as wildcard) |
| `moodle user get <user_id>` | Get user details by ID |
| `moodle user create --username <name> --firstname <fn> --lastname <ln> --email <email> --password <pw>` | Create a new user |
| `moodle user update <id> [--firstname <fn>] [--lastname <ln>] [--email <email>]` | Update a user |
| `moodle user delete <id> [id2 ...]` | Delete users (requires confirmation) |

### Enrolment management

| Command | Description |
|---------|-------------|
| `moodle enrol my-courses` | List courses the current user is enrolled in |
| `moodle enrol list-users <course_id>` | List enrolled users in a course |

### Grade management

| Command | Description |
|---------|-------------|
| `moodle grade get <course_id> [--user-id <id>]` | Get grade items for a course |
| `moodle grade report <course_id> [--user-id <id>]` | Get full grade report for a course |

### Assignment management

| Command | Description |
|---------|-------------|
| `moodle assign list [--course-id <id>]` | List assignments (optionally filtered by course) |
| `moodle assign submissions <assignment_id> [id2 ...]` | Get submissions for assignment(s) |
| `moodle assign grade --assignment-id <id> --user-id <id> --grade <score> [--feedback <text>]` | Grade a submission |

### Forum management

| Command | Description |
|---------|-------------|
| `moodle forum list <course_id>` | List forums in a course |
| `moodle forum discussions <forum_id>` | List discussions in a forum |
| `moodle forum post --forum-id <id> --subject <text> --message <text>` | Post a new discussion to a forum |

### Quiz management

| Command | Description |
|---------|-------------|
| `moodle quiz list <course_id>` | List quizzes in a course |
| `moodle quiz attempts <quiz_id> [--user-id <id>]` | List quiz attempts |

### Calendar management

| Command | Description |
|---------|-------------|
| `moodle calendar events [--course-id <id>]` | List calendar events (optionally filtered by course) |
| `moodle calendar create --name <name> --timestart <unix_ts> [--duration <secs>] [--description <text>] [--course-id <id>] [--type user\|course\|site]` | Create a calendar event |

### Messaging

| Command | Description |
|---------|-------------|
| `moodle message send <user_id> <text>` | Send a message to a user |
| `moodle message list [--from-user <id>]` | List recent messages |
| `moodle message conversations` | List conversations |

### Activity completion

| Command | Description |
|---------|-------------|
| `moodle completion status <course_id> [--user-id <id>]` | Show activity completion status for a course |
| `moodle completion update <cmid> <true\|false>` | Manually mark an activity as complete or incomplete |

### File management

| Command | Description |
|---------|-------------|
| `moodle file list <contextid> [--component user] [--filearea private] [--itemid 0] [--filepath /]` | List files in a Moodle file area |
| `moodle file upload <local_path> [--component user] [--filearea draft] [--itemid 0]` | Upload a file to Moodle |

### Cohort management

| Command | Description |
|---------|-------------|
| `moodle cohort list` | List all cohorts |
| `moodle cohort create --name <name> [--idnumber <code>] [--description <text>]` | Create a cohort |
| `moodle cohort delete <id> [id2 ...]` | Delete cohorts (requires confirmation) |
| `moodle cohort add-members <cohort_id> <user_id> [user_id ...]` | Add users to a cohort |
| `moodle cohort remove-members <cohort_id> <user_id> [user_id ...]` | Remove users from a cohort |

### Role management

| Command | Description |
|---------|-------------|
| `moodle role assign --role-id <id> --user-id <id> --context-id <id>` | Assign a role to a user in a context |
| `moodle role unassign --role-id <id> --user-id <id> --context-id <id>` | Unassign a role from a user in a context |

### Generic API call

| Command | Description |
|---------|-------------|
| `moodle call <function_name> [-P key=value ...]` | Call any Moodle web service function directly |

## Usage Tips

- All commands return structured data. The agent will present it in a readable format.
- **Timestamps**: Moodle returns Unix timestamps (e.g. `duedate: 1744320000`). The agent will always convert these to readable dates automatically — no need to ask.
- For destructive operations (delete, update), the agent will ask for confirmation.
- Use `moodle course list` first to discover course IDs, then drill into specific courses.
- Use `moodle enrol list-users <course_id>` to see who is enrolled in a course.
- Use `moodle grade report <course_id>` to get a full picture of student performance.
- The `moodle call` command is an escape hatch for any Moodle WS function not covered by the other commands.

## Privacy & Data Access Rules

**CRITICAL: The moodle-cli runs with the credentials of the LAMB user who configured the Moodle integration. The agent MUST enforce these rules:**

### Rule 1: Only the authenticated user's data

All moodle commands execute using **the Moodle token of the user who set up the integration**. The agent may ONLY query data that belongs to or is accessible to this authenticated user. Never query data about a specific different user unless it's in the context of the authenticated user's own courses (e.g. seeing who is enrolled in a course the authenticated user teaches).

### Rule 2: Never query another user by ID

Commands that accept a `--user-id` or `<user_id>` parameter (`moodle user get <id>`, `moodle grade get --user-id <id>`, `moodle grade report --user-id <id>`, `moodle completion status --user-id <id>`, `moodle message send <user_id>`) MUST only be used with the authenticated user's own ID. If the user asks about another person's data, explain that you can only show data for the authenticated user.

Exception: `moodle enrol list-users <course_id>` is allowed because it shows enrolment in a course context (the authenticated user likely has permission to see this as a teacher).

### Rule 3: Never expose other users' personal data

If a command returns data about other users (e.g. `moodle user list` shows all users), do NOT display personal details (email, full name) of users other than the authenticated user. Summarize counts instead.

### Rule 4: Scope queries to the authenticated user

When the user asks "show me my courses", "what are my grades", etc., use `moodle enrol my-courses` (not `moodle course list`). Use `moodle user me` (not `moodle user get <id>`). Prefer self-scoped commands over general ones.

### Rule 5: Refuse unauthorized queries

If the user asks you to look up another specific user's data (grades, profile details, messages, completion status), politely refuse: "I can only access Moodle data for your account. I cannot look up other users' private information."
2 changes: 2 additions & 0 deletions backend/lamb/aac/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ async def create_session(
"explain-assistant": "Explain",
"test-and-evaluate": "Test",
"test-lti-tools": "LTI Setup",
"setup-moodle": "Setup: Moodle",
"query-moodle": "Moodle Query",
}
if skill_id:
base = _skill_titles.get(skill_id, skill_id)
Expand Down
3 changes: 3 additions & 0 deletions backend/lamb/aac/skills/about_lamb.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Greet briefly (2 lines max). Say you can help with:
- Rubrics and evaluation
- Publishing to Moodle/LMS
- Troubleshooting
- Moodle data queries (courses, users, grades) — **privacy: only your own data**

Do NOT list all features. Do NOT dump the docs index. Just offer help and wait.

Expand All @@ -37,6 +38,8 @@ When the user asks about something they could DO right now, switch to the approp
- "Improve my assistant 18" → `lamb skill load improve-assistant --assistant 18`
- "Explain how assistant 18 works" → `lamb skill load explain-assistant --assistant 18`
- "Test my assistant" → `lamb skill load test-and-evaluate --assistant 18`
- "Set up Moodle" → `lamb skill load setup-moodle`
- "Query Moodle data" → `lamb skill load query-moodle`

The skill switch happens seamlessly — the conversation continues, the skill adds expertise.
If unsure which skill, use `lamb skill list` to see all available skills.
Expand Down
Loading