atif-sql 0.4.0__tar.gz → 0.6.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 (195) hide show
  1. {atif_sql-0.4.0 → atif_sql-0.6.0}/PKG-INFO +4 -5
  2. {atif_sql-0.4.0 → atif_sql-0.6.0}/README.md +2 -2
  3. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/pyproject.toml +3 -3
  4. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/README.md +4 -1
  5. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/pyproject.toml +8 -8
  6. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/app.py +161 -32
  7. atif_sql-0.6.0/packages/atif-cli/src/atif_cli/vector_probe.py +137 -0
  8. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/README.md +1 -1
  9. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/pyproject.toml +2 -2
  10. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/model_prices.json +1895 -167
  11. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/pricing.py +77 -38
  12. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/schema_version.py +11 -1
  13. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/pyproject.toml +2 -2
  14. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/slug.py +1 -1
  15. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/pyproject.toml +3 -3
  16. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/examples.py +2 -2
  17. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/projections.py +1 -1
  18. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/registry.py +127 -37
  19. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/stored_artifacts.py +40 -1
  20. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/README.md +40 -10
  21. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/pyproject.toml +4 -5
  22. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/__init__.py +4 -3
  23. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/application/embed.py +17 -11
  24. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/application/prune.py +1 -1
  25. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/errors.py +38 -0
  26. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/ports.py +1 -5
  27. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/sql_literal.py +12 -9
  28. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/cohere_bedrock.py +4 -4
  29. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/corpus_text_rows.py +46 -15
  30. atif_sql-0.6.0/packages/atif-embed/src/atif_embed/infrastructure/lance_extension.py +74 -0
  31. atif_sql-0.6.0/packages/atif-embed/src/atif_embed/infrastructure/lance_store.py +706 -0
  32. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/settings.py +4 -4
  33. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/pyproject.toml +2 -2
  34. {atif_sql-0.4.0 → atif_sql-0.6.0}/pyproject.toml +21 -9
  35. atif_sql-0.4.0/packages/atif-embed/src/atif_embed/infrastructure/lance_store.py +0 -440
  36. {atif_sql-0.4.0 → atif_sql-0.6.0}/.gitignore +0 -0
  37. {atif_sql-0.4.0 → atif_sql-0.6.0}/.vale/styles/Google/LICENSE +0 -0
  38. {atif_sql-0.4.0 → atif_sql-0.6.0}/.vale/styles/proselint/LICENSE +0 -0
  39. {atif_sql-0.4.0 → atif_sql-0.6.0}/.vale/styles/proselint/README.md +0 -0
  40. {atif_sql-0.4.0 → atif_sql-0.6.0}/LICENSE +0 -0
  41. {atif_sql-0.4.0 → atif_sql-0.6.0}/docs/README.md +0 -0
  42. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/LICENSE +0 -0
  43. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/README.md +0 -0
  44. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/__init__.py +0 -0
  45. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/__init__.py +0 -0
  46. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/analyze.py +0 -0
  47. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/prompts.py +0 -0
  48. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/__init__.py +0 -0
  49. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/_shared.py +0 -0
  50. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/classify.py +0 -0
  51. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/conflicts.py +0 -0
  52. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/friction.py +0 -0
  53. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/perceived.py +0 -0
  54. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/__init__.py +0 -0
  55. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/authorship.py +0 -0
  56. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/config.py +0 -0
  57. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/costs.py +0 -0
  58. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/friction.py +0 -0
  59. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/layout.py +0 -0
  60. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/models.py +0 -0
  61. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/ports.py +0 -0
  62. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/transcript.py +0 -0
  63. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/__init__.py +0 -0
  64. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/corpus_reader.py +0 -0
  65. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/parquet_cache.py +0 -0
  66. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/settings.py +0 -0
  67. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/sqlite_state/__init__.py +0 -0
  68. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/sqlite_state/checkpointer.py +0 -0
  69. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/sqlite_state/retry_queue.py +0 -0
  70. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/py.typed +0 -0
  71. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/LICENSE +0 -0
  72. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/__init__.py +0 -0
  73. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/__main__.py +0 -0
  74. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/converter_adapter.py +0 -0
  75. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/corpus.py +0 -0
  76. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/cron.py +0 -0
  77. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/duck_errors.py +0 -0
  78. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/embed_lake.py +0 -0
  79. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/errors.py +0 -0
  80. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/lake.py +0 -0
  81. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/lake_sessions.py +0 -0
  82. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/output.py +0 -0
  83. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/py.typed +0 -0
  84. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/LICENSE +0 -0
  85. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/__init__.py +0 -0
  86. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/application/__init__.py +0 -0
  87. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/application/convert_and_audit.py +0 -0
  88. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/application/convert_codex.py +0 -0
  89. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/__init__.py +0 -0
  90. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/agents.py +0 -0
  91. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/__init__.py +0 -0
  92. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/agent.py +0 -0
  93. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/content.py +0 -0
  94. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/final_metrics.py +0 -0
  95. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/metrics.py +0 -0
  96. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/observation.py +0 -0
  97. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/observation_result.py +0 -0
  98. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/step.py +0 -0
  99. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/subagent_trajectory_ref.py +0 -0
  100. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/tool_call.py +0 -0
  101. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/trajectory.py +0 -0
  102. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/trajectory_validator.py +0 -0
  103. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/blobs.py +0 -0
  104. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/claude_code_conversion.py +0 -0
  105. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/codex_conversion.py +0 -0
  106. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/codex_edges.py +0 -0
  107. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/codex_enrichment.py +0 -0
  108. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/codex_fidelity.py +0 -0
  109. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/codex_nested_calls.py +0 -0
  110. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/edges.py +0 -0
  111. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/enrichment.py +0 -0
  112. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/errors.py +0 -0
  113. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/fidelity.py +0 -0
  114. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/result_signals.py +0 -0
  115. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/session_events.py +0 -0
  116. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/__init__.py +0 -0
  117. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/census.py +0 -0
  118. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/claude_code_converter.py +0 -0
  119. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/codex_adapter.py +0 -0
  120. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/codex_census.py +0 -0
  121. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/codex_converter.py +0 -0
  122. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/harbor_adapter.py +0 -0
  123. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/raw_records.py +0 -0
  124. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/source_archive.py +0 -0
  125. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/py.typed +0 -0
  126. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/LICENSE +0 -0
  127. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/README.md +0 -0
  128. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/__init__.py +0 -0
  129. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/application/__init__.py +0 -0
  130. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/application/materialize.py +0 -0
  131. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/__init__.py +0 -0
  132. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/agents.py +0 -0
  133. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/generation.py +0 -0
  134. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/layout.py +0 -0
  135. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/ports.py +0 -0
  136. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/session_id.py +0 -0
  137. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/sessions.py +0 -0
  138. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/source_layout.py +0 -0
  139. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/watermark.py +0 -0
  140. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/__init__.py +0 -0
  141. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/atomic.py +0 -0
  142. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/compress_artifacts.py +0 -0
  143. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/fake_converter.py +0 -0
  144. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/scanner.py +0 -0
  145. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/settings.py +0 -0
  146. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/source_archive.py +0 -0
  147. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/py.typed +0 -0
  148. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/LICENSE +0 -0
  149. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/README.md +0 -0
  150. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/__init__.py +0 -0
  151. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/__init__.py +0 -0
  152. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/artifacts.py +0 -0
  153. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/authorship.py +0 -0
  154. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/catalog.py +0 -0
  155. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/columnar.py +0 -0
  156. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/embedding_guard.py +0 -0
  157. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/lake.py +0 -0
  158. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/lake_sessions.py +0 -0
  159. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/raw_readers.py +0 -0
  160. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/session_id.py +0 -0
  161. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/sql_literal.py +0 -0
  162. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/__init__.py +0 -0
  163. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/analytics.py +0 -0
  164. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/authorship.py +0 -0
  165. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/columnar.py +0 -0
  166. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/lake.py +0 -0
  167. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/lake_sessions.py +0 -0
  168. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/lake_settings.py +0 -0
  169. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/lake_steps.py +0 -0
  170. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/py.typed +0 -0
  171. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/LICENSE +0 -0
  172. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/application/__init__.py +0 -0
  173. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/__init__.py +0 -0
  174. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/discovery.py +0 -0
  175. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/embedding_guard.py +0 -0
  176. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/text_stamp.py +0 -0
  177. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/__init__.py +0 -0
  178. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/gemma_local.py +0 -0
  179. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/lake_text_rows.py +0 -0
  180. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/providers.py +0 -0
  181. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/py.typed +0 -0
  182. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/LICENSE +0 -0
  183. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/README.md +0 -0
  184. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/__init__.py +0 -0
  185. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/domain/__init__.py +0 -0
  186. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/domain/ports.py +0 -0
  187. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/domain/registry.py +0 -0
  188. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/domain/schema.py +0 -0
  189. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/infrastructure/__init__.py +0 -0
  190. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/infrastructure/anthropic_bedrock.py +0 -0
  191. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/infrastructure/bedrock_common.py +0 -0
  192. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/infrastructure/openai_bedrock.py +0 -0
  193. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/infrastructure/providers.py +0 -0
  194. {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/infrastructure/settings.py +0 -0
  195. {atif_sql-0.4.0 → atif_sql-0.6.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.6.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,9 +21,8 @@ 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
- Requires-Dist: lancedb<0.40,>=0.30
27
26
  Requires-Dist: loguru>=0.7.3
28
27
  Requires-Dist: polars>=1.40.0
29
28
  Requires-Dist: pyarrow>=23.0.1
@@ -83,7 +82,7 @@ uv tool install 'atif-sql[local]' --torch-backend cpu
83
82
  ```
84
83
 
85
84
  Python 3.13 or newer. The install is substantial and deliberately so, because the analytics and
86
- vector paths carry `polars`, `pyarrow`, `lancedb`, and `duckdb`. Conversion itself needs none of
85
+ vector paths carry `polars`, `pyarrow`, and `duckdb`. Conversion itself needs none of
87
86
  that weight: the ATIF models are vendored from Harbor and step pricing reads a vendored copy of
88
87
  litellm's price data, so neither `harbor` nor `litellm` is installed. Prebuilt wheels cover CPython
89
88
  3.13 on manylinux x86_64 and aarch64, macOS arm64, and Windows x86_64. Alpine and other musl targets
@@ -151,7 +150,7 @@ the only thing documented as installable is the `atif-sql` CLI above.
151
150
  | `atif-duck` | DuckDB views + macros over the materialized corpus (core surface plus the v2 analytics surface) |
152
151
  | `atif-models` | Model alias registry + structured-output LLM clients for GPT-6 and Claude 5.5 on Bedrock; no other package hardcodes an LLM model id |
153
152
  | `atif-analytics` | the LLM pipelines `atif-sql analyze` runs: classify, conflicts, friction, and perceived |
154
- | `atif-embed` | Cohere Embed v4 on Bedrock or EmbeddingGemma 2 on this machine + LanceDB vector store + embedding backfill |
153
+ | `atif-embed` | Cohere Embed v4 on Bedrock or EmbeddingGemma 2 on this machine + a Lance vector store written through DuckDB's lance extension + embedding backfill |
155
154
  | `atif-cli` | the composition root, and the source of the `atif-sql` command (see [What you can do](#what-you-can-do)) |
156
155
 
157
156
  ## Quick start
@@ -41,7 +41,7 @@ uv tool install 'atif-sql[local]' --torch-backend cpu
41
41
  ```
42
42
 
43
43
  Python 3.13 or newer. The install is substantial and deliberately so, because the analytics and
44
- vector paths carry `polars`, `pyarrow`, `lancedb`, and `duckdb`. Conversion itself needs none of
44
+ vector paths carry `polars`, `pyarrow`, and `duckdb`. Conversion itself needs none of
45
45
  that weight: the ATIF models are vendored from Harbor and step pricing reads a vendored copy of
46
46
  litellm's price data, so neither `harbor` nor `litellm` is installed. Prebuilt wheels cover CPython
47
47
  3.13 on manylinux x86_64 and aarch64, macOS arm64, and Windows x86_64. Alpine and other musl targets
@@ -109,7 +109,7 @@ the only thing documented as installable is the `atif-sql` CLI above.
109
109
  | `atif-duck` | DuckDB views + macros over the materialized corpus (core surface plus the v2 analytics surface) |
110
110
  | `atif-models` | Model alias registry + structured-output LLM clients for GPT-6 and Claude 5.5 on Bedrock; no other package hardcodes an LLM model id |
111
111
  | `atif-analytics` | the LLM pipelines `atif-sql analyze` runs: classify, conflicts, friction, and perceived |
112
- | `atif-embed` | Cohere Embed v4 on Bedrock or EmbeddingGemma 2 on this machine + LanceDB vector store + embedding backfill |
112
+ | `atif-embed` | Cohere Embed v4 on Bedrock or EmbeddingGemma 2 on this machine + a Lance vector store written through DuckDB's lance extension + embedding backfill |
113
113
  | `atif-cli` | the composition root, and the source of the `atif-sql` command (see [What you can do](#what-you-can-do)) |
114
114
 
115
115
  ## Quick start
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "atif-analytics"
3
- version = "0.4.0"
3
+ version = "0.6.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.6.0",
24
24
  "loguru>=0.7.3",
25
25
  "polars>=1.40.0",
26
26
  "pyarrow>=23.0.1",
@@ -37,7 +37,7 @@ Issues = "https://github.com/laithalsaadoon/atif-sql/issues"
37
37
  Changelog = "https://github.com/laithalsaadoon/atif-sql/blob/main/CHANGELOG.md"
38
38
 
39
39
  [build-system]
40
- requires = ["uv_build>=0.11.14,<0.13"]
40
+ requires = ["uv_build>=0.11.14,<0.14"]
41
41
  build-backend = "uv_build"
42
42
 
43
43
  [tool.uv.build-backend]
@@ -16,13 +16,16 @@ atif-sql materialize [--force] [--quiesce-seconds N]
16
16
  [--sessions id,id] # contract-compatible extension:
17
17
  # plan only the named sessions
18
18
  atif-sql status # corpus freshness, read-only
19
+ atif-sql status --vector-probe # time search's exact kNN over the store
20
+ # (no Bedrock; the nightly compact lane
21
+ # logs it per corpus)
19
22
  atif-sql query 'SQL' [--format auto|table|json|csv]
20
23
  atif-sql schema # static catalog, no duckdb bind, <50ms
21
24
  atif-sql examples # tested example queries per view/macro
22
25
  # (alias: atif-sql query --examples)
23
26
  atif-sql analyze # v2 analytics pipelines; dry-run by
24
27
  # default, spends only with --no-dry-run
25
- atif-sql embed --all --no-dry-run # embed corpus steps into LanceDB
28
+ atif-sql embed --all --no-dry-run # embed corpus steps into the Lance store
26
29
  # (a real run needs --all or --limit N)
27
30
  atif-sql search '<text>' # embed the text, then semantic kNN
28
31
  atif-sql cron install | status # print the refresh crontab block, or
@@ -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.6.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.6.0",
33
+ "atif-converter==0.6.0",
34
+ "atif-corpus==0.6.0",
35
+ "atif-duck==0.6.0",
36
+ "atif-embed==0.6.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
 
@@ -50,7 +50,7 @@ Issues = "https://github.com/laithalsaadoon/atif-sql/issues"
50
50
  Changelog = "https://github.com/laithalsaadoon/atif-sql/blob/main/CHANGELOG.md"
51
51
 
52
52
  [build-system]
53
- requires = ["uv_build>=0.11.14,<0.13"]
53
+ requires = ["uv_build>=0.11.14,<0.14"]
54
54
  build-backend = "uv_build"
55
55
 
56
56
  [tool.uv.build-backend]
@@ -16,7 +16,7 @@ The twelve registered commands — nine ``@app.command`` functions plus the
16
16
  * ``status`` corpus freshness — read-only, fast
17
17
  * ``query`` SQL over the atif-duck views/macros with classified errors
18
18
  * ``analyze`` run the analytics pipelines — CALLS BEDROCK, spends money
19
- * ``embed`` backfill step embeddings into LanceDB — CALLS BEDROCK
19
+ * ``embed`` backfill step embeddings into the Lance store — CALLS BEDROCK
20
20
  * ``search`` embed one query string, then kNN over the store — CALLS BEDROCK
21
21
  * ``examples`` derived, test-executed example queries (also ``query --examples``)
22
22
  * ``schema`` static catalog dump, no DuckDB bind, <50ms
@@ -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>
@@ -1136,6 +1151,55 @@ def _lake_surface(corpus_root: Path) -> dict[str, Any]:
1136
1151
  }
1137
1152
 
1138
1153
 
1154
+ def _emit_vector_probe(corpus_root: Path, fmt: OutputFormat) -> None:
1155
+ """``status --vector-probe``: time ``search``'s exact kNN over this corpus's store."""
1156
+ import duckdb
1157
+
1158
+ from atif_cli.duck_errors import classify_duckdb_error
1159
+ from atif_cli.vector_probe import PROBE_K, probe_store
1160
+ from atif_embed.domain.errors import DomainError, LanceExtensionMissing
1161
+
1162
+ store = _embed_settings(fmt).resolve_lance_uri(corpus_root)
1163
+ try:
1164
+ probe = probe_store(store, corpus=corpus_root.name)
1165
+ except LanceExtensionMissing as exc:
1166
+ err = ClassifiedError(
1167
+ kind="extension_missing",
1168
+ exit_code=EXIT_CODES["extension_missing"],
1169
+ message=f"the lance DuckDB extension is not installed, so the embeddings "
1170
+ f"store at {store} cannot be read",
1171
+ hint="run: atif-sql embed --install-extension (a one-time download)",
1172
+ )
1173
+ emit_error(err, fmt)
1174
+ raise SystemExit(err.exit_code) from exc
1175
+ except DomainError as exc:
1176
+ # A store the extension can't open is terminal, as it is for embed.
1177
+ kind = "terminal_state" if exc.terminal else "runtime_error"
1178
+ err = ClassifiedError(kind=kind, exit_code=EXIT_CODES[kind], message=str(exc))
1179
+ emit_error(err, fmt)
1180
+ raise SystemExit(err.exit_code) from exc
1181
+ except duckdb.Error as exc:
1182
+ err = classify_duckdb_error(exc)
1183
+ emit_error(err, fmt)
1184
+ raise SystemExit(err.exit_code) from exc
1185
+ if probe is None:
1186
+ err = ClassifiedError(
1187
+ kind="no_embeddings",
1188
+ exit_code=EXIT_CODES["no_embeddings"],
1189
+ message=f"no embeddings to scan at {store}",
1190
+ hint="Run: atif-sql embed --all --no-dry-run (or --limit N)",
1191
+ )
1192
+ emit_error(err, fmt)
1193
+ raise SystemExit(err.exit_code)
1194
+ if resolve_format(fmt) is OutputFormat.TABLE:
1195
+ print(
1196
+ f"vector probe: {probe.vector_rows:,} vectors, exact top-{PROBE_K} scan in "
1197
+ f"{probe.scan_s:.3f}s ({probe.store})"
1198
+ )
1199
+ else:
1200
+ emit_json(probe.as_dict(), fmt)
1201
+
1202
+
1139
1203
  def _dir_bytes(root: Path) -> int:
1140
1204
  """Total bytes of every file under ``root`` (0 if absent)."""
1141
1205
  if not root.is_dir():
@@ -1150,6 +1214,7 @@ def status(
1150
1214
  source_root: Path | None = None,
1151
1215
  corpus_root: Path | None = None,
1152
1216
  quiesce_seconds: int | None = None,
1217
+ vector_probe: Annotated[bool, cyclopts.Parameter(name="--vector-probe")] = False,
1153
1218
  fmt: Annotated[OutputFormat, cyclopts.Parameter(name="--format")] = OutputFormat.AUTO,
1154
1219
  ) -> None:
1155
1220
  """Report corpus freshness: watermark age, counts, bytes, staleness.
@@ -1189,7 +1254,26 @@ def status(
1189
1254
  not installed; ``query`` binds ``message_embeddings`` empty rather than
1190
1255
  downloading it, so run ``atif-sql embed --install-extension``). Read
1191
1256
  from DuckDB's local extension listing; never installs anything.
1257
+
1258
+ ``--vector-probe`` prints a timing instead of the report. It runs the
1259
+ exact top-10 cosine scan ``search`` runs over the embeddings store, with
1260
+ the stored embedding of the smallest uuid as the query vector, so it
1261
+ calls no embedding model and reaches no network. Only the scan is timed,
1262
+ on the store's own bounded connection (1 GiB, 2 threads). Output:
1263
+ ``{corpus, store, vector_rows, scan_s}``. It reads every stored vector,
1264
+ so it costs what a ``search`` costs, and a plain ``status`` never runs
1265
+ it. The nightly compact lane logs it per corpus to tell when ``search``
1266
+ needs a vector index. Exit codes: 0, 2 no_embeddings (no store, or one
1267
+ with no rows), 78 extension_missing (the lance extension isn't
1268
+ installed; the probe never downloads it) or terminal_state (the store
1269
+ holds a Lance dataset the extension can't open), and a DuckDB error
1270
+ during the scan exits the way ``query``'s do (70 for a runtime error).
1192
1271
  """
1272
+ if vector_probe:
1273
+ settings = _corpus_settings(source_root, corpus_root, agent)
1274
+ _emit_vector_probe(settings.corpus_root, fmt)
1275
+ return
1276
+
1193
1277
  import time
1194
1278
 
1195
1279
  from atif_cli.corpus import storage_layout
@@ -1364,7 +1448,11 @@ def query(
1364
1448
  parquet> (USE_TMP_FILE false)`` used to overwrite one, so every
1365
1449
  statement kind that names a file is refused BEFORE execution, for any
1366
1450
  uid, by DuckDB's own parser: COPY, EXPORT, ATTACH, DETACH, INSTALL,
1367
- LOAD, and PREPARE/EXECUTE (which could defer one). Exit 70, kind
1451
+ LOAD, and PREPARE/EXECUTE (which could defer one). So are PRAGMA and
1452
+ MULTI, which DuckDB 2.0 no longer expands while parsing (a PRAGMA can be
1453
+ ``COPY FROM DATABASE`` or ``IMPORT DATABASE``, and a MULTI can wrap a
1454
+ COPY): run ``pragma_table_info(...)``, ``DESCRIBE`` or ``SHOW`` instead,
1455
+ and give a PIVOT an explicit ``IN`` list. Exit 70, kind
1368
1456
  ``sandbox_refused``.
1369
1457
  * A 0444 file mode does not bind root, so ``query`` refuses to run as
1370
1458
  uid 0 (exit 77) unless ``ATIF_SQL_ALLOW_ROOT=1`` is set, which logs a
@@ -1405,7 +1493,7 @@ def query(
1405
1493
  * 65 catalog_error unknown view/macro/column (try ``atif-sql schema``)
1406
1494
  * 65 embedding_mismatch the Lance store was written by another provider
1407
1495
  * 70 sandbox_refused a statement kind the sandbox never runs (COPY,
1408
- EXPORT, ATTACH, INSTALL, LOAD, PREPARE, ...)
1496
+ EXPORT, ATTACH, INSTALL, LOAD, PREPARE, PRAGMA, ...)
1409
1497
  * 70 runtime_error everything else (an unmaterialized corpus, or SQL
1410
1498
  the sandbox refused)
1411
1499
  * 77 root_refused running as uid 0 without ``ATIF_SQL_ALLOW_ROOT=1``
@@ -1507,7 +1595,10 @@ def query(
1507
1595
  message=f"the query sandbox does not run {', '.join(refused)} statements",
1508
1596
  hint="query runs SELECT and in-memory DDL/DML only; COPY, EXPORT, "
1509
1597
  "ATTACH, DETACH, INSTALL, LOAD, PREPARE and EXECUTE are refused "
1510
- "because they reach files or defer a statement past this check",
1598
+ "because they reach files or defer a statement past this check, and "
1599
+ "PRAGMA and MULTI because the parser no longer says what they hold "
1600
+ "(use pragma_table_info(...), DESCRIBE or SHOW for a PRAGMA, and an "
1601
+ "explicit IN list for a PIVOT)",
1511
1602
  )
1512
1603
  emit_error(err, fmt)
1513
1604
  raise SystemExit(err.exit_code)
@@ -1783,8 +1874,9 @@ def _install_lance_extension(fmt: OutputFormat, *, quiet: bool = False) -> None:
1783
1874
  from loguru import logger
1784
1875
 
1785
1876
  logger.warning(
1786
- "Could not install the lance DuckDB extension ({}); vector search stays "
1787
- "unavailable until `atif-sql embed --install-extension` succeeds",
1877
+ "Could not install the lance DuckDB extension ({}); the embeddings store "
1878
+ "can't be written or searched until `atif-sql embed --install-extension` "
1879
+ "succeeds",
1788
1880
  str(exc).splitlines()[0],
1789
1881
  )
1790
1882
  return
@@ -1809,7 +1901,7 @@ def embed(
1809
1901
  corpus_root: Path | None = None,
1810
1902
  fmt: Annotated[OutputFormat, cyclopts.Parameter(name="--format")] = OutputFormat.AUTO,
1811
1903
  ) -> None:
1812
- """Embed unembedded corpus steps and append them to LanceDB.
1904
+ """Embed unembedded corpus steps and append them to the Lance store.
1813
1905
 
1814
1906
  Provider
1815
1907
  --------
@@ -1847,14 +1939,17 @@ def embed(
1847
1939
 
1848
1940
  Extension
1849
1941
  ---------
1850
- ``query`` and ``search`` read the store through DuckDB's lance extension
1851
- and never install it themselves (installing is a 242 MB download from
1852
- the extension repository, and ``query`` runs unattended). It is
1853
- installed here, where the network is already a deliberate act: every
1854
- REAL run installs it first, and ``--install-extension`` installs it and
1855
- exits without touching Bedrock or the store (``{"extension": "lance",
1856
- "installed": true, "install_path": ...}``). ``atif-sql status`` reports
1857
- whether it is present.
1942
+ The store is written and read only through DuckDB's lance extension.
1943
+ ``query`` and ``search`` never install it themselves (installing is a
1944
+ 242 MB download from the extension repository, and ``query`` runs
1945
+ unattended). It is installed here, where the network is already a
1946
+ deliberate act: every REAL run installs it first, and
1947
+ ``--install-extension`` installs it and exits without touching Bedrock
1948
+ or the store (``{"extension": "lance", "installed": true,
1949
+ "install_path": ...}``). A dry run never installs it, so a dry run over
1950
+ an existing store without the extension exits 78 and names
1951
+ ``--install-extension``. ``atif-sql status`` reports whether it is
1952
+ present.
1858
1953
 
1859
1954
  Cost
1860
1955
  ----
@@ -1969,6 +2064,7 @@ def _prune_orphans(corpus_root: Path | None, *, dry_run: bool, fmt: OutputFormat
1969
2064
  from atif_corpus.application.materialize import read_sink_pending
1970
2065
  from atif_corpus.domain.layout import CorpusLayout
1971
2066
  from atif_embed.application.prune import prune_orphans
2067
+ from atif_embed.domain.errors import DomainError
1972
2068
  from atif_embed.infrastructure.lance_store import LanceVectorStore
1973
2069
 
1974
2070
  settings = _corpus_settings(None, corpus_root)
@@ -1987,12 +2083,21 @@ def _prune_orphans(corpus_root: Path | None, *, dry_run: bool, fmt: OutputFormat
1987
2083
  fmt,
1988
2084
  )
1989
2085
  raise SystemExit(EXIT_CODES["lake_unavailable"])
1990
- report = prune_orphans(
1991
- settings.corpus_root,
1992
- lake=lake_steps_port(),
1993
- store=LanceVectorStore(lance_uri, dim=embed_settings.embedding_dim),
1994
- dry_run=dry_run,
1995
- )
2086
+ try:
2087
+ report = prune_orphans(
2088
+ settings.corpus_root,
2089
+ lake=lake_steps_port(),
2090
+ store=LanceVectorStore(
2091
+ lance_uri, dim=embed_settings.embedding_dim, install_extension=not dry_run
2092
+ ),
2093
+ dry_run=dry_run,
2094
+ )
2095
+ except DomainError as exc:
2096
+ # The store's own errors (a missing extension on a dry prune, a store
2097
+ # the extension can't open) get the same envelope as embed's.
2098
+ kind = "terminal_state" if exc.terminal else "runtime_error"
2099
+ emit_error(ClassifiedError(kind=kind, exit_code=EXIT_CODES[kind], message=str(exc)), fmt)
2100
+ raise SystemExit(EXIT_CODES[kind]) from exc
1996
2101
  if report is None:
1997
2102
  emit_error(
1998
2103
  ClassifiedError(
@@ -2072,7 +2177,8 @@ def search(
2072
2177
  extension_missing (a store exists but the lance DuckDB extension is not
2073
2178
  installed; run ``atif-sql embed --install-extension``), lake_unavailable
2074
2179
  (``--all-corpora`` without a usable lake) or terminal_state (the gemma
2075
- provider is selected and the ``local`` extra is not installed).
2180
+ provider is selected and the ``local`` extra is not installed, or the store
2181
+ holds a Lance dataset the extension can't open).
2076
2182
  """
2077
2183
  _refuse_root("search", fmt)
2078
2184
 
@@ -2083,7 +2189,11 @@ def search(
2083
2189
  classify_duckdb_error,
2084
2190
  classify_registration_error,
2085
2191
  )
2086
- from atif_duck.infrastructure.registry import lance_extension_installed, register
2192
+ from atif_duck.infrastructure.registry import (
2193
+ lance_extension_installed,
2194
+ lance_store_unreadable,
2195
+ register,
2196
+ )
2087
2197
  from atif_embed.application.embed import embed_query
2088
2198
  from atif_embed.domain.errors import DomainError
2089
2199
 
@@ -2144,6 +2254,23 @@ def search(
2144
2254
 
2145
2255
  row = con.execute("SELECT count(*) FROM message_embeddings").fetchone()
2146
2256
  if not row or int(row[0]) == 0:
2257
+ # No rows can also mean a store the extension can't open, which
2258
+ # binds as an empty one; embedding into it can't fix that.
2259
+ unreadable = [uri for uri in stores or [lance_uri] if lance_store_unreadable(con, uri)]
2260
+ if unreadable:
2261
+ emit_error(
2262
+ ClassifiedError(
2263
+ kind="terminal_state",
2264
+ exit_code=EXIT_CODES["terminal_state"],
2265
+ message=f"the embeddings store at {unreadable[0]} holds a Lance dataset "
2266
+ "the lance extension can't open; its newest manifest may be damaged "
2267
+ "or partly written",
2268
+ hint="restore the store from a backup, or move it aside and re-embed "
2269
+ "with `atif-sql embed --all --no-dry-run`",
2270
+ ),
2271
+ fmt,
2272
+ )
2273
+ raise SystemExit(EXIT_CODES["terminal_state"])
2147
2274
  emit_error(
2148
2275
  ClassifiedError(
2149
2276
  kind="no_embeddings",
@@ -2177,12 +2304,14 @@ def search(
2177
2304
 
2178
2305
 
2179
2306
  #: ``search``'s kNN, one text per shape. Rank by cosine similarity
2180
- #: descending: ORDER BY array_cosine_distance (== 1 - sim) ASC is what
2181
- #: triggers the cosine HNSW index lookup. Using array_distance here (L2) would
2182
- #: silently bypass the index AND give wrong ranks: the raw int8-cast-to-float
2183
- #: document vectors have magnitudes in the thousands while the query vector is
2184
- #: unit-normalized — only cosine is magnitude-invariant. ``{dim}`` is the only
2185
- #: placeholder, filled with ``int(len(vector))``.
2307
+ #: descending: ORDER BY array_cosine_distance (== 1 - sim) ASC with a LIMIT,
2308
+ #: which DuckDB runs as an exact top-k over a full scan of the Lance table
2309
+ #: (EXPLAIN shows a ``Lance Table Scan`` with ``Lance Limit: none`` under the
2310
+ #: join and a ``Top N`` above it; no vector index is read).
2311
+ #: Cosine is the only correct metric here, not a speed choice: the raw
2312
+ #: int8-cast-to-float document vectors have magnitudes in the thousands while
2313
+ #: the query vector is unit-normalized, so array_distance (L2) would give wrong
2314
+ #: ranks. ``{dim}`` is the only placeholder, filled with ``int(len(vector))``.
2186
2315
  _SEARCH_SQL = """
2187
2316
  WITH qv AS (SELECT CAST(? AS FLOAT[{dim}]) AS v)
2188
2317
  SELECT me.uuid AS uuid,
@@ -0,0 +1,137 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ """Time the exact kNN scan ``search`` runs, with no Bedrock call and no network.
4
+
5
+ ``atif-sql status --vector-probe`` runs this, and the nightly compact lane in
6
+ ``scripts/atif-sql-refresh.sh`` logs the result for each corpus. ``search``
7
+ reads no vector index: it ranks every stored vector, so what it pays grows
8
+ with the store, and this number is how the lane notices when it's grown too
9
+ far.
10
+
11
+ The probe runs :data:`PROBE_SQL`, which ranks the store's rows the way
12
+ ``search`` ranks them (the same ``qv`` CTE, cosine similarity, and ``ORDER BY
13
+ array_cosine_distance ... ASC`` with a ``LIMIT``), minus ``search``'s join back
14
+ to ``steps``. Its query vector is the stored embedding of the smallest uuid,
15
+ read in the same single statement that picks it, so a store gives the same
16
+ statement every night and nothing is embedded. Only the scan is timed.
17
+ Attaching the store, counting its rows and reading the probe vector all
18
+ happen before the clock starts. Reading the probe vector reads every vector
19
+ once, so the timed scan always runs on a warm page cache.
20
+
21
+ The connection is the store's own, from
22
+ :func:`atif_embed.infrastructure.lance_store.connect_db`: 1 GiB of DuckDB
23
+ memory, 2 threads, a private spill directory, no extension autoload, and no
24
+ download of the lance extension. The compact lane runs inside a 4G memory
25
+ scope, and an unbounded connection would size itself from the whole host.
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import time
31
+ from dataclasses import dataclass
32
+ from pathlib import Path
33
+ from typing import Any
34
+
35
+ #: How many neighbors the probe asks for: ``search``'s default ``-k``.
36
+ PROBE_K = 10
37
+
38
+ #: The probe's kNN. Every line from the ``qv`` CTE to ``LIMIT ?`` except the
39
+ #: ``FROM`` matches a line of ``atif_cli.app._SEARCH_SQL``, and
40
+ #: ``tests/test_vector_probe.py`` checks that, so a change to how ``search``
41
+ #: ranks has to change this too. ``{dim}`` is filled with ``int()`` and
42
+ #: ``{table}`` with the store's table name, never with caller text.
43
+ PROBE_SQL = """
44
+ WITH qv AS (SELECT CAST(? AS FLOAT[{dim}]) AS v)
45
+ SELECT me.uuid AS uuid,
46
+ array_cosine_similarity(me.embedding, (SELECT v FROM qv)) AS sim
47
+ FROM {table} me
48
+ ORDER BY array_cosine_distance(me.embedding, (SELECT v FROM qv)) ASC
49
+ LIMIT ?
50
+ """
51
+
52
+
53
+ @dataclass(frozen=True)
54
+ class VectorProbe:
55
+ """One timed scan of one store.
56
+
57
+ ``hits`` holds the ``(uuid, sim)`` rows the scan returned, best first.
58
+ It's for tests and isn't part of :meth:`as_dict`: the lane logs the
59
+ timing, not the neighbors.
60
+ """
61
+
62
+ corpus: str
63
+ store: Path
64
+ vector_rows: int
65
+ scan_s: float
66
+ hits: tuple[tuple[str, float], ...]
67
+
68
+ def as_dict(self) -> dict[str, Any]:
69
+ """The probe's JSON payload: ``{corpus, store, vector_rows, scan_s}``."""
70
+ return {
71
+ "corpus": self.corpus,
72
+ "store": str(self.store),
73
+ "vector_rows": self.vector_rows,
74
+ "scan_s": self.scan_s,
75
+ }
76
+
77
+
78
+ def probe_sql(*, dim: int, table: str) -> str:
79
+ """:data:`PROBE_SQL` for a ``dim``-wide store whose table is ``table``."""
80
+ return PROBE_SQL.format(dim=int(dim), table=table)
81
+
82
+
83
+ def probe_store(store: Path, *, corpus: str, k: int = PROBE_K) -> VectorProbe | None:
84
+ """Time one exact top-``k`` scan of the store at ``store``.
85
+
86
+ Returns ``None`` when there's nothing to scan: no store directory, no
87
+ embeddings table in it, or no rows. The directory is checked before
88
+ connecting, because opening a store creates its directory.
89
+
90
+ Raises
91
+ ------
92
+ LanceExtensionMissing
93
+ The lance extension isn't installed. The probe never downloads it.
94
+ duckdb.Error
95
+ The store couldn't be read.
96
+ """
97
+ from atif_embed.infrastructure.lance_store import (
98
+ STORE_ALIAS,
99
+ TABLE_NAME,
100
+ connect_db,
101
+ has_table,
102
+ )
103
+
104
+ if not store.is_dir():
105
+ return None
106
+ table = f"{STORE_ALIAS}.main.{TABLE_NAME}"
107
+ with connect_db(store) as conn:
108
+ if not has_table(conn):
109
+ return None
110
+ con = conn.con
111
+ vector_rows = int(con.execute(f"SELECT count(*) FROM {table}").fetchall()[0][0]) # noqa: S608 # nosec B608 - table is the store's constant name
112
+ if vector_rows == 0:
113
+ return None
114
+ # One statement, so a delete landing between "which uuid" and "its
115
+ # vector" can't leave nothing to read. It's one scan that reads every
116
+ # vector, which also leaves them in the page cache: the timed scan
117
+ # below runs warm every night, so nights compare like with like.
118
+ first = con.execute(
119
+ f"SELECT uuid, embedding FROM {table} ORDER BY uuid LIMIT 1" # noqa: S608 # nosec B608 - table is the store's constant name
120
+ ).fetchall()
121
+ if not first:
122
+ return None
123
+ vector = first[0][1]
124
+ sql = probe_sql(dim=len(vector), table=table)
125
+ started = time.perf_counter()
126
+ rows = con.execute(sql, [list(vector), int(k)]).fetchall()
127
+ scan_s = time.perf_counter() - started
128
+ return VectorProbe(
129
+ corpus=corpus,
130
+ store=store,
131
+ vector_rows=vector_rows,
132
+ scan_s=round(scan_s, 3),
133
+ hits=tuple((str(uuid), float(sim)) for uuid, sim in rows),
134
+ )
135
+
136
+
137
+ __all__ = ["PROBE_K", "PROBE_SQL", "VectorProbe", "probe_sql", "probe_store"]
@@ -29,7 +29,7 @@ value: `atif_converter.domain.pricing` returns the same floats, bit for bit,
29
29
  without litellm being installed. It reads `domain/model_prices.json`, a
30
30
  filtered copy of litellm's public `model_prices_and_context_window.json` (MIT;
31
31
  the notice and source ref are in its `meta` block), and repeats litellm
32
- 1.104.2's arithmetic in the same float operation order. The table holds the
32
+ 1.106.3's arithmetic in the same float operation order. The table holds the
33
33
  Claude and OpenAI text models our transcripts name under the `anthropic`,
34
34
  `openai`, `bedrock` and `bedrock_converse` providers, and keeps litellm's
35
35
  `fallback_generalizations` rules, which route an unmapped `claude-<family>-<n>`
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "atif-converter"
3
- version = "0.4.0"
3
+ version = "0.6.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"
@@ -37,7 +37,7 @@ Issues = "https://github.com/laithalsaadoon/atif-sql/issues"
37
37
  Changelog = "https://github.com/laithalsaadoon/atif-sql/blob/main/CHANGELOG.md"
38
38
 
39
39
  [build-system]
40
- requires = ["uv_build>=0.11.14,<0.13"]
40
+ requires = ["uv_build>=0.11.14,<0.14"]
41
41
  build-backend = "uv_build"
42
42
 
43
43
  [tool.uv.build-backend]