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.
Files changed (38) hide show
  1. videovector-1.0.0/LICENSE +22 -0
  2. videovector-1.0.0/PKG-INFO +238 -0
  3. videovector-1.0.0/README.md +199 -0
  4. videovector-1.0.0/pyproject.toml +91 -0
  5. videovector-1.0.0/setup.cfg +4 -0
  6. videovector-1.0.0/tests/test_client_auth_mode.py +35 -0
  7. videovector-1.0.0/tests/test_config_auth.py +97 -0
  8. videovector-1.0.0/tests/test_examples_public_safety.py +32 -0
  9. videovector-1.0.0/tests/test_http_client.py +247 -0
  10. videovector-1.0.0/tests/test_new_resources.py +1796 -0
  11. videovector-1.0.0/tests/test_polling_and_models.py +329 -0
  12. videovector-1.0.0/videovector/__init__.py +274 -0
  13. videovector-1.0.0/videovector/_client.py +282 -0
  14. videovector-1.0.0/videovector/_config.py +114 -0
  15. videovector-1.0.0/videovector/_exceptions.py +161 -0
  16. videovector-1.0.0/videovector/_http.py +520 -0
  17. videovector-1.0.0/videovector/_pagination.py +263 -0
  18. videovector-1.0.0/videovector/_types.py +1076 -0
  19. videovector-1.0.0/videovector/_version.py +3 -0
  20. videovector-1.0.0/videovector/py.typed +0 -0
  21. videovector-1.0.0/videovector/resources/__init__.py +41 -0
  22. videovector-1.0.0/videovector/resources/api_keys.py +220 -0
  23. videovector-1.0.0/videovector/resources/connectors.py +534 -0
  24. videovector-1.0.0/videovector/resources/exports.py +361 -0
  25. videovector-1.0.0/videovector/resources/import_jobs.py +300 -0
  26. videovector-1.0.0/videovector/resources/indexes.py +272 -0
  27. videovector-1.0.0/videovector/resources/prompt_runs.py +632 -0
  28. videovector-1.0.0/videovector/resources/prompts.py +448 -0
  29. videovector-1.0.0/videovector/resources/rate_limits.py +48 -0
  30. videovector-1.0.0/videovector/resources/search.py +697 -0
  31. videovector-1.0.0/videovector/resources/usage.py +168 -0
  32. videovector-1.0.0/videovector/resources/videos.py +522 -0
  33. videovector-1.0.0/videovector/resources/webhooks.py +459 -0
  34. videovector-1.0.0/videovector.egg-info/PKG-INFO +238 -0
  35. videovector-1.0.0/videovector.egg-info/SOURCES.txt +36 -0
  36. videovector-1.0.0/videovector.egg-info/dependency_links.txt +1 -0
  37. videovector-1.0.0/videovector.egg-info/requires.txt +12 -0
  38. 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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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 == []