schemarouter 0.2.0a1__tar.gz → 0.3.0a1__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 (150) hide show
  1. schemarouter-0.3.0a1/.github/workflows/auto-tag-release.yml +99 -0
  2. schemarouter-0.3.0a1/.github/workflows/ci.yml +205 -0
  3. schemarouter-0.3.0a1/.github/workflows/release.yml +161 -0
  4. schemarouter-0.3.0a1/CHANGELOG.md +103 -0
  5. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/CONTRIBUTING.md +16 -9
  6. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/PKG-INFO +125 -11
  7. schemarouter-0.3.0a1/README.ko.md +365 -0
  8. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/README.md +113 -10
  9. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/SECURITY.md +35 -8
  10. schemarouter-0.3.0a1/benchmarks/decision-routing-v1.json +1010 -0
  11. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/adapter-authoring.md +20 -0
  12. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/architecture.md +68 -8
  13. schemarouter-0.3.0a1/docs/changelog.md +103 -0
  14. schemarouter-0.3.0a1/docs/compatibility.md +113 -0
  15. schemarouter-0.3.0a1/docs/concepts/decision-backends.md +100 -0
  16. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/contributing.md +16 -9
  17. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/framework-maturity.md +49 -26
  18. schemarouter-0.3.0a1/docs/getting-started/installation.md +81 -0
  19. schemarouter-0.3.0a1/docs/guides/adapter-plugins.md +76 -0
  20. schemarouter-0.3.0a1/docs/guides/decision-benchmark.md +116 -0
  21. schemarouter-0.3.0a1/docs/guides/execution-policy.md +139 -0
  22. schemarouter-0.3.0a1/docs/guides/mcp.md +109 -0
  23. schemarouter-0.3.0a1/docs/guides/openapi-compatibility.md +60 -0
  24. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/guides/openapi.md +10 -0
  25. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/guides/runtime.md +18 -0
  26. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/index.md +20 -8
  27. schemarouter-0.3.0a1/docs/integrations/jev.md +132 -0
  28. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/integrations/langchain.md +28 -1
  29. schemarouter-0.3.0a1/docs/integrations/llamaindex.md +57 -0
  30. schemarouter-0.3.0a1/docs/integrations/opentelemetry.md +67 -0
  31. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/reference/api.md +25 -0
  32. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/reference/runtime.md +4 -0
  33. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/release-checklist.md +29 -6
  34. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/releases/0.2.0a1.md +2 -2
  35. schemarouter-0.3.0a1/docs/releases/0.3.0a1.md +108 -0
  36. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/security/threat-model.md +36 -9
  37. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/versioning.md +25 -2
  38. schemarouter-0.3.0a1/examples/langchain_quickstart.py +25 -0
  39. schemarouter-0.3.0a1/examples/llamaindex_quickstart.py +25 -0
  40. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/mkdocs.yml +7 -0
  41. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/pyproject.toml +30 -1
  42. schemarouter-0.3.0a1/scripts/benchmark_decision_routing.py +548 -0
  43. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/__init__.py +54 -2
  44. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/adapters/__init__.py +27 -1
  45. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/adapters/base.py +1 -0
  46. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/adapters/mcp.py +119 -19
  47. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/adapters/openapi.py +71 -15
  48. schemarouter-0.3.0a1/src/schemarouter/adapters/plugins.py +133 -0
  49. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/adapters/python.py +3 -2
  50. schemarouter-0.3.0a1/src/schemarouter/decision_policy.py +28 -0
  51. schemarouter-0.3.0a1/src/schemarouter/decisions.py +178 -0
  52. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/errors.py +8 -0
  53. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/executor.py +173 -12
  54. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/ingestion.py +14 -6
  55. schemarouter-0.3.0a1/src/schemarouter/integrations/__init__.py +14 -0
  56. schemarouter-0.3.0a1/src/schemarouter/integrations/jev.py +227 -0
  57. schemarouter-0.3.0a1/src/schemarouter/integrations/llamaindex.py +173 -0
  58. schemarouter-0.3.0a1/src/schemarouter/integrations/opentelemetry.py +179 -0
  59. schemarouter-0.3.0a1/src/schemarouter/openapi_compatibility.py +423 -0
  60. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/planner.py +156 -5
  61. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/policy.py +34 -10
  62. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/runs.py +32 -1
  63. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/runtime.py +61 -11
  64. schemarouter-0.3.0a1/tests/test_adapter_plugins.py +126 -0
  65. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/tests/test_adapters.py +152 -0
  66. schemarouter-0.3.0a1/tests/test_benchmark_corpus.py +69 -0
  67. schemarouter-0.3.0a1/tests/test_decision_policy.py +21 -0
  68. schemarouter-0.3.0a1/tests/test_decisions.py +114 -0
  69. schemarouter-0.3.0a1/tests/test_execution_controls.py +355 -0
  70. schemarouter-0.3.0a1/tests/test_jev_integration.py +216 -0
  71. schemarouter-0.3.0a1/tests/test_llamaindex_integration.py +81 -0
  72. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/tests/test_mcp_integration.py +3 -0
  73. schemarouter-0.3.0a1/tests/test_mcp_transport.py +134 -0
  74. schemarouter-0.3.0a1/tests/test_openapi_compatibility.py +149 -0
  75. schemarouter-0.3.0a1/tests/test_opentelemetry_integration.py +152 -0
  76. schemarouter-0.3.0a1/tests/test_package_metadata.py +7 -0
  77. schemarouter-0.3.0a1/tests/test_planner.py +218 -0
  78. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/tests/test_public_api.py +25 -0
  79. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/tests/test_registry.py +66 -0
  80. schemarouter-0.3.0a1/tests/test_release_metadata.py +123 -0
  81. schemarouter-0.2.0a1/.github/workflows/ci.yml +0 -90
  82. schemarouter-0.2.0a1/.github/workflows/release.yml +0 -113
  83. schemarouter-0.2.0a1/CHANGELOG.md +0 -45
  84. schemarouter-0.2.0a1/docs/changelog.md +0 -36
  85. schemarouter-0.2.0a1/docs/compatibility.md +0 -48
  86. schemarouter-0.2.0a1/docs/getting-started/installation.md +0 -61
  87. schemarouter-0.2.0a1/docs/guides/execution-policy.md +0 -58
  88. schemarouter-0.2.0a1/docs/guides/mcp.md +0 -62
  89. schemarouter-0.2.0a1/src/schemarouter/integrations/__init__.py +0 -3
  90. schemarouter-0.2.0a1/tests/test_package_metadata.py +0 -5
  91. schemarouter-0.2.0a1/tests/test_planner.py +0 -79
  92. schemarouter-0.2.0a1/tests/test_release_metadata.py +0 -47
  93. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/.github/dependabot.yml +0 -0
  94. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/.github/workflows/compatibility.yml +0 -0
  95. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/.github/workflows/docs.yml +0 -0
  96. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/.gitignore +0 -0
  97. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/LICENSE +0 -0
  98. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/assets/brand/README.md +0 -0
  99. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/assets/brand/schemarouter-lockup-dark.svg +0 -0
  100. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/assets/brand/schemarouter-lockup-light.svg +0 -0
  101. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/assets/brand/schemarouter-mark-dark.svg +0 -0
  102. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/assets/brand/schemarouter-mark-light.svg +0 -0
  103. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/assets/brand/schemarouter-social-preview.svg +0 -0
  104. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/concepts/adapters.md +0 -0
  105. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/concepts/execution.md +0 -0
  106. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/concepts/planning.md +0 -0
  107. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/concepts/registry.md +0 -0
  108. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/concepts/schema-router.md +0 -0
  109. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/concepts/tool-endpoint.md +0 -0
  110. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/getting-started/ingestion-paths.md +0 -0
  111. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/getting-started/quickstart.md +0 -0
  112. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/guides/html-documentation.md +0 -0
  113. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/guides/model-analyzer.md +0 -0
  114. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/guides/optimade.md +0 -0
  115. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/guides/python-tools.md +0 -0
  116. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/guides/retry.md +0 -0
  117. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/license.md +0 -0
  118. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/project/brand.md +0 -0
  119. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/recipes/many-tools.md +0 -0
  120. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/recipes/mcp-agent.md +0 -0
  121. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/recipes/openapi-agent.md +0 -0
  122. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/reference/models.md +0 -0
  123. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/reference/planning.md +0 -0
  124. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/docs/stylesheets/brand.css +0 -0
  125. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/examples/quickstart.py +0 -0
  126. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/scripts/live_openapi_smoke.py +0 -0
  127. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/scripts/live_optimade_smoke.py +0 -0
  128. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/_version.py +0 -0
  129. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/adapters/optimade.py +0 -0
  130. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/analyzers/__init__.py +0 -0
  131. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/analyzers/model.py +0 -0
  132. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/integrations/langchain.py +0 -0
  133. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/models.py +0 -0
  134. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/proposals.py +0 -0
  135. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/py.typed +0 -0
  136. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/registry.py +0 -0
  137. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/src/schemarouter/validation.py +0 -0
  138. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/tests/fixtures/mcp_http_server.py +0 -0
  139. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/tests/test_adapter_registry.py +0 -0
  140. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/tests/test_brand_assets.py +0 -0
  141. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/tests/test_executor.py +0 -0
  142. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/tests/test_ingestion.py +0 -0
  143. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/tests/test_langchain_integration.py +0 -0
  144. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/tests/test_model_analyzer.py +0 -0
  145. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/tests/test_optimade_adapter.py +0 -0
  146. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/tests/test_optimade_security.py +0 -0
  147. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/tests/test_policy.py +0 -0
  148. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/tests/test_proposals.py +0 -0
  149. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/tests/test_python_adapter.py +0 -0
  150. {schemarouter-0.2.0a1 → schemarouter-0.3.0a1}/tests/test_runtime_interface.py +0 -0
