schemarouter 0.2.0a1__py3-none-any.whl

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.
@@ -0,0 +1,96 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from jsonschema import exceptions, validators
6
+
7
+ from .errors import SchemaValidationError
8
+ from .models import EndpointSpec
9
+
10
+
11
+ def _synthesized_input_schema(endpoint: EndpointSpec) -> dict[str, Any]:
12
+ properties = {
13
+ parameter.name: parameter.json_schema or {}
14
+ for parameter in endpoint.parameters
15
+ }
16
+ required = [
17
+ parameter.name
18
+ for parameter in endpoint.parameters
19
+ if parameter.required
20
+ ]
21
+ schema: dict[str, Any] = {
22
+ "type": "object",
23
+ "properties": properties,
24
+ "additionalProperties": False,
25
+ }
26
+ if required:
27
+ schema["required"] = required
28
+ return schema
29
+
30
+
31
+ def _synthesized_output_schema(endpoint: EndpointSpec) -> dict[str, Any]:
32
+ properties = {
33
+ field.name: field.json_schema or {}
34
+ for field in endpoint.output_fields
35
+ }
36
+ required = [
37
+ name
38
+ for name in endpoint.metadata.get("output_required", [])
39
+ if name in properties
40
+ ]
41
+ schema: dict[str, Any] = {
42
+ "type": "object",
43
+ "properties": properties,
44
+ }
45
+ if required:
46
+ schema["required"] = required
47
+ return schema
48
+
49
+
50
+ def effective_input_schema(endpoint: EndpointSpec) -> dict[str, Any]:
51
+ return endpoint.input_schema or _synthesized_input_schema(endpoint)
52
+
53
+
54
+ def effective_output_schema(endpoint: EndpointSpec) -> dict[str, Any]:
55
+ if endpoint.output_schema:
56
+ return endpoint.output_schema
57
+ if endpoint.output_fields:
58
+ return _synthesized_output_schema(endpoint)
59
+ return {}
60
+
61
+
62
+ def validate_json_schema_value(
63
+ value: Any,
64
+ schema: dict[str, Any],
65
+ *,
66
+ context: str,
67
+ ) -> None:
68
+ if not schema:
69
+ return
70
+
71
+ try:
72
+ validator_cls = validators.validator_for(schema)
73
+ validator_cls.check_schema(schema)
74
+ validator = validator_cls(schema)
75
+ errors = sorted(
76
+ validator.iter_errors(value),
77
+ key=lambda error: [str(part) for part in error.absolute_path],
78
+ )
79
+ except exceptions.SchemaError as exc:
80
+ raise SchemaValidationError(
81
+ f"{context}: declared JSON Schema is invalid: {exc.message}"
82
+ ) from exc
83
+ except Exception as exc: # unresolved refs and validator/runtime failures fail closed
84
+ raise SchemaValidationError(
85
+ f"{context}: JSON Schema could not be evaluated safely"
86
+ ) from exc
87
+
88
+ if not errors:
89
+ return
90
+
91
+ first = errors[0]
92
+ path = ".".join(str(part) for part in first.absolute_path)
93
+ location = f" at {path}" if path else ""
94
+ raise SchemaValidationError(
95
+ f"{context}{location}: {first.message}"
96
+ )
@@ -0,0 +1,297 @@
1
+ Metadata-Version: 2.5
2
+ Name: schemarouter
3
+ Version: 0.2.0a1
4
+ Summary: Schema-aware planning and execution layer for LLM tool ecosystems
5
+ Project-URL: Homepage, https://github.com/JDeun/SchemaRouter
6
+ Project-URL: Repository, https://github.com/JDeun/SchemaRouter
7
+ Project-URL: Documentation, https://jdeun.github.io/SchemaRouter/
8
+ Project-URL: Issues, https://github.com/JDeun/SchemaRouter/issues
9
+ Project-URL: Research, https://github.com/JDeun/paper_SchemaRouter
10
+ Project-URL: Brand, https://jdeun.github.io/SchemaRouter/project/brand/
11
+ Author: Yong-eun Cho
12
+ License-Expression: MIT
13
+ License-File: LICENSE
14
+ Keywords: agents,llm,mcp,openapi,optimade,schema,tool-routing
15
+ Classifier: Development Status :: 3 - Alpha
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.10
25
+ Requires-Dist: httpx<1,>=0.27
26
+ Requires-Dist: jsonschema<5,>=4.23
27
+ Requires-Dist: pydantic<3,>=2.8
28
+ Requires-Dist: pyyaml<7,>=6.0
29
+ Provides-Extra: dev
30
+ Requires-Dist: build>=1.2; extra == 'dev'
31
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
32
+ Requires-Dist: pytest>=8.2; extra == 'dev'
33
+ Requires-Dist: ruff>=0.6; extra == 'dev'
34
+ Requires-Dist: twine>=5; extra == 'dev'
35
+ Provides-Extra: docs
36
+ Requires-Dist: mkdocs-material<10,>=9.7; extra == 'docs'
37
+ Requires-Dist: mkdocstrings-python<3,>=2; extra == 'docs'
38
+ Requires-Dist: mkdocstrings<2,>=1; extra == 'docs'
39
+ Provides-Extra: langchain
40
+ Requires-Dist: langchain-core<2,>=1.6; extra == 'langchain'
41
+ Provides-Extra: mcp
42
+ Requires-Dist: mcp<3,>=2; extra == 'mcp'
43
+ Description-Content-Type: text/markdown
44
+
45
+ <p align="center">
46
+ <picture>
47
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/JDeun/SchemaRouter/main/docs/assets/brand/schemarouter-lockup-dark.svg">
48
+ <img alt="SchemaRouter" src="https://raw.githubusercontent.com/JDeun/SchemaRouter/main/docs/assets/brand/schemarouter-lockup-light.svg" width="760">
49
+ </picture>
50
+ </p>
51
+
52
+ # SchemaRouter
53
+
54
+ **Schema-aware planning and execution for LLM tool ecosystems.**
55
+
56
+ [![CI](https://github.com/JDeun/SchemaRouter/actions/workflows/ci.yml/badge.svg)](https://github.com/JDeun/SchemaRouter/actions/workflows/ci.yml)
57
+ [![Docs](https://github.com/JDeun/SchemaRouter/actions/workflows/docs.yml/badge.svg)](https://github.com/JDeun/SchemaRouter/actions/workflows/docs.yml)
58
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/JDeun/SchemaRouter/blob/main/LICENSE)
59
+
60
+ SchemaRouter compiles a natural-language request plus a registered capability catalog into a small,
61
+ typed, auditable execution plan.
62
+
63
+ It goes beyond `Query -> Tool` routing:
64
+
65
+ ```text
66
+ Query
67
+ -> Tool
68
+ -> Endpoint
69
+ -> Parameters
70
+ -> Response fields
71
+ -> Evidence / policy
72
+ -> Schema validation
73
+ -> Execute
74
+ ```
75
+
76
+ SchemaRouter is intentionally narrower than LangChain or LangGraph. It is designed to sit at the
77
+ **tool-schema boundary** between an agent and structured capability sources such as OpenAPI, MCP,
78
+ OPTIMADE, Python callables, and third-party adapter protocols.
79
+
80
+ > Status: **0.2.0a1 pre-release candidate**. The adapter ecosystem, OPTIMADE support, package
81
+ > artifact, and public OpenAPI/OPTIMADE compatibility smokes are release-gated in CI.
82
+
83
+ ## Why
84
+
85
+ As an agent gains more tools, choosing the tool is only one part of the problem. The runtime also
86
+ needs to know:
87
+
88
+ - which operation inside that tool is relevant;
89
+ - which parameters are declared and valid;
90
+ - which response fields should be retained;
91
+ - whether the operation is read-only, mutating, destructive, or unclassified;
92
+ - whether the schema changed after planning;
93
+ - whether the raw tool result actually satisfies the declared contract.
94
+
95
+ SchemaRouter makes those decisions explicit.
96
+
97
+ ## Quickstart
98
+
99
+ ```python
100
+ from pydantic import BaseModel
101
+
102
+ from schemarouter import PlanRequest, SchemaRouter, schema_tool
103
+
104
+
105
+ class Weather(BaseModel):
106
+ city: str
107
+ temperature: float
108
+
109
+
110
+ @schema_tool(read_only=True)
111
+ def current_weather(city: str) -> Weather:
112
+ return Weather(city=city, temperature=20.5)
113
+
114
+
115
+ router = SchemaRouter()
116
+ router.add_callable(current_weather)
117
+
118
+ results = router.invoke(
119
+ PlanRequest(
120
+ query="city temperature",
121
+ arguments={"city": "Seoul"},
122
+ )
123
+ )
124
+
125
+ print(results[0].data)
126
+ ```
127
+
128
+ The same execution vocabulary works across capability sources:
129
+
130
+ ```python
131
+ router.invoke(request)
132
+ await router.ainvoke(request)
133
+
134
+ router.batch(requests)
135
+ await router.abatch(requests)
136
+
137
+ router.stream(request)
138
+ router.astream(request)
139
+ router.astream_events(request)
140
+ ```
141
+
142
+ ## Bring your schema
143
+
144
+ ### OpenAPI
145
+
146
+ ```python
147
+ router = await SchemaRouter.from_url(
148
+ "https://api.example.com/openapi.json",
149
+ kind="openapi",
150
+ )
151
+ ```
152
+
153
+ ### OPTIMADE
154
+
155
+ ```python
156
+ router = await SchemaRouter.from_url(
157
+ "https://www.crystallography.net/cod/optimade",
158
+ kind="optimade",
159
+ )
160
+ ```
161
+
162
+ OPTIMADE entry schemas are discovered from `/info/<entry_type>`. Planned fields are translated
163
+ into the protocol's `response_fields` query parameter before execution.
164
+
165
+ ### MCP
166
+
167
+ ```bash
168
+ pip install -e ".[mcp]"
169
+ ```
170
+
171
+ ```python
172
+ router = await SchemaRouter.from_url(
173
+ "http://localhost:8000/mcp",
174
+ kind="mcp",
175
+ )
176
+ ```
177
+
178
+ ### Python
179
+
180
+ ```python
181
+ router.add_callable(my_typed_function)
182
+ ```
183
+
184
+ ### Human-readable API docs
185
+
186
+ ```python
187
+ proposal = await router.inspect_url(
188
+ "https://docs.example.com/api",
189
+ model=documentation_model,
190
+ )
191
+
192
+ router.approve_proposal(
193
+ proposal,
194
+ base_url="https://api.example.com",
195
+ )
196
+ ```
197
+
198
+ Human-readable documentation never becomes executable automatically. It first becomes an
199
+ evidence-grounded proposal and then requires explicit approval.
200
+
201
+ ## Core guarantees
202
+
203
+ - **Schema-constrained planning** — unknown tools, endpoints, parameters, and fields cannot become
204
+ executable calls.
205
+ - **Runtime JSON Schema validation** — validate arguments before invocation and raw output before
206
+ projection.
207
+ - **Schema and binding drift detection** — stale plans and stale transports fail closed.
208
+ - **Local execution authority** — remote metadata and model output cannot grant mutation or
209
+ destructive permissions.
210
+ - **Credential separation** — schema-fetch credentials and runtime credentials stay in different
211
+ channels.
212
+ - **Read-only retries by default** — contract violations are never retried.
213
+ - **Redacted runtime events by default** — payload tracing is opt-in.
214
+ - **Pluggable registry** — custom registries can implement the public `ToolRegistry` protocol.
215
+ - **Pluggable source adapters** — `AdapterRegistry` lets structured protocols compile into the same
216
+ `ToolSpec` / `EndpointSpec` execution model.
217
+
218
+ ## With LangChain
219
+
220
+ Install the optional integration:
221
+
222
+ ```bash
223
+ pip install -e ".[langchain]"
224
+ ```
225
+
226
+ Then expose registered endpoints as LangChain `StructuredTool` objects:
227
+
228
+ ```python
229
+ from schemarouter.integrations import to_langchain_tools
230
+
231
+ tools = to_langchain_tools(router)
232
+ ```
233
+
234
+ Execution still flows through SchemaRouter's policy, fingerprint, input, and output validation.
235
+
236
+ ## Documentation
237
+
238
+ Full documentation is organized as a framework manual rather than embedded in this README:
239
+
240
+ - [Getting started](https://jdeun.github.io/SchemaRouter/getting-started/installation/)
241
+ - [Core concepts](https://jdeun.github.io/SchemaRouter/concepts/schema-router/)
242
+ - [OpenAPI guide](https://jdeun.github.io/SchemaRouter/guides/openapi/)
243
+ - [OPTIMADE guide](https://jdeun.github.io/SchemaRouter/guides/optimade/)
244
+ - [MCP guide](https://jdeun.github.io/SchemaRouter/guides/mcp/)
245
+ - [LangChain integration](https://jdeun.github.io/SchemaRouter/integrations/langchain/)
246
+ - [API reference](https://jdeun.github.io/SchemaRouter/reference/api/)
247
+ - [Architecture](https://jdeun.github.io/SchemaRouter/architecture/)
248
+ - [Security](https://github.com/JDeun/SchemaRouter/blob/main/SECURITY.md)
249
+ - [Brand assets](https://jdeun.github.io/SchemaRouter/project/brand/)
250
+
251
+ Build the docs locally with:
252
+
253
+ ```bash
254
+ pip install -e ".[docs]"
255
+ mkdocs serve
256
+ ```
257
+
258
+ ## Development
259
+
260
+ ```bash
261
+ python -m venv .venv
262
+ source .venv/bin/activate
263
+ pip install -e ".[dev]"
264
+ ruff check .
265
+ pytest -q -m "not mcp_integration"
266
+ python examples/quickstart.py
267
+ ```
268
+
269
+ Optional integration suites are isolated from the core package:
270
+
271
+ ```bash
272
+ pip install -e ".[dev,mcp]"
273
+ pytest -q tests/test_mcp_integration.py
274
+
275
+ pip install -e ".[dev,langchain]"
276
+ pytest -q tests/test_langchain_integration.py
277
+ ```
278
+
279
+ ## Project scope
280
+
281
+ SchemaRouter does **not** implement another chat abstraction, graph runtime, model-provider layer,
282
+ memory system, or checkpoint store. Those belong in surrounding agent frameworks.
283
+
284
+ Its scope is:
285
+
286
+ > **Natural-language request -> typed tool execution plan -> validated execution.**
287
+
288
+ ## Research
289
+
290
+ SchemaRouter originated from
291
+ [SchemaRouter: Field-Aware Tool Routing for Efficient Heterogeneous Agentic RAG](https://github.com/JDeun/paper_SchemaRouter).
292
+ The framework keeps the research idea while removing harness assumptions such as one endpoint per
293
+ tool and fixture-only execution.
294
+
295
+ ## License
296
+
297
+ [MIT](LICENSE) © 2026 Yong-eun Cho
@@ -0,0 +1,28 @@
1
+ schemarouter/__init__.py,sha256=35KxcP_aDxLvh3qoSCxxemhQK8pu8fE7Yh8h6Wo5cKU,2242
2
+ schemarouter/_version.py,sha256=TSc1i5RpPmxz8SJO0026XvlaNzvXzcmAVrWlPIiCqiU,204
3
+ schemarouter/errors.py,sha256=_xXTd0VnEwvCal1KQlQyjHKwtJ09PWmCzT5AEJ9VJTU,1446
4
+ schemarouter/executor.py,sha256=q65t37boNR6QH6MMBHzZ3HZfqk6KhrqfNI_neD4KZ8k,7571
5
+ schemarouter/ingestion.py,sha256=ulpu6bDadgctFMVLDkiTRVHgCg9gIqy0OEEMeq52qZs,11907
6
+ schemarouter/models.py,sha256=H2nbHTS71JYqwW8EPMjB0b3cmS5avhYrnvgBG5UeB6Q,5217
7
+ schemarouter/planner.py,sha256=chsE524EYCjM__bB_qTJfjPk4wolRZyOCmu2eyGGkMM,8754
8
+ schemarouter/policy.py,sha256=uH_cYlid7X_GAwcA07Q4vFIVH9d5ZbEUnc4HWNVoEYY,1649
9
+ schemarouter/proposals.py,sha256=TtqifnSZhTCtCZiGR7ecG9iySgBn9MrQwNbq57BtlZ4,13774
10
+ schemarouter/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ schemarouter/registry.py,sha256=7drBnDUXOhVshvVA29k1fjko_Ug3TFLbIR4Q8nEI584,2734
12
+ schemarouter/runs.py,sha256=a34lM_59nfBiZbQuAGn28rbyDrDAfXJch6hId0Gw9OU,2124
13
+ schemarouter/runtime.py,sha256=gnc50L6uL9nJg1KxmyoV9u7XlKj9GDxRBY3K58Knn4Q,22329
14
+ schemarouter/validation.py,sha256=Fp7y6uOX7d14xVIA0D29wTOWe1UfzW61kQnzE1H6mTQ,2673
15
+ schemarouter/adapters/__init__.py,sha256=BaMVOLTSpUaQL9IYynwXUmICMBMrrrzD4GSDYIKF04M,833
16
+ schemarouter/adapters/base.py,sha256=UDe2rt1TwRyNpNBVQJrJWWX5WaKRrDw2n0WJ9X9o1nI,2137
17
+ schemarouter/adapters/mcp.py,sha256=Kyon1cG85cu9JrZDH-kP1clIjXCdvNrebLZLlC_sNdE,6299
18
+ schemarouter/adapters/openapi.py,sha256=Hd2l49V-Wi093b9swrB8ruhMR4y5z-b9L0tmB3iUg8M,16161
19
+ schemarouter/adapters/optimade.py,sha256=SVuKlwFve7o4o4h-Nu276MfqiMChOc-LQqOlM83OChQ,23289
20
+ schemarouter/adapters/python.py,sha256=_T6x4TKIF4_rWTsMlgq4qCSnYLIs8a6wLytHIrebu2s,6292
21
+ schemarouter/analyzers/__init__.py,sha256=FD0SPxM73z1Lhp6Esdz6itv5DJd6sioSTG1GoMtqmqc,132
22
+ schemarouter/analyzers/model.py,sha256=g6otRPvPlFKAjviajyHI3nDk7HfQjzC-fSGuiDDMKyY,7031
23
+ schemarouter/integrations/__init__.py,sha256=kAhuW7AcwtddfxaveLfeghD9NyAa2nvn_o3FplzyLvw,116
24
+ schemarouter/integrations/langchain.py,sha256=t9HaljVsNMT4dljqat1o_l8MpBjrVoBMjtkyxhTlFsY,2871
25
+ schemarouter-0.2.0a1.dist-info/METADATA,sha256=aQCoePBaMis_lKolA0LLl-LFxaPa4ButfbbbY0qjcsA,9190
26
+ schemarouter-0.2.0a1.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
27
+ schemarouter-0.2.0a1.dist-info/licenses/LICENSE,sha256=yvJzTnx0kTgez87WW1o-9QRwYw0vY7ascd0JCfgloEk,1069
28
+ schemarouter-0.2.0a1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yong-eun Cho
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.