vecshift 0.1.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. {vecshift-0.1.0 → vecshift-0.3.0}/.github/workflows/ci.yml +11 -11
  2. {vecshift-0.1.0 → vecshift-0.3.0}/.github/workflows/release.yml +8 -8
  3. {vecshift-0.1.0 → vecshift-0.3.0}/CHANGELOG.md +29 -2
  4. {vecshift-0.1.0 → vecshift-0.3.0}/PKG-INFO +20 -4
  5. {vecshift-0.1.0 → vecshift-0.3.0}/README.md +17 -3
  6. {vecshift-0.1.0 → vecshift-0.3.0}/demo/run.sh +3 -0
  7. {vecshift-0.1.0 → vecshift-0.3.0}/docs/demo.md +1 -0
  8. {vecshift-0.1.0 → vecshift-0.3.0}/docs/migrations.md +41 -1
  9. {vecshift-0.1.0 → vecshift-0.3.0}/docs/security.md +9 -1
  10. {vecshift-0.1.0 → vecshift-0.3.0}/pyproject.toml +2 -0
  11. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/__init__.py +1 -1
  12. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/cli.py +79 -3
  13. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/cli_apply.py +158 -2
  14. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/cli_bench.py +65 -10
  15. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/cli_cutover.py +47 -26
  16. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/cli_eval.py +166 -3
  17. vecshift-0.3.0/src/vecshift/cli_init.py +416 -0
  18. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/cli_plan.py +86 -61
  19. vecshift-0.3.0/src/vecshift/cli_status.py +365 -0
  20. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/cli_style.py +5 -0
  21. vecshift-0.3.0/src/vecshift/envfile.py +118 -0
  22. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/jobs/spec.py +39 -6
  23. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/planning/planner.py +1 -1
  24. vecshift-0.3.0/src/vecshift/ui.py +432 -0
  25. vecshift-0.3.0/tests/integration/test_pgvector_init_status.py +158 -0
  26. vecshift-0.3.0/tests/test_cli.py +91 -0
  27. vecshift-0.3.0/tests/test_envfile.py +82 -0
  28. {vecshift-0.1.0 → vecshift-0.3.0}/tests/test_security.py +39 -0
  29. vecshift-0.3.0/tests/test_ui.py +139 -0
  30. vecshift-0.1.0/tests/test_cli.py +0 -36
  31. {vecshift-0.1.0 → vecshift-0.3.0}/.gitignore +0 -0
  32. {vecshift-0.1.0 → vecshift-0.3.0}/Dockerfile +0 -0
  33. {vecshift-0.1.0 → vecshift-0.3.0}/LICENSE +0 -0
  34. {vecshift-0.1.0 → vecshift-0.3.0}/NOTICE +0 -0
  35. {vecshift-0.1.0 → vecshift-0.3.0}/RELEASING.md +0 -0
  36. {vecshift-0.1.0 → vecshift-0.3.0}/SECURITY.md +0 -0
  37. {vecshift-0.1.0 → vecshift-0.3.0}/demo/compose.yaml +0 -0
  38. {vecshift-0.1.0 → vecshift-0.3.0}/demo/out/.gitignore +0 -0
  39. {vecshift-0.1.0 → vecshift-0.3.0}/demo/seed.py +0 -0
  40. {vecshift-0.1.0 → vecshift-0.3.0}/docs/architecture.md +0 -0
  41. {vecshift-0.1.0 → vecshift-0.3.0}/docs/bench.md +0 -0
  42. {vecshift-0.1.0 → vecshift-0.3.0}/docs/connectors/pgvector.md +0 -0
  43. {vecshift-0.1.0 → vecshift-0.3.0}/docs/eval.md +0 -0
  44. {vecshift-0.1.0 → vecshift-0.3.0}/docs/images/doctor-report.png +0 -0
  45. {vecshift-0.1.0 → vecshift-0.3.0}/docs/images/eval-report.png +0 -0
  46. {vecshift-0.1.0 → vecshift-0.3.0}/docs/images/logo/vecshift-dark.svg +0 -0
  47. {vecshift-0.1.0 → vecshift-0.3.0}/docs/images/logo/vecshift-light.svg +0 -0
  48. {vecshift-0.1.0 → vecshift-0.3.0}/docs/images/logo/vecshift-mark-dark.svg +0 -0
  49. {vecshift-0.1.0 → vecshift-0.3.0}/docs/images/logo/vecshift-mark-light.svg +0 -0
  50. {vecshift-0.1.0 → vecshift-0.3.0}/docs/images/logo/vecshift-mark.svg +0 -0
  51. {vecshift-0.1.0 → vecshift-0.3.0}/docs/images/social-preview.png +0 -0
  52. {vecshift-0.1.0 → vecshift-0.3.0}/docs/prior-art.md +0 -0
  53. {vecshift-0.1.0 → vecshift-0.3.0}/docs/roadmap.md +0 -0
  54. {vecshift-0.1.0 → vecshift-0.3.0}/scripts/release_notes.py +0 -0
  55. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/assets/bench.css +0 -0
  56. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/assets/eval.css +0 -0
  57. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/assets/report.css +0 -0
  58. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/assets/report.js +0 -0
  59. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/bench/__init__.py +0 -0
  60. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/bench/corpus.py +0 -0
  61. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/bench/generate.py +0 -0
  62. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/bench/html.py +0 -0
  63. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/bench/metrics.py +0 -0
  64. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/bench/runner.py +0 -0
  65. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/connectors/__init__.py +0 -0
  66. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/connectors/pgvector/__init__.py +0 -0
  67. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/connectors/pgvector/connection.py +0 -0
  68. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/connectors/pgvector/documents.py +0 -0
  69. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/connectors/pgvector/inspect.py +0 -0
  70. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/connectors/pgvector/search.py +0 -0
  71. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/connectors/pgvector/switch.py +0 -0
  72. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/connectors/pgvector/target.py +0 -0
  73. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/connectors/pgvector/writer.py +0 -0
  74. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/core/__init__.py +0 -0
  75. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/core/capabilities.py +0 -0
  76. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/core/contracts.py +0 -0
  77. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/core/fingerprint.py +0 -0
  78. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/core/record.py +0 -0
  79. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/doctor/__init__.py +0 -0
  80. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/doctor/checks.py +0 -0
  81. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/doctor/findings.py +0 -0
  82. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/doctor/html.py +0 -0
  83. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/doctor/profile.py +0 -0
  84. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/embeddings/__init__.py +0 -0
  85. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/embeddings/cache.py +0 -0
  86. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/embeddings/providers.py +0 -0
  87. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/embeddings/spec.py +0 -0
  88. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/eval/__init__.py +0 -0
  89. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/eval/html.py +0 -0
  90. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/eval/metrics.py +0 -0
  91. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/eval/queries.py +0 -0
  92. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/eval/runner.py +0 -0
  93. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/html_kit.py +0 -0
  94. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/jobs/__init__.py +0 -0
  95. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/migrate/__init__.py +0 -0
  96. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/migrate/engine.py +0 -0
  97. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/migrate/state.py +0 -0
  98. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/planning/__init__.py +0 -0
  99. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/planning/plan.py +0 -0
  100. {vecshift-0.1.0 → vecshift-0.3.0}/src/vecshift/py.typed +0 -0
  101. {vecshift-0.1.0 → vecshift-0.3.0}/tests/__init__.py +0 -0
  102. {vecshift-0.1.0 → vecshift-0.3.0}/tests/integration/__init__.py +0 -0
  103. {vecshift-0.1.0 → vecshift-0.3.0}/tests/integration/conftest.py +0 -0
  104. {vecshift-0.1.0 → vecshift-0.3.0}/tests/integration/test_pgvector_apply.py +0 -0
  105. {vecshift-0.1.0 → vecshift-0.3.0}/tests/integration/test_pgvector_bench.py +0 -0
  106. {vecshift-0.1.0 → vecshift-0.3.0}/tests/integration/test_pgvector_cutover.py +0 -0
  107. {vecshift-0.1.0 → vecshift-0.3.0}/tests/integration/test_pgvector_doctor.py +0 -0
  108. {vecshift-0.1.0 → vecshift-0.3.0}/tests/integration/test_pgvector_eval.py +0 -0
  109. {vecshift-0.1.0 → vecshift-0.3.0}/tests/integration/test_pgvector_plan.py +0 -0
  110. {vecshift-0.1.0 → vecshift-0.3.0}/tests/test_bench.py +0 -0
  111. {vecshift-0.1.0 → vecshift-0.3.0}/tests/test_contracts.py +0 -0
  112. {vecshift-0.1.0 → vecshift-0.3.0}/tests/test_doctor_checks.py +0 -0
  113. {vecshift-0.1.0 → vecshift-0.3.0}/tests/test_doctor_html.py +0 -0
  114. {vecshift-0.1.0 → vecshift-0.3.0}/tests/test_embeddings.py +0 -0
  115. {vecshift-0.1.0 → vecshift-0.3.0}/tests/test_eval.py +0 -0
  116. {vecshift-0.1.0 → vecshift-0.3.0}/tests/test_eval_html.py +0 -0
  117. {vecshift-0.1.0 → vecshift-0.3.0}/tests/test_fingerprint.py +0 -0
  118. {vecshift-0.1.0 → vecshift-0.3.0}/tests/test_jobs.py +0 -0
  119. {vecshift-0.1.0 → vecshift-0.3.0}/tests/test_migrate.py +0 -0
  120. {vecshift-0.1.0 → vecshift-0.3.0}/tests/test_pgvector_connection.py +0 -0
  121. {vecshift-0.1.0 → vecshift-0.3.0}/tests/test_planner.py +0 -0
  122. {vecshift-0.1.0 → vecshift-0.3.0}/tests/test_record.py +0 -0
  123. {vecshift-0.1.0 → vecshift-0.3.0}/tests/test_release.py +0 -0
