Skip to content

Commit 6b2d9b3

Browse files
committed
Add backwards compatibility for add_pattern_pair
1 parent 0ced73e commit 6b2d9b3

File tree

5 files changed

+88
-45
lines changed

5 files changed

+88
-45
lines changed

CHANGELOG.md

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5151
- Introduced a new `Aggregation` dataclass to represent both the aggregated `text` and
5252
a string identifying the `type` of aggregation (ex. "sentence", "word", "my custom
5353
aggregation")
54-
# MRKB TODO -- don't break. leave pattern_id as-is and remove 'type', so that the old
55-
remove param can remain
5654
- **BREAKING**: `BaseTextAggregator.text` now returns an `Aggregation` (instead of `str`).
5755
To update: `aggregated_text = myAggregator.text` -> `aggregated_text = myAggregator.text.text`
5856
- **BREAKING**: `BaseTextAggregator.aggregate()` now returns `Optional[Aggregation]`
@@ -65,36 +63,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6563
- `SimpleTextAggregator`, `SkipTagsAggregator`, `PatternPairAggregator` updated to
6664
produce/consume `Aggregation` objects.
6765
68-
- Augmented the `PatterPairAggregator`:
69-
- **BREAKING CHANGES**: Support for the items below resulted in two breaking changes to the
70-
`PatternPairAggregator` methods
71-
1. The `add_pattern_pair` method arguments have changed:
72-
- The `pattern_id` argument is now `type`
73-
- The `remove_match` argument has been replaced with the `action` argument. To update,
74-
change `remove_match: True` to `action: MatchAction.REMOVE` or `remove_match: False` to
75-
`action: MatchAction.KEEP`
76-
2. The `PatternMatch` type returned to handlers registered via `on_pattern_match` has been
77-
updated to subclass from the new `Aggregation` type, which means that `content` has been
78-
replaced with `text` and `pattern_id` has been replaced with `type`:
79-
```
80-
async dev on_match_tag(match: PatternMatch):
81-
pattern = match.type # instead of match.pattern_id
82-
text = match.text # instead of match.content
83-
```
84-
- `PatternPairAggregator` now supports `type` and `action` per pattern.
85-
- New `MatchAction` enum: `REMOVE`, `KEEP`, `AGGREGATE`, allowing customization for how
86-
a match should be handled.
87-
- `REMOVE`: The text along with its delimiters will be removed from the streaming text.
88-
Sentence aggregation will continue on as if this text did not exist.
89-
- `KEEP`: The delimiters will be removed, but the content between them will be kept.
90-
Sentence aggregation will continue on with the internal text included.
91-
- `AGGREGATE`: The delimiters will be removed and the content between will be treated
92-
as a separate aggregation. Any text before the start of the pattern will be
93-
returned early, whether or not a complete sentence was found. Then the pattern
94-
will be returned. Then the aggregation will continue on sentence matching after
95-
the closing delimiter is found. The content between the delimiters is not
96-
aggregated by sentence. It is aggregated as one single block of text.
97-
- `PatternMatch` now extends `Aggregation` and provides richer info to handlers.
66+
- Augmented the `PatternPairAggregator`:
67+
- Introduced a new, preferred version of `add_pattern` to support a new option for treating a
68+
match as a separate aggregation returned from `aggregate()`. This replaces the now
69+
deprecated `add_pattern_pair` method and you provide a `MatchAction` in lieu of the `remove_match` field.
70+
- `MatchAction` enum: `REMOVE`, `KEEP`, `AGGREGATE`, allowing customization for how
71+
a match should be handled.
72+
- `REMOVE`: The text along with its delimiters will be removed from the streaming text.
73+
Sentence aggregation will continue on as if this text did not exist.
74+
- `KEEP`: The delimiters will be removed, but the content between them will be kept.
75+
Sentence aggregation will continue on with the internal text included.
76+
- `AGGREGATE`: The delimiters will be removed and the content between will be treated
77+
as a separate aggregation. Any text before the start of the pattern will be
78+
returned early, whether or not a complete sentence was found. Then the pattern
79+
will be returned. Then the aggregation will continue on sentence matching after
80+
the closing delimiter is found. The content between the delimiters is not
81+
aggregated by sentence. It is aggregated as one single block of text.
82+
- `PatternMatch` now extends `Aggregation` and provides richer info to handlers.
83+
- **BREAKING**: The `PatternMatch` type returned to handlers registered via `on_pattern_match`
84+
has been updated to subclass from the new `Aggregation` type, which means that `content`
85+
has been replaced with `text` and `pattern_id` has been replaced with `type`:
86+
```
87+
async dev on_match_tag(match: PatternMatch):
88+
pattern = match.type # instead of match.pattern_id
89+
text = match.text # instead of match.content
90+
```
9891
9992
### Changed
10093
@@ -127,6 +120,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
127120
behavior, but if you want to override the aggregation behavior, you should use the new
128121
processor.
129122
123+
- Deprecated `add_pattern_pair` in the `PatternPairAggregator` which takes a `pattern_id`
124+
and `remove_match` field in favor of the new `add_pattern` method which takes a `type` and an
125+
`action`
126+
130127
### Fixed
131128
132129
- Fixed subtle issue of assistant context messages ending up with double spaces

examples/foundational/35-pattern-pair-voice-switching.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments):
110110
pattern_aggregator = PatternPairAggregator()
111111

