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