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.
- {atif_sql-0.4.0 → atif_sql-0.6.0}/PKG-INFO +4 -5
- {atif_sql-0.4.0 → atif_sql-0.6.0}/README.md +2 -2
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/pyproject.toml +3 -3
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/README.md +4 -1
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/pyproject.toml +8 -8
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/app.py +161 -32
- atif_sql-0.6.0/packages/atif-cli/src/atif_cli/vector_probe.py +137 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/README.md +1 -1
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/pyproject.toml +2 -2
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/model_prices.json +1895 -167
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/pricing.py +77 -38
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/schema_version.py +11 -1
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/pyproject.toml +2 -2
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/slug.py +1 -1
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/pyproject.toml +3 -3
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/examples.py +2 -2
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/projections.py +1 -1
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/registry.py +127 -37
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/stored_artifacts.py +40 -1
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/README.md +40 -10
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/pyproject.toml +4 -5
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/__init__.py +4 -3
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/application/embed.py +17 -11
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/application/prune.py +1 -1
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/errors.py +38 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/ports.py +1 -5
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/sql_literal.py +12 -9
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/cohere_bedrock.py +4 -4
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/corpus_text_rows.py +46 -15
- atif_sql-0.6.0/packages/atif-embed/src/atif_embed/infrastructure/lance_extension.py +74 -0
- atif_sql-0.6.0/packages/atif-embed/src/atif_embed/infrastructure/lance_store.py +706 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/settings.py +4 -4
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/pyproject.toml +2 -2
- {atif_sql-0.4.0 → atif_sql-0.6.0}/pyproject.toml +21 -9
- atif_sql-0.4.0/packages/atif-embed/src/atif_embed/infrastructure/lance_store.py +0 -440
- {atif_sql-0.4.0 → atif_sql-0.6.0}/.gitignore +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/.vale/styles/Google/LICENSE +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/.vale/styles/proselint/LICENSE +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/.vale/styles/proselint/README.md +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/LICENSE +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/docs/README.md +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/LICENSE +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/README.md +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/analyze.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/prompts.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/_shared.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/classify.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/conflicts.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/friction.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/application/use_cases/perceived.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/authorship.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/config.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/costs.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/friction.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/layout.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/models.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/ports.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/domain/transcript.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/corpus_reader.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/parquet_cache.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/settings.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/sqlite_state/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/sqlite_state/checkpointer.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/infrastructure/sqlite_state/retry_queue.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-analytics/src/atif_analytics/py.typed +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/LICENSE +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/__main__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/converter_adapter.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/corpus.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/cron.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/duck_errors.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/embed_lake.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/errors.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/lake.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/lake_sessions.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/output.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-cli/src/atif_cli/py.typed +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/LICENSE +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/application/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/application/convert_and_audit.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/application/convert_codex.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/agents.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/agent.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/content.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/final_metrics.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/metrics.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/observation.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/observation_result.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/step.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/subagent_trajectory_ref.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/tool_call.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/trajectory.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/atif/trajectory_validator.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/blobs.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/claude_code_conversion.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/codex_conversion.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/codex_edges.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/codex_enrichment.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/codex_fidelity.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/codex_nested_calls.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/edges.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/enrichment.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/errors.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/fidelity.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/result_signals.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/domain/session_events.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/census.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/claude_code_converter.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/codex_adapter.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/codex_census.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/codex_converter.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/harbor_adapter.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/raw_records.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/infrastructure/source_archive.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-converter/src/atif_converter/py.typed +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/LICENSE +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/README.md +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/application/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/application/materialize.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/agents.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/generation.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/layout.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/ports.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/session_id.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/sessions.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/source_layout.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/domain/watermark.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/atomic.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/compress_artifacts.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/fake_converter.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/scanner.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/settings.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/infrastructure/source_archive.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-corpus/src/atif_corpus/py.typed +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/LICENSE +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/README.md +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/artifacts.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/authorship.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/catalog.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/columnar.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/embedding_guard.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/lake.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/lake_sessions.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/raw_readers.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/session_id.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/domain/sql_literal.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/analytics.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/authorship.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/columnar.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/lake.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/lake_sessions.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/lake_settings.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/infrastructure/lake_steps.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-duck/src/atif_duck/py.typed +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/LICENSE +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/application/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/discovery.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/embedding_guard.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/domain/text_stamp.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/gemma_local.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/lake_text_rows.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/infrastructure/providers.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-embed/src/atif_embed/py.typed +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/LICENSE +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/README.md +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/domain/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/domain/ports.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/domain/registry.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/domain/schema.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/infrastructure/__init__.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/infrastructure/anthropic_bedrock.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/infrastructure/bedrock_common.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/infrastructure/openai_bedrock.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/infrastructure/providers.py +0 -0
- {atif_sql-0.4.0 → atif_sql-0.6.0}/packages/atif-models/src/atif_models/infrastructure/settings.py +0 -0
- {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.
|
|
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
|
|
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`,
|
|
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 +
|
|
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`,
|
|
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 +
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
33
|
-
"atif-converter==0.
|
|
34
|
-
"atif-corpus==0.
|
|
35
|
-
"atif-duck==0.
|
|
36
|
-
"atif-embed==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>=
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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).
|
|
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 ({});
|
|
1787
|
-
"
|
|
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
|
|
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
|
-
|
|
1851
|
-
and never install it themselves (installing is a
|
|
1852
|
-
the extension repository, and ``query`` runs
|
|
1853
|
-
installed here, where the network is already a
|
|
1854
|
-
REAL run installs it first, and
|
|
1855
|
-
exits without touching Bedrock
|
|
1856
|
-
"
|
|
1857
|
-
|
|
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
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
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
|
|
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
|
|
2181
|
-
#:
|
|
2182
|
-
#:
|
|
2183
|
-
#:
|
|
2184
|
-
#:
|
|
2185
|
-
#:
|
|
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.
|
|
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.
|
|
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.
|
|
40
|
+
requires = ["uv_build>=0.11.14,<0.14"]
|
|
41
41
|
build-backend = "uv_build"
|
|
42
42
|
|
|
43
43
|
[tool.uv.build-backend]
|