112112
# Add pattern for voice switching
113-
pattern_aggregator.add_pattern_pair(
113+
pattern_aggregator.add_pattern(
114114
type="voice",
115115
start_pattern="<voice>",
116116
end_pattern="</voice>",

src/pipecat/extensions/ivr/ivr_navigator.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,15 +118,15 @@ def _get_conversation_history(self) -> List[dict]:
118118
def _setup_xml_patterns(self):
119119
"""Set up XML pattern detection and handlers."""
120120
# Register DTMF pattern
121-
self._aggregator.add_pattern_pair("dtmf", "<dtmf>", "</dtmf>", action=MatchAction.REMOVE)
121+
self._aggregator.add_pattern("dtmf", "<dtmf>", "</dtmf>", action=MatchAction.REMOVE)
122122
self._aggregator.on_pattern_match("dtmf", self._handle_dtmf_action)
123123

124124
# Register mode pattern
125-
self._aggregator.add_pattern_pair("mode", "<mode>", "</mode>", action=MatchAction.REMOVE)
125+
self._aggregator.add_pattern("mode", "<mode>", "</mode>", action=MatchAction.REMOVE)
126126
self._aggregator.on_pattern_match("mode", self._handle_mode_action)
127127

128128
# Register IVR pattern
129-
self._aggregator.add_pattern_pair("ivr", "<ivr>", "</ivr>", action=MatchAction.REMOVE)
129+
self._aggregator.add_pattern("ivr", "<ivr>", "</ivr>", action=MatchAction.REMOVE)
130130
self._aggregator.on_pattern_match("ivr", self._handle_ivr_action)
131131

132132
async def process_frame(self, frame: Frame, direction: FrameDirection):

src/pipecat/utils/text/pattern_pair_aggregator.py

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,16 @@ class MatchAction(Enum):
2525
"""Actions to take when a pattern pair is matched.
2626
2727
Parameters:
28-
REMOVE: Remove the matched pattern from the text.
29-
KEEP: Keep the matched pattern in the text as normal text.
30-
AGGREGATE: Return the matched pattern as a separate aggregation object.
28+
REMOVE: The text along with its delimiters will be removed from the streaming text.
29+
Sentence aggregation will continue on as if this text did not exist.
30+
KEEP: The delimiters will be removed, but the content between them will be kept.
31+
Sentence aggregation will continue on with the internal text included.
32+
AGGREGATE: The delimiters will be removed and the content between will be treated
33+
as a separate aggregation. Any text before the start of the pattern will be
34+
returned early, whether or not a complete sentence was found. Then the pattern
35+
will be returned. Then the aggregation will continue on sentence matching after
36+
the closing delimiter is found. The content between the delimiters is not
37+
aggregated by sentence. It is aggregated as one single block of text.
3138
"""
3239

3340
REMOVE = "remove"
@@ -106,7 +113,7 @@ def text(self) -> Aggregation:
106113
return Aggregation(self._text, pattern_start[1].get("type", "sentence"))
107114
return Aggregation(self._text, "sentence")
108115

109-
def add_pattern_pair(
116+
def add_pattern(
110117
self,
111118
type: str,
112119
start_pattern: str,
@@ -148,6 +155,46 @@ def add_pattern_pair(
148155
}
149156
return self
150157

158+
def add_pattern_pair(
159+
self, pattern_id: str, start_pattern: str, end_pattern: str, remove_match: bool = True
160+
):
161+
"""Add a pattern pair to detect in the text.
162+
163+
.. deprecated:: 0.0.95
164+
This function is deprecated and will be removed in a future version.
165+
Use `add_pattern` with a type and MatchAction instead.
166+
167+
This method calls `add_pattern` setting type with the provided pattern_id and action
168+
to either MatchAction.REMOVE or MatchAction.KEEP based on `remove_match`.
169+
170+
Args:
171+
pattern_id: Identifier for this pattern pair. Should be unique and ideally descriptive.
172+
(e.g., 'code', 'speaker', 'custom'). pattern_id can not be 'sentence' as that is
173+
reserved for the default behavior.
174+
start_pattern: Pattern that marks the beginning of content.
175+
end_pattern: Pattern that marks the end of content.
176+
remove_match: If True, the matched pattern will be removed from the text. (Same as MatchAction.REMOVE)
177+
If False, it will be kept and treated as normal text. (Same as MatchAction.KEEP)
178+
"""
179+
import warnings
180+
181+
with warnings.catch_warnings():
182+
warnings.simplefilter("once")
183+
warnings.warn(
184+
"add_pattern_pair with a pattern_id or remove_match is deprecated and will be"
185+
" removed in a future version. Use add_pattern with a type and MatchAction instead",
186+
DeprecationWarning,
187+
stacklevel=2,
188+
)
189+
190+
action = MatchAction.REMOVE if remove_match else MatchAction.KEEP
191+
return self.add_pattern(
192+
type=pattern_id,
193+
start_pattern=start_pattern,
194+
end_pattern=end_pattern,
195+
action=action,
196+
)
197+
151198
def on_pattern_match(
152199
self, type: str, handler: Callable[[PatternMatch], Awaitable[None]]
153200
) -> "PatternPairAggregator":

tests/test_pattern_pair_aggregator.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,11 @@ def setUp(self):
2222

2323
# Add a test pattern
2424
self.aggregator.add_pattern_pair(
25-
type="test_pattern",
25+
pattern_id="test_pattern",
2626
start_pattern="<test>",
2727
end_pattern="</test>",
28-
action=MatchAction.REMOVE,
2928
)
30-
self.aggregator.add_pattern_pair(
29+
self.aggregator.add_pattern(
3130
type="code_pattern",
3231
start_pattern="<code>",
3332
end_pattern="</code>",
@@ -119,14 +118,14 @@ async def test_multiple_patterns(self):
119118
voice_handler = AsyncMock()
120119
emphasis_handler = AsyncMock()
121120

122-
self.aggregator.add_pattern_pair(
121+
self.aggregator.add_pattern(
123122
type="voice",
124123
start_pattern="<voice>",
125124
end_pattern="</voice>",
126125
action=MatchAction.REMOVE,
127126
)
128127

129-
self.aggregator.add_pattern_pair(
128+
self.aggregator.add_pattern(
130129
type="emphasis",
131130
start_pattern="<em>",
132131
end_pattern="</em>",

0 commit comments

Comments
 (0)