Skip to content

FEAT add TargetConfiguration & pieces#1573

Open
hannahwestra25 wants to merge 9 commits intomicrosoft:mainfrom
hannahwestra25:hawestra/add_target_config_pieces
Open

FEAT add TargetConfiguration & pieces#1573
hannahwestra25 wants to merge 9 commits intomicrosoft:mainfrom
hannahwestra25:hawestra/add_target_config_pieces

Conversation

@hannahwestra25
Copy link
Copy Markdown
Contributor

Description

This PR is 2 of ~5 PRs to introduce/integrate TargetConfiguration. This PR adds TargetConfiguration and the necessary pieces within it. This will replace the current pattern where capability checks, normalization, and error handling are scattered across targets, attacks, converters, and scorers.
NOTE: this just adds the classes, does not replace any of the current logic nor is it used anywhere in this PR
New classes:

  1. UnsupportedCapabilityBehavior: ADAPT or RAISE per missing capability
  2. CapabilityHandlingPolicy: Frozen dataclass mapping capabilities to UnsupportedCapabilityBehavior
  3. ConversationNormalizationPipeline: Resolves capability gaps into an ordered normalizer tuple used before the conversation is sent to the target to produce a conversation which the target can handle

Full spec: https://microsoft-my.sharepoint.com/:w:/p/hannahwestra/cQoo3V80Zd4FTafKPqjDmr4vEgUCBfTfToMrXUEegSaVhFPj2A

Tests and Documentation

added tests

@hannahwestra25 hannahwestra25 marked this pull request as ready for review April 6, 2026 21:27
"""The resolved normalization pipeline."""
return self._pipeline

def supports(self, *, capability: CapabilityName) -> bool:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to differentiate between "natively supports" (i.e. this method) and "can support" , i.e. the current requires method ( which I also have a comment on maybe renaming)

I can imagine why someone might be interested to know if a target can handle their inputs natively, or needs some preprocessing (normalization) to handle it. Is that the idea behind having these 2 methods?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the idea behind these two is to differentiate so I updated the naming to includes (per your other comment). I thought about renaming this one to natively supports but the "supports" seems redundant as you noted in your other comment. I renamed the other function as well and updated the description of this function. I could rename to natively_includes ? or something like that ? wdyt ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like includes too for this, I think it does imply it is natively defined on the target and now easy to contrast with ensure_can_handle. Thank you!

Copy link
Copy Markdown
Contributor

@behnam-o behnam-o left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

except KeyError as exc:
raise AttributeError(capability.value) from exc

def __getattr__(self, name: str) -> UnsupportedCapabilityBehavior:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this return type be changed to NoReturn ?

Returns:
list[Message]: The (possibly adapted) message list.
"""
result = list(messages)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this needed?

try:
return self.behaviors[capability]
except KeyError as exc:
raise AttributeError(capability.value) from exc
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: why AttributeError? this is just a KeyError right?

)


def _default_normalizers() -> dict[CapabilityName, MessageListNormalizer[Message]]:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wonder if this can be called and saved as a constant instead of calling each time? Are there any race conditions (i.e. Normalizer state is modified across calls)?

Comment on lines +56 to +57
CapabilityName.MULTI_TURN: UnsupportedCapabilityBehavior.RAISE,
CapabilityName.SYSTEM_PROMPT: UnsupportedCapabilityBehavior.RAISE,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be consistent with keys in _NORMALIZER_REGISTRY?

Comment on lines +35 to +36
CapabilityName.SYSTEM_PROMPT: _NormalizerRegistryEntry(order=0, normalizer_factory=GenericSystemSquashNormalizer),
CapabilityName.MULTI_TURN: _NormalizerRegistryEntry(order=1, normalizer_factory=HistorySquashNormalizer),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want to add a unit test or check that all the orders are unique?

Comment on lines +17 to +30
_ADAPT_ALL = CapabilityHandlingPolicy(
behaviors={
CapabilityName.SYSTEM_PROMPT: UnsupportedCapabilityBehavior.ADAPT,
CapabilityName.MULTI_TURN: UnsupportedCapabilityBehavior.ADAPT,
CapabilityName.JSON_SCHEMA: UnsupportedCapabilityBehavior.RAISE,
CapabilityName.JSON_OUTPUT: UnsupportedCapabilityBehavior.RAISE,
CapabilityName.MULTI_MESSAGE_PIECES: UnsupportedCapabilityBehavior.RAISE,
CapabilityName.EDITABLE_HISTORY: UnsupportedCapabilityBehavior.RAISE,
}
)


def _make_message(role: ChatMessageRole, content: str) -> Message:
return Message(message_pieces=[MessagePiece(role=role, original_value=content)])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: create shared conftest test fixtures?

Copy link
Copy Markdown
Contributor

@jsong468 jsong468 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for a great design spec and clean implementation :)

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.

3 participants