Task Views

Overview

By default the log viewer chooses sensible defaults for how a task’s samples are listed, how their scores are summarized, and how scanner results are displayed. For your own task, however you have a better idea which scores matter, which columns are worth showing, and more. Your task can customize the view directly by using the viewer arg of the Task.

For example, the following customizes the Task’s view by passing a set of columns, a default sort, and enables multiline display (which may be better for long textual fields):

from inspect_ai import Task, task
from inspect_ai.scorer import match
from inspect_ai.viewer import (
    TaskSamplesColumn,
    TaskSamplesSort,
    TaskSamplesView,
    ViewerConfig,
)

@task
def popularity():
    return Task(
        name="popularity",
        dataset=dataset,
        scorer=match(location="any"),
        viewer=ViewerConfig(
            task_samples_view=TaskSamplesView(
                name="Default",
                columns=[
                    TaskSamplesColumn(id="sampleId"),
                    TaskSamplesColumn(id="input"),
                    TaskSamplesColumn(id="target"),
                    TaskSamplesColumn(id="answer"),
                    TaskSamplesColumn.score("match"),
                    TaskSamplesColumn(id="sampleStatus", visible=False),
                    TaskSamplesColumn(id="tokens", visible=False),
                    TaskSamplesColumn(id="duration", visible=False),
                ],
                sort=[
                    TaskSamplesSort.score("match", dir="asc"),
                    TaskSamplesSort(column="sampleId", dir="asc"),
                ],
                multiline=True,
            )
        ),
    )

The configuration that you specify in your task will be serialized into the eval log when it is written, so the configuration will be used for anyone viewing the log file produced using the configuration.

ViewerConfig covers four areas, each configured independently:

Field Configures
task_samples_view The task’s sample list — the grid of samples shown for an eval log.
sample_score_view The score panel in an individual sample’s score display.
scanner_result_view The scanner results sidebar (for scanners).
trust_content Whether the log’s content may be rendered richly (see Untrusted Content).
Note

These settings are defaults, not overrides. The viewer honors them only until a reviewer customizes the view in their own browser. The resolution priority is user override > view configuration > viewer built-in, so a user’s hand-picked columns or sort always win and are never clobbered by the eval default.

The Sample List

task_samples_view configures the grid of samples shown for an eval log. A TaskSamplesView requires a name and accepts optional column, sort, and presentation settings — any field left unset falls back to the viewer’s built-in behaviour.

from inspect_ai.viewer import (
    ViewerConfig,
    TaskSamplesView,
    TaskSamplesColumn,
    TaskSamplesSort,
)

ViewerConfig(
    task_samples_view=TaskSamplesView(
        name="Triage",
        columns=[
            TaskSamplesColumn(id="sampleId"),
            TaskSamplesColumn(id="input"),
            TaskSamplesColumn.score("my_scorer"),
            TaskSamplesColumn(id="target", visible=False),
        ],
        sort=[TaskSamplesSort.score("my_scorer", dir="desc")],
        multiline=True,
    )
)
1
Controls the order of these columns.
2
Hide these columns by default.
3
Sort by my_scorer’s value, descending.
4
Enable multiline display.

Columns

You can control the order and visibility of columns within the task’s sample list by passing a list of TaskSamplesColumn to columns. The order of the columns in this list is the default order for the columns in the sample view. Use visible=False on a column to hide it by default. Leaving columns as None uses the viewer’s built-in column set for that log’s shape.

All samples have the following built-in columns available:

sampleStatus, sampleId, sampleUuid, epoch, input, target, answer, tokens, duration, retries, error, limit.

Score columns

Score columns are addressed by scorer name with the score() helpers. For simple scorers, you can just pass the scorer name:

TaskSamplesColumn.score("my_scorer")
TaskSamplesSort.score("my_scorer", dir="desc")

Pass the score you’d like to target when a scorer emits a dictionary of named sub-scores:

TaskSamplesColumn.score("my_scorer", "calibration")

Sort

sort is a list of TaskSamplesSort entries (each a column id and a dir of "asc" or "desc"), applied in order. Use TaskSamplesSort.score(...) to sort on a score column.

Score labels

score_labels renames score column headers for display, keyed by score name. Lookup falls back to the scorer name when no override is set.

TaskSamplesView(
    name="Audit",
    score_labels={
        "situational_awareness": "Situational Awareness",
        "ascii-art": "ASCII Art",
    },
)

Color scales

Numeric score cells can be shaded with a background heat scale. score_color_scales is keyed by score name, and each entry is either a named palette, a ScoreColorScale (a palette with an explicit value range), or — for categorical scores — a map from value to a semantic role.

from inspect_ai.viewer import TaskSamplesView, ScoreColorScale

TaskSamplesView(
    name="Heat",
    score_color_scales={
        # named palette anchored to the observed data range
        "accuracy": "good-high",
        # palette pinned to a known 1..10 rubric so middling values
        # aren't paint-clamped when the data clusters at one end
        "concerning": ScoreColorScale(palette="good-low", min=1, max=10),
        # categorical score → semantic roles
        "verdict": {"yes": "bad", "no": "good", "maybe": "warn"},
    },
    color_scales_enabled=True,
)

Numeric palettes map a value’s position in the range to a colour along a gradient (low → high):

