docsynth 0.0.0__tar.gz → 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 (266) hide show
  1. docsynth-0.1.0/.env.example +92 -0
  2. docsynth-0.1.0/.github/CODEOWNERS +8 -0
  3. docsynth-0.1.0/.github/pull_request_template.md +76 -0
  4. docsynth-0.1.0/.github/workflows/ci.yml +52 -0
  5. docsynth-0.1.0/.github/workflows/docs.yml +29 -0
  6. docsynth-0.1.0/.github/workflows/release.yml +50 -0
  7. docsynth-0.1.0/.gitignore +70 -0
  8. docsynth-0.1.0/CONTRIBUTING.md +74 -0
  9. docsynth-0.1.0/Dockerfile +67 -0
  10. docsynth-0.1.0/LICENSE +21 -0
  11. docsynth-0.1.0/PKG-INFO +223 -0
  12. docsynth-0.1.0/README.md +166 -0
  13. docsynth-0.1.0/TODO.md +665 -0
  14. docsynth-0.1.0/deploy/gcp/deploy.sh +824 -0
  15. docsynth-0.1.0/deploy/gcp/run.example.yaml +239 -0
  16. docsynth-0.1.0/deploy/gcp/verify_coherence.py +65 -0
  17. docsynth-0.1.0/pyproject.toml +117 -0
  18. docsynth-0.1.0/samples/00_meta-sidebar-01_en-US_b2b_saas_USD_wm.pdf +0 -0
  19. docsynth-0.1.0/samples/01_meta-sidebar-01_en-CA_wholesale_CAD_mark-nologo.pdf +0 -0
  20. docsynth-0.1.0/samples/02_receipt-compact-01_en-CA_retail_CAD_mark.pdf +0 -0
  21. docsynth-0.1.0/samples/03_meta-sidebar-01_en-CA_wholesale_CAD_mark-nologo.pdf +0 -0
  22. docsynth-0.1.0/samples/04_receipt-compact-01_en-CA_auto_repair_CAD_mark.pdf +0 -0
  23. docsynth-0.1.0/samples/05_banner-header-06_en-CA_wholesale_CAD_mark.pdf +0 -0
  24. docsynth-0.1.0/samples/06_telecom-itemized-37_en-US_telecom_USD_plain.pdf +0 -0
  25. docsynth-0.1.0/samples/07_banner-header-06_en-CA_wholesale_CAD_mark.pdf +0 -0
  26. docsynth-0.1.0/samples/08_boxed-form-01_en-GB_ai_platform_GBP_mark.pdf +0 -0
  27. docsynth-0.1.0/samples/09_banner-header-06_en-GB_auto_repair_GBP_nologo.pdf +0 -0
  28. docsynth-0.1.0/samples/10_boxed-form-01_en-CA_auto_repair_CAD_mark-nologo.pdf +0 -0
  29. docsynth-0.1.0/samples/11_fullbleed-05_en-CA_auto_repair_CAD_mark.pdf +0 -0
  30. docsynth-0.1.0/samples/12_boxed-form-01_en-GB_retail_GBP_plain.pdf +0 -0
  31. docsynth-0.1.0/samples/13_fullbleed-05_en-GB_retail_GBP_mark.pdf +0 -0
  32. docsynth-0.1.0/samples/14_banner-header-06_en-GB_retail_GBP_mark-wm-nologo.pdf +0 -0
  33. docsynth-0.1.0/samples/15_banner-header-06_en-US_b2b_saas_USD_nologo.pdf +0 -0
  34. docsynth-0.1.0/samples/16_meta-sidebar-01_en-US_b2b_saas_USD_wm.pdf +0 -0
  35. docsynth-0.1.0/samples/17_receipt-compact-01_en-GB_retail_GBP_plain.pdf +0 -0
  36. docsynth-0.1.0/samples/18_fullbleed-05_en-GB_ai_platform_GBP_mark.pdf +0 -0
  37. docsynth-0.1.0/samples/19_receipt-compact-01_en-GB_retail_GBP_plain.pdf +0 -0
  38. docsynth-0.1.0/samples/20_boxed-form-01_en-CA_auto_repair_CAD_mark-nologo.pdf +0 -0
  39. docsynth-0.1.0/samples/21_banner-header-06_en-US_auto_repair_USD_mark.pdf +0 -0
  40. docsynth-0.1.0/samples/22_telecom-itemized-37_en-CA_telecom_CAD_mark.pdf +0 -0
  41. docsynth-0.1.0/samples/23_meta-sidebar-01_en-GB_auto_repair_GBP_plain.pdf +0 -0
  42. docsynth-0.1.0/samples/24_receipt-compact-01_en-US_b2b_saas_USD_mark.pdf +0 -0
  43. docsynth-0.1.0/samples/25_meta-sidebar-01_en-GB_ai_platform_GBP_mark.pdf +0 -0
  44. docsynth-0.1.0/samples/26_boxed-form-01_en-US_b2b_saas_USD_plain.pdf +0 -0
  45. docsynth-0.1.0/samples/27_boxed-form-01_en-US_b2b_saas_USD_plain.pdf +0 -0
  46. docsynth-0.1.0/samples/28_fullbleed-05_en-CA_retail_CAD_mark-nologo.pdf +0 -0
  47. docsynth-0.1.0/samples/29_receipt-compact-01_en-US_retail_USD_plain.pdf +0 -0
  48. docsynth-0.1.0/samples/30_meta-sidebar-01_fr-CA_wholesale_CAD_nologo.pdf +0 -0
  49. docsynth-0.1.0/samples/31_meta-sidebar-01_fr-CA_retail_CAD_plain.pdf +0 -0
  50. docsynth-0.1.0/samples/32_meta-sidebar-01_fr-FR_ai_platform_EUR_mark.pdf +0 -0
  51. docsynth-0.1.0/samples/33_meta-sidebar-01_fr-FR_ai_platform_EUR_mark.pdf +0 -0
  52. docsynth-0.1.0/samples/34_banner-header-06_fr-FR_accounting_EUR_mark-wm.pdf +0 -0
  53. docsynth-0.1.0/samples/35_banner-header-06_fr-FR_accounting_EUR_mark-wm.pdf +0 -0
  54. docsynth-0.1.0/samples/36_boxed-form-01_fr-FR_auto_repair_EUR_mark.pdf +0 -0
  55. docsynth-0.1.0/samples/37_boxed-form-01_fr-FR_auto_repair_EUR_mark.pdf +0 -0
  56. docsynth-0.1.0/samples/38_bundled-noto-serif_en-US.pdf +0 -0
  57. docsynth-0.1.0/samples/39_bundled-jetbrains-mono_en-US.pdf +0 -0
  58. docsynth-0.1.0/samples/40_telecom-contd_en_multipage.pdf +0 -0
  59. docsynth-0.1.0/samples/41_condition-clean.pdf +0 -0
  60. docsynth-0.1.0/samples/42_condition-light-scan.pdf +0 -0
  61. docsynth-0.1.0/samples/43_condition-heavy-scan.pdf +0 -0
  62. docsynth-0.1.0/samples/44_condition-handwritten.pdf +0 -0
  63. docsynth-0.1.0/samples/45_handwritten_fr_neat.pdf +0 -0
  64. docsynth-0.1.0/samples/46_handwritten_en_scrawl.pdf +0 -0
  65. docsynth-0.1.0/samples/47_handwritten_crisp.pdf +0 -0
  66. docsynth-0.1.0/samples/48_handwritten_goods-receipt.pdf +0 -0
  67. docsynth-0.1.0/samples/README.md +50 -0
  68. docsynth-0.1.0/scripts/smoke_catalogue.py +213 -0
  69. docsynth-0.1.0/site-docs/README.md +38 -0
  70. docsynth-0.1.0/site-docs/docs/architecture/concurrency.md +68 -0
  71. docsynth-0.1.0/site-docs/docs/architecture/contract.md +128 -0
  72. docsynth-0.1.0/site-docs/docs/architecture/data-model.md +47 -0
  73. docsynth-0.1.0/site-docs/docs/architecture/flows.md +74 -0
  74. docsynth-0.1.0/site-docs/docs/architecture/overview.md +100 -0
  75. docsynth-0.1.0/site-docs/docs/core/misc.md +60 -0
  76. docsynth-0.1.0/site-docs/docs/core/overview.md +51 -0
  77. docsynth-0.1.0/site-docs/docs/core/pipeline.md +83 -0
  78. docsynth-0.1.0/site-docs/docs/core/providers.md +77 -0
  79. docsynth-0.1.0/site-docs/docs/core/state.md +82 -0
  80. docsynth-0.1.0/site-docs/docs/core/storage.md +69 -0
  81. docsynth-0.1.0/site-docs/docs/design-decisions.md +97 -0
  82. docsynth-0.1.0/site-docs/docs/index.md +45 -0
  83. docsynth-0.1.0/site-docs/docs/invoice/catalogue.md +69 -0
  84. docsynth-0.1.0/site-docs/docs/invoice/composition.md +59 -0
  85. docsynth-0.1.0/site-docs/docs/invoice/content.md +64 -0
  86. docsynth-0.1.0/site-docs/docs/invoice/export.md +54 -0
  87. docsynth-0.1.0/site-docs/docs/invoice/generation.md +72 -0
  88. docsynth-0.1.0/site-docs/docs/invoice/overview.md +67 -0
  89. docsynth-0.1.0/site-docs/docs/invoice/reference.md +39 -0
  90. docsynth-0.1.0/site-docs/docs/invoice/rendering.md +64 -0
  91. docsynth-0.1.0/site-docs/docs/operations/deploy-tool.md +60 -0
  92. docsynth-0.1.0/site-docs/docs/operations/docs-site.md +38 -0
  93. docsynth-0.1.0/site-docs/docs/operations/gcp.md +53 -0
  94. docsynth-0.1.0/site-docs/docs/operations/publishing.md +79 -0
  95. docsynth-0.1.0/site-docs/docs/operations/running.md +51 -0
  96. docsynth-0.1.0/site-docs/docs/quickstart.md +106 -0
  97. docsynth-0.1.0/site-docs/docs/studio/catalogue.md +58 -0
  98. docsynth-0.1.0/site-docs/docs/studio/dispatch.md +43 -0
  99. docsynth-0.1.0/site-docs/docs/studio/overview.md +58 -0
  100. docsynth-0.1.0/site-docs/docs/studio/projects.md +52 -0
  101. docsynth-0.1.0/site-docs/docs/studio/steps.md +45 -0
  102. docsynth-0.1.0/site-docs/docs/studio/tuning.md +50 -0
  103. docsynth-0.1.0/site-docs/mkdocs.yml +96 -0
  104. docsynth-0.1.0/site-docs/requirements.txt +3 -0
  105. docsynth-0.1.0/src/docsynth/__init__.py +16 -0
  106. docsynth-0.1.0/src/docsynth/__main__.py +10 -0
  107. docsynth-0.1.0/src/docsynth/cli.py +1134 -0
  108. docsynth-0.1.0/src/docsynth/core/__init__.py +55 -0
  109. docsynth-0.1.0/src/docsynth/core/content.py +137 -0
  110. docsynth-0.1.0/src/docsynth/core/enums.py +62 -0
  111. docsynth-0.1.0/src/docsynth/core/fonts/ATTRIBUTION.md +38 -0
  112. docsynth-0.1.0/src/docsynth/core/fonts/OFL.txt +109 -0
  113. docsynth-0.1.0/src/docsynth/core/fonts/__init__.py +187 -0
  114. docsynth-0.1.0/src/docsynth/core/fonts/files/hand-casual-400.woff2 +0 -0
  115. docsynth-0.1.0/src/docsynth/core/fonts/files/hand-neat-400.woff2 +0 -0
  116. docsynth-0.1.0/src/docsynth/core/fonts/files/hand-print-400.woff2 +0 -0
  117. docsynth-0.1.0/src/docsynth/core/fonts/files/hand-scrawl-400.woff2 +0 -0
  118. docsynth-0.1.0/src/docsynth/core/fonts/files/mono-invoice-400.woff2 +0 -0
  119. docsynth-0.1.0/src/docsynth/core/fonts/files/mono-invoice-700.woff2 +0 -0
  120. docsynth-0.1.0/src/docsynth/core/fonts/files/sans-neutral-400.woff2 +0 -0
  121. docsynth-0.1.0/src/docsynth/core/fonts/files/sans-neutral-700.woff2 +0 -0
  122. docsynth-0.1.0/src/docsynth/core/fonts/files/serif-classic-400.woff2 +0 -0
  123. docsynth-0.1.0/src/docsynth/core/fonts/files/serif-classic-700.woff2 +0 -0
  124. docsynth-0.1.0/src/docsynth/core/fonts/files/signature-400.woff2 +0 -0
  125. docsynth-0.1.0/src/docsynth/core/fonts/files/slab-400.woff2 +0 -0
  126. docsynth-0.1.0/src/docsynth/core/fonts/files/slab-700.woff2 +0 -0
  127. docsynth-0.1.0/src/docsynth/core/fonts/files/stamp-400.woff2 +0 -0
  128. docsynth-0.1.0/src/docsynth/core/locale/__init__.py +34 -0
  129. docsynth-0.1.0/src/docsynth/core/locale/enums.py +67 -0
  130. docsynth-0.1.0/src/docsynth/core/locale/formatting.py +201 -0
  131. docsynth-0.1.0/src/docsynth/core/locale/labels.py +90 -0
  132. docsynth-0.1.0/src/docsynth/core/logging.py +179 -0
  133. docsynth-0.1.0/src/docsynth/core/money.py +42 -0
  134. docsynth-0.1.0/src/docsynth/core/pack.py +143 -0
  135. docsynth-0.1.0/src/docsynth/core/pipeline/__init__.py +51 -0
  136. docsynth-0.1.0/src/docsynth/core/pipeline/degrade.py +180 -0
  137. docsynth-0.1.0/src/docsynth/core/pipeline/export.py +72 -0
  138. docsynth-0.1.0/src/docsynth/core/pipeline/golden.py +69 -0
  139. docsynth-0.1.0/src/docsynth/core/pipeline/manifest.py +414 -0
  140. docsynth-0.1.0/src/docsynth/core/pipeline/pdf.py +309 -0
  141. docsynth-0.1.0/src/docsynth/core/pipeline/planner.py +36 -0
  142. docsynth-0.1.0/src/docsynth/core/pipeline/renderer.py +50 -0
  143. docsynth-0.1.0/src/docsynth/core/pipeline/run.py +184 -0
  144. docsynth-0.1.0/src/docsynth/core/pipeline/source.py +64 -0
  145. docsynth-0.1.0/src/docsynth/core/pipeline/worker.py +162 -0
  146. docsynth-0.1.0/src/docsynth/core/providers/__init__.py +50 -0
  147. docsynth-0.1.0/src/docsynth/core/providers/anthropic_provider.py +178 -0
  148. docsynth-0.1.0/src/docsynth/core/providers/base.py +80 -0
  149. docsynth-0.1.0/src/docsynth/core/providers/budget.py +210 -0
  150. docsynth-0.1.0/src/docsynth/core/providers/catalogue_runner.py +292 -0
  151. docsynth-0.1.0/src/docsynth/core/providers/factory.py +112 -0
  152. docsynth-0.1.0/src/docsynth/core/providers/mix.py +196 -0
  153. docsynth-0.1.0/src/docsynth/core/providers/openai_compatible.py +153 -0
  154. docsynth-0.1.0/src/docsynth/core/providers/pricing.py +69 -0
  155. docsynth-0.1.0/src/docsynth/core/record.py +47 -0
  156. docsynth-0.1.0/src/docsynth/core/registry.py +73 -0
  157. docsynth-0.1.0/src/docsynth/core/render/__init__.py +5 -0
  158. docsynth-0.1.0/src/docsynth/core/render/environment.py +115 -0
  159. docsynth-0.1.0/src/docsynth/core/selection.py +272 -0
  160. docsynth-0.1.0/src/docsynth/core/sinks/__init__.py +62 -0
  161. docsynth-0.1.0/src/docsynth/core/sinks/arrow.py +85 -0
  162. docsynth-0.1.0/src/docsynth/core/sinks/base.py +41 -0
  163. docsynth-0.1.0/src/docsynth/core/sinks/bigquery.py +89 -0
  164. docsynth-0.1.0/src/docsynth/core/sinks/duckdb_sink.py +67 -0
  165. docsynth-0.1.0/src/docsynth/core/sinks/parquet.py +54 -0
  166. docsynth-0.1.0/src/docsynth/core/state/__init__.py +50 -0
  167. docsynth-0.1.0/src/docsynth/core/state/base.py +274 -0
  168. docsynth-0.1.0/src/docsynth/core/state/dynamodb.py +500 -0
  169. docsynth-0.1.0/src/docsynth/core/state/firestore.py +434 -0
  170. docsynth-0.1.0/src/docsynth/core/state/sqlite.py +361 -0
  171. docsynth-0.1.0/src/docsynth/core/storage/__init__.py +50 -0
  172. docsynth-0.1.0/src/docsynth/core/storage/base.py +50 -0
  173. docsynth-0.1.0/src/docsynth/core/storage/gcs.py +67 -0
  174. docsynth-0.1.0/src/docsynth/core/storage/local.py +68 -0
  175. docsynth-0.1.0/src/docsynth/core/storage/s3.py +81 -0
  176. docsynth-0.1.0/src/docsynth/core/usage/__init__.py +107 -0
  177. docsynth-0.1.0/src/docsynth/core/usage/base.py +236 -0
  178. docsynth-0.1.0/src/docsynth/core/usage/dynamodb.py +113 -0
  179. docsynth-0.1.0/src/docsynth/core/usage/firestore.py +106 -0
  180. docsynth-0.1.0/src/docsynth/core/usage/shard.py +73 -0
  181. docsynth-0.1.0/src/docsynth/packs/__init__.py +13 -0
  182. docsynth-0.1.0/src/docsynth/packs/invoice/__init__.py +205 -0
  183. docsynth-0.1.0/src/docsynth/packs/invoice/artifact.py +567 -0
  184. docsynth-0.1.0/src/docsynth/packs/invoice/build_run.py +295 -0
  185. docsynth-0.1.0/src/docsynth/packs/invoice/catalog.py +489 -0
  186. docsynth-0.1.0/src/docsynth/packs/invoice/composition.py +228 -0
  187. docsynth-0.1.0/src/docsynth/packs/invoice/context.py +183 -0
  188. docsynth-0.1.0/src/docsynth/packs/invoice/enums.py +271 -0
  189. docsynth-0.1.0/src/docsynth/packs/invoice/handwriting.py +347 -0
  190. docsynth-0.1.0/src/docsynth/packs/invoice/jurisdictions.py +177 -0
  191. docsynth-0.1.0/src/docsynth/packs/invoice/labels.py +286 -0
  192. docsynth-0.1.0/src/docsynth/packs/invoice/llm_build.py +464 -0
  193. docsynth-0.1.0/src/docsynth/packs/invoice/logos.py +112 -0
  194. docsynth-0.1.0/src/docsynth/packs/invoice/procedural.py +960 -0
  195. docsynth-0.1.0/src/docsynth/packs/invoice/record.py +614 -0
  196. docsynth-0.1.0/src/docsynth/packs/invoice/sampler.py +414 -0
  197. docsynth-0.1.0/src/docsynth/packs/invoice/stamps.py +324 -0
  198. docsynth-0.1.0/src/docsynth/packs/invoice/templates/_base.html.j2 +41 -0
  199. docsynth-0.1.0/src/docsynth/packs/invoice/templates/_macros.j2 +224 -0
  200. docsynth-0.1.0/src/docsynth/packs/invoice/templates/_styles.css.j2 +190 -0
  201. docsynth-0.1.0/src/docsynth/packs/invoice/templates/archetypes/banner-header-06.html.j2 +87 -0
  202. docsynth-0.1.0/src/docsynth/packs/invoice/templates/archetypes/boxed-form-01.html.j2 +82 -0
  203. docsynth-0.1.0/src/docsynth/packs/invoice/templates/archetypes/fullbleed-05.html.j2 +86 -0
  204. docsynth-0.1.0/src/docsynth/packs/invoice/templates/archetypes/handwritten-form-01.html.j2 +298 -0
  205. docsynth-0.1.0/src/docsynth/packs/invoice/templates/archetypes/meta-sidebar-01.html.j2 +19 -0
  206. docsynth-0.1.0/src/docsynth/packs/invoice/templates/archetypes/receipt-compact-01.html.j2 +84 -0
  207. docsynth-0.1.0/src/docsynth/packs/invoice/templates/archetypes/telecom-itemized-37.html.j2 +95 -0
  208. docsynth-0.1.0/src/docsynth/packs/invoice/validation.py +240 -0
  209. docsynth-0.1.0/src/docsynth/studio/__init__.py +42 -0
  210. docsynth-0.1.0/src/docsynth/studio/app.py +109 -0
  211. docsynth-0.1.0/src/docsynth/studio/gcp.py +275 -0
  212. docsynth-0.1.0/src/docsynth/studio/local.py +202 -0
  213. docsynth-0.1.0/src/docsynth/studio/mixes.py +105 -0
  214. docsynth-0.1.0/src/docsynth/studio/progress.py +72 -0
  215. docsynth-0.1.0/src/docsynth/studio/prompts.py +137 -0
  216. docsynth-0.1.0/src/docsynth/studio/registry.py +116 -0
  217. docsynth-0.1.0/src/docsynth/studio/targets.py +39 -0
  218. docsynth-0.1.0/src/docsynth/studio/types.py +152 -0
  219. docsynth-0.1.0/src/docsynth/studio/wizard.py +280 -0
  220. docsynth-0.1.0/templates/manifest.yaml +260 -0
  221. docsynth-0.1.0/tests/factories.py +190 -0
  222. docsynth-0.1.0/tests/fakes.py +100 -0
  223. docsynth-0.1.0/tests/test_archetypes.py +226 -0
  224. docsynth-0.1.0/tests/test_bigquery.py +80 -0
  225. docsynth-0.1.0/tests/test_bigquery_integration.py +62 -0
  226. docsynth-0.1.0/tests/test_build_run.py +396 -0
  227. docsynth-0.1.0/tests/test_catalogue_artifact.py +245 -0
  228. docsynth-0.1.0/tests/test_catalogue_runner.py +478 -0
  229. docsynth-0.1.0/tests/test_cli.py +489 -0
  230. docsynth-0.1.0/tests/test_cloud_storage.py +81 -0
  231. docsynth-0.1.0/tests/test_content.py +130 -0
  232. docsynth-0.1.0/tests/test_degrade.py +181 -0
  233. docsynth-0.1.0/tests/test_dynamodb.py +401 -0
  234. docsynth-0.1.0/tests/test_export.py +94 -0
  235. docsynth-0.1.0/tests/test_firestore.py +399 -0
  236. docsynth-0.1.0/tests/test_fonts.py +132 -0
  237. docsynth-0.1.0/tests/test_gcs_emulator.py +68 -0
  238. docsynth-0.1.0/tests/test_goods_receipt.py +172 -0
  239. docsynth-0.1.0/tests/test_handwriting.py +225 -0
  240. docsynth-0.1.0/tests/test_i18n.py +164 -0
  241. docsynth-0.1.0/tests/test_llm_build.py +335 -0
  242. docsynth-0.1.0/tests/test_logging.py +244 -0
  243. docsynth-0.1.0/tests/test_logos.py +59 -0
  244. docsynth-0.1.0/tests/test_pack.py +200 -0
  245. docsynth-0.1.0/tests/test_pdf.py +316 -0
  246. docsynth-0.1.0/tests/test_pipeline.py +274 -0
  247. docsynth-0.1.0/tests/test_procedural_catalogue.py +246 -0
  248. docsynth-0.1.0/tests/test_providers.py +488 -0
  249. docsynth-0.1.0/tests/test_render.py +303 -0
  250. docsynth-0.1.0/tests/test_run_manifest.py +229 -0
  251. docsynth-0.1.0/tests/test_run_planning.py +209 -0
  252. docsynth-0.1.0/tests/test_sampler.py +216 -0
  253. docsynth-0.1.0/tests/test_schema.py +357 -0
  254. docsynth-0.1.0/tests/test_selection.py +399 -0
  255. docsynth-0.1.0/tests/test_sinks.py +163 -0
  256. docsynth-0.1.0/tests/test_spend_rollup.py +261 -0
  257. docsynth-0.1.0/tests/test_stamps.py +125 -0
  258. docsynth-0.1.0/tests/test_state.py +275 -0
  259. docsynth-0.1.0/tests/test_storage.py +70 -0
  260. docsynth-0.1.0/tests/test_studio.py +923 -0
  261. docsynth-0.1.0/tests/test_usage.py +264 -0
  262. docsynth-0.1.0/tests/test_usage_adapters.py +208 -0
  263. docsynth-0.1.0/tests/test_validation.py +189 -0
  264. docsynth-0.0.0/PKG-INFO +0 -5
  265. docsynth-0.0.0/docsynth/__init__.py +0 -1
  266. docsynth-0.0.0/pyproject.toml +0 -12
@@ -0,0 +1,92 @@
1
+ # docsynth — environment configuration
2
+ #
3
+ # cp .env.example .env then fill in the blanks.
4
+ #
5
+ # .env is gitignored. Never commit real credentials.
6
+ # Values marked [required] must be set before the relevant mode will start;
7
+ # startup validation fails fast rather than erroring mid-run.
8
+
9
+
10
+ # ═══════════════════════════════════════════════════════════════════════════
11
+ # LLM providers — catalogue generation (Step 2, one-time)
12
+ # ═══════════════════════════════════════════════════════════════════════════
13
+
14
+ # DeepSeek V4-Flash — 40% of catalogue. https://platform.deepseek.com
15
+ DEEPSEEK_API_KEY=
16
+ DEEPSEEK_BASE_URL=https://api.deepseek.com
17
+ DEEPSEEK_MODEL=deepseek-v4-flash
18
+
19
+ # Qwen3.5-Flash via Alibaba DashScope — 40% of catalogue.
20
+ # Use the Singapore endpoint unless your account is China-mainland.
21
+ DASHSCOPE_API_KEY=
22
+ DASHSCOPE_BASE_URL=https://dashscope-intl.aliyuncs.com/compatible-mode/v1
23
+ DASHSCOPE_MODEL=qwen3.5-flash
24
+
25
+ # Anthropic Claude Haiku 4.5 — 20% of catalogue. https://console.anthropic.com
26
+ # Leave ANTHROPIC_API_KEY blank to fall back to an `ant auth login` profile.
27
+ ANTHROPIC_API_KEY=
28
+ ANTHROPIC_MODEL=claude-haiku-4-5
29
+ # Batches API halves cost; catalogue generation is offline so keep this on.
30
+ ANTHROPIC_USE_BATCH=true
31
+
32
+
33
+ # ═══════════════════════════════════════════════════════════════════════════
34
+ # Logo generation (Step 2b) — abstract marks only; wordmarks are programmatic
35
+ # ═══════════════════════════════════════════════════════════════════════════
36
+
37
+ # Black Forest Labs FLUX 2 Pro. https://api.bfl.ai
38
+ BFL_API_KEY=
39
+ BFL_MODEL=flux-2-pro
40
+ LOGO_ATTEMPTS_PER_COMPANY=3
41
+
42
+
43
+ # ═══════════════════════════════════════════════════════════════════════════
44
+ # Google Cloud
45
+ # ═══════════════════════════════════════════════════════════════════════════
46
+
47
+ GOOGLE_CLOUD_PROJECT= # [required] GCP project ID
48
+ GCP_REGION=us-central1
49
+
50
+ # Local dev only. On Cloud Run, leave blank and use the attached service account.
51
+ GOOGLE_APPLICATION_CREDENTIALS=
52
+
53
+ GCS_BUCKET= # [required] e.g. docsynth-artifacts
54
+ FIRESTORE_DATABASE=(default)
55
+ BQ_DATASET=docsynth_golden # external tables land here
56
+
57
+ # Artifact Registry path for the container image
58
+ ARTIFACT_REGISTRY= # e.g. us-central1-docker.pkg.dev/PROJECT/docsynth
59
+ CLOUD_RUN_SERVICE=docsynth-generate
60
+ CLOUD_RUN_JOB=docsynth-export
61
+
62
+
63
+ # ═══════════════════════════════════════════════════════════════════════════
64
+ # Run behaviour
65
+ # ═══════════════════════════════════════════════════════════════════════════
66
+
67
+ CATALOG_VERSION=v1 # pinned per run; bump = regeneration
68
+ WORK_UNIT_SIZE=1000 # invoices per task / per JSONL shard
69
+ RENDER_CONCURRENCY=8 # parallel Chromium pages per instance
70
+ MAX_LINE_ITEMS=8000 # hard cap — validation error, not a timeout
71
+ LARGE_INVOICE_THRESHOLD=250 # above this, route to the slow queue
72
+
73
+ # Playwright resolves its own bundled Chromium if left blank.
74
+ CHROMIUM_PATH=
75
+
76
+
77
+ # ═══════════════════════════════════════════════════════════════════════════
78
+ # Budget guards — generation aborts rather than silently overrunning
79
+ # ═══════════════════════════════════════════════════════════════════════════
80
+
81
+ CATALOG_BUDGET_USD=50.00
82
+ LOGO_BUDGET_USD=25.00
83
+ TEST_BUDGET_USD=10.00 # separate pot for smoke runs
84
+ ABORT_ON_BUDGET_EXCEEDED=true
85
+
86
+
87
+ # ═══════════════════════════════════════════════════════════════════════════
88
+ # Observability
89
+ # ═══════════════════════════════════════════════════════════════════════════
90
+
91
+ LOG_LEVEL=INFO
92
+ LOG_FORMAT=json # json | console
@@ -0,0 +1,8 @@
1
+ # Code owners for docsynth.
2
+ # https://docs.github.com/articles/about-code-owners
3
+ #
4
+ # @kashodev owns everything in this repo by default — every path, one owner.
5
+ # Add more specific rules below the global one if that ever changes (later rules
6
+ # win over earlier ones for a matching path).
7
+
8
+ * @kashodev
@@ -0,0 +1,76 @@
1
+ <!--
2
+ Fill in every section that applies and delete the ones that do not. A section
3
+ left as an unedited prompt is worse than a deleted one — it reads as answered
4
+ when it isn't.
5
+
6
+ Keep it honest: if something is untested, partially done, or a known trade-off,
7
+ say so here rather than letting a reviewer discover it.
8
+ -->
9
+
10
+ ## What
11
+
12
+ <!-- A high-level summary of the change and its scope. What does this PR do, and
13
+ just as importantly, what is deliberately *not* in it? -->
14
+
15
+ ## Why
16
+
17
+ <!-- The problem being solved and why this solution. Link the ticket/issue.
18
+ If this is a fix, describe the failure it addresses — ideally the symptom a
19
+ user or a test would see. If a previous approach was replaced, say what was
20
+ wrong with it. -->
21
+
22
+ Closes #
23
+
24
+ ## How
25
+
26
+ <!-- High-level implementation notes: the shape of the change, key design
27
+ decisions, and the trade-offs behind them.
28
+
29
+ - Alternatives considered, and why they were rejected.
30
+ - Anything that constrains the design (a protocol, determinism, the exactness
31
+ of the golden data, a licence).
32
+ - Follow-ups deliberately left out of scope (link the TODO entry). -->
33
+
34
+ ## Testing
35
+
36
+ <!-- How the change was verified. Be specific enough that a reviewer could repeat it.
37
+
38
+ - New/changed automated tests and what property each pins down.
39
+ - Full-suite result (e.g. `NNN passed, N skipped`).
40
+ - Manual verification steps, with the commands used.
41
+ - Anything gated/skipped in CI (emulators, Chromium, cloud creds) and how to
42
+ run it locally.
43
+ - What is *not* covered, and why. -->
44
+
45
+ - [ ] Full test suite passes locally
46
+ - [ ] New behaviour has tests that fail without the change
47
+
48
+ ## Visuals
49
+
50
+ <!-- Rendering or UI changes: before/after images. For generated documents,
51
+ attach or reference the sample files that demonstrate the change.
52
+ For complex features, a diagram of the flow or data model.
53
+ Delete this section if the change has no visible output. -->
54
+
55
+ | Before | After |
56
+ | --- | --- |
57
+ | | |
58
+
59
+ ## Reviewer focus
60
+
61
+ <!-- Direct attention where it is most needed.
62
+
63
+ - The files/functions with the real complexity, and what to check about them.
64
+ - Assumptions worth challenging.
65
+ - What can be skimmed (mechanical renames, generated files, moved code with
66
+ no behaviour change).
67
+ - Any part you are unsure about and want a second opinion on. -->
68
+
69
+ ## Risk & rollout
70
+
71
+ <!-- Delete if not applicable.
72
+
73
+ - Backwards compatibility: schema/protocol/config changes, migrations.
74
+ - Effect on existing runs or already-generated data.
75
+ - New dependencies and their licences.
76
+ - How to roll back if this goes wrong. -->
@@ -0,0 +1,52 @@
1
+ name: ci
2
+
3
+ # Runs the test suite on every push to main and every PR, and lints the Python
4
+ # files a PR changed. Chromium- and emulator-backed tests skip themselves when
5
+ # those services aren't present, and the cloud extras aren't installed, so the
6
+ # core suite runs green on a plain runner.
7
+ on:
8
+ push:
9
+ branches: [main]
10
+ pull_request:
11
+
12
+ concurrency:
13
+ group: ci-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ test:
21
+ name: test (py${{ matrix.python-version }})
22
+ runs-on: ubuntu-latest
23
+ strategy:
24
+ fail-fast: false
25
+ matrix:
26
+ python-version: ["3.12", "3.13"]
27
+ steps:
28
+ - uses: actions/checkout@v4
29
+ - uses: actions/setup-python@v5
30
+ with:
31
+ python-version: ${{ matrix.python-version }}
32
+ cache: pip
33
+ cache-dependency-path: pyproject.toml
34
+ - run: pip install -e '.[dev]'
35
+ # `python -m pytest` (not bare `pytest`) puts the repo root on sys.path, which
36
+ # the suite needs — some tests import helpers via `from tests.… import …`.
37
+ - run: python -m pytest -q
38
+
39
+ lint:
40
+ name: ruff
41
+ # Repo-wide gate: the pre-existing lint debt was cleared, so the whole tree
42
+ # must stay clean. Rule scoping lives in pyproject's [tool.ruff.lint].
43
+ runs-on: ubuntu-latest
44
+ steps:
45
+ - uses: actions/checkout@v4
46
+ - uses: actions/setup-python@v5
47
+ with:
48
+ python-version: "3.12"
49
+ # Pinned so a new ruff release can't fail the repo-wide gate on rules the
50
+ # tree was never checked against. Bump deliberately.
51
+ - run: pip install ruff==0.16.0
52
+ - run: ruff check .
@@ -0,0 +1,29 @@
1
+ name: docs
2
+
3
+ # Build the mkdocs-material site and deploy it to GitHub Pages (the gh-pages
4
+ # branch) whenever the docs change on main. Enable Pages once in
5
+ # Settings → Pages → Deploy from branch: gh-pages.
6
+ on:
7
+ push:
8
+ branches: [main]
9
+ paths:
10
+ - "site-docs/**"
11
+ - ".github/workflows/docs.yml"
12
+ workflow_dispatch:
13
+
14
+ permissions:
15
+ contents: write
16
+
17
+ jobs:
18
+ deploy:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ with:
23
+ fetch-depth: 0
24
+ - uses: actions/setup-python@v5
25
+ with:
26
+ python-version: "3.x"
27
+ - run: pip install -r site-docs/requirements.txt
28
+ - name: Build & deploy to GitHub Pages
29
+ run: mkdocs gh-deploy --force --config-file site-docs/mkdocs.yml
@@ -0,0 +1,50 @@
1
+ name: release
2
+
3
+ # Publish to PyPI via Trusted Publishing (OIDC) — no stored API token.
4
+ # Fires when a GitHub Release is published; build the sdist+wheel and upload.
5
+ # One-time PyPI + GitHub setup is documented in
6
+ # site-docs/docs/operations/publishing.md.
7
+ on:
8
+ release:
9
+ types: [published]
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ build:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.12"
22
+ - name: Tag must match [project].version
23
+ run: |
24
+ ver=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
25
+ tag="${GITHUB_REF_NAME#v}"
26
+ if [ "$ver" != "$tag" ]; then
27
+ echo "release tag '$tag' != pyproject version '$ver'"; exit 1
28
+ fi
29
+ echo "publishing version $ver"
30
+ - run: pip install build
31
+ - run: python -m build
32
+ - run: pipx run twine check dist/*
33
+ - uses: actions/upload-artifact@v4
34
+ with:
35
+ name: dist
36
+ path: dist/
37
+
38
+ publish:
39
+ needs: build
40
+ runs-on: ubuntu-latest
41
+ # The trusted publisher on PyPI is scoped to this environment.
42
+ environment: pypi
43
+ permissions:
44
+ id-token: write # mint the OIDC token PyPI verifies
45
+ steps:
46
+ - uses: actions/download-artifact@v4
47
+ with:
48
+ name: dist
49
+ path: dist/
50
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,70 @@
1
+ # ─── Secrets ────────────────────────────────────────────────────────────────
2
+ .env
3
+ .env.*
4
+ !.env.example
5
+ *-service-account*.json
6
+ gcp-credentials*.json
7
+
8
+ # ─── Design scratch (never committed) ───────────────────────────────────────
9
+ # Feature-exploration write-ups: thinking-in-progress, superseded as soon as the
10
+ # decision lands in DESIGN.md / docs/. Keeping them out of git stops stale
11
+ # analysis being mistaken for the current design.
12
+ feature_explorations/
13
+
14
+ # ─── Reference material (never committed) ───────────────────────────────────
15
+ # Third-party images used only as visual reference while building the
16
+ # procedural equivalents. Not ours to redistribute, and not needed to build.
17
+ templates/stamps/
18
+
19
+ # ─── Generated artefacts ────────────────────────────────────────────────────
20
+ out/
21
+ catalog/
22
+ *.parquet
23
+ *.jsonl
24
+ *.jsonl.gz
25
+
26
+ # ─── Python ─────────────────────────────────────────────────────────────────
27
+ __pycache__/
28
+ *.py[cod]
29
+ .venv/
30
+ venv/
31
+ .mypy_cache/
32
+ .ruff_cache/
33
+ .pytest_cache/
34
+ *.egg-info/
35
+ dist/
36
+ build/
37
+ .coverage
38
+ htmlcov/
39
+
40
+ # ─── Tooling / OS ───────────────────────────────────────────────────────────
41
+ .DS_Store
42
+ .idea/
43
+ .vscode/
44
+
45
+ # Belt-and-suspenders: ignore PDFs anywhere by default. The original reason was
46
+ # the source templates, which carried real personal data and have since been
47
+ # removed from the repo — but the blanket rule stays, so a stray real-world PDF
48
+ # dropped anywhere in the tree can never be committed by accident.
49
+ # Case-insensitive matching is on by default on macOS but NOT on Linux, so the
50
+ # explicit glob protects a Linux clone too.
51
+ *.pdf
52
+ *.Pdf
53
+ *.PDF
54
+
55
+ # ...with one deliberate exception: samples/ holds generated showcase documents.
56
+ # They are synthetic end to end — procedural catalogue data, no personal data —
57
+ # and are committed so the rendering can be reviewed without running a job.
58
+ !samples/**/*.pdf
59
+ !samples/**/*.PDF
60
+
61
+ # Real run configurations. `run.example.yaml` is the committed template; every
62
+ # other run.yaml is one operator's actual run — project ids, bucket names, doc
63
+ # counts and secret *names* — which is noise in review at best and a leak at
64
+ # worst. Copy the example, never commit the copy.
65
+ run.yaml
66
+ run.*.yaml
67
+ !run.example.yaml
68
+
69
+ # mkdocs build output for the docs site
70
+ site-docs/site/
@@ -0,0 +1,74 @@
1
+ # Contributing
2
+
3
+ ## Branching
4
+
5
+ `main` is protected — it does not accept direct pushes. **All work goes on a
6
+ branch and lands through a pull request**, however small.
7
+
8
+ ```bash
9
+ git switch -c <type>/<short-description> # branch from an up-to-date main
10
+ # ...work, commit...
11
+ git push -u origin <branch>
12
+ ```
13
+
14
+ Branch names are `<type>/<short-description>`, where type is one of:
15
+
16
+ | Type | For |
17
+ |---|---|
18
+ | `feat/` | new capability |
19
+ | `fix/` | a defect |
20
+ | `refactor/` | restructuring with no behaviour change |
21
+ | `docs/` | documentation only |
22
+ | `chore/` | tooling, packaging, housekeeping |
23
+ | `test/` | tests only |
24
+
25
+ One PR per logical change. Two unrelated changes on one branch make review
26
+ harder and a revert riskier — branch twice instead.
27
+
28
+ ## Pull requests
29
+
30
+ [`.github/pull_request_template.md`](.github/pull_request_template.md) is applied
31
+ automatically. Fill in every section that applies and **delete the ones that do
32
+ not** — a section left as an unedited prompt reads as answered when it isn't.
33
+
34
+ The template asks for the *What*, *Why*, *How* (including trade-offs and
35
+ alternatives), *Testing*, *Visuals*, *Reviewer focus*, and *Risk & rollout*. The
36
+ two that most often get skipped and matter most:
37
+
38
+ - **How** — the reasoning, not a restatement of the diff. A reviewer can read the
39
+ code; they cannot read why you rejected the other approach.
40
+ - **Reviewer focus** — say what to scrutinise and what to skim. A 900-line diff
41
+ that is 850 lines of moved code deserves that note.
42
+
43
+ Be honest in the PR body about anything untested, partially done, or knowingly
44
+ traded off. It is much cheaper to flag it than to have a reviewer find it.
45
+
46
+ ## Before opening a PR
47
+
48
+ ```bash
49
+ PYTHONPATH=src pytest -q # full suite
50
+ ```
51
+
52
+ - New behaviour needs a test that fails without the change.
53
+ - Some tests are gated and skip without their dependency (Chromium, the
54
+ Firestore/GCS emulators, boto3/moto, a real BigQuery project). If your change
55
+ touches those paths, run them locally and say so in *Testing*.
56
+ - Rendering changes: regenerate the affected files in `samples/` and reference
57
+ them in *Visuals*.
58
+
59
+ ## Things that will fail review
60
+
61
+ - Committing anything with real personal data. PDFs are gitignored by default;
62
+ the `samples/` exception exists only because that content is wholly synthetic.
63
+ - Committing third-party reference material (see `templates/stamps/`).
64
+ - Changing a golden value. The record is the ground truth an extraction pipeline
65
+ is scored against — rendering may change freely, the computed figures may not.
66
+ - Changing how the RNG is consumed **without saying so in the PR**. Drawing an
67
+ extra value, drawing in a different order, or drawing a different number of
68
+ values shifts every subsequent draw, so every document changes for a given
69
+ `(run_id, index)`. That is sometimes the right thing to do — making line items
70
+ distinct was — but it means a run cannot be resumed across the change, and an
71
+ old run id no longer reproduces its old corpus. Call it out explicitly under
72
+ *Risk & rollout* so a reviewer can weigh it — reproducibility is per code
73
+ version, not absolute (see the docs site → Architecture → Concurrency &
74
+ coordination).
@@ -0,0 +1,67 @@
1
+ # docsynth container.
2
+ #
3
+ # NOT required to try docsynth. The local-first path — filesystem storage,
4
+ # SQLite state, Parquet + DuckDB golden — runs from a plain `pip install docsynth`
5
+ # with no container and no cloud account.
6
+ #
7
+ # This image exists for the two things that genuinely benefit from one:
8
+ #
9
+ # 1. Reproducible rendering. PDF generation drives headless Chromium via
10
+ # Playwright, whose output is sensitive to browser and font versions.
11
+ # Pinning them in an image makes 250k documents byte-reproducible across
12
+ # machines. The Playwright base image ships a matched Chromium.
13
+ #
14
+ # 2. The Cloud Run deploy artifact (GCP path). The same image runs as the
15
+ # generation Service and the export Job — one build, two entry commands.
16
+ #
17
+ # Build: docker build -t docsynth .
18
+ # Local: docker run --rm -v "$PWD/out:/data" docsynth generate --config ...
19
+ # Cloud: pushed to Artifact Registry, deployed to Cloud Run (see deploy.sh).
20
+
21
+ # The base image bundles a Chromium build matched to ONE Playwright version, and
22
+ # the pip package MUST be that exact version or launch fails with
23
+ # "Executable doesn't exist". They are declared together here, from a single arg,
24
+ # so they can never drift — an unpinned `playwright>=…` floating to a newer
25
+ # release than the base image is what silently broke every rendered document.
26
+ # Bump both by bumping this one number (and picking the matching `vX.Y.Z-noble`).
27
+ ARG PLAYWRIGHT_VERSION=1.61.0
28
+ FROM mcr.microsoft.com/playwright/python:v1.61.0-noble
29
+ ARG PLAYWRIGHT_VERSION
30
+
31
+ # Stream stdout/stderr immediately, so a long job's progress reaches Cloud
32
+ # Logging as it happens rather than only when the process exits.
33
+ ENV PYTHONUNBUFFERED=1
34
+
35
+ WORKDIR /app
36
+
37
+ # Install dependencies first so the layer caches across code changes.
38
+ COPY pyproject.toml README.md ./
39
+ COPY src ./src
40
+
41
+ # Pin Playwright to the base image's version *before* installing the package, so
42
+ # `.[gcp,anthropic]` (whose `playwright>=…` is already satisfied) cannot pull a
43
+ # newer, browser-mismatched release. `[gcp]` adds the GCS / Firestore / BigQuery
44
+ # SDKs for the cloud path; drop it for a purely local image, add `[aws]` for S3.
45
+ RUN pip install --no-cache-dir "playwright==${PLAYWRIGHT_VERSION}" \
46
+ && pip install --no-cache-dir ".[gcp,anthropic]" \
47
+ && test "$(python -c 'import importlib.metadata as m; print(m.version("playwright"))')" = "${PLAYWRIGHT_VERSION}"
48
+
49
+ # Ship the invoice pack's templates (they live inside the package).
50
+ COPY templates/manifest.yaml ./templates/manifest.yaml
51
+
52
+ # Smoke-test the wiring that actually breaks in production. `import` alone passed
53
+ # happily while rendering was completely broken — the pack registered, but every
54
+ # document failed to launch a browser. So this renders ONE real PDF end to end
55
+ # (record → HTML → Chromium → PDF bytes) and fails the build if it cannot. It is
56
+ # the one assertion standing between a broken image and a green run over zero
57
+ # documents.
58
+ #
59
+ # Written as `python -c` one-liners rather than a heredoc on purpose: Cloud
60
+ # Build's daemon has no BuildKit heredoc support, and a `RUN … <<'PY'` block gets
61
+ # parsed as Dockerfile instructions (a Python `from x import y` reads as a second
62
+ # `FROM`).
63
+ RUN python -c "import docsynth.packs; print('packs:', docsynth.available_packs())"
64
+ RUN python -c "import docsynth.packs; from docsynth.core import get_pack; from docsynth.core.pipeline.pdf import PdfRenderer; pack=get_pack('invoice'); rec=pack.default_source().generate('docker-build-check', 0); pdf=PdfRenderer(pack).render(rec).data; assert pdf[:5]==b'%PDF-', 'renderer did not return a PDF'; assert len(pdf) > 1000, f'suspiciously small PDF ({len(pdf)} bytes)'; print(f'render check: {len(pdf)} byte PDF OK')"
65
+
66
+ ENTRYPOINT ["docsynth"]
67
+ CMD ["--help"]
docsynth-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Adam Okasha
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.