Skip to content

MAINT: Updating backend to use registry to create converters#2112

Open
rlundeen2 wants to merge 9 commits into
microsoft:mainfrom
rlundeen2:rlundeen2-use-converter-identifier-dto
Open

MAINT: Updating backend to use registry to create converters#2112
rlundeen2 wants to merge 9 commits into
microsoft:mainfrom
rlundeen2:rlundeen2-use-converter-identifier-dto

Conversation

@rlundeen2

Copy link
Copy Markdown
Contributor

Summary

Makes the backend converter service build converters entirely through the registry, deleting the custom construction/introspection glue it used to carry. This is the converter analog of the target-service work (Phase 4.5): the consumer-side payoff of the converter registry landed in Phases 1–3.

  • create_converter_async resolves the class and constructs via registry.create_instance(...), which coerces plain values and resolves registry references by name. The registry is now the single build path for converters.
  • Deletes the hand-rolled glue in converter_service: _serialize_type, _build_parameter_schema, _resolve_converter_params, and inspect.signature-based introspection.
  • GET /converters/catalog and data-URI persistence read their parameter shapes from registry.get_registered_class_metadata instead of re-deriving them.
  • Every converter parameter is now expressed as one pyrit.models.Parameter, and ConverterInstance embeds a lossless serialized ConverterIdentifier (dropping the redundant backend-only DTOs).
  • Frontend create-converter panel renders straight from the catalog/Parameter shape and no longer does its own Optional[...] unwrapping.

Architecture / responsibilities

Aligns with the two-registry model documented in doc/code/registry/0_registry.md: the class registry owns "build from name + params" and the paired .instances container holds the built converters. The backend service stops owning any converter-construction responsibility and simply delegates to the registry, keeping the Parameter contract as the single source of truth across registry, backend, CLI, and frontend.

Breaking

Converter references now resolve by registry name (or an already-built instance) via registry.create_instance, replacing the old nested {"converter": {"converter_id": ...}} reference payload. The current frontend never sent that shape (it blocks LLM/composite converters), so there's no UI regression, but external API clients relying on the nested reference form must switch to string names.

Plan / phase

Part of the unified-registry redesign — see the design gist. This lands the newly-added Phase 3.5 — Converter service builds through the registry (the consumer-side payoff of Phases 1–3, mirroring Phase 4.5 for targets).

Testing

  • pytest tests/unit — 10148 passed, 121 skipped
  • Frontend npm run type-check, npm run lint, and converter jest suites (141 passed) all green

rlundeen2 and others added 9 commits June 25, 2026 19:15
…es (Phase 4)

Move TargetRegistry and ScorerRegistry off the legacy instance-only RetrievableInstanceRegistry stack onto the unified Registry + .instances shape already used by ConverterRegistry. Both registries now build instances by name (LLM scorers resolve a chat_target registry name; composite scorers and RoundRobinTarget resolve a list of names) and hold pre-configured instances under .instances.

Per the no-deprecation decision, the old singleton surface (register_instance / get_instance_by_name / reset_instance) is removed entirely and every call site is updated to the .instances.* pattern in the same change.

- Add Param build markers/aliases to scorer/target identifiers (chat_target, scorers, targets)

- Make reference resolution list-aware; collapse the resolver getter to a flat ComponentType -> Registry map; move the resolvability gate onto the Registry base

- Add TargetRegistry/ScorerRegistry under pyrit/registry/components with TargetMetadata/ScorerMetadata

- Update all production consumers, tests, and docs/notebooks

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… scenarios notebook

Address review findings on the Phase 4 target/scorer registry migration:

- microsoft#3: _resolve_registry_reference now raises a clear ValueError when a
  list-annotated reference receives a non-list value (and vice versa),
  instead of constructing the component with the wrong argument shape and
  failing obscurely downstream. Docstring Raises updated.
- microsoft#4: add tests for pre-built instances passed inside a list (passthrough),
  mixed name/instance lists, and the new shape-mismatch guards, for both
  the target and scorer registries.
- #1: refresh the stale generated output in doc/code/scenarios/0_scenarios.ipynb
  that still referenced the removed TargetRegistry.register_instance, matching
  the already-migrated source docstrings.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The legacy RetrievableInstanceRegistry had zero production subclasses after
