argus-cortex 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.
- argus_cortex-0.3.0/.env.example +21 -0
- {argus_cortex-0.1.0 → argus_cortex-0.3.0}/.github/workflows/release.yml +9 -1
- {argus_cortex-0.1.0 → argus_cortex-0.3.0}/.gitignore +3 -1
- argus_cortex-0.3.0/CHANGELOG.md +77 -0
- argus_cortex-0.3.0/CLAUDE.md +48 -0
- argus_cortex-0.3.0/PKG-INFO +164 -0
- argus_cortex-0.3.0/README.md +121 -0
- {argus_cortex-0.1.0 → argus_cortex-0.3.0}/pyproject.toml +23 -2
- {argus_cortex-0.1.0 → argus_cortex-0.3.0}/src/argus_cortex/__init__.py +70 -0
- {argus_cortex-0.1.0 → argus_cortex-0.3.0}/src/argus_cortex/_version.py +2 -2
- argus_cortex-0.3.0/src/argus_cortex/server.py +155 -0
- argus_cortex-0.3.0/src/argus_cortex/store/__init__.py +72 -0
- argus_cortex-0.3.0/src/argus_cortex/store/blob.py +310 -0
- argus_cortex-0.3.0/src/argus_cortex/store/config.py +76 -0
- argus_cortex-0.3.0/src/argus_cortex/store/errors.py +82 -0
- argus_cortex-0.3.0/src/argus_cortex/store/lineage.py +447 -0
- argus_cortex-0.3.0/src/argus_cortex/store/models.py +116 -0
- argus_cortex-0.3.0/src/argus_cortex/store/schema.py +79 -0
- argus_cortex-0.3.0/src/argus_cortex/store/vector.py +316 -0
- argus_cortex-0.3.0/src/argus_cortex/xmp.py +409 -0
- argus_cortex-0.3.0/tests/test_blob.py +311 -0
- argus_cortex-0.3.0/tests/test_server.py +151 -0
- argus_cortex-0.3.0/tests/test_store.py +419 -0
- argus_cortex-0.3.0/tests/test_store_pg_integration.py +93 -0
- argus_cortex-0.3.0/tests/test_vector.py +335 -0
- argus_cortex-0.3.0/tests/test_xmp.py +157 -0
- argus_cortex-0.1.0/PKG-INFO +0 -72
- argus_cortex-0.1.0/README.md +0 -47
- {argus_cortex-0.1.0 → argus_cortex-0.3.0}/.copier-answers.yml +0 -0
- {argus_cortex-0.1.0 → argus_cortex-0.3.0}/.github/workflows/ci.yml +0 -0
- {argus_cortex-0.1.0 → argus_cortex-0.3.0}/LICENSE +0 -0
- {argus_cortex-0.1.0 → argus_cortex-0.3.0}/Makefile +0 -0
- {argus_cortex-0.1.0 → argus_cortex-0.3.0}/src/argus_cortex/backends.py +0 -0
- {argus_cortex-0.1.0 → argus_cortex-0.3.0}/src/argus_cortex/py.typed +0 -0
- {argus_cortex-0.1.0 → argus_cortex-0.3.0}/src/argus_cortex/taxonomy.py +0 -0
- {argus_cortex-0.1.0 → argus_cortex-0.3.0}/src/argus_cortex/wire.py +0 -0
- {argus_cortex-0.1.0 → argus_cortex-0.3.0}/tests/test_backends.py +0 -0
- {argus_cortex-0.1.0 → argus_cortex-0.3.0}/tests/test_smoke.py +0 -0
- {argus_cortex-0.1.0 → argus_cortex-0.3.0}/tests/test_taxonomy.py +0 -0
- {argus_cortex-0.1.0 → argus_cortex-0.3.0}/tests/test_wire.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Optional stateful services layer (argus_cortex.store).
|
|
2
|
+
# Every value is optional: unset a URL and that feature no-ops (graceful degrade).
|
|
3
|
+
# Copy to .env and fill in only the services you actually run.
|
|
4
|
+
|
|
5
|
+
# Postgres — lineage DAG + human-edit capture (Phase 1).
|
|
6
|
+
# Unset -> open_lineage_store() returns a no-op NullLineageStore.
|
|
7
|
+
CORTEX_PG_URL=postgresql://user:pass@localhost:5432/argus
|
|
8
|
+
# Connection-pool bounds (optional; defaults 0 / 8 — min 0 holds no idle conn).
|
|
9
|
+
CORTEX_PG_POOL_MIN_SIZE=0
|
|
10
|
+
CORTEX_PG_POOL_MAX_SIZE=8
|
|
11
|
+
|
|
12
|
+
# Qdrant — image + tag-set embeddings (Phase 2).
|
|
13
|
+
CORTEX_QDRANT_URL=http://localhost:6333
|
|
14
|
+
|
|
15
|
+
# MinIO / S3 — owned image bytes (Phase 3). Scheme-less endpoint defaults to TLS;
|
|
16
|
+
# write http:// explicitly for a plaintext endpoint. Region matters on AWS S3.
|
|
17
|
+
CORTEX_S3_ENDPOINT=http://localhost:9000
|
|
18
|
+
CORTEX_S3_ACCESS_KEY=
|
|
19
|
+
CORTEX_S3_SECRET_KEY=
|
|
20
|
+
CORTEX_S3_BUCKET=argus
|
|
21
|
+
CORTEX_S3_REGION=us-east-1
|
|
@@ -62,7 +62,15 @@ jobs:
|
|
|
62
62
|
|
|
63
63
|
publish-pypi:
|
|
64
64
|
runs-on: ubuntu-latest
|
|
65
|
-
|
|
65
|
+
# Ordered *after* the TestPyPI rehearsal so its outcome is visible first, but
|
|
66
|
+
# deliberately not gated on it. A TestPyPI-only fault — a missing trusted
|
|
67
|
+
# publisher, an index outage — must not silently withhold the real release:
|
|
68
|
+
# that failure mode shipped argus-proof images for two tags with no wheel on
|
|
69
|
+
# PyPI, and nothing surfaced it because publish-image is independent.
|
|
70
|
+
# Only the build + install gate is required; a TestPyPI failure still marks
|
|
71
|
+
# the whole run failed, so the problem stays loud.
|
|
72
|
+
needs: [test-install, publish-testpypi]
|
|
73
|
+
if: "!cancelled() && needs.test-install.result == 'success'"
|
|
66
74
|
environment: pypi
|
|
67
75
|
steps:
|
|
68
76
|
- uses: actions/download-artifact@v4
|
|
@@ -6,5 +6,7 @@ build/
|
|
|
6
6
|
*.egg-info/
|
|
7
7
|
.pytest_cache/
|
|
8
8
|
.ruff_cache/
|
|
9
|
+
# local service config for the optional stores (.env.example stays tracked)
|
|
10
|
+
.env
|
|
9
11
|
# hatch-vcs writes this at build time (version derived from git tags)
|
|
10
|
-
src/
|
|
12
|
+
src/argus_cortex/_version.py
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.3.0] - 2026-10-25
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **`argus_cortex.xmp`** — the suite's one XMP sidecar implementation, hoisted
|
|
14
|
+
from argus-lens so lens (keywords/caption) and argus-curator (face regions,
|
|
15
|
+
content rating) share a writer. `write_xmp()` / `read_xmp()` /
|
|
16
|
+
`merge_xmp()` / `parse_xmp()` / `render_xmp()` / `sidecar_path()` over an
|
|
17
|
+
`XmpData` model, all exported from the top-level package.
|
|
18
|
+
- Namespaces: `dc` (subject, description), `xmp` (Rating, Label), `lr`
|
|
19
|
+
(hierarchicalSubject), `Iptc4xmpExt` (PersonInImage), `mwg-rs` regions
|
|
20
|
+
(`XmpRegions` / `XmpRegion`, MWG centre-based normalised coordinates), and a
|
|
21
|
+
new `argus` namespace for `ContentRating`. The `argus` namespace URI
|
|
22
|
+
(`https://github.com/smk762/argus-cortex/ns/xmp/1.0/`) is now a wire
|
|
23
|
+
contract.
|
|
24
|
+
- **Writes merge**: each property set in `XmpData` replaces that property
|
|
25
|
+
(latest write wins, element or attribute form, across every
|
|
26
|
+
`rdf:Description`); everything else in the sidecar — Lightroom develop
|
|
27
|
+
settings, GPS, another stage's fields, foreign namespace prefixes — is
|
|
28
|
+
preserved. `None` leaves a property untouched, an empty value removes it;
|
|
29
|
+
`replace=True` discards the existing sidecar.
|
|
30
|
+
- Writes are atomic (temp file + rename) and keep the existing file mode
|
|
31
|
+
(`0644` for a new sidecar). An unparseable existing sidecar raises
|
|
32
|
+
`XmpError` and is never clobbered; a DTD / entity declaration is refused.
|
|
33
|
+
- Stdlib `xml.etree` + pydantic only, so the top-level import stays
|
|
34
|
+
pydantic-only.
|
|
35
|
+
- `CLAUDE.md` agent guide.
|
|
36
|
+
|
|
37
|
+
### Security
|
|
38
|
+
- **eMudhra/emSign CA incident.** The extras that make outbound TLS
|
|
39
|
+
connections (`[remote]` httpx, `[qdrant]`, `[s3]`) now require
|
|
40
|
+
`certifi>=2026.10.19`, which no longer trusts the compromised emSign
|
|
41
|
+
intermediates. See https://discuss.python.org/t/109576.
|
|
42
|
+
|
|
43
|
+
## [0.2.0] - 2026-07-21
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
- **Optional stateful-services layer** (`argus_cortex.store`), each store opt-in
|
|
47
|
+
via `CORTEX_*` env vars and a `Null*` no-op when unset:
|
|
48
|
+
- Phase 1 — Postgres lineage store (`source_asset → caption → human_edit →
|
|
49
|
+
dataset_membership → training_run`), backed by a psycopg connection pool
|
|
50
|
+
with an atomic `transaction()` boundary (`[postgres]` extra).
|
|
51
|
+
- Phase 2 — Qdrant vector store for image / tag-set embeddings
|
|
52
|
+
(`[qdrant]` extra).
|
|
53
|
+
- Phase 3 — MinIO/S3 content-addressed blob store, keyed on sha256
|
|
54
|
+
(`content_key()` == `source_asset.sha256`) (`[s3]` extra).
|
|
55
|
+
- **`argus_cortex.server`** — shared pure-ASGI `WriteGuard`, the
|
|
56
|
+
`constant_refuse()` / `cross_site_refuse()` predicates, `env_flag()` and the
|
|
57
|
+
`UNSAFE_METHODS` / `TRUTHY` / `FALSY` constants (`[server]` extra).
|
|
58
|
+
|
|
59
|
+
### Fixed
|
|
60
|
+
- Release: a TestPyPI failure no longer gates the real PyPI release.
|
|
61
|
+
|
|
62
|
+
## [0.1.0] - 2026-07-08
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
- Initial release (renamed from `argus-core`; that PyPI name was taken):
|
|
66
|
+
- `argus_cortex.taxonomy` — `TargetProfile`, `TargetStyle`, `TargetCategory`.
|
|
67
|
+
- `argus_cortex.wire` — versioned wire-schema toolkit (`make_versioned_base`,
|
|
68
|
+
`check_version` with an injectable error type, `render_schema` /
|
|
69
|
+
`wire_schema`, `VersionError`).
|
|
70
|
+
- `argus_cortex.backends` — the local/remote AI-backend contract
|
|
71
|
+
(`LocalBackend`, `RemoteBackend`, `RemoteProvider`, `with_retries`
|
|
72
|
+
retrying only transient errors, `resolve_device`).
|
|
73
|
+
|
|
74
|
+
[Unreleased]: https://github.com/smk762/argus-cortex/compare/v0.3.0...HEAD
|
|
75
|
+
[0.3.0]: https://github.com/smk762/argus-cortex/compare/v0.2.0...v0.3.0
|
|
76
|
+
[0.2.0]: https://github.com/smk762/argus-cortex/compare/v0.1.0...v0.2.0
|
|
77
|
+
[0.1.0]: https://github.com/smk762/argus-cortex/releases/tag/v0.1.0
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# CLAUDE.md — argus-cortex
|
|
2
|
+
|
|
3
|
+
Guidance for AI agents working in this repo. Human-facing usage lives in [README.md](README.md); this file is the orientation an agent needs to change code safely.
|
|
4
|
+
|
|
5
|
+
## What this is
|
|
6
|
+
|
|
7
|
+
The **shared foundation library** of the Argus suite (quarry → curator → lens → forge → proof). It holds the code the stages *share* so it lives in one place instead of copy-pasted per repo: the target taxonomy, the wire-schema versioning discipline, the local/remote AI-backend contract, and the optional stateful-services layer.
|
|
8
|
+
|
|
9
|
+
Naming: the working directory is `argus-core`, but the GitHub repo, the PyPI package, and the import name are all **`argus-cortex`** (`src/argus_cortex/`). Don't "fix" the mismatch.
|
|
10
|
+
|
|
11
|
+
Because everything here is imported by the downstream stages, **a change ripples across the whole suite** — treat this repo as an API, not a leaf. Read [README.md](README.md) for the *why* and worked examples; it is the source of truth for behaviour.
|
|
12
|
+
|
|
13
|
+
## Layout
|
|
14
|
+
|
|
15
|
+
`src/argus_cortex/` (top level is pydantic-only; everything heavier is behind an extra and lazily imported):
|
|
16
|
+
|
|
17
|
+
- `taxonomy.py` — the "moat" types every stage inherits verbatim: `TargetProfile`, `TargetStyle`/`TargetCategory` literals. Keep it dependency-light; a change here retrains everyone's expectations.
|
|
18
|
+
- `wire.py` — the versioned wire-schema toolkit consumers build their contracts on: `make_versioned_base(field_name, current, majors)` (a Pydantic base that stamps + version-checks a field like `proof_version`/`manifest_version`), `check_version()` (the major-compatibility gate), `render_schema()`/`wire_schema()` (the canonical committed-schema string behind each consumer's `schema --check` CLI), and `VersionError`.
|
|
19
|
+
- `backends.py` — the AI-backend contract: `Backend`/`LocalBackend`/`RemoteBackend` (point at a hosted service via `from_host(host, port)` or `from_provider(RemoteProvider.…)`), plus `with_retries()` and `resolve_device()`. httpx is lazy, behind `[remote]`.
|
|
20
|
+
- `server.py` — the shared FastAPI/ASGI scaffolding other repos' micro-servers mount: `WriteGuard` (pure-ASGI method gate), the `constant_refuse()` (read-only/replay) and `cross_site_refuse()` (cross-origin write) predicates, `env_flag()`, and the `UNSAFE_METHODS`/`TRUTHY`/`FALSY` constants. Starlette is behind `[server]`.
|
|
21
|
+
- `xmp.py` — the suite's XMP sidecar read/merge/write (`write_xmp`/`read_xmp`/`merge_xmp`/`parse_xmp`, `XmpData`/`XmpRegions`, `XmpError`), used by lens (keywords/caption) and curator (faces, content rating). Stdlib `xml.etree` only. **Writes merge — latest wins per property, foreign properties preserved; keep that.** `None` = leave untouched, empty = remove. A DTD is refused and an unparseable sidecar raises rather than being overwritten. The `argus` namespace URI is a wire contract — don't change it.
|
|
22
|
+
- `store/` — the optional stateful-services layer, opt-in per `CORTEX_*` env var: `lineage.py` (Phase 1 Postgres DAG, `open_lineage_store()`), `vector.py` (Phase 2 Qdrant, `open_vector_store()`), `blob.py` (Phase 3 MinIO/S3, `open_blob_store()`), over `config.py`/`models.py`/`errors.py`. Each `open_*()` returns a `Null*` no-op when its URL is unset. Drivers are behind `[postgres]`/`[qdrant]`/`[s3]`.
|
|
23
|
+
|
|
24
|
+
There is **no CLI and no committed schema in this repo** — the `schema`/`schema --check` pattern is something `wire.py` *enables* for downstream repos, not something cortex runs on itself.
|
|
25
|
+
|
|
26
|
+
## Commands
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
make install # uv venv + editable install with [dev]
|
|
30
|
+
make test # uv run --no-sync pytest --tb=short -q
|
|
31
|
+
make lint # ruff check + format --check (pinned ruff 0.15.16)
|
|
32
|
+
make format # ruff format + check --fix
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Run one test: `uv run --no-sync pytest tests/test_wire.py::test_name -q`.
|
|
36
|
+
|
|
37
|
+
## Conventions & gotchas
|
|
38
|
+
|
|
39
|
+
- **This is shared code — changes ripple.** A `taxonomy.py` edit or a `wire.py`/`backends.py` semantics change lands in every stage on their next bump. Prefer additive, backward-compatible changes; if you break the wire contract, that is a **major** version (`check_version` is what refuses the old major downstream).
|
|
40
|
+
- **Keep the top-level import pydantic-only.** `import argus_cortex` must not pull in starlette/httpx/psycopg/qdrant/minio — those are optional extras, imported lazily inside the module that needs them (see the `try/except ImportError` → "install `argus-cortex[extra]`" pattern). Don't hoist an optional import to module top level.
|
|
41
|
+
- **Stores fail *open* to a no-op, not an error.** `open_*()` returns a `Null*` store when the `CORTEX_*` URL is unset, so a stage with no DB configured still runs. Preserve that; blob identity is content-addressed on sha256 (`content_key()` == `source_asset.sha256`) — storage, dedup, and lineage join share one identity.
|
|
42
|
+
- **`WriteGuard` is pure ASGI on purpose** (not `BaseHTTPMiddleware`), so it never buffers the SSE/NDJSON streams downstream servers return. Register it *before* `CORSMiddleware`. `env_flag()` warns on a mistyped protection flag rather than silently disabling it — keep that fail-safe behaviour.
|
|
43
|
+
- **Versioning is git-tag-derived** (`hatch-vcs`). Never hand-edit a version; `src/argus_cortex/_version.py` is generated (gitignored). Tag `vX.Y.Z` to release.
|
|
44
|
+
- Pydantic v2 everywhere; async tests via `asyncio_mode = auto`. Ruff pinned **0.15.16**, line-length 120, target py311.
|
|
45
|
+
|
|
46
|
+
## CI / release
|
|
47
|
+
|
|
48
|
+
CI runs via the shared [`argus-ci`](https://github.com/smk762/argus-ci) reusable workflow (ruff + pytest with the `dev` extra — which installs the real psycopg/qdrant/minio/starlette clients so each store's live driver path is exercised, not just fakes). Release publishes to PyPI (OIDC trusted publishing) on `v*` tags — **no container image, this is a library**. Scaffolded from [`argus-pkg-template`](https://github.com/smk762/argus-pkg-template); run `copier update` to pull tooling changes.
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: argus-cortex
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Shared foundation for the Argus suite: taxonomy, versioned wire-schema tooling, and the local/remote AI backend contract
|
|
5
|
+
Project-URL: Repository, https://github.com/smk762/argus-cortex
|
|
6
|
+
Author: smk762
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Requires-Dist: pydantic>=2.7
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: certifi>=2026.10.19; extra == 'dev'
|
|
20
|
+
Requires-Dist: httpx2>=2.7; extra == 'dev'
|
|
21
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
22
|
+
Requires-Dist: minio>=7.2; extra == 'dev'
|
|
23
|
+
Requires-Dist: psycopg[binary,pool]>=3.1; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: qdrant-client>=1.10; extra == 'dev'
|
|
27
|
+
Requires-Dist: ruff==0.15.16; extra == 'dev'
|
|
28
|
+
Requires-Dist: starlette>=0.37; extra == 'dev'
|
|
29
|
+
Provides-Extra: postgres
|
|
30
|
+
Requires-Dist: psycopg[binary,pool]>=3.1; extra == 'postgres'
|
|
31
|
+
Provides-Extra: qdrant
|
|
32
|
+
Requires-Dist: certifi>=2026.10.19; extra == 'qdrant'
|
|
33
|
+
Requires-Dist: qdrant-client>=1.10; extra == 'qdrant'
|
|
34
|
+
Provides-Extra: remote
|
|
35
|
+
Requires-Dist: certifi>=2026.10.19; extra == 'remote'
|
|
36
|
+
Requires-Dist: httpx>=0.27; extra == 'remote'
|
|
37
|
+
Provides-Extra: s3
|
|
38
|
+
Requires-Dist: certifi>=2026.10.19; extra == 's3'
|
|
39
|
+
Requires-Dist: minio>=7.2; extra == 's3'
|
|
40
|
+
Provides-Extra: server
|
|
41
|
+
Requires-Dist: starlette>=0.37; extra == 'server'
|
|
42
|
+
Description-Content-Type: text/markdown
|
|
43
|
+
|
|
44
|
+
# argus-cortex
|
|
45
|
+
|
|
46
|
+
Shared foundation for the Argus suite: taxonomy, versioned wire-schema tooling, and the local/remote AI backend contract.
|
|
47
|
+
|
|
48
|
+
Part of the [Argus suite](https://github.com/smk762?tab=repositories&q=argus) (quarry → curator → lens → forge → proof). This is the library the stages import so the code they *share* — the target taxonomy, the wire-schema versioning discipline, and the way they call local or hosted models — lives in one place instead of being copy-pasted per repo.
|
|
49
|
+
|
|
50
|
+
## What's here
|
|
51
|
+
|
|
52
|
+
- **`argus_cortex.taxonomy`** — `TargetProfile` / `TargetStyle` / `TargetCategory`, the "moat" types every stage inherits verbatim.
|
|
53
|
+
- **`argus_cortex.wire`** — the versioned wire-schema toolkit: `check_version()` (major-compatibility gate), `make_versioned_base()` (a Pydantic base that stamps + checks a version field like `proof_version` / `manifest_version`), and `wire_schema()` / `render_schema()` for the committed-schema `schema --check` CLI pattern.
|
|
54
|
+
- **`argus_cortex.backends`** — the AI-backend contract generalised from argus-lens: `Backend` / `LocalBackend` / `RemoteBackend` (point at a hosted service by `base_url`, `host`/`port`, or a known `RemoteProvider`), plus `with_retries()` and `resolve_device()`. httpx lives behind the `[remote]` extra.
|
|
55
|
+
- **`argus_cortex.server`** — shared write-guard + env-flag scaffolding for the suite's FastAPI micro-servers: `WriteGuard` (a pure-ASGI method-gate that never buffers streaming responses), the `constant_refuse()` / `cross_site_refuse()` predicates (read-only/replay mode and cross-site-write protection are two configs of the one guard), the `UNSAFE_METHODS` / `TRUTHY` / `FALSY` constants, and `env_flag()` (warns on a mistyped protection flag rather than silently disabling it). Starlette lives behind the `[server]` extra.
|
|
56
|
+
- **`argus_cortex.xmp`** — the suite's one XMP sidecar implementation (hoisted from argus-lens): `write_xmp()` / `read_xmp()` / `merge_xmp()` / `parse_xmp()` over an `XmpData` model covering `dc` (keywords, description), `xmp` (Rating, Label), `lr` (hierarchical keywords), `Iptc4xmpExt` (PersonInImage), `mwg-rs` face/pet regions, and `argus:ContentRating`. Writes **merge**: each property you set replaces that property (latest write wins), everything else in the sidecar — Lightroom develop settings, GPS, another stage's fields — is preserved. `None` leaves a property alone, an empty value removes it. Writes are atomic; an unparseable existing sidecar raises `XmpError` instead of being clobbered. Stdlib only.
|
|
57
|
+
- **`argus_cortex.store`** — the optional **stateful services layer** (the suite's persistent "memory"). External and opt-in — every store is configured by `CORTEX_*` env vars and no-ops when its URL is unset.
|
|
58
|
+
- **Phase 1 — Postgres lineage store.** Persists the `source_asset → caption → human_edit → dataset_membership → training_run` DAG for LoRA reproducibility, edit capture, and the feedback loop. Backed by a psycopg connection **pool** (concurrent `asyncio.to_thread` calls are safe; a dropped connection is replaced), with a `transaction()` boundary for atomic multi-step writes. `open_lineage_store()` returns a no-op store when `CORTEX_PG_URL` is unset; psycopg lives behind the `[postgres]` extra.
|
|
59
|
+
- **Phase 2 — Qdrant vector store.** Stores image + tag-set embeddings for retrieval-augmented few-shot and near-duplicate curation; put a `caption_id`/`asset_id` in a point's payload and a search hit joins straight back to the lineage. `open_vector_store()` no-ops when `CORTEX_QDRANT_URL` is unset; qdrant-client lives behind the `[qdrant]` extra. cortex stores vectors it's *handed* — computing embeddings is the caller's job.
|
|
60
|
+
- **Phase 3 — MinIO/S3 blob store.** Owns image bytes when cortex can't rely on the filesystem/Immich (exported/selected training images). Blobs are content-addressed on sha256 (`content_key()` == `source_asset.sha256`), so storage, dedup, and lineage join share one identity. `open_blob_store()` no-ops when `CORTEX_S3_ENDPOINT`/`CORTEX_S3_BUCKET` are unset; the `minio` client lives behind the `[s3]` extra.
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from argus_cortex.wire import make_versioned_base, render_schema
|
|
64
|
+
from argus_cortex.backends import RemoteBackend, RemoteProvider
|
|
65
|
+
|
|
66
|
+
# per-package versioned base (readable field name kept local, logic shared)
|
|
67
|
+
_Versioned = make_versioned_base("proof_version", "1.0", ("1",))
|
|
68
|
+
|
|
69
|
+
# reach a hosted scorer by IP/port, or a known provider
|
|
70
|
+
scorer = RemoteBackend.from_host("192.168.1.20", 9000, api_key="…")
|
|
71
|
+
nim = RemoteBackend.from_provider(RemoteProvider.NVIDIA_NIM, api_key="…")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from argus_cortex.xmp import XmpData, XmpRegion, XmpRegions, write_xmp
|
|
76
|
+
|
|
77
|
+
# lens writes captions/keywords …
|
|
78
|
+
write_xmp("pics/beach.jpg", XmpData(keywords=["beach", "sunset"], description="a beach at sunset"))
|
|
79
|
+
# … curator later adds faces + a content rating to the same sidecar; lens's fields survive
|
|
80
|
+
write_xmp(
|
|
81
|
+
"pics/beach.jpg",
|
|
82
|
+
XmpData(
|
|
83
|
+
persons=["Ada"],
|
|
84
|
+
regions=XmpRegions(width=4000, height=3000, regions=[XmpRegion(x=0.52, y=0.4, w=0.1, h=0.18, name="Ada")]),
|
|
85
|
+
content_rating="safe",
|
|
86
|
+
),
|
|
87
|
+
)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from argus_cortex.store import open_lineage_store, SourceAsset, Caption, HumanEdit
|
|
92
|
+
|
|
93
|
+
# no-op if CORTEX_PG_URL is unset; PostgresLineageStore if it's set
|
|
94
|
+
store = open_lineage_store() # reads CORTEX_* from the environment
|
|
95
|
+
store.ensure_schema() # idempotent bootstrap of the lineage tables
|
|
96
|
+
|
|
97
|
+
# one atomic unit — the asset + caption + edit commit together or not at all
|
|
98
|
+
with store.transaction():
|
|
99
|
+
asset_id = store.record_asset(SourceAsset(uri="immich://…", sha256="…"))
|
|
100
|
+
# lens emits a CaptionResult; cortex ingests it (cortex never imports lens)
|
|
101
|
+
cap_id = store.record_caption(asset_id, Caption.from_caption_result(result, profile={...}))
|
|
102
|
+
store.record_edit(cap_id, HumanEdit(edited_caption="…", editor="alice"))
|
|
103
|
+
|
|
104
|
+
# the (model_caption, edited_caption) pairs the summariser feedback loop trains on
|
|
105
|
+
pairs = store.caption_edit_pairs()
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from argus_cortex.store import open_vector_store, IMAGE_COLLECTION
|
|
110
|
+
|
|
111
|
+
# no-op if CORTEX_QDRANT_URL is unset; QdrantVectorStore if it's set
|
|
112
|
+
vec = open_vector_store()
|
|
113
|
+
vec.ensure_collection(IMAGE_COLLECTION, dim=512) # idempotent
|
|
114
|
+
|
|
115
|
+
# store an embedding you computed, tagged with its lineage id
|
|
116
|
+
vec.upsert(IMAGE_COLLECTION, point_id=cap_id, vector=embedding, payload={"caption_id": cap_id})
|
|
117
|
+
|
|
118
|
+
# retrieve similar past captions to steer the summariser (few-shot)
|
|
119
|
+
hits = vec.search(IMAGE_COLLECTION, vector=query_embedding, top_k=5)
|
|
120
|
+
similar_caption_ids = [h.payload["caption_id"] for h in hits] # join back to Postgres
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from argus_cortex.store import open_blob_store
|
|
125
|
+
|
|
126
|
+
# no-op if CORTEX_S3_ENDPOINT / CORTEX_S3_BUCKET are unset; S3BlobStore if set
|
|
127
|
+
blobs = open_blob_store()
|
|
128
|
+
blobs.ensure_bucket()
|
|
129
|
+
|
|
130
|
+
# content-addressed: the returned key IS the sha256 (== source_asset.sha256)
|
|
131
|
+
sha = blobs.put_content_addressed(image_bytes, content_type="image/jpeg")
|
|
132
|
+
data = blobs.get(sha) # bytes, or None if absent
|
|
133
|
+
link = blobs.url(sha, expires=3600) # presigned GET URL
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Install
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
uv pip install argus-cortex # core (pydantic only)
|
|
140
|
+
uv pip install "argus-cortex[remote]" # + httpx for RemoteBackend
|
|
141
|
+
uv pip install "argus-cortex[server]" # + starlette for argus_cortex.server (WriteGuard, env_flag)
|
|
142
|
+
uv pip install "argus-cortex[postgres]" # + psycopg for the lineage store
|
|
143
|
+
uv pip install "argus-cortex[qdrant]" # + qdrant-client for the vector store
|
|
144
|
+
uv pip install "argus-cortex[s3]" # + minio for the blob store
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The stateful services are configured via `CORTEX_*` env vars — see [`.env.example`](.env.example). Each is optional: leave a URL unset and that feature degrades to a no-op.
|
|
148
|
+
|
|
149
|
+
## Develop
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
make install # venv + editable install with the "dev" extras
|
|
153
|
+
make test
|
|
154
|
+
make lint
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## CI / Release
|
|
158
|
+
|
|
159
|
+
- **CI** runs via the shared [`argus-ci`](https://github.com/smk762/argus-ci) reusable workflow.
|
|
160
|
+
- **Release** publishes to PyPI (OIDC trusted publishing) on `v*` tags. No container image — this is a library.
|
|
161
|
+
- Versioning is derived from git tags via `hatch-vcs` — tag `vX.Y.Z` to cut a release.
|
|
162
|
+
|
|
163
|
+
This repo was scaffolded from [`argus-pkg-template`](https://github.com/smk762/argus-pkg-template).
|
|
164
|
+
Run `copier update` to pull template changes (CI, release, tooling).
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# argus-cortex
|
|
2
|
+
|
|
3
|
+
Shared foundation for the Argus suite: taxonomy, versioned wire-schema tooling, and the local/remote AI backend contract.
|
|
4
|
+
|
|
5
|
+
Part of the [Argus suite](https://github.com/smk762?tab=repositories&q=argus) (quarry → curator → lens → forge → proof). This is the library the stages import so the code they *share* — the target taxonomy, the wire-schema versioning discipline, and the way they call local or hosted models — lives in one place instead of being copy-pasted per repo.
|
|
6
|
+
|
|
7
|
+
## What's here
|
|
8
|
+
|
|
9
|
+
- **`argus_cortex.taxonomy`** — `TargetProfile` / `TargetStyle` / `TargetCategory`, the "moat" types every stage inherits verbatim.
|
|
10
|
+
- **`argus_cortex.wire`** — the versioned wire-schema toolkit: `check_version()` (major-compatibility gate), `make_versioned_base()` (a Pydantic base that stamps + checks a version field like `proof_version` / `manifest_version`), and `wire_schema()` / `render_schema()` for the committed-schema `schema --check` CLI pattern.
|
|
11
|
+
- **`argus_cortex.backends`** — the AI-backend contract generalised from argus-lens: `Backend` / `LocalBackend` / `RemoteBackend` (point at a hosted service by `base_url`, `host`/`port`, or a known `RemoteProvider`), plus `with_retries()` and `resolve_device()`. httpx lives behind the `[remote]` extra.
|
|
12
|
+
- **`argus_cortex.server`** — shared write-guard + env-flag scaffolding for the suite's FastAPI micro-servers: `WriteGuard` (a pure-ASGI method-gate that never buffers streaming responses), the `constant_refuse()` / `cross_site_refuse()` predicates (read-only/replay mode and cross-site-write protection are two configs of the one guard), the `UNSAFE_METHODS` / `TRUTHY` / `FALSY` constants, and `env_flag()` (warns on a mistyped protection flag rather than silently disabling it). Starlette lives behind the `[server]` extra.
|
|
13
|
+
- **`argus_cortex.xmp`** — the suite's one XMP sidecar implementation (hoisted from argus-lens): `write_xmp()` / `read_xmp()` / `merge_xmp()` / `parse_xmp()` over an `XmpData` model covering `dc` (keywords, description), `xmp` (Rating, Label), `lr` (hierarchical keywords), `Iptc4xmpExt` (PersonInImage), `mwg-rs` face/pet regions, and `argus:ContentRating`. Writes **merge**: each property you set replaces that property (latest write wins), everything else in the sidecar — Lightroom develop settings, GPS, another stage's fields — is preserved. `None` leaves a property alone, an empty value removes it. Writes are atomic; an unparseable existing sidecar raises `XmpError` instead of being clobbered. Stdlib only.
|
|
14
|
+
- **`argus_cortex.store`** — the optional **stateful services layer** (the suite's persistent "memory"). External and opt-in — every store is configured by `CORTEX_*` env vars and no-ops when its URL is unset.
|
|
15
|
+
- **Phase 1 — Postgres lineage store.** Persists the `source_asset → caption → human_edit → dataset_membership → training_run` DAG for LoRA reproducibility, edit capture, and the feedback loop. Backed by a psycopg connection **pool** (concurrent `asyncio.to_thread` calls are safe; a dropped connection is replaced), with a `transaction()` boundary for atomic multi-step writes. `open_lineage_store()` returns a no-op store when `CORTEX_PG_URL` is unset; psycopg lives behind the `[postgres]` extra.
|
|
16
|
+
- **Phase 2 — Qdrant vector store.** Stores image + tag-set embeddings for retrieval-augmented few-shot and near-duplicate curation; put a `caption_id`/`asset_id` in a point's payload and a search hit joins straight back to the lineage. `open_vector_store()` no-ops when `CORTEX_QDRANT_URL` is unset; qdrant-client lives behind the `[qdrant]` extra. cortex stores vectors it's *handed* — computing embeddings is the caller's job.
|
|
17
|
+
- **Phase 3 — MinIO/S3 blob store.** Owns image bytes when cortex can't rely on the filesystem/Immich (exported/selected training images). Blobs are content-addressed on sha256 (`content_key()` == `source_asset.sha256`), so storage, dedup, and lineage join share one identity. `open_blob_store()` no-ops when `CORTEX_S3_ENDPOINT`/`CORTEX_S3_BUCKET` are unset; the `minio` client lives behind the `[s3]` extra.
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from argus_cortex.wire import make_versioned_base, render_schema
|
|
21
|
+
from argus_cortex.backends import RemoteBackend, RemoteProvider
|
|
22
|
+
|
|
23
|
+
# per-package versioned base (readable field name kept local, logic shared)
|
|
24
|
+
_Versioned = make_versioned_base("proof_version", "1.0", ("1",))
|
|
25
|
+
|
|
26
|
+
# reach a hosted scorer by IP/port, or a known provider
|
|
27
|
+
scorer = RemoteBackend.from_host("192.168.1.20", 9000, api_key="…")
|
|
28
|
+
nim = RemoteBackend.from_provider(RemoteProvider.NVIDIA_NIM, api_key="…")
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from argus_cortex.xmp import XmpData, XmpRegion, XmpRegions, write_xmp
|
|
33
|
+
|
|
34
|
+
# lens writes captions/keywords …
|
|
35
|
+
write_xmp("pics/beach.jpg", XmpData(keywords=["beach", "sunset"], description="a beach at sunset"))
|
|
36
|
+
# … curator later adds faces + a content rating to the same sidecar; lens's fields survive
|
|
37
|
+
write_xmp(
|
|
38
|
+
"pics/beach.jpg",
|
|
39
|
+
XmpData(
|
|
40
|
+
persons=["Ada"],
|
|
41
|
+
regions=XmpRegions(width=4000, height=3000, regions=[XmpRegion(x=0.52, y=0.4, w=0.1, h=0.18, name="Ada")]),
|
|
42
|
+
content_rating="safe",
|
|
43
|
+
),
|
|
44
|
+
)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from argus_cortex.store import open_lineage_store, SourceAsset, Caption, HumanEdit
|
|
49
|
+
|
|
50
|
+
# no-op if CORTEX_PG_URL is unset; PostgresLineageStore if it's set
|
|
51
|
+
store = open_lineage_store() # reads CORTEX_* from the environment
|
|
52
|
+
store.ensure_schema() # idempotent bootstrap of the lineage tables
|
|
53
|
+
|
|
54
|
+
# one atomic unit — the asset + caption + edit commit together or not at all
|
|
55
|
+
with store.transaction():
|
|
56
|
+
asset_id = store.record_asset(SourceAsset(uri="immich://…", sha256="…"))
|
|
57
|
+
# lens emits a CaptionResult; cortex ingests it (cortex never imports lens)
|
|
58
|
+
cap_id = store.record_caption(asset_id, Caption.from_caption_result(result, profile={...}))
|
|
59
|
+
store.record_edit(cap_id, HumanEdit(edited_caption="…", editor="alice"))
|
|
60
|
+
|
|
61
|
+
# the (model_caption, edited_caption) pairs the summariser feedback loop trains on
|
|
62
|
+
pairs = store.caption_edit_pairs()
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from argus_cortex.store import open_vector_store, IMAGE_COLLECTION
|
|
67
|
+
|
|
68
|
+
# no-op if CORTEX_QDRANT_URL is unset; QdrantVectorStore if it's set
|
|
69
|
+
vec = open_vector_store()
|
|
70
|
+
vec.ensure_collection(IMAGE_COLLECTION, dim=512) # idempotent
|
|
71
|
+
|
|
72
|
+
# store an embedding you computed, tagged with its lineage id
|
|
73
|
+
vec.upsert(IMAGE_COLLECTION, point_id=cap_id, vector=embedding, payload={"caption_id": cap_id})
|
|
74
|
+
|
|
75
|
+
# retrieve similar past captions to steer the summariser (few-shot)
|
|
76
|
+
hits = vec.search(IMAGE_COLLECTION, vector=query_embedding, top_k=5)
|
|
77
|
+
similar_caption_ids = [h.payload["caption_id"] for h in hits] # join back to Postgres
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from argus_cortex.store import open_blob_store
|
|
82
|
+
|
|
83
|
+
# no-op if CORTEX_S3_ENDPOINT / CORTEX_S3_BUCKET are unset; S3BlobStore if set
|
|
84
|
+
blobs = open_blob_store()
|
|
85
|
+
blobs.ensure_bucket()
|
|
86
|
+
|
|
87
|
+
# content-addressed: the returned key IS the sha256 (== source_asset.sha256)
|
|
88
|
+
sha = blobs.put_content_addressed(image_bytes, content_type="image/jpeg")
|
|
89
|
+
data = blobs.get(sha) # bytes, or None if absent
|
|
90
|
+
link = blobs.url(sha, expires=3600) # presigned GET URL
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Install
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
uv pip install argus-cortex # core (pydantic only)
|
|
97
|
+
uv pip install "argus-cortex[remote]" # + httpx for RemoteBackend
|
|
98
|
+
uv pip install "argus-cortex[server]" # + starlette for argus_cortex.server (WriteGuard, env_flag)
|
|
99
|
+
uv pip install "argus-cortex[postgres]" # + psycopg for the lineage store
|
|
100
|
+
uv pip install "argus-cortex[qdrant]" # + qdrant-client for the vector store
|
|
101
|
+
uv pip install "argus-cortex[s3]" # + minio for the blob store
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The stateful services are configured via `CORTEX_*` env vars — see [`.env.example`](.env.example). Each is optional: leave a URL unset and that feature degrades to a no-op.
|
|
105
|
+
|
|
106
|
+
## Develop
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
make install # venv + editable install with the "dev" extras
|
|
110
|
+
make test
|
|
111
|
+
make lint
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## CI / Release
|
|
115
|
+
|
|
116
|
+
- **CI** runs via the shared [`argus-ci`](https://github.com/smk762/argus-ci) reusable workflow.
|
|
117
|
+
- **Release** publishes to PyPI (OIDC trusted publishing) on `v*` tags. No container image — this is a library.
|
|
118
|
+
- Versioning is derived from git tags via `hatch-vcs` — tag `vX.Y.Z` to cut a release.
|
|
119
|
+
|
|
120
|
+
This repo was scaffolded from [`argus-pkg-template`](https://github.com/smk762/argus-pkg-template).
|
|
121
|
+
Run `copier update` to pull template changes (CI, release, tooling).
|
|
@@ -24,8 +24,29 @@ dependencies = [
|
|
|
24
24
|
]
|
|
25
25
|
|
|
26
26
|
[project.optional-dependencies]
|
|
27
|
-
remote = ["httpx>=0.27"]
|
|
28
|
-
|
|
27
|
+
remote = ["httpx>=0.27", "certifi>=2026.10.19"]
|
|
28
|
+
postgres = ["psycopg[binary,pool]>=3.1"]
|
|
29
|
+
qdrant = ["qdrant-client>=1.10", "certifi>=2026.10.19"]
|
|
30
|
+
s3 = ["minio>=7.2", "certifi>=2026.10.19"]
|
|
31
|
+
# The shared ASGI write-guard + env-flag scaffolding for the suite's servers
|
|
32
|
+
# (argus_cortex.server). Starlette ships with FastAPI, so consumers already have
|
|
33
|
+
# it via their own [server] extra.
|
|
34
|
+
server = ["starlette>=0.37"]
|
|
35
|
+
# The store drivers are in dev so each store's real client path (pool/enum/client
|
|
36
|
+
# construction, error wrapping) is exercised in CI, not just via fakes.
|
|
37
|
+
# starlette + httpx power the pure-ASGI guard tests (Starlette TestClient).
|
|
38
|
+
dev = [
|
|
39
|
+
"pytest>=8.0",
|
|
40
|
+
"pytest-asyncio>=0.23",
|
|
41
|
+
"ruff==0.15.16",
|
|
42
|
+
"psycopg[binary,pool]>=3.1",
|
|
43
|
+
"qdrant-client>=1.10",
|
|
44
|
+
"minio>=7.2",
|
|
45
|
+
"starlette>=0.37",
|
|
46
|
+
"httpx>=0.27",
|
|
47
|
+
"httpx2>=2.7",
|
|
48
|
+
"certifi>=2026.10.19",
|
|
49
|
+
]
|
|
29
50
|
|
|
30
51
|
[project.urls]
|
|
31
52
|
Repository = "https://github.com/smk762/argus-cortex"
|
|
@@ -23,6 +23,30 @@ from argus_cortex.backends import (
|
|
|
23
23
|
resolve_device,
|
|
24
24
|
with_retries,
|
|
25
25
|
)
|
|
26
|
+
from argus_cortex.store import (
|
|
27
|
+
IMAGE_COLLECTION,
|
|
28
|
+
TAGSET_COLLECTION,
|
|
29
|
+
BlobStore,
|
|
30
|
+
Caption,
|
|
31
|
+
HumanEdit,
|
|
32
|
+
LineageStore,
|
|
33
|
+
NullBlobStore,
|
|
34
|
+
NullLineageStore,
|
|
35
|
+
NullVectorStore,
|
|
36
|
+
PostgresLineageStore,
|
|
37
|
+
QdrantVectorStore,
|
|
38
|
+
S3BlobStore,
|
|
39
|
+
SourceAsset,
|
|
40
|
+
StoreConfig,
|
|
41
|
+
StoreError,
|
|
42
|
+
TrainingRun,
|
|
43
|
+
VectorHit,
|
|
44
|
+
VectorStore,
|
|
45
|
+
content_key,
|
|
46
|
+
open_blob_store,
|
|
47
|
+
open_lineage_store,
|
|
48
|
+
open_vector_store,
|
|
49
|
+
)
|
|
26
50
|
from argus_cortex.taxonomy import TargetCategory, TargetProfile, TargetStyle
|
|
27
51
|
from argus_cortex.wire import (
|
|
28
52
|
VersionError,
|
|
@@ -32,6 +56,18 @@ from argus_cortex.wire import (
|
|
|
32
56
|
schema_major,
|
|
33
57
|
wire_schema,
|
|
34
58
|
)
|
|
59
|
+
from argus_cortex.xmp import (
|
|
60
|
+
XmpData,
|
|
61
|
+
XmpError,
|
|
62
|
+
XmpRegion,
|
|
63
|
+
XmpRegions,
|
|
64
|
+
merge_xmp,
|
|
65
|
+
parse_xmp,
|
|
66
|
+
read_xmp,
|
|
67
|
+
render_xmp,
|
|
68
|
+
sidecar_path,
|
|
69
|
+
write_xmp,
|
|
70
|
+
)
|
|
35
71
|
|
|
36
72
|
__all__ = [
|
|
37
73
|
"__version__",
|
|
@@ -46,6 +82,17 @@ __all__ = [
|
|
|
46
82
|
"render_schema",
|
|
47
83
|
"schema_major",
|
|
48
84
|
"wire_schema",
|
|
85
|
+
# xmp sidecars
|
|
86
|
+
"XmpData",
|
|
87
|
+
"XmpError",
|
|
88
|
+
"XmpRegion",
|
|
89
|
+
"XmpRegions",
|
|
90
|
+
"merge_xmp",
|
|
91
|
+
"parse_xmp",
|
|
92
|
+
"read_xmp",
|
|
93
|
+
"render_xmp",
|
|
94
|
+
"sidecar_path",
|
|
95
|
+
"write_xmp",
|
|
49
96
|
# backends
|
|
50
97
|
"Backend",
|
|
51
98
|
"BackendError",
|
|
@@ -55,4 +102,27 @@ __all__ = [
|
|
|
55
102
|
"RemoteProvider",
|
|
56
103
|
"resolve_device",
|
|
57
104
|
"with_retries",
|
|
105
|
+
# store (optional stateful services layer)
|
|
106
|
+
"StoreConfig",
|
|
107
|
+
"StoreError",
|
|
108
|
+
"LineageStore",
|
|
109
|
+
"NullLineageStore",
|
|
110
|
+
"PostgresLineageStore",
|
|
111
|
+
"open_lineage_store",
|
|
112
|
+
"Caption",
|
|
113
|
+
"HumanEdit",
|
|
114
|
+
"SourceAsset",
|
|
115
|
+
"TrainingRun",
|
|
116
|
+
"VectorStore",
|
|
117
|
+
"NullVectorStore",
|
|
118
|
+
"QdrantVectorStore",
|
|
119
|
+
"VectorHit",
|
|
120
|
+
"open_vector_store",
|
|
121
|
+
"IMAGE_COLLECTION",
|
|
122
|
+
"TAGSET_COLLECTION",
|
|
123
|
+
"BlobStore",
|
|
124
|
+
"NullBlobStore",
|
|
125
|
+
"S3BlobStore",
|
|
126
|
+
"open_blob_store",
|
|
127
|
+
"content_key",
|
|
58
128
|
]
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.
|
|
22
|
-
__version_tuple__ = version_tuple = (0,
|
|
21
|
+
__version__ = version = '0.3.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 3, 0)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|