schift 0.2.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 (164) hide show
  1. {schift-0.2.0 → schift-0.3.0}/.gitignore +7 -14
  2. schift-0.3.0/PKG-INFO +652 -0
  3. {schift-0.2.0/python → schift-0.3.0}/pyproject.toml +1 -1
  4. schift-0.3.0/schift/aggregate.py +64 -0
  5. schift-0.3.0/schift/artifacts.py +93 -0
  6. schift-0.3.0/schift/benchmark_suites.py +147 -0
  7. schift-0.3.0/schift/buckets.py +242 -0
  8. schift-0.3.0/schift/completions.py +176 -0
  9. schift-0.3.0/schift/drift.py +131 -0
  10. schift-0.3.0/schift/jobs.py +102 -0
  11. {schift-0.2.0/python → schift-0.3.0}/schift/schift_client.py +45 -0
  12. schift-0.3.0/schift/tasks.py +104 -0
  13. schift-0.3.0/schift/workflows.py +330 -0
  14. schift-0.2.0/PKG-INFO +0 -15
  15. schift-0.2.0/README.md +0 -281
  16. schift-0.2.0/cli/README.md +0 -303
  17. schift-0.2.0/cli/pyproject.toml +0 -30
  18. schift-0.2.0/cli/schift_cli/__init__.py +0 -1
  19. schift-0.2.0/cli/schift_cli/client.py +0 -119
  20. schift-0.2.0/cli/schift_cli/commands/__init__.py +0 -0
  21. schift-0.2.0/cli/schift_cli/commands/auth.py +0 -68
  22. schift-0.2.0/cli/schift_cli/commands/bench.py +0 -65
  23. schift-0.2.0/cli/schift_cli/commands/catalog.py +0 -74
  24. schift-0.2.0/cli/schift_cli/commands/db.py +0 -96
  25. schift-0.2.0/cli/schift_cli/commands/embed.py +0 -104
  26. schift-0.2.0/cli/schift_cli/commands/migrate.py +0 -127
  27. schift-0.2.0/cli/schift_cli/commands/query.py +0 -66
  28. schift-0.2.0/cli/schift_cli/commands/skill.py +0 -110
  29. schift-0.2.0/cli/schift_cli/commands/usage.py +0 -50
  30. schift-0.2.0/cli/schift_cli/config.py +0 -58
  31. schift-0.2.0/cli/schift_cli/data/schift-best-practices/AGENTS.md +0 -77
  32. schift-0.2.0/cli/schift_cli/data/schift-best-practices/CLAUDE.md +0 -1
  33. schift-0.2.0/cli/schift_cli/data/schift-best-practices/SKILL.md +0 -89
  34. schift-0.2.0/cli/schift_cli/data/schift-best-practices/references/bucket-organization.md +0 -126
  35. schift-0.2.0/cli/schift_cli/data/schift-best-practices/references/bucket-upload.md +0 -116
  36. schift-0.2.0/cli/schift_cli/data/schift-best-practices/references/chatbot-widget.md +0 -238
  37. schift-0.2.0/cli/schift_cli/data/schift-best-practices/references/cost-batching.md +0 -179
  38. schift-0.2.0/cli/schift_cli/data/schift-best-practices/references/cost-storage-tiers.md +0 -183
  39. schift-0.2.0/cli/schift_cli/data/schift-best-practices/references/deploy-cloudrun.md +0 -140
  40. schift-0.2.0/cli/schift_cli/data/schift-best-practices/references/embed-batch-processing.md +0 -86
  41. schift-0.2.0/cli/schift_cli/data/schift-best-practices/references/embed-error-handling.md +0 -155
  42. schift-0.2.0/cli/schift_cli/data/schift-best-practices/references/embed-multimodal.md +0 -100
  43. schift-0.2.0/cli/schift_cli/data/schift-best-practices/references/embed-task-types.md +0 -135
  44. schift-0.2.0/cli/schift_cli/data/schift-best-practices/references/rag-chunking.md +0 -173
  45. schift-0.2.0/cli/schift_cli/data/schift-best-practices/references/rag-workflow-builder.md +0 -205
  46. schift-0.2.0/cli/schift_cli/data/schift-best-practices/references/sdk-async-patterns.md +0 -103
  47. schift-0.2.0/cli/schift_cli/data/schift-best-practices/references/sdk-auth-patterns.md +0 -76
  48. schift-0.2.0/cli/schift_cli/data/schift-best-practices/references/search-collection-design.md +0 -229
  49. schift-0.2.0/cli/schift_cli/data/schift-best-practices/references/search-hybrid.md +0 -163
  50. schift-0.2.0/cli/schift_cli/data/schift-best-practices/references/search-similarity-tuning.md +0 -134
  51. schift-0.2.0/cli/schift_cli/display.py +0 -85
  52. schift-0.2.0/cli/schift_cli/main.py +0 -39
  53. schift-0.2.0/pyproject.toml +0 -18
  54. schift-0.2.0/src/schift/__init__.py +0 -26
  55. schift-0.2.0/src/schift/async_client.py +0 -82
  56. schift-0.2.0/src/schift/bucket.py +0 -153
  57. schift-0.2.0/src/schift/client.py +0 -87
  58. schift-0.2.0/src/schift/collection.py +0 -85
  59. schift-0.2.0/src/schift/models.py +0 -25
  60. schift-0.2.0/src/schift/workflow.py +0 -273
  61. schift-0.2.0/src/schift/workflow_engine.py +0 -332
  62. schift-0.2.0/src/schift/workflow_models.py +0 -215
  63. schift-0.2.0/src/schift/workflow_nodes.py +0 -828
  64. schift-0.2.0/src/schift/workflow_yaml.py +0 -264
  65. schift-0.2.0/swift/Package.swift +0 -27
  66. schift-0.2.0/swift/Sources/Schift/Errors.swift +0 -39
  67. schift-0.2.0/swift/Sources/Schift/HTTP.swift +0 -184
  68. schift-0.2.0/swift/Sources/Schift/Projection.swift +0 -68
  69. schift-0.2.0/swift/Sources/Schift/SchiftClient.swift +0 -258
  70. schift-0.2.0/swift/Sources/Schift/Types.swift +0 -281
  71. schift-0.2.0/swift/Tests/SchiftTests/SchiftClientTests.swift +0 -158
  72. schift-0.2.0/tests/test_async_client.py +0 -19
  73. schift-0.2.0/tests/test_bucket.py +0 -62
  74. schift-0.2.0/tests/test_client.py +0 -49
  75. schift-0.2.0/tests/test_workflow.py +0 -109
  76. schift-0.2.0/tests/test_workflow_engine.py +0 -834
  77. schift-0.2.0/tests/test_workflow_yaml.py +0 -434
  78. schift-0.2.0/ts/.github/workflows/publish.yml +0 -45
  79. schift-0.2.0/ts/README.md +0 -254
  80. schift-0.2.0/ts/package-lock.json +0 -2517
  81. schift-0.2.0/ts/package.json +0 -76
  82. schift-0.2.0/ts/src/__tests__/tools.test.ts +0 -153
  83. schift-0.2.0/ts/src/client.ts +0 -423
  84. schift-0.2.0/ts/src/errors.ts +0 -25
  85. schift-0.2.0/ts/src/index.ts +0 -87
  86. schift-0.2.0/ts/src/tools.ts +0 -321
  87. schift-0.2.0/ts/src/types.ts +0 -175
  88. schift-0.2.0/ts/src/workflow/__tests__/engine.test.ts +0 -820
  89. schift-0.2.0/ts/src/workflow/__tests__/workflow.test.ts +0 -502
  90. schift-0.2.0/ts/src/workflow/__tests__/yaml.test.ts +0 -406
  91. schift-0.2.0/ts/src/workflow/builder.ts +0 -214
  92. schift-0.2.0/ts/src/workflow/client.ts +0 -243
  93. schift-0.2.0/ts/src/workflow/engine.ts +0 -349
  94. schift-0.2.0/ts/src/workflow/index.ts +0 -66
  95. schift-0.2.0/ts/src/workflow/nodes.ts +0 -904
  96. schift-0.2.0/ts/src/workflow/types.ts +0 -244
  97. schift-0.2.0/ts/src/workflow/yaml.ts +0 -409
  98. schift-0.2.0/ts/src/workflow-editor/adapter.ts +0 -111
  99. schift-0.2.0/ts/src/workflow-editor/components/BlockConfigPanel.tsx +0 -315
  100. schift-0.2.0/ts/src/workflow-editor/components/BlockPalette.tsx +0 -138
  101. schift-0.2.0/ts/src/workflow-editor/components/SchemaBuilder.tsx +0 -354
  102. schift-0.2.0/ts/src/workflow-editor/components/WorkflowBuilder.tsx +0 -449
  103. schift-0.2.0/ts/src/workflow-editor/components/WorkflowCanvas.tsx +0 -377
  104. schift-0.2.0/ts/src/workflow-editor/components/WorkflowEditor.tsx +0 -37
  105. schift-0.2.0/ts/src/workflow-editor/components/WorkflowList.tsx +0 -303
  106. schift-0.2.0/ts/src/workflow-editor/context.tsx +0 -80
  107. schift-0.2.0/ts/src/workflow-editor/defaults.tsx +0 -190
  108. schift-0.2.0/ts/src/workflow-editor/examples.ts +0 -411
  109. schift-0.2.0/ts/src/workflow-editor/index.ts +0 -70
  110. schift-0.2.0/ts/src/workflow-editor/types.ts +0 -141
  111. schift-0.2.0/ts/src/workflow-editor/utils/workflow-graph.ts +0 -58
  112. schift-0.2.0/ts/tsconfig.json +0 -18
  113. schift-0.2.0/ts/tsup.config.ts +0 -24
  114. schift-0.2.0/uv.lock +0 -474
  115. {schift-0.2.0/python → schift-0.3.0}/README.md +0 -0
  116. {schift-0.2.0/python → schift-0.3.0}/bench_report.html +0 -0
  117. {schift-0.2.0/python → schift-0.3.0}/docs/ADAPTER_INTERFACE_CONTRACT.md +0 -0
  118. {schift-0.2.0/python → schift-0.3.0}/docs/CLIENT_AUTH_CONTRACT.md +0 -0
  119. {schift-0.2.0/python → schift-0.3.0}/docs/DB_CONTRACT.md +0 -0
  120. {schift-0.2.0/python → schift-0.3.0}/docs/DB_RECORD_SCHEMA.md +0 -0
  121. {schift-0.2.0/python → schift-0.3.0}/docs/EMBED_CONTRACT.md +0 -0
  122. {schift-0.2.0/python → schift-0.3.0}/docs/MIGRATE_BENCH_CONTRACT.md +0 -0
  123. {schift-0.2.0/python → schift-0.3.0}/docs/MIGRATE_FIT_CONTRACT.md +0 -0
  124. {schift-0.2.0/python → schift-0.3.0}/docs/MIGRATE_RUN_CONTRACT.md +0 -0
  125. {schift-0.2.0/python → schift-0.3.0}/docs/MODEL_CATALOG_CONTRACT.md +0 -0
  126. {schift-0.2.0/python → schift-0.3.0}/docs/ROUTING_DECISION_SCHEMA.md +0 -0
  127. {schift-0.2.0/python → schift-0.3.0}/docs/ROUTING_EVENTS_CONTRACT.md +0 -0
  128. {schift-0.2.0/python → schift-0.3.0}/docs/ROUTING_POLICY_CONTRACT.md +0 -0
  129. {schift-0.2.0/python → schift-0.3.0}/docs/SDK_FUNCTION_INVENTORY.md +0 -0
  130. {schift-0.2.0/python → schift-0.3.0}/docs/SDK_MODULE_BOUNDARIES.md +0 -0
  131. {schift-0.2.0/python → schift-0.3.0}/docs/SEARCH_CONTRACT.md +0 -0
  132. {schift-0.2.0/python → schift-0.3.0}/docs/SEARCH_QUERY_SCHEMA.md +0 -0
  133. {schift-0.2.0/python → schift-0.3.0}/docs/USAGE_CONTRACT.md +0 -0
  134. {schift-0.2.0/python → schift-0.3.0}/schift/__init__.py +0 -0
  135. {schift-0.2.0/python → schift-0.3.0}/schift/_http.py +0 -0
  136. {schift-0.2.0/python → schift-0.3.0}/schift/_migrate_module.py +0 -0
  137. {schift-0.2.0/python → schift-0.3.0}/schift/adapters/__init__.py +0 -0
  138. {schift-0.2.0/python → schift-0.3.0}/schift/adapters/base.py +0 -0
  139. {schift-0.2.0/python → schift-0.3.0}/schift/adapters/chroma.py +0 -0
  140. {schift-0.2.0/python → schift-0.3.0}/schift/adapters/elasticsearch.py +0 -0
  141. {schift-0.2.0/python → schift-0.3.0}/schift/adapters/file.py +0 -0
  142. {schift-0.2.0/python → schift-0.3.0}/schift/adapters/milvus.py +0 -0
  143. {schift-0.2.0/python → schift-0.3.0}/schift/adapters/mongodb.py +0 -0
  144. {schift-0.2.0/python → schift-0.3.0}/schift/adapters/pgvector.py +0 -0
  145. {schift-0.2.0/python → schift-0.3.0}/schift/adapters/pinecone.py +0 -0
  146. {schift-0.2.0/python → schift-0.3.0}/schift/adapters/qdrant.py +0 -0
  147. {schift-0.2.0/python → schift-0.3.0}/schift/adapters/redis.py +0 -0
  148. {schift-0.2.0/python → schift-0.3.0}/schift/adapters/registry.py +0 -0
  149. {schift-0.2.0/python → schift-0.3.0}/schift/adapters/weaviate.py +0 -0
  150. {schift-0.2.0/python → schift-0.3.0}/schift/bench.py +0 -0
  151. {schift-0.2.0/python → schift-0.3.0}/schift/catalog.py +0 -0
  152. {schift-0.2.0/python → schift-0.3.0}/schift/chat.py +0 -0
  153. {schift-0.2.0/python → schift-0.3.0}/schift/client.py +0 -0
  154. {schift-0.2.0/python → schift-0.3.0}/schift/db.py +0 -0
  155. {schift-0.2.0/python → schift-0.3.0}/schift/embed.py +0 -0
  156. {schift-0.2.0/python → schift-0.3.0}/schift/migrate.py +0 -0
  157. {schift-0.2.0/python → schift-0.3.0}/schift/projection.py +0 -0
  158. {schift-0.2.0/python → schift-0.3.0}/schift/query.py +0 -0
  159. {schift-0.2.0/python → schift-0.3.0}/schift/report.py +0 -0
  160. {schift-0.2.0/python → schift-0.3.0}/schift/rerank.py +0 -0
  161. {schift-0.2.0/python → schift-0.3.0}/schift/routing.py +0 -0
  162. {schift-0.2.0/python → schift-0.3.0}/schift/search.py +0 -0
  163. {schift-0.2.0/python → schift-0.3.0}/schift/tools.py +0 -0
  164. {schift-0.2.0/python → schift-0.3.0}/schift/usage.py +0 -0
