Skip to content

Commit 91c4bc6

Browse files
chuenchen309claudeanakin87
authored
fix: match literal MIME types with regex metacharacters in DocumentTypeRouter (#11973)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Stefano Fiorucci <stefanofiorucci@gmail.com>
1 parent 01c11de commit 91c4bc6

3 files changed

Lines changed: 33 additions & 3 deletions

File tree

haystack/components/routers/document_type_router.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,12 @@ def run(self, documents: list[Document]) -> dict[str, list[Document]]:
134134

135135
matched = False
136136
if mime_type:
137-
for pattern in self._mime_type_patterns:
138-
if pattern.fullmatch(mime_type):
139-
mime_types[pattern.pattern].append(doc)
137+
for bucket_key, pattern in zip(self.mime_types, self._mime_type_patterns, strict=True):
138+
# Match an exact MIME type first, so literal types containing regex
139+
# metacharacters (e.g. the '+' in 'image/svg+xml') are not misread as
140+
# a regex; fall back to regex matching for patterns like 'audio/.*'.
141+
if mime_type == bucket_key or pattern.fullmatch(mime_type):
142+
mime_types[bucket_key].append(doc)
140143
matched = True
141144
break
142145
if not matched:
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
fixes:
3+
- |
4+
Fixed ``DocumentTypeRouter`` misrouting documents whose MIME type contains a
5+
regex metacharacter. A declared type such as the standard IANA ``image/svg+xml``
6+
was compiled as a regex, so the ``+`` was treated as a quantifier and the
7+
document fell into ``unclassified`` instead of its own bucket. Declared MIME
8+
types are now matched by exact equality first, falling back to regex matching
9+
so patterns like ``audio/.*`` keep working.

test/components/routers/test_document_type_router.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,24 @@ def test_run_with_mime_type_meta_field(self):
107107
assert result["audio/x-wav"][0].content == "Audio content"
108108
assert result["unclassified"][0].content == "Unknown type"
109109

110+
def test_run_with_literal_mime_type_containing_regex_metacharacter(self):
111+
# 'image/svg+xml' is a standard IANA type; the '+' must be treated as a
112+
# literal, not a regex quantifier. A regex pattern like 'audio/.*' must
113+
# still match by regex.
114+
docs = [
115+
Document(content="An SVG", meta={"mime_type": "image/svg+xml"}),
116+
Document(content="Some audio", meta={"mime_type": "audio/mpeg"}),
117+
]
118+
119+
router = DocumentTypeRouter(mime_type_meta_field="mime_type", mime_types=["image/svg+xml", "audio/.*"])
120+
result = router.run(documents=docs)
121+
122+
assert "unclassified" not in result
123+
assert len(result["image/svg+xml"]) == 1
124+
assert result["image/svg+xml"][0].content == "An SVG"
125+
assert len(result["audio/.*"]) == 1
126+
assert result["audio/.*"][0].content == "Some audio"
127+
110128
def test_run_with_file_path_meta_field(self):
111129
docs = [
112130
Document(content="Example text", meta={"file_path": "example.txt"}),

0 commit comments

Comments
 (0)