attachments 0.2.0__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. attachments-0.2.2/PKG-INFO +132 -0
  2. attachments-0.2.2/README.md +93 -0
  3. {attachments-0.2.0 → attachments-0.2.2}/pyproject.toml +1 -1
  4. {attachments-0.2.0 → attachments-0.2.2}/src/attachments/renderers.py +33 -19
  5. attachments-0.2.2/src/attachments.egg-info/PKG-INFO +132 -0
  6. attachments-0.2.0/PKG-INFO +0 -237
  7. attachments-0.2.0/README.md +0 -198
  8. attachments-0.2.0/src/attachments.egg-info/PKG-INFO +0 -237
  9. {attachments-0.2.0 → attachments-0.2.2}/LICENSE +0 -0
  10. {attachments-0.2.0 → attachments-0.2.2}/setup.cfg +0 -0
  11. {attachments-0.2.0 → attachments-0.2.2}/src/attachments/__init__.py +0 -0
  12. {attachments-0.2.0 → attachments-0.2.2}/src/attachments/audio_processing.py +0 -0
  13. {attachments-0.2.0 → attachments-0.2.2}/src/attachments/config.py +0 -0
  14. {attachments-0.2.0 → attachments-0.2.2}/src/attachments/core.py +0 -0
  15. {attachments-0.2.0 → attachments-0.2.2}/src/attachments/detectors.py +0 -0
  16. {attachments-0.2.0 → attachments-0.2.2}/src/attachments/exceptions.py +0 -0
  17. {attachments-0.2.0 → attachments-0.2.2}/src/attachments/image_processing.py +0 -0
  18. {attachments-0.2.0 → attachments-0.2.2}/src/attachments/office_contact_sheet.py +0 -0
  19. {attachments-0.2.0 → attachments-0.2.2}/src/attachments/parsers.py +0 -0
  20. {attachments-0.2.0 → attachments-0.2.2}/src/attachments/utils.py +0 -0
  21. {attachments-0.2.0 → attachments-0.2.2}/src/attachments.egg-info/SOURCES.txt +0 -0
  22. {attachments-0.2.0 → attachments-0.2.2}/src/attachments.egg-info/dependency_links.txt +0 -0
  23. {attachments-0.2.0 → attachments-0.2.2}/src/attachments.egg-info/requires.txt +0 -0
  24. {attachments-0.2.0 → attachments-0.2.2}/src/attachments.egg-info/top_level.txt +0 -0
  25. {attachments-0.2.0 → attachments-0.2.2}/tests/test_audio_processing.py +0 -0
  26. {attachments-0.2.0 → attachments-0.2.2}/tests/test_base.py +0 -0
  27. {attachments-0.2.0 → attachments-0.2.2}/tests/test_core_functionality.py +0 -0
  28. {attachments-0.2.0 → attachments-0.2.2}/tests/test_document_parsing.py +0 -0
  29. {attachments-0.2.0 → attachments-0.2.2}/tests/test_html_parsing.py +0 -0
  30. {attachments-0.2.0 → attachments-0.2.2}/tests/test_image_processing.py +0 -0
  31. {attachments-0.2.0 → attachments-0.2.2}/tests/test_indexing_slicing.py +0 -0
  32. {attachments-0.2.0 → attachments-0.2.2}/tests/test_markdown_rendering.py +0 -0
  33. {attachments-0.2.0 → attachments-0.2.2}/tests/test_office_contact_sheet.py +0 -0
  34. {attachments-0.2.0 → attachments-0.2.2}/tests/test_pdf_parsing.py +0 -0
  35. {attachments-0.2.0 → attachments-0.2.2}/tests/test_pptx_parsing.py +0 -0
  36. {attachments-0.2.0 → attachments-0.2.2}/tests/test_simple.py +0 -0
@@ -0,0 +1,132 @@
1
+ Metadata-Version: 2.4
2
+ Name: attachments
3
+ Version: 0.2.2
4
+ Summary: A Python library to handle various file types for LLMs.
5
+ Author-email: Maxim Rivest <mrive052@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/yourusername/attachments
8
+ Project-URL: Bug Tracker, https://github.com/yourusername/attachments/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: Pillow
15
+ Requires-Dist: python-dotenv
16
+ Requires-Dist: pytest
17
+ Requires-Dist: beautifulsoup4
18
+ Requires-Dist: html2text
19
+ Requires-Dist: markdownify
20
+ Requires-Dist: lxml
21
+ Requires-Dist: python-magic
22
+ Requires-Dist: mammoth
23
+ Requires-Dist: PyMuPDF
24
+ Requires-Dist: python-pptx
25
+ Requires-Dist: python-docx
26
+ Requires-Dist: odfpy
27
+ Requires-Dist: pillow-heif
28
+ Requires-Dist: pydub
29
+ Requires-Dist: SpeechRecognition
30
+ Requires-Dist: markitdown[all]>=0.1.1
31
+ Requires-Dist: pypdfium2
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest; extra == "dev"
34
+ Requires-Dist: flake8; extra == "dev"
35
+ Requires-Dist: mypy; extra == "dev"
36
+ Requires-Dist: black; extra == "dev"
37
+ Requires-Dist: filelock; extra == "dev"
38
+ Dynamic: license-file
39
+
40
+ # Attachments
41
+ ## Easiest way to give context to LLMs
42
+
43
+ Attachments has the ambition to be the general funnel for any files to be transformed into images+text for large language models context. For this to be true we, has a community, have to stop implementing this funnel (file -> images+text) each on our own. If you have a detector + parser + renderer contibute it! This will help you and us. Don't let your code alone in a dark corner of you computer.
44
+
45
+ ## Very quickstart
46
+
47
+ ```bash
48
+ > pip install attachments
49
+ ```
50
+
51
+ Most users will not have to learn anything more then that: `Attachments("path/to/file.pdf")`
52
+
53
+ ```python
54
+ from attachments import Attachments
55
+
56
+ a = Attachment("/the/path/or/url/to/your/to/the/content/you/want/to/give/the/llm.xlsx", "another.pdf", "another.pptx"...)
57
+
58
+ prompt_ready_context = str(a)
59
+ images_ready_for_llm = a.images
60
+ ```
61
+
62
+ That is really just it!
63
+
64
+ You can print it `print(a)`, you can interpolate it `f"the content is {a}"` you can string it `str(a)`. This will give you something very good to give the llm so that the AI can consider you content.
65
+
66
+ Nowadays, most genAI models comes with the ability to see images too. So you also have all attachments in images forms by using `a.images`.
67
+ This is a list of base64 encoded images, this is the fundamental format the most llm provider accept.
68
+
69
+ The simplest way to use and think about attachments is that if you want to put you best foot forward and up you chances that the llm grok your content you should pass all of the text in `a` to the prompt and all of the images in `a.images` in the image input. We aim for making those two as 'prompt engineered' as possible. *Attachments* is young but already very powerful and used in production. The api will not change. Maybe advanced feature and syntax will be added but the core will stay the same. Mostly we will support more file types and we will have better rendering for better performance and with less extreneous tokens.
70
+
71
+ ## How to give attachments to openai llms?
72
+
73
+ ```python
74
+ from openai import OpenAI
75
+ from attachments import Attachments
76
+
77
+ pdf_attachment = Attachments("https://github.com/microsoft/markitdown/raw/refs/heads/main/packages/markitdown/tests/test_files/test.pdf")
78
+
79
+ prompt = f"""
80
+ Analyze the following documents:
81
+ {pdf_attachment}
82
+ """
83
+
84
+ content = [{"type": "input_image","image_url": image} for image in pdf_attachment.images] + \
85
+ [{"type": "input_text", "text": prompt}]
86
+
87
+ response = OpenAI().responses.create(model="gpt-4.1-nano", input=[{"role": "user", "content": content}])
88
+ response.output_text
89
+ ```
90
+
91
+ ## Usage
92
+
93
+ ### Basic Initialization
94
+ Create an `Attachments` object by passing one or more local file paths or URLs. Image processing commands can be appended to image paths.
95
+
96
+ ```python
97
+ from attachments import Attachments
98
+
99
+ # Initialize with various local files, URLs, and image processing commands
100
+ a = Attachments(
101
+ "docs/report.pdf",
102
+ "images/diagram.png[resize:400xauto]",
103
+ "https://www.example.com/article.html",
104
+ "photos/vacation.heic[rotate:90,format:jpeg,quality:80]"
105
+ )
106
+
107
+ # The library will download URLs, process files, and extract content.
108
+ ```
109
+
110
+ ### Indexing Attachments
111
+ You can get a new `Attachments` object containing a subset of the original attachments using integer or slice indexing:
112
+ ```python
113
+ first_attachment = a[0]
114
+ first_two_attachments = a[0:2]
115
+
116
+ print(f"Selected attachment: {first_attachment}")
117
+ ```
118
+
119
+ ### Page/Slide Selection
120
+ Specify pages for PDFs or slides for PPTX files using bracket notation in the path string:
121
+ ```python
122
+ # Process only page 1 and pages 3 through 5 of a PDF
123
+ specific_pages_pdf = Attachments("long_document.pdf[1,3-5]")
124
+
125
+ # Process the first three slides and the last slide of a presentation
126
+ specific_slides_pptx = Attachments("presentation.pptx[:3,N]")
127
+ # 'N' refers to the last page/slide. Negative indexing like [-1:] also works.
128
+ ```
129
+
130
+ ## Supported File Types
131
+ * **Documents**: PDF (`.pdf`), PowerPoint (`.pptx`), Excel (`.xlsx`), Word (`.docx`), much more to come!
132
+ * **Images**: JPEG (`.jpg`, `.jpeg`), PNG (`.png`), GIF (`.gif`), BMP (`.bmp`), WEBP (`.webp`), TIFF (`.tiff`), HEIC (`.heic`), HEIF (`.heif`)
@@ -0,0 +1,93 @@
1
+ # Attachments
2
+ ## Easiest way to give context to LLMs
3
+
4
+ Attachments has the ambition to be the general funnel for any files to be transformed into images+text for large language models context. For this to be true we, has a community, have to stop implementing this funnel (file -> images+text) each on our own. If you have a detector + parser + renderer contibute it! This will help you and us. Don't let your code alone in a dark corner of you computer.
5
+
6
+ ## Very quickstart
7
+
8
+ ```bash
9
+ > pip install attachments
10
+ ```
11
+
12
+ Most users will not have to learn anything more then that: `Attachments("path/to/file.pdf")`
13
+
14
+ ```python
15
+ from attachments import Attachments
16
+
17
+ a = Attachment("/the/path/or/url/to/your/to/the/content/you/want/to/give/the/llm.xlsx", "another.pdf", "another.pptx"...)
18
+
19
+ prompt_ready_context = str(a)
20
+ images_ready_for_llm = a.images
21
+ ```
22
+
23
+ That is really just it!
24
+
25
+ You can print it `print(a)`, you can interpolate it `f"the content is {a}"` you can string it `str(a)`. This will give you something very good to give the llm so that the AI can consider you content.
26
+
27
+ Nowadays, most genAI models comes with the ability to see images too. So you also have all attachments in images forms by using `a.images`.
28
+ This is a list of base64 encoded images, this is the fundamental format the most llm provider accept.
29
+
30
+ The simplest way to use and think about attachments is that if you want to put you best foot forward and up you chances that the llm grok your content you should pass all of the text in `a` to the prompt and all of the images in `a.images` in the image input. We aim for making those two as 'prompt engineered' as possible. *Attachments* is young but already very powerful and used in production. The api will not change. Maybe advanced feature and syntax will be added but the core will stay the same. Mostly we will support more file types and we will have better rendering for better performance and with less extreneous tokens.
31
+
32
+ ## How to give attachments to openai llms?
33
+
34
+ ```python
35
+ from openai import OpenAI
36
+ from attachments import Attachments
37
+
38
+ pdf_attachment = Attachments("https://github.com/microsoft/markitdown/raw/refs/heads/main/packages/markitdown/tests/test_files/test.pdf")
39
+
40
+ prompt = f"""
41
+ Analyze the following documents:
42
+ {pdf_attachment}
43
+ """
44
+
45
+ content = [{"type": "input_image","image_url": image} for image in pdf_attachment.images] + \
46
+ [{"type": "input_text", "text": prompt}]
47
+
48
+ response = OpenAI().responses.create(model="gpt-4.1-nano", input=[{"role": "user", "content": content}])
49
+ response.output_text
50
+ ```
51
+
52
+ ## Usage
53
+
54
+ ### Basic Initialization
55
+ Create an `Attachments` object by passing one or more local file paths or URLs. Image processing commands can be appended to image paths.
56
+
57
+ ```python
58
+ from attachments import Attachments
59
+
60
+ # Initialize with various local files, URLs, and image processing commands
61
+ a = Attachments(
62
+ "docs/report.pdf",
63
+ "images/diagram.png[resize:400xauto]",
64
+ "https://www.example.com/article.html",
65
+ "photos/vacation.heic[rotate:90,format:jpeg,quality:80]"
66
+ )
67
+
68
+ # The library will download URLs, process files, and extract content.
69
+ ```
70
+
71
+ ### Indexing Attachments
72
+ You can get a new `Attachments` object containing a subset of the original attachments using integer or slice indexing:
73
+ ```python
74
+ first_attachment = a[0]
75
+ first_two_attachments = a[0:2]
76
+
77
+ print(f"Selected attachment: {first_attachment}")
78
+ ```
79
+
80
+ ### Page/Slide Selection
81
+ Specify pages for PDFs or slides for PPTX files using bracket notation in the path string:
82
+ ```python
83
+ # Process only page 1 and pages 3 through 5 of a PDF
84
+ specific_pages_pdf = Attachments("long_document.pdf[1,3-5]")
85
+
86
+ # Process the first three slides and the last slide of a presentation
87
+ specific_slides_pptx = Attachments("presentation.pptx[:3,N]")
88
+ # 'N' refers to the last page/slide. Negative indexing like [-1:] also works.
89
+ ```
90
+
91
+ ## Supported File Types
92
+ * **Documents**: PDF (`.pdf`), PowerPoint (`.pptx`), Excel (`.xlsx`), Word (`.docx`), much more to come!
93
+ * **Images**: JPEG (`.jpg`, `.jpeg`), PNG (`.png`), GIF (`.gif`), BMP (`.bmp`), WEBP (`.webp`), TIFF (`.tiff`), HEIC (`.heic`), HEIF (`.heif`)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "attachments"
7
- version = "0.2.0"
7
+ version = "0.2.2"
8
8
  authors = [
9
9
  { name="Maxim Rivest", email="mrive052@gmail.com" },
10
10
  ]
@@ -3,6 +3,7 @@
3
3
  from abc import ABC, abstractmethod
4
4
  from typing import List, Dict, Any
5
5
  import xml.etree.ElementTree as ET
6
+ import re
6
7
 
7
8
  class BaseRenderer(ABC):
8
9
  """Abstract base class for content renderers."""
@@ -51,12 +52,35 @@ class RendererRegistry:
51
52
 
52
53
  class DefaultXMLRenderer(BaseRenderer):
53
54
  """Renders parsed content into a default XML-like string format."""
55
+ def _sanitize_xml_attr(self, value):
56
+ """Ensure value is a string and escape/replace invalid XML attribute characters."""
57
+ if value is None:
58
+ return ""
59
+ value = str(value)
60
+ # Remove control characters except tab, newline, carriage return
61
+ value = re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]', '', value)
62
+ # Replace invalid XML attribute chars (", <, >, &, ')
63
+ value = value.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
64
+ value = value.replace('"', '&quot;').replace("'", '&apos;')
65
+ return value
66
+
67
+ def _sanitize_xml_text(self, value):
68
+ """Ensure value is a string and escape/replace invalid XML text characters."""
69
+ if value is None:
70
+ return ""
71
+ value = str(value)
72
+ # Remove control characters except tab, newline, carriage return
73
+ value = re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]', '', value)
74
+ # ElementTree will escape &, <, > automatically in text, but we can pre-escape for safety
75
+ value = value.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
76
+ return value
77
+
54
78
  def render(self, attachments_data: List[Dict[str, Any]]) -> str:
55
79
  root = ET.Element("attachments")
56
80
  for item_data in attachments_data:
57
- item_id = item_data.get("id", "unknown")
58
- item_type = item_data.get("type", "unknown")
59
- original_path = item_data.get("original_path_str", item_data.get("file_path", "N/A"))
81
+ item_id = self._sanitize_xml_attr(item_data.get("id", "unknown"))
82
+ item_type = self._sanitize_xml_attr(item_data.get("type", "unknown"))
83
+ original_path = self._sanitize_xml_attr(item_data.get("original_path_str", item_data.get("file_path", "N/A")))
60
84
 
61
85
  attachment_element = ET.SubElement(root, "attachment")
62
86
  attachment_element.set("id", item_id)
@@ -64,31 +88,21 @@ class DefaultXMLRenderer(BaseRenderer):
64
88
  attachment_element.set("original_path", original_path)
65
89
 
66
90
  content_text = item_data.get("text", "")
67
-
68
91
  content_element = ET.SubElement(attachment_element, "content")
69
- # Using a direct assignment for CDATA.
70
- # ET doesn't have a specific CDATA type, but this is a common way to handle it.
71
- # Actual CDATA wrapping might need to happen during serialization if the library doesn't do it.
72
- if content_text: # Only add text if it's not empty
73
- content_element.text = content_text # Store as regular text; XML serializer should handle escaping.
74
- # For literal CDATA, one might need a custom serializer or to build the string manually.
92
+ if content_text:
93
+ content_element.text = self._sanitize_xml_text(content_text)
75
94
  else:
76
- # If there's no text content, we can leave the content tag empty
77
- # or add a comment, or omit it. For now, an empty tag is fine.
78
95
  pass
79
96
 
80
- # Serialize to string with pretty print
81
- # ET.indent(root) # Available in Python 3.9+
82
97
  xml_str = ET.tostring(root, encoding="utf-8").decode("utf-8")
83
-
84
- # Basic pretty printing for older Python versions or if ET.indent is not sufficient
85
98
  try:
86
99
  import xml.dom.minidom
87
100
  dom = xml.dom.minidom.parseString(xml_str)
88
101
  return dom.toprettyxml(indent=" ")
89
- except ImportError:
90
- # Fallback if minidom is not available (though it's standard)
91
- return xml_str
102
+ except Exception as e:
103
+ print("[attachments] XML rendering error. Invalid XML string was:\n", xml_str)
104
+ print("Exception:", e)
105
+ raise
92
106
 
93
107
  class PlainTextRenderer(BaseRenderer):
94
108
  """Renders parsed content into a simple plain text string,
@@ -0,0 +1,132 @@
1
+ Metadata-Version: 2.4
2
+ Name: attachments
3
+ Version: 0.2.2
4
+ Summary: A Python library to handle various file types for LLMs.
5
+ Author-email: Maxim Rivest <mrive052@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/yourusername/attachments
8
+ Project-URL: Bug Tracker, https://github.com/yourusername/attachments/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: Pillow
15
+ Requires-Dist: python-dotenv
16
+ Requires-Dist: pytest
17
+ Requires-Dist: beautifulsoup4
18
+ Requires-Dist: html2text
19
+ Requires-Dist: markdownify
20
+ Requires-Dist: lxml
21
+ Requires-Dist: python-magic
22
+ Requires-Dist: mammoth
23
+ Requires-Dist: PyMuPDF
24
+ Requires-Dist: python-pptx
25
+ Requires-Dist: python-docx
26
+ Requires-Dist: odfpy
27
+ Requires-Dist: pillow-heif
28
+ Requires-Dist: pydub
29
+ Requires-Dist: SpeechRecognition
30
+ Requires-Dist: markitdown[all]>=0.1.1
31
+ Requires-Dist: pypdfium2
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest; extra == "dev"
34
+ Requires-Dist: flake8; extra == "dev"
35
+ Requires-Dist: mypy; extra == "dev"
36
+ Requires-Dist: black; extra == "dev"
37
+ Requires-Dist: filelock; extra == "dev"
38
+ Dynamic: license-file
39
+
40
+ # Attachments
41
+ ## Easiest way to give context to LLMs
42
+
43
+ Attachments has the ambition to be the general funnel for any files to be transformed into images+text for large language models context. For this to be true we, has a community, have to stop implementing this funnel (file -> images+text) each on our own. If you have a detector + parser + renderer contibute it! This will help you and us. Don't let your code alone in a dark corner of you computer.
44
+
45
+ ## Very quickstart
46
+
47
+ ```bash
48
+ > pip install attachments
49
+ ```
50
+
51
+ Most users will not have to learn anything more then that: `Attachments("path/to/file.pdf")`
52
+
53
+ ```python
54
+ from attachments import Attachments
55
+
56
+ a = Attachment("/the/path/or/url/to/your/to/the/content/you/want/to/give/the/llm.xlsx", "another.pdf", "another.pptx"...)
57
+
58
+ prompt_ready_context = str(a)
59
+ images_ready_for_llm = a.images
60
+ ```
61
+
62
+ That is really just it!
63
+
64
+ You can print it `print(a)`, you can interpolate it `f"the content is {a}"` you can string it `str(a)`. This will give you something very good to give the llm so that the AI can consider you content.
65
+
66
+ Nowadays, most genAI models comes with the ability to see images too. So you also have all attachments in images forms by using `a.images`.
67
+ This is a list of base64 encoded images, this is the fundamental format the most llm provider accept.
68
+
69
+ The simplest way to use and think about attachments is that if you want to put you best foot forward and up you chances that the llm grok your content you should pass all of the text in `a` to the prompt and all of the images in `a.images` in the image input. We aim for making those two as 'prompt engineered' as possible. *Attachments* is young but already very powerful and used in production. The api will not change. Maybe advanced feature and syntax will be added but the core will stay the same. Mostly we will support more file types and we will have better rendering for better performance and with less extreneous tokens.
70
+
71
+ ## How to give attachments to openai llms?
72
+
73
+ ```python
74
+ from openai import OpenAI
75
+ from attachments import Attachments
76
+
77
+ pdf_attachment = Attachments("https://github.com/microsoft/markitdown/raw/refs/heads/main/packages/markitdown/tests/test_files/test.pdf")
78
+
79
+ prompt = f"""
80
+ Analyze the following documents:
81
+ {pdf_attachment}
82
+ """
83
+
84
+ content = [{"type": "input_image","image_url": image} for image in pdf_attachment.images] + \
85
+ [{"type": "input_text", "text": prompt}]
86
+
87
+ response = OpenAI().responses.create(model="gpt-4.1-nano", input=[{"role": "user", "content": content}])
88
+ response.output_text
89
+ ```
90
+
91
+ ## Usage
92
+
93
+ ### Basic Initialization
94
+ Create an `Attachments` object by passing one or more local file paths or URLs. Image processing commands can be appended to image paths.
95
+
96
+ ```python
97
+ from attachments import Attachments
98
+
99
+ # Initialize with various local files, URLs, and image processing commands
100
+ a = Attachments(
101
+ "docs/report.pdf",
102
+ "images/diagram.png[resize:400xauto]",
103
+ "https://www.example.com/article.html",
104
+ "photos/vacation.heic[rotate:90,format:jpeg,quality:80]"
105
+ )
106
+
107
+ # The library will download URLs, process files, and extract content.
108
+ ```
109
+
110
+ ### Indexing Attachments
111
+ You can get a new `Attachments` object containing a subset of the original attachments using integer or slice indexing:
112
+ ```python
113
+ first_attachment = a[0]
114
+ first_two_attachments = a[0:2]
115
+
116
+ print(f"Selected attachment: {first_attachment}")
117
+ ```
118
+
119
+ ### Page/Slide Selection
120
+ Specify pages for PDFs or slides for PPTX files using bracket notation in the path string:
121
+ ```python
122
+ # Process only page 1 and pages 3 through 5 of a PDF
123
+ specific_pages_pdf = Attachments("long_document.pdf[1,3-5]")
124
+
125
+ # Process the first three slides and the last slide of a presentation
126
+ specific_slides_pptx = Attachments("presentation.pptx[:3,N]")
127
+ # 'N' refers to the last page/slide. Negative indexing like [-1:] also works.
128
+ ```
129
+
130
+ ## Supported File Types
131
+ * **Documents**: PDF (`.pdf`), PowerPoint (`.pptx`), Excel (`.xlsx`), Word (`.docx`), much more to come!
132
+ * **Images**: JPEG (`.jpg`, `.jpeg`), PNG (`.png`), GIF (`.gif`), BMP (`.bmp`), WEBP (`.webp`), TIFF (`.tiff`), HEIC (`.heic`), HEIF (`.heif`)
@@ -1,237 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: attachments
3
- Version: 0.2.0
4
- Summary: A Python library to handle various file types for LLMs.
5
- Author-email: Maxim Rivest <mrive052@gmail.com>
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/yourusername/attachments
8
- Project-URL: Bug Tracker, https://github.com/yourusername/attachments/issues
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: Operating System :: OS Independent
11
- Requires-Python: >=3.10
12
- Description-Content-Type: text/markdown
13
- License-File: LICENSE
14
- Requires-Dist: Pillow
15
- Requires-Dist: python-dotenv
16
- Requires-Dist: pytest
17
- Requires-Dist: beautifulsoup4
18
- Requires-Dist: html2text
19
- Requires-Dist: markdownify
20
- Requires-Dist: lxml
21
- Requires-Dist: python-magic
22
- Requires-Dist: mammoth
23
- Requires-Dist: PyMuPDF
24
- Requires-Dist: python-pptx
25
- Requires-Dist: python-docx
26
- Requires-Dist: odfpy
27
- Requires-Dist: pillow-heif
28
- Requires-Dist: pydub
29
- Requires-Dist: SpeechRecognition
30
- Requires-Dist: markitdown[all]>=0.1.1
31
- Requires-Dist: pypdfium2
32
- Provides-Extra: dev
33
- Requires-Dist: pytest; extra == "dev"
34
- Requires-Dist: flake8; extra == "dev"
35
- Requires-Dist: mypy; extra == "dev"
36
- Requires-Dist: black; extra == "dev"
37
- Requires-Dist: filelock; extra == "dev"
38
- Dynamic: license-file
39
-
40
- # Attachments
41
- ## Quickstart
42
- A Python library designed to seamlessly handle various file types (local or URLs), process them, and present them in formats suitable for Large Language Models (LLMs).
43
-
44
- It is meant to be very minimal.
45
-
46
- most user will not have to learn anything more then that: `Attachments("path/to/file.pdf")`
47
-
48
- ```bash
49
- > pip install attachments
50
- ```
51
-
52
- ```python
53
- from attachments import Attachments
54
-
55
- a = Attachments("https://github.com/microsoft/markitdown/raw/refs/heads/main/packages/markitdown/tests/test_files/test.pdf",
56
- "https://github.com/microsoft/markitdown/raw/refs/heads/main/packages/markitdown/tests/test_files/test.pptx",
57
- "https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/BremenBotanikaZen.jpg/1280px-BremenBotanikaZen.jpg")
58
- print(a)# will print a string representation of the pdf, pptx and image that can be used in a prompt
59
- ```
60
-
61
- ```xml
62
- <attachments>
63
- <pdf_attachment_1>
64
- page 1:
65
- Hello PDF!
66
- </pdf_attachment_1>
67
- <pptx_attachment_2>
68
- --- Slide 1 ---
69
- Slide 1 Title This is the first slide. Content for page 1.
70
-
71
- --- Slide 2 ---
72
- Slide 2 Title This is the second slide. Content for page 2.
73
-
74
- --- Slide 3 ---
75
- Slide 3 Title This is the third slide. Content for page 3.
76
- </pptx_attachment_2>
77
- </attachments>
78
- ```
79
-
80
- The ambition of `attachments` is to provide a robust reader, processor, and renderer for a wide array of common file types, simplifying the process of providing complex, multi-modal context to LLMs. The power comes from doing a best effort media -> text + image prompt and thus most of the value of `attachments` is not visible in long tutorial and lots of things to learn but in rather in it's simplicity and ability to get out of your way and let you pass files/data/information to LLMs.
81
-
82
- ## Key Features
83
-
84
- * **Versatile File Handling**: Process a variety of file types including PDFs, PPTX, HTML, common image formats, and audio files.
85
- * **Local and URL Support**: Accepts local file paths and URLs as input.
86
- * **Content Extraction**: Extracts text from documents and rich metadata from all supported types.
87
- * **Advanced Image Processing**:
88
- * On-the-fly transformations via path string commands: resizing (e.g., `image.jpg[resize:500x300]`, `image.png[resize:200xauto]`), rotation (`image.heic[rotate:90]`), and format conversion (`image.webp[format:png]`).
89
- * Configurable output quality for JPEG/WEBP.
90
- * **Rich Jupyter/IPython Display**: Automatic rich Markdown rendering when an `Attachments` object is the last item in a cell, featuring:
91
- * A summary of all attachments with detailed metadata.
92
- * A multi-column image gallery for visual previews of image attachments.
93
- * **Powerful Indexing**:
94
- * Select specific pages from PDFs or slides from PPTX files (e.g., `"file.pdf[1,3-5,N]"`, `"presentation.pptx[:3,-1:]"`).
95
- * `Attachments` objects themselves are indexable and sliceable (e.g., `subset = attachments[0:2]`).
96
- * **LLM-Ready Outputs**:
97
- * Default XML rendering (`str(attachments)`) provides a structured representation suitable for LLM prompts, including detailed metadata and textual content.
98
- * `.images` property: Conveniently access a list of base64-encoded image strings (e.g., `data:image/jpeg;base64,...`), ready for multi-modal LLM APIs.
99
- * `.audios` property: Provides a list of audio file data, each with a filename, a `BytesIO` file object, and content type, suitable for audio-processing APIs (e.g., OpenAI Whisper).
100
- * **Broad Image Format Support**: Handles JPEG, PNG, GIF, BMP, WEBP, TIFF, and modern formats like HEIC/HEIF (requires `libheif`).
101
-
102
- ## Installation
103
-
104
- ```bash
105
- pip install attachments
106
- ```
107
- For full HEIC/HEIF image support, you may need to install `libheif` on your system:
108
- * macOS: `brew install libheif`
109
- * Debian/Ubuntu: `sudo apt-get install libheif-examples`
110
-
111
- ## Usage
112
-
113
- ### Basic Initialization
114
- Create an `Attachments` object by passing one or more local file paths or URLs. Image processing commands can be appended to image paths.
115
-
116
- ```python
117
- from attachments import Attachments
118
-
119
- # Initialize with various local files, URLs, and image processing commands
120
- a = Attachments(
121
- "docs/report.pdf",
122
- "images/diagram.png[resize:400xauto]",
123
- "https://www.example.com/article.html",
124
- "photos/vacation.heic[rotate:90,format:jpeg,quality:80]"
125
- )
126
-
127
- # The library will download URLs, process files, and extract content.
128
- ```
129
-
130
- ### Default XML Output for LLMs
131
- Simply converting an `Attachments` object to a string (or using it in an f-string) renders it as XML, which is useful for many LLM prompts.
132
-
133
- ```python
134
- prompt = f"""
135
- Analyze the following documents:
136
- {a}
137
- """
138
- print(prompt)
139
-
140
- # Output (simplified):
141
- # Analyze the following documents:
142
- # <attachments>
143
- # <attachment id="pdf1" type="pdf" original_path_str="docs/report.pdf" file_path="docs/report.pdf">
144
- # <meta name="num_pages" value="10" />
145
- # <meta name="indices_processed" value="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" />
146
- # <content>
147
- # ... extracted text from PDF ...
148
- # </content>
149
- # </attachment>
150
- # <attachment id="png2" type="png" original_path_str="images/diagram.png[resize:400xauto]" file_path="images/diagram.png">
151
- # <meta name="dimensions" value="400x..." />
152
- # <meta name="original_format" value="PNG" />
153
- # <meta name="applied_operations" value="{'resize': (400, None)}" />
154
- # <meta name="output_format_target" value="jpeg" />
155
- # <content>
156
- # [Image: diagram.png (original: PNG ...) -> processed to 400x... for output as jpeg]
157
- # </content>
158
- # </attachment>
159
- # ... other attachments ...
160
- # </attachments>
161
- ```
162
-
163
- ### Rich Display in Jupyter/IPython
164
- If an `Attachments` object is the last expression in a Jupyter Notebook or IPython console cell, it will automatically render a rich Markdown summary:
165
-
166
- ```python
167
- # In a Jupyter cell:
168
- from attachments import Attachments
169
- a = Attachments("report.pdf", "image.png[resize:150x150]", "chart.jpg")
170
- a # This will display the rich summary and image gallery
171
- ```
172
- This output includes a main summary of all attachments (ID, type, source, extracted metadata/text snippets) and a separate "Image Previews" section with a multi-column gallery of image thumbnails.
173
-
174
- ### Accessing Processed Data and Images
175
-
176
- **1. Parsed Data:**
177
- Each processed attachment's data is stored in the `attachments_data` list:
178
- ```python
179
- for item in a.attachments_data:
180
- print(f"ID: {item['id']}, Type: {item['type']}")
181
- if 'text' in item:
182
- print(f" Text snippet: {item['text'][:100]}...")
183
- if item['type'] in ['jpeg', 'png', 'heic']: # Image types
184
- print(f" Dimensions: {item['width']}x{item['height']}")
185
- print(f" Original Format: {item['original_format']}")
186
- ```
187
-
188
- **2. Base64 Images for LLMs:**
189
- The `.images` property provides a list of base64-encoded strings for all processed images, ready for use with multi-modal LLM APIs.
190
- ```python
191
- base64_image_list = a.images
192
- if base64_image_list:
193
- print(f"First image data URI: {base64_image_list[0][:50]}...")
194
- # Output: First image data URI: data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD...
195
- ```
196
-
197
- **3. Audio Files for LLMs:**
198
- The `.audios` property returns a list of dictionaries, each prepared for audio API submission (e.g., to OpenAI Whisper). Each dictionary contains:
199
- * `'filename'`: The original filename (e.g., `'speech.mp3'`).
200
- * `'file_object'`: An `io.BytesIO` object containing the raw audio data. Its `.name` attribute is set to the filename.
201
- * `'content_type'`: The detected MIME type of the audio (e.g., `'audio/mpeg'`).
202
-
203
- ```python
204
- audio_files_for_api = a.audios
205
- if audio_files_for_api:
206
- first_audio = audio_files_for_api[0]
207
- print(f"Audio Filename: {first_audio['filename']}")
208
- print(f"Audio Content-Type: {first_audio['content_type']}")
209
- # The first_audio['file_object'] can be directly passed to APIs like openai.Audio.transcribe
210
- # For example: transcript = openai.Audio.transcribe("whisper-1", first_audio['file_object'])
211
- ```
212
-
213
- ### Indexing Attachments
214
- You can get a new `Attachments` object containing a subset of the original attachments using integer or slice indexing:
215
- ```python
216
- first_attachment = a[0]
217
- first_two_attachments = a[0:2]
218
-
219
- print(f"Selected attachment: {first_attachment}")
220
- ```
221
-
222
- ### Page/Slide Selection
223
- Specify pages for PDFs or slides for PPTX files using bracket notation in the path string:
224
- ```python
225
- # Process only page 1 and pages 3 through 5 of a PDF
226
- specific_pages_pdf = Attachments("long_document.pdf[1,3-5]")
227
-
228
- # Process the first three slides and the last slide of a presentation
229
- specific_slides_pptx = Attachments("presentation.pptx[:3,N]")
230
- # 'N' refers to the last page/slide. Negative indexing like [-1:] also works.
231
- ```
232
-
233
- ## Supported File Types
234
- * **Documents**: PDF (`.pdf`), PowerPoint (`.pptx`)
235
- * **Web**: HTML (`.html`, URLs)
236
- * **Images**: JPEG (`.jpg`, `.jpeg`), PNG (`.png`), GIF (`.gif`), BMP (`.bmp`), WEBP (`.webp`), TIFF (`.tiff`), HEIC (`.heic`), HEIF (`.heif`)
237
- * **Audio**: FLAC (`.flac`), M4A (`.m4a`), MP3 (`.mp3`), MP4 audio (`.mp4`), MPEG audio (`.mpeg`, `.mpga`), Ogg audio (`.oga`, `.ogg`), WAV (`.wav`), WebM audio (`.webm`)
@@ -1,198 +0,0 @@
1
- # Attachments
2
- ## Quickstart
3
- A Python library designed to seamlessly handle various file types (local or URLs), process them, and present them in formats suitable for Large Language Models (LLMs).
4
-
5
- It is meant to be very minimal.
6
-
7
- most user will not have to learn anything more then that: `Attachments("path/to/file.pdf")`
8
-
9
- ```bash
10
- > pip install attachments
11
- ```
12
-
13
- ```python
14
- from attachments import Attachments
15
-
16
- a = Attachments("https://github.com/microsoft/markitdown/raw/refs/heads/main/packages/markitdown/tests/test_files/test.pdf",
17
- "https://github.com/microsoft/markitdown/raw/refs/heads/main/packages/markitdown/tests/test_files/test.pptx",
18
- "https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/BremenBotanikaZen.jpg/1280px-BremenBotanikaZen.jpg")
19
- print(a)# will print a string representation of the pdf, pptx and image that can be used in a prompt
20
- ```
21
-
22
- ```xml
23
- <attachments>
24
- <pdf_attachment_1>
25
- page 1:
26
- Hello PDF!
27
- </pdf_attachment_1>
28
- <pptx_attachment_2>
29
- --- Slide 1 ---
30
- Slide 1 Title This is the first slide. Content for page 1.
31
-
32
- --- Slide 2 ---
33
- Slide 2 Title This is the second slide. Content for page 2.
34
-
35
- --- Slide 3 ---
36
- Slide 3 Title This is the third slide. Content for page 3.
37
- </pptx_attachment_2>
38
- </attachments>
39
- ```
40
-
41
- The ambition of `attachments` is to provide a robust reader, processor, and renderer for a wide array of common file types, simplifying the process of providing complex, multi-modal context to LLMs. The power comes from doing a best effort media -> text + image prompt and thus most of the value of `attachments` is not visible in long tutorial and lots of things to learn but in rather in it's simplicity and ability to get out of your way and let you pass files/data/information to LLMs.
42
-
43
- ## Key Features
44
-
45
- * **Versatile File Handling**: Process a variety of file types including PDFs, PPTX, HTML, common image formats, and audio files.
46
- * **Local and URL Support**: Accepts local file paths and URLs as input.
47
- * **Content Extraction**: Extracts text from documents and rich metadata from all supported types.
48
- * **Advanced Image Processing**:
49
- * On-the-fly transformations via path string commands: resizing (e.g., `image.jpg[resize:500x300]`, `image.png[resize:200xauto]`), rotation (`image.heic[rotate:90]`), and format conversion (`image.webp[format:png]`).
50
- * Configurable output quality for JPEG/WEBP.
51
- * **Rich Jupyter/IPython Display**: Automatic rich Markdown rendering when an `Attachments` object is the last item in a cell, featuring:
52
- * A summary of all attachments with detailed metadata.
53
- * A multi-column image gallery for visual previews of image attachments.
54
- * **Powerful Indexing**:
55
- * Select specific pages from PDFs or slides from PPTX files (e.g., `"file.pdf[1,3-5,N]"`, `"presentation.pptx[:3,-1:]"`).
56
- * `Attachments` objects themselves are indexable and sliceable (e.g., `subset = attachments[0:2]`).
57
- * **LLM-Ready Outputs**:
58
- * Default XML rendering (`str(attachments)`) provides a structured representation suitable for LLM prompts, including detailed metadata and textual content.
59
- * `.images` property: Conveniently access a list of base64-encoded image strings (e.g., `data:image/jpeg;base64,...`), ready for multi-modal LLM APIs.
60
- * `.audios` property: Provides a list of audio file data, each with a filename, a `BytesIO` file object, and content type, suitable for audio-processing APIs (e.g., OpenAI Whisper).
61
- * **Broad Image Format Support**: Handles JPEG, PNG, GIF, BMP, WEBP, TIFF, and modern formats like HEIC/HEIF (requires `libheif`).
62
-
63
- ## Installation
64
-
65
- ```bash
66
- pip install attachments
67
- ```
68
- For full HEIC/HEIF image support, you may need to install `libheif` on your system:
69
- * macOS: `brew install libheif`
70
- * Debian/Ubuntu: `sudo apt-get install libheif-examples`
71
-
72
- ## Usage
73
-
74
- ### Basic Initialization
75
- Create an `Attachments` object by passing one or more local file paths or URLs. Image processing commands can be appended to image paths.
76
-
77
- ```python
78
- from attachments import Attachments
79
-
80
- # Initialize with various local files, URLs, and image processing commands
81
- a = Attachments(
82
- "docs/report.pdf",
83
- "images/diagram.png[resize:400xauto]",
84
- "https://www.example.com/article.html",
85
- "photos/vacation.heic[rotate:90,format:jpeg,quality:80]"
86
- )
87
-
88
- # The library will download URLs, process files, and extract content.
89
- ```
90
-
91
- ### Default XML Output for LLMs
92
- Simply converting an `Attachments` object to a string (or using it in an f-string) renders it as XML, which is useful for many LLM prompts.
93
-
94
- ```python
95
- prompt = f"""
96
- Analyze the following documents:
97
- {a}
98
- """
99
- print(prompt)
100
-
101
- # Output (simplified):
102
- # Analyze the following documents:
103
- # <attachments>
104
- # <attachment id="pdf1" type="pdf" original_path_str="docs/report.pdf" file_path="docs/report.pdf">
105
- # <meta name="num_pages" value="10" />
106
- # <meta name="indices_processed" value="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" />
107
- # <content>
108
- # ... extracted text from PDF ...
109
- # </content>
110
- # </attachment>
111
- # <attachment id="png2" type="png" original_path_str="images/diagram.png[resize:400xauto]" file_path="images/diagram.png">
112
- # <meta name="dimensions" value="400x..." />
113
- # <meta name="original_format" value="PNG" />
114
- # <meta name="applied_operations" value="{'resize': (400, None)}" />
115
- # <meta name="output_format_target" value="jpeg" />
116
- # <content>
117
- # [Image: diagram.png (original: PNG ...) -> processed to 400x... for output as jpeg]
118
- # </content>
119
- # </attachment>
120
- # ... other attachments ...
121
- # </attachments>
122
- ```
123
-
124
- ### Rich Display in Jupyter/IPython
125
- If an `Attachments` object is the last expression in a Jupyter Notebook or IPython console cell, it will automatically render a rich Markdown summary:
126
-
127
- ```python
128
- # In a Jupyter cell:
129
- from attachments import Attachments
130
- a = Attachments("report.pdf", "image.png[resize:150x150]", "chart.jpg")
131
- a # This will display the rich summary and image gallery
132
- ```
133
- This output includes a main summary of all attachments (ID, type, source, extracted metadata/text snippets) and a separate "Image Previews" section with a multi-column gallery of image thumbnails.
134
-
135
- ### Accessing Processed Data and Images
136
-
137
- **1. Parsed Data:**
138
- Each processed attachment's data is stored in the `attachments_data` list:
139
- ```python
140
- for item in a.attachments_data:
141
- print(f"ID: {item['id']}, Type: {item['type']}")
142
- if 'text' in item:
143
- print(f" Text snippet: {item['text'][:100]}...")
144
- if item['type'] in ['jpeg', 'png', 'heic']: # Image types
145
- print(f" Dimensions: {item['width']}x{item['height']}")
146
- print(f" Original Format: {item['original_format']}")
147
- ```
148
-
149
- **2. Base64 Images for LLMs:**
150
- The `.images` property provides a list of base64-encoded strings for all processed images, ready for use with multi-modal LLM APIs.
151
- ```python
152
- base64_image_list = a.images
153
- if base64_image_list:
154
- print(f"First image data URI: {base64_image_list[0][:50]}...")
155
- # Output: First image data URI: data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD...
156
- ```
157
-
158
- **3. Audio Files for LLMs:**
159
- The `.audios` property returns a list of dictionaries, each prepared for audio API submission (e.g., to OpenAI Whisper). Each dictionary contains:
160
- * `'filename'`: The original filename (e.g., `'speech.mp3'`).
161
- * `'file_object'`: An `io.BytesIO` object containing the raw audio data. Its `.name` attribute is set to the filename.
162
- * `'content_type'`: The detected MIME type of the audio (e.g., `'audio/mpeg'`).
163
-
164
- ```python
165
- audio_files_for_api = a.audios
166
- if audio_files_for_api:
167
- first_audio = audio_files_for_api[0]
168
- print(f"Audio Filename: {first_audio['filename']}")
169
- print(f"Audio Content-Type: {first_audio['content_type']}")
170
- # The first_audio['file_object'] can be directly passed to APIs like openai.Audio.transcribe
171
- # For example: transcript = openai.Audio.transcribe("whisper-1", first_audio['file_object'])
172
- ```
173
-
174
- ### Indexing Attachments
175
- You can get a new `Attachments` object containing a subset of the original attachments using integer or slice indexing:
176
- ```python
177
- first_attachment = a[0]
178
- first_two_attachments = a[0:2]
179
-
180
- print(f"Selected attachment: {first_attachment}")
181
- ```
182
-
183
- ### Page/Slide Selection
184
- Specify pages for PDFs or slides for PPTX files using bracket notation in the path string:
185
- ```python
186
- # Process only page 1 and pages 3 through 5 of a PDF
187
- specific_pages_pdf = Attachments("long_document.pdf[1,3-5]")
188
-
189
- # Process the first three slides and the last slide of a presentation
190
- specific_slides_pptx = Attachments("presentation.pptx[:3,N]")
191
- # 'N' refers to the last page/slide. Negative indexing like [-1:] also works.
192
- ```
193
-
194
- ## Supported File Types
195
- * **Documents**: PDF (`.pdf`), PowerPoint (`.pptx`)
196
- * **Web**: HTML (`.html`, URLs)
197
- * **Images**: JPEG (`.jpg`, `.jpeg`), PNG (`.png`), GIF (`.gif`), BMP (`.bmp`), WEBP (`.webp`), TIFF (`.tiff`), HEIC (`.heic`), HEIF (`.heif`)
198
- * **Audio**: FLAC (`.flac`), M4A (`.m4a`), MP3 (`.mp3`), MP4 audio (`.mp4`), MPEG audio (`.mpeg`, `.mpga`), Ogg audio (`.oga`, `.ogg`), WAV (`.wav`), WebM audio (`.webm`)
@@ -1,237 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: attachments
3
- Version: 0.2.0
4
- Summary: A Python library to handle various file types for LLMs.
5
- Author-email: Maxim Rivest <mrive052@gmail.com>
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/yourusername/attachments
8
- Project-URL: Bug Tracker, https://github.com/yourusername/attachments/issues
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: Operating System :: OS Independent
11
- Requires-Python: >=3.10
12
- Description-Content-Type: text/markdown
13
- License-File: LICENSE
14
- Requires-Dist: Pillow
15
- Requires-Dist: python-dotenv
16
- Requires-Dist: pytest
17
- Requires-Dist: beautifulsoup4
18
- Requires-Dist: html2text
19
- Requires-Dist: markdownify
20
- Requires-Dist: lxml
21
- Requires-Dist: python-magic
22
- Requires-Dist: mammoth
23
- Requires-Dist: PyMuPDF
24
- Requires-Dist: python-pptx
25
- Requires-Dist: python-docx
26
- Requires-Dist: odfpy
27
- Requires-Dist: pillow-heif
28
- Requires-Dist: pydub
29
- Requires-Dist: SpeechRecognition
30
- Requires-Dist: markitdown[all]>=0.1.1
31
- Requires-Dist: pypdfium2
32
- Provides-Extra: dev
33
- Requires-Dist: pytest; extra == "dev"
34
- Requires-Dist: flake8; extra == "dev"
35
- Requires-Dist: mypy; extra == "dev"
36
- Requires-Dist: black; extra == "dev"
37
- Requires-Dist: filelock; extra == "dev"
38
- Dynamic: license-file
39
-
40
- # Attachments
41
- ## Quickstart
42
- A Python library designed to seamlessly handle various file types (local or URLs), process them, and present them in formats suitable for Large Language Models (LLMs).
43
-
44
- It is meant to be very minimal.
45
-
46
- most user will not have to learn anything more then that: `Attachments("path/to/file.pdf")`
47
-
48
- ```bash
49
- > pip install attachments
50
- ```
51
-
52
- ```python
53
- from attachments import Attachments
54
-
55
- a = Attachments("https://github.com/microsoft/markitdown/raw/refs/heads/main/packages/markitdown/tests/test_files/test.pdf",
56
- "https://github.com/microsoft/markitdown/raw/refs/heads/main/packages/markitdown/tests/test_files/test.pptx",
57
- "https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/BremenBotanikaZen.jpg/1280px-BremenBotanikaZen.jpg")
58
- print(a)# will print a string representation of the pdf, pptx and image that can be used in a prompt
59
- ```
60
-
61
- ```xml
62
- <attachments>
63
- <pdf_attachment_1>
64
- page 1:
65
- Hello PDF!
66
- </pdf_attachment_1>
67
- <pptx_attachment_2>
68
- --- Slide 1 ---
69
- Slide 1 Title This is the first slide. Content for page 1.
70
-
71
- --- Slide 2 ---
72
- Slide 2 Title This is the second slide. Content for page 2.
73
-
74
- --- Slide 3 ---
75
- Slide 3 Title This is the third slide. Content for page 3.
76
- </pptx_attachment_2>
77
- </attachments>
78
- ```
79
-
80
- The ambition of `attachments` is to provide a robust reader, processor, and renderer for a wide array of common file types, simplifying the process of providing complex, multi-modal context to LLMs. The power comes from doing a best effort media -> text + image prompt and thus most of the value of `attachments` is not visible in long tutorial and lots of things to learn but in rather in it's simplicity and ability to get out of your way and let you pass files/data/information to LLMs.
81
-
82
- ## Key Features
83
-
84
- * **Versatile File Handling**: Process a variety of file types including PDFs, PPTX, HTML, common image formats, and audio files.
85
- * **Local and URL Support**: Accepts local file paths and URLs as input.
86
- * **Content Extraction**: Extracts text from documents and rich metadata from all supported types.
87
- * **Advanced Image Processing**:
88
- * On-the-fly transformations via path string commands: resizing (e.g., `image.jpg[resize:500x300]`, `image.png[resize:200xauto]`), rotation (`image.heic[rotate:90]`), and format conversion (`image.webp[format:png]`).
89
- * Configurable output quality for JPEG/WEBP.
90
- * **Rich Jupyter/IPython Display**: Automatic rich Markdown rendering when an `Attachments` object is the last item in a cell, featuring:
91
- * A summary of all attachments with detailed metadata.
92
- * A multi-column image gallery for visual previews of image attachments.
93
- * **Powerful Indexing**:
94
- * Select specific pages from PDFs or slides from PPTX files (e.g., `"file.pdf[1,3-5,N]"`, `"presentation.pptx[:3,-1:]"`).
95
- * `Attachments` objects themselves are indexable and sliceable (e.g., `subset = attachments[0:2]`).
96
- * **LLM-Ready Outputs**:
97
- * Default XML rendering (`str(attachments)`) provides a structured representation suitable for LLM prompts, including detailed metadata and textual content.
98
- * `.images` property: Conveniently access a list of base64-encoded image strings (e.g., `data:image/jpeg;base64,...`), ready for multi-modal LLM APIs.
99
- * `.audios` property: Provides a list of audio file data, each with a filename, a `BytesIO` file object, and content type, suitable for audio-processing APIs (e.g., OpenAI Whisper).
100
- * **Broad Image Format Support**: Handles JPEG, PNG, GIF, BMP, WEBP, TIFF, and modern formats like HEIC/HEIF (requires `libheif`).
101
-
102
- ## Installation
103
-
104
- ```bash
105
- pip install attachments
106
- ```
107
- For full HEIC/HEIF image support, you may need to install `libheif` on your system:
108
- * macOS: `brew install libheif`
109
- * Debian/Ubuntu: `sudo apt-get install libheif-examples`
110
-
111
- ## Usage
112
-
113
- ### Basic Initialization
114
- Create an `Attachments` object by passing one or more local file paths or URLs. Image processing commands can be appended to image paths.
115
-
116
- ```python
117
- from attachments import Attachments
118
-
119
- # Initialize with various local files, URLs, and image processing commands
120
- a = Attachments(
121
- "docs/report.pdf",
122
- "images/diagram.png[resize:400xauto]",
123
- "https://www.example.com/article.html",
124
- "photos/vacation.heic[rotate:90,format:jpeg,quality:80]"
125
- )
126
-
127
- # The library will download URLs, process files, and extract content.
128
- ```
129
-
130
- ### Default XML Output for LLMs
131
- Simply converting an `Attachments` object to a string (or using it in an f-string) renders it as XML, which is useful for many LLM prompts.
132
-
133
- ```python
134
- prompt = f"""
135
- Analyze the following documents:
136
- {a}
137
- """
138
- print(prompt)
139
-
140
- # Output (simplified):
141
- # Analyze the following documents:
142
- # <attachments>
143
- # <attachment id="pdf1" type="pdf" original_path_str="docs/report.pdf" file_path="docs/report.pdf">
144
- # <meta name="num_pages" value="10" />
145
- # <meta name="indices_processed" value="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" />
146
- # <content>
147
- # ... extracted text from PDF ...
148
- # </content>
149
- # </attachment>
150
- # <attachment id="png2" type="png" original_path_str="images/diagram.png[resize:400xauto]" file_path="images/diagram.png">
151
- # <meta name="dimensions" value="400x..." />
152
- # <meta name="original_format" value="PNG" />
153
- # <meta name="applied_operations" value="{'resize': (400, None)}" />
154
- # <meta name="output_format_target" value="jpeg" />
155
- # <content>
156
- # [Image: diagram.png (original: PNG ...) -> processed to 400x... for output as jpeg]
157
- # </content>
158
- # </attachment>
159
- # ... other attachments ...
160
- # </attachments>
161
- ```
162
-
163
- ### Rich Display in Jupyter/IPython
164
- If an `Attachments` object is the last expression in a Jupyter Notebook or IPython console cell, it will automatically render a rich Markdown summary:
165
-
166
- ```python
167
- # In a Jupyter cell:
168
- from attachments import Attachments
169
- a = Attachments("report.pdf", "image.png[resize:150x150]", "chart.jpg")
170
- a # This will display the rich summary and image gallery
171
- ```
172
- This output includes a main summary of all attachments (ID, type, source, extracted metadata/text snippets) and a separate "Image Previews" section with a multi-column gallery of image thumbnails.
173
-
174
- ### Accessing Processed Data and Images
175
-
176
- **1. Parsed Data:**
177
- Each processed attachment's data is stored in the `attachments_data` list:
178
- ```python
179
- for item in a.attachments_data:
180
- print(f"ID: {item['id']}, Type: {item['type']}")
181
- if 'text' in item:
182
- print(f" Text snippet: {item['text'][:100]}...")
183
- if item['type'] in ['jpeg', 'png', 'heic']: # Image types
184
- print(f" Dimensions: {item['width']}x{item['height']}")
185
- print(f" Original Format: {item['original_format']}")
186
- ```
187
-
188
- **2. Base64 Images for LLMs:**
189
- The `.images` property provides a list of base64-encoded strings for all processed images, ready for use with multi-modal LLM APIs.
190
- ```python
191
- base64_image_list = a.images
192
- if base64_image_list:
193
- print(f"First image data URI: {base64_image_list[0][:50]}...")
194
- # Output: First image data URI: data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD...
195
- ```
196
-
197
- **3. Audio Files for LLMs:**
198
- The `.audios` property returns a list of dictionaries, each prepared for audio API submission (e.g., to OpenAI Whisper). Each dictionary contains:
199
- * `'filename'`: The original filename (e.g., `'speech.mp3'`).
200
- * `'file_object'`: An `io.BytesIO` object containing the raw audio data. Its `.name` attribute is set to the filename.
201
- * `'content_type'`: The detected MIME type of the audio (e.g., `'audio/mpeg'`).
202
-
203
- ```python
204
- audio_files_for_api = a.audios
205
- if audio_files_for_api:
206
- first_audio = audio_files_for_api[0]
207
- print(f"Audio Filename: {first_audio['filename']}")
208
- print(f"Audio Content-Type: {first_audio['content_type']}")
209
- # The first_audio['file_object'] can be directly passed to APIs like openai.Audio.transcribe
210
- # For example: transcript = openai.Audio.transcribe("whisper-1", first_audio['file_object'])
211
- ```
212
-
213
- ### Indexing Attachments
214
- You can get a new `Attachments` object containing a subset of the original attachments using integer or slice indexing:
215
- ```python
216
- first_attachment = a[0]
217
- first_two_attachments = a[0:2]
218
-
219
- print(f"Selected attachment: {first_attachment}")
220
- ```
221
-
222
- ### Page/Slide Selection
223
- Specify pages for PDFs or slides for PPTX files using bracket notation in the path string:
224
- ```python
225
- # Process only page 1 and pages 3 through 5 of a PDF
226
- specific_pages_pdf = Attachments("long_document.pdf[1,3-5]")
227
-
228
- # Process the first three slides and the last slide of a presentation
229
- specific_slides_pptx = Attachments("presentation.pptx[:3,N]")
230
- # 'N' refers to the last page/slide. Negative indexing like [-1:] also works.
231
- ```
232
-
233
- ## Supported File Types
234
- * **Documents**: PDF (`.pdf`), PowerPoint (`.pptx`)
235
- * **Web**: HTML (`.html`, URLs)
236
- * **Images**: JPEG (`.jpg`, `.jpeg`), PNG (`.png`), GIF (`.gif`), BMP (`.bmp`), WEBP (`.webp`), TIFF (`.tiff`), HEIC (`.heic`), HEIF (`.heif`)
237
- * **Audio**: FLAC (`.flac`), M4A (`.m4a`), MP3 (`.mp3`), MP4 audio (`.mp4`), MPEG audio (`.mpeg`, `.mpga`), Ogg audio (`.oga`, `.ogg`), WAV (`.wav`), WebM audio (`.webm`)
File without changes
File without changes