chapkit 0.26.0__tar.gz → 0.28.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 (69) hide show
  1. {chapkit-0.26.0 → chapkit-0.28.0}/PKG-INFO +7 -8
  2. {chapkit-0.26.0 → chapkit-0.28.0}/README.md +5 -6
  3. {chapkit-0.26.0 → chapkit-0.28.0}/pyproject.toml +4 -14
  4. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/artifact/__init__.py +6 -0
  5. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/artifact/router.py +38 -9
  6. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/artifact/schemas.py +34 -0
  7. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/__init__.py +0 -0
  8. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/alembic/__init__.py +0 -0
  9. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/alembic/env.py +0 -0
  10. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/alembic/script.py.mako +0 -0
  11. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/alembic/versions/20251010_0927_4d869b5fb06e_initial_schema.py +0 -0
  12. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/alembic/versions/__init__.py +0 -0
  13. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/alembic_helpers.py +0 -0
  14. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/api/__init__.py +0 -0
  15. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/api/dependencies.py +0 -0
  16. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/api/service_builder.py +0 -0
  17. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/artifact/manager.py +0 -0
  18. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/artifact/models.py +0 -0
  19. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/artifact/repository.py +0 -0
  20. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/__init__.py +0 -0
  21. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/__main__.py +0 -0
  22. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/artifact.py +0 -0
  23. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/cli.py +0 -0
  24. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/init.py +0 -0
  25. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/migrate.py +0 -0
  26. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/mlproject.py +0 -0
  27. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/run.py +0 -0
  28. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/.gitignore +0 -0
  29. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/.python-version +0 -0
  30. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/Dockerfile.jinja2 +0 -0
  31. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/README.md.jinja2 +0 -0
  32. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/compose.yml.jinja2 +0 -0
  33. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/main.py.jinja2 +0 -0
  34. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/main_shell.py.jinja2 +0 -0
  35. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/migrate/CHAPKIT_migrate.md.jinja2 +0 -0
  36. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/migrate/Dockerfile_migrate.jinja2 +0 -0
  37. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/migrate/Makefile_migrate.jinja2 +0 -0
  38. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/migrate/README_migrate.md.jinja2 +0 -0
  39. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/migrate/dockerignore_migrate.jinja2 +0 -0
  40. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/migrate/gitignore_migrate.jinja2 +0 -0
  41. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/migrate/main_migrate.py.jinja2 +0 -0
  42. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/migrate/pyproject_migrate.toml.jinja2 +0 -0
  43. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/migrate/requirements_migrate.txt.jinja2 +0 -0
  44. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/pyproject.toml.jinja2 +0 -0
  45. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/scripts/predict.R.jinja2 +0 -0
  46. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/scripts/predict_model.py.jinja2 +0 -0
  47. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/scripts/train.R.jinja2 +0 -0
  48. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/templates/scripts/train_model.py.jinja2 +0 -0
  49. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/test/__init__.py +0 -0
  50. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/test/command.py +0 -0
  51. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/test/generator.py +0 -0
  52. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/test/runner.py +0 -0
  53. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/cli/test/utils.py +0 -0
  54. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/config/__init__.py +0 -0
  55. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/config/manager.py +0 -0
  56. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/config/models.py +0 -0
  57. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/config/repository.py +0 -0
  58. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/config/router.py +0 -0
  59. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/config/schemas.py +0 -0
  60. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/data/__init__.py +0 -0
  61. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/data/dataframe.py +0 -0
  62. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/ml/__init__.py +0 -0
  63. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/ml/manager.py +0 -0
  64. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/ml/router.py +0 -0
  65. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/ml/runner.py +0 -0
  66. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/ml/schemas.py +0 -0
  67. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/py.typed +0 -0
  68. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/scheduler.py +0 -0
  69. {chapkit-0.26.0 → chapkit-0.28.0}/src/chapkit/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: chapkit
3
- Version: 0.26.0
3
+ Version: 0.28.0
4
4
  Summary: ML and data service modules built on servicekit - config, artifacts, and ML workflows
5
5
  Keywords: ml,machine-learning,data-science,artifacts,tasks,config,servicekit
6
6
  Author: Morten Hansen
@@ -16,7 +16,7 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
16
16
  Requires-Dist: geojson-pydantic>=2.1.1
17
17
  Requires-Dist: typer>=0.24.2
18
18
  Requires-Dist: jinja2>=3.1.6
19
- Requires-Dist: servicekit>=0.11.0
19
+ Requires-Dist: servicekit>=0.12.0
20
20
  Requires-Dist: httpx>=0.28.1