@@ -25,10 +25,10 @@ jobs:
25
25
  runs-on: ubuntu-latest
26
26
  timeout-minutes: 10
27
27
  steps:
28
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
28
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
29
29
  with:
30
30
  persist-credentials: false
31
- - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
31
+ - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
32
32
  - run: uv sync --locked
33
33
  - run: uv run ruff check .
34
34
  - run: uv run ruff format --check .
@@ -45,10 +45,10 @@ jobs:
45
45
  env:
46
46
  UV_PYTHON: ${{ matrix.python-version }}
47
47
  steps:
48
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
48
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
49
49
  with:
50
50
  persist-credentials: false
51
- - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
51
+ - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
52
52
  - run: uv sync --locked
53
53
  - run: uv run pytest --cov=vecshift --cov-report=term-missing
54
54
 
@@ -78,10 +78,10 @@ jobs:
78
78
  env:
79
79
  VECSHIFT_TEST_PG_DSN: postgresql://postgres:postgres@localhost:5432/postgres
80
80
  steps:
81
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
81
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
82
82
  with:
83
83
  persist-credentials: false
84
- - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
84
+ - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
85
85
  - run: uv sync --locked
86
86
  - run: uv run pytest tests/integration
87
87
 
@@ -90,10 +90,10 @@ jobs:
90
90
  runs-on: ubuntu-latest
