coloph-toolset 0.1.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.
- coloph_toolset-0.1.0/.github/workflows/ci.yml +30 -0
- coloph_toolset-0.1.0/.github/workflows/release.yml +30 -0
- coloph_toolset-0.1.0/.gitignore +9 -0
- coloph_toolset-0.1.0/CHANGELOG.md +11 -0
- coloph_toolset-0.1.0/CONTRIBUTING.md +36 -0
- coloph_toolset-0.1.0/LICENSE +21 -0
- coloph_toolset-0.1.0/PKG-INFO +108 -0
- coloph_toolset-0.1.0/README.md +90 -0
- coloph_toolset-0.1.0/docs/contracts.md +81 -0
- coloph_toolset-0.1.0/docs/roadmap.md +19 -0
- coloph_toolset-0.1.0/examples/01-tool-declarations/README.md +25 -0
- coloph_toolset-0.1.0/examples/01-tool-declarations/pyproject.toml +8 -0
- coloph_toolset-0.1.0/examples/01-tool-declarations/shipping_quote.py +57 -0
- coloph_toolset-0.1.0/examples/01-tool-declarations/test_shipping_quote.py +36 -0
- coloph_toolset-0.1.0/pyproject.toml +45 -0
- coloph_toolset-0.1.0/scripts/release.py +33 -0
- coloph_toolset-0.1.0/scripts/smoke_wheel.py +42 -0
- coloph_toolset-0.1.0/src/coloph_toolset/__init__.py +21 -0
- coloph_toolset-0.1.0/src/coloph_toolset/_argument_model.py +66 -0
- coloph_toolset-0.1.0/src/coloph_toolset/_decorator.py +543 -0
- coloph_toolset-0.1.0/src/coloph_toolset/py.typed +0 -0
- coloph_toolset-0.1.0/tests/import_probe.py +15 -0
- coloph_toolset-0.1.0/tests/test_contract.py +353 -0
- coloph_toolset-0.1.0/tests/test_deferred_annotations.py +22 -0
- coloph_toolset-0.1.0/tests/test_import.py +12 -0
- coloph_toolset-0.1.0/uv.lock +478 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on: [push, pull_request]
|
|
3
|
+
permissions:
|
|
4
|
+
contents: read
|
|
5
|
+
jobs:
|
|
6
|
+
minimum-dependencies:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
steps:
|
|
9
|
+
- uses: actions/checkout@v4
|
|
10
|
+
- uses: astral-sh/setup-uv@v6
|
|
11
|
+
with:
|
|
12
|
+
python-version: '3.12'
|
|
13
|
+
- run: uv run --isolated --python 3.12 --with pydantic==2.12.0 --with annotated-types==0.7.0 python -m pytest
|
|
14
|
+
contracts:
|
|
15
|
+
strategy:
|
|
16
|
+
matrix:
|
|
17
|
+
python: ['3.11', '3.12', '3.13', '3.14']
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: astral-sh/setup-uv@v6
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python }}
|
|
24
|
+
- run: uv sync --locked
|
|
25
|
+
- run: uv run pytest
|
|
26
|
+
- run: uv run mypy
|
|
27
|
+
- run: uv run python -m ruff check .
|
|
28
|
+
- run: uv run python -m ruff format --check .
|
|
29
|
+
- run: uv build
|
|
30
|
+
- run: uv run python scripts/smoke_wheel.py
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
tags: ['v*']
|
|
5
|
+
permissions:
|
|
6
|
+
contents: write
|
|
7
|
+
id-token: write
|
|
8
|
+
jobs:
|
|
9
|
+
release:
|
|
10
|
+
environment:
|
|
11
|
+
name: pypi
|
|
12
|
+
url: https://pypi.org/p/coloph-toolset
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: astral-sh/setup-uv@v6
|
|
17
|
+
with:
|
|
18
|
+
python-version: '3.12'
|
|
19
|
+
- run: uv sync --locked
|
|
20
|
+
- run: uv run pytest
|
|
21
|
+
- run: uv run mypy
|
|
22
|
+
- run: uv run python -m ruff check .
|
|
23
|
+
- run: uv run python -m ruff format --check .
|
|
24
|
+
- run: uv build
|
|
25
|
+
- run: uv run python scripts/smoke_wheel.py
|
|
26
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
27
|
+
- run: uv run python scripts/release.py
|
|
28
|
+
env:
|
|
29
|
+
GH_TOKEN: ${{ github.token }}
|
|
30
|
+
RELEASE_TAG: ${{ github.ref_name }}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- Add lazy function declarations and parameter inspection.
|
|
6
|
+
- Generate JSON Schema and argument models from the same retained annotations.
|
|
7
|
+
- Preserve supported constraints, nullable required inputs, defaults, and hidden argument projections.
|
|
8
|
+
- Reject unsupported signatures and metadata before application dispatch.
|
|
9
|
+
- Add the standalone shipping-quote example and clean wheel smoke coverage.
|
|
10
|
+
|
|
11
|
+
Execution remains application-owned in this release.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Local checks
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
uv sync --locked
|
|
7
|
+
uv run pytest
|
|
8
|
+
uv run mypy
|
|
9
|
+
uv run python -m ruff check .
|
|
10
|
+
uv run python -m ruff format --check .
|
|
11
|
+
uv build
|
|
12
|
+
uv run python scripts/smoke_wheel.py
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Keep fixes and tests within the current milestone. Add contract tests for changes in public behavior.
|
|
16
|
+
Preserve every project under `examples/`. Each project needs source, a README, dependency metadata, and automated smoke coverage.
|
|
17
|
+
Examples must use public imports and work without a Coloph checkout or private services.
|
|
18
|
+
|
|
19
|
+
## Release
|
|
20
|
+
|
|
21
|
+
This package initially distributes wheels and source archives through GitHub Releases. It does not require a PyPI publishing credential.
|
|
22
|
+
|
|
23
|
+
1. Update the version and changelog.
|
|
24
|
+
2. Run all local checks and the clean wheel smoke.
|
|
25
|
+
3. Review and commit the changes.
|
|
26
|
+
4. Push the reviewed commit to the public default branch.
|
|
27
|
+
5. Wait for CI on the exact release commit.
|
|
28
|
+
6. Create an immutable `vX.Y.Z` tag on that commit.
|
|
29
|
+
7. Push the tag. The release workflow reruns tests and publishes the wheel and source archive.
|
|
30
|
+
8. Validate the published artifacts in a clean environment.
|
|
31
|
+
|
|
32
|
+
The package version, tag, and release notes must match. Never move an existing release tag.
|
|
33
|
+
Coloph can adopt a release only after the public default branch and release contain its tested commit.
|
|
34
|
+
Record the release and Coloph dependency pin in the owning Coloph issue.
|
|
35
|
+
|
|
36
|
+
When PyPI distribution is introduced, the release process must publish the same tested artifacts and version there.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 golergka
|
|
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.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: coloph-toolset
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Declare Python tools once, export their schemas, and validate their arguments.
|
|
5
|
+
Project-URL: Repository, https://github.com/golergka/coloph-toolset
|
|
6
|
+
Project-URL: Issues, https://github.com/golergka/coloph-toolset/issues
|
|
7
|
+
Project-URL: Changelog, https://github.com/golergka/coloph-toolset/blob/main/CHANGELOG.md
|
|
8
|
+
Author: golergka
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Typing :: Typed
|
|
14
|
+
Requires-Python: >=3.11
|
|
15
|
+
Requires-Dist: annotated-types<1,>=0.7
|
|
16
|
+
Requires-Dist: pydantic<3,>=2.12
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# coloph-toolset
|
|
20
|
+
|
|
21
|
+
Declare Python tools once. Export their JSON Schema and validate arguments before your application calls the function.
|
|
22
|
+
|
|
23
|
+
Version 0.1 provides declarations and validation. It does not manage execution, transactions, authorization, or model providers.
|
|
24
|
+
Later releases add catalogs and adapters. The [roadmap](docs/roadmap.md) describes that sequence.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
uv add coloph-toolset
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The package requires Python 3.11 or later and Pydantic 2.12 or later within major version 2.
|
|
33
|
+
No Coloph installation, database, credentials, HTTP framework, or agent framework is required.
|
|
34
|
+
|
|
35
|
+
## Declare and validate
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from typing import Annotated
|
|
39
|
+
from pydantic import Field, ValidationError
|
|
40
|
+
from coloph_toolset import tool, tool_for
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@tool()
|
|
44
|
+
def shipping_quote(
|
|
45
|
+
ctx,
|
|
46
|
+
quantity: Annotated[int, "Number of parcels", Field(gt=0)],
|
|
47
|
+
destination: Annotated[str | None, "Country code, or null for collection"],
|
|
48
|
+
insured: Annotated[bool, "Include insurance"] = False,
|
|
49
|
+
) -> dict[str, object]:
|
|
50
|
+
return {"quantity": quantity, "destination": destination, "insured": insured}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
declaration = tool_for(shipping_quote)
|
|
54
|
+
schema = declaration.json_schema()
|
|
55
|
+
arguments = declaration.validate_arguments({"quantity": "2", "destination": None})
|
|
56
|
+
result = shipping_quote(None, **arguments)
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
declaration.validate_arguments({"quantity": 0, "destination": None})
|
|
60
|
+
except ValidationError as error:
|
|
61
|
+
print(error.errors(include_url=False))
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Validation never calls the function. Calling the Python function directly does not apply validation.
|
|
65
|
+
The application owns execution and must use validated arguments at its dispatch boundary.
|
|
66
|
+
|
|
67
|
+
## Context and restricted arguments
|
|
68
|
+
|
|
69
|
+
The declaration expects a required first parameter named `ctx`.
|
|
70
|
+
Its type is application-owned and its annotation is not evaluated. Context never appears in the argument schema.
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
@tool(model_hidden_args=("internal",))
|
|
74
|
+
def inspect_order(ctx, order: str, internal: bool = False):
|
|
75
|
+
return ctx.lookup(order, internal=internal)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
public = tool_for(inspect_order)
|
|
79
|
+
public.validate_arguments({"order": "demo"})
|
|
80
|
+
# An external "internal" argument raises ValidationError.
|
|
81
|
+
operator_schema = public.json_schema(include_hidden=True)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Hidden arguments require valid defaults. Only trusted application code can select `include_hidden=True`.
|
|
85
|
+
Argument projection is not an authorization system.
|
|
86
|
+
|
|
87
|
+
## Contracts and examples
|
|
88
|
+
|
|
89
|
+
- [Argument contract and API](docs/contracts.md)
|
|
90
|
+
- [Standalone shipping-quote project](examples/01-tool-declarations/README.md)
|
|
91
|
+
- [Development and release procedure](CONTRIBUTING.md)
|
|
92
|
+
- [Changes](CHANGELOG.md)
|
|
93
|
+
|
|
94
|
+
Every milestone adds a project under `examples/`. CI runs all preserved examples against the current library.
|
|
95
|
+
|
|
96
|
+
## Development
|
|
97
|
+
|
|
98
|
+
```sh
|
|
99
|
+
uv sync --locked
|
|
100
|
+
uv run pytest
|
|
101
|
+
uv run mypy
|
|
102
|
+
uv run python -m ruff check .
|
|
103
|
+
uv run python -m ruff format --check .
|
|
104
|
+
uv build
|
|
105
|
+
uv run python scripts/smoke_wheel.py
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
MIT licensed.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# coloph-toolset
|
|
2
|
+
|
|
3
|
+
Declare Python tools once. Export their JSON Schema and validate arguments before your application calls the function.
|
|
4
|
+
|
|
5
|
+
Version 0.1 provides declarations and validation. It does not manage execution, transactions, authorization, or model providers.
|
|
6
|
+
Later releases add catalogs and adapters. The [roadmap](docs/roadmap.md) describes that sequence.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
uv add coloph-toolset
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The package requires Python 3.11 or later and Pydantic 2.12 or later within major version 2.
|
|
15
|
+
No Coloph installation, database, credentials, HTTP framework, or agent framework is required.
|
|
16
|
+
|
|
17
|
+
## Declare and validate
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from typing import Annotated
|
|
21
|
+
from pydantic import Field, ValidationError
|
|
22
|
+
from coloph_toolset import tool, tool_for
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@tool()
|
|
26
|
+
def shipping_quote(
|
|
27
|
+
ctx,
|
|
28
|
+
quantity: Annotated[int, "Number of parcels", Field(gt=0)],
|
|
29
|
+
destination: Annotated[str | None, "Country code, or null for collection"],
|
|
30
|
+
insured: Annotated[bool, "Include insurance"] = False,
|
|
31
|
+
) -> dict[str, object]:
|
|
32
|
+
return {"quantity": quantity, "destination": destination, "insured": insured}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
declaration = tool_for(shipping_quote)
|
|
36
|
+
schema = declaration.json_schema()
|
|
37
|
+
arguments = declaration.validate_arguments({"quantity": "2", "destination": None})
|
|
38
|
+
result = shipping_quote(None, **arguments)
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
declaration.validate_arguments({"quantity": 0, "destination": None})
|
|
42
|
+
except ValidationError as error:
|
|
43
|
+
print(error.errors(include_url=False))
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Validation never calls the function. Calling the Python function directly does not apply validation.
|
|
47
|
+
The application owns execution and must use validated arguments at its dispatch boundary.
|
|
48
|
+
|
|
49
|
+
## Context and restricted arguments
|
|
50
|
+
|
|
51
|
+
The declaration expects a required first parameter named `ctx`.
|
|
52
|
+
Its type is application-owned and its annotation is not evaluated. Context never appears in the argument schema.
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
@tool(model_hidden_args=("internal",))
|
|
56
|
+
def inspect_order(ctx, order: str, internal: bool = False):
|
|
57
|
+
return ctx.lookup(order, internal=internal)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
public = tool_for(inspect_order)
|
|
61
|
+
public.validate_arguments({"order": "demo"})
|
|
62
|
+
# An external "internal" argument raises ValidationError.
|
|
63
|
+
operator_schema = public.json_schema(include_hidden=True)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Hidden arguments require valid defaults. Only trusted application code can select `include_hidden=True`.
|
|
67
|
+
Argument projection is not an authorization system.
|
|
68
|
+
|
|
69
|
+
## Contracts and examples
|
|
70
|
+
|
|
71
|
+
- [Argument contract and API](docs/contracts.md)
|
|
72
|
+
- [Standalone shipping-quote project](examples/01-tool-declarations/README.md)
|
|
73
|
+
- [Development and release procedure](CONTRIBUTING.md)
|
|
74
|
+
- [Changes](CHANGELOG.md)
|
|
75
|
+
|
|
76
|
+
Every milestone adds a project under `examples/`. CI runs all preserved examples against the current library.
|
|
77
|
+
|
|
78
|
+
## Development
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
uv sync --locked
|
|
82
|
+
uv run pytest
|
|
83
|
+
uv run mypy
|
|
84
|
+
uv run python -m ruff check .
|
|
85
|
+
uv run python -m ruff format --check .
|
|
86
|
+
uv build
|
|
87
|
+
uv run python scripts/smoke_wheel.py
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
MIT licensed.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Argument contract
|
|
2
|
+
|
|
3
|
+
## Declaration lifetime
|
|
4
|
+
|
|
5
|
+
`@tool()` attaches a `Tool` at `fn.tool` and returns the original function.
|
|
6
|
+
`tool_for(fn)` reads that declaration. `Tool(fn, ...)` also works without a decorator for application integration.
|
|
7
|
+
|
|
8
|
+
Signature structure is validated at construction. Parameter annotations are resolved lazily on first inspection.
|
|
9
|
+
Schema construction validates defaults and rejects unsupported metadata. A malformed declaration raises `DeclarationError`, the exported name for `TypeError`.
|
|
10
|
+
Applications must build their argument models during startup before they accept calls.
|
|
11
|
+
|
|
12
|
+
Annotations are trusted developer code. String annotations resolve in the original function's global namespace, including through `functools.wraps`.
|
|
13
|
+
Function-local forward names must be available in that namespace or use evaluated annotations.
|
|
14
|
+
Context and return annotations are not resolved. Schema inspection never calls the body.
|
|
15
|
+
Do not mutate a function's signature, annotations, defaults, or declaration after registration.
|
|
16
|
+
|
|
17
|
+
## Supported signatures and types
|
|
18
|
+
|
|
19
|
+
- Plain synchronous functions with named external arguments. Positional-only external arguments and variadic arguments are rejected.
|
|
20
|
+
- A required injected context named `ctx` as the first positional parameter.
|
|
21
|
+
- `bool`, `int`, `float`, and `str`.
|
|
22
|
+
- Homogeneous string or integer `Literal` choices.
|
|
23
|
+
- Lists of supported scalar or literal values. Nested lists and nullable list elements are not supported in 0.1.
|
|
24
|
+
- Nullable forms of those types, including a nullable list.
|
|
25
|
+
- `Annotated` descriptions and the constraint metadata listed next.
|
|
26
|
+
|
|
27
|
+
Async functions, generators, bound methods, arbitrary callable objects, unconstrained `Any`, mixed unions, and nested models are not supported in 0.1.
|
|
28
|
+
These declarations fail explicitly. Async execution belongs to a later milestone.
|
|
29
|
+
|
|
30
|
+
## Metadata
|
|
31
|
+
|
|
32
|
+
One string in `Annotated` supplies the argument description.
|
|
33
|
+
Supported Pydantic `Field` options are `description`, `title`, `examples`, `gt`, `ge`, `lt`, `le`, `multiple_of`, `min_length`, `max_length`, `pattern`, and `strict`.
|
|
34
|
+
Direct `annotated_types.Gt`, `Ge`, `Lt`, `Le`, `MultipleOf`, `MinLen`, and `MaxLen` metadata also works, as does `pydantic.Strict`.
|
|
35
|
+
Constraints must apply to the annotated type. Duplicate descriptions and unsupported metadata raise `DeclarationError`.
|
|
36
|
+
Literal choices cannot carry additional value constraints in 0.1. Their containing list can carry list constraints.
|
|
37
|
+
|
|
38
|
+
Function signatures own defaults. `Field` defaults, default factories, aliases, serializers, validators, schema overrides, and exclusion controls are rejected.
|
|
39
|
+
This keeps validation, parameter names, and exported schemas aligned.
|
|
40
|
+
|
|
41
|
+
Applications can pass exact marker classes through `metadata_types=(MyMarker,)`.
|
|
42
|
+
The declaration retains these markers in `ToolParam.metadata` and removes them from the validation annotation.
|
|
43
|
+
Markers are descriptive data only. They cannot implement Pydantic validators or change JSON Schema through this interface.
|
|
44
|
+
|
|
45
|
+
## Validation and defaults
|
|
46
|
+
|
|
47
|
+
`Tool.validate_arguments(object, include_hidden=False)` returns normalized values in a new dictionary.
|
|
48
|
+
It raises `pydantic.ValidationError` for invalid input. `error.errors()` provides field locations, codes, messages, and input values.
|
|
49
|
+
Applications own error presentation and any redaction of submitted values.
|
|
50
|
+
|
|
51
|
+
Pydantic 2's default coercion rules apply. Numeric strings can become numbers. Accepted boolean strings can become booleans.
|
|
52
|
+
Strings do not automatically accept numbers. `Field(strict=True)` disables coercion for its annotated type.
|
|
53
|
+
This policy is identical through `validate_arguments()` and the generated argument model.
|
|
54
|
+
|
|
55
|
+
Omission uses the declared default. Explicit null requires a nullable annotation.
|
|
56
|
+
A nullable argument without a default is still required. Unknown arguments are forbidden.
|
|
57
|
+
Defaults are validated even for hidden arguments. Validation supplies independent list values, so one call cannot mutate another call's validated defaults.
|
|
58
|
+
|
|
59
|
+
Hidden arguments are absent from the default schema and rejected as external input.
|
|
60
|
+
`include_hidden=True` creates the full contract for trusted application callers.
|
|
61
|
+
The restricted result contains only visible arguments. The application owns how it supplies private values during execution.
|
|
62
|
+
|
|
63
|
+
## Public inspection API
|
|
64
|
+
|
|
65
|
+
- `Tool.params`: ordered `ToolParam` records with the validation annotation, default, description, scalar type, choices, repeated/nullable flags, and application metadata.
|
|
66
|
+
- `ToolParam.required`: whether the signature has no default.
|
|
67
|
+
- `Tool.description` and `Tool.summary`: full docstring and first line, or an empty string.
|
|
68
|
+
- `Tool.argument_model(include_hidden=False, name=None, descriptions=None)`: a Pydantic model for that projection.
|
|
69
|
+
- `Tool.json_schema(include_hidden=False)`: a fresh validation schema.
|
|
70
|
+
- `annotation_with_description(fn, parameter, description)`: a description override that retains the original annotation for adapter signatures.
|
|
71
|
+
|
|
72
|
+
Description overrides accept real parameter names only. They do not change validation.
|
|
73
|
+
Treat generated model classes and parameter metadata as read-only.
|
|
74
|
+
|
|
75
|
+
## Boundaries
|
|
76
|
+
|
|
77
|
+
Importing `coloph_toolset` does not import Pydantic until annotation/schema work needs it.
|
|
78
|
+
The package does not replace stdout or read environment files.
|
|
79
|
+
It has no KB names, reference registry, credentials, database connections, workflow state, or agent framework dependency.
|
|
80
|
+
|
|
81
|
+
The [Pydantic field contract](https://docs.pydantic.dev/latest/concepts/fields/) describes the underlying constraint and default behavior.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Extraction milestones
|
|
2
|
+
|
|
3
|
+
The extraction plan tracks six sequential releases.
|
|
4
|
+
|
|
5
|
+
1. Declarations, canonical argument validation, and the shipping-quote example.
|
|
6
|
+
2. Catalogs, lazy trees, CLI generation, and a task CLI example.
|
|
7
|
+
3. Invocation, typed context, results, sync/async execution, and a runtime example.
|
|
8
|
+
4. An optional HTTP adapter and a standalone service example.
|
|
9
|
+
5. An optional Pydantic AI adapter and a deterministic agent example.
|
|
10
|
+
6. Hierarchical discovery, documentation gates, terminal controls, and a hierarchical agent example.
|
|
11
|
+
|
|
12
|
+
Each milestone adds a project under `examples/` and preserves every previous project.
|
|
13
|
+
CI runs all examples against the current package. Pins to obsolete releases do not replace current-version coverage.
|
|
14
|
+
|
|
15
|
+
Version 0.1 does not promise CLI or HTTP parity in Coloph before those adapters migrate.
|
|
16
|
+
Coloph retains reference resolution, authorization, workflow policy, resource management, and output behavior until their respective extraction milestones.
|
|
17
|
+
|
|
18
|
+
The 0.x public API can change in a minor release. Release notes must describe required migrations.
|
|
19
|
+
Patch releases preserve documented contracts. The package must remain useful at every milestone.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Shipping quote
|
|
2
|
+
|
|
3
|
+
This standalone project uses only the public package and its Pydantic dependency.
|
|
4
|
+
It exports a schema, normalizes valid arguments, calls a function, and displays structured validation errors.
|
|
5
|
+
|
|
6
|
+
From this directory:
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
uv run shipping_quote.py
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The project installs the public wheel. No Coloph source, database, credentials, or environment file is needed.
|
|
13
|
+
The valid example returns a quote of 1,000 cents for two parcels.
|
|
14
|
+
The rejected examples show a quantity constraint, a missing nullable argument, and an attempt to supply a hidden argument.
|
|
15
|
+
|
|
16
|
+
To use a locally built current wheel:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
uv venv .venv
|
|
20
|
+
uv pip install --python .venv/bin/python ../../dist/coloph_toolset-0.1.0-py3-none-any.whl
|
|
21
|
+
uv run --no-project --python .venv/bin/python shipping_quote.py
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The repository's `scripts/smoke_wheel.py` installs the current wheel into a temporary environment and runs this project outside the checkout.
|
|
25
|
+
Repository CI also runs this project's tests against the current library, so the release URL cannot hide a compatibility regression.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""A standalone tool declaration with schema export and input validation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Annotated, Literal
|
|
7
|
+
|
|
8
|
+
from pydantic import Field, ValidationError
|
|
9
|
+
|
|
10
|
+
from coloph_toolset import tool, tool_for
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@tool(model_hidden_args=("account_discount",))
|
|
14
|
+
def shipping_quote(
|
|
15
|
+
ctx,
|
|
16
|
+
quantity: Annotated[int, "Number of parcels", Field(gt=0, le=100)],
|
|
17
|
+
destination: Annotated[str | None, "Country code, or null for collection"],
|
|
18
|
+
service: Annotated[Literal["standard", "express"], "Delivery service"] = "standard",
|
|
19
|
+
insured: Annotated[bool, "Include insurance"] = False,
|
|
20
|
+
account_discount: Annotated[bool, "Private account discount"] = False,
|
|
21
|
+
) -> dict[str, object]:
|
|
22
|
+
"""Calculate a shipping quote without external services."""
|
|
23
|
+
unit_cost = 0 if destination is None else (1200 if service == "express" else 500)
|
|
24
|
+
total = quantity * unit_cost + (200 if insured else 0)
|
|
25
|
+
if account_discount:
|
|
26
|
+
total = total * 9 // 10
|
|
27
|
+
return {"amount_cents": total, "currency": "USD", "parcels": quantity}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def main() -> None:
|
|
31
|
+
declaration = tool_for(shipping_quote)
|
|
32
|
+
arguments = declaration.validate_arguments({"quantity": "2", "destination": "AR"})
|
|
33
|
+
rejected = []
|
|
34
|
+
for value in (
|
|
35
|
+
{"quantity": 0, "destination": "AR"},
|
|
36
|
+
{"quantity": 2},
|
|
37
|
+
{"quantity": 2, "destination": None, "account_discount": True},
|
|
38
|
+
):
|
|
39
|
+
try:
|
|
40
|
+
declaration.validate_arguments(value)
|
|
41
|
+
except ValidationError as error:
|
|
42
|
+
rejected.append(error.errors(include_url=False))
|
|
43
|
+
print(
|
|
44
|
+
json.dumps(
|
|
45
|
+
{
|
|
46
|
+
"schema": declaration.json_schema(),
|
|
47
|
+
"arguments": arguments,
|
|
48
|
+
"quote": shipping_quote(None, **arguments),
|
|
49
|
+
"rejected": rejected,
|
|
50
|
+
},
|
|
51
|
+
indent=2,
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
if __name__ == "__main__":
|
|
57
|
+
main()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Executable example contract, included in the cumulative example test suite."""
|
|
2
|
+
|
|
3
|
+
import importlib.util
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
from pydantic import ValidationError
|
|
9
|
+
|
|
10
|
+
from coloph_toolset import tool_for
|
|
11
|
+
|
|
12
|
+
spec = importlib.util.spec_from_file_location("shipping_quote", Path(__file__).with_name("shipping_quote.py"))
|
|
13
|
+
assert spec and spec.loader
|
|
14
|
+
module = importlib.util.module_from_spec(spec)
|
|
15
|
+
spec.loader.exec_module(module)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_quote_and_rejections(capsys):
|
|
19
|
+
module.main()
|
|
20
|
+
output = json.loads(capsys.readouterr().out)
|
|
21
|
+
assert output["quote"]["amount_cents"] == 1000
|
|
22
|
+
assert output["arguments"]["quantity"] == 2
|
|
23
|
+
assert [errors[0]["type"] for errors in output["rejected"]] == [
|
|
24
|
+
"greater_than",
|
|
25
|
+
"missing",
|
|
26
|
+
"extra_forbidden",
|
|
27
|
+
]
|
|
28
|
+
assert "account_discount" not in output["schema"]["properties"]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_collection_and_insurance():
|
|
32
|
+
declaration = tool_for(module.shipping_quote)
|
|
33
|
+
values = declaration.validate_arguments({"quantity": 1, "destination": None, "insured": True})
|
|
34
|
+
assert module.shipping_quote(None, **values)["amount_cents"] == 200
|
|
35
|
+
with pytest.raises(ValidationError):
|
|
36
|
+
declaration.validate_arguments({"quantity": 101, "destination": "AR"})
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27,<2"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "coloph-toolset"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Declare Python tools once, export their schemas, and validate their arguments."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{name = "golergka"}]
|
|
13
|
+
dependencies = ["pydantic>=2.12,<3", "annotated-types>=0.7,<1"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Typing :: Typed",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Repository = "https://github.com/golergka/coloph-toolset"
|
|
22
|
+
Issues = "https://github.com/golergka/coloph-toolset/issues"
|
|
23
|
+
Changelog = "https://github.com/golergka/coloph-toolset/blob/main/CHANGELOG.md"
|
|
24
|
+
|
|
25
|
+
[dependency-groups]
|
|
26
|
+
dev = ["pytest>=8,<10", "ruff>=0.11,<1", "mypy>=1.15,<2", "build>=1.2,<2"]
|
|
27
|
+
|
|
28
|
+
[tool.hatch.build.targets.wheel]
|
|
29
|
+
packages = ["src/coloph_toolset"]
|
|
30
|
+
|
|
31
|
+
[tool.ruff]
|
|
32
|
+
line-length = 120
|
|
33
|
+
target-version = "py311"
|
|
34
|
+
|
|
35
|
+
[tool.ruff.lint]
|
|
36
|
+
select = ["E", "F", "I"]
|
|
37
|
+
|
|
38
|
+
[tool.mypy]
|
|
39
|
+
python_version = "3.11"
|
|
40
|
+
strict = true
|
|
41
|
+
files = ["src/coloph_toolset"]
|
|
42
|
+
|
|
43
|
+
[tool.pytest.ini_options]
|
|
44
|
+
testpaths = ["tests", "examples"]
|
|
45
|
+
addopts = "-ra"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Publish tested artifacts only when the tag matches package metadata."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
import tomllib
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
root = Path(__file__).resolve().parents[1]
|
|
9
|
+
version = tomllib.loads((root / "pyproject.toml").read_text())["project"]["version"]
|
|
10
|
+
tag = os.environ["RELEASE_TAG"]
|
|
11
|
+
if tag != f"v{version}":
|
|
12
|
+
raise SystemExit(f"Tag {tag!r} does not match package version {version!r}")
|
|
13
|
+
artifacts = sorted((root / "dist").iterdir())
|
|
14
|
+
if {file.name for file in artifacts} != {
|
|
15
|
+
f"coloph_toolset-{version}-py3-none-any.whl",
|
|
16
|
+
f"coloph_toolset-{version}.tar.gz",
|
|
17
|
+
}:
|
|
18
|
+
raise SystemExit("The release must contain exactly the matching wheel and source archive")
|
|
19
|
+
subprocess.run(
|
|
20
|
+
[
|
|
21
|
+
"gh",
|
|
22
|
+
"release",
|
|
23
|
+
"create",
|
|
24
|
+
tag,
|
|
25
|
+
*map(str, artifacts),
|
|
26
|
+
"--verify-tag",
|
|
27
|
+
"--title",
|
|
28
|
+
f"coloph-toolset {version}",
|
|
29
|
+
"--notes-file",
|
|
30
|
+
str(root / "CHANGELOG.md"),
|
|
31
|
+
],
|
|
32
|
+
check=True,
|
|
33
|
+
)
|