21
21
  Requires-Dist: pandas>=2.3.3 ; extra == 'dataframe'
22
22
  Requires-Dist: polars>=1.34.0 ; extra == 'dataframe'
@@ -257,13 +257,12 @@ Chapkit extends servicekit's `BaseServiceBuilder` with ML-specific features and
257
257
 
258
258
  See the `examples/` directory for complete working examples:
259
259
 
260
- - `config/` - Config CRUD walkthrough
261
- - `config_artifact/` - Config with artifact linking
262
- - `artifact/` - Read-only artifact API with hierarchical storage
263
- - `ml_functional/`, `ml_class/`, `ml_shell/` - ML workflow patterns (`FunctionalModelRunner`, class-based `BaseModelRunner`, `ShellModelRunner`)
264
- - `ml_pipeline/` - Multi-stage ML pipeline with hierarchical artifacts
260
+ - `config/` - Config CRUD walkthrough with seeding and a custom `ServiceInfo`
261
+ - `artifact/` - Artifact hierarchies with config linking, read-only API, and non-JSON payloads
262
+ - `ml_class/` - Class-based `BaseModelRunner` with lifecycle hooks and validation
265
263
  - `library_usage/` - Using chapkit as a library with custom models
266
- - `dataframe_usage/` - Working with `chapkit.data.DataFrame`
264
+
265
+ For the functional and shell runner patterns, scaffold a project with `chapkit init` (`--template shell-py` for the shell runner) — the generated `main.py` is the canonical example.
267
266
 
268
267
  For a fresh project, prefer `chapkit init` (see [`docs/guides/cli-scaffolding.md`](docs/guides/cli-scaffolding.md)) — the `examples/` directory targets specific patterns rather than a full starting point.
269
268
 
@@ -219,13 +219,12 @@ Chapkit extends servicekit's `BaseServiceBuilder` with ML-specific features and
219
219
 
220
220
  See the `examples/` directory for complete working examples:
221
221
 
222
- - `config/` - Config CRUD walkthrough
223
- - `config_artifact/` - Config with artifact linking
224
- - `artifact/` - Read-only artifact API with hierarchical storage
225
- - `ml_functional/`, `ml_class/`, `ml_shell/` - ML workflow patterns (`FunctionalModelRunner`, class-based `BaseModelRunner`, `ShellModelRunner`)
226
- - `ml_pipeline/` - Multi-stage ML pipeline with hierarchical artifacts
222
+ - `config/` - Config CRUD walkthrough with seeding and a custom `ServiceInfo`
223
+ - `artifact/` - Artifact hierarchies with config linking, read-only API, and non-JSON payloads
224
+ - `ml_class/` - Class-based `BaseModelRunner` with lifecycle hooks and validation
227
225
  - `library_usage/` - Using chapkit as a library with custom models
228
- - `dataframe_usage/` - Working with `chapkit.data.DataFrame`
226
+
227
+ For the functional and shell runner patterns, scaffold a project with `chapkit init` (`--template shell-py` for the shell runner) — the generated `main.py` is the canonical example.
229
228
 
230
229
  For a fresh project, prefer `chapkit init` (see [`docs/guides/cli-scaffolding.md`](docs/guides/cli-scaffolding.md)) — the `examples/` directory targets specific patterns rather than a full starting point.
231
230
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "chapkit"
3
- version = "0.26.0"
3
+ version = "0.28.0"
4
4
  description = "ML and data service modules built on servicekit - config, artifacts, and ML workflows"
5
5
  readme = "README.md"
6
6
  authors = [{ name = "Morten Hansen", email = "morten@dhis2.org" }]
@@ -28,7 +28,7 @@ dependencies = [
28
28
  "geojson-pydantic>=2.1.1",
29
29
  "typer>=0.24.2",
30
30
  "jinja2>=3.1.6",
31
- "servicekit>=0.11.0",
31
+ "servicekit>=0.12.0",
32
32
  "httpx>=0.28.1",
33
33
  ]
34
34
 
@@ -145,27 +145,17 @@ disallow_any_unimported = false
145
145
  module = [
146
146
  "chapkit.artifact.*",
147
147
  "chapkit.config.*",
148
- "chapkit.task.*",
149
148
  "examples.*",
150
149
  ]
151
150
  warn_return_any = false
152
151
 
153
152
  [[tool.mypy.overrides]]