@@ -0,0 +1,99 @@
1
+ name: Auto Tag Release
2
+
3
+ on:
4
+ workflow_run:
5
+ workflows: ["CI"]
6
+ types: [completed]
7
+
8
+ permissions:
9
+ contents: write
10
+
11
+ concurrency:
12
+ group: auto-tag-release-${{ github.event.workflow_run.head_sha }}
13
+ cancel-in-progress: false
14
+
15
+ jobs:
16
+ tag:
17
+ if: >-
18
+ github.event.workflow_run.conclusion == 'success' &&
19
+ github.event.workflow_run.head_branch == 'main'
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - uses: actions/checkout@v7
23
+ with:
24
+ ref: ${{ github.event.workflow_run.head_sha }}
25
+ fetch-depth: 0
26
+
27
+ - name: Verify release commit is still current main
28
+ env:
29
+ RELEASE_SHA: ${{ github.event.workflow_run.head_sha }}
30
+ run: |
31
+ git fetch origin main --depth=1
32
+ current_main="$(git rev-parse origin/main)"
33
+ if [[ "$current_main" != "$RELEASE_SHA" ]]; then
34
+ echo "CI succeeded for $RELEASE_SHA, but current main is $current_main; skipping stale run."
35
+ echo "SKIP_RELEASE_TAG=true" >> "$GITHUB_ENV"
36
+ fi
37
+
38
+ - name: Resolve release metadata
39
+ if: env.SKIP_RELEASE_TAG != 'true'
40
+ id: metadata
41
+ run: |
42
+ python - <<'PY'
43
+ import os
44
+ import re
45
+ import tomllib
46
+ from pathlib import Path
47
+
48
+ version = tomllib.loads(Path("pyproject.toml").read_text())["project"]["version"]
49
+ if ".dev" in version:
50
+ with Path(os.environ["GITHUB_OUTPUT"]).open("a", encoding="utf-8") as output:
51
+ print("releasable=false", file=output)
52
+ raise SystemExit(0)
53
+
54
+ release_notes = Path("docs") / "releases" / f"{version}.md"
55
+ if not release_notes.is_file():
56
+ raise SystemExit(f"missing release notes for releasable version: {release_notes}")
57
+
58
+ changelog = Path("CHANGELOG.md").read_text(encoding="utf-8")
59
+ if f"## {version} -" not in changelog:
60
+ raise SystemExit(f"CHANGELOG.md is missing a dated {version} release heading")
61
+
62
+ if not re.fullmatch(r"\d+\.\d+\.\d+(?:(?:a|b|rc)\d+)?", version):
63
+ raise SystemExit(f"unsupported automatic release version format: {version!r}")
64
+
65
+ with Path(os.environ["GITHUB_OUTPUT"]).open("a", encoding="utf-8") as output:
66
+ print("releasable=true", file=output)
67
+ print(f"version={version}", file=output)
68
+ print(f"tag=v{version}", file=output)
69
+ PY
70
+
71
+ - name: Check whether release tag already exists
72
+ if: >-
73
+ env.SKIP_RELEASE_TAG != 'true' &&
74
+ steps.metadata.outputs.releasable == 'true'
75
+ id: existing
76
+ env:
77
+ RELEASE_TAG: ${{ steps.metadata.outputs.tag }}
78
+ run: |
79
+ if git ls-remote --exit-code --tags origin "refs/tags/$RELEASE_TAG" >/dev/null 2>&1; then
80
+ echo "exists=true" >> "$GITHUB_OUTPUT"
81
+ echo "$RELEASE_TAG already exists; nothing to do."
82
+ else
83
+ echo "exists=false" >> "$GITHUB_OUTPUT"
84
+ fi
85
+
86
+ - name: Create annotated release tag
87
+ if: >-
88
+ env.SKIP_RELEASE_TAG != 'true' &&
89
+ steps.metadata.outputs.releasable == 'true' &&
90
+ steps.existing.outputs.exists == 'false'
91
+ env:
92
+ RELEASE_SHA: ${{ github.event.workflow_run.head_sha }}
93
+ RELEASE_TAG: ${{ steps.metadata.outputs.tag }}
94
+ RELEASE_VERSION: ${{ steps.metadata.outputs.version }}
95
+ run: |
96
+ git config user.name "github-actions[bot]"
97
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
98
+ git tag -a "$RELEASE_TAG" "$RELEASE_SHA" -m "SchemaRouter $RELEASE_VERSION"
99
+ git push origin "refs/tags/$RELEASE_TAG"
@@ -0,0 +1,205 @@
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
+ python-preview:
35
+ continue-on-error: true
36
+ runs-on: ubuntu-latest
37
+ steps:
38
+ - uses: actions/checkout@v7
39
+ - uses: actions/setup-python@v7
40
+ with:
41
+ python-version: "3.15.0-rc.2"
42
+ allow-prereleases: true
43
+ - run: python -m pip install --upgrade pip
44
+ - run: pip install -e ".[dev]"
45
+ - run: ruff check .
46
+ - run: pytest -q -m "not mcp_integration"
47
+ - run: python examples/quickstart.py
48
+
49
+ windows-smoke:
50
+ runs-on: windows-latest
51
+ steps:
52
+ - uses: actions/checkout@v7
53
+ - uses: actions/setup-python@v7
54
+ with:
55
+ python-version: "3.14"
56
+ - run: python -m pip install --upgrade pip
57
+ - run: pip install -e ".[dev]"
58
+ - run: ruff check .
59
+ - run: pytest -q -m "not mcp_integration"
60
+ - run: python examples/quickstart.py
61
+
62
+ typecheck:
63
+ runs-on: ubuntu-latest
64
+ steps:
65
+ - uses: actions/checkout@v7
66
+ - uses: actions/setup-python@v7
67
+ with:
68
+ python-version: "3.12"
69
+ - run: python -m pip install --upgrade pip
70
+ - run: pip install -e ".[dev,mcp,langchain,llamaindex,jev,otel]"
71
+ - run: pyright
72
+
73
+ coverage:
74
+ runs-on: ubuntu-latest
75
+ steps:
76
+ - uses: actions/checkout@v7
77
+ - uses: actions/setup-python@v7
78
+ with:
79
+ python-version: "3.12"
80
+ - run: python -m pip install --upgrade pip
81
+ - run: pip install -e ".[dev,mcp,langchain,llamaindex,jev,otel]"
82
+ - run: pytest -q --cov=schemarouter --cov-branch --cov-report=term-missing --cov-report=xml:coverage.xml
83
+ - uses: actions/upload-artifact@v4
84
+ with:
85
+ name: coverage-report
86
+ path: coverage.xml
87
+ if-no-files-found: error
88
+ retention-days: 14
89
+
90
+ minimum-dependencies:
91
+ runs-on: ubuntu-latest
92
+ steps:
93
+ - uses: actions/checkout@v7
94
+ - uses: actions/setup-python@v7
95
+ with:
96
+ python-version: "3.10"
97
+ - run: python -m pip install --upgrade pip
98
+ - run: pip install "pytest>=8.2" "pytest-asyncio>=0.23"
99
+ - run: pip install "httpx==0.27.0" "jsonschema==4.23.0" "pydantic==2.8.0" "pyyaml==6.0.0"
100
+ - run: pip install -e . --no-deps
101
+ - run: pytest -q -m "not mcp_integration"
102
+ - run: python examples/quickstart.py
103
+
104
+ package:
105
+ runs-on: ubuntu-latest
106
+ steps:
107
+ - uses: actions/checkout@v7
108
+ - uses: actions/setup-python@v7
109
+ with:
110
+ python-version: "3.12"
111
+ - run: python -m pip install --upgrade pip
112
+ - run: pip install build twine
113
+ - run: python -m build
114
+ - run: twine check dist/*
115
+ - name: Verify license is packaged
116
+ run: |
117
+ python - <<'PY'
118
+ import glob
119
+ import zipfile
120
+
121
+ wheel = glob.glob("dist/*.whl")[0]
122
+ names = zipfile.ZipFile(wheel).namelist()
123
+ assert any(name.endswith("/licenses/LICENSE") for name in names), names
124
+ PY
125
+ - run: python -m venv /tmp/schemarouter-wheel
126
+ - run: /tmp/schemarouter-wheel/bin/python -m pip install --upgrade pip
127
+ - run: /tmp/schemarouter-wheel/bin/pip install dist/*.whl
128
+ - run: /tmp/schemarouter-wheel/bin/python -c "from importlib.metadata import version; import schemarouter; assert schemarouter.__version__ == version('schemarouter')"
129
+ - run: /tmp/schemarouter-wheel/bin/python examples/quickstart.py
130
+ - run: /tmp/schemarouter-wheel/bin/python scripts/benchmark_decision_routing.py
131
+ - run: python -m venv /tmp/schemarouter-sdist
132
+ - run: /tmp/schemarouter-sdist/bin/python -m pip install --upgrade pip
133
+ - run: /tmp/schemarouter-sdist/bin/pip install dist/*.tar.gz
134
+ - run: /tmp/schemarouter-sdist/bin/python -c "from importlib.metadata import version; import schemarouter; assert schemarouter.__version__ == version('schemarouter')"
135
+ - run: /tmp/schemarouter-sdist/bin/python examples/quickstart.py
136
+
137
+ langchain-integration:
138
+ runs-on: ubuntu-latest
139
+ steps:
140
+ - uses: actions/checkout@v7
141
+ - uses: actions/setup-python@v7
142
+ with:
143
+ python-version: "3.12"
144
+ - run: python -m pip install --upgrade pip
145
+ - run: pip install -e ".[dev,langchain]"
146
+ - run: pytest -q tests/test_langchain_integration.py
147
+ - run: python examples/langchain_quickstart.py
148
+
149
+ llamaindex-integration:
150
+ runs-on: ubuntu-latest
151
+ steps:
152
+ - uses: actions/checkout@v7
153
+ - uses: actions/setup-python@v7
154
+ with:
155
+ python-version: "3.12"
156
+ - run: python -m pip install --upgrade pip
157
+ - run: pip install -e ".[dev,llamaindex]"
158
+ - run: pytest -q tests/test_llamaindex_integration.py
159
+ - run: python examples/llamaindex_quickstart.py
160
+
161
+ jev-integration:
162
+ runs-on: ubuntu-latest
163
+ steps:
164
+ - uses: actions/checkout@v7
165
+ - uses: actions/setup-python@v7
166
+ with:
167
+ python-version: "3.12"
168
+ - run: python -m pip install --upgrade pip
169
+ - run: pip install -e ".[dev,jev]"
170
+ - run: pytest -q tests/test_jev_integration.py
171
+
172
+ otel-integration:
173
+ runs-on: ubuntu-latest
174
+ steps:
175
+ - uses: actions/checkout@v7
176
+ - uses: actions/setup-python@v7
177
+ with:
178
+ python-version: "3.12"
179
+ - run: python -m pip install --upgrade pip
180
+ - run: pip install -e ".[dev,otel]"
181
+ - run: pytest -q tests/test_opentelemetry_integration.py
182
+
183
+ mcp-integration:
184
+ runs-on: ubuntu-latest
185
+ steps:
186
+ - uses: actions/checkout@v7
187
+ - uses: actions/setup-python@v7
188
+ with:
189
+ python-version: "3.12"
190
+ - run: python -m pip install --upgrade pip
191
+ - run: pip install -e ".[dev,mcp]"
192
+ - run: pytest -q tests/test_mcp_integration.py
193
+
194
+ docs:
195
+ runs-on: ubuntu-latest
196
+ steps:
197
+ - uses: actions/checkout@v7
198
+ - uses: actions/setup-python@v7
199
+ with:
200
+ python-version: "3.12"
201
+ - run: python -m pip install --upgrade pip
202
+ - run: pip install -e ".[docs]"
203
+ - run: mkdocs build --strict
204
+ env:
205
+ NO_MKDOCS_2_WARNING: "1"
@@ -0,0 +1,161 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ quality:
13
+ uses: ./.github/workflows/ci.yml
14
+
15
+ build:
16
+ needs: quality
17
+ runs-on: ubuntu-latest
18
+ outputs:
19
+ version: ${{ steps.metadata.outputs.version }}
20
+ release_notes: ${{ steps.metadata.outputs.release_notes }}
21
+ prerelease: ${{ steps.metadata.outputs.prerelease }}
22
+ steps:
23
+ - uses: actions/checkout@v7
24
+ - uses: actions/setup-python@v7
25
+ with:
26
+ python-version: "3.12"
27
+ - name: Resolve and verify release metadata
28
+ id: metadata
29
+ run: |
30
+ python - <<'PY'
31
+ import os
32
+ import re
33
+ import tomllib
34
+ from pathlib import Path
35
+
36
+ version = tomllib.loads(Path("pyproject.toml").read_text())["project"]["version"]
37
+ tag = os.environ["GITHUB_REF_NAME"]
38
+ expected = f"v{version}"
39
+ if tag != expected:
40
+ raise SystemExit(f"tag {tag!r} does not match package version {expected!r}")
41
+ if ".dev" in version:
42
+ raise SystemExit("development versions cannot be published by the release workflow")
43
+
44
+ release_notes = Path("docs") / "releases" / f"{version}.md"
45
+ if not release_notes.is_file():
46
+ raise SystemExit(f"missing release notes: {release_notes}")
47
+
48
+ prerelease = bool(re.search(r"(?:a|b|rc)\d+(?:$|[.+-])", version))
49
+ with Path(os.environ["GITHUB_OUTPUT"]).open("a", encoding="utf-8") as output:
50
+ print(f"version={version}", file=output)
51
+ print(f"release_notes={release_notes.as_posix()}", file=output)
52
+ print(f"prerelease={'true' if prerelease else 'false'}", file=output)
53
+ PY
54
+ - run: python -m pip install --upgrade pip
55
+ - run: pip install build twine
56
+ - run: python -m build
57
+ - run: twine check dist/*
58
+ - name: Verify release artifact
59
+ env:
60
+ RELEASE_VERSION: ${{ steps.metadata.outputs.version }}
61
+ run: |
62
+ python - <<'PY'
63
+ import glob
64
+ import os
65
+ import subprocess
66
+ import sys
67
+ import tempfile
68
+ import venv
69
+ import zipfile
70
+ from pathlib import Path
71
+
72
+ wheel = glob.glob("dist/*.whl")[0]
73
+ sdist = glob.glob("dist/*.tar.gz")[0]
74
+
75
+ wheel_names = zipfile.ZipFile(wheel).namelist()
76
+ assert any(name.endswith("/licenses/LICENSE") for name in wheel_names), wheel_names
77
+
78
+ import tarfile
79
+
80
+ with tarfile.open(sdist, "r:gz") as archive:
81
+ sdist_names = archive.getnames()
82
+ assert any(name.endswith("/LICENSE") for name in sdist_names), sdist_names
83
+
84
+ for artifact in (wheel, sdist):
85
+ with tempfile.TemporaryDirectory() as root:
86
+ env = Path(root) / "venv"
87
+ venv.EnvBuilder(with_pip=True).create(env)
88
+ python = env / ("Scripts/python.exe" if sys.platform == "win32" else "bin/python")
89
+ subprocess.check_call([str(python), "-m", "pip", "install", artifact])
90
+ subprocess.check_call([
91
+ str(python),
92
+ "-c",
93
+ (
94
+ "from importlib.metadata import version; "
95
+ "import schemarouter; "
96
+ f"assert schemarouter.__version__ == {os.environ['RELEASE_VERSION']!r}; "
97
+ f"assert version('schemarouter') == {os.environ['RELEASE_VERSION']!r}"
98
+ ),
99
+ ])
100
+ subprocess.check_call([str(python), "examples/quickstart.py"])
101
+ PY
102
+ - uses: actions/upload-artifact@v4
103
+ with:
104
+ name: python-dist
105
+ path: dist/
106
+ if-no-files-found: error
107
+ retention-days: 7
108
+
109
+ github-release:
110
+ needs: build
111
+ permissions:
112
+ contents: write
113
+ runs-on: ubuntu-latest
114
+ steps:
115
+ - uses: actions/checkout@v7
116
+ - uses: actions/download-artifact@v4
117
+ with:
118
+ name: python-dist
119
+ path: dist
120
+ - name: Create or update GitHub release
121
+ env:
122
+ GH_TOKEN: ${{ github.token }}
123
+ RELEASE_VERSION: ${{ needs.build.outputs.version }}
124
+ RELEASE_NOTES: ${{ needs.build.outputs.release_notes }}
125
+ IS_PRERELEASE: ${{ needs.build.outputs.prerelease }}
126
+ run: |
127
+ prerelease_args=()
128
+ if [[ "$IS_PRERELEASE" == "true" ]]; then
129
+ prerelease_args+=(--prerelease)
130
+ fi
131
+
132
+ if gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1; then
133
+ gh release upload "$GITHUB_REF_NAME" dist/* --clobber
134
+ gh release edit "$GITHUB_REF_NAME" \
135
+ --title "SchemaRouter $RELEASE_VERSION" \
136
+ --notes-file "$RELEASE_NOTES" \
137
+ "${prerelease_args[@]}"
138
+ else
139
+ gh release create "$GITHUB_REF_NAME" dist/* \
140
+ --title "SchemaRouter $RELEASE_VERSION" \
141
+ --notes-file "$RELEASE_NOTES" \
142
+ "${prerelease_args[@]}" \
143
+ --verify-tag
144
+ fi
145
+
146
+ pypi:
147
+ needs: build
148
+ permissions:
149
+ id-token: write
150
+ runs-on: ubuntu-latest
151
+ environment:
152
+ name: pypi
153
+ url: https://pypi.org/project/schemarouter/
154
+ steps:
155
+ - uses: actions/download-artifact@v4
156
+ with:
157
+ name: python-dist
158
+ path: dist
159
+ - uses: pypa/gh-action-pypi-publish@release/v1
160
+ with:
161
+ packages-dir: dist/
@@ -0,0 +1,103 @@
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
+ No unreleased changes yet.
11
+
12
+ ## 0.3.0a1 - 2026-09-22
13
+
14
+ ### Added
15
+
16
+ - provider-neutral bounded `DecisionBackend` contracts with deterministic fallback;
17
+ - optional `JevDecisionBackend` backed by TypeSafe System One through `schemarouter[jev]`;
18
+ - optional LlamaIndex `FunctionTool` integration;
19
+ - runnable LangChain and LlamaIndex integration examples enforced in CI;
20
+ - provider-neutral decision-routing benchmark harness covering accuracy, abstention, latency,
21
+ token usage, and optional cost estimation;
22
+ - explicit ecosystem compatibility matrix and integration maintenance policy;
23
+ - Korean project overview in `README.ko.md`;
24
+ - Python 3.13 and 3.14 core CI coverage;
25
+ - a non-blocking Python 3.15 release-candidate preview;
26
+ - a Windows + Python 3.14 smoke surface;
27
+ - a Pyright static-type gate for the typed package surface;
28
+ - full-suite branch-coverage reporting with an 82% blocking floor;
29
+ - declared lower-bound runtime dependency testing;
30
+ - clean wheel and sdist installation smoke tests;
31
+ - a checked-in 144-case multilingual/adversarial decision-routing corpus with JSON/CSV output,
32
+ category metrics, invalid-plan rate, abstention/fallback tracking, and p50/p95 latency;
33
+ - machine-readable OpenAPI compatibility reports for partial/unsupported constructs;
34
+ - authenticated MCP trusted-header and custom client-factory transport boundaries;
35
+ - trusted sync/async per-call approval callbacks;
36
+ - per-run execution budgets for logical calls, attempts, remote attempts, elapsed time, per-tool
37
+ quotas, and application-defined cost units;
38
+ - optional privacy-preserving OpenTelemetry run/tool span export;
39
+ - explicit allowlisted third-party adapter plugins through `schemarouter.adapters` entry points.
40
+
41
+ ### Changed
42
+
43
+ - current `main` now identifies as `0.3.0.dev0`, separating unreleased development builds from
44
+ the published `0.2.0a1` alpha;
45
+ - release automation now derives version, release title, release-note path, prerelease state, and
46
+ artifact verification from `pyproject.toml` instead of hard-coded release literals;
47
+ - tag releases reuse the same full CI quality workflow as pull requests and verify both wheel and
48
+ sdist artifacts in clean environments before publication;
49
+ - superseded pull-request CI runs are cancelled automatically to avoid stale validation consuming
50
+ runner capacity;
51
+ - releasable `main` commits are automatically annotated with `v<version>` only after green CI,
52
+ current-head verification, release-note/changelog validation, and duplicate-tag checks.
53
+
54
+ ### Security
55
+
56
+ - OpenAPI runtime responses are now streamed through a bounded reader with a 16 MiB default limit,
57
+ enforcing the bound both from declared `Content-Length` and actual bytes received;
58
+ - Jev option IDs are validated before confidence-based abstention, so unknown IDs always fail
59
+ closed;
60
+ - Jev credentials remain SDK client configuration and are never placed in model state;
61
+ - `DecisionOption.metadata` is not forwarded to Jev;
62
+ - provider failures can deterministically fall back without weakening execution policy;
63
+ - MCP credentials are rejected in URLs and remain outside tool/planner metadata;
64
+ - MCP protocol-controlled headers cannot be overridden through trusted runtime headers;
65
+ - adapter entry points are metadata-only during discovery and are never auto-imported;
66
+ - OpenTelemetry export omits payload values, RunConfig metadata, tags, and exception messages;
67
+ - approval callback failures and execution budget exhaustion fail closed.
68
+
69
+ ## 0.2.0a1 - 2026-09-20
70
+
71
+ ### Added
72
+
73
+ - pluggable structured-source `AdapterRegistry` with explicit and priority-based auto discovery;
74
+ - OPTIMADE v1 discovery through base and entry-type info endpoints;
75
+ - OPTIMADE field-aware execution that maps planned fields to `response_fields`;
76
+ - call-aware invoker support for protocol adapters that need the full `ToolCall`;
77
+ - typed tool, endpoint, parameter, response-field, plan, and result contracts;
78
+ - namespaced versioned registry;
79
+ - schema-aware planning with recall-preserving field projection;
80
+ - provider-neutral model-assisted query analysis;
81
+ - OpenAPI 3.x URL ingestion and guarded HTTP execution;
82
+ - MCP tool discovery and execution;
83
+ - evidence-grounded proposals for human-readable API documentation;
84
+ - runtime JSON Schema validation for arguments and raw outputs;
85
+ - schema and invoker-binding drift detection;
86
+ - fail-closed execution policy for mutations, destructive calls, and unclassified remote tools;
87
+ - Runnable-style `invoke`, `batch`, `stream`, and typed event APIs;
88
+ - run configuration, concurrency control, and safe retry policy;
89
+ - typed Python callable registration with `@schema_tool`;
90
+ - optional LangChain `StructuredTool` integration;
91
+ - framework maturity, architecture, release, and versioning documentation;
92
+ - MIT licensing and package metadata;
93
+ - MkDocs Material documentation site with guides, recipes, and generated API reference;
94
+ - SchemaRouter brace-and-routing-hub brand system with light/dark marks, lockups, favicon, and
95
+ social preview source artwork.
96
+
97
+ ### Security
98
+
99
+ - separated schema-fetch credentials from runtime API credentials;
100
+ - restricted schema redirects and runtime API origins;
101
+ - blocked model/tool control of sensitive runtime headers;
102
+ - redacted event payloads by default;
103
+ - prevented automatic retries for non-read-only calls.
@@ -10,17 +10,19 @@ python -m venv .venv
10
10
  source .venv/bin/activate
11
11
  pip install -e ".[dev]"
12
12
  ruff check .
13
- pytest -q
13
+ pytest -q -m "not mcp_integration"
14
14
  python examples/quickstart.py
15
- ```
16
-
17
- Optional integrations have separate extras and tests. For example:
18
15
 
19
- ```bash
20
- pip install -e ".[dev,langchain]"
21
- pytest -q tests/test_langchain_integration.py
16
+ # Check the complete typed surface and optional integrations.
17
+ pip install -e ".[dev,mcp,langchain,llamaindex,jev,otel]"
18
+ pyright
19
+ pytest -q --cov=schemarouter --cov-branch --cov-report=term-missing
22
20
  ```
23
21
 
22
+ Optional integrations have separate extras and focused tests. The required CI exercises
23
+ LangChain, LlamaIndex, Jev/TypeSafe, MCP, and OpenTelemetry independently in addition to the full
24
+ coverage run.
25
+
24
26
  ## Design rules
25
27
 
26
28
  Changes must preserve these principles:
@@ -32,7 +34,9 @@ Changes must preserve these principles:
32
34
  5. raw input/output values are validated before crossing the trusted execution boundary;
33
35
  6. mutations, destructive calls, and ambiguous remote side effects require local policy;
34
36
  7. observability must not expose payloads by default;
35
- 8. integrations must call through SchemaRouter execution rather than bypassing it.
37
+ 8. integrations must call through SchemaRouter execution rather than bypassing it;
38
+ 9. installed adapter plugins must never be auto-imported from discovery alone;
39
+ 10. approval and execution-budget failures must remain fail-closed.
36
40
 
37
41
  ## Adding an adapter
38
42
 
@@ -65,7 +69,10 @@ A change is not complete until:
65
69
 
66
70
  - tests cover its public behavior and adversarial failure cases;
67
71
  - warnings remain clean;
68
- - supported Python versions pass;
72
+ - supported Python versions pass, including the Windows smoke surface;
73
+ - Pyright and the 82% branch-coverage floor pass;
74
+ - minimum declared runtime dependencies remain usable;
75
+ - wheel and sdist clean-install smokes pass;
69
76
  - public behavior is documented;
70
77
  - new optional dependencies are isolated behind extras;
71
78
  - breaking API changes include a changelog and migration note.