Skip to content

feat: aignostics-sdk v2 — consolidated integration branch [PYSDK-133]#661

Draft
ari-nz wants to merge 39 commits into
mainfrom
feat/PYSDK-133/integration
Draft

feat: aignostics-sdk v2 — consolidated integration branch [PYSDK-133]#661
ari-nz wants to merge 39 commits into
mainfrom
feat/PYSDK-133/integration

Conversation

@ari-nz
Copy link
Copy Markdown
Collaborator

@ari-nz ari-nz commented May 28, 2026

Purpose

This is a wiring-verification branch that merges all 9 PYSDK-133 feature branches in sequence, so the overall workspace layout can be inspected end-to-end. It is NOT the final merge path — the individual phase PRs will be reviewed separately and merged one-by-one into `feat/PYSDK-133/python-sdk-slim`. This integration branch will be rebased/recreated once all phases have landed.

Branches merged (in order)

  1. `feat/PYSDK-134/workspace-scaffolding` — uv workspace, two package stubs
  2. `feat/PYSDK-135/source-migration` — git mv of source into packages/
  3. `feat/PYSDK-136/import-rewrite` — all imports rewritten to `aignostics_sdk.*`
  4. `feat/PYSDK-137/slim-cli` — `packages/aignostics-sdk/src/aignostics_sdk/cli.py`
  5. `feat/PYSDK-141/tests` — `slim` marker + smoke tests
  6. `feat/PYSDK-138/dependency-split` — slim vs full dep split
  7. `feat/PYSDK-139/tooling-updates` — noxfile, pyrightconfig, coverage paths
  8. `feat/PYSDK-140/cicd-pipeline` — dual-package publish workflow
  9. `feat/PYSDK-142/docs-and-migration` — README, docs, migration guide

Conflict resolutions

  • pyrightconfig.json (PYSDK-136 vs PYSDK-139): kept union — removed stale `src/aignostics/*` paths, kept `examples/**` and PYSDK-139's `extraPaths`
  • noxfile.py (PYSDK-139 vs PYSDK-140): kept both sets of changes, took PYSDK-140's more descriptive docstring for `dist` session
  • packages/aignostics-sdk/pyproject.toml (PYSDK-138 vs PYSDK-142): kept PYSDK-138's slim dep list (correct), incorporated PYSDK-142's CVE comments where applicable; PYSDK-142 was branched from PYSDK-134 so it had the full (pre-split) dep list
  • packages/aignostics/pyproject.toml (PYSDK-138 vs PYSDK-142): kept PYSDK-138's real heavy deps over PYSDK-142's "Phase 5 placeholder" comment; added PYSDK-142's CVE comments for jupyter/marimo optional deps

Agent-added fixup commits

Two commits were added on top of the 9 merge commits to make the workspace pass `uv build` and `make lint`. These represent known issues that individual phase PRs will need to address properly:

`fix(packaging): resolve LICENSE/README paths for hatchling build isolation`

Hatchling cannot follow `../../LICENSE` paths outside the package root during build isolation. Workaround: copied `LICENSE` and `README.md` into each package dir and updated references. The intended solution (a hatchling build hook, or accepted duplication) should be finalised in PYSDK-134 or PYSDK-138.

`fix(tooling): suppress new ruff/mypy/pyright issues introduced by workspace migration`

Three sub-fixes:

  • PLW0717/RUF075 ruff ignores: ruff was bumped 0.15.12 → 0.15.14 by the dep changes; these two rules are new and flag pre-existing code patterns. PYSDK-139 (tooling) is the natural home for these suppressions when individual PRs land.
  • mypy `follow_imports = "skip"` for `aignx.*`: the aignx codegen symlink lives inside `packages/aignostics-sdk/src/` (as a symlink to `codegen/out/aignx`), so mypy follows it and sees incompatible types when checking platform code. On main this was invisible because `src/aignostics/` (the old path) didn't contain the symlink. Whether aignx belongs inside the package source tree is a design question for the codegen migration (planned for a later phase).
  • pyright `/aignx/` exclude: same root cause as the mypy fix.

Validation results

