videovector 1.0.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.
- videovector-1.0.0/LICENSE +22 -0
- videovector-1.0.0/PKG-INFO +238 -0
- videovector-1.0.0/README.md +199 -0
- videovector-1.0.0/pyproject.toml +91 -0
- videovector-1.0.0/setup.cfg +4 -0
- videovector-1.0.0/tests/test_client_auth_mode.py +35 -0
- videovector-1.0.0/tests/test_config_auth.py +97 -0
- videovector-1.0.0/tests/test_examples_public_safety.py +32 -0
- videovector-1.0.0/tests/test_http_client.py +247 -0
- videovector-1.0.0/tests/test_new_resources.py +1796 -0
- videovector-1.0.0/tests/test_polling_and_models.py +329 -0
- videovector-1.0.0/videovector/__init__.py +274 -0
- videovector-1.0.0/videovector/_client.py +282 -0
- videovector-1.0.0/videovector/_config.py +114 -0
- videovector-1.0.0/videovector/_exceptions.py +161 -0
- videovector-1.0.0/videovector/_http.py +520 -0
- videovector-1.0.0/videovector/_pagination.py +263 -0
- videovector-1.0.0/videovector/_types.py +1076 -0
- videovector-1.0.0/videovector/_version.py +3 -0
- videovector-1.0.0/videovector/py.typed +0 -0
- videovector-1.0.0/videovector/resources/__init__.py +41 -0
- videovector-1.0.0/videovector/resources/api_keys.py +220 -0
- videovector-1.0.0/videovector/resources/connectors.py +534 -0
- videovector-1.0.0/videovector/resources/exports.py +361 -0
- videovector-1.0.0/videovector/resources/import_jobs.py +300 -0
- videovector-1.0.0/videovector/resources/indexes.py +272 -0
- videovector-1.0.0/videovector/resources/prompt_runs.py +632 -0
- videovector-1.0.0/videovector/resources/prompts.py +448 -0
- videovector-1.0.0/videovector/resources/rate_limits.py +48 -0
- videovector-1.0.0/videovector/resources/search.py +697 -0
- videovector-1.0.0/videovector/resources/usage.py +168 -0
- videovector-1.0.0/videovector/resources/videos.py +522 -0
- videovector-1.0.0/videovector/resources/webhooks.py +459 -0
- videovector-1.0.0/videovector.egg-info/PKG-INFO +238 -0
- videovector-1.0.0/videovector.egg-info/SOURCES.txt +36 -0
- videovector-1.0.0/videovector.egg-info/dependency_links.txt +1 -0
- videovector-1.0.0/videovector.egg-info/requires.txt +12 -0
- videovector-1.0.0/videovector.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vector Methods
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: videovector
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python SDK for the VideoVector API
|
|
5
|
+
Author: Vector Methods
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/VectorMethods/videovector-python
|
|
8
|
+
Project-URL: Documentation, https://github.com/VectorMethods/videovector-python#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/VectorMethods/videovector-python
|
|
10
|
+
Project-URL: Issues, https://github.com/VectorMethods/videovector-python/issues
|
|
11
|
+
Keywords: video,search,ai,ml,computer-vision,video-understanding,semantic-search,sdk,api
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
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 :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Topic :: Multimedia :: Video
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.9
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: httpx>=0.25.0
|
|
28
|
+
Requires-Dist: pydantic>=2.0.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-httpx>=0.30.0; extra == "dev"
|
|
33
|
+
Requires-Dist: build>=1.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: twine>=5.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
36
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
37
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
# VideoVector Python SDK
|
|
41
|
+
|
|
42
|
+
Official Python SDK for the VideoVector API.
|
|
43
|
+
|
|
44
|
+
This repository is the public source of truth for the Python package, SDK documentation, examples, tests, and release workflow. It does not contain backend, MCP, frontend, deployment, or internal operations code.
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install videovector
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
For local development from this repository:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install -e ".[dev]"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Quickstart
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
import os
|
|
62
|
+
|
|
63
|
+
from videovector import VideoVector
|
|
64
|
+
|
|
65
|
+
with VideoVector(api_key=os.environ["VIDEO_VECTOR_API_KEY"]) as client:
|
|
66
|
+
index = client.indexes.create(name="Field Review Clips")
|
|
67
|
+
|
|
68
|
+
upload = client.videos.upload(
|
|
69
|
+
file="/path/to/video.mp4",
|
|
70
|
+
title="Store walk-through",
|
|
71
|
+
index_id=index.index_id,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
results = client.search.text(
|
|
75
|
+
index_id=index.index_id,
|
|
76
|
+
query="employee restocking shelves near checkout",
|
|
77
|
+
top_k=10,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
for result in results:
|
|
81
|
+
print(result.video_id, result.start_time, result.similarity_score)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Configuration
|
|
85
|
+
|
|
86
|
+
The SDK reads credentials and operational settings from constructor arguments or environment variables.
|
|
87
|
+
|
|
88
|
+
| Setting | Environment variable | Notes |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| API key | `VIDEO_VECTOR_API_KEY` | Recommended for server-to-server workflow calls. |
|
|
91
|
+
| JWT bearer token | `VIDEO_VECTOR_BEARER_TOKEN` | Required for JWT-only endpoints such as API key management. |
|
|
92
|
+
| Auth mode | `VIDEO_VECTOR_AUTH_MODE` | Optional; use `api_key` or `bearer` when both credentials are present. |
|
|
93
|
+
| Base URL | `VIDEO_VECTOR_BASE_URL` | Defaults to `https://api.vectormethods.com/api/v2`. |
|
|
94
|
+
| Timeout | `VIDEO_VECTOR_TIMEOUT` | Seconds; default is `60`. |
|
|
95
|
+
| Retries | `VIDEO_VECTOR_MAX_RETRIES` | Default is `3`. |
|
|
96
|
+
|
|
97
|
+
Explicit constructor arguments override environment values:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from videovector import VideoVector
|
|
101
|
+
|
|
102
|
+
client = VideoVector(
|
|
103
|
+
api_key="<VIDEO_VECTOR_API_KEY>",
|
|
104
|
+
base_url="https://api.vectormethods.com/api/v2",
|
|
105
|
+
timeout=90,
|
|
106
|
+
max_retries=5,
|
|
107
|
+
)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Configure one auth mode at a time. If both an API key and bearer token are present, set `auth_mode` or `VIDEO_VECTOR_AUTH_MODE`.
|
|
111
|
+
|
|
112
|
+
## Resource Overview
|
|
113
|
+
|
|
114
|
+
- `client.videos`: upload, retrieve, process, segments, batch helpers, and playground media.
|
|
115
|
+
- `client.indexes`: index CRUD, paginated index videos, and prompt-run history.
|
|
116
|
+
- `client.prompts`: prompt CRUD, schema validation, usage, video-level synthesis, and semantic indexing config.
|
|
117
|
+
- `client.prompt_runs`: estimate, execute, poll, retrieve results, inspect failures, retry failed segments, and inspect LLM calls.
|
|
118
|
+
- `client.search`: text, image, multimodal, filter, multi-run, and playground search.
|
|
119
|
+
- `client.connectors`: GCS, S3, and Azure connector creation, testing, browsing, and deletion.
|
|
120
|
+
- `client.import_jobs`: bulk import from configured connectors.
|
|
121
|
+
- `client.exports`: index and prompt-run metadata exports.
|
|
122
|
+
- `client.webhooks`: webhook CRUD, delivery inspection, retries, event discovery, and secret rotation.
|
|
123
|
+
- `client.api_keys`: API key CRUD, rotate, revoke, and delete with bearer auth.
|
|
124
|
+
- `client.usage`: usage metrics, history, details, and breakdowns.
|
|
125
|
+
- `client.rate_limits`: rate-limit status and refresh.
|
|
126
|
+
|
|
127
|
+
Endpoint-by-endpoint coverage is documented in [docs/backend-parity-matrix.md](docs/backend-parity-matrix.md).
|
|
128
|
+
|
|
129
|
+
## Pagination
|
|
130
|
+
|
|
131
|
+
Paginated endpoints return `SyncPage[T]` or `AsyncPage[T]`.
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
page = client.indexes.list_videos("idx_archive", limit=50)
|
|
135
|
+
for video in page.auto_paging_iter():
|
|
136
|
+
print(video.video_id)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from videovector import AsyncVideoVector
|
|
141
|
+
|
|
142
|
+
async with AsyncVideoVector(api_key="<VIDEO_VECTOR_API_KEY>") as client:
|
|
143
|
+
page = await client.videos.list_playground(limit=25)
|
|
144
|
+
async for video in page.auto_paging_iter():
|
|
145
|
+
print(video.video_id)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Error Handling
|
|
149
|
+
|
|
150
|
+
SDK exceptions map to API status classes:
|
|
151
|
+
|
|
152
|
+
- `AuthenticationError` for `401`
|
|
153
|
+
- `AuthorizationError` for `403`
|
|
154
|
+
- `NotFoundError` for `404`
|
|
155
|
+
- `ValidationError` for validation and other non-specialized `4xx` responses
|
|
156
|
+
- `RateLimitError` for `429`, including `retry_after` when provided
|
|
157
|
+
- `ConflictError` for `409`
|
|
158
|
+
- `ExternalServiceError` for `5xx`
|
|
159
|
+
- `TimeoutError`, `ConnectionError`, and `VideoVectorError` for transport or generic SDK failures
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
import os
|
|
163
|
+
|
|
164
|
+
from videovector import RateLimitError, ValidationError, VideoVector
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
with VideoVector(api_key=os.environ["VIDEO_VECTOR_API_KEY"]) as client:
|
|
168
|
+
client.search.text(index_id="idx_archive", query="")
|
|
169
|
+
except ValidationError as exc:
|
|
170
|
+
print(exc.error_code, exc.message)
|
|
171
|
+
except RateLimitError as exc:
|
|
172
|
+
print("retry after", exc.retry_after)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Retry and Idempotency
|
|
176
|
+
|
|
177
|
+
Automatic retries are enabled for:
|
|
178
|
+
|
|
179
|
+
- idempotent methods: `GET`, `HEAD`, `OPTIONS`, `PUT`, and `DELETE`
|
|
180
|
+
- any request that includes an explicit `idempotency_key`
|
|
181
|
+
|
|
182
|
+
For non-idempotent operations, pass an idempotency key when retrying is safe:
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
run = client.prompt_runs.execute(
|
|
186
|
+
prompt_id="prompt_scene_review",
|
|
187
|
+
target={"type": "index", "index_id": "idx_archive"},
|
|
188
|
+
idempotency_key="scene-review-2026-05-07",
|
|
189
|
+
)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Examples
|
|
193
|
+
|
|
194
|
+
The [examples](examples) directory contains runnable, environment-driven examples for common and advanced workflows:
|
|
195
|
+
|
|
196
|
+
- quickstart upload/search
|
|
197
|
+
- sync and async client usage
|
|
198
|
+
- custom prompt and schema design
|
|
199
|
+
- video-level synthesis
|
|
200
|
+
- text, image, multimodal, and filter search
|
|
201
|
+
- GCS, S3, and Azure connectors
|
|
202
|
+
- import jobs, exports, webhooks, idempotency, failed-segment recovery, usage, and rate limits
|
|
203
|
+
|
|
204
|
+
Examples intentionally use placeholders and environment variables. Do not hardcode credentials in application code.
|
|
205
|
+
|
|
206
|
+
## Development
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
python -m pip install --upgrade pip
|
|
210
|
+
python -m pip install -e ".[dev]"
|
|
211
|
+
ruff check videovector tests examples
|
|
212
|
+
mypy videovector
|
|
213
|
+
pytest -q tests
|
|
214
|
+
python -m build
|
|
215
|
+
python -m twine check dist/*
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Optional local secret scan:
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
gitleaks detect --source . --no-git --redact
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Unsupported Surfaces
|
|
225
|
+
|
|
226
|
+
This SDK intentionally does not wrap:
|
|
227
|
+
|
|
228
|
+
- internal operations endpoints
|
|
229
|
+
- billing endpoints
|
|
230
|
+
- MCP endpoints
|
|
231
|
+
- raw binary preview helpers such as GIF and thumbnail routes
|
|
232
|
+
- server-sent processing event streams
|
|
233
|
+
|
|
234
|
+
Use the REST API directly for unsupported public helper routes, and use the separate MCP package for MCP-specific workflows.
|
|
235
|
+
|
|
236
|
+
## License
|
|
237
|
+
|
|
238
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# VideoVector Python SDK
|
|
2
|
+
|
|
3
|
+
Official Python SDK for the VideoVector API.
|
|
4
|
+
|
|
5
|
+
This repository is the public source of truth for the Python package, SDK documentation, examples, tests, and release workflow. It does not contain backend, MCP, frontend, deployment, or internal operations code.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install videovector
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
For local development from this repository:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install -e ".[dev]"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quickstart
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
import os
|
|
23
|
+
|
|
24
|
+
from videovector import VideoVector
|
|
25
|
+
|
|
26
|
+
with VideoVector(api_key=os.environ["VIDEO_VECTOR_API_KEY"]) as client:
|
|
27
|
+
index = client.indexes.create(name="Field Review Clips")
|
|
28
|
+
|
|
29
|
+
upload = client.videos.upload(
|
|
30
|
+
file="/path/to/video.mp4",
|
|
31
|
+
title="Store walk-through",
|
|
32
|
+
index_id=index.index_id,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
results = client.search.text(
|
|
36
|
+
index_id=index.index_id,
|
|
37
|
+
query="employee restocking shelves near checkout",
|
|
38
|
+
top_k=10,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
for result in results:
|
|
42
|
+
print(result.video_id, result.start_time, result.similarity_score)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Configuration
|
|
46
|
+
|
|
47
|
+
The SDK reads credentials and operational settings from constructor arguments or environment variables.
|
|
48
|
+
|
|
49
|
+
| Setting | Environment variable | Notes |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| API key | `VIDEO_VECTOR_API_KEY` | Recommended for server-to-server workflow calls. |
|
|
52
|
+
| JWT bearer token | `VIDEO_VECTOR_BEARER_TOKEN` | Required for JWT-only endpoints such as API key management. |
|
|
53
|
+
| Auth mode | `VIDEO_VECTOR_AUTH_MODE` | Optional; use `api_key` or `bearer` when both credentials are present. |
|
|
54
|
+
| Base URL | `VIDEO_VECTOR_BASE_URL` | Defaults to `https://api.vectormethods.com/api/v2`. |
|
|
55
|
+
| Timeout | `VIDEO_VECTOR_TIMEOUT` | Seconds; default is `60`. |
|
|
56
|
+
| Retries | `VIDEO_VECTOR_MAX_RETRIES` | Default is `3`. |
|
|
57
|
+
|
|
58
|
+
Explicit constructor arguments override environment values:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from videovector import VideoVector
|
|
62
|
+
|
|
63
|
+
client = VideoVector(
|
|
64
|
+
api_key="<VIDEO_VECTOR_API_KEY>",
|
|
65
|
+
base_url="https://api.vectormethods.com/api/v2",
|
|
66
|
+
timeout=90,
|
|
67
|
+
max_retries=5,
|
|
68
|
+
)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Configure one auth mode at a time. If both an API key and bearer token are present, set `auth_mode` or `VIDEO_VECTOR_AUTH_MODE`.
|
|
72
|
+
|
|
73
|
+
## Resource Overview
|
|
74
|
+
|
|
75
|
+
- `client.videos`: upload, retrieve, process, segments, batch helpers, and playground media.
|
|
76
|
+
- `client.indexes`: index CRUD, paginated index videos, and prompt-run history.
|
|
77
|
+
- `client.prompts`: prompt CRUD, schema validation, usage, video-level synthesis, and semantic indexing config.
|
|
78
|
+
- `client.prompt_runs`: estimate, execute, poll, retrieve results, inspect failures, retry failed segments, and inspect LLM calls.
|
|
79
|
+
- `client.search`: text, image, multimodal, filter, multi-run, and playground search.
|
|
80
|
+
- `client.connectors`: GCS, S3, and Azure connector creation, testing, browsing, and deletion.
|
|
81
|
+
- `client.import_jobs`: bulk import from configured connectors.
|
|
82
|
+
- `client.exports`: index and prompt-run metadata exports.
|
|
83
|
+
- `client.webhooks`: webhook CRUD, delivery inspection, retries, event discovery, and secret rotation.
|
|
84
|
+
- `client.api_keys`: API key CRUD, rotate, revoke, and delete with bearer auth.
|
|
85
|
+
- `client.usage`: usage metrics, history, details, and breakdowns.
|
|
86
|
+
- `client.rate_limits`: rate-limit status and refresh.
|
|
87
|
+
|
|
88
|
+
Endpoint-by-endpoint coverage is documented in [docs/backend-parity-matrix.md](docs/backend-parity-matrix.md).
|
|
89
|
+
|
|
90
|
+
## Pagination
|
|
91
|
+
|
|
92
|
+
Paginated endpoints return `SyncPage[T]` or `AsyncPage[T]`.
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
page = client.indexes.list_videos("idx_archive", limit=50)
|
|
96
|
+
for video in page.auto_paging_iter():
|
|
97
|
+
print(video.video_id)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from videovector import AsyncVideoVector
|
|
102
|
+
|
|
103
|
+
async with AsyncVideoVector(api_key="<VIDEO_VECTOR_API_KEY>") as client:
|
|
104
|
+
page = await client.videos.list_playground(limit=25)
|
|
105
|
+
async for video in page.auto_paging_iter():
|
|
106
|
+
print(video.video_id)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Error Handling
|
|
110
|
+
|
|
111
|
+
SDK exceptions map to API status classes:
|
|
112
|
+
|
|
113
|
+
- `AuthenticationError` for `401`
|
|
114
|
+
- `AuthorizationError` for `403`
|
|
115
|
+
- `NotFoundError` for `404`
|
|
116
|
+
- `ValidationError` for validation and other non-specialized `4xx` responses
|
|
117
|
+
- `RateLimitError` for `429`, including `retry_after` when provided
|
|
118
|
+
- `ConflictError` for `409`
|
|
119
|
+
- `ExternalServiceError` for `5xx`
|
|
120
|
+
- `TimeoutError`, `ConnectionError`, and `VideoVectorError` for transport or generic SDK failures
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
import os
|
|
124
|
+
|
|
125
|
+
from videovector import RateLimitError, ValidationError, VideoVector
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
with VideoVector(api_key=os.environ["VIDEO_VECTOR_API_KEY"]) as client:
|
|
129
|
+
client.search.text(index_id="idx_archive", query="")
|
|
130
|
+
except ValidationError as exc:
|
|
131
|
+
print(exc.error_code, exc.message)
|
|
132
|
+
except RateLimitError as exc:
|
|
133
|
+
print("retry after", exc.retry_after)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Retry and Idempotency
|
|
137
|
+
|
|
138
|
+
Automatic retries are enabled for:
|
|
139
|
+
|
|
140
|
+
- idempotent methods: `GET`, `HEAD`, `OPTIONS`, `PUT`, and `DELETE`
|
|
141
|
+
- any request that includes an explicit `idempotency_key`
|
|
142
|
+
|
|
143
|
+
For non-idempotent operations, pass an idempotency key when retrying is safe:
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
run = client.prompt_runs.execute(
|
|
147
|
+
prompt_id="prompt_scene_review",
|
|
148
|
+
target={"type": "index", "index_id": "idx_archive"},
|
|
149
|
+
idempotency_key="scene-review-2026-05-07",
|
|
150
|
+
)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Examples
|
|
154
|
+
|
|
155
|
+
The [examples](examples) directory contains runnable, environment-driven examples for common and advanced workflows:
|
|
156
|
+
|
|
157
|
+
- quickstart upload/search
|
|
158
|
+
- sync and async client usage
|
|
159
|
+
- custom prompt and schema design
|
|
160
|
+
- video-level synthesis
|
|
161
|
+
- text, image, multimodal, and filter search
|
|
162
|
+
- GCS, S3, and Azure connectors
|
|
163
|
+
- import jobs, exports, webhooks, idempotency, failed-segment recovery, usage, and rate limits
|
|
164
|
+
|
|
165
|
+
Examples intentionally use placeholders and environment variables. Do not hardcode credentials in application code.
|
|
166
|
+
|
|
167
|
+
## Development
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
python -m pip install --upgrade pip
|
|
171
|
+
python -m pip install -e ".[dev]"
|
|
172
|
+
ruff check videovector tests examples
|
|
173
|
+
mypy videovector
|
|
174
|
+
pytest -q tests
|
|
175
|
+
python -m build
|
|
176
|
+
python -m twine check dist/*
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Optional local secret scan:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
gitleaks detect --source . --no-git --redact
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Unsupported Surfaces
|
|
186
|
+
|
|
187
|
+
This SDK intentionally does not wrap:
|
|
188
|
+
|
|
189
|
+
- internal operations endpoints
|
|
190
|
+
- billing endpoints
|
|
191
|
+
- MCP endpoints
|
|
192
|
+
- raw binary preview helpers such as GIF and thumbnail routes
|
|
193
|
+
- server-sent processing event streams
|
|
194
|
+
|
|
195
|
+
Use the REST API directly for unsupported public helper routes, and use the separate MCP package for MCP-specific workflows.
|
|
196
|
+
|
|
197
|
+
## License
|
|
198
|
+
|
|
199
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77.0.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "videovector"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Python SDK for the VideoVector API"
|
|
9
|
+
authors = [
|
|
10
|
+
{name = "Vector Methods"}
|
|
11
|
+
]
|
|
12
|
+
license = "MIT"
|
|
13
|
+
readme = "README.md"
|
|
14
|
+
requires-python = ">=3.9"
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.9",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
25
|
+
"Topic :: Multimedia :: Video",
|
|
26
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
27
|
+
"Typing :: Typed",
|
|
28
|
+
]
|
|
29
|
+
keywords = [
|
|
30
|
+
"video",
|
|
31
|
+
"search",
|
|
32
|
+
"ai",
|
|
33
|
+
"ml",
|
|
34
|
+
"computer-vision",
|
|
35
|
+
"video-understanding",
|
|
36
|
+
"semantic-search",
|
|
37
|
+
"sdk",
|
|
38
|
+
"api",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
dependencies = [
|
|
42
|
+
"httpx>=0.25.0",
|
|
43
|
+
"pydantic>=2.0.0",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[project.optional-dependencies]
|
|
47
|
+
dev = [
|
|
48
|
+
"pytest>=7.0.0",
|
|
49
|
+
"pytest-asyncio>=0.21.0",
|
|
50
|
+
"pytest-httpx>=0.30.0",
|
|
51
|
+
"build>=1.0.0",
|
|
52
|
+
"twine>=5.0.0",
|
|
53
|
+
"black>=23.0.0",
|
|
54
|
+
"mypy>=1.0.0",
|
|
55
|
+
"ruff>=0.1.0",
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
[project.urls]
|
|
59
|
+
Homepage = "https://github.com/VectorMethods/videovector-python"
|
|
60
|
+
Documentation = "https://github.com/VectorMethods/videovector-python#readme"
|
|
61
|
+
Repository = "https://github.com/VectorMethods/videovector-python"
|
|
62
|
+
Issues = "https://github.com/VectorMethods/videovector-python/issues"
|
|
63
|
+
|
|
64
|
+
[tool.setuptools.packages.find]
|
|
65
|
+
where = ["."]
|
|
66
|
+
include = ["videovector*"]
|
|
67
|
+
|
|
68
|
+
[tool.setuptools.package-data]
|
|
69
|
+
videovector = ["py.typed"]
|
|
70
|
+
|
|
71
|
+
[tool.black]
|
|
72
|
+
line-length = 100
|
|
73
|
+
target-version = ["py39", "py310", "py311", "py312"]
|
|
74
|
+
|
|
75
|
+
[tool.ruff]
|
|
76
|
+
line-length = 100
|
|
77
|
+
target-version = "py39"
|
|
78
|
+
|
|
79
|
+
[tool.ruff.lint]
|
|
80
|
+
select = ["E", "F", "I", "W"]
|
|
81
|
+
ignore = ["E501"]
|
|
82
|
+
|
|
83
|
+
[tool.mypy]
|
|
84
|
+
python_version = "3.9"
|
|
85
|
+
warn_return_any = true
|
|
86
|
+
warn_unused_configs = true
|
|
87
|
+
disallow_untyped_defs = true
|
|
88
|
+
plugins = ["pydantic.mypy"]
|
|
89
|
+
|
|
90
|
+
[tool.pytest.ini_options]
|
|
91
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from videovector import AsyncVideoVector, VideoVector
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_sync_client_accepts_explicit_auth_mode_with_dual_credentials() -> None:
|
|
9
|
+
client = VideoVector(
|
|
10
|
+
api_key="vv_test_api_key",
|
|
11
|
+
bearer_token="bearer-token",
|
|
12
|
+
auth_mode="api_key",
|
|
13
|
+
)
|
|
14
|
+
try:
|
|
15
|
+
headers = dict(client._http._client.headers) # type: ignore[attr-defined]
|
|
16
|
+
assert headers.get("x-api-key") == "vv_test_api_key"
|
|
17
|
+
assert "authorization" not in headers
|
|
18
|
+
finally:
|
|
19
|
+
client.close()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_async_client_accepts_explicit_auth_mode_with_dual_credentials() -> None:
|
|
23
|
+
client = AsyncVideoVector(
|
|
24
|
+
api_key="vv_test_api_key",
|
|
25
|
+
bearer_token="bearer-token",
|
|
26
|
+
auth_mode="bearer",
|
|
27
|
+
)
|
|
28
|
+
headers = client._http._default_headers() # type: ignore[attr-defined]
|
|
29
|
+
assert headers.get("Authorization") == "Bearer bearer-token"
|
|
30
|
+
assert "X-API-Key" not in headers
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_client_rejects_dual_credentials_without_auth_mode() -> None:
|
|
34
|
+
with pytest.raises(ValueError, match="Provide only one authentication method"):
|
|
35
|
+
VideoVector(api_key="vv_test_api_key", bearer_token="bearer-token")
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from videovector._config import DEFAULT_BASE_URL, ClientConfig
|
|
7
|
+
|
|
8
|
+
CANONICAL_BASE_URL = "https://api.vectormethods.com/api/v2"
|
|
9
|
+
LEGACY_RAW_STAGE_BASE_URL = (
|
|
10
|
+
"https://playground-" + "api-stg-" + "udk7d32fva-uc.a.run.app/api/v2"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_config_accepts_api_key() -> None:
|
|
15
|
+
cfg = ClientConfig.from_env(api_key="vv_test_api_key")
|
|
16
|
+
assert cfg.api_key == "vv_test_api_key"
|
|
17
|
+
assert cfg.bearer_token is None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_config_accepts_bearer_token() -> None:
|
|
21
|
+
cfg = ClientConfig.from_env(bearer_token="bearer-token-test")
|
|
22
|
+
assert cfg.bearer_token == "bearer-token-test"
|
|
23
|
+
assert cfg.api_key is None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_config_uses_bearer_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
27
|
+
monkeypatch.setenv("VIDEO_VECTOR_BEARER_TOKEN", "env-bearer")
|
|
28
|
+
cfg = ClientConfig.from_env()
|
|
29
|
+
assert cfg.bearer_token == "env-bearer"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_legacy_env_prefix_is_not_supported(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
33
|
+
legacy_api_key_env = "VIDEO_" + "SEARCH_API_KEY"
|
|
34
|
+
monkeypatch.setenv(legacy_api_key_env, "legacy-token")
|
|
35
|
+
with pytest.raises(ValueError, match="Authentication is required"):
|
|
36
|
+
ClientConfig.from_env()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_config_requires_auth() -> None:
|
|
40
|
+
with pytest.raises(ValueError, match="Authentication is required"):
|
|
41
|
+
ClientConfig.from_env()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_config_rejects_both_auth_modes() -> None:
|
|
45
|
+
with pytest.raises(ValueError, match="Provide only one authentication method"):
|
|
46
|
+
ClientConfig.from_env(api_key="vv_test_api_key", bearer_token="<VIDEO_VECTOR_BEARER_TOKEN>")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_config_env_auth_mode_validation(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
50
|
+
monkeypatch.setenv("VIDEO_VECTOR_API_KEY", "vv_test_api_key")
|
|
51
|
+
monkeypatch.setenv("VIDEO_VECTOR_AUTH_MODE", "invalid")
|
|
52
|
+
with pytest.raises(ValueError, match="VIDEO_VECTOR_AUTH_MODE"):
|
|
53
|
+
ClientConfig.from_env()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_config_allows_both_credentials_with_explicit_auth_mode() -> None:
|
|
57
|
+
cfg = ClientConfig.from_env(
|
|
58
|
+
api_key="vv_test_api_key",
|
|
59
|
+
bearer_token="bearer-token-test",
|
|
60
|
+
auth_mode="bearer",
|
|
61
|
+
)
|
|
62
|
+
assert cfg.auth_mode == "bearer"
|
|
63
|
+
assert cfg.api_key == "vv_test_api_key"
|
|
64
|
+
assert cfg.bearer_token == "bearer-token-test"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_config_uses_verified_default_base_url() -> None:
|
|
68
|
+
cfg = ClientConfig.from_env(api_key="vv_test_api_key")
|
|
69
|
+
assert cfg.base_url == DEFAULT_BASE_URL
|
|
70
|
+
assert DEFAULT_BASE_URL == CANONICAL_BASE_URL
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_legacy_raw_stage_url_is_absent_from_repo_text() -> None:
|
|
74
|
+
repo_root = Path(__file__).resolve().parents[1]
|
|
75
|
+
ignored_parts = {
|
|
76
|
+
".git",
|
|
77
|
+
".mypy_cache",
|
|
78
|
+
".pytest_cache",
|
|
79
|
+
".ruff_cache",
|
|
80
|
+
".venv",
|
|
81
|
+
"__pycache__",
|
|
82
|
+
"build",
|
|
83
|
+
"dist",
|
|
84
|
+
}
|
|
85
|
+
offenders: list[str] = []
|
|
86
|
+
|
|
87
|
+
for path in repo_root.rglob("*"):
|
|
88
|
+
if not path.is_file() or ignored_parts.intersection(path.parts):
|
|
89
|
+
continue
|
|
90
|
+
try:
|
|
91
|
+
content = path.read_text(encoding="utf-8")
|
|
92
|
+
except UnicodeDecodeError:
|
|
93
|
+
continue
|
|
94
|
+
if LEGACY_RAW_STAGE_BASE_URL in content:
|
|
95
|
+
offenders.append(str(path.relative_to(repo_root)))
|
|
96
|
+
|
|
97
|
+
assert offenders == []
|