schemarouter 0.2.0a1__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.
- schemarouter-0.3.0/.github/workflows/ci.yml +190 -0
- schemarouter-0.3.0/.github/workflows/python-preview.yml +30 -0
- schemarouter-0.3.0/.github/workflows/release.yml +353 -0
- schemarouter-0.3.0/CHANGELOG.md +119 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/CONTRIBUTING.md +16 -9
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/PKG-INFO +125 -12
- schemarouter-0.3.0/README.ko.md +364 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/README.md +112 -10
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/SECURITY.md +35 -8
- schemarouter-0.3.0/benchmarks/decision-routing-v1.json +1010 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/adapter-authoring.md +20 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/architecture.md +68 -8
- schemarouter-0.3.0/docs/changelog.md +119 -0
- schemarouter-0.3.0/docs/compatibility.md +112 -0
- schemarouter-0.3.0/docs/concepts/decision-backends.md +100 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/contributing.md +16 -9
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/framework-maturity.md +49 -26
- schemarouter-0.3.0/docs/getting-started/installation.md +82 -0
- schemarouter-0.3.0/docs/guides/adapter-plugins.md +76 -0
- schemarouter-0.3.0/docs/guides/decision-benchmark.md +116 -0
- schemarouter-0.3.0/docs/guides/execution-policy.md +139 -0
- schemarouter-0.3.0/docs/guides/mcp.md +117 -0
- schemarouter-0.3.0/docs/guides/openapi-compatibility.md +60 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/guides/openapi.md +10 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/guides/runtime.md +18 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/index.md +20 -8
- schemarouter-0.3.0/docs/integrations/jev.md +132 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/integrations/langchain.md +28 -1
- schemarouter-0.3.0/docs/integrations/llamaindex.md +57 -0
- schemarouter-0.3.0/docs/integrations/opentelemetry.md +67 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/reference/api.md +25 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/reference/runtime.md +4 -0
- schemarouter-0.3.0/docs/release-checklist.md +74 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/releases/0.2.0a1.md +2 -2
- schemarouter-0.3.0/docs/releases/0.3.0.md +79 -0
- schemarouter-0.3.0/docs/releases/0.3.0a1.md +109 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/security/threat-model.md +36 -9
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/versioning.md +29 -2
- schemarouter-0.3.0/examples/langchain_quickstart.py +25 -0
- schemarouter-0.3.0/examples/llamaindex_quickstart.py +25 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/mkdocs.yml +7 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/pyproject.toml +31 -2
- schemarouter-0.3.0/scripts/benchmark_decision_routing.py +548 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/__init__.py +54 -2
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/adapters/__init__.py +27 -1
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/adapters/base.py +1 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/adapters/mcp.py +119 -19
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/adapters/openapi.py +71 -15
- schemarouter-0.3.0/src/schemarouter/adapters/plugins.py +133 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/adapters/python.py +3 -2
- schemarouter-0.3.0/src/schemarouter/decision_policy.py +28 -0
- schemarouter-0.3.0/src/schemarouter/decisions.py +178 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/errors.py +8 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/executor.py +173 -12
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/ingestion.py +14 -6
- schemarouter-0.3.0/src/schemarouter/integrations/__init__.py +14 -0
- schemarouter-0.3.0/src/schemarouter/integrations/jev.py +227 -0
- schemarouter-0.3.0/src/schemarouter/integrations/llamaindex.py +173 -0
- schemarouter-0.3.0/src/schemarouter/integrations/opentelemetry.py +179 -0
- schemarouter-0.3.0/src/schemarouter/openapi_compatibility.py +423 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/planner.py +156 -5
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/policy.py +34 -10
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/runs.py +32 -1
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/runtime.py +61 -11
- schemarouter-0.3.0/tests/test_adapter_plugins.py +126 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/tests/test_adapters.py +152 -0
- schemarouter-0.3.0/tests/test_benchmark_corpus.py +69 -0
- schemarouter-0.3.0/tests/test_decision_policy.py +21 -0
- schemarouter-0.3.0/tests/test_decisions.py +114 -0
- schemarouter-0.3.0/tests/test_execution_controls.py +355 -0
- schemarouter-0.3.0/tests/test_jev_integration.py +216 -0
- schemarouter-0.3.0/tests/test_llamaindex_integration.py +81 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/tests/test_mcp_integration.py +3 -0
- schemarouter-0.3.0/tests/test_mcp_transport.py +134 -0
- schemarouter-0.3.0/tests/test_openapi_compatibility.py +149 -0
- schemarouter-0.3.0/tests/test_opentelemetry_integration.py +152 -0
- schemarouter-0.3.0/tests/test_package_metadata.py +7 -0
- schemarouter-0.3.0/tests/test_planner.py +218 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/tests/test_public_api.py +25 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/tests/test_registry.py +66 -0
- schemarouter-0.3.0/tests/test_release_metadata.py +113 -0
- schemarouter-0.2.0a1/.github/workflows/ci.yml +0 -90
- schemarouter-0.2.0a1/.github/workflows/release.yml +0 -113
- schemarouter-0.2.0a1/CHANGELOG.md +0 -45
- schemarouter-0.2.0a1/docs/changelog.md +0 -36
- schemarouter-0.2.0a1/docs/compatibility.md +0 -48
- schemarouter-0.2.0a1/docs/getting-started/installation.md +0 -61
- schemarouter-0.2.0a1/docs/guides/execution-policy.md +0 -58
- schemarouter-0.2.0a1/docs/guides/mcp.md +0 -62
- schemarouter-0.2.0a1/docs/release-checklist.md +0 -49
- schemarouter-0.2.0a1/src/schemarouter/integrations/__init__.py +0 -3
- schemarouter-0.2.0a1/tests/test_package_metadata.py +0 -5
- schemarouter-0.2.0a1/tests/test_planner.py +0 -79
- schemarouter-0.2.0a1/tests/test_release_metadata.py +0 -47
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/.github/dependabot.yml +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/.github/workflows/compatibility.yml +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/.github/workflows/docs.yml +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/.gitignore +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/LICENSE +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/assets/brand/README.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/assets/brand/schemarouter-lockup-dark.svg +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/assets/brand/schemarouter-lockup-light.svg +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/assets/brand/schemarouter-mark-dark.svg +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/assets/brand/schemarouter-mark-light.svg +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/assets/brand/schemarouter-social-preview.svg +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/concepts/adapters.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/concepts/execution.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/concepts/planning.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/concepts/registry.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/concepts/schema-router.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/concepts/tool-endpoint.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/getting-started/ingestion-paths.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/getting-started/quickstart.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/guides/html-documentation.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/guides/model-analyzer.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/guides/optimade.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/guides/python-tools.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/guides/retry.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/license.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/project/brand.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/recipes/many-tools.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/recipes/mcp-agent.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/recipes/openapi-agent.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/reference/models.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/reference/planning.md +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/docs/stylesheets/brand.css +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/examples/quickstart.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/scripts/live_openapi_smoke.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/scripts/live_optimade_smoke.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/_version.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/adapters/optimade.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/analyzers/__init__.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/analyzers/model.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/integrations/langchain.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/models.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/proposals.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/py.typed +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/registry.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/src/schemarouter/validation.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/tests/fixtures/mcp_http_server.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/tests/test_adapter_registry.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/tests/test_brand_assets.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/tests/test_executor.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/tests/test_ingestion.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/tests/test_langchain_integration.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/tests/test_model_analyzer.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/tests/test_optimade_adapter.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/tests/test_optimade_security.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/tests/test_policy.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/tests/test_proposals.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/tests/test_python_adapter.py +0 -0
- {schemarouter-0.2.0a1 → schemarouter-0.3.0}/tests/test_runtime_interface.py +0 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
test:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
strategy:
|
|
20
|
+
fail-fast: false
|
|
21
|
+
matrix:
|
|
22
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v7
|
|
25
|
+
- uses: actions/setup-python@v7
|
|
26
|
+
with:
|
|
27
|
+
python-version: ${{ matrix.python-version }}
|
|
28
|
+
- run: python -m pip install --upgrade pip
|
|
29
|
+
- run: pip install -e ".[dev]"
|
|
30
|
+
- run: ruff check .
|
|
31
|
+
- run: pytest -q -m "not mcp_integration"
|
|
32
|
+
- run: python examples/quickstart.py
|
|
33
|
+
|
|
34
|
+
windows-smoke:
|
|
35
|
+
runs-on: windows-latest
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v7
|
|
38
|
+
- uses: actions/setup-python@v7
|
|
39
|
+
with:
|
|
40
|
+
python-version: "3.14"
|
|
41
|
+
- run: python -m pip install --upgrade pip
|
|
42
|
+
- run: pip install -e ".[dev]"
|
|
43
|
+
- run: ruff check .
|
|
44
|
+
- run: pytest -q -m "not mcp_integration"
|
|
45
|
+
- run: python examples/quickstart.py
|
|
46
|
+
|
|
47
|
+
typecheck:
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/checkout@v7
|
|
51
|
+
- uses: actions/setup-python@v7
|
|
52
|
+
with:
|
|
53
|
+
python-version: "3.12"
|
|
54
|
+
- run: python -m pip install --upgrade pip
|
|
55
|
+
- run: pip install -e ".[dev,mcp,langchain,llamaindex,jev,otel]"
|
|
56
|
+
- run: pyright
|
|
57
|
+
|
|
58
|
+
coverage:
|
|
59
|
+
runs-on: ubuntu-latest
|
|
60
|
+
steps:
|
|
61
|
+
- uses: actions/checkout@v7
|
|
62
|
+
- uses: actions/setup-python@v7
|
|
63
|
+
with:
|
|
64
|
+
python-version: "3.12"
|
|
65
|
+
- run: python -m pip install --upgrade pip
|
|
66
|
+
- run: pip install -e ".[dev,mcp,langchain,llamaindex,jev,otel]"
|
|
67
|
+
- run: pytest -q --cov=schemarouter --cov-branch --cov-report=term-missing --cov-report=xml:coverage.xml
|
|
68
|
+
- uses: actions/upload-artifact@v7
|
|
69
|
+
with:
|
|
70
|
+
name: coverage-report
|
|
71
|
+
path: coverage.xml
|
|
72
|
+
if-no-files-found: error
|
|
73
|
+
retention-days: 14
|
|
74
|
+
|
|
75
|
+
minimum-dependencies:
|
|
76
|
+
runs-on: ubuntu-latest
|
|
77
|
+
steps:
|
|
78
|
+
- uses: actions/checkout@v7
|
|
79
|
+
- uses: actions/setup-python@v7
|
|
80
|
+
with:
|
|
81
|
+
python-version: "3.10"
|
|
82
|
+
- run: python -m pip install --upgrade pip
|
|
83
|
+
- run: pip install "pytest>=8.2" "pytest-asyncio>=0.23"
|
|
84
|
+
- run: pip install "httpx==0.27.0" "jsonschema==4.23.0" "pydantic==2.8.0" "pyyaml==6.0.0"
|
|
85
|
+
- run: pip install -e . --no-deps
|
|
86
|
+
- run: pytest -q -m "not mcp_integration"
|
|
87
|
+
- run: python examples/quickstart.py
|
|
88
|
+
|
|
89
|
+
package:
|
|
90
|
+
runs-on: ubuntu-latest
|
|
91
|
+
steps:
|
|
92
|
+
- uses: actions/checkout@v7
|
|
93
|
+
- uses: actions/setup-python@v7
|
|
94
|
+
with:
|
|
95
|
+
python-version: "3.12"
|
|
96
|
+
- run: python -m pip install --upgrade pip
|
|
97
|
+
- run: pip install build twine
|
|
98
|
+
- run: python -m build
|
|
99
|
+
- run: twine check dist/*
|
|
100
|
+
- name: Verify license is packaged
|
|
101
|
+
run: |
|
|
102
|
+
python - <<'PY'
|
|
103
|
+
import glob
|
|
104
|
+
import zipfile
|
|
105
|
+
|
|
106
|
+
wheel = glob.glob("dist/*.whl")[0]
|
|
107
|
+
names = zipfile.ZipFile(wheel).namelist()
|
|
108
|
+
assert any(name.endswith("/licenses/LICENSE") for name in names), names
|
|
109
|
+
PY
|
|
110
|
+
- run: python -m venv /tmp/schemarouter-wheel
|
|
111
|
+
- run: /tmp/schemarouter-wheel/bin/python -m pip install --upgrade pip
|
|
112
|
+
- run: /tmp/schemarouter-wheel/bin/pip install dist/*.whl
|
|
113
|
+
- run: /tmp/schemarouter-wheel/bin/python -c "from importlib.metadata import version; import schemarouter; assert schemarouter.__version__ == version('schemarouter')"
|
|
114
|
+
- run: /tmp/schemarouter-wheel/bin/python examples/quickstart.py
|
|
115
|
+
- run: /tmp/schemarouter-wheel/bin/python scripts/benchmark_decision_routing.py
|
|
116
|
+
- run: python -m venv /tmp/schemarouter-sdist
|
|
117
|
+
- run: /tmp/schemarouter-sdist/bin/python -m pip install --upgrade pip
|
|
118
|
+
- run: /tmp/schemarouter-sdist/bin/pip install dist/*.tar.gz
|
|
119
|
+
- run: /tmp/schemarouter-sdist/bin/python -c "from importlib.metadata import version; import schemarouter; assert schemarouter.__version__ == version('schemarouter')"
|
|
120
|
+
- run: /tmp/schemarouter-sdist/bin/python examples/quickstart.py
|
|
121
|
+
|
|
122
|
+
langchain-integration:
|
|
123
|
+
runs-on: ubuntu-latest
|
|
124
|
+
steps:
|
|
125
|
+
- uses: actions/checkout@v7
|
|
126
|
+
- uses: actions/setup-python@v7
|
|
127
|
+
with:
|
|
128
|
+
python-version: "3.12"
|
|
129
|
+
- run: python -m pip install --upgrade pip
|
|
130
|
+
- run: pip install -e ".[dev,langchain]"
|
|
131
|
+
- run: pytest -q tests/test_langchain_integration.py
|
|
132
|
+
- run: python examples/langchain_quickstart.py
|
|
133
|
+
|
|
134
|
+
llamaindex-integration:
|
|
135
|
+
runs-on: ubuntu-latest
|
|
136
|
+
steps:
|
|
137
|
+
- uses: actions/checkout@v7
|
|
138
|
+
- uses: actions/setup-python@v7
|
|
139
|
+
with:
|
|
140
|
+
python-version: "3.12"
|
|
141
|
+
- run: python -m pip install --upgrade pip
|
|
142
|
+
- run: pip install -e ".[dev,llamaindex]"
|
|
143
|
+
- run: pytest -q tests/test_llamaindex_integration.py
|
|
144
|
+
- run: python examples/llamaindex_quickstart.py
|
|
145
|
+
|
|
146
|
+
jev-integration:
|
|
147
|
+
runs-on: ubuntu-latest
|
|
148
|
+
steps:
|
|
149
|
+
- uses: actions/checkout@v7
|
|
150
|
+
- uses: actions/setup-python@v7
|
|
151
|
+
with:
|
|
152
|
+
python-version: "3.12"
|
|
153
|
+
- run: python -m pip install --upgrade pip
|
|
154
|
+
- run: pip install -e ".[dev,jev]"
|
|
155
|
+
- run: pytest -q tests/test_jev_integration.py
|
|
156
|
+
|
|
157
|
+
otel-integration:
|
|
158
|
+
runs-on: ubuntu-latest
|
|
159
|
+
steps:
|
|
160
|
+
- uses: actions/checkout@v7
|
|
161
|
+
- uses: actions/setup-python@v7
|
|
162
|
+
with:
|
|
163
|
+
python-version: "3.12"
|
|
164
|
+
- run: python -m pip install --upgrade pip
|
|
165
|
+
- run: pip install -e ".[dev,otel]"
|
|
166
|
+
- run: pytest -q tests/test_opentelemetry_integration.py
|
|
167
|
+
|
|
168
|
+
mcp-integration:
|
|
169
|
+
runs-on: ubuntu-latest
|
|
170
|
+
steps:
|
|
171
|
+
- uses: actions/checkout@v7
|
|
172
|
+
- uses: actions/setup-python@v7
|
|
173
|
+
with:
|
|
174
|
+
python-version: "3.12"
|
|
175
|
+
- run: python -m pip install --upgrade pip
|
|
176
|
+
- run: pip install -e ".[dev,mcp]"
|
|
177
|
+
- run: pytest -q tests/test_mcp_integration.py
|
|
178
|
+
|
|
179
|
+
docs:
|
|
180
|
+
runs-on: ubuntu-latest
|
|
181
|
+
steps:
|
|
182
|
+
- uses: actions/checkout@v7
|
|
183
|
+
- uses: actions/setup-python@v7
|
|
184
|
+
with:
|
|
185
|
+
python-version: "3.12"
|
|
186
|
+
- run: python -m pip install --upgrade pip
|
|
187
|
+
- run: pip install -e ".[docs]"
|
|
188
|
+
- run: mkdocs build --strict
|
|
189
|
+
env:
|
|
190
|
+
NO_MKDOCS_2_WARNING: "1"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Python Preview
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: python-preview-${{ github.event.pull_request.number || github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
python-preview:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
timeout-minutes: 20
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v7
|
|
22
|
+
- uses: actions/setup-python@v7
|
|
23
|
+
with:
|
|
24
|
+
python-version: "3.15.0-rc.2"
|
|
25
|
+
allow-prereleases: true
|
|
26
|
+
- run: python -m pip install --upgrade pip
|
|
27
|
+
- run: pip install -e ".[dev]"
|
|
28
|
+
- run: ruff check .
|
|
29
|
+
- run: pytest -q -m "not mcp_integration"
|
|
30
|
+
- run: python examples/quickstart.py
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_run:
|
|
5
|
+
workflows: ["CI"]
|
|
6
|
+
types: [completed]
|
|
7
|
+
push:
|
|
8
|
+
tags:
|
|
9
|
+
- "v*"
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
concurrency:
|
|
15
|
+
group: release-${{ github.repository }}
|
|
16
|
+
cancel-in-progress: false
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
prepare:
|
|
20
|
+
if: >-
|
|
21
|
+
(github.event_name == 'workflow_run' &&
|
|
22
|
+
github.event.workflow_run.conclusion == 'success' &&
|
|
23
|
+
github.event.workflow_run.head_branch == 'main') ||
|
|
24
|
+
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v'))
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
permissions:
|
|
27
|
+
contents: write
|
|
28
|
+
outputs:
|
|
29
|
+
should_release: ${{ steps.state.outputs.should_release }}
|
|
30
|
+
version: ${{ steps.metadata.outputs.version }}
|
|
31
|
+
tag: ${{ steps.metadata.outputs.tag }}
|
|
32
|
+
release_sha: ${{ steps.metadata.outputs.release_sha }}
|
|
33
|
+
release_notes: ${{ steps.metadata.outputs.release_notes }}
|
|
34
|
+
prerelease: ${{ steps.metadata.outputs.prerelease }}
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v7
|
|
37
|
+
with:
|
|
38
|
+
ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.ref_name }}
|
|
39
|
+
fetch-depth: 0
|
|
40
|
+
|
|
41
|
+
- name: Verify successful CI still refers to current main
|
|
42
|
+
if: github.event_name == 'workflow_run'
|
|
43
|
+
env:
|
|
44
|
+
TESTED_SHA: ${{ github.event.workflow_run.head_sha }}
|
|
45
|
+
run: |
|
|
46
|
+
git fetch origin main --tags
|
|
47
|
+
current_main="$(git rev-parse origin/main)"
|
|
48
|
+
if [[ "$current_main" != "$TESTED_SHA" ]]; then
|
|
49
|
+
echo "CI passed for stale main SHA $TESTED_SHA; current main is $current_main."
|
|
50
|
+
echo "SKIP_RELEASE=true" >> "$GITHUB_ENV"
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
- name: Resolve release metadata and target SHA
|
|
54
|
+
id: metadata
|
|
55
|
+
env:
|
|
56
|
+
EVENT_NAME: ${{ github.event_name }}
|
|
57
|
+
TESTED_SHA: ${{ github.event.workflow_run.head_sha }}
|
|
58
|
+
PUSHED_TAG: ${{ github.ref_name }}
|
|
59
|
+
run: |
|
|
60
|
+
python - <<'PY'
|
|
61
|
+
import os
|
|
62
|
+
import re
|
|
63
|
+
import subprocess
|
|
64
|
+
import tomllib
|
|
65
|
+
from pathlib import Path
|
|
66
|
+
|
|
67
|
+
output_path = Path(os.environ["GITHUB_OUTPUT"])
|
|
68
|
+
|
|
69
|
+
def emit(**values: object) -> None:
|
|
70
|
+
with output_path.open("a", encoding="utf-8") as output:
|
|
71
|
+
for key, value in values.items():
|
|
72
|
+
print(f"{key}={value}", file=output)
|
|
73
|
+
|
|
74
|
+
if os.environ.get("SKIP_RELEASE") == "true":
|
|
75
|
+
emit(releasable="false")
|
|
76
|
+
raise SystemExit(0)
|
|
77
|
+
|
|
78
|
+
def read_version_at(ref: str | None = None) -> str:
|
|
79
|
+
if ref is None:
|
|
80
|
+
content = Path("pyproject.toml").read_bytes()
|
|
81
|
+
else:
|
|
82
|
+
content = subprocess.check_output(
|
|
83
|
+
["git", "show", f"{ref}:pyproject.toml"]
|
|
84
|
+
)
|
|
85
|
+
return tomllib.loads(content.decode("utf-8"))["project"]["version"]
|
|
86
|
+
|
|
87
|
+
version = read_version_at()
|
|
88
|
+
if ".dev" in version:
|
|
89
|
+
emit(releasable="false")
|
|
90
|
+
raise SystemExit(0)
|
|
91
|
+
|
|
92
|
+
if not re.fullmatch(r"\d+\.\d+\.\d+(?:(?:a|b|rc)\d+)?", version):
|
|
93
|
+
raise SystemExit(
|
|
94
|
+
f"unsupported automatic release version format: {version!r}"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
release_notes = Path("docs") / "releases" / f"{version}.md"
|
|
98
|
+
if not release_notes.is_file():
|
|
99
|
+
raise SystemExit(f"missing release notes: {release_notes}")
|
|
100
|
+
|
|
101
|
+
changelog = Path("CHANGELOG.md").read_text(encoding="utf-8")
|
|
102
|
+
if f"## {version} -" not in changelog:
|
|
103
|
+
raise SystemExit(
|
|
104
|
+
f"CHANGELOG.md is missing a dated {version} release heading"
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
tag = f"v{version}"
|
|
108
|
+
event_name = os.environ["EVENT_NAME"]
|
|
109
|
+
|
|
110
|
+
if event_name == "push":
|
|
111
|
+
pushed_tag = os.environ["PUSHED_TAG"]
|
|
112
|
+
if pushed_tag != tag:
|
|
113
|
+
raise SystemExit(
|
|
114
|
+
f"tag {pushed_tag!r} does not match package version {tag!r}"
|
|
115
|
+
)
|
|
116
|
+
subprocess.check_call(["git", "fetch", "origin", "tag", tag, "--force"])
|
|
117
|
+
release_sha = subprocess.check_output(
|
|
118
|
+
["git", "rev-list", "-n", "1", tag],
|
|
119
|
+
text=True,
|
|
120
|
+
).strip()
|
|
121
|
+
else:
|
|
122
|
+
tested_sha = os.environ["TESTED_SHA"]
|
|
123
|
+
remote = subprocess.run(
|
|
124
|
+
[
|
|
125
|
+
"git",
|
|
126
|
+
"ls-remote",
|
|
127
|
+
"--exit-code",
|
|
128
|
+
"--tags",
|
|
129
|
+
"origin",
|
|
130
|
+
f"refs/tags/{tag}",
|
|
131
|
+
],
|
|
132
|
+
stdout=subprocess.DEVNULL,
|
|
133
|
+
stderr=subprocess.DEVNULL,
|
|
134
|
+
check=False,
|
|
135
|
+
)
|
|
136
|
+
if remote.returncode == 0:
|
|
137
|
+
subprocess.check_call(
|
|
138
|
+
["git", "fetch", "origin", "tag", tag, "--force"]
|
|
139
|
+
)
|
|
140
|
+
release_sha = subprocess.check_output(
|
|
141
|
+
["git", "rev-list", "-n", "1", tag],
|
|
142
|
+
text=True,
|
|
143
|
+
).strip()
|
|
144
|
+
if release_sha != tested_sha:
|
|
145
|
+
ancestor = subprocess.run(
|
|
146
|
+
[
|
|
147
|
+
"git",
|
|
148
|
+
"merge-base",
|
|
149
|
+
"--is-ancestor",
|
|
150
|
+
release_sha,
|
|
151
|
+
tested_sha,
|
|
152
|
+
],
|
|
153
|
+
check=False,
|
|
154
|
+
)
|
|
155
|
+
if ancestor.returncode != 0:
|
|
156
|
+
raise SystemExit(
|
|
157
|
+
f"existing {tag} points to {release_sha}, which is not "
|
|
158
|
+
f"an ancestor of green main {tested_sha}"
|
|
159
|
+
)
|
|
160
|
+
tagged_version = read_version_at(release_sha)
|
|
161
|
+
if tagged_version != version:
|
|
162
|
+
raise SystemExit(
|
|
163
|
+
f"existing {tag} contains package version "
|
|
164
|
+
f"{tagged_version!r}, expected {version!r}"
|
|
165
|
+
)
|
|
166
|
+
else:
|
|
167
|
+
release_sha = tested_sha
|
|
168
|
+
|
|
169
|
+
prerelease = bool(re.search(r"(?:a|b|rc)\d+(?:$|[.+-])", version))
|
|
170
|
+
emit(
|
|
171
|
+
releasable="true",
|
|
172
|
+
version=version,
|
|
173
|
+
tag=tag,
|
|
174
|
+
release_sha=release_sha,
|
|
175
|
+
release_notes=release_notes.as_posix(),
|
|
176
|
+
prerelease="true" if prerelease else "false",
|
|
177
|
+
)
|
|
178
|
+
PY
|
|
179
|
+
|
|
180
|
+
- name: Create annotated tag when missing
|
|
181
|
+
if: >-
|
|
182
|
+
github.event_name == 'workflow_run' &&
|
|
183
|
+
steps.metadata.outputs.releasable == 'true'
|
|
184
|
+
env:
|
|
185
|
+
RELEASE_SHA: ${{ steps.metadata.outputs.release_sha }}
|
|
186
|
+
RELEASE_TAG: ${{ steps.metadata.outputs.tag }}
|
|
187
|
+
RELEASE_VERSION: ${{ steps.metadata.outputs.version }}
|
|
188
|
+
run: |
|
|
189
|
+
if git ls-remote --exit-code --tags origin "refs/tags/$RELEASE_TAG" >/dev/null 2>&1; then
|
|
190
|
+
echo "$RELEASE_TAG already exists."
|
|
191
|
+
exit 0
|
|
192
|
+
fi
|
|
193
|
+
|
|
194
|
+
git config user.name "github-actions[bot]"
|
|
195
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
196
|
+
git tag -a "$RELEASE_TAG" "$RELEASE_SHA" -m "SchemaRouter $RELEASE_VERSION"
|
|
197
|
+
git push origin "refs/tags/$RELEASE_TAG"
|
|
198
|
+
|
|
199
|
+
- name: Check whether GitHub release already exists
|
|
200
|
+
id: state
|
|
201
|
+
if: steps.metadata.outputs.releasable == 'true'
|
|
202
|
+
env:
|
|
203
|
+
GH_TOKEN: ${{ github.token }}
|
|
204
|
+
RELEASE_TAG: ${{ steps.metadata.outputs.tag }}
|
|
205
|
+
run: |
|
|
206
|
+
if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then
|
|
207
|
+
echo "should_release=false" >> "$GITHUB_OUTPUT"
|
|
208
|
+
echo "$RELEASE_TAG is already published on GitHub."
|
|
209
|
+
else
|
|
210
|
+
echo "should_release=true" >> "$GITHUB_OUTPUT"
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
build:
|
|
214
|
+
needs: prepare
|
|
215
|
+
if: needs.prepare.outputs.should_release == 'true'
|
|
216
|
+
runs-on: ubuntu-latest
|
|
217
|
+
outputs:
|
|
218
|
+
version: ${{ needs.prepare.outputs.version }}
|
|
219
|
+
release_notes: ${{ needs.prepare.outputs.release_notes }}
|
|
220
|
+
prerelease: ${{ needs.prepare.outputs.prerelease }}
|
|
221
|
+
tag: ${{ needs.prepare.outputs.tag }}
|
|
222
|
+
release_sha: ${{ needs.prepare.outputs.release_sha }}
|
|
223
|
+
steps:
|
|
224
|
+
- uses: actions/checkout@v7
|
|
225
|
+
with:
|
|
226
|
+
ref: ${{ needs.prepare.outputs.release_sha }}
|
|
227
|
+
- uses: actions/setup-python@v7
|
|
228
|
+
with:
|
|
229
|
+
python-version: "3.12"
|
|
230
|
+
- name: Verify release checkout metadata
|
|
231
|
+
env:
|
|
232
|
+
RELEASE_VERSION: ${{ needs.prepare.outputs.version }}
|
|
233
|
+
run: |
|
|
234
|
+
python - <<'PY'
|
|
235
|
+
import os
|
|
236
|
+
import tomllib
|
|
237
|
+
from pathlib import Path
|
|
238
|
+
|
|
239
|
+
version = tomllib.loads(Path("pyproject.toml").read_text())["project"]["version"]
|
|
240
|
+
expected = os.environ["RELEASE_VERSION"]
|
|
241
|
+
if version != expected:
|
|
242
|
+
raise SystemExit(
|
|
243
|
+
f"release checkout version {version!r} does not match {expected!r}"
|
|
244
|
+
)
|
|
245
|
+
PY
|
|
246
|
+
- run: python -m pip install --upgrade pip
|
|
247
|
+
- run: pip install build twine
|
|
248
|
+
- run: python -m build
|
|
249
|
+
- run: twine check dist/*
|
|
250
|
+
- name: Verify release artifacts
|
|
251
|
+
env:
|
|
252
|
+
RELEASE_VERSION: ${{ needs.prepare.outputs.version }}
|
|
253
|
+
run: |
|
|
254
|
+
python - <<'PY'
|
|
255
|
+
import glob
|
|
256
|
+
import os
|
|
257
|
+
import subprocess
|
|
258
|
+
import sys
|
|
259
|
+
import tempfile
|
|
260
|
+
import venv
|
|
261
|
+
import zipfile
|
|
262
|
+
from pathlib import Path
|
|
263
|
+
|
|
264
|
+
wheel = glob.glob("dist/*.whl")[0]
|
|
265
|
+
sdist = glob.glob("dist/*.tar.gz")[0]
|
|
266
|
+
|
|
267
|
+
wheel_names = zipfile.ZipFile(wheel).namelist()
|
|
268
|
+
assert any(name.endswith("/licenses/LICENSE") for name in wheel_names), wheel_names
|
|
269
|
+
|
|
270
|
+
import tarfile
|
|
271
|
+
|
|
272
|
+
with tarfile.open(sdist, "r:gz") as archive:
|
|
273
|
+
sdist_names = archive.getnames()
|
|
274
|
+
assert any(name.endswith("/LICENSE") for name in sdist_names), sdist_names
|
|
275
|
+
|
|
276
|
+
for artifact in (wheel, sdist):
|
|
277
|
+
with tempfile.TemporaryDirectory() as root:
|
|
278
|
+
env = Path(root) / "venv"
|
|
279
|
+
venv.EnvBuilder(with_pip=True).create(env)
|
|
280
|
+
python = env / (
|
|
281
|
+
"Scripts/python.exe" if sys.platform == "win32" else "bin/python"
|
|
282
|
+
)
|
|
283
|
+
subprocess.check_call([str(python), "-m", "pip", "install", artifact])
|
|
284
|
+
subprocess.check_call(
|
|
285
|
+
[
|
|
286
|
+
str(python),
|
|
287
|
+
"-c",
|
|
288
|
+
(
|
|
289
|
+
"from importlib.metadata import version; "
|
|
290
|
+
"import schemarouter; "
|
|
291
|
+
f"assert schemarouter.__version__ == "
|
|
292
|
+
f"{os.environ['RELEASE_VERSION']!r}; "
|
|
293
|
+
f"assert version('schemarouter') == "
|
|
294
|
+
f"{os.environ['RELEASE_VERSION']!r}"
|
|
295
|
+
),
|
|
296
|
+
]
|
|
297
|
+
)
|
|
298
|
+
subprocess.check_call([str(python), "examples/quickstart.py"])
|
|
299
|
+
PY
|
|
300
|
+
- uses: actions/upload-artifact@v7
|
|
301
|
+
with:
|
|
302
|
+
name: python-dist
|
|
303
|
+
path: dist/
|
|
304
|
+
if-no-files-found: error
|
|
305
|
+
retention-days: 7
|
|
306
|
+
|
|
307
|
+
github-release:
|
|
308
|
+
needs: [prepare, build]
|
|
309
|
+
if: needs.prepare.outputs.should_release == 'true'
|
|
310
|
+
permissions:
|
|
311
|
+
contents: write
|
|
312
|
+
runs-on: ubuntu-latest
|
|
313
|
+
steps:
|
|
314
|
+
- uses: actions/checkout@v7
|
|
315
|
+
with:
|
|
316
|
+
ref: ${{ needs.prepare.outputs.release_sha }}
|
|
317
|
+
- uses: actions/download-artifact@v8
|
|
318
|
+
with:
|
|
319
|
+
name: python-dist
|
|
320
|
+
path: dist
|
|
321
|
+
- name: Create GitHub release
|
|
322
|
+
env:
|
|
323
|
+
GH_TOKEN: ${{ github.token }}
|
|
324
|
+
RELEASE_TAG: ${{ needs.prepare.outputs.tag }}
|
|
325
|
+
RELEASE_VERSION: ${{ needs.prepare.outputs.version }}
|
|
326
|
+
RELEASE_NOTES: ${{ needs.prepare.outputs.release_notes }}
|
|
327
|
+
IS_PRERELEASE: ${{ needs.prepare.outputs.prerelease }}
|
|
328
|
+
run: |
|
|
329
|
+
prerelease_args=()
|
|
330
|
+
if [[ "$IS_PRERELEASE" == "true" ]]; then
|
|
331
|
+
prerelease_args+=(--prerelease)
|
|
332
|
+
fi
|
|
333
|
+
|
|
334
|
+
gh release create "$RELEASE_TAG" dist/* --title "SchemaRouter $RELEASE_VERSION" --notes-file "$RELEASE_NOTES" "${prerelease_args[@]}" --verify-tag
|
|
335
|
+
|
|
336
|
+
pypi:
|
|
337
|
+
needs: [prepare, build]
|
|
338
|
+
if: needs.prepare.outputs.should_release == 'true'
|
|
339
|
+
permissions:
|
|
340
|
+
id-token: write
|
|
341
|
+
runs-on: ubuntu-latest
|
|
342
|
+
environment:
|
|
343
|
+
name: pypi
|
|
344
|
+
url: https://pypi.org/project/schemarouter/
|
|
345
|
+
steps:
|
|
346
|
+
- uses: actions/download-artifact@v8
|
|
347
|
+
with:
|
|
348
|
+
name: python-dist
|
|
349
|
+
path: dist
|
|
350
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
351
|
+
with:
|
|
352
|
+
packages-dir: dist/
|
|
353
|
+
skip-existing: true
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to SchemaRouter are documented here.
|
|
4
|
+
|
|
5
|
+
The project is pre-1.0 and follows the compatibility rules in
|
|
6
|
+
[`docs/versioning.md`](docs/versioning.md).
|
|
7
|
+
|
|
8
|
+
## Unreleased
|
|
9
|
+
|
|
10
|
+
## 0.3.0 - 2026-09-22
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- promoted the 0.3 line to the first non-prerelease release after the 0.3.0a1 validation cycle;
|
|
15
|
+
- standard PyPI installation no longer requires `--pre`;
|
|
16
|
+
- package maturity metadata now identifies the project as beta while retaining the documented
|
|
17
|
+
pre-1.0 compatibility policy;
|
|
18
|
+
- GitHub Pages deployment and protected-main required-check enforcement are now active repository
|
|
19
|
+
controls.
|
|
20
|
+
|
|
21
|
+
### Compatibility
|
|
22
|
+
|
|
23
|
+
- no intentional public API changes were introduced relative to 0.3.0a1;
|
|
24
|
+
- the 0.x line remains pre-1.0, so later minor releases may still include deliberate, documented
|
|
25
|
+
compatibility changes.
|
|
26
|
+
|
|
27
|
+
## 0.3.0a1 - 2026-09-22
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- provider-neutral bounded `DecisionBackend` contracts with deterministic fallback;
|
|
32
|
+
- optional `JevDecisionBackend` backed by TypeSafe System One through `schemarouter[jev]`;
|
|
33
|
+
- optional LlamaIndex `FunctionTool` integration;
|
|
34
|
+
- runnable LangChain and LlamaIndex integration examples enforced in CI;
|
|
35
|
+
- provider-neutral decision-routing benchmark harness covering accuracy, abstention, latency,
|
|
36
|
+
token usage, and optional cost estimation;
|
|
37
|
+
- explicit ecosystem compatibility matrix and integration maintenance policy;
|
|
38
|
+
- Korean project overview in `README.ko.md`;
|
|
39
|
+
- Python 3.13 and 3.14 core CI coverage;
|
|
40
|
+
- a non-blocking Python 3.15 release-candidate preview;
|
|
41
|
+
- a Windows + Python 3.14 smoke surface;
|
|
42
|
+
- a Pyright static-type gate for the typed package surface;
|
|
43
|
+
- full-suite branch-coverage reporting with an 82% blocking floor;
|
|
44
|
+
- declared lower-bound runtime dependency testing;
|
|
45
|
+
- clean wheel and sdist installation smoke tests;
|
|
46
|
+
- a checked-in 144-case multilingual/adversarial decision-routing corpus with JSON/CSV output,
|
|
47
|
+
category metrics, invalid-plan rate, abstention/fallback tracking, and p50/p95 latency;
|
|
48
|
+
- machine-readable OpenAPI compatibility reports for partial/unsupported constructs;
|
|
49
|
+
- authenticated MCP trusted-header and custom client-factory transport boundaries;
|
|
50
|
+
- trusted sync/async per-call approval callbacks;
|
|
51
|
+
- per-run execution budgets for logical calls, attempts, remote attempts, elapsed time, per-tool
|
|
52
|
+
quotas, and application-defined cost units;
|
|
53
|
+
- optional privacy-preserving OpenTelemetry run/tool span export;
|
|
54
|
+
- explicit allowlisted third-party adapter plugins through `schemarouter.adapters` entry points.
|
|
55
|
+
|
|
56
|
+
### Changed
|
|
57
|
+
|
|
58
|
+
- release preparation now uses explicit PEP 440 release versions while normal post-release
|
|
59
|
+
development returns to a `.dev0` version;
|
|
60
|
+
- release automation now derives version, release title, release-note path, prerelease state, and
|
|
61
|
+
artifact verification from `pyproject.toml` instead of hard-coded release literals;
|
|
62
|
+
- release publication now consumes a successful current-`main` CI result, verifies or creates the
|
|
63
|
+
annotated version tag, and clean-installs both wheel and sdist artifacts before publication;
|
|
64
|
+
- superseded pull-request CI runs are cancelled automatically to avoid stale validation consuming
|
|
65
|
+
runner capacity;
|
|
66
|
+
- releasable `main` commits are automatically published by the top-level Release workflow only
|
|
67
|
+
after green CI, current-head verification, release-note/changelog validation, and duplicate-tag
|
|
68
|
+
checks; existing unpublished tags may be resumed only from a compatible ancestor SHA.
|
|
69
|
+
|
|
70
|
+
### Security
|
|
71
|
+
|
|
72
|
+
- OpenAPI runtime responses are now streamed through a bounded reader with a 16 MiB default limit,
|
|
73
|
+
enforcing the bound both from declared `Content-Length` and actual bytes received;
|
|
74
|
+
- Jev option IDs are validated before confidence-based abstention, so unknown IDs always fail
|
|
75
|
+
closed;
|
|
76
|
+
- Jev credentials remain SDK client configuration and are never placed in model state;
|
|
77
|
+
- `DecisionOption.metadata` is not forwarded to Jev;
|
|
78
|
+
- provider failures can deterministically fall back without weakening execution policy;
|
|
79
|
+
- MCP credentials are rejected in URLs and remain outside tool/planner metadata;
|
|
80
|
+
- MCP protocol-controlled headers cannot be overridden through trusted runtime headers;
|
|
81
|
+
- adapter entry points are metadata-only during discovery and are never auto-imported;
|
|
82
|
+
- OpenTelemetry export omits payload values, RunConfig metadata, tags, and exception messages;
|
|
83
|
+
- approval callback failures and execution budget exhaustion fail closed.
|
|
84
|
+
|
|
85
|
+
## 0.2.0a1 - 2026-09-20
|
|
86
|
+
|
|
87
|
+
### Added
|
|
88
|
+
|
|
89
|
+
- pluggable structured-source `AdapterRegistry` with explicit and priority-based auto discovery;
|
|
90
|
+
- OPTIMADE v1 discovery through base and entry-type info endpoints;
|
|
91
|
+
- OPTIMADE field-aware execution that maps planned fields to `response_fields`;
|
|
92
|
+
- call-aware invoker support for protocol adapters that need the full `ToolCall`;
|
|
93
|
+
- typed tool, endpoint, parameter, response-field, plan, and result contracts;
|
|
94
|
+
- namespaced versioned registry;
|
|
95
|
+
- schema-aware planning with recall-preserving field projection;
|
|
96
|
+
- provider-neutral model-assisted query analysis;
|
|
97
|
+
- OpenAPI 3.x URL ingestion and guarded HTTP execution;
|
|
98
|
+
- MCP tool discovery and execution;
|
|
99
|
+
- evidence-grounded proposals for human-readable API documentation;
|
|
100
|
+
- runtime JSON Schema validation for arguments and raw outputs;
|
|
101
|
+
- schema and invoker-binding drift detection;
|
|
102
|
+
- fail-closed execution policy for mutations, destructive calls, and unclassified remote tools;
|
|
103
|
+
- Runnable-style `invoke`, `batch`, `stream`, and typed event APIs;
|
|
104
|
+
- run configuration, concurrency control, and safe retry policy;
|
|
105
|
+
- typed Python callable registration with `@schema_tool`;
|
|
106
|
+
- optional LangChain `StructuredTool` integration;
|
|
107
|
+
- framework maturity, architecture, release, and versioning documentation;
|
|
108
|
+
- MIT licensing and package metadata;
|
|
109
|
+
- MkDocs Material documentation site with guides, recipes, and generated API reference;
|
|
110
|
+
- SchemaRouter brace-and-routing-hub brand system with light/dark marks, lockups, favicon, and
|
|
111
|
+
social preview source artwork.
|
|
112
|
+
|
|
113
|
+
### Security
|
|
114
|
+
|
|
115
|
+
- separated schema-fetch credentials from runtime API credentials;
|
|
116
|
+
- restricted schema redirects and runtime API origins;
|
|
117
|
+
- blocked model/tool control of sensitive runtime headers;
|
|
118
|
+
- redacted event payloads by default;
|
|
119
|
+
- prevented automatic retries for non-read-only calls.
|