154
- module = "examples.custom_migrations.*"
155
- ignore_missing_imports = true
156
- ignore_errors = true
157
-
158
- [[tool.mypy.overrides]]
159
- module = "examples.artifact_manager.*"
160
- ignore_errors = true
161
-
162
- [[tool.mypy.overrides]]
163
- module = ["chapkit.data.dataframe", "examples.dataframe_usage.*"]
153
+ module = "chapkit.data.dataframe"
164
154
  disable_error_code = ["import-not-found"]
165
155
 
166
156
  [tool.pyright]
167
157
  include = ["src", "tests", "examples"]
168
- exclude = ["examples/artifact_manager", "examples/dataframe_usage", "**/.venv"]
158
+ exclude = ["**/.venv"]
169
159
  pythonVersion = "3.13"
170
160
  typeCheckingMode = "strict"
171
161
  useLibraryCodeForTypes = true
@@ -9,6 +9,8 @@ from .schemas import (
9
9
  ArtifactHierarchy,
10
10
  ArtifactIn,
11
11
  ArtifactOut,
12
+ ArtifactSummaryOut,
13
+ ArtifactSummaryTreeNode,
12
14
  ArtifactTreeNode,
13
15
  BaseArtifactData,
14
16
  GenericArtifactData,
@@ -16,6 +18,7 @@ from .schemas import (
16
18
  MLMetadata,
17
19
  MLPredictionArtifactData,
18
20
  MLTrainingWorkspaceArtifactData,
21
+ strip_content,
19
22
  validate_artifact_data,
20
23
  )
21
24
 
@@ -24,6 +27,8 @@ __all__ = [
24
27
  "ArtifactHierarchy",
25
28
  "ArtifactIn",
26
29
  "ArtifactOut",
30
+ "ArtifactSummaryOut",
31
+ "ArtifactSummaryTreeNode",
27
32
  "ArtifactTreeNode",
28
33
  "ArtifactRepository",
29
34
  "ArtifactManager",
@@ -35,5 +40,6 @@ __all__ = [
35
40
  "GenericArtifactData",
36
41
  "MLMetadata",
37
42
  "GenericMetadata",
43
+ "strip_content",
38
44
  "validate_artifact_data",
39
45
  ]
@@ -5,11 +5,17 @@ from typing import Any
5
5
 
6
6
  from fastapi import Depends, HTTPException, Response, status
7
7
  from servicekit.api.crud import CrudPermissions, CrudRouter
8
+ from servicekit.schemas import PaginatedResponse
8
9
 
9
10
  from ..config.schemas import BaseConfig, ConfigOut
10
11
  from ..data import DataFrame
11
12
  from .manager import ArtifactManager
12
- from .schemas import ArtifactIn, ArtifactOut, ArtifactTreeNode
13
+ from .schemas import (
14
+ ArtifactIn,
15
+ ArtifactOut,
16
+ ArtifactSummaryOut,
17
+ ArtifactSummaryTreeNode,
18
+ )
13
19
 
14
20
 
15
21
  class ArtifactRouter(CrudRouter[ArtifactIn, ArtifactOut]):
@@ -40,6 +46,29 @@ class ArtifactRouter(CrudRouter[ArtifactIn, ArtifactOut]):
40
46
  **kwargs,
41
47
  )
42
48
 
49
+ def _register_find_all_route(self, manager_dependency: Any, manager_annotation: Any) -> None:
50
+ """Register the list route returning content-less artifact summaries."""
51
+ summary_annotation: Any = ArtifactSummaryOut
52
+ collection_response_model: Any = list[summary_annotation] | PaginatedResponse[summary_annotation]
53
+
54
+ @self.router.get("", response_model=collection_response_model)
55
+ async def find_all(
56
+ page: int | None = None,
57
+ size: int | None = None,
58
+ manager: ArtifactManager = manager_dependency,
59
+ ) -> list[ArtifactSummaryOut] | PaginatedResponse[ArtifactSummaryOut]:
60
+ from servicekit.api.pagination import create_paginated_response
61
+
62
+ if page is not None and size is not None:
63
+ items, total = await manager.find_paginated(page, size)
64
+ summaries = [ArtifactSummaryOut.from_artifact(item) for item in items]
65
+ return create_paginated_response(summaries, total, page, size)
66
+ artifacts = await manager.find_all()
67
+ return [ArtifactSummaryOut.from_artifact(item) for item in artifacts]
68
+
69
+ self._annotate_manager(find_all, manager_annotation)
70
+ find_all.__annotations__["return"] = list[summary_annotation] | PaginatedResponse[summary_annotation]
71
+
43
72
  def _register_routes(self) -> None:
44
73
  """Register artifact CRUD routes and tree operations."""
45
74
  super()._register_routes()
@@ -49,7 +78,7 @@ class ArtifactRouter(CrudRouter[ArtifactIn, ArtifactOut]):
49
78
  async def expand_artifact(
50
79
  entity_id: str,
51
80
  manager: ArtifactManager = Depends(manager_factory),
52
- ) -> ArtifactTreeNode:
81
+ ) -> ArtifactSummaryTreeNode:
53
82
  ulid_id = self._parse_ulid(entity_id)
