| # Evaluation and scorer contract |
|
|
| BarunAction uses deterministic, model-free scoring. Raw model text is parsed as Action IR v1, |
| validated against the supplied tool schemas, and compared with a canonical gold AST. No evaluator |
| model, fuzzy string rule, output extraction, or repair step participates in the headline score. |
|
|
| The released versions are: |
|
|
| | Layer | Version | |
| | --- | --- | |
| | Action IR evaluator | `action-ir-v1.0.0` | |
| | Mobile Actions scorer | `barun-mobile-actions-score-v1` | |
| | Float generation record | `barun-greedy-generation-v1` | |
| | Int8 generation record | `barun-greedy-generation-int8-v1` | |
|
|
| ## Source of truth |
|
|
| | Concern | Implementation | |
| | --- | --- | |
| | Strict JSON decoding and Action IR validation | [`src/barunlm/evaluation/action_ir.py`](../src/barunlm/evaluation/action_ir.py) | |
| | Sample and aggregate metric logic | [`src/barunlm/evaluation/evaluator.py`](../src/barunlm/evaluation/evaluator.py) | |
| | Mobile tool-schema parsing and aligned scoring | [`src/barunlm/evaluation/mobile_actions.py`](../src/barunlm/evaluation/mobile_actions.py) | |
| | Deterministic generation and checkpoint verification | [`src/barunlm/evaluation/generation.py`](../src/barunlm/evaluation/generation.py) | |
| | Generate-and-score command | [`scripts/evaluate_mobile_actions.py`](../scripts/evaluate_mobile_actions.py) | |
| | Scorer regression tests | [`tests/test_mobile_actions_scoring.py`](../tests/test_mobile_actions_scoring.py) | |
|
|
| Documentation is explanatory; these checked-in functions define the executable metric. |
|
|
| ## Strict JSON parsing |
|
|
| `decode_json_object()` accepts exactly one UTF-8 JSON object. Leading and trailing JSON whitespace |
| is allowed. The following are deterministic parse failures: |
|
|
| - prose, Markdown fences, prefixes, suffixes, or a second JSON value; |
| - a non-object top-level value; |
| - duplicate object keys; |
| - `NaN`, `Infinity`, numeric overflow, or non-finite values; |
| - invalid UTF-8; and |
| - collection nesting deeper than 64 levels. |
|
|
| The evaluator never searches for a JSON substring and never retries with a repaired output. |
|
|
| ## Schema validation |
|
|
| `validate_action_ir()` requires one of four exact decision shapes: |
|
|
| ```json |
| {"calls":[{"args":{"query":"Cubbon Park"},"tool":"show_map"}],"decision":"CALL","mode":"SINGLE"} |
| ``` |
|
|
| ```json |
| {"calls":[{"args":{"body":"Running late","to":"Asha"},"tool":"send_message"}],"decision":"CONFIRM","mode":"SINGLE"} |
| ``` |
|
|
| ```json |
| {"decision":"CLARIFY","missing":["datetime"]} |
| ``` |
|
|
| ```json |
| {"decision":"ABSTAIN"} |
| ``` |
|
|
| Unknown or missing fields fail. `CALL` and `CONFIRM` require a non-empty `calls` list and one of |
| `SINGLE`, `SERIAL`, or `PARALLEL`. A call must contain exactly `tool` and `args`; the tool must be |
| declared; required arguments must be present; extra arguments are allowed only when the schema says |
| so; and every nested value must have the declared JSON type. `SINGLE` requires exactly one call. |
|
|
| `CLARIFY.missing` must be non-empty, sorted, unique, and string-valued. `ABSTAIN` may contain no |
| other field. |
|
|
| ## Canonical equality |
|
|
| After validation: |
|
|
| - object keys are sorted recursively; |
| - structural JSON whitespace and input key order are ignored; |
| - strings and object keys are Unicode NFC-normalized; |
| - whitespace inside string values remains exact; |
| - integers, numbers, booleans, nulls, arrays, and objects retain their JSON types; |
| - no optional argument default is inserted; |
| - `SINGLE` and `SERIAL` calls remain ordered; and |
| - `PARALLEL` calls are compared as a multiset of canonical calls, preserving duplicates. |
|
|
| Two outputs are AST exact only when their decisions, modes, calls, and typed argument values match |
| under those rules. Semantically similar dates, addresses, names, or strings do not receive credit |
| unless they are represented identically after the declared deterministic canonicalization. |
|
|
| ## Mobile Actions adapter |
|
|
| For each manifest row, `schemas_from_prompt()` parses the exact `TOOLS` block rendered by |
| `barun-mobile-actions-adapter-v2`. It rejects malformed lines, duplicate tools, duplicate argument |
| names, and any type outside the frozen `string`, `integer`, `number`, or `boolean` subset. Tool |
| descriptions are model-visible but do not alter equality. |
|
|
| `score_rows()` requires a one-to-one ID join: |
|
|
| - manifest IDs and prediction IDs must each be unique; |
| - every manifest ID must have one prediction; |
| - no unknown prediction ID is allowed; and |
| - predictions may contain only `id`, `prediction_raw`, `truncated`, `generation_failure`, |
| `prompt_tokens`, and `generated_tokens`. |
|
|
| For this diagnostic, the deterministic execution hook reports success exactly when the Action IR |
| AST matches. Therefore policy-safe executable success and AST exact match have the same 602/756 |
| value on the released all-`CALL` population. This equivalence is specific to this adapter and must |
| not be generalized to a real tool simulator. |
|
|
| ## Failure accounting |
|
|
| A row is incorrect when any of the following occurs: |
|
|
| - prediction is missing; |
| - generation reports a failure; |
| - generation reaches the 192-token cap and is marked truncated; |
| - raw text fails strict JSON parsing; |
| - parsed JSON fails Action IR or tool-schema validation; or |
| - the valid AST differs from gold. |
|
|
| Failures remain visible in `failure_counts`; they are not dropped from denominators. Candidate-v2 |
| produced 756/756 parse-valid outputs, 755/756 schema-valid outputs, and 602/756 exact outputs. Its |
| single schema-invalid output used an invalid call count. Qwen produced 755/756 parse-valid, |
| 754/756 schema-valid, and 663/756 exact outputs: one invalid JSON string and one additional parsed |
| object with an unknown field. |
|
|
| ## Secondary diagnostics |
|
|
| The aggregate also reports decision accuracy, per-tool metrics, argument-key and argument-value |
| F1, per-scenario success, and failure categories. |
|
|
| Argument facts include sample ID, call index, tool, argument name, and—when scoring values—the |
| canonical JSON value. Including the sample ID prevents a correct fact on one row from canceling a |
| miss on another. `PARALLEL` calls are canonically sorted before assigning call indices. A row with |
| no gold and no predicted argument facts receives row-level macro-F1 1; errors on such rows remain |
| visible in decision and primary metrics. |
|
|
| The aggregate schema contains false-action, clarification, confirmation, and abstention fields, |
| but the released 756-row population has zero eligible examples for those decisions. Values with a |
| zero denominator are placeholders, not evidence of safe behavior. |
|
|
| ## Generation contract |
|
|
| The headline float and Qwen results use unconstrained deterministic greedy decoding: |
|
|
| - `do_sample=false`; |
| - `num_beams=1`; |
| - `temperature`, `top_k`, and `top_p` disabled; |
| - repetition penalty `1.0`; |
| - no-repeat n-gram size `0`; and |
| - maximum 192 new tokens. |
|
|
| There is no grammar constraint. Candidate-v2 used generation batch size 128 in its selection run; |
| the matched Qwen lane used 64. Batch size is recorded because it can affect runtime, but it does |
| not change the scoring definition. |
|
|
| ## Re-score the checked-in predictions |
|
|
| The repository distributes raw predictions and aggregate evidence, but not the prompt/gold |
| manifest. Regenerate the pinned development manifest following |
| [`docs/training.md`](training.md), verify SHA-256 |
| `988bdce5874d1f1a775feeb5ba2b58cd2bdc128f57e73cb9a63d535fae7c1d55`, then run: |
|
|
| ```console |
| python - <<'PY' |
| from barunlm.evaluation.mobile_actions import write_scores |
| |
| write_scores( |
| "./local-data/mobile-actions/dev.jsonl", |
| "./benchmarks/evidence/barunaction-predictions.jsonl", |
| "./reproduced/barunaction", |
| ) |
| write_scores( |
| "./local-data/mobile-actions/dev.jsonl", |
| "./benchmarks/evidence/qwen-predictions.jsonl", |
| "./reproduced/qwen", |
| ) |
| PY |
| ``` |
|
|
| Expected aggregate hashes: |
|
|
| | Output | SHA-256 | |
| | --- | --- | |
| | BarunAction aggregate | `5d7244d2fa449a7ce4b2aa3a20fc095fb118a38920b1dd9181f56669a7ddfea1` | |
| | Qwen aggregate | `2184b93d383f1136c050d95e71fb129bbe083b3fea4de86da6bdf5ec7f3e1756` | |
|
|
| To generate and score a new float checkpoint in one pass: |
|
|
| ```console |
| python scripts/evaluate_mobile_actions.py \ |
| --checkpoint ./models/BarunAction-35M \ |
| --manifest ./local-data/mobile-actions/dev.jsonl \ |
| --manifest-sha256 988bdce5874d1f1a775feeb5ba2b58cd2bdc128f57e73cb9a63d535fae7c1d55 \ |
| --output ./new-evaluation \ |
| --device cpu \ |
| --batch-size 16 \ |
| --max-new-tokens 192 |
| ``` |
|
|
| The output directory must not already exist. The generator verifies the checkpoint before loading |
| it and writes sample-level predictions before the scorer writes sample and aggregate records. |
|
|
| ## Interpretation boundary |
|
|
| The Mobile development result tests strict serialization, familiar-tool selection, and argument |
| binding under one seven-tool schema family. It does not measure official test performance, |
| abstention, ambiguity resolution, confirmation, false-action rate, prompt-injection robustness, |
| renamed or unseen schemas, broad personal-action utility, or real-world execution safety. |
|
|