@@ -26,7 +26,6 @@ api/.aws-sam/
26
26
  target/
27
27
  web/.test-dist/
28
28
  .wrangler/
29
- research/polarity-and-relation-retrieval-poc/.venv/
30
29
  infra/tfplan
31
30
  research/korean-legal-bench/kbl/
32
31
  research/korean-legal-bench/kcl/
@@ -36,24 +35,18 @@ scripts/ingest_korean_law/*.log
36
35
 
37
36
  # engine runtime data
38
37
  engine/data/
38
+ engine/data-sq8/
39
39
 
40
40
  # large binary data
41
+ *.bin
41
42
  *.pt
42
- api/weights/*.npy
43
- research/**/*.npy
44
- api/cache/
45
- research/*/cache/
46
- research/*/cache-no-xet/
47
- research/*/gold_data/
48
- research/beir-mteb/encoded/
49
- research/beir-mteb/raw/
50
- research/beir-mteb/pods_active.json
51
- research/beir-mteb/launch_pods.sh
52
- research/polarity-and-relation-retrieval-poc/runs/
53
- *.npz
54
43
  *.npy
44
+ *.npz
55
45
  *.pkl
56
- docs/demo/recordings/*.webm
46
+ *.webm
47
+
48
+ # large research data
49
+ research/longmemeval/data/
57
50
 
58
51
  # terraform
59
52
  infra/.terraform/
schift-0.3.0/PKG-INFO ADDED
@@ -0,0 +1,652 @@
1
+ Metadata-Version: 2.4
2
+ Name: schift
3
+ Version: 0.3.0
4
+ Summary: Embedding model migration SDK. Switch models without re-embedding.
5
+ Project-URL: Homepage, https://schift.io
6
+ Project-URL: Documentation, https://docs.schift.io
7
+ Project-URL: Repository, https://github.com/schift-io/schift-py
8
+ Project-URL: Bug Tracker, https://github.com/schift-io/schift-py/issues
9
+ Author-email: Schift <hello@schift.io>
10
+ License-Expression: MIT
11
+ Keywords: embeddings,migration,projection,rag,vector-database
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Requires-Python: >=3.9
22
+ Requires-Dist: httpx>=0.24
23
+ Requires-Dist: numpy>=1.24
24
+ Provides-Extra: all
25
+ Requires-Dist: chromadb>=0.4; extra == 'all'
26
+ Requires-Dist: elasticsearch>=8.0; extra == 'all'
27
+ Requires-Dist: pgvector>=0.2; extra == 'all'
28
+ Requires-Dist: pinecone-client>=5.0; extra == 'all'
29
+ Requires-Dist: psycopg[binary]>=3.1; extra == 'all'
30
+ Requires-Dist: pymilvus>=2.4; extra == 'all'
31
+ Requires-Dist: pymongo>=4.7; extra == 'all'
32
+ Requires-Dist: qdrant-client>=1.7; extra == 'all'
33
+ Requires-Dist: redis>=5.0; extra == 'all'
34
+ Requires-Dist: weaviate-client>=4.4; extra == 'all'
35
+ Provides-Extra: chroma
36
+ Requires-Dist: chromadb>=0.4; extra == 'chroma'
37
+ Provides-Extra: dev
38
+ Requires-Dist: pytest>=7.0; extra == 'dev'
39
+ Requires-Dist: ruff>=0.1; extra == 'dev'
40
+ Provides-Extra: elasticsearch
41
+ Requires-Dist: elasticsearch>=8.0; extra == 'elasticsearch'
42
+ Provides-Extra: milvus
43
+ Requires-Dist: pymilvus>=2.4; extra == 'milvus'
44
+ Provides-Extra: mongodb
45
+ Requires-Dist: pymongo>=4.7; extra == 'mongodb'
46
+ Provides-Extra: pinecone
47
+ Requires-Dist: pinecone-client>=5.0; extra == 'pinecone'
48
+ Provides-Extra: postgres
49
+ Requires-Dist: pgvector>=0.2; extra == 'postgres'
50
+ Requires-Dist: psycopg[binary]>=3.1; extra == 'postgres'
51
+ Provides-Extra: qdrant
52
+ Requires-Dist: qdrant-client>=1.7; extra == 'qdrant'
53
+ Provides-Extra: redis
54
+ Requires-Dist: redis>=5.0; extra == 'redis'
55
+ Provides-Extra: weaviate
56
+ Requires-Dist: weaviate-client>=4.4; extra == 'weaviate'
57
+ Description-Content-Type: text/markdown
58
+
59
+ # Schift Python SDK
60
+
61
+ Python client and local migration toolkit for Schift.
62
+
63
+ The package currently exposes two public client styles:
64
+
65
+ - `Schift`: modular client for live API operations such as catalog lookup, embedding, routing, search, usage, and hosted collection management.
66
+ - `Client`: legacy projection client for fitting and downloading `Projection` objects that run locally.
67
+
68
+ ## Install
69
+
70
+ Base package:
71
+
72
+ ```bash
73
+ pip install schift
74
+ ```
75
+
76
+ Optional adapters:
77
+
78
+ ```bash
79
+ pip install "schift[postgres]"
80
+ pip install "schift[qdrant]"
81
+ pip install "schift[all]"
82
+ ```
83
+
84
+ Development extras:
85
+
86
+ ```bash
87
+ pip install -e ".[dev]"
88
+ ```
89
+
90
+ ## Authentication And Base URLs
91
+
92
+ The modular client reads `SCHIFT_API_KEY` automatically:
93
+
94
+ ```bash
95
+ export SCHIFT_API_KEY=sch_your_key_here
96
+ ```
97
+
98
+ Equivalent explicit construction:
99
+
100
+ ```python
101
+ from schift import Schift
102
+
103
+ client = Schift(
104
+ api_key="sch_your_key_here",
105
+ base_url="https://api.schift.io/v1",
106
+ timeout=60.0,
107
+ )
108
+ ```
109
+
110
+ Notes:
111
+
112
+ - `Schift` expects a `/v1` base URL.
113
+ - The legacy `Client` defaults to `https://api.schift.io` and appends `/v1/...` internally.
114
+
115
+ ## Client Lifecycle
116
+
117
+ `Schift` holds a shared `httpx.Client`, so prefer a context manager for short-lived scripts:
118
+
119
+ ```python
120
+ from schift import Schift
121
+
122
+ with Schift() as client:
123
+ models = client.catalog.list()
124
+ ```
125
+
126
+ For long-running processes, keep one `Schift` instance and call `close()` during shutdown.
127
+
128
+ ## Quickstart
129
+
130
+ ```python
131
+ from schift import Schift
132
+
133
+ with Schift() as client:
134
+ models = client.catalog.list()
135
+ vector = client.embed(
136
+ "quarterly revenue report",
137
+ model="openai/text-embedding-3-small",
138
+ )
139
+
140
+ client.db.create_collection(name="finance-docs", dimension=len(vector))
141
+ client.db.upsert(
142
+ collection="finance-docs",
143
+ vectors=[
144
+ {
145
+ "id": "doc-1",
146
+ "values": vector.tolist(),
147
+ "metadata": {"source": "q1-report"},
148
+ }
149
+ ],
150
+ )
151
+
152
+ hits = client.query(
153
+ "revenue guidance",
154
+ collection="finance-docs",
155
+ top_k=5,
156
+ )
157
+
158
+ print(models[0]["id"] if models else "no models")
159
+ print(hits[0] if hits else "no hits")
160
+ ```
161
+
162
+ ## Module Reference
163
+
164
+ ### `catalog`
165
+
166
+ List available models and inspect one model by ID.
167
+
168
+ ```python
169
+ from schift import Schift
170
+
171
+ with Schift() as client:
172
+ models = client.catalog.list()
173
+ model = client.catalog.get("openai/text-embedding-3-small")
174
+ ```
175
+
176
+ ### `embed`
177
+
178
+ The embed module is callable for single-text requests and exposes `batch()` for multiple texts.
179
+
180
+ ```python
181
+ from schift import Schift
182
+
183
+ with Schift() as client:
184
+ one = client.embed(
185
+ "hello world",
186
+ model="openai/text-embedding-3-small",
187
+ )
188
+
189
+ many = client.embed.batch(
190
+ texts=["hello", "goodbye"],
191
+ model="openai/text-embedding-3-small",
192
+ dimensions=1024,
193
+ )
194
+ ```
195
+
196
+ Return values are NumPy arrays.
197
+
198
+ ### `routing`
199
+
200
+ Read or update the server-side routing policy used by Schift when a model is omitted.
201
+
202
+ ```python
203
+ from schift import Schift
204
+
205
+ with Schift() as client:
206
+ current = client.routing.get()
207
+ updated = client.routing.set(
208
+ primary="openai/text-embedding-3-small",
209
+ fallback="google/gemini-embedding-001",
210
+ mode="failover",
211
+ )
212
+ ```
213
+
214
+ ### `bench`
215
+
216
+ Run a server-side benchmark between two model IDs. The SDK returns `BenchReport`.
217
+
218
+ ```python
219
+ from schift import Schift
220
+
221
+ with Schift() as client:
222
+ report = client.bench.run(
223
+ source="openai/text-embedding-3-small",
224
+ target="google/gemini-embedding-001",
225
+ data="./eval_queries.jsonl",
226
+ )
227
+
228
+ print(report.verdict)
229
+ print(report.summary())
230
+ ```
231
+
232
+ ### `db`
233
+
234
+ Manage hosted collections and write vectors or raw documents.
235
+
236
+ ```python
237
+ from schift import Schift
238
+
239
+ with Schift() as client:
240
+ vector = client.embed(
241
+ "Schift reduces vector migration downtime.",
242
+ model="openai/text-embedding-3-small",
243
+ )
244
+
245
+ collection = client.db.create_collection(
246
+ name="product-docs",
247
+ dimension=len(vector),
248
+ )
249
+
250
+ client.db.upsert(
251
+ collection="product-docs",
252
+ vectors=[
253
+ {
254
+ "id": "doc-1",
255
+ "values": vector.tolist(),
256
+ "metadata": {"title": "Launch plan"},
257
+ }
258
+ ],
259
+ )
260
+
261
+ client.db.upsert_text(
262
+ collection="product-docs",
263
+ documents=[
264
+ {
265
+ "id": "doc-2",
266
+ "text": "Schift reduces vector migration downtime.",
267
+ "metadata": {"title": "Overview"},
268
+ }
269
+ ],
270
+ model="openai/text-embedding-3-small",
271
+ )
272
+
273
+ stats = client.db.collection_stats("product-docs")
274
+ ```
275
+
276
+ Available methods:
277
+
278
+ - `create_collection(name, dimension)`
279
+ - `list_collections()`
280
+ - `get_collection(name)`
281
+ - `collection_stats(name)`
282
+ - `delete_collection(name)`
283
+ - `upsert(collection, vectors)`
284
+ - `upsert_text(collection, documents, model)`
285
+
286
+ ### `query`
287
+
288
+ The query module is callable and supports hosted collections or an external DB handle.
289
+
290
+ ```python
291
+ from schift import Schift
292
+
293
+ with Schift() as client:
294
+ hosted = client.query(
295
+ "vector migration rollback plan",
296
+ collection="product-docs",
297
+ top_k=10,
298
+ rerank=True,
299
+ rerank_top_k=5,
300
+ )
301
+
302
+ passthrough = client.query(
303
+ "incident response",
304
+ db="prod-search-db",
305
+ model="openai/text-embedding-3-small",
306
+ top_k=5,
307
+ )
308
+ ```
309
+
310
+ ### `rerank`
311
+
312
+ Rerank a list of candidate documents with a cross-encoder style endpoint.
313
+
314
+ ```python
315
+ from schift import Schift
316
+
317
+ with Schift() as client:
318
+ reranked = client.rerank(
319
+ "incident response",
320
+ documents=[
321
+ {"id": "doc-1", "text": "..."},
322
+ {"id": "doc-2", "text": "..."},
323
+ ],
324
+ top_k=2,
325
+ )
326
+ ```
327
+
328
+ ### `usage`
329
+
330
+ Fetch aggregate usage for billing or dashboards.
331
+
332
+ ```python
333
+ from schift import Schift
334
+
335
+ with Schift() as client:
336
+ usage = client.usage.get(period="30d", granularity="day")
337
+ ```
338
+
339
+ ## Workflows
340
+
341
+ Build and run RAG pipelines as composable DAGs.
342
+
343
+ ### Quick Start
344
+
345
+ ```python
346
+ from schift import Schift
347
+
348
+ with Schift() as client:
349
+ # Create from template
350
+ wf = client.workflow.create_rag("Product Search")
351
+
352
+ # Run with inputs
353
+ result = client.workflow.run(wf.id, inputs={"query": "best laptop"})
354
+ print(result.outputs["answer"])
355
+ ```
356
+
357
+ ### CRUD
358
+
359
+ ```python
360
+ wf = client.workflow.create("My Pipeline", description="Custom RAG")
361
+ workflows = client.workflow.list()
362
+ wf = client.workflow.get(wf.id)
363
+ wf = client.workflow.update(wf.id, name="Renamed")
364
+ client.workflow.delete(wf.id)
365
+ ```
366
+
367
+ ### Building a Graph
368
+
369
+ ```python
370
+ wf = client.workflow.create("Custom RAG")
371
+
372
+ # Add blocks
373
+ start = client.workflow.add_block(wf.id, "start", title="Start")
374
+ retriever = client.workflow.add_block(wf.id, "retriever", config={
375
+ "collection": "my-docs",
376
+ "top_k": 5,
377
+ "rerank": True,
378
+ "rerank_top_k": 3,
379
+ })
380
+ prompt = client.workflow.add_block(wf.id, "prompt_template", config={
381
+ "template": "Answer based on:\n{{results}}\n\nQuestion: {{query}}",
382
+ })
383
+ llm = client.workflow.add_block(wf.id, "llm", config={
384
+ "model": "openai/gpt-4o-mini", # or "anthropic/claude-sonnet-4-20250514", "gemini-2.5-flash"
385
+ "temperature": 0.7,
386
+ })
387
+ end = client.workflow.add_block(wf.id, "end")
388
+
389
+ # Connect blocks
390
+ client.workflow.add_edge(wf.id, start["id"], retriever["id"])
391
+ client.workflow.add_edge(wf.id, retriever["id"], prompt["id"])
392
+ client.workflow.add_edge(wf.id, prompt["id"], llm["id"])
393
+ client.workflow.add_edge(wf.id, llm["id"], end["id"])
394
+ ```
395
+
396
+ ### Multiple Inputs
397
+
398
+ The start node forwards all input variables to downstream blocks:
399
+
400
+ ```python
401
+ result = client.workflow.run(wf.id, inputs={
402
+ "query": "maternity leave policy",
403
+ "user_id": "u-123",
404
+ "language": "ko",
405
+ })
406
+ ```
407
+
408
+ Reference variables in prompt templates with `{{variable}}` syntax.
409
+
410
+ ### Validation & Run History
411
+
412
+ ```python
413
+ # Validate graph (cycles, missing connections, etc.)
414
+ v = client.workflow.validate(wf.id)
415
+ print(v.valid, v.errors)
416
+
417
+ # List past runs
418
+ runs = client.workflow.runs(wf.id)
419
+ for r in runs:
420
+ print(r.run_id, r.status, r.outputs)
421
+ ```
422
+
423
+ ### YAML Import / Export
424
+
425
+ ```python
426
+ # Export to YAML
427
+ yaml_str = client.workflow.to_yaml(wf.id, path="pipeline.yaml")
428
+
429
+ # Load from file
430
+ definition = client.workflow.from_yaml("pipeline.yaml")
431
+
432
+ # Push YAML to create on server
433
+ wf = client.workflow.push_yaml("pipeline.yaml")
434
+ ```
435
+
436
+ ### Templates
437
+
438
+ | Method | Template |
439
+ |--------|----------|
440
+ | `create_rag(name)` | Retriever -> Prompt -> LLM |
441
+ | `create_doc_qa(name)` | Document QA with sources |
442
+ | `create_chat(name)` | Conversational RAG |
443
+ | `create_ocr_ingest(name)` | OCR -> Chunk -> Embed |
444
+
445
+ ### Async Workflows
446
+
447
+ ```python
448
+ from schift import AsyncSchift
449
+
450
+ async with AsyncSchift() as client:
451
+ wf = await client.workflow.create_rag("Async RAG")
452
+ result = await client.workflow.run(wf.id, inputs={"query": "hello"})
453
+ ```
454
+
455
+ ### Block Types
456
+
457
+ | Category | Types |
458
+ |----------|-------|
459
+ | Control | `start`, `end`, `conditional`, `loop` |
460
+ | Retrieval | `retriever` (with rerank toggle), `reranker` |
461
+ | LLM | `llm` (OpenAI/Anthropic/Google), `prompt_template`, `answer` |
462
+ | Data | `document_loader`, `chunker`, `embedder`, `text_processor` |
463
+ | Integration | `api_call`, `webhook`, `code_executor` |
464
+ | Storage | `vector_store`, `cache` |
465
+
466
+ ### API Reference
467
+
468
+ | Method | Description |
469
+ |--------|-------------|
470
+ | `workflow.create(name)` | Create workflow |
471
+ | `workflow.get(id)` | Get workflow |
472
+ | `workflow.list()` | List workflows |
473
+ | `workflow.update(id, **kw)` | Update workflow |
474
+ | `workflow.delete(id)` | Delete workflow |
475
+ | `workflow.run(id, inputs)` | Run workflow |
476
+ | `workflow.runs(id)` | List past runs |
477
+ | `workflow.validate(id)` | Validate graph |
478
+ | `workflow.add_block(id, type, config)` | Add block |
479
+ | `workflow.add_edge(id, src, tgt)` | Add edge |
480
+ | `workflow.to_yaml(id)` | Export as YAML |
481
+ | `workflow.from_yaml(path)` | Load YAML definition |
482
+ | `workflow.push_yaml(path)` | Import YAML to server |
483
+
484
+ ## Projection Workflow
485
+
486
+ Projection creation still lives in the legacy `Client` API. It returns a local `Projection` object that can transform vectors without further API calls.
487
+
488
+ ```python
489
+ from schift import Client
490
+
491
+ legacy = Client(api_key="sch_your_key_here")
492
+
493
+ # Embed the same sample texts with both providers before fitting.
494
+ source_pairs = old_model_embeddings
495
+ target_pairs = new_model_embeddings
496
+
497
+ projection = legacy.fit(
498
+ source=source_pairs,
499
+ target=target_pairs,
500
+ source_model="openai/text-embedding-3-small",
501
+ target_model="google/gemini-embedding-001",
502
+ project_name="openai-to-gemini",
503
+ )
504
+
505
+ converted = projection.transform(source_pairs[:10])
506
+ projection.save("./projection-openai-to-gemini")
507
+ ```
508
+
509
+ You can later reload a saved projection:
510
+
511
+ ```python
512
+ from schift import Projection
513
+
514
+ projection = Projection.load("./projection-openai-to-gemini")
515
+ ```
516
+
517
+ The legacy client also supports:
518
+
519
+ - `fit(...)`
520
+ - `bench(...)`
521
+ - `list_projections()`
522
+ - `get_projection(project_id)`
523
+
524
+ ## Local Migration With Adapters
525
+
526
+ The local migration engine reads vectors from a source adapter, applies a `Projection`, and writes to a sink adapter. This path does not depend on hosted collection APIs.
527
+
528
+ ```python
529
+ from schift import Projection
530
+ from schift.migrate import migrate
531
+ from schift.adapters.file import NpyAdapter
532
+
533
+ projection = Projection.load("./projection-openai-to-gemini")
534
+
535
+ source = NpyAdapter("old_embeddings.npy")
536
+ sink = NpyAdapter("new_embeddings.npy")
537
+
538
+ result = migrate(
539
+ source=source,
540
+ sink=sink,
541
+ projection=projection,
542
+ batch_size=2048,
543
+ dry_run=True,
544
+ )
545
+
546
+ print(result)
547
+ ```
548
+
549
+ The same engine is exposed through `Schift().migrate.run(...)`.
550
+
551
+ ### Built-in Adapters
552
+
553
+ #### NumPy files
554
+
555
+ ```python
556
+ from schift.adapters.file import NpyAdapter
557
+
558
+ adapter = NpyAdapter("embeddings.npy")
559
+ ```
560
+
561
+ #### pgvector
562
+
563
+ Requires `pip install "schift[postgres]"`.
564
+
565
+ ```python
566
+ from schift.adapters.pgvector import PgVectorAdapter
567
+
568
+ adapter = PgVectorAdapter(
569
+ conninfo="postgresql://user:password@localhost/mydb",
570
+ table="documents",
571
+ embedding_column="embedding",
572
+ id_column="id",
573
+ )
574
+ ```
575
+
576
+ #### Qdrant
577
+
578
+ Requires `pip install "schift[qdrant]"`.
579
+
580
+ ```python
581
+ from schift.adapters.qdrant import QdrantAdapter
582
+
583
+ adapter = QdrantAdapter(
584
+ url="http://localhost:6333",
585
+ collection="documents",
586
+ api_key=None,
587
+ )
588
+ ```
589
+
590
+ #### Registry Helpers
591
+
592
+ ```python
593
+ from schift.adapters import get_adapter, list_adapters
594
+
595
+ print(list_adapters())
596
+
597
+ adapter = get_adapter(
598
+ {
599
+ "type": "npy",
600
+ "path": "embeddings.npy",
601
+ }
602
+ )
603
+ ```
604
+
605
+ ## Errors
606
+
607
+ Both client styles raise exceptions from `schift.client`:
608
+
609
+ - `AuthError`
610
+ - `QuotaError`
611
+ - `SchiftError`
612
+
613
+ Example:
614
+
615
+ ```python
616
+ from schift import Schift
617
+ from schift.client import AuthError, QuotaError, SchiftError
618
+
619
+ try:
620
+ with Schift() as client:
621
+ client.catalog.list()
622
+ except AuthError:
623
+ ...
624
+ except QuotaError:
625
+ ...
626
+ except SchiftError:
627
+ ...
628
+ ```
629
+
630
+ ## Development
631
+
632
+ From `sdk/python/`:
633
+
634
+ ```bash
635
+ python -m pip install -e ".[dev]"
636
+ python -m ruff check .
637
+ python -m pytest
638
+ ```
639
+
640
+ ## Source Layout
641
+
642
+ ```text
643
+ sdk/python/
644
+ ├── schift/
645
+ │ ├── schift_client.py # modular SDK entry point
646
+ │ ├── client.py # legacy projection client
647
+ │ ├── projection.py # local projection object
648
+ │ ├── adapters/ # npy, pgvector, qdrant
649
+ │ ├── workflow.py # workflow CRUD, blocks, edges, YAML, templates
650
+ │ └── *.py # catalog, embed, routing, db, query, rerank, usage
651
+ └── docs/ # request/response contracts and schemas
652
+ ```
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "schift"
7
- version = "0.1.0"
7
+ version = "0.3.0"
8
8
  description = "Embedding model migration SDK. Switch models without re-embedding."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"