Palette Low → High Use when
good-high higher is better (e.g. accuracy)
good-low lower is better (e.g. error rate)
neutral magnitude only, no good/bad signal
diverging signed values centred on the midpoint

Categorical roles assign a fixed colour to each mapped value:

Role Colour Conveys
good green a positive / desired outcome
bad red a negative / undesired outcome
warn amber a borderline / needs-attention outcome
info blue a neutral, informational value
muted grey a low-salience value

The swatches above approximate light mode; the actual cell colours are theme variables that adapt to light / dark. Pass/fail and boolean scores ignore this config — their pre-coloured pills already encode the result. color_scales_enabled seeds the toolbar heatmap toggle’s initial state.

Row layout

multiline controls row density: True (the default) gives list-style multi-line rows; False gives compact single-line rows. compact_scores narrows score columns and rotates their headers 45° to fit many scores side by side.

The Score Panel

sample_score_view configures the score panel shown in an individual sample’s detail view (distinct from the score columns in the sample list above). It applies when a sample has three or more scores.

‘Chips’ score panel

‘Chips’ score panel
from inspect_ai.viewer import ViewerConfig, SampleScoreView, SampleScoreViewSort

ViewerConfig(
    sample_score_view=SampleScoreView(
        default="chips",
        sort=SampleScoreViewSort(column="value", dir="desc"),
    )
)
  • default chooses the initial rendering mode: chips (wrapping pills) or grid (a sortable table). When unset, the viewer picks based on the number of scores.
  • sort sets the initial sort by name (scorer name) or value, with a direction of asc or desc.

Scanner Results

scanner_result_view customizes the scanner results sidebar for samples produced with scanners. It is a glob-keyed map from scanner-name pattern to a ScannerResultView, so you can target specific scanners or apply one configuration to all of them:

from inspect_ai.viewer import (
    ViewerConfig,
    ScannerResultView,
    ScannerResultField,
    MetadataField,
)

ViewerConfig(
    scanner_result_view={
        "*": ScannerResultView(
            fields=[
                ScannerResultField(name="explanation", label="Rationale"),
                MetadataField(key="summary", label="Summary"),
                "value",
            ],
            exclude_fields=["validation"],
        ),
    }
)

Keys are fnmatch-style globs ("*", "audit_*", or exact scanner names). As a shorthand, you can pass a bare ScannerResultView instead of a map to apply a single configuration to every scanner.

fields is an ordered list of sections to render; entries are:

  • A ScannerResultField — a built-in section (explanation, label, value, validation, answer, or metadata), with an optional label override and collapsed default.
  • A MetadataField — promotes a single metadata[key] entry into its own top-level section.
  • A bare string — shorthand for the matching ScannerResultField name.

exclude_fields subtracts sections from the resolved set. Excluding a MetadataField also removes that key from the generic metadata dump.

Untrusted Content

By default the viewer renders log content richly: model output and scores are shown as markdown (with math and syntax highlighting), terminal output keeps its colors, and images, audio, and video are displayed. When the content comes from a model you can’t trust (for example, during pre-deployment testing of a model without safeguards), set trust_content=False to have the viewer show all of the log’s content as plain text instead:

@task
def untrusted_eval():
    return Task(
        dataset=...,
        solver=...,
        viewer=ViewerConfig(trust_content=False),
    )

For such logs, the viewer:

  • Shows markdown, math, and code as their literal source text.
  • Shows terminal escape sequences rather than interpreting them.
  • Replaces images, audio, video, and terminal recordings with a placeholder.
  • Shows links as text, with their destination, rather than making them clickable.
  • Makes invisible and bidirectional-override characters visible (e.g. ⟨U+202E⟩).

Unlike the other settings, this isn’t a default that a reviewer’s preferences override. To mark a task you didn’t write as untrusted, use task_with(). Since viewer replaces the task’s whole viewer configuration, copy the existing one to keep its other settings:

from inspect_ai import task_with

task = existing_task()
untrusted = task_with(
    task,
    viewer=(task.viewer or ViewerConfig()).model_copy(
        update={"trust_content": False}
    ),
)

Retrying an eval (eval_retry() or inspect eval-retry) keeps its logs untrusted.

Viewing Untrusted Logs

To show every log as untrusted, whatever its own setting (for example, logs written before this setting existed, or by a task that doesn’t set it), start the viewer with --no-trust-content:

$ inspect view --no-trust-content

You can also set INSPECT_VIEW_TRUST_CONTENT=false in the environment inspect view runs in. From Python, pass trust_content=False to view().

This option applies only to the inspect view server. Bundled and embedded viewers (inspect view bundle, inspect view embed, eval_set(embed_viewer=True)) rely on each log’s own ViewerConfig(trust_content=False).

The viewer option only lowers trust. Leaving it unset, or passing --trust-content, defers to each log, and a log that sets trust_content=False is never shown richly.

Note

trust_content is applied by the viewer when it reads the log. Viewers that predate the setting (including older versions of the VS Code extension) ignore it, and older versions of Inspect drop it if they rewrite the log. Scout does not yet honor it. The setting controls rendering only; it doesn’t make the content safe to copy or act on.

Reference

See the inspect_ai.viewer reference for the complete field list of every type described here.