This directory contains developer tools for measuring Anonymizer runs and
exporting measurement JSONL to tables. Run the tools inside the project
environment, either with an activated venv or through uv run.
Use these tools when you need evidence about cost, latency, reliability, or anonymization quality. They are not product entry points.
Start here when you have a measurements.jsonl file and want to analyze it in
pandas, Polars, a spreadsheet, or another local tool.
uv run python tools/measurement/export_measurements.py \
benchmark-runs/suite/measurements.jsonl \
--output benchmark-runs/suite/tables \
--overwriteBy default, the exporter writes one Parquet table per measurement record type
plus manifest.json:
run.parquetstage.parquetrecord.parquetevaluation_record.parquetwhen replace judge evaluation is enabledndd_workflow.parquetwhen DataDesigner adapter records are presentmodel_workflow.parquetwhen direct model workflow records are present
Use CSV or JSONL when those are easier to inspect:
uv run python tools/measurement/export_measurements.py \
benchmark-runs/suite/measurements.jsonl \
--output benchmark-runs/suite/tables-csv \
--format csv \
--overwriteThen load the tables directly:
import pandas as pd
records = pd.read_parquet("benchmark-runs/suite/tables/record.parquet")
stages = pd.read_parquet("benchmark-runs/suite/tables/stage.parquet")
ndd = pd.read_parquet("benchmark-runs/suite/tables/ndd_workflow.parquet")You can also read the raw log, but the exporter is the better default because
it splits records by record_type and normalizes nested fields into columns.
import pandas as pd
raw = pd.read_json("benchmark-runs/suite/measurements.jsonl", lines=True)The measurement system has four cooperating layers:
- Instrumentation in Anonymizer emits JSONL records for runs, stages, DataDesigner workflows, direct model workflows, per-record safety metrics, and optional sanitized replace-judge evaluation metrics.
- Benchmark runners create repeatable workloads and write those JSONL records plus optional sidecars such as detection artifacts and DataDesigner traces.
- Analysis tools convert raw run artifacts into case, group, and model tables.
- Shared benchmark helpers record sanitized execution context and finalize W&B benchmark logging without moving aggregation into execution backends.
External/distributed execution is a separate boundary. Detection export APIs are responsible for building DataDesigner configs that an external runtime can execute. The tools here should consume the resulting measurement JSONL, detection artifacts, and trace sidecars; they should not own Slurm orchestration or distributed DataDesigner execution.
| Task | Tool |
|---|---|
| Export raw measurement JSONL to tables | export_measurements.py |
| Run repeatable Anonymizer suites | run_benchmarks.py |
| Inspect detection artifact sidecars | analyze_detection_artifacts.py |
| Analyze benchmark output directories | analyze_benchmark_output.py |
| Write an atomic external-case completion seal | write_completion_seal.py |
| Import one sealed external case into W&B | import_wandb_run.py |
| Create W&B benchmark workspaces or reports | create_wandb_report.py |
| Run benchmark parameter sweeps | sweep_benchmarks.py |
Most workflows start with run_benchmarks.py, then either export the raw
measurement log with export_measurements.py or summarize the benchmark output
directory with analyze_benchmark_output.py. W&B workflows can also create
project workspaces, reports, or one-run-per-arm sweeps from the benchmark
artifacts.
The scripts keep workload-specific row models and metric logic local, but share
boring command and export policy through measurement_tools/:
measurement_tools.cli:LogFormat, logging setup, and structured bad-input errors.measurement_tools.execution: sanitized local/Slurm execution metadata for benchmark summaries and W&B config.measurement_tools.tables:ExportFormat, model-row table specs, manifest writing, and CSV/Parquet/JSONL output.measurement_tools.stats: small numeric helpers used by analysis groupers.measurement_tools.wandb_completion: completion-seal models, atomic writes, and snapshot verification.measurement_tools.wandb_ingress: bounded descriptor-based capture and strict measurement record parsing.measurement_tools.wandb_metric_schema: shared W&B metric names, path construction, and scalar aggregation policies.measurement_tools.wandb_models: resolved settings and outbound payload models with field exposure policies.measurement_tools.wandb_report_models: historical v1 and current v2 report views with explicit comparison axes.measurement_tools.wandb_setup: guarded W&B environment, staging, and SDK lifecycle ownership.
This is intentionally composition-based. New analysis tools should declare their own row models and call the shared helpers rather than inheriting from a common analyzer base class.
run_benchmarks.py runs repeatable Anonymizer workloads and writes the same
measurement JSONL format, one raw file per benchmark case plus a combined
measurements.jsonl.
uv run python tools/measurement/run_benchmarks.py suite.yaml --output benchmark-runs/suite
uv run python tools/measurement/run_benchmarks.py suite.yaml --dry-run --json
uv run python tools/measurement/run_benchmarks.py suite.yaml \
--output benchmark-runs/suite \
--dd-trace last_message
uv run python tools/measurement/run_benchmarks.py suite.yaml \
--output benchmark-runs/suite \
--dd-task-traceThe repo-data smoke suite can be run with DataDesigner traces enabled:
bash tools/measurement/examples/run-repo-data-smoke-with-dd-traces.shThe script writes to /tmp/anonymizer-repo-data-smoke-dd-traces by default.
Pass a different output directory as the first argument, or set
DD_TRACE_MODE=all_messages when full chat history is needed:
DD_TRACE_MODE=all_messages \
bash tools/measurement/examples/run-repo-data-smoke-with-dd-traces.sh \
/tmp/anonymizer-repo-data-smoke-dd-traces-full.github/workflows/benchmark-ci.yml runs the same benchmark runner from a
manual GitHub Actions dispatch. It targets the self-hosted
anonymizer-evals runner, checks out the requested ref, installs the project
environment, runs a suite, appends a short case summary to the GitHub step
summary, and uploads the full output directory as a workflow artifact.
The job is intentionally manual. It runs only through workflow_dispatch; it
does not run on push, pull_request, schedule, or the default PR CI path.
GitHub exposes manual dispatch only after the workflow file exists on the
repository default branch. After that, launch it from the Actions UI, GitHub
CLI, or API.
The default suite is tools/measurement/examples/repo-data-smoke.yaml. Dispatch
inputs let operators choose the ref, suite path, output directory,
DataDesigner message trace mode, sanitized scheduler task traces, and
fail-fast behavior. The workflow requires the repository secret
NVIDIA_API_KEY because the default model configuration uses NVIDIA-hosted
models.
The ref input defaults to main. To benchmark a PR or experiment branch, set
ref to that branch name or commit SHA. The workflow checks out that ref and
uses the benchmark runner and suite files from the checkout, so the selected ref
must contain tools/measurement/run_benchmarks.py and the requested suite path.
Benchmark suites are YAML files with three parts:
workloads: input datasets and text-column metadata.configs: Anonymizer replace or rewrite configurations.matrix: optional workload/config pairs and repetition counts. When omitted, every workload is crossed with every config once.
Example:
suite_id: biography-smoke
model_configs: ./model-configs.yaml
model_providers: ./providers.yaml
run_tags:
anonymizer_ref: main
commit_sha: abc123
pipeline_id: "456"
case_retries: 1
case_retry_backoff_sec: 10
workloads:
- id: biographies
source: ./data/biographies.csv
text_column: text
row_limit: 25
- id: support
source: ./data/support.csv
text_column: body
id_column: ticket_id
row_offset: 100
row_limit: 50
configs:
- id: redact-default
replace: redact
evaluate: true
- id: hash-agent-labels
detect:
entity_labels: [person, email, api_key, password]
replace:
strategy: hash
digest_length: 12
- id: rewrite-low-risk
rewrite:
risk_tolerance: low
max_repair_iterations: 1
matrix:
- workload: biographies
config: redact-default
repetitions: 3
- workload: support
config: hash-agent-labelsUse row_limit and row_offset to create cheap, repeatable slices of a local
CSV or Parquet workload. The runner materializes a per-case sliced input under
raw/inputs/ before calling Anonymizer, so each case keeps a stable input file
even when the matrix has multiple configs or repetitions. Slicing is rejected
for URL-like sources because the runner cannot safely materialize a local
subset without downloading the whole dataset first.
Relative paths in suite files are resolved from the suite file's directory.
The runner refuses to write into a non-empty output directory unless
--overwrite is set. By default it also exports Parquet tables into tables/;
pass --no-export when only raw measurement JSONL is needed.
Set model_configs and model_providers explicitly in checked-in or CI suites.
Relying on Anonymizer defaults makes a run depend on the caller's installed
defaults and provider environment. In provider YAML, put environment variable
names such as NVIDIA_API_KEY in api_key; do not commit raw keys. The bundled
repo-data-smoke.yaml follows this pattern with adjacent model/provider files.
Use run_tags for stable suite-level metadata copied into every measurement
record, such as source refs, commit SHAs, CI pipeline IDs, topology labels, or
benchmark-suite revisions. The runner reserves suite_id, workload_id,
config_id, repetition, and case_id for its own case identity tags.
The benchmark runner can log sanitized measurement summaries to Weights &
Biases. W&B benchmark logging is disabled by default. Only
tools/measurement/run_benchmarks.py starts W&B runs; the Anonymizer SDK and
product CLI do not.
Install the optional measurement dependency group before enabling W&B benchmark logging:
uv sync --group measurementEnable a benchmark run with CLI flags:
uv run python tools/measurement/run_benchmarks.py suite.yaml --wandb-mode offline
uv run python tools/measurement/run_benchmarks.py suite.yaml --wandb-mode online --wandb-project my-project
uv run python tools/measurement/run_benchmarks.py suite.yaml --wandb-mode online --wandb-entity my-team --wandb-group nightly --wandb-job-type benchmark --wandb-run-name biography-smoke-main --wandb-tags privacy,nightly
uv run python tools/measurement/run_benchmarks.py suite.yaml --wandb-mode online --wandb-base-url https://wandb.example.comThe same settings can come from environment variables when the corresponding
CLI flag is omitted: ANONYMIZER_MEASUREMENT_WANDB_MODE,
ANONYMIZER_MEASUREMENT_WANDB_ENTITY,
ANONYMIZER_MEASUREMENT_WANDB_PROJECT,
ANONYMIZER_MEASUREMENT_WANDB_BASE_URL,
ANONYMIZER_MEASUREMENT_WANDB_GROUP,
ANONYMIZER_MEASUREMENT_WANDB_JOB_TYPE,
ANONYMIZER_MEASUREMENT_WANDB_RUN_NAME,
ANONYMIZER_MEASUREMENT_WANDB_TAGS, and
ANONYMIZER_MEASUREMENT_WANDB_LOG_TABLES. Generic W&B routing variables such
as WANDB_MODE, WANDB_PROJECT, WANDB_GROUP, WANDB_JOB_TYPE, WANDB_NAME,
and WANDB_TAGS are deliberately ignored. Precedence is an explicit CLI value,
then the corresponding ANONYMIZER_MEASUREMENT_WANDB_* variable, then the
publisher default. The W&B SDK still receives its audited timeout variables,
including WANDB_HTTP_TIMEOUT and WANDB_INIT_TIMEOUT. Authentication comes
from the SDK's local credential files under the preserved home directory or
from WANDB_API_KEY when the launcher provides it in the process environment.
Set a self-hosted endpoint through --wandb-base-url or
ANONYMIZER_MEASUREMENT_WANDB_BASE_URL; ambient WANDB_BASE_URL is ignored.
Remote endpoints require HTTPS. Plain HTTP is accepted only for loopback
development endpoints. SDK error reporting is disabled before W&B is imported.
W&B limits each tag to 64 characters. Configuration rejects unsafe operator
tags before benchmark execution. The publisher compacts long generated suite or
branch tags to a readable prefix plus a 12-character SHA-256 suffix.
Each W&B benchmark run config includes sanitized benchmark metadata: suite/case counts, retry and execution flags, package/runtime versions, git commit/branch/dirty state, model source presence booleans, workload summaries, matrix entries, and one sanitized object per benchmark config. Workload metadata stores source kind and suffix instead of the raw source. Config metadata records settings such as detection thresholds, entity-label count/hash, replacement strategy knobs, rewrite risk tolerance, repair settings, and booleans for prompt-bearing fields rather than their raw text.
The benchmark suite finishes and closes its artifacts before the native
publisher starts. The publisher then reads the measurement JSONL as one bounded
regular-file snapshot. Descriptor-relative traversal pins every path component
and refuses symlinks. The publisher verifies the file owner, link count,
identity, size, SHA-256 digest, schema version, record count, run identities,
and case statuses before importing or initializing the W&B SDK. Hard links,
special files, source mutation, oversized lines, excessive JSON nesting, trace
records, unknown fields, coercions, negative counts, and non-finite numbers
fail validation. Model-workflow extension fields remain under an explicit
local_fields object in the local artifact; W&B ingress validates that shape
and excludes the object from outbound payloads. Unknown top-level fields still
fail validation. The default limits are 32 MiB, 100,000 records, 1 MiB per
line, and 16 JSON levels.
Publication remains best-effort at the benchmark boundary, so validation or
W&B failures do not change benchmark case status.
By default the runner logs only aggregate benchmark and measurement scalars.
Passing --wandb-log-tables uploads sanitized measurement tables as W&B tables;
this remains separate from DataDesigner trace sidecars. Seven strict table-row
models project run, stage, record, evaluation, model-workflow, NDD-workflow, and
trace-coverage records directly from the captured snapshot. The native path
does not parse those records through Pandas. Strict nested models also validate
all run config, summary, and history payloads. The runner disables W&B console,
code, Git, machine metadata, system statistics, and requirements capture. Raw
input text, prompts, model responses, replacement maps, entity payloads, trace
records, paths, URLs, data summaries, model/provider config payloads, and suite
run_tags are excluded. Use --wandb-tags for tags intentionally sent to W&B.
Suite, workload, and config identifiers, Git branch names, and W&B routing fields are visible metadata. Keep these values free of customer data and other sensitive information.
W&B writes local run state under <benchmark-output>/.wandb-private. The runner
creates this directory with owner-only permissions before calling the W&B SDK.
The runner rejects symlinks, untrusted writable parent directories, and output
directories owned by another user. Root-owned, group-writable project
directories are trusted shared-storage ancestors, but the final benchmark output
directory and .wandb-private staging directory must still be owned by the
current user. Keep the directory when an offline run must be synchronized later.
During SDK use, a process-wide guard replaces the process environment with a
minimal runtime allowlist, audited W&B timeout settings, and the fully resolved
publisher settings. Local W&B credential files remain available through the
preserved home directory, and WANDB_API_KEY remains available when explicitly
provided by a launcher. Proxy settings, custom CA settings, and ambient
WANDB_* routing values are withheld from the SDK. The
guard restores the exact original environment after the explicit run handle finishes.
Nested or concurrent native publishers in one process are rejected. Each
native run receives a fresh opaque 128-bit ID and uses resume="never".
Successful initialization is paired with exactly one finish() call. Strict
publisher callers receive both publication and finish errors when both fail;
the native runner logs only the exception type to avoid echoing source values.
Before an external launcher starts benchmark work, verify W&B authentication from
the same account or container and the same preserved home directory used by the
publisher. Keep credentials in the local W&B credential files or pass
WANDB_API_KEY through the launcher environment. Do not pass a key on the
command line.
# W&B public cloud, using a stored SDK credential
uv run wandb login --verify --cloud
# Self-hosted or dedicated cloud, using a stored SDK credential
uv run wandb login --verify --host https://wandb.example.comBoth commands verify the stored credential against the selected endpoint and
exit nonzero on failure. Pass the same endpoint to the benchmark or importer
through --wandb-base-url; the publisher ignores ambient WANDB_BASE_URL.
Avoid wandb status in launcher logs because it prints active settings that can
include credential material. The publisher also withholds proxy and custom-CA
environment variables, so test connectivity without relying on those variables.
External Slurm workflows must write completion-seal.json after the
measurement writer closes and the case reaches its completed terminal stage
without an errored workflow or stage record. Every measurement record must
carry the same case_id, workload_id, config_id, and repetition tags, and
the run ID must equal the case ID. The seal binds the measurement SHA-256
digest, byte and record counts, expected run ID, case identity, Slurm
provenance, and producer commit. The writer pins each parent directory without
following symlinks and rejects untrusted writable directories. Root-owned,
group-writable project directories are trusted shared-storage ancestors, but the
final case directory that receives completion-seal.json must still be owned by
the current user. The companion workflow in anonymizer-experiments calls the
seal writer after its case report:
uv run python tools/measurement/write_completion_seal.py \
/path/to/case/benchmark/measurements.jsonl \
--case-id dataset__config__r000 \
--workload-id dataset-split \
--config-id config \
--repetition 0 \
--phase baseline \
--case-index 0 \
--slurm-job detection=12345 \
--slurm-job replacement=12346 \
--producer-repository anonymizer-experiments \
--producer-commit 0123456789abcdef0123456789abcdef01234567Import the sealed case with the strict importer:
uv run python tools/measurement/import_wandb_run.py \
/path/to/case/benchmark/measurements.jsonl \
--seal-path /path/to/case/benchmark/completion-seal.json \
--wandb-mode online \
--wandb-entity my-team \
--wandb-project nemo-anonymizer-benchmarks \
--jsonExternal launchers can add safe benchmark identity fields to W&B config for filtering and comparisons:
--benchmark-role candidate \
--benchmark-kind pr \
--suite-version 2026-07-30 \
--branch contributor/feat/wandb-benchmark-identity \
--commit-sha 0123456789abcdef0123456789abcdef01234567 \
--pr-number 210 \
--anonymizer-config-id rat-rewrite-throughput \
--anonymizer-mode rewrite--pr-number is required for --benchmark-kind pr and branch. Main and
release baselines can omit it.
The importer captures the seal and JSONL once, verifies their content binding,
builds the complete typed payload, then initializes W&B. It derives a stable
128-bit run ID from the destination, sealed case identity, seal digest, schema
namespace, and sanitizer version. Identical imports use resume="allow" and
converge on the same run ID. A completed matching publication is a no-op;
recovery of an incomplete publication uses deterministic history step zero,
and conflicting sealed content is rejected. Changed bytes require a new valid
seal and produce a different run ID. Init, config, log, summary, and finish failures exit
nonzero; error logs include the exception type without its message.
Strict online imports require --wandb-entity (or
ANONYMIZER_MEASUREMENT_WANDB_ENTITY) so the result always identifies the
destination and includes a W&B run URL. Offline imports may omit the entity.
The goal of W&B support is to get sanitized benchmark data into W&B. Workspaces, reports, views, and panels are presentation layers on top of that data. They can be edited in W&B, regenerated with the tooling below, or replaced as the benchmark questions change.
Create a W&B benchmark workspace or report for uploaded benchmark runs with the report utility:
uv run python tools/measurement/create_wandb_report.py \
entity/project/run-id
uv run python tools/measurement/create_wandb_report.py \
entity/project \
--workspaceThe benchmark report utility can create a manual W&B workspace with focused
sections for benchmark summary, privacy, utility, cost/throughput, sweep
comparison, and sanitized tables. The sweep comparison section includes a Run
Comparer panel plus parameter-tradeoff panels. The same utility can still build
a draft report from sanitized benchmark run summary/config fields and a panel
grid filtered to one run. Pass --publish when a report should be saved as a
published report instead of a draft. Workspace/report generation requires the
measurement dependency group because it uses the wandb[workspaces] extra.
The reader projects remote data into closed v1 and v2 run views. It groups
native runs by suite identity, sweep runs by arm identity, and imported runs by
config identity. Mixed run kinds fail before report creation. The Markdown
renderer escapes link labels, code spans, table cells, headings, and list text,
and reconstructs run URLs from validated entity, project, and run identifiers.
Generated workspaces and reports are not canonical artifacts. The W&B project data is canonical. If a workspace layout, report panel, or project view stops answering the benchmark question, update it directly in W&B or rerun the workspace/report command.
Reports include panels for case health and latency, DataDesigner row flow, request health, token usage, throughput, record privacy counters, replacement quality counters, stage throughput, and sanitized measurement tables when table logging is enabled.
For parameter sweeps, use one W&B benchmark run per sweep arm:
sweep_id: qwen-threshold-smoke
base_suite: ./repo-data-smoke-qwen3-coder-next.yaml
parameters:
configs.*.detect.gliner_threshold: [0.2, 0.4]
configs.legal-hash-agent-labels.replace.digest_length: [8, 12]uv run python tools/measurement/sweep_benchmarks.py sweep.yaml \
--wandb-mode online \
--wandb-entity my-team \
--wandb-project nemo-anonymizer-benchmarks \
--create-workspaceEach arm materializes a generated suite under the sweep output root and runs
run_benchmarks.py once. Sweep metadata is copied into safe run tags and W&B
benchmark run config fields such as sweep_id, sweep_arm_id, and
sweep_param_* so W&B benchmark workspaces and reports can compare arms
directly. A benchmark group workspace or report can also be created later:
uv run python tools/measurement/create_wandb_report.py \
my-team/nemo-anonymizer-benchmarks \
--group qwen-threshold-smoke
uv run python tools/measurement/create_wandb_report.py \
my-team/nemo-anonymizer-benchmarks \
--workspace \
--group qwen-threshold-smokeSet evaluate: true on a replace config when the benchmark should run
Anonymizer.evaluate() after run() and capture the LLM-as-judge work in the
same case. This is intentionally replace-only for now; rewrite runs already
perform their internal evaluation/repair loop during run().
When evaluation is enabled, the safe measurement log includes
evaluation_record rows with judge verdict booleans and invalid-item counts.
It does not persist the evaluated result dataframe or trace dataframe. Those
dataframes can contain original text, entity values, replacement values, raw
judge outputs, prompts, and model responses.
Before starting a real run, the benchmark runner performs cheap preflight
checks: suite/config parsing, local dataset existence, CSV/Parquet text-column
metadata, provider YAML shape, and active model-alias references. --dry-run
runs those same checks, expands the planned matrix, and skips output-dir writes
and model work.
Use case_retries and case_retry_backoff_sec for long-running suites on
shared model endpoints. Retries are disabled by default. When enabled, a failed
case is retried with the same case_id and output paths; the final case still
records attempt_count and attempt_errors in summary.json. --fail-fast
remains fail-fast and bypasses retries.
For debugging DataDesigner calls, pass --dd-trace last_message or
--dd-trace all_messages. Trace records are written separately from sanitized
measurements, under traces/{case_id}.jsonl by default. Use --trace-dir to
choose another directory. last_message stores only the final prompt message
for each DataDesigner model call; all_messages stores the full message list.
DataDesigner traces may contain raw input text, prompts, model outputs, entity values, replacement values, secrets, and PII. Treat them as debug artifacts: keep them out of shared benchmark bundles unless they have been reviewed or redacted.
Anonymizer requests standard LLM-column traces through DataDesigner native LLM
column trace side effects. That covers LLMTextColumnConfig and
LLMStructuredColumnConfig. Model-backed CustomColumnConfig generator
functions are traced through a temporary Anonymizer shim that instruments the
per-run DataDesigner model registry and returned model facades. This is a
brittle bridge over private DataDesigner internals until DataDesigner exposes a
public model-call trace sink.
Safe measurement output includes a dd_trace_coverage record with native,
private-facade, and unsupported column counts so trace-enabled runs can detect
which path covered each workflow.
Pass --dd-task-trace to collect sanitized DataDesigner async scheduler task
timing records. The benchmark runner writes one sidecar per case under
task-traces/{case_id}.jsonl by default; use --task-trace-dir to choose
another directory.
Task trace records are separate from raw message traces. They include scheduler metadata such as workflow name, column, row group, row index, task type, status, relative dispatch/slot-acquired/completion offsets, queue wait time, execution time, total time, and whether an error was present. They intentionally do not store raw DataDesigner error strings because those can contain prompts, outputs, or source values.
Offsets are relative to the earliest positive dispatched_at timestamp in each
DataDesigner workflow trace batch written into the case sidecar. They are meant
for timeline analysis without storing host-specific wall-clock timestamps.
uv run python tools/measurement/run_benchmarks.py \
suite.yaml \
--output benchmark-runs/suite \
--dd-task-traceanalyze_benchmark_output.py joins measurements.jsonl, optional
DataDesigner traces, and detection artifact sidecars into richer case/group
tables:
uv run python tools/measurement/analyze_benchmark_output.py \
benchmark-runs/suite-id \
--output benchmark-runs/suite-id/analysis \
--format csvImportant outputs:
case_analysis.*: one row per benchmark case.group_analysis.*: median and aggregate metrics grouped by workload/config.model_usage.*: one row per measured model usage entry.model_usage_group_analysis.*: model usage rolled up by workflow/model.
Use --detection-artifacts to provide an explicit detection artifact JSONL
sidecar. Otherwise, the analyzer reads detection-artifacts.jsonl in the
benchmark directory when present.
Analysis tables are regular CSV/Parquet files. A typical local workflow:
import pandas as pd
cases = pd.read_parquet("benchmark-runs/suite/analysis/case_analysis.parquet")
groups = pd.read_parquet("benchmark-runs/suite/analysis/group_analysis.parquet")
cols = [
"workload_id",
"config_id",
"median_pipeline_elapsed_sec",
"median_observed_total_requests",
"median_observed_total_tokens",
"median_artifact_final_entity_signature_count",
]
print(groups[cols].sort_values(["workload_id", "median_pipeline_elapsed_sec"]))
failures = cases[
(cases["case_failed"]) |
(cases["observed_failed_requests"] > 0) |
(cases["dd_trace_error_count"] > 0)
]
print(failures[["case_id", "config_id", "observed_failed_requests", "dd_trace_error_count"]])Use metrics as signals, not as a single score.
Latency and throughput:
elapsed_sec: wall time for a measured stage or workflow.pipeline_elapsed_sec: end-to-end Anonymizer wall time for a case.records_per_pipeline_sec: completed input records per pipeline second.input_text_tokens_per_pipeline_sec: input text tokens processed per pipeline second.
Model work:
observed_total_requests: measured model requests from DataDesigner or direct model workflow records.observed_total_tokens: measured input plus output tokens.observed_failed_requests: provider-level failed requests.observed_bridge_fallback_requests: sync-client fallback requests recorded from DataDesigner traces.observed_non_bridge_failed_requests: failed requests after subtracting sync-client bridge fallbacks. Prefer this field when judging endpoint reliability from trace-enabled runs.
Detection artifacts:
seed_entity_count: detector or direct-seed candidate count before validation.seed_validation_candidate_count: candidates sent to validation.estimated_seed_validation_chunk_count: estimated validator chunks from the active validation chunk size.augmented_entity_count: augmenter suggestions.augmented_new_final_value_count: augmenter suggestions that add values not already present in the seed/final set.artifact_final_detector_entity_countandartifact_final_augmenter_entity_count: final entity source counts derived from detection artifact sidecars.artifact_final_entity_signature_countandartifact_final_entity_signature_hashes: opaque final-span signatures derived from detection artifacts. These do not include raw entity values.
Safety and replacement:
original_value_leak_count: count of protected original values still present in replaced output.replacement_missing_final_entity_count: final entity occurrences whose original value has no replacement-map entry.replacement_missing_final_value_count: unique final entity values with no replacement-map entry.replacement_synthetic_original_collision_count: final entity occurrences whose original value was reused as a synthetic replacement value elsewhere in the same record.
Replace judge evaluation:
detection_valid,type_fidelity_valid,relational_consistency_valid, andattribute_fidelity_valid: per-record judge verdicts whenevaluate: trueis enabled.detection_invalid_entity_count,type_fidelity_invalid_replacement_count,relational_consistency_invalid_relation_count, andattribute_fidelity_invalid_entity_count: counts of invalid judge findings. These fields count structures returned by the judges but do not include raw values, replacement strings, or judge reasoning text.case_analysisalso includes per-case rollups for each judge family:{family}_judged_record_count,{family}_valid_record_count,{family}_valid_rate, and the corresponding invalid-count field.group_analysisincludes grouped micro-rate rollups:sum_{family}_judged_record_count,sum_{family}_valid_record_count,micro_{family}_valid_rate, andsum_{invalid_count_field}. These rates are computed from summed counts, not medians of case-level rates.