Check Result
`uv sync --all-extras` ✅ resolves (419 packages)
`uv build --package aignostics-sdk` ✅ `aignostics_sdk-1.4.0-py3-none-any.whl`
`uv build --package aignostics` ✅ `aignostics-1.4.0-py3-none-any.whl`
`uv run --package aignostics-sdk aignostics-sdk --help` ✅ user + sdk commands shown
`uv run pytest tests/ --collect-only` ✅ 906 tests collected
`make lint` (ruff + pyright + mypy) ✅ all pass
Stale `aignostics.platform/utils` imports ✅ none (the `aignostics.constants` matches are correct — that's `packages/aignostics/`'s own namespace)

Note for reviewers

Do not merge this PR directly. Review and approve the 9 individual phase PRs targeting `feat/PYSDK-133/python-sdk-slim` instead. This branch exists solely to show the full picture assembled together.

ari-nz added 26 commits May 28, 2026 12:19
…ease

- Replace single uv publish step with sequential build+publish for aignostics-sdk (first) then aignostics (second) to respect dependency order
- Add pre-publish lockstep version consistency check that fails fast if the two packages diverge
- Add informational smoke_test_slim job (continue-on-error) that installs aignostics-sdk from PyPI after publish and asserts the CLI works, the slim import succeeds, and heavy deps (openslide) are absent
- Update noxfile dist() session to build both packages into dist/ so make dist stays consistent with the new publish flow
- GitHub release glob ./dist/* already covers both wheels since both are built into dist/
Adds documentation for the v2 dual-package distribution that splits the
SDK into aignostics-sdk (slim API client) and aignostics (full SDK),
including migration guide for users upgrading from v1.
…tics

Move heavy/domain-specific dependencies from aignostics-sdk to aignostics,
leaving only platform+utils runtime dependencies in the slim package.

Slim (aignostics-sdk): platform auth, HTTP, JWT, logging, MCP, nicegui,
fastapi, pydantic, typer, sentry, retry, jsonschema/jsf, tqdm — 28 deps.
Heavy (aignostics): WSI (openslide, wsidicom, pydicom), cloud storage
(boto3, google-cloud-storage), data (duckdb, pandas, fastparquet/pyarrow),
DICOM validation, IDC index, shapely, procrastinate, html-sanitizer,
humanize, pyyaml, packaging, python-dateutil, defusedxml — 30 deps.
CVE transitive overrides split accordingly (slim vs heavy deps).

Deviations from task guidance (import-trace is authoritative):
- tqdm kept in slim: imported by platform/_utils.py for upload progress
- humanize/pyyaml/packaging/python-dateutil/jsf moved to heavy (or left in
  slim as appropriate): not imported by platform/ or utils/ directly
- jsf kept in slim: used for JSON schema generation (jsonschema companion)
Update mypy, pyright, coverage, pytest, and dist build configs to target
packages/aignostics-sdk/src and packages/aignostics/src instead of the
legacy src/ layout introduced by PYSDK-134 workspace scaffolding.
Also fix four Pylance type-narrowing errors in noxfile.py latexmk version
detection (session.error() is not NoReturn in nox's type stubs).
- platform/ and utils/ → packages/aignostics-sdk/src/aignostics_sdk/
- application/, wsi/, dataset/, bucket/, qupath/, notebook/, gui/, system/, third_party/, cli.py → packages/aignostics/src/aignostics/
- constants.py split: INTERNAL_ORGS → aignostics-sdk/constants.py; remaining constants → aignostics/constants.py
- Created symlink packages/aignostics-sdk/src/aignx → codegen/out/aignx to bundle codegen into slim wheel
- Updated packages/aignostics-sdk/pyproject.toml: packages = [\"src/aignostics_sdk\", \"src/aignx\"]
- Updated packages/aignostics-sdk/src/aignostics_sdk/__init__.py with real module docstring
- Moved packages/aignostics/src/aignostics/__init__.py from src/aignostics/ (preserving full content)

Note: imports are intentionally broken at this stage. Import rewrites (aignostics.* → aignostics_sdk.* for slim-module consumers) are deferred to PYSDK-136.
- platform/ and utils/ → packages/aignostics-sdk/src/aignostics_sdk/
- application/, wsi/, dataset/, bucket/, qupath/, notebook/, gui/, system/, third_party/, cli.py → packages/aignostics/src/aignostics/
- constants.py split: INTERNAL_ORGS → aignostics-sdk/constants.py; remaining constants → aignostics/constants.py
- Created symlink packages/aignostics-sdk/src/aignx → codegen/out/aignx to bundle codegen into slim wheel
- Updated packages/aignostics-sdk/pyproject.toml: packages = ["src/aignostics_sdk", "src/aignx"]
- Updated packages/aignostics-sdk/src/aignostics_sdk/__init__.py with real module docstring
- Moved packages/aignostics/src/aignostics/__init__.py from src/aignostics/ (preserving full content)

Note: imports are intentionally broken at this stage. Import rewrites
(aignostics.* → aignostics_sdk.* for slim-module consumers) are deferred to PYSDK-136.
…sdk.*

- Mechanically rewrites 206 import statements across 91 files in
  packages/aignostics-sdk/, packages/aignostics/, and tests/ so that
  platform and utils references point at the new aignostics_sdk package
- Fixes two classes of non-obvious imports missed by naive sed:
    * from ..utils import  (relative two-level imports in heavy modules)
    * from .utils.boot import boot  (relative import in aignostics __init__)
- Restores aignostics.constants imports where heavy constants
  (WINDOW_TITLE, HETA_APPLICATION_ID, etc.) live, since aignostics_sdk.constants
  only carries the slim subset (INTERNAL_ORGS)
- Patches utils/_constants.py to preserve backward compat:
    * __project_name__ = "aignostics" (keeps ~/.aignostics token cache
      and AIGNOSTICS_* env-var prefix)
    * _package_name derives from __name__.split(".")[0] = "aignostics_sdk"
    * All three importlib.metadata calls now use _package_name so they
      resolve against the correct installed distribution
- 900 tests collect successfully after the rewrite (0 collection errors)
- PYSDK-137 (slim CLI) and PYSDK-141 (tests) build on top of this change
…rce migration

- Add packages/aignostics/src/aignostics/wsi/_pydicom_handler.py and
  packages/aignostics/src/aignostics/notebook/_notebook.py to ignore list
  (both were previously ignored under src/ but moved in PYSDK-135)
- Exclude examples/ from pyright (pre-existing errors from before the split)
- Remove stale extraPaths pointing at old src/ layout
Creates packages/aignostics-sdk/src/aignostics_sdk/cli.py with a
trimmed Typer app exposing only the platform CLI commands (user, sdk).

Deliberately avoids calling prepare_cli() to prevent auto-discovery
of heavy domain modules (application, wsi, dataset, bucket, qupath,
system) via locate_implementations(typer.Typer). Instead, applies
epilog and no-args-is-help behaviour directly by calling the private
helpers _add_epilog_recursively and _no_args_is_help_recursively.
…plit [PYSDK-141]

- Add `slim` pytest marker to pyproject.toml for aignostics-sdk package tests
- Create tests/aignostics_sdk/__init__.py and smoke_test.py
- 5 smoke tests verify importability of aignostics_sdk.platform.Client,
  aignostics_sdk.utils.{BaseService,Health}, aignx.codegen.exceptions.ApiException,
  __project_name__ backward-compat constant, and __version__ availability
- 1 xfail test documents missing aignostics_sdk.cli module (pending PYSDK-137)
- No stale `from aignostics.platform` / `from aignostics.utils` imports in tests/
- Collection: 904 tests collected (0 collection errors)
- Pre-existing failures in base branch (stale patch() paths from PYSDK-136
  import rewrite) are not introduced by this PR; they will be tracked separately
…ation

Hatchling builds packages in an isolated environment and cannot follow
relative paths outside the package root (e.g. ../../LICENSE). Copy
LICENSE and README.md into each package directory and update pyproject.toml
references from ../../LICENSE/README.md to ./LICENSE/README.md.

This unblocks `uv build --package` for the integration branch validation.
…kspace migration

- ruff: ignore PLW0717 (try clause too many statements) and RUF075 (fallible
  context manager) — introduced in ruff 0.15.13 by the dep version bump in
  PYSDK-138; pre-existing code patterns, to be addressed in individual PRs
- mypy: exclude packages/aignostics-sdk/src/aignx (codegen symlink) and add
  follow_imports=skip override for aignx.* to prevent imported codegen types
  from causing errors in caller code; aignx is not part of the SDK source
- pyright: add **/aignx/** to exclude list so the codegen symlink is not
  type-checked by pyright

These are integration-branch compatibility fixes, not feature changes.
Copilot AI review requested due to automatic review settings May 28, 2026 12:32
@ari-nz ari-nz added the skip:test:long_running Skip long-running tests (≥5min) label May 28, 2026
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
E Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

ari-nz added 2 commits May 28, 2026 18:57
…CLI entry points

Changes:
- Move packages/aignostics-sdk/src/aignostics_sdk/ to src/aignostics_sdk/
  and add symlink at packages/aignostics-sdk/src/aignostics_sdk -> ../../../src/aignostics_sdk
- Update _constants.py: rename __project_name__ to "aignostics-sdk" (PYSDK-136),
  introduce ENV_PREFIX ("AIGNOSTICS_SDK") for env var names to avoid hyphens,
  add _package_name for importlib.metadata calls
- Update _di.py: scan both "aignostics_sdk" and "aignostics" packages for DI
  discovery so heavy-module CLIs/services are still found
- Update _cli.py: prevent duplicate Typer registration in prepare_cli by
  checking already-registered sub-typers before adding discovered ones
- Trim utils __init__.py: remove _nav/_gui/_mcp/_di from __all__ (they
  belong to the heavy package), keep them accessible for backward compat;
  re-export ENV_PREFIX, locate_subclasses, NavItem, mcp_run etc. since
  the heavy package imports them
- Add [project.entry-points."aignostics.cli"] to aignostics-sdk pyproject.toml
  registering user and sdk sub-commands
- Update aignostics/cli.py to mount slim commands via entry-point discovery
  instead of direct imports
- Update aignostics cli.py: change lazy _gui/_mcp imports to public API
- Update all heavy-package settings files (_constants.py users) to use
  ENV_PREFIX instead of __project_name__.upper() (which would produce
  invalid "AIGNOSTICS-SDK" env var names with hyphens)
- Update GUI/nav imports across heavy package to use public aignostics_sdk.utils API
- Update slim cli.py display strings to say "aignostics-sdk"
Copilot AI review requested due to automatic review settings May 28, 2026 17:53
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

ari-nz added 2 commits May 28, 2026 20:50
…PREFIX to AIGNOSTICS

- Revert ENV_PREFIX from "AIGNOSTICS_SDK" back to "AIGNOSTICS" for
  backward compatibility with all existing env var consumers
- Move _nav.py, _gui.py, _mcp.py, _di.py from slim aignostics_sdk.utils
  to heavy aignostics.utils to keep heavy dependencies out of the slim
  package
- Create aignostics/utils/__init__.py that re-exports all slim symbols
  plus the four moved modules (locate_implementations, locate_subclasses,
  gui_run, BasePageBuilder, GUILocalFilePicker, mcp_run, etc.)
- Update all heavy-package consumers to import heavy symbols from
  aignostics.utils instead of aignostics_sdk.utils
- Fix prepare_cli in slim to lazily import locate_implementations from
  aignostics.utils to avoid circular dependency at module load time
- git mv application and system from packages/aignostics into
  packages/aignostics-sdk/src/aignostics_sdk/
- Fix all internal imports: aignostics.{platform,utils,constants} →
  aignostics_sdk.{platform,utils,constants}; aignostics.system →
  aignostics_sdk.system
- Make heavy-only imports lazy (bucket, wsi, gui.frame, utils.BasePageBuilder,
  utils.GUILocalFilePicker, utils.gui_run, utils.locate_subclasses) so the
  slim package imports cleanly without the full aignostics package installed
- Add HETA_APPLICATION_ID, TEST_APP_APPLICATION_ID,
  WSI_SUPPORTED_FILE_EXTENSIONS, WSI_SUPPORTED_FILE_EXTENSIONS_TEST_APP,
  and API_VERSIONS to aignostics_sdk.constants
- Create re-export shims in packages/aignostics/src/aignostics/application
  and system so existing aignostics.application / aignostics.system
  import paths continue to work
- Register application and system as aignostics.cli entry points in
  packages/aignostics-sdk/pyproject.toml
- Wire application_cli and system_cli into aignostics_sdk.cli
- Update tests: fix module-level imports and mock patch paths from
  aignostics.application.* / aignostics.system.* →
  aignostics_sdk.application.* / aignostics_sdk.system.*
- Remove xfail from smoke_test for test_slim_cli_entry_point (now passes)
  and add application/system assertions
Copilot AI review requested due to automatic review settings May 28, 2026 19:53
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 139 out of 224 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

packages/aignostics/src/aignostics/utils/init.py:108

  • find_spec("sentry") checks for a top-level package named sentry, which does not exist on PyPI — the Sentry SDK distributes as sentry_sdk. As a result, SentrySettings is never re-exported (and the corresponding block in the slim aignostics_sdk/utils/__init__.py is also dead code). This should probably be find_spec("sentry_sdk").

Comment on lines +15 to +17
from aignostics_sdk.utils._constants import __version__ # noqa: E402

API_VERSIONS: dict[str, str] = {"v1": __version__}
Copilot AI review requested due to automatic review settings May 28, 2026 20:16
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 141 out of 224 changed files in this pull request and generated 2 comments.

mock.patch.object(Service, "_get_public_ipv4", return_value=None),
mock.patch.object(Service, "_collect_all_settings", return_value={}),
mock.patch("aignostics.system._service.locate_subclasses", return_value=[]),
mock.patch("aignostics.utils.locate_subclasses", return_value=[]),
Comment on lines +78 to +81
if find_spec("sentry"):
from ._sentry import SentrySettings

__all__ += ["SentrySettings"]
ari-nz added 6 commits May 29, 2026 17:41
Startup time: platform import 3400ms→477ms, app._cli 4100ms→60ms.

Lazy-loading (platform/__init__.py, application/__init__.py, _client.py):
- PEP 562 __getattr__ on platform and application __init__ modules so
  importing the package loads 0 codegen modules at startup
- _client.py defers ApiClient, _AuthenticatedApi, resource imports into
  get_api_client() and method bodies
- application/_cli.py: _Service() lazy factory, RunState/DownloadProgress
  deferred to function scope; _utils.py TYPE_CHECKING for annotation-only
  platform types, runtime lazy imports inside functions
- All annotation-only codegen imports moved to TYPE_CHECKING blocks with
  from __future__ import annotations across platform/ and application/

Bug fixes (downstream feedback):
- Fix aignostics.qupath conditional import: guard now checks
  find_spec("aignostics.qupath") to prevent crash in slim-only envs
  with ijson installed
- Rename aignx.codegen → aignostics_sdk._codegen (eliminates namespace
  collision with aignx-cora-client / aignx-demeter-client); exclude
  _codegen from ruff/mypy/pyright and coverage
- ENV_PREFIX = "AIGNOSTICS" kept for backward compat; __project_name__
  correctly set to "aignostics-sdk" (distribution name); tests updated
  to use ENV_PREFIX for env-var key construction
- Fix all mock patch() paths in tests from aignostics.* to aignostics_sdk.*

Heavy package fixes:
- GUI relative imports (.._service, .._models, .._utils) replaced with
  absolute aignostics_sdk.* imports + noqa: PLC2701
- system/_gui.py: resolve assets path via _service.__file__ to work
  through symlink
- application/__init__.py: PageBuilder from heavy _gui._page_builder,
  not slim package
- nicegui moved from hard dep to optional [gui] extra in slim pyproject
- Boot time logging: startup_ms in trace message, DEBUG warning >2000ms
- system/_service.py: use Path(__file__).resolve() for openapi.json

Version: 1.4.3 published to GAR
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip:test:long_running Skip long-running tests (≥5min)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants