Skip to content

feat(annotation): configurable per-field markdown rendering#231

Closed
henrycgbaker wants to merge 1 commit into
feat/annotation-logical-constraints/05-export-summary-by-idfrom
feat/configurable-field-rendering
Closed

feat(annotation): configurable per-field markdown rendering#231
henrycgbaker wants to merge 1 commit into
feat/annotation-logical-constraints/05-export-summary-by-idfrom
feat/configurable-field-rendering

Conversation

@henrycgbaker

Copy link
Copy Markdown
Collaborator

Summary

Adds AnnotationSettings.field_render_mode (deployment / workspace / task scopes, sparse-dict overlay) controlling use_markdown on each rg.TextField. Lets a deployment opt selected fields into Argilla's markdown renderer — which also handles inline raw HTML — so content from pipelines that emit HTML (e.g. publikationsbot) displays as HTML in the annotator UI rather than escaping.

Mirrors the constraint_severity pattern from #216 very closely:

constraint_severity field_render_mode
Shape dict[str, Severity] dict[str, FieldRenderMode]
Default factory populated with all constraint IDs populated with all known field names → "plain"
Merge validator overlays user values onto defaults overlays user values onto defaults
Key validator rejects unknown constraint IDs rejects unknown field names (scope-aware)
Resolver method resolved_severity(ws, constraint_id) resolved_render_mode(ws, task, field_name)
Scope deployment + workspace deployment + workspace + task

The one difference: field_render_mode adds a per-task scope (TaskSettings.field_render_mode) since \"answer\" belongs to different tasks (grounding vs. generation) and a deployment might legitimately want them rendered differently. constraint_severity skips per-task because severity is per-constraint, not per-task — that reason doesn't apply here.

Files changed

  • core/schemas/annotation_task.py — adds FieldRenderMode type + TEXT_FIELDS registry
  • core/settings/annotation_settings.py — adds field_render_mode to all 3 scope classes, plus resolved_render_mode(), _merge_field_render_mode_defaults, _validate_field_render_mode_keys
  • core/annotation/argilla_task_definitions.py — local md() helper inside build_task_settings, wires use_markdown=md(task, name) into the 6 rg.TextField calls
  • core/annotation/collapsible_field.html — flips record-content slot from textContent to innerHTML so the widget renders pipeline HTML consistently with the main TextField (trust assumption: first-party content)
  • docs/decisions/0009-annotation-schema-configurability.md — addendum: rendering is operational, not schema (in the same layer as min_submitted, constraint_severity, locale); the hardcoded constraint on fields/questions/labels still holds
  • Tests:
    • tests/unit/core/settings/test_annotation_settings.pyTestFieldRenderModeDefaults, TestResolvedRenderMode, TestWorkspaceFieldRenderModeValidation, TestTaskFieldRenderModeValidation
    • tests/unit/core/annotation/test_argilla_task_definitions.pyTestTextFieldsRegistryMatchesActualTextFields (drift check), TestUseMarkdownWireUp (propagation across 3 scopes)

Example YAML

# Deployment-wide: render HTML answers as markdown
field_render_mode:
  answer: markdown

# Or with per-workspace / per-task overrides:
workspaces:
  grounding:
    field_render_mode:
      context_set: markdown
    tasks:
      grounding:
        field_render_mode:
          answer: plain  # override the deployment default just here

Dependency chain

Branched on feat/annotation-logical-constraints/05-export-summary-by-id (#217 head). Depends on #213#214#215#216#217 landing first. The work itself is independent of those changes' content but uses:

Test plan

  • pytest tests/unit/ — 754 pass, no regressions
  • Manual: on a fresh test deployment, set field_render_mode: {answer: markdown} in YAML, run pragmata annotation setup, verify resulting Argilla datasets have use_markdown=True on grounding/generation answer fields (already demoed manually on a downstream demo branch).
  • Manual: confirm collapsible widget renders embedded HTML correctly when fed pipeline content.

Adds AnnotationSettings.field_render_mode (deployment / workspace / task
scopes, sparse-dict overlay) that controls use_markdown on each
rg.TextField. Mirrors the constraint_severity pattern with the per-task
scope extended (since each field name belongs to specific tasks).

Defaults to "plain" everywhere; users opt fields into "markdown" via YAML
to let Argilla render pipeline-emitted HTML (e.g. publikationsbot output)
in the annotator UI. Schema (which fields exist) stays hardcoded per
ADR-0009; rendering is an operational layer above it (see ADR addendum).

Also flips the collapsible_field.html widget to use innerHTML for
record-supplied content so embedded HTML renders there too. Trust
assumption: record content comes from a first-party pipeline.

TEXT_FIELDS registry in core/schemas/annotation_task.py is the source
of truth for which fields can be configured; a unit test asserts it
matches the rg.TextField names actually constructed in build_task_settings.
@github-actions github-actions Bot added feature Adds or expands user-facing functionality size: M 200-499 LOC labels May 27, 2026
@henrycgbaker

Copy link
Copy Markdown
Collaborator Author

Splitting into #232 (TextField config) and #233 (widget HTML, WIP).

@henrycgbaker henrycgbaker deleted the feat/configurable-field-rendering branch May 27, 2026 22:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Adds or expands user-facing functionality size: M 200-499 LOC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant