agent-artifact-kit 0.1.0__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 (94) hide show
  1. agent_artifact_kit-0.1.0/.gitignore +10 -0
  2. agent_artifact_kit-0.1.0/LICENSE +21 -0
  3. agent_artifact_kit-0.1.0/PKG-INFO +225 -0
  4. agent_artifact_kit-0.1.0/PUBLISHING.md +70 -0
  5. agent_artifact_kit-0.1.0/README.md +177 -0
  6. agent_artifact_kit-0.1.0/artifactkit/__init__.py +89 -0
  7. agent_artifact_kit-0.1.0/artifactkit/adapters/__init__.py +0 -0
  8. agent_artifact_kit-0.1.0/artifactkit/adapters/mcp_server.py +170 -0
  9. agent_artifact_kit-0.1.0/artifactkit/adapters/strands_tools.py +202 -0
  10. agent_artifact_kit-0.1.0/artifactkit/backends/__init__.py +0 -0
  11. agent_artifact_kit-0.1.0/artifactkit/backends/docx_backend.py +189 -0
  12. agent_artifact_kit-0.1.0/artifactkit/backends/pdf_backend.py +121 -0
  13. agent_artifact_kit-0.1.0/artifactkit/backends/pptx_backend.py +254 -0
  14. agent_artifact_kit-0.1.0/artifactkit/backends/xlsx_backend.py +159 -0
  15. agent_artifact_kit-0.1.0/artifactkit/core/__init__.py +0 -0
  16. agent_artifact_kit-0.1.0/artifactkit/core/backend.py +35 -0
  17. agent_artifact_kit-0.1.0/artifactkit/core/destinations.py +153 -0
  18. agent_artifact_kit-0.1.0/artifactkit/core/errors.py +36 -0
  19. agent_artifact_kit-0.1.0/artifactkit/core/models.py +416 -0
  20. agent_artifact_kit-0.1.0/artifactkit/core/observability.py +90 -0
  21. agent_artifact_kit-0.1.0/artifactkit/core/parsing.py +177 -0
  22. agent_artifact_kit-0.1.0/artifactkit/core/registry.py +46 -0
  23. agent_artifact_kit-0.1.0/artifactkit/core/retry.py +54 -0
  24. agent_artifact_kit-0.1.0/artifactkit/core/service.py +459 -0
  25. agent_artifact_kit-0.1.0/docs/api-reference.md +90 -0
  26. agent_artifact_kit-0.1.0/docs/architecture.md +89 -0
  27. agent_artifact_kit-0.1.0/docs/index.md +47 -0
  28. agent_artifact_kit-0.1.0/docs/observability.md +115 -0
  29. agent_artifact_kit-0.1.0/docs/quickstart.md +149 -0
  30. agent_artifact_kit-0.1.0/mkdocs.yml +44 -0
  31. agent_artifact_kit-0.1.0/pyproject.toml +53 -0
  32. agent_artifact_kit-0.1.0/site/404.html +448 -0
  33. agent_artifact_kit-0.1.0/site/api-reference/index.html +4570 -0
  34. agent_artifact_kit-0.1.0/site/architecture/index.html +718 -0
  35. agent_artifact_kit-0.1.0/site/assets/_mkdocstrings.css +237 -0
  36. agent_artifact_kit-0.1.0/site/assets/images/favicon.png +0 -0
  37. agent_artifact_kit-0.1.0/site/assets/javascripts/bundle.d7400e89.min.js +16 -0
  38. agent_artifact_kit-0.1.0/site/assets/javascripts/bundle.d7400e89.min.js.map +7 -0
  39. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.ar.min.js +1 -0
  40. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.da.min.js +18 -0
  41. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.de.min.js +18 -0
  42. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.du.min.js +18 -0
  43. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.el.min.js +1 -0
  44. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.es.min.js +18 -0
  45. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.fi.min.js +18 -0
  46. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.fr.min.js +18 -0
  47. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.he.min.js +1 -0
  48. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.hi.min.js +1 -0
  49. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.hu.min.js +18 -0
  50. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.hy.min.js +1 -0
  51. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.it.min.js +18 -0
  52. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.ja.min.js +1 -0
  53. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.jp.min.js +1 -0
  54. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.kn.min.js +1 -0
  55. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.ko.min.js +1 -0
  56. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.multi.min.js +1 -0
  57. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.nl.min.js +18 -0
  58. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.no.min.js +18 -0
  59. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.pt.min.js +18 -0
  60. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.ro.min.js +18 -0
  61. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.ru.min.js +18 -0
  62. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.sa.min.js +1 -0
  63. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.stemmer.support.min.js +1 -0
  64. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.sv.min.js +18 -0
  65. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.ta.min.js +1 -0
  66. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.te.min.js +1 -0
  67. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.th.min.js +1 -0
  68. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.tr.min.js +18 -0
  69. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.vi.min.js +1 -0
  70. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/min/lunr.zh.min.js +1 -0
  71. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/tinyseg.js +206 -0
  72. agent_artifact_kit-0.1.0/site/assets/javascripts/lunr/wordcut.js +6708 -0
  73. agent_artifact_kit-0.1.0/site/assets/javascripts/workers/search.2c215733.min.js +42 -0
  74. agent_artifact_kit-0.1.0/site/assets/javascripts/workers/search.2c215733.min.js.map +7 -0
  75. agent_artifact_kit-0.1.0/site/assets/stylesheets/main.ec1eaa64.min.css +1 -0
  76. agent_artifact_kit-0.1.0/site/assets/stylesheets/main.ec1eaa64.min.css.map +1 -0
  77. agent_artifact_kit-0.1.0/site/assets/stylesheets/palette.ab4e12ef.min.css +1 -0
  78. agent_artifact_kit-0.1.0/site/assets/stylesheets/palette.ab4e12ef.min.css.map +1 -0
  79. agent_artifact_kit-0.1.0/site/index.html +665 -0
  80. agent_artifact_kit-0.1.0/site/objects.inv +0 -0
  81. agent_artifact_kit-0.1.0/site/observability/index.html +903 -0
  82. agent_artifact_kit-0.1.0/site/quickstart/index.html +794 -0
  83. agent_artifact_kit-0.1.0/site/search/search_index.json +1 -0
  84. agent_artifact_kit-0.1.0/site/sitemap.xml +3 -0
  85. agent_artifact_kit-0.1.0/site/sitemap.xml.gz +0 -0
  86. agent_artifact_kit-0.1.0/tests/test_adapters.py +118 -0
  87. agent_artifact_kit-0.1.0/tests/test_backends.py +359 -0
  88. agent_artifact_kit-0.1.0/tests/test_destinations.py +145 -0
  89. agent_artifact_kit-0.1.0/tests/test_models.py +71 -0
  90. agent_artifact_kit-0.1.0/tests/test_observability.py +209 -0
  91. agent_artifact_kit-0.1.0/tests/test_parsing.py +109 -0
  92. agent_artifact_kit-0.1.0/tests/test_registry.py +44 -0
  93. agent_artifact_kit-0.1.0/tests/test_retry.py +97 -0
  94. agent_artifact_kit-0.1.0/tests/test_service.py +184 -0
@@ -0,0 +1,10 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.egg-info/
4
+ .pytest_cache/
5
+ .venv/
6
+ venv/
7
+ dist/
8
+ build/
9
+ .coverage
10
+ htmlcov/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dipak Rimal
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,225 @@
1
+ Metadata-Version: 2.5
2
+ Name: agent-artifact-kit
3
+ Version: 0.1.0
4
+ Summary: Shared artifact-creation harness for agentic workflows: renders docx/pptx/xlsx/pdf or delivers arbitrary files, with pluggable destinations and verified writes. Installs as agent-artifact-kit, imports as artifactkit.
5
+ Project-URL: Repository, https://github.com/drimal/artifactkit
6
+ Project-URL: Issues, https://github.com/drimal/artifactkit/issues
7
+ Author: Dipak Rimal
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: agents,artifacts,docx,mcp,pdf,pptx,s3,strands,xlsx
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Office/Business
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: openpyxl>=3.1.0
23
+ Requires-Dist: pypdf>=4.0.0
24
+ Requires-Dist: python-docx>=1.1.0
25
+ Requires-Dist: python-pptx>=1.0.0
26
+ Requires-Dist: reportlab>=4.2.0
27
+ Provides-Extra: all
28
+ Requires-Dist: boto3>=1.34.0; extra == 'all'
29
+ Requires-Dist: mcp<2.0.0,>=1.0.0; extra == 'all'
30
+ Requires-Dist: strands-agents; extra == 'all'
31
+ Provides-Extra: dev
32
+ Requires-Dist: build; extra == 'dev'
33
+ Requires-Dist: pytest-cov; extra == 'dev'
34
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
35
+ Requires-Dist: twine; extra == 'dev'
36
+ Provides-Extra: docs
37
+ Requires-Dist: mkdocs; extra == 'docs'
38
+ Requires-Dist: mkdocs-material; extra == 'docs'
39
+ Requires-Dist: mkdocstrings[python]; extra == 'docs'
40
+ Requires-Dist: ruff; extra == 'docs'
41
+ Provides-Extra: mcp
42
+ Requires-Dist: mcp<2.0.0,>=1.0.0; extra == 'mcp'
43
+ Provides-Extra: s3
44
+ Requires-Dist: boto3>=1.34.0; extra == 's3'
45
+ Provides-Extra: strands
46
+ Requires-Dist: strands-agents; extra == 'strands'
47
+ Description-Content-Type: text/markdown
48
+
49
+ # artifactkit
50
+
51
+ Installs as `agent-artifact-kit`, imports as `artifactkit`
52
+ (same split as `beautifulsoup4` → `bs4`: the PyPI name is descriptive
53
+ for discoverability, the import name is short because that's what you
54
+ actually type in code).
55
+
56
+ Generic artifact creation harness for agentic workflows. Agents build a
57
+ declarative content spec; artifactkit renders it to docx/pptx/xlsx/pdf,
58
+ writes it to a destination (local disk or S3), verifies the write, and
59
+ returns the location. No agent workflow reimplements this on its own.
60
+
61
+ ## Install
62
+
63
+ ```
64
+ pip install agent-artifact-kit[s3] # local + S3
65
+ pip install agent-artifact-kit[all] # + Strands and MCP adapters
66
+ ```
67
+
68
+ All code below imports the package as `artifactkit` regardless of
69
+ which extras you installed.
70
+
71
+ ## Development
72
+
73
+ ```bash
74
+ pip install -e ".[dev]"
75
+ pytest tests/ -v
76
+ pytest tests/ --cov=artifactkit --cov-report=term-missing # 92% with all extras installed
77
+ ```
78
+
79
+ See `PUBLISHING.md` for how to build and release to PyPI, and
80
+ `mkdocs.yml` / `docs/` for the full documentation site (`pip install
81
+ -e ".[docs]"` then `mkdocs serve`).
82
+
83
+ ## Observability
84
+
85
+ Every call is logged (structured, correlated by an `operation_id`
86
+ returned on the result) and, if you supply a `MetricsSink`, measured.
87
+ See `docs/observability.md` for the exact log events, fields, and
88
+ metric names, or wire in your own sink:
89
+
90
+ ```python
91
+ from artifactkit import ArtifactService, MetricsSink
92
+
93
+ class MySink:
94
+ def increment(self, name, tags=None): ...
95
+ def timing(self, name, duration_ms, tags=None): ...
96
+
97
+ service = ArtifactService(metrics=MySink())
98
+ ```
99
+
100
+ ## Plain Python
101
+
102
+ ```python
103
+ from artifactkit import (
104
+ ArtifactService, ArtifactFormat, DocumentSpec, Heading, Paragraph,
105
+ destination_from_uri,
106
+ )
107
+
108
+ spec = DocumentSpec(
109
+ title="Q3 Summary",
110
+ blocks=(
111
+ Heading("Overview", level=1),
112
+ Paragraph.of("Revenue grew 12% quarter over quarter."),
113
+ ),
114
+ base_filename="q3_summary", # optional — bare name, no extension
115
+ )
116
+
117
+ service = ArtifactService()
118
+ result = service.create(
119
+ spec,
120
+ ArtifactFormat.DOCX,
121
+ destination=destination_from_uri("s3://reports-bucket/q3"),
122
+ include_presigned_url=True,
123
+ # filename omitted: resolves to "q3_summary.docx" from spec.base_filename,
124
+ # adapted to whatever format is requested. Pass filename= explicitly to
125
+ # override it for this call without changing the spec.
126
+ )
127
+ print(result.location, result.presigned_url)
128
+ ```
129
+
130
+ ## Strands agent
131
+
132
+ ```python
133
+ from strands import Agent
134
+ from artifactkit.adapters.strands_tools import create_docx, create_pptx, create_xlsx, create_pdf
135
+
136
+ agent = Agent(tools=[create_docx, create_pptx, create_xlsx, create_pdf])
137
+ ```
138
+
139
+ ## MCP server
140
+
141
+ ```
142
+ python -m artifactkit.adapters.mcp_server
143
+ ```
144
+
145
+ Any MCP-capable agent can then call `create_docx` / `create_pptx` /
146
+ `create_xlsx` / `create_pdf` with a JSON spec, a `destination_uri`, and
147
+ a `filename`.
148
+
149
+ ## Spec shape
150
+
151
+ See `artifactkit/core/parsing.py` for the exact dict shape each tool
152
+ expects — it's the single source of truth both adapters parse against.
153
+
154
+ ## Styling
155
+
156
+ Bare output from python-docx/pptx/openpyxl looks exactly like what it
157
+ is: unstyled. Set `theme` on any structured spec for a named preset
158
+ (`Theme.VIBRANT`, `Theme.CORPORATE`, `Theme.MINIMAL`) that each backend
159
+ applies in whatever way fits its format — colored headings and an
160
+ accent rule in a doc, a gradient hero slide with a decorative shape in
161
+ a deck, a native Excel Table with banded rows and a colored tab in a
162
+ workbook. See `docs/quickstart.md` for examples of all three.
163
+
164
+ ## Arbitrary files (code, images, anything else)
165
+
166
+ `create()` is for structured content that needs rendering (docx/pptx/xlsx/pdf).
167
+ For content you already have, code files, images, data files, whatever,
168
+ use `create_files()` instead. It skips rendering entirely and goes
169
+ straight to write + verify. If you hand it more than 5 files, it
170
+ bundles them into a single `.zip` automatically rather than doing one
171
+ write per file.
172
+
173
+ Each `RawFile` takes its content one of two ways:
174
+
175
+ ```python
176
+ from artifactkit import ArtifactService, RawFile, FileBundleSpec, destination_from_uri
177
+
178
+ # In-memory bytes (e.g. code the agent just wrote)
179
+ RawFile("main.py", content=b"print('hello')\n")
180
+
181
+ # A path already on disk (e.g. output from code that ran, a generated
182
+ # image, anything already written somewhere) -- read lazily at write
183
+ # time, never loaded into memory until it's actually needed.
184
+ RawFile("chart.png", source_path="/tmp/agent_run/chart.png")
185
+ ```
186
+
187
+ If a piece of code produced a whole directory of output, skip
188
+ building `RawFile`s by hand:
189
+
190
+ ```python
191
+ bundle = FileBundleSpec.from_directory("/tmp/agent_run", bundle_filename="run_output")
192
+ service = ArtifactService()
193
+ result = service.create_files(bundle, destination_from_uri("s3://reports-bucket/runs"))
194
+ # 5 or fewer files in the directory -> delivered individually
195
+ # 6+ -> bundled into run_output.zip
196
+ ```
197
+
198
+ Via Strands or MCP, this is the `deliver_files` tool. Each file entry
199
+ needs exactly one of `content_base64` (for content the agent is
200
+ holding directly) or `source_path` (for content already on disk,
201
+ avoids hauling large payloads through the model's context just to
202
+ hand them back to this tool).
203
+
204
+ **artifactkit never executes code or generates content.** It only
205
+ delivers bytes that already exist. If `app.py` needs to run and
206
+ produce files, that execution happens entirely outside this library,
207
+ with whatever sandboxed runtime the agent already has, and
208
+ `create_files()` picks up only once the output exists on disk.
209
+
210
+ ## Design notes
211
+
212
+ - Render, validate, and write/verify are three separate stages. Only
213
+ the write/verify stage retries; render and validate failures are
214
+ deterministic and retrying wastes time.
215
+ - `ArtifactValidationError` (bad spec/render) and `ArtifactWriteError`
216
+ (write couldn't be verified) are distinct exception types so a
217
+ calling agent can choose the right remediation.
218
+ - Destinations are pluggable via the `OutputDestination` protocol.
219
+ Adding GCS/Azure support means one new class, no changes to backends
220
+ or the service.
221
+ - `create()` and `create_files()` share the same write/verify/retry/
222
+ presign machinery (`ArtifactService._write_verified`), the only
223
+ difference is whether there's a render+validate step first. Arbitrary
224
+ files skip it because there's no format-specific structure to check
225
+ on content the caller already fully formed.
@@ -0,0 +1,70 @@
1
+ # Publishing artifactkit to PyPI
2
+
3
+ ## First-time setup
4
+
5
+ 1. Confirm the name is still free: https://pypi.org/project/agent-artifact-kit/
6
+ (a text search can miss a reserved-but-empty name -- check the URL directly)
7
+ 2. Create a PyPI account and, separately, a TestPyPI account: https://pypi.org/account/register/
8
+ 3. Fix the placeholder URLs in `pyproject.toml` (`[project.urls]`) once the
9
+ repo has a real home -- PyPI displays these on the project page.
10
+ 4. Recommended: use Trusted Publishing (OIDC from GitHub Actions) instead of
11
+ a long-lived API token: https://docs.pypi.org/trusted-publishers/
12
+ If publishing manually instead, generate a scoped API token under
13
+ PyPI account settings and use `__token__` as the username when prompted.
14
+
15
+ Note the name split: the PyPI/distribution name is
16
+ `agent-artifact-kit` (set in `pyproject.toml`'s `[project] name`),
17
+ but the importable package stays `artifactkit` (`[tool.hatch.build.targets.wheel]`
18
+ still points at the `artifactkit/` directory). Don't rename one without
19
+ checking the other -- `pip install agent-artifact-kit` should
20
+ always leave you with `import artifactkit` working, and the `all` extra's
21
+ self-reference (`agent-artifact-kit[s3,strands,mcp]`) must use
22
+ the distribution name, not the import name, or extras resolution breaks.
23
+
24
+ ## Documentation
25
+
26
+ ```bash
27
+ pip install -e ".[docs]"
28
+ mkdocs serve # live preview at http://127.0.0.1:8000
29
+ mkdocs build --strict # what CI should run before merging docs changes
30
+ ```
31
+
32
+ `--strict` turns warnings (broken `::` autodoc references, bad internal
33
+ links) into build failures — always run it before publishing, not just
34
+ `mkdocs build`.
35
+
36
+ ## Every release
37
+
38
+ ```bash
39
+ # from the package root
40
+ pip install --upgrade build twine
41
+
42
+ # 1. bump version in pyproject.toml, then:
43
+ rm -rf dist/
44
+ python -m build
45
+ twine check dist/* # catches metadata/README rendering problems
46
+ # before they reach PyPI
47
+
48
+ # 2. dry run against TestPyPI first
49
+ twine upload --repository testpypi dist/*
50
+ pip install --index-url https://test.pypi.org/simple/ agent-artifact-kit
51
+
52
+ # 3. the real upload
53
+ twine upload dist/*
54
+ ```
55
+
56
+ ## Before the first real upload
57
+
58
+ - Run the test suite: `pytest tests/ -v` (87 tests with all extras
59
+ installed; 78 pass + 9 skip cleanly without `[strands]`/`[mcp]`)
60
+ - Run `pytest --cov=artifactkit` if you want a coverage number to publish
61
+ - Confirm `pip install agent-artifact-kit[all]` in a fresh venv pulls everything
62
+ cleanly (this is what a new user's first experience will be)
63
+ - Double check `LICENSE` and the `authors` field in `pyproject.toml` reflect
64
+ who should actually be listed
65
+
66
+ ## Versioning
67
+
68
+ `pyproject.toml` version is the single source of truth. There is no
69
+ dynamic versioning configured, so bump it by hand before each release
70
+ and tag the commit to match (`git tag v0.1.1`).
@@ -0,0 +1,177 @@
1
+ # artifactkit
2
+
3
+ Installs as `agent-artifact-kit`, imports as `artifactkit`
4
+ (same split as `beautifulsoup4` → `bs4`: the PyPI name is descriptive
5
+ for discoverability, the import name is short because that's what you
6
+ actually type in code).
7
+
8
+ Generic artifact creation harness for agentic workflows. Agents build a
9
+ declarative content spec; artifactkit renders it to docx/pptx/xlsx/pdf,
10
+ writes it to a destination (local disk or S3), verifies the write, and
11
+ returns the location. No agent workflow reimplements this on its own.
12
+
13
+ ## Install
14
+
15
+ ```
16
+ pip install agent-artifact-kit[s3] # local + S3
17
+ pip install agent-artifact-kit[all] # + Strands and MCP adapters
18
+ ```
19
+
20
+ All code below imports the package as `artifactkit` regardless of
21
+ which extras you installed.
22
+
23
+ ## Development
24
+
25
+ ```bash
26
+ pip install -e ".[dev]"
27
+ pytest tests/ -v
28
+ pytest tests/ --cov=artifactkit --cov-report=term-missing # 92% with all extras installed
29
+ ```
30
+
31
+ See `PUBLISHING.md` for how to build and release to PyPI, and
32
+ `mkdocs.yml` / `docs/` for the full documentation site (`pip install
33
+ -e ".[docs]"` then `mkdocs serve`).
34
+
35
+ ## Observability
36
+
37
+ Every call is logged (structured, correlated by an `operation_id`
38
+ returned on the result) and, if you supply a `MetricsSink`, measured.
39
+ See `docs/observability.md` for the exact log events, fields, and
40
+ metric names, or wire in your own sink:
41
+
42
+ ```python
43
+ from artifactkit import ArtifactService, MetricsSink
44
+
45
+ class MySink:
46
+ def increment(self, name, tags=None): ...
47
+ def timing(self, name, duration_ms, tags=None): ...
48
+
49
+ service = ArtifactService(metrics=MySink())
50
+ ```
51
+
52
+ ## Plain Python
53
+
54
+ ```python
55
+ from artifactkit import (
56
+ ArtifactService, ArtifactFormat, DocumentSpec, Heading, Paragraph,
57
+ destination_from_uri,
58
+ )
59
+
60
+ spec = DocumentSpec(
61
+ title="Q3 Summary",
62
+ blocks=(
63
+ Heading("Overview", level=1),
64
+ Paragraph.of("Revenue grew 12% quarter over quarter."),
65
+ ),
66
+ base_filename="q3_summary", # optional — bare name, no extension
67
+ )
68
+
69
+ service = ArtifactService()
70
+ result = service.create(
71
+ spec,
72
+ ArtifactFormat.DOCX,
73
+ destination=destination_from_uri("s3://reports-bucket/q3"),
74
+ include_presigned_url=True,
75
+ # filename omitted: resolves to "q3_summary.docx" from spec.base_filename,
76
+ # adapted to whatever format is requested. Pass filename= explicitly to
77
+ # override it for this call without changing the spec.
78
+ )
79
+ print(result.location, result.presigned_url)
80
+ ```
81
+
82
+ ## Strands agent
83
+
84
+ ```python
85
+ from strands import Agent
86
+ from artifactkit.adapters.strands_tools import create_docx, create_pptx, create_xlsx, create_pdf
87
+
88
+ agent = Agent(tools=[create_docx, create_pptx, create_xlsx, create_pdf])
89
+ ```
90
+
91
+ ## MCP server
92
+
93
+ ```
94
+ python -m artifactkit.adapters.mcp_server
95
+ ```
96
+
97
+ Any MCP-capable agent can then call `create_docx` / `create_pptx` /
98
+ `create_xlsx` / `create_pdf` with a JSON spec, a `destination_uri`, and
99
+ a `filename`.
100
+
101
+ ## Spec shape
102
+
103
+ See `artifactkit/core/parsing.py` for the exact dict shape each tool
104
+ expects — it's the single source of truth both adapters parse against.
105
+
106
+ ## Styling
107
+
108
+ Bare output from python-docx/pptx/openpyxl looks exactly like what it
109
+ is: unstyled. Set `theme` on any structured spec for a named preset
110
+ (`Theme.VIBRANT`, `Theme.CORPORATE`, `Theme.MINIMAL`) that each backend
111
+ applies in whatever way fits its format — colored headings and an
112
+ accent rule in a doc, a gradient hero slide with a decorative shape in
113
+ a deck, a native Excel Table with banded rows and a colored tab in a
114
+ workbook. See `docs/quickstart.md` for examples of all three.
115
+
116
+ ## Arbitrary files (code, images, anything else)
117
+
118
+ `create()` is for structured content that needs rendering (docx/pptx/xlsx/pdf).
119
+ For content you already have, code files, images, data files, whatever,
120
+ use `create_files()` instead. It skips rendering entirely and goes
121
+ straight to write + verify. If you hand it more than 5 files, it
122
+ bundles them into a single `.zip` automatically rather than doing one
123
+ write per file.
124
+
125
+ Each `RawFile` takes its content one of two ways:
126
+
127
+ ```python
128
+ from artifactkit import ArtifactService, RawFile, FileBundleSpec, destination_from_uri
129
+
130
+ # In-memory bytes (e.g. code the agent just wrote)
131
+ RawFile("main.py", content=b"print('hello')\n")
132
+
133
+ # A path already on disk (e.g. output from code that ran, a generated
134
+ # image, anything already written somewhere) -- read lazily at write
135
+ # time, never loaded into memory until it's actually needed.
136
+ RawFile("chart.png", source_path="/tmp/agent_run/chart.png")
137
+ ```
138
+
139
+ If a piece of code produced a whole directory of output, skip
140
+ building `RawFile`s by hand:
141
+
142
+ ```python
143
+ bundle = FileBundleSpec.from_directory("/tmp/agent_run", bundle_filename="run_output")
144
+ service = ArtifactService()
145
+ result = service.create_files(bundle, destination_from_uri("s3://reports-bucket/runs"))
146
+ # 5 or fewer files in the directory -> delivered individually
147
+ # 6+ -> bundled into run_output.zip
148
+ ```
149
+
150
+ Via Strands or MCP, this is the `deliver_files` tool. Each file entry
151
+ needs exactly one of `content_base64` (for content the agent is
152
+ holding directly) or `source_path` (for content already on disk,
153
+ avoids hauling large payloads through the model's context just to
154
+ hand them back to this tool).
155
+
156
+ **artifactkit never executes code or generates content.** It only
157
+ delivers bytes that already exist. If `app.py` needs to run and
158
+ produce files, that execution happens entirely outside this library,
159
+ with whatever sandboxed runtime the agent already has, and
160
+ `create_files()` picks up only once the output exists on disk.
161
+
162
+ ## Design notes
163
+
164
+ - Render, validate, and write/verify are three separate stages. Only
165
+ the write/verify stage retries; render and validate failures are
166
+ deterministic and retrying wastes time.
167
+ - `ArtifactValidationError` (bad spec/render) and `ArtifactWriteError`
168
+ (write couldn't be verified) are distinct exception types so a
169
+ calling agent can choose the right remediation.
170
+ - Destinations are pluggable via the `OutputDestination` protocol.
171
+ Adding GCS/Azure support means one new class, no changes to backends
172
+ or the service.
173
+ - `create()` and `create_files()` share the same write/verify/retry/
174
+ presign machinery (`ArtifactService._write_verified`), the only
175
+ difference is whether there's a render+validate step first. Arbitrary
176
+ files skip it because there's no format-specific structure to check
177
+ on content the caller already fully formed.
@@ -0,0 +1,89 @@
1
+ """artifactkit: a shared artifact-creation harness for agentic workflows.
2
+
3
+ Public API surface — everything an agent or a plain-Python caller
4
+ needs is importable from the top level:
5
+
6
+ from artifactkit import (
7
+ ArtifactService, ArtifactFormat, ArtifactResult,
8
+ DocumentSpec, PresentationSpec, WorkbookSpec,
9
+ Heading, Paragraph, ListBlock, Table, Image, PageBreak, TextRun, TextStyle,
10
+ destination_from_uri,
11
+ ArtifactError, ArtifactValidationError, ArtifactWriteError,
12
+ )
13
+ """
14
+
15
+ from artifactkit.core.destinations import (
16
+ LocalDirectoryDestination,
17
+ OutputDestination,
18
+ S3Destination,
19
+ destination_from_uri,
20
+ )
21
+ from artifactkit.core.errors import (
22
+ ArtifactError,
23
+ ArtifactValidationError,
24
+ ArtifactWriteError,
25
+ DestinationError,
26
+ UnsupportedFormatError,
27
+ )
28
+ from artifactkit.core.models import (
29
+ ArtifactFormat,
30
+ ArtifactSpec,
31
+ DocumentSpec,
32
+ FileBundleSpec,
33
+ Heading,
34
+ Image,
35
+ ListBlock,
36
+ PageBreak,
37
+ Paragraph,
38
+ PresentationSpec,
39
+ Theme,
40
+ RawFile,
41
+ Sheet,
42
+ Slide,
43
+ Table,
44
+ TextRun,
45
+ TextStyle,
46
+ WorkbookSpec,
47
+ )
48
+ from artifactkit.core.observability import LoggingMetricsSink, MetricsSink, NoOpMetricsSink
49
+ from artifactkit.core.retry import RetryPolicy
50
+ from artifactkit.core.service import ArtifactResult, ArtifactService, FileDeliveryResult
51
+
52
+ __version__ = "0.1.0"
53
+
54
+ __all__ = [
55
+ "ArtifactService",
56
+ "ArtifactResult",
57
+ "ArtifactFormat",
58
+ "ArtifactSpec",
59
+ "RetryPolicy",
60
+ "DocumentSpec",
61
+ "Heading",
62
+ "Paragraph",
63
+ "ListBlock",
64
+ "Table",
65
+ "Image",
66
+ "PageBreak",
67
+ "TextRun",
68
+ "TextStyle",
69
+ "PresentationSpec",
70
+ "Theme",
71
+ "Slide",
72
+ "WorkbookSpec",
73
+ "Sheet",
74
+ "RawFile",
75
+ "FileBundleSpec",
76
+ "FileDeliveryResult",
77
+ "MetricsSink",
78
+ "NoOpMetricsSink",
79
+ "LoggingMetricsSink",
80
+ "OutputDestination",
81
+ "LocalDirectoryDestination",
82
+ "S3Destination",
83
+ "destination_from_uri",
84
+ "ArtifactError",
85
+ "ArtifactValidationError",
86
+ "ArtifactWriteError",
87
+ "UnsupportedFormatError",
88
+ "DestinationError",
89
+ ]