91
91
  timeout-minutes: 10
92
92
  steps:
93
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
93
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
94
94
  with:
95
95
  persist-credentials: false
96
- - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
96
+ - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
97
97
  - name: Known vulnerabilities in locked dependencies
98
98
  run: |
99
99
  uv export --frozen --all-extras --all-groups --no-hashes --no-emit-project -o requirements-audit.txt
@@ -106,10 +106,10 @@ jobs:
106
106
  runs-on: ubuntu-latest
107
107
  timeout-minutes: 10
108
108
  steps:
109
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
109
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
110
110
  with:
111
111
  persist-credentials: false
112
- - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
112
+ - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
113
113
  - run: uv build
114
114
  - name: PyPI metadata and README render
115
115
  run: uvx twine==7.0.0 check --strict dist/*
@@ -129,7 +129,7 @@ jobs:
129
129
  PYTHON_IMAGE: mirror.gcr.io/library/python:3.13-slim@sha256:70729b46c69b4f1e97c4822c1af3df53a1476cf5ddc6c087c0c10bc3a5678c2f
130
130
  PGVECTOR_IMAGE: mirror.gcr.io/pgvector/pgvector:pg17@sha256:ac08538c6f8b9904c33c8224c5e5706dbe760aca29db1d096972b4052c22a75d
131
131
  steps:
132
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
132
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
133
133
  with:
134
134
  persist-credentials: false
135
135
  - name: Run the offline demo end to end
@@ -23,10 +23,10 @@ jobs:
23
23
  permissions:
24
24
  contents: read
25
25
  steps:
26
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
26
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
27
27
  with:
28
28
  persist-credentials: false
29
- - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
29
+ - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
30
30
  with:
31
31
  # A release never restores a cache that another workflow could have written.
32
32
  enable-cache: false
@@ -47,7 +47,7 @@ jobs:
47
47
  - name: CHANGELOG.md has notes for this version
48
48
  run: python scripts/release_notes.py "$GITHUB_REF_NAME" > /dev/null
49
49
  - run: uvx twine==7.0.0 check --strict dist/*
50
- - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
50
+ - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
51
51
  with:
52
52
  name: dist
53
53
  path: dist/
@@ -66,7 +66,7 @@ jobs:
66
66
  permissions:
67
67
  id-token: write # trusted publishing to PyPI
68
68
  steps:
69
- - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
69
+ - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
70
70
  with:
71
71
  name: dist
72
72
  path: dist/
@@ -83,10 +83,10 @@ jobs:
83
83
  id-token: write # sign the provenance attestation
84
84
  attestations: write # store it
85
85
  steps:
86
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
86
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
87
87
  with:
88
88
  persist-credentials: false
89
- - uses: docker/setup-qemu-action@1f40c72289eff860ee54a304f1438e3cff362e0a # v4.3.0
89
+ - uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0
90
90
  - uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
91
91
  - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
92
92
  with:
@@ -127,10 +127,10 @@ jobs:
127
127
  permissions:
128
128
  contents: write # create the release
129
129
  steps:
130
- - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
130
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
131
131
  with:
132
132
  persist-credentials: false
133
- - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
133
+ - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
134
134
  with:
135
135
  name: dist
136
136
  path: dist/
@@ -7,7 +7,32 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ## [0.1.0]
10
+ ## [0.3.0] - 2026-10-10
11
+
12
+ ### Changed
13
+
14
+ - A new look at the terminal, across every command: a gradient block wordmark, numbered
15
+ steps and arrow-key menus in `vecshift init` (with hidden input for secrets), a panel
16
+ with the migration's progress in `vecshift status`, a live progress bar in `apply`,
17
+ highlighted SQL in `plan`, tables in `eval` and `bench`, and consistent headers,
18
+ findings, verdicts, and next steps everywhere. Off a terminal, output stays plain and
19
+ prompts fall back to typed answers, as before.
20
+ - New dependency: `questionary`, for the arrow-key menus.
21
+
22
+ ## [0.2.0] - 2026-10-10
23
+
24
+ ### Added
25
+
26
+ - `vecshift init` guides you at a terminal: it asks for the connection string (hidden),
27
+ lists the vector columns it finds, picks the text column, and offers a short list of
28
+ models and a spending limit, then shows the plan. Flags still work without prompts.
29
+ - Commands read settings such as `VECSHIFT_DSN` and `OPENAI_API_KEY` from a `.env` file in
30
+ the current folder. Variables already set take precedence; `--env-file` and
31
+ `--no-env-file` choose another file or none. `init` can save to it, privately.
32
+ - `vecshift status` shows where a migration stands and what to run next (`--json` too).
33
+ - Running `vecshift` alone, and `vecshift init`, show the logo at a colour terminal.
34
+
35
+ ## [0.1.0] - 2026-10-10
11
36
 
12
37
  The first release.
13
38
 
@@ -136,5 +161,7 @@ The first release.
136
161
  - `Capability` flags and plugin contracts for sources, targets, and embedding providers.
137
162
  - `vecshift fingerprint` and `vecshift --version` commands.
138
163
 
139
- [Unreleased]: https://github.com/Osamamu64/vecshift/compare/v0.1.0...HEAD
164
+ [Unreleased]: https://github.com/Osamamu64/vecshift/compare/v0.3.0...HEAD
165
+ [0.3.0]: https://github.com/Osamamu64/vecshift/compare/v0.2.0...v0.3.0
166
+ [0.2.0]: https://github.com/Osamamu64/vecshift/compare/v0.1.0...v0.2.0
140
167
  [0.1.0]: https://github.com/Osamamu64/vecshift/releases/tag/v0.1.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: vecshift
3
- Version: 0.1.0
3
+ Version: 0.3.0
4
4
  Summary: Zero-downtime embedding model migrations for pgvector and Supabase: plan, re-embed, evaluate, and cut over safely.
5
5
  Project-URL: Homepage, https://github.com/Osamamu64/vecshift
6
6
  Project-URL: Repository, https://github.com/Osamamu64/vecshift
@@ -31,6 +31,8 @@ Requires-Dist: numpy>=1.26
31
31
  Requires-Dist: psycopg[binary]>=3.2
32
32
  Requires-Dist: pydantic>=2.6
33
33
  Requires-Dist: pyyaml>=6
34
+ Requires-Dist: questionary>=2.0
35
+ Requires-Dist: rich>=13
34
36
  Requires-Dist: typer>=0.12
35
37
  Description-Content-Type: text/markdown
36
38
 
@@ -44,6 +46,9 @@ Description-Content-Type: text/markdown
44
46
  **Safe, observable embedding migrations for any vector store, with any embedding model.**
45
47
 
46
48
  [![CI](https://github.com/Osamamu64/vecshift/actions/workflows/ci.yml/badge.svg)](https://github.com/Osamamu64/vecshift/actions/workflows/ci.yml)
49
+ [![PyPI](https://img.shields.io/pypi/v/vecshift)](https://pypi.org/project/vecshift/)
50
+ [![Python](https://img.shields.io/pypi/pyversions/vecshift)](https://pypi.org/project/vecshift/)
51
+ [![Docker image](https://img.shields.io/badge/docker-ghcr.io%2Fosamamu64%2Fvecshift-2496ED?logo=docker&logoColor=white)](https://github.com/Osamamu64/vecshift/pkgs/container/vecshift)
47
52
  [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://github.com/Osamamu64/vecshift/blob/main/LICENSE)
48
53
  ![Status: alpha](https://img.shields.io/badge/status-alpha-orange)
49
54
 
@@ -181,14 +186,24 @@ the [benchmarking guide](https://github.com/Osamamu64/vecshift/blob/main/docs/be
181
186
 
182
187
  ### Plan a migration
183
188
 
189
+ ```bash
190
+ vecshift init
191
+ ```
192
+
193
+ At a terminal, `init` walks you through it: paste your connection string (hidden as you
194
+ type), pick the vector column and the text it came from, choose the new model, and set a
195
+ spending limit. It writes a commented `vecshift.yaml`, can save the connection string to
196
+ a private `.env` file if you want it to, and shows the plan. For scripts, pass everything
197
+ as flags:
198
+
184
199
  ```bash
185
200
  vecshift init --table public.documents --model openai/text-embedding-3-large,dims=1024
186
201
  vecshift plan
187
202
  ```
188
203
 
189
- `init` writes a commented `vecshift.yaml`. `plan` checks it against the database without
190
- changing anything: the SQL it would run, rows, tokens, cost, duration, and storage, plus
191
- anything that would make the migration fail.
204
+ `plan` checks the job against the database without changing anything: the SQL it would
205
+ run, rows, tokens, cost, duration, and storage, plus anything that would make the
206
+ migration fail.
192
207
 
193
208
  ### Run it
194
209
 
@@ -201,6 +216,7 @@ concurrently, while your application keeps reading and writing. It stops cleanly
201
216
  Ctrl-C, at your budget, or part way with `--until 50`, and running it again resumes.
202
217
 
203
218
  ```bash
219
+ vecshift status # where it stands, and what to run next
204
220
  vecshift eval # better on your data, and how fast? GO / NO-GO
205
221
  vecshift cutover --check # safe to switch?
206
222
  vecshift cutover # searches use the new vectors, under the same column name
@@ -8,6 +8,9 @@
8
8
  **Safe, observable embedding migrations for any vector store, with any embedding model.**
9
9
 
10
10
  [![CI](https://github.com/Osamamu64/vecshift/actions/workflows/ci.yml/badge.svg)](https://github.com/Osamamu64/vecshift/actions/workflows/ci.yml)
11
+ [![PyPI](https://img.shields.io/pypi/v/vecshift)](https://pypi.org/project/vecshift/)
12
+ [![Python](https://img.shields.io/pypi/pyversions/vecshift)](https://pypi.org/project/vecshift/)
13
+ [![Docker image](https://img.shields.io/badge/docker-ghcr.io%2Fosamamu64%2Fvecshift-2496ED?logo=docker&logoColor=white)](https://github.com/Osamamu64/vecshift/pkgs/container/vecshift)
11
14
  [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
12
15
  ![Status: alpha](https://img.shields.io/badge/status-alpha-orange)
13
16
 
@@ -145,14 +148,24 @@ the [benchmarking guide](docs/bench.md).
145
148
 
146
149
  ### Plan a migration
147
150
 
151
+ ```bash
152
+ vecshift init
153
+ ```
154
+
155
+ At a terminal, `init` walks you through it: paste your connection string (hidden as you
156
+ type), pick the vector column and the text it came from, choose the new model, and set a
157
+ spending limit. It writes a commented `vecshift.yaml`, can save the connection string to
158
+ a private `.env` file if you want it to, and shows the plan. For scripts, pass everything
159
+ as flags:
160
+
148
161
  ```bash
149
162
  vecshift init --table public.documents --model openai/text-embedding-3-large,dims=1024
150
163
  vecshift plan
151
164
  ```
152
165
 
153
- `init` writes a commented `vecshift.yaml`. `plan` checks it against the database without
154
- changing anything: the SQL it would run, rows, tokens, cost, duration, and storage, plus
155
- anything that would make the migration fail.
166
+ `plan` checks the job against the database without changing anything: the SQL it would
167
+ run, rows, tokens, cost, duration, and storage, plus anything that would make the
168
+ migration fail.
156
169
 
157
170
  ### Run it
158
171
 
@@ -165,6 +178,7 @@ concurrently, while your application keeps reading and writing. It stops cleanly
165
178
  Ctrl-C, at your budget, or part way with `--until 50`, and running it again resumes.
166
179
 
167
180
  ```bash
181
+ vecshift status # where it stands, and what to run next
168
182
  vecshift eval # better on your data, and how fast? GO / NO-GO
169
183
  vecshift cutover --check # safe to switch?
170
184
  vecshift cutover # searches use the new vectors, under the same column name
@@ -55,6 +55,9 @@ fi
55
55
  step "6. Switch searches to the new vectors" "vecshift cutover --yes"
56
56
  vecshift cutover --yes
57
57
 
58
+ step "7. Check where it stands" "vecshift status"
59
+ vecshift status
60
+
58
61
  printf '\nDone. Searches now use the new vectors, under the same column name.\n'
59
62
  printf 'Reports: demo/out/doctor.html and demo/out/eval.html\n'
60
63
  printf 'To switch back: docker compose run --rm --entrypoint vecshift vecshift rollback --yes\n'
@@ -30,6 +30,7 @@ in the vecshift container:
30
30
  and writes `demo/out/eval.html`. If the verdict isn't GO, the demo stops here.
31
31
  7. **`vecshift cutover`** renames the columns in one transaction, so `embedding` now holds
32
32
  the new vectors.
33
+ 8. **`vecshift status`** confirms the stage and says what to do next.
33
34
 
34
35
  Afterwards you can switch back, or run any other command, against the same database:
35
36
 
@@ -36,7 +36,21 @@ by one model can't be compared with vectors from another.
36
36
 
37
37
  ## The job file
38
38
 
39
- `vecshift init` writes a commented `vecshift.yaml`. The full set of settings:
39
+ `vecshift init` writes a commented `vecshift.yaml`. Run it at a terminal without flags and
40
+ it asks for what it needs:
41
+
42
+ 1. The connection string, unless `VECSHIFT_DSN` or `DATABASE_URL` is already set. It's
43
+ hidden as you type, checked by connecting, and asked again if it doesn't work.
44
+ 2. The vector column, from the ones it finds, and the column holding the source text.
45
+ 3. The model that made the current vectors (optional, for `eval`), the new model from a
46
+ short list or any spec, and a spending limit for paid models.
47
+
48
+ It then offers to save the connection string, and any API key the model needs, to `.env`
49
+ (see below), and to run `plan`. Saving defaults to no: nothing is written to disk unless
50
+ you say yes. With `--table` and `--model`, `init` asks nothing, for scripts; and
51
+ without a terminal it requires them.
52
+
53
+ The full set of settings:
40
54
 
41
55
  ```yaml
42
56
  version: 1
@@ -68,6 +82,32 @@ The file never contains credentials: the connection string comes from the enviro
68
82
  variable that `dsn_env` names. Sections whose settings are all commented out use the
69
83
  defaults, and unknown settings are rejected so typos don't go unnoticed.
70
84
 
85
+ ### Settings in a `.env` file
86
+
87
+ Every command reads `.env` from the current folder, if there is one, so you don't have to
88
+ export variables in each shell:
89
+
90
+ ```bash
91
+ VECSHIFT_DSN='postgresql://postgres.<ref>:<password>@<region>.pooler.supabase.com:5432/postgres'
92
+ OPENAI_API_KEY='sk-...'
93
+ ```
94
+
95
+ Variables already set in the environment win over the file. Use `--env-file PATH` to read
96
+ another file, or `--no-env-file` to read none. Keep the file private (`chmod 600 .env`;
97
+ vecshift warns if others can read it) and out of version control. When `init` saves to
98
+ it, it creates the file readable by you only, and offers to add it to `.gitignore`.
99
+
100
+ ## Where a migration stands
101
+
102
+ ```bash
103
+ vecshift status # or --json, for scripts and UIs
104
+ ```
105
+
106
+ `status` is read-only. It shows the stage (not started, filling the new column, ready to
107
+ cut over, cut over, or done), how many rows have a new vector, the index and sync
108
+ trigger, what `apply` has spent, the last cutover or rollback, and the command to run
109
+ next.
110
+
71
111
  ## What `plan` checks
72
112
 
73
113
  `plan` connects read-only, samples the table, and reports what `apply` would do, what it
@@ -40,6 +40,11 @@ and how it protects credentials. To report a vulnerability, see
40
40
  - Credentials inside URLs (`https://user:pass@...`) are rejected, and URL query strings
41
41
  are hidden wherever a spec is displayed.
42
42
  - Error messages from providers are shortened and never include the request's key.
43
+ - **`.env` files** are an optional alternative to exporting variables. They're read from
44
+ the current folder (or `--env-file`), never override variables already set, and
45
+ vecshift warns when other users can read one. `vecshift init` types secrets hidden, and
46
+ saves them to `.env` only when you say yes: the file is created `0600`, and init offers
47
+ to add it to `.gitignore`. Errors in the file name the line, never its value.
43
48
 
44
49
  ## The database
45
50
 
@@ -79,6 +84,7 @@ and how it protects credentials. To report a vulnerability, see
79
84
  token and row counts, and the IDs of rows the provider rejected, with the provider's
80
85
  error. Never row text or credentials. The directory is `0700` and the file `0600`, and
81
86
  it is replaced atomically so a crash can't leave it half-written.
87
+ - **`.env`**: only when you choose to save to it in `vecshift init`, as above.
82
88
  - **Files you ask for**: reports and saved queries are written only where you point them.
83
89
  Saved generated queries contain the query text and document IDs.
84
90
 
@@ -97,7 +103,9 @@ markup, the browser would refuse to run it or send anything anywhere.
97
103
  - `zizmor` for GitHub Actions security
98
104
  - Ruff's flake8-bandit rules (`S`) for code patterns
99
105
  - GitHub Actions are pinned to full commit SHAs, workflows get read-only tokens, and
100
- checkout doesn't persist credentials. Dependabot keeps dependencies and pins up to date.
106
+ checkout doesn't persist credentials. Dependabot keeps dependencies and pins up to date,
107
+ one grouped pull request per ecosystem a month, and only proposes releases at least two
108
+ weeks old (security updates aren't delayed).
101
109
  - The Docker image is built from a base image pinned by digest, installs dependencies
102
110
  from the lock file with `--require-hashes`, and runs as an unprivileged user. The demo's
103
111
  database is reachable only from the demo's own containers.
@@ -32,6 +32,8 @@ dependencies = [
32
32
  "psycopg[binary]>=3.2",
33
33
  "pydantic>=2.6",
34
34
  "pyyaml>=6",
35
+ "questionary>=2.0",
36
+ "rich>=13",
35
37
  "typer>=0.12",
36
38
  ]
37
39
 
@@ -4,7 +4,7 @@ from vecshift.core.capabilities import Capability
4
4
  from vecshift.core.fingerprint import EmbeddingFingerprint
5
5
  from vecshift.core.record import Record, SparseVector
6
6
 
7
- __version__ = "0.1.0"
7
+ __version__ = "0.3.0"
8
8
 
9
9
  __all__ = [
10
10
  "Capability",
@@ -15,13 +15,16 @@ from vecshift.cli_style import warn_if_password_on_command_line
15
15
  from vecshift.cli_style import wrap as _wrap
16
16
  from vecshift.core.fingerprint import EmbeddingFingerprint
17
17
  from vecshift.doctor import Report, Severity, run_checks
18
+ from vecshift.envfile import DEFAULT as DEFAULT_ENV_FILE
19
+
20
+ TAGLINE = "Safe, observable embedding migrations for any vector store."
18
21
 
19
22
  app = typer.Typer(
20
23
  name="vecshift",
21
24
  # Tracebacks must never print local variables: they can hold connection strings and keys.
22
25
  pretty_exceptions_show_locals=False,
23
- help="Safe, observable embedding migrations for any vector store.",
24
- no_args_is_help=True,
26
+ help=TAGLINE,
27
+ invoke_without_command=True,
25
28
  add_completion=False,
26
29
  )
27
30
 
@@ -32,8 +35,27 @@ def _print_version(value: bool) -> None:
32
35
  raise typer.Exit()
33
36
 
34
37
 
38
+ def _load_env_file(path: Path | None) -> None:
39
+ from vecshift import envfile
40
+
41
+ if path is None:
42
+ return
43
+ try:
44
+ envfile.load(path)
45
+ except envfile.EnvFileError as exc:
46
+ typer.secho(f"Couldn't read {path}: {exc}", err=True, fg=typer.colors.RED)
47
+ raise typer.Exit(2) from exc
48
+ if envfile.readable_by_others(path):
49
+ typer.secho(
50
+ f"Warning: other users of this machine can read {path}. Run: chmod 600 {path}",
51
+ err=True,
52
+ fg=typer.colors.YELLOW,
53
+ )
54
+
55
+
35
56
  @app.callback()
36
57
  def main(
58
+ ctx: typer.Context,
37
59
  version: Annotated[
38
60
  bool,
39
61
  typer.Option(
@@ -43,8 +65,25 @@ def main(
43
65
  help="Show the version and exit.",
44
66
  ),
45
67
  ] = False,
68
+ env_file: Annotated[
69
+ Path,
70
+ typer.Option(
71
+ dir_okay=False,
72
+ help="Read settings such as VECSHIFT_DSN from this file, if it exists. Variables "
73
+ "already set in the environment take precedence.",
74
+ ),
75
+ ] = DEFAULT_ENV_FILE,
76
+ no_env_file: Annotated[
77
+ bool, typer.Option("--no-env-file", help="Don't read a .env file.")
78
+ ] = False,
46
79
  ) -> None:
47
80
  """Safe, observable embedding migrations for any vector store."""
81
+ _load_env_file(None if no_env_file else env_file)
82
+ if ctx.invoked_subcommand is None:
83
+ from vecshift import ui
84
+
85
+ ui.banner(__version__, TAGLINE)
86
+ typer.echo(ctx.get_help())
48
87
 
49
88
 
50
89
  @app.command()
@@ -82,7 +121,39 @@ class FailOn(StrEnum):
82
121
  ERROR = "error"
83
122
 
84
123
 
124
+ def _render_fancy(report: Report, connection: str) -> None:
125
+ from rich.text import Text
126
+
127
+ from vecshift import ui
128
+
129
+ dims = f"({report.declared_dimensions})" if report.declared_dimensions else ""
130
+ rows = f"~{report.estimated_rows:,}" if report.estimated_rows is not None else "unknown"
131
+ ui.title("doctor", f"{report.store} via {connection}")
132
+ ui.kv(
133
+ [
134
+ ("Target", Text.assemble(report.target, (f" {report.vector_type}{dims}", "dim")), ""),
135
+ ("Rows", rows, f"inspected {report.sample_rows:,}, {report.sample_method}"),
136
+ ]
137
+ )
138
+ ui.section("Findings")
139
+ ui.findings(report.sorted_findings())
140
+ summary = Text(" ")
141
+ for severity, (_, _, name) in _STYLE.items():
142
+ n = report.count(severity)
143
+ icon, colour, _ = ui.SEVERITY_STYLE[severity.value]
144
+ plural = "s" if n != 1 and name in ("error", "warning") else ""
145
+ summary.append(f"{icon} {n} {name}{plural} ", style=colour if n else "dim")
146
+ ui.console.print()
147
+ ui.console.print(summary)
148
+ ui.console.print()
149
+
150
+
85
151
  def _render(report: Report, connection: str) -> None:
152
+ from vecshift import ui
153
+
154
+ if ui.fancy():
155
+ _render_fancy(report, connection)
156
+ return
86
157
  dims = f"({report.declared_dimensions})" if report.declared_dimensions else ""
87
158
  rows = f"~{report.estimated_rows:,}" if report.estimated_rows is not None else "unknown"
88
159
  typer.secho(f"vecshift doctor · {report.store} via {connection}", bold=True)
@@ -218,7 +289,8 @@ from vecshift.cli_bench import bench # noqa: E402
218
289
 
219
290
  app.command()(bench)
220
291
 
221
- from vecshift.cli_plan import init, plan # noqa: E402
292
+ from vecshift.cli_init import init # noqa: E402
293
+ from vecshift.cli_plan import plan # noqa: E402
222
294
 
223
295
  app.command()(init)
224
296
  app.command()(plan)
@@ -234,3 +306,7 @@ app.command()(cleanup)
234
306
  from vecshift.cli_eval import eval_ # noqa: E402
235
307
 
236
308
  app.command(name="eval")(eval_)
309
+
310
+ from vecshift.cli_status import status # noqa: E402
311
+
312
+ app.command()(status)