54
83
 
55
84
  expanded = await manager.expand_artifact(ulid_id)
@@ -58,12 +87,12 @@ class ArtifactRouter(CrudRouter[ArtifactIn, ArtifactOut]):
58
87
  status_code=status.HTTP_404_NOT_FOUND,
59
88
  detail=f"Artifact with id {entity_id} not found",
60
89
  )
61
- return expanded
90
+ return ArtifactSummaryTreeNode.from_tree_node(expanded)
62
91
 
63
92
  async def build_tree(
64
93
  entity_id: str,
65
94
  manager: ArtifactManager = Depends(manager_factory),
66
- ) -> ArtifactTreeNode:
95
+ ) -> ArtifactSummaryTreeNode:
67
96
  ulid_id = self._parse_ulid(entity_id)
68
97
 
69
98
  tree = await manager.build_tree(ulid_id)
@@ -72,22 +101,22 @@ class ArtifactRouter(CrudRouter[ArtifactIn, ArtifactOut]):
72
101
  status_code=status.HTTP_404_NOT_FOUND,
73
102
  detail=f"Artifact with id {entity_id} not found",
74
103
  )
75
- return tree
104
+ return ArtifactSummaryTreeNode.from_tree_node(tree)
76
105
 
77
106
  self.register_entity_operation(
78
107
  "expand",
79
108
  expand_artifact,
80
- response_model=ArtifactTreeNode,
109
+ response_model=ArtifactSummaryTreeNode,
81
110
  summary="Expand artifact",
82
- description="Get artifact with hierarchy metadata but without children",
111
+ description="Get artifact with hierarchy metadata but without children or content",
83
112
  )
84
113
 
85
114
  self.register_entity_operation(
86
115
  "tree",
87
116
  build_tree,
88
- response_model=ArtifactTreeNode,
117
+ response_model=ArtifactSummaryTreeNode,
89
118
  summary="Build artifact tree",
90
- description="Build hierarchical tree structure rooted at the given artifact",
119
+ description="Build hierarchical tree structure rooted at the given artifact, without content",
91
120
  )
92
121
 
93
122
  # Conditionally register config access endpoint
@@ -39,6 +39,40 @@ class ArtifactTreeNode(ArtifactOut):
39
39
  return cls.model_validate(artifact.model_dump())
40
40
 
41
41
 
42
+ def strip_content(data: Any) -> Any:
43
+ """Return a shallow copy of artifact data without the heavy 'content' key."""
44
+ if not isinstance(data, dict):
45
+ return data
46
+ return {key: value for key, value in data.items() if key != "content"}
47
+
48
+
49
+ class ArtifactSummaryOut(ArtifactOut):
50
+ """Artifact output without the heavy inline 'content', for list responses."""
51
+
52
+ @classmethod
53
+ def from_artifact(cls, artifact: ArtifactOut) -> Self:
54
+ """Create a content-less summary from a full artifact output schema."""
55
+ return cls.model_validate({**artifact.model_dump(exclude={"data"}), "data": strip_content(artifact.data)})
56
+
57
+
58
+ class ArtifactSummaryTreeNode(ArtifactTreeNode):
59
+ """Artifact tree node without inline 'content', with content stripped recursively."""
60
+
61
+ @classmethod
62
+ def from_tree_node(cls, node: ArtifactTreeNode) -> Self:
63
+ """Create a content-less tree node, stripping content from the node and its children."""
64
+ children = (
65
+ [cls.from_tree_node(child).model_dump() for child in node.children] if node.children is not None else None
66
+ )
67
+ return cls.model_validate(
68
+ {
69
+ **node.model_dump(exclude={"data", "children"}),
70
+ "data": strip_content(node.data),
71
+ "children": children,
72
+ }
73
+ )
74
+
75
+
42
76
  class ArtifactHierarchy(BaseModel):
43
77
  """Configuration for artifact hierarchy with level labels."""
44
78
 
File without changes
File without changes