atif-sql 0.4.0__tar.gz → 0.5.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 (192) hide show
  1. {atif_sql-0.4.0 → atif_sql-0.5.0}/PKG-INFO +2 -2
  2. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/pyproject.toml +2 -2
  3. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-cli/pyproject.toml +7 -7
  4. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-cli/src/atif_cli/app.py +28 -6
  5. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/pyproject.toml +1 -1
  6. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/pyproject.toml +1 -1
  7. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/pyproject.toml +2 -2
  8. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/domain/examples.py +2 -2
  9. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/infrastructure/projections.py +1 -1
  10. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/infrastructure/registry.py +52 -17
  11. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/infrastructure/stored_artifacts.py +40 -1
  12. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/pyproject.toml +2 -2
  13. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/infrastructure/corpus_text_rows.py +46 -15
  14. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-models/pyproject.toml +1 -1
  15. {atif_sql-0.4.0 → atif_sql-0.5.0}/pyproject.toml +3 -3
  16. {atif_sql-0.4.0 → atif_sql-0.5.0}/.gitignore +0 -0
  17. {atif_sql-0.4.0 → atif_sql-0.5.0}/.vale/styles/Google/LICENSE +0 -0
  18. {atif_sql-0.4.0 → atif_sql-0.5.0}/.vale/styles/proselint/LICENSE +0 -0
  19. {atif_sql-0.4.0 → atif_sql-0.5.0}/.vale/styles/proselint/README.md +0 -0
  20. {atif_sql-0.4.0 → atif_sql-0.5.0}/LICENSE +0 -0
  21. {atif_sql-0.4.0 → atif_sql-0.5.0}/README.md +0 -0
  22. {atif_sql-0.4.0 → atif_sql-0.5.0}/docs/README.md +0 -0
  23. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/LICENSE +0 -0
  24. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/README.md +0 -0
  25. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/__init__.py +0 -0
  26. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/application/__init__.py +0 -0
  27. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/application/analyze.py +0 -0
  28. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/application/prompts.py +0 -0
  29. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/__init__.py +0 -0
  30. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/_shared.py +0 -0
  31. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/classify.py +0 -0
  32. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/conflicts.py +0 -0
  33. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/friction.py +0 -0
  34. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/perceived.py +0 -0
  35. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/domain/__init__.py +0 -0
  36. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/domain/authorship.py +0 -0
  37. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/domain/config.py +0 -0
  38. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/domain/costs.py +0 -0
  39. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/domain/friction.py +0 -0
  40. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/domain/layout.py +0 -0
  41. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/domain/models.py +0 -0
  42. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/domain/ports.py +0 -0
  43. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/domain/transcript.py +0 -0
  44. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/infrastructure/__init__.py +0 -0
  45. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/infrastructure/corpus_reader.py +0 -0
  46. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/infrastructure/parquet_cache.py +0 -0
  47. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/infrastructure/settings.py +0 -0
  48. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/infrastructure/sqlite_state/__init__.py +0 -0
  49. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/infrastructure/sqlite_state/checkpointer.py +0 -0
  50. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/infrastructure/sqlite_state/retry_queue.py +0 -0
  51. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-analytics/src/atif_analytics/py.typed +0 -0
  52. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-cli/LICENSE +0 -0
  53. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-cli/README.md +0 -0
  54. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-cli/src/atif_cli/__init__.py +0 -0
  55. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-cli/src/atif_cli/__main__.py +0 -0
  56. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-cli/src/atif_cli/converter_adapter.py +0 -0
  57. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-cli/src/atif_cli/corpus.py +0 -0
  58. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-cli/src/atif_cli/cron.py +0 -0
  59. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-cli/src/atif_cli/duck_errors.py +0 -0
  60. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-cli/src/atif_cli/embed_lake.py +0 -0
  61. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-cli/src/atif_cli/errors.py +0 -0
  62. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-cli/src/atif_cli/lake.py +0 -0
  63. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-cli/src/atif_cli/lake_sessions.py +0 -0
  64. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-cli/src/atif_cli/output.py +0 -0
  65. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-cli/src/atif_cli/py.typed +0 -0
  66. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/LICENSE +0 -0
  67. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/README.md +0 -0
  68. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/__init__.py +0 -0
  69. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/application/__init__.py +0 -0
  70. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/application/convert_and_audit.py +0 -0
  71. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/application/convert_codex.py +0 -0
  72. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/__init__.py +0 -0
  73. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/agents.py +0 -0
  74. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/atif/__init__.py +0 -0
  75. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/atif/agent.py +0 -0
  76. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/atif/content.py +0 -0
  77. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/atif/final_metrics.py +0 -0
  78. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/atif/metrics.py +0 -0
  79. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/atif/observation.py +0 -0
  80. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/atif/observation_result.py +0 -0
  81. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/atif/step.py +0 -0
  82. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/atif/subagent_trajectory_ref.py +0 -0
  83. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/atif/tool_call.py +0 -0
  84. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/atif/trajectory.py +0 -0
  85. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/atif/trajectory_validator.py +0 -0
  86. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/blobs.py +0 -0
  87. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/claude_code_conversion.py +0 -0
  88. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/codex_conversion.py +0 -0
  89. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/codex_edges.py +0 -0
  90. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/codex_enrichment.py +0 -0
  91. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/codex_fidelity.py +0 -0
  92. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/codex_nested_calls.py +0 -0
  93. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/edges.py +0 -0
  94. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/enrichment.py +0 -0
  95. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/errors.py +0 -0
  96. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/fidelity.py +0 -0
  97. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/model_prices.json +0 -0
  98. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/pricing.py +0 -0
  99. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/result_signals.py +0 -0
  100. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/schema_version.py +0 -0
  101. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/domain/session_events.py +0 -0
  102. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/infrastructure/__init__.py +0 -0
  103. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/infrastructure/census.py +0 -0
  104. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/infrastructure/claude_code_converter.py +0 -0
  105. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/infrastructure/codex_adapter.py +0 -0
  106. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/infrastructure/codex_census.py +0 -0
  107. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/infrastructure/codex_converter.py +0 -0
  108. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/infrastructure/harbor_adapter.py +0 -0
  109. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/infrastructure/raw_records.py +0 -0
  110. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/infrastructure/source_archive.py +0 -0
  111. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-converter/src/atif_converter/py.typed +0 -0
  112. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/LICENSE +0 -0
  113. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/README.md +0 -0
  114. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/__init__.py +0 -0
  115. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/application/__init__.py +0 -0
  116. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/application/materialize.py +0 -0
  117. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/domain/__init__.py +0 -0
  118. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/domain/agents.py +0 -0
  119. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/domain/generation.py +0 -0
  120. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/domain/layout.py +0 -0
  121. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/domain/ports.py +0 -0
  122. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/domain/session_id.py +0 -0
  123. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/domain/sessions.py +0 -0
  124. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/domain/slug.py +0 -0
  125. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/domain/source_layout.py +0 -0
  126. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/domain/watermark.py +0 -0
  127. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/infrastructure/__init__.py +0 -0
  128. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/infrastructure/atomic.py +0 -0
  129. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/infrastructure/compress_artifacts.py +0 -0
  130. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/infrastructure/fake_converter.py +0 -0
  131. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/infrastructure/scanner.py +0 -0
  132. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/infrastructure/settings.py +0 -0
  133. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/infrastructure/source_archive.py +0 -0
  134. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-corpus/src/atif_corpus/py.typed +0 -0
  135. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/LICENSE +0 -0
  136. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/README.md +0 -0
  137. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/__init__.py +0 -0
  138. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/domain/__init__.py +0 -0
  139. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/domain/artifacts.py +0 -0
  140. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/domain/authorship.py +0 -0
  141. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/domain/catalog.py +0 -0
  142. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/domain/columnar.py +0 -0
  143. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/domain/embedding_guard.py +0 -0
  144. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/domain/lake.py +0 -0
  145. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/domain/lake_sessions.py +0 -0
  146. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/domain/raw_readers.py +0 -0
  147. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/domain/session_id.py +0 -0
  148. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/domain/sql_literal.py +0 -0
  149. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/infrastructure/__init__.py +0 -0
  150. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/infrastructure/analytics.py +0 -0
  151. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/infrastructure/authorship.py +0 -0
  152. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/infrastructure/columnar.py +0 -0
  153. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/infrastructure/lake.py +0 -0
  154. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/infrastructure/lake_sessions.py +0 -0
  155. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/infrastructure/lake_settings.py +0 -0
  156. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/infrastructure/lake_steps.py +0 -0
  157. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-duck/src/atif_duck/py.typed +0 -0
  158. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/LICENSE +0 -0
  159. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/README.md +0 -0
  160. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/__init__.py +0 -0
  161. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/application/__init__.py +0 -0
  162. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/application/embed.py +0 -0
  163. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/application/prune.py +0 -0
  164. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/domain/__init__.py +0 -0
  165. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/domain/discovery.py +0 -0
  166. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/domain/embedding_guard.py +0 -0
  167. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/domain/errors.py +0 -0
  168. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/domain/ports.py +0 -0
  169. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/domain/sql_literal.py +0 -0
  170. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/domain/text_stamp.py +0 -0
  171. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/infrastructure/__init__.py +0 -0
  172. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/infrastructure/cohere_bedrock.py +0 -0
  173. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/infrastructure/gemma_local.py +0 -0
  174. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/infrastructure/lake_text_rows.py +0 -0
  175. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/infrastructure/lance_store.py +0 -0
  176. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/infrastructure/providers.py +0 -0
  177. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/infrastructure/settings.py +0 -0
  178. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-embed/src/atif_embed/py.typed +0 -0
  179. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-models/LICENSE +0 -0
  180. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-models/README.md +0 -0
  181. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-models/src/atif_models/__init__.py +0 -0
  182. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-models/src/atif_models/domain/__init__.py +0 -0
  183. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-models/src/atif_models/domain/ports.py +0 -0
  184. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-models/src/atif_models/domain/registry.py +0 -0
  185. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-models/src/atif_models/domain/schema.py +0 -0
  186. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-models/src/atif_models/infrastructure/__init__.py +0 -0
  187. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-models/src/atif_models/infrastructure/anthropic_bedrock.py +0 -0
  188. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-models/src/atif_models/infrastructure/bedrock_common.py +0 -0
  189. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-models/src/atif_models/infrastructure/openai_bedrock.py +0 -0
  190. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-models/src/atif_models/infrastructure/providers.py +0 -0
  191. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-models/src/atif_models/infrastructure/settings.py +0 -0
  192. {atif_sql-0.4.0 → atif_sql-0.5.0}/packages/atif-models/src/atif_models/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: atif-sql
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: ATIF-native analytics over Claude Code agent trajectories: convert, materialize, and query agent sessions with DuckDB.
5
5
  Project-URL: Homepage, https://github.com/laithalsaadoon/atif-sql
6
6
  Project-URL: Repository, https://github.com/laithalsaadoon/atif-sql
@@ -21,7 +21,7 @@ Requires-Dist: anthropic>=1.11.0
21
21
  Requires-Dist: anyio>=4.11.0
22
22
  Requires-Dist: boto3>=1.42.91
23
23
  Requires-Dist: cyclopts>=5.1.1
24
- Requires-Dist: duckdb<2,>=1.5.2
24
+ Requires-Dist: duckdb<3,>=2.0.0
25
25
  Requires-Dist: imagesize>=2.0
26
26
  Requires-Dist: lancedb<0.40,>=0.30
27
27
  Requires-Dist: loguru>=0.7.3
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "atif-analytics"
3
- version = "0.4.0"
3
+ version = "0.5.0"
4
4
  description = "LLM analytics pipelines over the materialized ATIF corpus: session classification, stance conflicts, user friction, and perceived errors."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -20,7 +20,7 @@ dependencies = [
20
20
  # a LOCAL source an external installer never sees, so a bare name would ship as
21
21
  # `Requires-Dist: atif-models` and resolve to whatever the newest release of
22
22
  # that name is. `[tool.commitizen] version_files` rewrites this line on a bump.
23
- "atif-models==0.4.0",
23
+ "atif-models==0.5.0",
24
24
  "loguru>=0.7.3",
25
25
  "polars>=1.40.0",
26
26
  "pyarrow>=23.0.1",
@@ -5,7 +5,7 @@
5
5
  # import-linter can read what it is allowed to import, and so its tests have a
6
6
  # home — none of which needs an index.
7
7
  name = "atif-cli"
8
- version = "0.4.0"
8
+ version = "0.5.0"
9
9
  description = "ATIF-native analytics over Claude Code agent trajectories: convert, materialize, and query agent sessions with DuckDB."
10
10
  readme = "README.md"
11
11
  license = "Apache-2.0"
@@ -29,17 +29,17 @@ dependencies = [
29
29
  # namespace to whatever the newest release of that name is, by whoever owns it.
30
30
  # One version moves the whole workspace, so `==` is the honest constraint, and
31
31
  # `[tool.commitizen] version_files` rewrites every one of these on each bump.
32
- "atif-analytics==0.4.0",
33
- "atif-converter==0.4.0",
34
- "atif-corpus==0.4.0",
35
- "atif-duck==0.4.0",
36
- "atif-embed==0.4.0",
32
+ "atif-analytics==0.5.0",
33
+ "atif-converter==0.5.0",
34
+ "atif-corpus==0.5.0",
35
+ "atif-duck==0.5.0",
36
+ "atif-embed==0.5.0",
37
37
  "cyclopts>=5.1.1",
38
38
  # Imported directly by duck_errors.py to classify a DuckDB failure into the
39
39
  # parse/catalog/runtime exit-code taxonomy. Reaching it transitively through
40
40
  # atif-duck would make this package's exit codes depend on a sibling's
41
41
  # dependency choice.
42
- "duckdb>=1.5.2,<2",
42
+ "duckdb>=2.0.0,<3",
43
43
  "loguru>=0.7.3",
44
44
  ]
45
45
 
@@ -454,6 +454,15 @@ def _refuse_root(command: str, fmt: OutputFormat) -> None:
454
454
  #: PREPARE and EXECUTE (a prepared COPY parses as PREPARE). An allowlist, so a
455
455
  #: statement kind a future DuckDB adds is refused until someone reads what it
456
456
  #: does.
457
+ #:
458
+ #: PRAGMA and MULTI are out because DuckDB 2.0's parser no longer says what
459
+ #: they hold. 1.5 expanded a PRAGMA while parsing, so ``PRAGMA table_info``
460
+ #: came back as SELECT and ``COPY FROM DATABASE`` as COPY_DATABASE; 2.0 hands
461
+ #: back the unexpanded PRAGMA for every pragma and for ``COPY FROM DATABASE``
462
+ #: and ``IMPORT DATABASE`` alike, and one MULTI for a PIVOT that has to build
463
+ #: its column list first, whatever statement wraps it (``COPY (PIVOT ...) TO``
464
+ #: is one MULTI). Their SELECT forms still run: ``pragma_table_info('t')``,
465
+ #: ``DESCRIBE``, ``SHOW TABLES``, and PIVOT with an explicit ``IN`` list.
457
466
  _QUERY_STATEMENT_KINDS: frozenset[str] = frozenset(
458
467
  {
459
468
  "SELECT",
@@ -469,7 +478,6 @@ _QUERY_STATEMENT_KINDS: frozenset[str] = frozenset(
469
478
  "DELETE",
470
479
  "MERGE_INTO",
471
480
  "CALL",
472
- "PRAGMA",
473
481
  "TRANSACTION",
474
482
  "VACUUM",
475
483
  "ANALYZE",
@@ -485,10 +493,17 @@ def _refused_statement_kinds(con: Any, sql: str) -> list[str]:
485
493
  connection so a PRAGMA that reads a file while parsing meets the same
486
494
  allowlist the query would. A parse error propagates as DuckDB's own
487
495
  ``ParserException`` (exit 64), exactly as ``execute`` would have raised it.
496
+
497
+ A statement whose type the Python binding has no member for is refused as
498
+ ``UNKNOWN``: DuckDB 2.0's ``StatementType`` raises ``ValueError`` there
499
+ (``UPDATE EXTENSIONS`` is type 29), where 1.5 answered ``???``.
488
500
  """
489
501
  refused: list[str] = []
490
502
  for statement in con.extract_statements(sql):
491
- kind = str(statement.type.name)
503
+ try:
504
+ kind = str(statement.type.name)
505
+ except ValueError:
506
+ kind = "UNKNOWN"
492
507
  if kind not in _QUERY_STATEMENT_KINDS and kind not in refused:
493
508
  refused.append(kind)
494
509
  return refused
@@ -557,7 +572,7 @@ def _harden_query_connection(
557
572
  ``register`` bound this connection to). A file grant lets
558
573
  ``read_parquet`` open that exact path. Nothing else under the corpus
559
574
  is named, so ``read_text`` of a ``trajectory.json`` is refused. DuckDB
560
- 1.5.5 has no read-only grant (measured: a ``read_parquet`` relation
575
+ has no read-only grant, on 1.5.5 or 2.0.0 (measured: a ``read_parquet`` relation
561
576
  bound before ``enable_external_access=false`` is refused at query time
562
577
  without a grant, and ``duckdb_settings()`` lists no write switch), so
563
578
  each grant is read-write and ``COPY ... TO <granted parquet>
@@ -1364,7 +1379,11 @@ def query(
1364
1379
  parquet> (USE_TMP_FILE false)`` used to overwrite one, so every
1365
1380
  statement kind that names a file is refused BEFORE execution, for any
1366
1381
  uid, by DuckDB's own parser: COPY, EXPORT, ATTACH, DETACH, INSTALL,
1367
- LOAD, and PREPARE/EXECUTE (which could defer one). Exit 70, kind
1382
+ LOAD, and PREPARE/EXECUTE (which could defer one). So are PRAGMA and
1383
+ MULTI, which DuckDB 2.0 no longer expands while parsing (a PRAGMA can be
1384
+ ``COPY FROM DATABASE`` or ``IMPORT DATABASE``, and a MULTI can wrap a
1385
+ COPY): run ``pragma_table_info(...)``, ``DESCRIBE`` or ``SHOW`` instead,
1386
+ and give a PIVOT an explicit ``IN`` list. Exit 70, kind
1368
1387
  ``sandbox_refused``.
1369
1388
  * A 0444 file mode does not bind root, so ``query`` refuses to run as
1370
1389
  uid 0 (exit 77) unless ``ATIF_SQL_ALLOW_ROOT=1`` is set, which logs a
@@ -1405,7 +1424,7 @@ def query(
1405
1424
  * 65 catalog_error unknown view/macro/column (try ``atif-sql schema``)
1406
1425
  * 65 embedding_mismatch the Lance store was written by another provider
1407
1426
  * 70 sandbox_refused a statement kind the sandbox never runs (COPY,
1408
- EXPORT, ATTACH, INSTALL, LOAD, PREPARE, ...)
1427
+ EXPORT, ATTACH, INSTALL, LOAD, PREPARE, PRAGMA, ...)
1409
1428
  * 70 runtime_error everything else (an unmaterialized corpus, or SQL
1410
1429
  the sandbox refused)
1411
1430
  * 77 root_refused running as uid 0 without ``ATIF_SQL_ALLOW_ROOT=1``
@@ -1507,7 +1526,10 @@ def query(
1507
1526
  message=f"the query sandbox does not run {', '.join(refused)} statements",
1508
1527
  hint="query runs SELECT and in-memory DDL/DML only; COPY, EXPORT, "
1509
1528
  "ATTACH, DETACH, INSTALL, LOAD, PREPARE and EXECUTE are refused "
1510
- "because they reach files or defer a statement past this check",
1529
+ "because they reach files or defer a statement past this check, and "
1530
+ "PRAGMA and MULTI because the parser no longer says what they hold "
1531
+ "(use pragma_table_info(...), DESCRIBE or SHOW for a PRAGMA, and an "
1532
+ "explicit IN list for a PIVOT)",
1511
1533
  )
1512
1534
  emit_error(err, fmt)
1513
1535
  raise SystemExit(err.exit_code)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "atif-converter"
3
- version = "0.4.0"
3
+ version = "0.5.0"
4
4
  description = "Claude Code and Codex transcripts -> ATIF conversion, with the fidelity policy per agent."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "atif-corpus"
3
- version = "0.4.0"
3
+ version = "0.5.0"
4
4
  description = "Corpus materialization for ATIF trajectories: discovery, watermarks, quiescence, atomic artifact writes."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "atif-duck"
3
- version = "0.4.0"
3
+ version = "0.5.0"
4
4
  description = "DuckDB views and macros over the materialized ATIF corpus."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -17,7 +17,7 @@ classifiers = [
17
17
  "Typing :: Typed",
18
18
  ]
19
19
  dependencies = [
20
- "duckdb>=1.5.2,<2",
20
+ "duckdb>=2.0.0,<3",
21
21
  "loguru>=0.7.3",
22
22
  # LakeSettings (atif_duck.infrastructure.lake_settings): the lake root and
23
23
  # its batch sizes come from ATIF_SQL_* env vars like every other setting.
@@ -86,8 +86,8 @@ ARG_EXEMPLARS: dict[str, str] = {
86
86
  "interval_text": "'7 days'",
87
87
  "query_vec": (
88
88
  "(SELECT list_transform("
89
- "me.embedding, v -> (v / sqrt(list_sum("
90
- "list_transform(me.embedding, x -> x::DOUBLE * x))))::FLOAT"
89
+ "me.embedding, lambda v: (v / sqrt(list_sum("
90
+ "list_transform(me.embedding, lambda x: x::DOUBLE * x))))::FLOAT"
91
91
  ") FROM message_embeddings me LIMIT 1)"
92
92
  ),
93
93
  }
@@ -117,7 +117,7 @@ def step_columns(access: StepMemberAccess) -> tuple[tuple[str, str], ...]:
117
117
  (
118
118
  f"CASE WHEN {access.type_of('message')} = 'ARRAY' "
119
119
  f"THEN array_to_string(list_transform({message_parts}, "
120
- "part -> json_extract_string(part, '$')), '\n\n') "
120
+ "lambda part: json_extract_string(part, '$')), '\n\n') "
121
121
  f"ELSE {access.string('message')} END"
122
122
  ),
123
123
  "message",
@@ -123,7 +123,7 @@ from atif_duck.infrastructure.projections import (
123
123
  step_columns,
124
124
  step_key_columns,
125
125
  )
126
- from atif_duck.infrastructure.stored_artifacts import decoded_size, stored_artifact
126
+ from atif_duck.infrastructure.stored_artifacts import decoded_size, longest_line, stored_artifact
127
127
 
128
128
  if TYPE_CHECKING:
129
129
  from collections.abc import Callable, Collection, Iterable, Mapping, Sequence
@@ -232,11 +232,17 @@ _OBJECT_SIZE_HEADROOM_BYTES: int = 1_048_576
232
232
  LANCE_EXTENSION: str = "lance"
233
233
 
234
234
 
235
+ def _bounded(largest: int) -> int:
236
+ """``largest`` plus headroom, floored at DuckDB's default and capped at :data:`_OBJECT_SIZE_CAP`."""
237
+ with_headroom = largest + largest // _OBJECT_SIZE_HEADROOM_DIVISOR + _OBJECT_SIZE_HEADROOM_BYTES
238
+ return max(_OBJECT_SIZE_FLOOR, min(_OBJECT_SIZE_CAP, with_headroom))
239
+
240
+
235
241
  def _object_size_bound(paths: Iterable[Path]) -> int:
236
- """``maximum_object_size`` for a ``read_json`` over ``paths``, sized from the files.
242
+ """``maximum_object_size`` for a one-document ``read_json`` over ``paths``, sized from the files.
237
243
 
238
- The bound is not free. DuckDB's eager JSON reader reserves about twice
239
- this many bytes PER THREAD before it parses anything, so the 1 GiB
244
+ The bound is not free. DuckDB's JSON reader allocates each read buffer
245
+ at twice this many bytes, and holds at least one a thread, so the 1 GiB
240
246
  constant this replaced cost 2 GiB a thread: over 300 ``edges.jsonl``
241
247
  files of under 8 MB each it exhausted a 6 GB ``memory_limit`` at four
242
248
  threads and a 25 GB one at sixteen, while 16 MiB read the same rows in
@@ -244,19 +250,48 @@ def _object_size_bound(paths: Iterable[Path]) -> int:
244
250
  claimed lowering it changed nothing; that measurement held the thread
245
251
  count at one.
246
252
 
247
- A newline-delimited file's objects are its lines and a one-document file
248
- IS its object, so the largest file present bounds every object either
249
- reader meets. That size plus headroom, floored at DuckDB's default and
250
- capped at :data:`_OBJECT_SIZE_CAP`, is the bound. A compressed file is
251
- measured by its decompressed size (read from its frame header), because
252
- that's what the reader holds. A path that vanished between the listing
253
- and the stat counts as zero.
253
+ A one-document file IS its object, so the largest file present bounds
254
+ every object the reader meets. That size plus headroom
255
+ (:func:`_bounded`) is the bound. A compressed file is measured by its
256
+ decompressed size (read from its frame header), because that's what the
257
+ reader holds. A path that vanished between the listing and the stat
258
+ counts as zero. A newline-delimited reader takes
259
+ :func:`_line_size_bound` instead.
254
260
  """
255
- largest = 0
261
+ return _bounded(max((decoded_size(path) for path in paths), default=0))
262
+
263
+
264
+ def _line_size_bound(paths: Iterable[Path]) -> int:
265
+ """``maximum_object_size`` for a newline-delimited ``read_json`` over ``paths``: its longest line.
266
+
267
+ A newline-delimited file's objects are its lines, so the bound follows
268
+ the longest line, not the largest file. Sized from the file, one large
269
+ file set the buffers of every file in the statement: a lake batch of 512
270
+ sessions holding the 120 MB ``edges.jsonl`` of the largest live session
271
+ read all 512 files with 302 MB buffers (twice a 151 MB bound). That read
272
+ alone needed 2,046 MiB at four threads on DuckDB 2.0.0 (1,342 MiB on
273
+ 1.5.6), all but 2 MiB of the lake writer's 2 GiB cap, so ``lake
274
+ rebuild`` and ``lake verify`` ran out of memory (measured 2026-10-25).
275
+ DuckDB 2.0 scans each file of a list as its own parallel scan
276
+ (``read_single_json_file.cpp``) and opens files ahead of the scan, so
277
+ more of those buffers are live at once than on 1.5, which gave a list at
278
+ least as long as the thread count one buffer a thread. The longest line
279
+ of the 13,071 ``edges.jsonl`` files in the copied corpora (1.9 GB) is
280
+ 960 bytes, and of their ``session_events.jsonl`` files 8,913 bytes, so in
281
+ practice the bound is DuckDB's default.
282
+
283
+ A file that fits under the floor with its headroom can't hold a longer
284
+ line, so only a larger file is read, once, to find its longest line
285
+ (:func:`~atif_duck.infrastructure.stored_artifacts.longest_line`). The
286
+ same headroom, floor and cap apply.
287
+ """
288
+ longest = 0
256
289
  for path in paths:
257
- largest = max(largest, decoded_size(path))
258
- with_headroom = largest + largest // _OBJECT_SIZE_HEADROOM_DIVISOR + _OBJECT_SIZE_HEADROOM_BYTES
259
- return max(_OBJECT_SIZE_FLOOR, min(_OBJECT_SIZE_CAP, with_headroom))
290
+ size = decoded_size(path)
291
+ if _bounded(size) > _OBJECT_SIZE_FLOOR:
292
+ size = longest_line(path)
293
+ longest = max(longest, size)
294
+ return _bounded(longest)
260
295
 
261
296
 
262
297
  # Explicit projection for ``v_raw_trajectories``.
@@ -370,7 +405,7 @@ def _register_events_json(con: duckdb.DuckDBPyConnection, paths: Sequence[Path])
370
405
  the columnar producer applies, and the two sources agree byte for byte.
371
406
  The file list is the statement's one parameter.
372
407
  """
373
- bound = _object_size_bound(paths)
408
+ bound = _line_size_bound(paths)
374
409
  con.execute(
375
410
  f"""
376
411
  CREATE OR REPLACE TEMP TABLE {_RAW_SESSION_EVENTS_JSON_TABLE} AS
@@ -895,7 +930,7 @@ def register_raw(
895
930
  if path is not None
896
931
  ]
897
932
  edges_source: list[str] = [str(path) for path in edge_paths]
898
- edges_bound = _object_size_bound(edge_paths)
933
+ edges_bound = _line_size_bound(edge_paths)
899
934
  if wanted is None:
900
935
  loss_source: str | list[str] = str(sessions_dir / "*" / "loss_report.json")
901
936
  else:
@@ -13,11 +13,14 @@ A zstd frame written by atif-sql records its decompressed size in the frame
13
13
  header, which :func:`decoded_size` reads (18 bytes, no decompression): DuckDB's
14
14
  ``maximum_object_size`` has to be sized from the document, not from the far
15
15
  smaller compressed file. A frame without that field (written by another tool)
16
- is measured by decompressing it once.
16
+ is measured by decompressing it once. A newline-delimited artifact's objects
17
+ are its lines, so :func:`longest_line` measures the longest one, decompressing
18
+ the file once.
17
19
  """
18
20
 
19
21
  from __future__ import annotations
20
22
 
23
+ import contextlib
21
24
  import io
22
25
  from typing import TYPE_CHECKING
23
26
 
@@ -76,6 +79,41 @@ def decoded_size(path: Path) -> int:
76
79
  return 0
77
80
 
78
81
 
82
+ def _longest_line(path: Path) -> int:
83
+ import zstandard
84
+
85
+ longest = run = 0
86
+ with path.open("rb") as raw, contextlib.ExitStack() as stack:
87
+ stream = (
88
+ stack.enter_context(zstandard.ZstdDecompressor().stream_reader(raw))
89
+ if is_compressed(path)
90
+ else raw
91
+ )
92
+ while chunk := stream.read(_MEASURE_CHUNK_BYTES):
93
+ pieces = chunk.split(b"\n")
94
+ if len(pieces) == 1:
95
+ run += len(chunk)
96
+ continue
97
+ inner = max(map(len, pieces[1:-1]), default=0)
98
+ longest = max(longest, run + len(pieces[0]), inner)
99
+ run = len(pieces[-1])
100
+ return max(longest, run)
101
+
102
+
103
+ def longest_line(path: Path) -> int:
104
+ """The artifact's longest line in bytes once decompressed, newline excluded; 0 if unreadable.
105
+
106
+ Read a chunk at a time, so a file with no newline in it costs one chunk
107
+ of memory rather than the whole file.
108
+ """
109
+ import zstandard
110
+
111
+ try:
112
+ return _longest_line(path)
113
+ except (OSError, zstandard.ZstdError):
114
+ return 0
115
+
116
+
79
117
  def read_bytes(path: Path) -> bytes:
80
118
  """The artifact's decompressed bytes."""
81
119
  if not is_compressed(path):
@@ -110,6 +148,7 @@ __all__ = [
110
148
  "decoded_size",
111
149
  "is_compressed",
112
150
  "iter_lines",
151
+ "longest_line",
113
152
  "read_bytes",
114
153
  "stored_artifact",
115
154
  ]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "atif-embed"
3
- version = "0.4.0"
3
+ version = "0.5.0"
4
4
  description = "Embedding providers (Cohere Embed v4 on Bedrock, or EmbeddingGemma 2 on this machine) + LanceDB vector store + embed backfill over the ATIF corpus."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -18,7 +18,7 @@ classifiers = [
18
18
  ]
19
19
  dependencies = [
20
20
  "boto3>=1.42.91",
21
- "duckdb>=1.5.2,<2",
21
+ "duckdb>=2.0.0,<3",
22
22
  "lancedb>=0.30,<0.40",
23
23
  "loguru>=0.7.3",
24
24
  "polars>=1.40.0",
@@ -45,7 +45,7 @@ from loguru import logger
45
45
  from atif_embed.domain.discovery import PendingSelection
46
46
 
47
47
  if TYPE_CHECKING:
48
- from collections.abc import Iterator
48
+ from collections.abc import Iterable, Iterator
49
49
  from pathlib import Path
50
50
 
51
51
  from atif_embed.domain.text_stamp import PendingText
@@ -53,9 +53,19 @@ if TYPE_CHECKING:
53
53
  #: Minimum characters for a step text to be worth embedding.
54
54
  MIN_TEXT_CHARS = 32
55
55
 
56
- #: ``read_json`` upper bound — live trajectory.json files reach 85 MB
57
- #: (harbor inlines subagent sidechains); 1 GiB gives ~10x headroom.
58
- _MAX_OBJECT_SIZE = 1_073_741_824
56
+ #: Ceiling for a batch's ``read_json`` ``maximum_object_size``. Live
57
+ #: trajectory.json files reach 85 MB (harbor inlines subagent sidechains);
58
+ #: 1 GiB gives ~10x headroom.
59
+ _OBJECT_SIZE_CAP = 1_073_741_824
60
+
61
+ #: Floor for the same bound: DuckDB's own default.
62
+ _OBJECT_SIZE_FLOOR = 16_777_216
63
+
64
+ #: Headroom over a batch's largest document: a quarter of it plus one MiB, so
65
+ #: a file that grows a little between the stat and the read still parses.
66
+ #: The same rule as atif-duck's registry, which this package may not import.
67
+ _OBJECT_SIZE_HEADROOM_DIVISOR = 4
68
+ _OBJECT_SIZE_HEADROOM_BYTES = 1_048_576
59
69
 
60
70
  #: The trajectory's name, and the suffix it carries when stored compressed
61
71
  #: (twins of atif-corpus's, pinned in atif-cli's tests).
@@ -77,13 +87,14 @@ _FETCH_PAGE_ROWS = 512
77
87
  _BATCH_MAX_BYTES = 4 * 1024 * 1024
78
88
 
79
89
 
80
- def _step_texts_sql() -> str:
90
+ def _step_texts_sql(max_object_size: int) -> str:
81
91
  """SQL selecting ``(uuid, text_content)`` from ONE BATCH of trajectories.
82
92
 
83
93
  The batch's paths are NOT in this text: ``read_json(?)`` takes them as one
84
94
  bound list parameter at execute time, so a path is data to DuckDB and
85
- never part of the statement. The only interpolations are the two integer
86
- constants below.
95
+ never part of the statement. The only interpolations are integers: the
96
+ batch's ``maximum_object_size`` (:func:`_object_size_bound`) and the text
97
+ floor.
87
98
 
88
99
  One trajectory document per file -> ``format='auto'`` (NOT NDJSON). The
89
100
  explicit ``columns`` projection is a strict filter AND it skips JSON
@@ -107,7 +118,7 @@ def _step_texts_sql() -> str:
107
118
  THEN array_to_string(
108
119
  list_transform(
109
120
  json_extract(step, '$.message[*].text'),
110
- part -> json_extract_string(part, '$')
121
+ lambda part: json_extract_string(part, '$')
111
122
  ),
112
123
  '\n\n'
113
124
  )
@@ -118,7 +129,7 @@ def _step_texts_sql() -> str:
118
129
  format='auto',
119
130
  filename=true,
120
131
  columns={{steps: 'JSON[]'}},
121
- maximum_object_size={_MAX_OBJECT_SIZE}
132
+ maximum_object_size={int(max_object_size)}
122
133
  ) t,
123
134
  UNNEST(t.steps) AS s(step)
124
135
  )
@@ -128,7 +139,7 @@ def _step_texts_sql() -> str:
128
139
  AND text_content IS NOT NULL
129
140
  AND length(text_content) >= {MIN_TEXT_CHARS}
130
141
  ORDER BY filename, step_id
131
- """ # noqa: S608 # nosec B608 - paths are a bound parameter; both limits are int constants
142
+ """ # noqa: S608 # nosec B608 - paths are a bound parameter; both limits are ints
132
143
 
133
144
 
134
145
  def _decoded_size(path: Path) -> int:
@@ -173,7 +184,26 @@ def _complete_trajectory_paths(corpus_root: Path) -> list[tuple[str, int]]:
173
184
  return paths
174
185
 
175
186
 
176
- def _batch_by_bytes(paths: list[tuple[str, int]], *, max_bytes: int) -> list[list[str]]:
187
+ def _object_size_bound(sizes: Iterable[int]) -> int:
188
+ """``maximum_object_size`` for one batch: its largest document plus headroom.
189
+
190
+ Floored at DuckDB's default and capped at :data:`_OBJECT_SIZE_CAP`. The
191
+ bound is not free: DuckDB's JSON reader reserves buffers of about twice
192
+ it before it parses anything, and on DuckDB 2.0 the number of those
193
+ reservations grows with the files in one statement (to about 50), at any
194
+ thread count. Measured 2026-10-25 over 40 one-kilobyte trajectories in
195
+ one statement at one thread, under the fixed 1 GiB bound this replaced:
196
+ 2.0.0 ran out of a 49 GB ``memory_limit`` (44 GiB already reserved, 2 GiB
197
+ at a time), where 1.5.6 finished under 8 GB. A batch is a few MiB of
198
+ documents, so its bound is the floor, and a session above the batch
199
+ budget holds a statement alone with a bound sized to it.
200
+ """
201
+ largest = max(sizes, default=0)
202
+ with_headroom = largest + largest // _OBJECT_SIZE_HEADROOM_DIVISOR + _OBJECT_SIZE_HEADROOM_BYTES
203
+ return max(_OBJECT_SIZE_FLOOR, min(_OBJECT_SIZE_CAP, with_headroom))
204
+
205
+
206
+ def _batch_by_bytes(paths: list[tuple[str, int]], *, max_bytes: int) -> list[list[tuple[str, int]]]:
177
207
  """Group ``(path, size)`` pairs into batches of at most ``max_bytes``.
178
208
 
179
209
  Order is preserved, so batching cannot reorder rows. A file at or above
@@ -181,15 +211,15 @@ def _batch_by_bytes(paths: list[tuple[str, int]], *, max_bytes: int) -> list[lis
181
211
  budget is a target, and the true residency floor is the largest single
182
212
  session, which no grouping can lower.
183
213
  """
184
- batches: list[list[str]] = []
185
- current: list[str] = []
214
+ batches: list[list[tuple[str, int]]] = []
215
+ current: list[tuple[str, int]] = []
186
216
  current_bytes = 0
187
217
  for path, size in paths:
188
218
  if current and current_bytes + size > max_bytes:
189
219
  batches.append(current)
190
220
  current = []
191
221
  current_bytes = 0
192
- current.append(path)
222
+ current.append((path, size))
193
223
  current_bytes += size
194
224
  if current:
195
225
  batches.append(current)
@@ -262,7 +292,8 @@ def _step_rows(paths: list[tuple[str, int]]) -> Iterator[tuple[str, str]]:
262
292
  con = duckdb.connect(":memory:")
263
293
  try:
264
294
  for batch in _batch_by_bytes(paths, max_bytes=_BATCH_MAX_BYTES):
265
- result = con.execute(_step_texts_sql(), [batch])
295
+ bound = _object_size_bound(size for _, size in batch)
296
+ result = con.execute(_step_texts_sql(bound), [[path for path, _ in batch]])
266
297
  while page := result.fetchmany(_FETCH_PAGE_ROWS):
267
298
  for row in page:
268
299
  yield str(row[0]), str(row[1])
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "atif-models"
3
- version = "0.4.0"
3
+ version = "0.5.0"
4
4
  description = "Model alias registry + structured-output LLM client for atif-sql (CONTRACT-V2: no other package hardcodes a model id)."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -15,7 +15,7 @@
15
15
 
16
16
  [project]
17
17
  name = "atif-sql"
18
- version = "0.4.0"
18
+ version = "0.5.0"
19
19
  description = "ATIF-native analytics over Claude Code agent trajectories: convert, materialize, and query agent sessions with DuckDB."
20
20
  readme = "README.md"
21
21
  license = "Apache-2.0"
@@ -43,7 +43,7 @@ dependencies = [
43
43
  "anyio>=4.11.0",
44
44
  "boto3>=1.42.91",
45
45
  "cyclopts>=5.1.1",
46
- "duckdb>=1.5.2,<2",
46
+ "duckdb>=2.0.0,<3",
47
47
  "imagesize>=2.0",
48
48
  "lancedb>=0.30,<0.40",
49
49
  "loguru>=0.7.3",
@@ -484,7 +484,7 @@ allowed_prefixes = ["Merge", "Revert", "Pull request", "fixup!", "squash!"]
484
484
  # The single source of truth for that one version. The workspace root is virtual and
485
485
  # carries no [project] table, so commitizen's default `version_provider = "commitizen"`
486
486
  # reads this key rather than a PEP 621 field.
487
- version = "0.4.0"
487
+ version = "0.5.0"
488
488
  version_scheme = "pep440"
489
489
  # `v0.2.0`. publish.yml derives the release version by stripping the leading `v`, and
490
490
  # `gh release create --verify-tag` fails loudly if this format and that expectation
File without changes
File without changes
File without changes
File without changes