Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/elenacarino-max/Pildora4_ext_StarWars/llms.txt

Use this file to discover all available pages before exploring further.

The code validator (core/code_validator.py) is the security core of MLflow Jedi. Rather than running anything a student types, it parses Python source text into an Abstract Syntax Tree using the built-in ast module and inspects only the tree’s structure. This means every submission — from a single mlflow.set_experiment call to a full mission script — is analysed purely as data. No import side-effects, no network calls, no filesystem writes can ever originate from student input.

Security model

Two guarantees underpin every submission check:
  1. Student code is never executed. _parse_safe() calls ast.parse(code, mode="exec") and then walks the resulting tree. The code string never reaches eval(), exec(), or compile().
  2. Only whitelisted MLflow calls are accepted. Any call whose dotted name is absent from ALLOWED_CALLS causes immediate rejection with a descriptive Spanish-language error message.
These two properties together mean a student cannot import libraries, shadow builtins, or call arbitrary APIs — even if they craft syntactically valid Python.

ValidationResult

Every validator function returns a single ValidationResult dataclass:
@dataclass
class ValidationResult:
    valid: bool
    message: str
    calls: list[str]  # MLflow calls found in the code
FieldTypeDescription
validboolTrue if the submission satisfies all checks, False otherwise
messagestrHuman-readable feedback shown to the student (written in Spanish)
callslist[str]Flat list of every dotted call name found during the AST walk
The calls field is populated even on failure so the UI can highlight which calls were detected.

Allowed calls

Only the following eight MLflow tracking calls are permitted anywhere in student-submitted code. Any other call — including helpers, builtins, or third-party functions — triggers an “not authorized in this terminal” error.
mlflow.set_experiment
mlflow.start_run
mlflow.log_param
mlflow.log_params
mlflow.log_metric
mlflow.log_metrics
mlflow.log_artifact
mlflow.sklearn.log_model
These map directly to the full set of MLflow concepts taught across the six Chambers: Experiment, Run, Parameters, Metrics, Artifacts, and Model.

Forbidden constructs

The _parse_safe() function rejects submissions before any per-challenge logic runs if any of the following are detected:
ConstructAST node typeReason
import statementast.ImportPrevents arbitrary library loading
from ... importast.ImportFromSame as above
lambda expressionast.LambdaPrevents inline callable injection
global statementast.GlobalPrevents global scope manipulation
nonlocal statementast.NonlocalPrevents closure scope manipulation
Any call not in ALLOWED_CALLSast.CallEnforces the MLflow-only whitelist
Code longer than 12,000 charactersHard size cap applied before parsing
A submission that contains even a single forbidden node returns:
ValidationResult(
    valid=False,
    message="El Archivo solo acepta instrucciones de tracking.",
    calls=[...]
)

validate_chamber(chamber_id, code) → ValidationResult

validate_chamber checks a single-concept snippet against the learning goal of one of the six Chambers. After _parse_safe() succeeds, it verifies that the specific required call is present and, for some chambers, applies additional structural constraints.
def validate_chamber(chamber_id: str, code: str) -> ValidationResult: ...
chamber_idRequired callExtra constraint
experimentmlflow.set_experimentFirst positional argument must be the string literal "Biblioteca_Jedi_Digits"
runmlflow.start_runThe call must appear inside an ast.With block
parametersmlflow.log_paramsNone beyond presence
metricsmlflow.log_metricsNone beyond presence
artifactsmlflow.log_artifactNone beyond presence
modelmlflow.sklearn.log_modelMust supply keyword arguments sk_model and artifact_path
On success every chamber returns:
ValidationResult(True, "Sello recuperado. El Archivo reconoce el concepto.", [...])
On failure the message is a domain-themed hint that steers the student toward the correct MLflow concept without revealing the solution outright (e.g. “Primero selecciona la campaña que agrupará las ejecuciones.” for the experiment chamber).

validate_mission_submission(code, mission_id) → ValidationResult

validate_mission_submission enforces the full Código Jedi 5–4–2–1 pattern: five required MLflow calls, four registered metrics, two artifact files, and one logged model — all inside a named run.
def validate_mission_submission(code: str, mission_id: str | None = None) -> ValidationResult: ...
The checks are applied in order:
1

Parse safety

_parse_safe() runs first. Any forbidden construct or oversized submission fails here.
2

All 5 required calls present

The submission must contain every call in {mlflow.start_run, mlflow.log_params, mlflow.log_metrics, mlflow.log_artifact, mlflow.sklearn.log_model}. Missing calls are listed in the error message.
3

Two artifact calls

mlflow.log_artifact must appear at least 2 times in the AST. A single call fails with “La misión necesita dos artefactos de evidencia.”
4

log_params receives parametros

The first positional argument to mlflow.log_params must be an ast.Name node with id == "parametros".
5

log_metrics receives metricas

The first positional argument to mlflow.log_metrics must be an ast.Name node with id == "metricas".
6

Correct artifact filenames

The string literals passed to the two mlflow.log_artifact calls must be exactly "matriz_confusion.png" and "classification_report.json".
7

log_model keyword arguments

mlflow.sklearn.log_model must supply both sk_model and artifact_path as keyword arguments.
8

run_name matches mission

When mission_id is provided, the run_name keyword of mlflow.start_run must match exactly:
mission_idRequired run_name
tatooineMision_Tatooine
coruscantMision_Coruscant
mustafarMision_Mustafar

validate_question_answer(question, answer) → ValidationResult

validate_question_answer handles the didactic questions embedded in the Chamber flow. Questions carry a kind field that selects the validation path.
def validate_question_answer(question: dict, answer: str) -> ValidationResult: ...
choice questions — a simple equality check:
valid = answer == question["solution"]
No AST parsing is performed. The function returns a congratulatory message on success or a generic guidance message on failure. code questions — full _parse_safe() followed by checks driven by the question’s checks dict:
checks keyBehaviour
expected_callsEvery listed call name must appear in the submission
forbidden_callsNone of the listed call names may appear
min_call_countexpected_calls[0] must appear at least this many times
require_withThe AST must contain at least one ast.With node
keyword / keywordsEach listed keyword argument name must appear somewhere in the call graph
constantA specific literal value must appear as an ast.Constant node

Rejection flow example

The following snippet is syntactically valid Python but is always rejected because the import statement is a forbidden construct:
# This is rejected — import is forbidden
import mlflow
mlflow.set_experiment("test")
# → ValidationResult(valid=False, message="El Archivo solo acepta instrucciones de tracking.")
The student must write only the bare MLflow call — no imports, no assignments beyond what the chamber’s starter code provides.
After validate_mission_submission returns valid=True, the app calls an internal execute_safe_mission() function that trains a scikit-learn RandomForestClassifier on the digits dataset using a fixed, controlled template (MISSION_TEMPLATE from missions.py). The student’s validated code is never evaluated as Python — it serves only as a structural proof that the student understands the 5–4–2–1 tracking pattern.

Build docs developers (and LLMs) love