targets and scorers moved onto the unified Registry + .instances stack. Its
get/get_entry/get_all_instances helpers are fully provided by the new-stack
DefaultInstanceRegistry. Remove the class and its exports; BaseInstanceRegistry
stays for AttackTechniqueRegistry.

Repoint the legacy-base tests' ConcreteTestRegistry onto BaseInstanceRegistry
with inline retrieval helpers (a test double) so BaseInstanceRegistry coverage
is preserved, and drop the obsolete Base/Retrievable split assertions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Eliminate the ~95% duplication across TargetRegistry, ScorerRegistry, and
ConverterRegistry by moving the shared mechanics into the base Registry:

- Default _discover() scans _discovery_package().__all__ for concrete
  _base_type() subclasses (skipping the base and abstract classes via
  inspect.isabstract) and registers each by class name.
- _get_registry_name default snaps to the class name (was snake_case);
  registries with a different scheme override it.
- New _base_type()/_discovery_package() hooks (NotImplementedError defaults)
  let registries with bespoke discovery override _discover() instead.

The three component registries now only supply _base_type, _discovery_package,
_identifier_type, _metadata_class, plus their .instances container and any
projected metadata properties. Abstract-base skipping is now uniform across
all three. Updated the base Registry test to the class-name default.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…r service test

The converter-service mock helper set `mock_target.__class__.__name__ =
'MockChatTarget'` on a `MagicMock(spec=PromptTarget)`. A spec'd mock returns
the real spec class from `__class__` (so isinstance works), so this mutated the
global `PromptTarget.__name__` permanently. Any later test reading that name —
e.g. TargetRegistry's instance-type rejection message — then saw 'MockChatTarget',
making test_register_instance_rejects_non_target fail in the full suite.

The mock's identifier already carries class_name='MockChatTarget' via
get_identifier(), so the line was redundant; remove it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Task 1: ConverterInstance composes ConverterIdentifier instead of
duplicating its fields.

Task 2: Make pyrit.models.Parameter the single JSON-serializable
parameter descriptor reused across the converter, scenario, and
initializer domains, the backend API, and the CLI. Collapses the
near-identical ConverterParameterSchema, scenario parameter metadata,
and InitializerParameterSummary onto Parameter. Serialization projects
the live param_type into display fields (type_name/choices/is_list).

List-valued defaults are preserved as a list of display strings instead
of being flattened to a single string, so list[str] params round-trip.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… converter/Parameter unification branch

# Conflicts:
#	pyrit/registry/components/converter_registry.py
Resolved 3 conflicts:
- converter_service.py: kept origin/main's DEFAULT_MEDIA_EXTENSIONS media
  consolidation; both sides' extra typing imports were orphaned by the merge,
  so the import is reduced to the only symbol still used (Any).
- pyrit_scan.py: kept origin/main's Parameter-based scenario coercer (microsoft#2092),
  which supersedes this branch's _SCALAR_TYPE_COERCERS approach toward the same
  typed-coercion goal.
- test_pyrit_scan.py: migrated origin/main's new coercion tests from the
  pre-unification "param_type" payload field to the canonical "type_name"/
  "is_list"/"choices" wire format the backend now emits.

Full unit suite: 10045 passed, 121 skipped.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… into branch

Pulls origin/main, which migrated the target and scorer registries onto
the unified Registry base and moved the CLI onto pyrit.models, into the
converter/Parameter unification branch.

Conflict resolution + finalization:
- AttackTechniqueRegistry moved onto the unified Registry base; affected
  scenario/setup tests updated to reset_registry_singleton().
- Unify scenario/initializer catalog DTOs onto pyrit.models.Parameter:
  dropped ScenarioParameterSummary / InitializerParameterSummary in favor
  of list[Parameter] on RegisteredScenario / RegisteredInitializer, so the
  one reusable Parameter descriptor is used across registry, backend, and
  CLI.
- Parameter is now round-trippable: a model_validator(mode="before")
  reconstructs the live param_type from the serialized display fields
  (type_name / choices / is_list), so REST clients (the CLI) deserialize
  catalog payloads straight into coercion-ready Parameter objects.
- CLI build_parameters_from_api simplified to a pass-through; _output and
  pyrit_scan read Parameter.type_name. CLI tests build typed Parameter
  instances instead of the removed Summary DTOs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@romanlutz romanlutz self-assigned this Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants