atif-sql 0.5.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.5.0 → atif_sql-0.6.0}/PKG-INFO +3 -4
  2. {atif_sql-0.5.0 → atif_sql-0.6.0}/README.md +2 -2
  3. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/pyproject.toml +3 -3
  4. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-cli/README.md +4 -1
  5. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-cli/pyproject.toml +7 -7
  6. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/app.py +133 -26
  7. atif_sql-0.6.0/packages/atif-cli/src/atif_cli/vector_probe.py +137 -0
  8. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/README.md +1 -1
  9. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/pyproject.toml +2 -2
  10. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/model_prices.json +1895 -167
  11. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/pricing.py +77 -38
  12. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/schema_version.py +11 -1
  13. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/pyproject.toml +2 -2
  14. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/slug.py +1 -1
  15. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/pyproject.toml +2 -2
  16. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/registry.py +75 -20
  17. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/README.md +40 -10
  18. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/pyproject.toml +3 -4
  19. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/__init__.py +4 -3
  20. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/application/embed.py +17 -11
  21. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/application/prune.py +1 -1
  22. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/errors.py +38 -0
  23. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/ports.py +1 -5
  24. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/sql_literal.py +12 -9
  25. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/cohere_bedrock.py +4 -4
  26. atif_sql-0.6.0/packages/atif-embed/src/atif_embed/infrastructure/lance_extension.py +74 -0
  27. atif_sql-0.6.0/packages/atif-embed/src/atif_embed/infrastructure/lance_store.py +706 -0
  28. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/settings.py +4 -4
  29. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-models/pyproject.toml +2 -2
  30. {atif_sql-0.5.0 → atif_sql-0.6.0}/pyproject.toml +20 -8
  31. atif_sql-0.5.0/packages/atif-embed/src/atif_embed/infrastructure/lance_store.py +0 -440
  32. {atif_sql-0.5.0 → atif_sql-0.6.0}/.gitignore +0 -0
  33. {atif_sql-0.5.0 → atif_sql-0.6.0}/.vale/styles/Google/LICENSE +0 -0
  34. {atif_sql-0.5.0 → atif_sql-0.6.0}/.vale/styles/proselint/LICENSE +0 -0
  35. {atif_sql-0.5.0 → atif_sql-0.6.0}/.vale/styles/proselint/README.md +0 -0
  36. {atif_sql-0.5.0 → atif_sql-0.6.0}/LICENSE +0 -0
  37. {atif_sql-0.5.0 → atif_sql-0.6.0}/docs/README.md +0 -0
  38. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/LICENSE +0 -0
  39. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/README.md +0 -0
  40. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/__init__.py +0 -0
  41. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/__init__.py +0 -0
  42. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/analyze.py +0 -0
  43. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/prompts.py +0 -0
  44. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/__init__.py +0 -0
  45. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/_shared.py +0 -0
  46. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/classify.py +0 -0
  47. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/conflicts.py +0 -0
  48. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/friction.py +0 -0
  49. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/perceived.py +0 -0
  50. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/__init__.py +0 -0
  51. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/authorship.py +0 -0
  52. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/config.py +0 -0
  53. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/costs.py +0 -0
  54. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/friction.py +0 -0
  55. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/layout.py +0 -0
  56. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/models.py +0 -0
  57. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/ports.py +0 -0
  58. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/transcript.py +0 -0
  59. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/__init__.py +0 -0
  60. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/corpus_reader.py +0 -0
  61. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/parquet_cache.py +0 -0
  62. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/settings.py +0 -0
  63. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/sqlite_state/__init__.py +0 -0
  64. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/sqlite_state/checkpointer.py +0 -0
  65. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/sqlite_state/retry_queue.py +0 -0
  66. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/py.typed +0 -0
  67. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-cli/LICENSE +0 -0
  68. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/__init__.py +0 -0
  69. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/__main__.py +0 -0
  70. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/converter_adapter.py +0 -0
  71. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/corpus.py +0 -0
  72. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/cron.py +0 -0
  73. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/duck_errors.py +0 -0
  74. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/embed_lake.py +0 -0
  75. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/errors.py +0 -0
  76. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/lake.py +0 -0
  77. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/lake_sessions.py +0 -0
  78. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/output.py +0 -0
  79. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/py.typed +0 -0
  80. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/LICENSE +0 -0
  81. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/__init__.py +0 -0
  82. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/application/__init__.py +0 -0
  83. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/application/convert_and_audit.py +0 -0
  84. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/application/convert_codex.py +0 -0
  85. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/__init__.py +0 -0
  86. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/agents.py +0 -0
  87. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/__init__.py +0 -0
  88. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/agent.py +0 -0
  89. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/content.py +0 -0
  90. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/final_metrics.py +0 -0
  91. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/metrics.py +0 -0
  92. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/observation.py +0 -0
  93. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/observation_result.py +0 -0
  94. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/step.py +0 -0
  95. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/subagent_trajectory_ref.py +0 -0
  96. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/tool_call.py +0 -0
  97. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/trajectory.py +0 -0
  98. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/trajectory_validator.py +0 -0
  99. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/blobs.py +0 -0
  100. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/claude_code_conversion.py +0 -0
  101. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/codex_conversion.py +0 -0
  102. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/codex_edges.py +0 -0
  103. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/codex_enrichment.py +0 -0
  104. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/codex_fidelity.py +0 -0
  105. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/codex_nested_calls.py +0 -0
  106. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/edges.py +0 -0
  107. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/enrichment.py +0 -0
  108. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/errors.py +0 -0
  109. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/fidelity.py +0 -0
  110. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/result_signals.py +0 -0
  111. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/session_events.py +0 -0
  112. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/__init__.py +0 -0
  113. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/census.py +0 -0
  114. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/claude_code_converter.py +0 -0
  115. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/codex_adapter.py +0 -0
  116. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/codex_census.py +0 -0
  117. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/codex_converter.py +0 -0
  118. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/harbor_adapter.py +0 -0
  119. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/raw_records.py +0 -0
  120. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/source_archive.py +0 -0
  121. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/py.typed +0 -0
  122. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/LICENSE +0 -0
  123. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/README.md +0 -0
  124. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/__init__.py +0 -0
  125. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/application/__init__.py +0 -0
  126. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/application/materialize.py +0 -0
  127. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/__init__.py +0 -0
  128. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/agents.py +0 -0
  129. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/generation.py +0 -0
  130. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/layout.py +0 -0
  131. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/ports.py +0 -0
  132. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/session_id.py +0 -0
  133. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/sessions.py +0 -0
  134. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/source_layout.py +0 -0
  135. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/watermark.py +0 -0
  136. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/__init__.py +0 -0
  137. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/atomic.py +0 -0
  138. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/compress_artifacts.py +0 -0
  139. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/fake_converter.py +0 -0
  140. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/scanner.py +0 -0
  141. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/settings.py +0 -0
  142. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/source_archive.py +0 -0
  143. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/py.typed +0 -0
  144. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/LICENSE +0 -0
  145. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/README.md +0 -0
  146. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/__init__.py +0 -0
  147. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/__init__.py +0 -0
  148. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/artifacts.py +0 -0
  149. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/authorship.py +0 -0
  150. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/catalog.py +0 -0
  151. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/columnar.py +0 -0
  152. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/embedding_guard.py +0 -0
  153. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/examples.py +0 -0
  154. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/lake.py +0 -0
  155. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/lake_sessions.py +0 -0
  156. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/raw_readers.py +0 -0
  157. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/session_id.py +0 -0
  158. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/sql_literal.py +0 -0
  159. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/__init__.py +0 -0
  160. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/analytics.py +0 -0
  161. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/authorship.py +0 -0
  162. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/columnar.py +0 -0
  163. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/lake.py +0 -0
  164. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/lake_sessions.py +0 -0
  165. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/lake_settings.py +0 -0
  166. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/lake_steps.py +0 -0
  167. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/projections.py +0 -0
  168. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/stored_artifacts.py +0 -0
  169. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/py.typed +0 -0
  170. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/LICENSE +0 -0
  171. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/application/__init__.py +0 -0
  172. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/__init__.py +0 -0
  173. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/discovery.py +0 -0
  174. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/embedding_guard.py +0 -0
  175. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/text_stamp.py +0 -0
  176. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/__init__.py +0 -0
  177. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/corpus_text_rows.py +0 -0
  178. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/gemma_local.py +0 -0
  179. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/lake_text_rows.py +0 -0
  180. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/providers.py +0 -0
  181. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/py.typed +0 -0
  182. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-models/LICENSE +0 -0
  183. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-models/README.md +0 -0
  184. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/__init__.py +0 -0
  185. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/domain/__init__.py +0 -0
  186. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/domain/ports.py +0 -0
  187. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/domain/registry.py +0 -0
  188. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/domain/schema.py +0 -0
  189. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/infrastructure/__init__.py +0 -0
  190. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/infrastructure/anthropic_bedrock.py +0 -0
  191. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/infrastructure/bedrock_common.py +0 -0
  192. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/infrastructure/openai_bedrock.py +0 -0
  193. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/infrastructure/providers.py +0 -0
  194. {atif_sql-0.5.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/infrastructure/settings.py +0 -0
  195. {atif_sql-0.5.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.5.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
@@ -23,7 +23,6 @@ Requires-Dist: boto3>=1.42.91
23
23
  Requires-Dist: cyclopts>=5.1.1
24
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.5.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.5.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.5.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,11 +29,11 @@ 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.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",
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
@@ -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
@@ -1151,6 +1151,55 @@ def _lake_surface(corpus_root: Path) -> dict[str, Any]:
1151
1151
  }
1152
1152
 
1153
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
+
1154
1203
  def _dir_bytes(root: Path) -> int:
1155
1204
  """Total bytes of every file under ``root`` (0 if absent)."""
1156
1205
  if not root.is_dir():
@@ -1165,6 +1214,7 @@ def status(
1165
1214
  source_root: Path | None = None,
1166
1215
  corpus_root: Path | None = None,
1167
1216
  quiesce_seconds: int | None = None,
1217
+ vector_probe: Annotated[bool, cyclopts.Parameter(name="--vector-probe")] = False,
1168
1218
  fmt: Annotated[OutputFormat, cyclopts.Parameter(name="--format")] = OutputFormat.AUTO,
1169
1219
  ) -> None:
1170
1220
  """Report corpus freshness: watermark age, counts, bytes, staleness.
@@ -1204,7 +1254,26 @@ def status(
1204
1254
  not installed; ``query`` binds ``message_embeddings`` empty rather than
1205
1255
  downloading it, so run ``atif-sql embed --install-extension``). Read
1206
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).
1207
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
+
1208
1277
  import time
1209
1278
 
1210
1279
  from atif_cli.corpus import storage_layout
@@ -1805,8 +1874,9 @@ def _install_lance_extension(fmt: OutputFormat, *, quiet: bool = False) -> None:
1805
1874
  from loguru import logger
1806
1875
 
1807
1876
  logger.warning(
1808
- "Could not install the lance DuckDB extension ({}); vector search stays "
1809
- "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",
1810
1880
  str(exc).splitlines()[0],
1811
1881
  )
1812
1882
  return
@@ -1831,7 +1901,7 @@ def embed(
1831
1901
  corpus_root: Path | None = None,
1832
1902
  fmt: Annotated[OutputFormat, cyclopts.Parameter(name="--format")] = OutputFormat.AUTO,
1833
1903
  ) -> None:
1834
- """Embed unembedded corpus steps and append them to LanceDB.
1904
+ """Embed unembedded corpus steps and append them to the Lance store.
1835
1905
 
1836
1906
  Provider
1837
1907
  --------
@@ -1869,14 +1939,17 @@ def embed(
1869
1939
 
1870
1940
  Extension
1871
1941
  ---------
1872
- ``query`` and ``search`` read the store through DuckDB's lance extension
1873
- and never install it themselves (installing is a 242 MB download from
1874
- the extension repository, and ``query`` runs unattended). It is
1875
- installed here, where the network is already a deliberate act: every
1876
- REAL run installs it first, and ``--install-extension`` installs it and
1877
- exits without touching Bedrock or the store (``{"extension": "lance",
1878
- "installed": true, "install_path": ...}``). ``atif-sql status`` reports
1879
- 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.
1880
1953
 
1881
1954
  Cost
1882
1955
  ----
@@ -1991,6 +2064,7 @@ def _prune_orphans(corpus_root: Path | None, *, dry_run: bool, fmt: OutputFormat
1991
2064
  from atif_corpus.application.materialize import read_sink_pending
1992
2065
  from atif_corpus.domain.layout import CorpusLayout
1993
2066
  from atif_embed.application.prune import prune_orphans
2067
+ from atif_embed.domain.errors import DomainError
1994
2068
  from atif_embed.infrastructure.lance_store import LanceVectorStore
1995
2069
 
1996
2070
  settings = _corpus_settings(None, corpus_root)
@@ -2009,12 +2083,21 @@ def _prune_orphans(corpus_root: Path | None, *, dry_run: bool, fmt: OutputFormat
2009
2083
  fmt,
2010
2084
  )
2011
2085
  raise SystemExit(EXIT_CODES["lake_unavailable"])
2012
- report = prune_orphans(
2013
- settings.corpus_root,
2014
- lake=lake_steps_port(),
2015
- store=LanceVectorStore(lance_uri, dim=embed_settings.embedding_dim),
2016
- dry_run=dry_run,
2017
- )
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
2018
2101
  if report is None:
2019
2102
  emit_error(
2020
2103
  ClassifiedError(
@@ -2094,7 +2177,8 @@ def search(
2094
2177
  extension_missing (a store exists but the lance DuckDB extension is not
2095
2178
  installed; run ``atif-sql embed --install-extension``), lake_unavailable
2096
2179
  (``--all-corpora`` without a usable lake) or terminal_state (the gemma
2097
- 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).
2098
2182
  """
2099
2183
  _refuse_root("search", fmt)
2100
2184
 
@@ -2105,7 +2189,11 @@ def search(
2105
2189
  classify_duckdb_error,
2106
2190
  classify_registration_error,
2107
2191
  )
2108
- 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
+ )
2109
2197
  from atif_embed.application.embed import embed_query
2110
2198
  from atif_embed.domain.errors import DomainError
2111
2199
 
@@ -2166,6 +2254,23 @@ def search(
2166
2254
 
2167
2255
  row = con.execute("SELECT count(*) FROM message_embeddings").fetchone()
2168
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"])
2169
2274
  emit_error(
2170
2275
  ClassifiedError(
2171
2276
  kind="no_embeddings",
@@ -2199,12 +2304,14 @@ def search(
2199
2304
 
2200
2305
 
2201
2306
  #: ``search``'s kNN, one text per shape. Rank by cosine similarity
2202
- #: descending: ORDER BY array_cosine_distance (== 1 - sim) ASC is what
2203
- #: triggers the cosine HNSW index lookup. Using array_distance here (L2) would
2204
- #: silently bypass the index AND give wrong ranks: the raw int8-cast-to-float
2205
- #: document vectors have magnitudes in the thousands while the query vector is
2206
- #: unit-normalized — only cosine is magnitude-invariant. ``{dim}`` is the only
2207
- #: 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))``.
2208
2315
  _SEARCH_SQL = """
2209
2316
  WITH qv AS (SELECT CAST(? AS FLOAT[{dim}]) AS v)
2210
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.5.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]