langchain-openapi-tools 1.1.3__tar.gz → 2.0.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.
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/ARCHITECTURE.md +57 -13
- langchain_openapi_tools-2.0.0/CHANGELOG.md +58 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/PKG-INFO +56 -3
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/README.md +55 -2
- langchain_openapi_tools-2.0.0/docs/architecture.md +72 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/docs/toolkit.md +12 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/langchain_openapi_tools/__init__.py +14 -0
- langchain_openapi_tools-2.0.0/langchain_openapi_tools/adapters.py +126 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/langchain_openapi_tools/executor.py +146 -8
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/langchain_openapi_tools/loader.py +12 -7
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/langchain_openapi_tools/models.py +26 -5
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/langchain_openapi_tools/parser.py +130 -16
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/langchain_openapi_tools/schema_converter.py +105 -3
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/langchain_openapi_tools/swagger.py +26 -1
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/pyproject.toml +1 -1
- langchain_openapi_tools-2.0.0/tests/test_adapters.py +96 -0
- langchain_openapi_tools-2.0.0/tests/test_base_url_resolution.py +324 -0
- langchain_openapi_tools-2.0.0/tests/test_compat_matrix.py +486 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/tests/test_loader.py +4 -1
- langchain_openapi_tools-2.0.0/tests/test_media_types.py +204 -0
- langchain_openapi_tools-2.0.0/tests/test_openapi_31.py +190 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/uv.lock +1 -1
- langchain_openapi_tools-1.1.3/CHANGELOG.md +0 -36
- langchain_openapi_tools-1.1.3/docs/architecture.md +0 -60
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/.github/ISSUE_TEMPLATE/documentation.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/.github/dependabot.yml +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/.github/workflows/ci.yml +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/.github/workflows/pages.yml +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/.github/workflows/publish.yml +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/.github/workflows/release.yml +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/.gitignore +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/.pre-commit-config.yaml +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/CODE_OF_CONDUCT.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/CONTRIBUTING.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/LICENSE +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/Makefile +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/benchmarks/benchmark.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/docs/api/executor.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/docs/api/loader.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/docs/api/middleware.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/docs/api/parser.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/docs/api/toolkit.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/docs/authentication.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/docs/contributing.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/docs/examples/crossref.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/docs/examples/petstore.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/docs/index.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/docs/installation.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/docs/middleware.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/docs/prompt_optimization.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/docs/quickstart.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/examples/README.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/examples/crossref/README.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/examples/crossref/crossref.json +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/examples/crossref/main.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/examples/crossref/requirements.txt +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/examples/petstore/README.md +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/examples/petstore/main.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/examples/petstore/petstore.json +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/examples/petstore/requirements.txt +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/langchain_openapi/__init__.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/langchain_openapi/py.typed +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/langchain_openapi_tools/enums.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/langchain_openapi_tools/exceptions.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/langchain_openapi_tools/middleware.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/langchain_openapi_tools/providers.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/langchain_openapi_tools/py.typed +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/langchain_openapi_tools/toolkit.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/langchain_openapi_tools/utils.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/mkdocs.yml +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/404.html +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/api/executor/index.html +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/api/loader/index.html +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/api/middleware/index.html +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/api/parser/index.html +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/api/toolkit/index.html +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/architecture/index.html +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/_mkdocstrings.css +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/images/favicon.png +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/bundle.d7400e89.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/bundle.d7400e89.min.js.map +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.ar.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.da.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.de.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.du.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.el.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.es.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.fi.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.fr.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.he.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.hi.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.hu.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.hy.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.it.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.ja.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.jp.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.kn.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.ko.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.multi.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.nl.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.no.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.pt.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.ro.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.ru.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.sa.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.stemmer.support.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.sv.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.ta.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.te.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.th.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.tr.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.vi.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/min/lunr.zh.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/tinyseg.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/lunr/wordcut.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/workers/search.2c215733.min.js +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/javascripts/workers/search.2c215733.min.js.map +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/stylesheets/main.ec1eaa64.min.css +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/stylesheets/main.ec1eaa64.min.css.map +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/stylesheets/palette.ab4e12ef.min.css +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/assets/stylesheets/palette.ab4e12ef.min.css.map +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/authentication/index.html +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/contributing/index.html +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/examples/crossref/index.html +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/examples/petstore/index.html +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/index.html +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/installation/index.html +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/middleware/index.html +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/objects.inv +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/prompt_optimization/index.html +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/quickstart/index.html +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/search/search_index.json +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/sitemap.xml +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/sitemap.xml.gz +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/site/toolkit/index.html +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/tests/test_compatibility.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/tests/test_e2e.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/tests/test_executor.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/tests/test_import.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/tests/test_middleware.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/tests/test_parser.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/tests/test_providers.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/tests/test_resolver.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/tests/test_schema_converter.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/tests/test_swagger.py +0 -0
- {langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/tests/test_toolkit.py +0 -0
|
@@ -30,13 +30,24 @@ The library is structured as a linear transform pipeline with isolated responsib
|
|
|
30
30
|
│ OpenAPI Loader │
|
|
31
31
|
│ - Parsing (JSON / YAML) │
|
|
32
32
|
│ - Ref Resolution ($ref expansion) │
|
|
33
|
+
│ - Remembers spec source URL │
|
|
34
|
+
└───────────────────────────┬────────────────────────────┘
|
|
35
|
+
│
|
|
36
|
+
▼
|
|
37
|
+
┌────────────────────────────────────────────────────────┐
|
|
38
|
+
│ Spec Adapter Layer │
|
|
39
|
+
│ - Version detection (swagger2 / 3.0 / 3.1) │
|
|
40
|
+
│ - Swagger2Adapter → OpenAPI 3.0 normalization │
|
|
41
|
+
│ - OpenAPI3Adapter (pass-through, 3.0 & 3.1) │
|
|
33
42
|
└───────────────────────────┬────────────────────────────┘
|
|
34
43
|
│
|
|
35
44
|
▼
|
|
36
45
|
┌────────────────────────────────────────────────────────┐
|
|
37
46
|
│ Internal Models │
|
|
38
47
|
│ - Parsed Spec & Operation trees │
|
|
39
|
-
│ - Schema normalization
|
|
48
|
+
│ - Schema normalization (incl. oneOf/anyOf/ │
|
|
49
|
+
│ allOf / const / read-write flags) │
|
|
50
|
+
│ - `spec_family` tag preserved from adapter │
|
|
40
51
|
└───────────────────────────┬────────────────────────────┘
|
|
41
52
|
│
|
|
42
53
|
▼
|
|
@@ -75,7 +86,31 @@ The library is structured as a linear transform pipeline with isolated responsib
|
|
|
75
86
|
|
|
76
87
|
The **OpenAPI Loader** is responsible for reading OpenAPI specifications from local file paths, raw strings, or remote HTTP URLs. It handles format auto-detection (JSON vs. YAML), syntax validation, and recursively resolves relative and external `$ref` pointers into a unified, flattened schema representation.
|
|
77
88
|
|
|
78
|
-
|
|
89
|
+
When loading from a URL, the loader preserves the source URL on the resulting `OpenAPISpec` (`spec.source_url`). This lets `OpenAPISpec.from_dict` and `SwaggerNormalizer` resolve relative `servers` entries and missing `host` fields to absolute URLs, so `RequestBuilder` always receives an absolute base URL — even for specs like `https://fakerestapi.azurewebsites.net/swagger/v1/swagger.json` that omit the `servers` block entirely.
|
|
90
|
+
|
|
91
|
+
### 3.2 Spec Adapter Layer
|
|
92
|
+
|
|
93
|
+
The **Spec Adapter Layer** (`langchain_openapi_tools.adapters`) provides a
|
|
94
|
+
uniform entry point across every supported specification family. It exposes:
|
|
95
|
+
|
|
96
|
+
* `detect_spec_version(spec_dict) -> Literal["swagger2", "openapi30", "openapi31"]`
|
|
97
|
+
* `SpecAdapter` (`typing.Protocol`) — the shared adapter interface.
|
|
98
|
+
* `Swagger2Adapter` — wraps `SwaggerNormalizer` to lift Swagger 2.0 documents
|
|
99
|
+
into an OpenAPI 3.0-compatible dictionary while remembering the source URL
|
|
100
|
+
so `host`/`basePath` gaps can be filled in from the fetch location.
|
|
101
|
+
* `OpenAPI3Adapter` — a pass-through adapter for both OpenAPI 3.0.x and
|
|
102
|
+
3.1.x documents.
|
|
103
|
+
* `select_adapter(spec_dict)` and `normalize_spec(spec_dict, source_url)` —
|
|
104
|
+
convenience helpers that pick the right adapter and return a `(normalized
|
|
105
|
+
dict, spec_family)` tuple.
|
|
106
|
+
|
|
107
|
+
`OpenAPISpec.from_dict` calls `normalize_spec` before parsing so downstream
|
|
108
|
+
components (`OpenAPIParser`, `SchemaConverter`, `RequestBuilder`) only ever
|
|
109
|
+
see a single canonical shape. `OpenAPISpec.spec_family` records which
|
|
110
|
+
family the document originated from, which is useful for logging and for
|
|
111
|
+
version-aware feature toggles in the future.
|
|
112
|
+
|
|
113
|
+
### 3.3 Internal Models & Specification Parser
|
|
79
114
|
|
|
80
115
|
The **OpenAPI Parser** (`OpenAPIParser`) transforms an `OpenAPISpec` into strongly typed, normalized Python dataclasses using the `ReferenceResolver` for local `$ref` pointer resolution:
|
|
81
116
|
|
|
@@ -99,16 +134,19 @@ OpenAPI Spec (File / URL / Dict)
|
|
|
99
134
|
* **`RequestBody`**: Defines the payload expected by POST/PUT/PATCH operations (`required`, `description`, `content`).
|
|
100
135
|
* **`Response`**: Defines an HTTP response status definition (`status_code`, `description`, `content`).
|
|
101
136
|
* **`MediaType`**: Maps a MIME content-type string to its schema and example payloads (`content_type`, `schema`, `example`, `examples`).
|
|
102
|
-
* **`Schema`**: Normalized schema structure representing primitive and complex types (`type`, `format`, `properties`, `items`, `required`, `enum`, `default`, `nullable`, `description`).
|
|
137
|
+
* **`Schema`**: Normalized schema structure representing primitive and complex types (`type`, `format`, `properties`, `items`, `required`, `enum`, `default`, `nullable`, `description`, `const`, `one_of`, `any_of`, `all_of`, `read_only`, `write_only`, `deprecated`).
|
|
103
138
|
|
|
104
139
|
#### Current Scope Limitations
|
|
105
140
|
|
|
106
|
-
The
|
|
107
|
-
|
|
141
|
+
The parser handles JSON Schema union keywords (`oneOf`, `anyOf`, `allOf`) and
|
|
142
|
+
OpenAPI 3.1 features such as list-form `type` and `const`, but intentionally
|
|
143
|
+
skips:
|
|
144
|
+
- `discriminator`-driven union routing.
|
|
108
145
|
- OpenAPI callbacks, links, and webhooks.
|
|
146
|
+
- `patternProperties`, `$dynamicRef`, `$dynamicAnchor`.
|
|
109
147
|
- Advanced example inheritance chains.
|
|
110
148
|
|
|
111
|
-
### 3.
|
|
149
|
+
### 3.4 Schema Converter
|
|
112
150
|
|
|
113
151
|
The **Schema Converter** (`SchemaConverter` and `PydanticFactory`) converts internal schema models (`Schema`) into dynamic Pydantic `BaseModel` classes using `pydantic.create_model()`.
|
|
114
152
|
|
|
@@ -135,6 +173,11 @@ Dynamic Pydantic Model (type[BaseModel])
|
|
|
135
173
|
| `array` | `list[T]` | Recursively typed items (e.g. `list[str]`) |
|
|
136
174
|
| `object` | Dynamic `BaseModel` | Dynamic nested models (e.g. `SearchWorksInput_Address`) |
|
|
137
175
|
| `enum` | Dynamic `Enum` | String Enum subclass (e.g. `SortEnum`) |
|
|
176
|
+
| `oneOf` / `anyOf` | `typing.Union[...]` | Variants are recursively converted |
|
|
177
|
+
| `allOf` | Merged `BaseModel` | Properties and `required` sets are combined |
|
|
178
|
+
| `type: [X, "null"]` | `Optional[X]` | 3.1-style nullable union |
|
|
179
|
+
| `nullable: true` | `Optional[X]` | 3.0-style nullable flag |
|
|
180
|
+
| `const` | `Literal[value]` | Fixed constant value |
|
|
138
181
|
|
|
139
182
|
#### Model Naming Conventions
|
|
140
183
|
|
|
@@ -144,13 +187,14 @@ Dynamic Pydantic Model (type[BaseModel])
|
|
|
144
187
|
|
|
145
188
|
#### Current Limitations
|
|
146
189
|
|
|
147
|
-
The converter
|
|
148
|
-
|
|
190
|
+
The converter now covers `oneOf` / `anyOf` / `allOf`, list-form `type`,
|
|
191
|
+
`nullable`, and `const`. It intentionally does not yet cover:
|
|
192
|
+
- `discriminator`-driven union routing (variants are unions without tag dispatch).
|
|
149
193
|
- Recursive schemas.
|
|
150
|
-
- XML schema
|
|
151
|
-
-
|
|
194
|
+
- XML schema-driven request serialization.
|
|
195
|
+
- `patternProperties`, `$dynamicRef`, `$dynamicAnchor`.
|
|
152
196
|
|
|
153
|
-
### 3.
|
|
197
|
+
### 3.5 Async HTTP Executor Engine
|
|
154
198
|
|
|
155
199
|
The **Async HTTP Executor** (`AsyncHTTPExecutor`) executes an `Operation` asynchronously using `httpx.AsyncClient`, transforming inputs into normalized `ResponseData` instances.
|
|
156
200
|
|
|
@@ -190,11 +234,11 @@ OpenAPIError
|
|
|
190
234
|
└── ExecutionTimeoutError
|
|
191
235
|
```
|
|
192
236
|
|
|
193
|
-
### 3.
|
|
237
|
+
### 3.6 Authentication Provider
|
|
194
238
|
|
|
195
239
|
The **Authentication Provider** manages auth headers and query parameters dynamically. It supports multiple authentication schemes (API Key, HTTP Bearer, HTTP Basic, OAuth2) and securely injects credentials into outbound HTTP requests without exposing secret values to the LLM agent or tool definitions.
|
|
196
240
|
|
|
197
|
-
### 3.
|
|
241
|
+
### 3.7 Tool Generator
|
|
198
242
|
|
|
199
243
|
The **Tool Generator** acts as the high-level factory interface for end users. It converts parsed operations into `langchain_core.tools.BaseTool` instances. It supports configuration options such as filtering by HTTP method, operation tags, or path patterns, as well as customizing tool naming conventions and prompt description templates.
|
|
200
244
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `langchain-openapi` will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## [Unreleased]
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Spec Adapter Architecture (`langchain_openapi_tools.adapters`)**: Introduced a `SpecAdapter` protocol plus concrete `Swagger2Adapter` and `OpenAPI3Adapter` implementations that provide a uniform pipeline entry for Swagger 2.0, OpenAPI 3.0.x, and OpenAPI 3.1.x documents. New public helpers: `detect_spec_version`, `select_adapter`, and `normalize_spec`. `OpenAPISpec.spec_family` records the source family, and `OpenAPISpec.from_dict` now routes through the adapter layer.
|
|
14
|
+
- **OpenAPI 3.1 Schema Support**: Parser and `SchemaConverter` now understand union `type` arrays (`["string", "null"]`), `const`, `oneOf` / `anyOf` (rendered as `typing.Union`), `allOf` (merged into a single Pydantic model), and the `readOnly` / `writeOnly` / `deprecated` field flags. `Schema` gained `const`, `one_of`, `any_of`, `all_of`, `read_only`, `write_only`, and `deprecated` attributes.
|
|
15
|
+
- **Full Media-Type Dispatch**: `RequestBuilder` now inspects each operation's declared `requestBody` content types and dispatches to the correct httpx encoding path — `application/json` (and `+json` vendor variants), `application/x-www-form-urlencoded`, `multipart/form-data` (with file-part support), `text/*`, and `application/xml`. Pydantic-model bodies produced by the dynamic args schema are coerced back to JSON-safe primitives before serialization. `BuiltRequest` gained `data`, `files`, and `content` fields.
|
|
16
|
+
- **Real-World Compatibility Tests**: Added `tests/test_adapters.py`, `tests/test_openapi_31.py`, `tests/test_media_types.py`, and `tests/test_compat_matrix.py` (Petstore, Crossref, GitHub, Stripe, Kubernetes, ASP.NET fakerestapi excerpts) exercising the full pipeline end-to-end via `respx`.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- **Base URL Resolution Across the Pipeline**: The specification's source URL is now propagated from `OpenAPILoader.from_url` → `OpenAPISpec` → `OpenAPIParser` → `RequestBuilder`. This closes an architectural gap where relative or missing `servers` entries produced relative request URLs.
|
|
20
|
+
- OpenAPI 3.x specs that omit the `servers` block (e.g. `https://fakerestapi.azurewebsites.net/swagger/v1/swagger.json`) now default to the document's `scheme://host` origin, matching the OpenAPI 3.x specification.
|
|
21
|
+
- OpenAPI 3.x relative server URLs (e.g. `"/api/v2"`) are resolved against the document location via `urllib.parse.urljoin`.
|
|
22
|
+
- Swagger 2.0 specs that omit `host` fall back to the source URL's host and scheme.
|
|
23
|
+
- `OpenAPISpec` gained a `source_url` field; `OpenAPISpec.from_dict` and `SwaggerNormalizer` accept an optional `source_url` argument for programmatic use.
|
|
24
|
+
- Redundant Swagger normalization calls in `OpenAPILoader.load` and `OpenAPIParser.__init__` were removed — `OpenAPISpec.from_dict` is now the single normalization site.
|
|
25
|
+
- No changes to `RequestBuilder` or `AsyncHTTPExecutor` were required to fix base-URL resolution; they transparently receive an absolute base URL because `spec.servers[0]` is guaranteed absolute whenever the spec was loaded from a URL.
|
|
26
|
+
|
|
27
|
+
### Compatibility
|
|
28
|
+
- All previously public APIs (`OpenAPIToolkit`, `OpenAPILoader`, `OpenAPIParser`, `OpenAPISpec`, `SwaggerNormalizer`, `RequestBuilder`, `AsyncHTTPExecutor`, middleware, providers) retain their original signatures. New parameters (`source_url`, `spec_family`) are optional.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## [1.0.2] - 2026-08-01
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
- **Base URL Generation**: Fixed Swagger 2.0 base URL generation (e.g. Crossref host/basePath/schemes resolution) to prevent relative URL execution errors.
|
|
36
|
+
- **URL Resolution**: Updated `RequestBuilder` to use `urllib.parse.urljoin` for safe URL concatenation.
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
- **Package Naming Clarification**: Clarified PyPI package name (`langchain-openapi-tools`) vs import module name (`langchain_openapi`) across documentation.
|
|
40
|
+
- **Prompt Optimization & Config**: Introduced `OpenAPIToolkitConfig` for user-controlled description modes (`full`, `compact`, `minimal`) and description prompt compression (`compress_descriptions=True`).
|
|
41
|
+
- **Tool Description Customization**: Added `tool_description_overrides` dict and custom `description_builder` callback functions.
|
|
42
|
+
- **Operation & Tag Filtering**: Added tag filtering (`include_tags`, `exclude_tags`) and operation filtering (`include_operations`, `exclude_operations`) at initial toolkit construction level.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## [1.0.0] - 2026-08-01
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
- **First Production Release (v1.0.0)**.
|
|
50
|
+
- **OpenAPI Loader**: Load specs from remote URLs, local JSON/YAML files, or Python dictionaries.
|
|
51
|
+
- **OpenAPI Parser**: Strongly typed internal data models for operations, parameters, request bodies, and responses with local `$ref` resolution.
|
|
52
|
+
- **Schema Converter Engine**: Convert JSON Schema parameters and request bodies to dynamic Pydantic models at runtime with optional multi-page pagination controls.
|
|
53
|
+
- **Async HTTP Executor Engine**: Asynchronous execution using `httpx.AsyncClient` with dynamic parameter formatting and response parsing.
|
|
54
|
+
- **LangChain Tool Factory**: Convert OpenAPI operations into dynamic LangChain `StructuredTool` collections with filtering options.
|
|
55
|
+
- **Authentication & Request Providers**: Pluggable provider system (`BearerAuthProvider`, `APIKeyHeaderProvider`, `APIKeyQueryProvider`, `BasicAuthProvider`, `CompositeProvider`).
|
|
56
|
+
- **Production Middleware**: Onion-style pipeline (`RetryMiddleware`, `RateLimitMiddleware`, `CacheMiddleware`, `PaginationMiddleware`, `LoggingMiddleware`).
|
|
57
|
+
- **Documentation & Examples**: Comprehensive MkDocs site, complete example applications (Crossref, GitHub, Petstore, JSONPlaceholder), and benchmark suite.
|
|
58
|
+
- **CI/CD & Release Automation**: GitHub Actions for CI, Release generation, PyPI Trusted Publishing, and GitHub Pages deployment.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: langchain-openapi-tools
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
4
4
|
Summary: Convert OpenAPI specifications into native, production-grade LangChain tools.
|
|
5
5
|
Project-URL: Homepage, https://github.com/abhaywani114/langchain-openapi
|
|
6
6
|
Project-URL: Documentation, https://abhaywani114.github.io/langchain-openapi/
|
|
@@ -109,9 +109,38 @@ from langchain_openapi_tools import OpenAPIToolkit
|
|
|
109
109
|
|
|
110
110
|
## Supported Specifications
|
|
111
111
|
|
|
112
|
-
- ✅ **Swagger 2.0** (Automatically normalized to OpenAPI 3.0)
|
|
112
|
+
- ✅ **Swagger 2.0** (Automatically normalized to OpenAPI 3.0 via the built-in adapter layer)
|
|
113
113
|
- ✅ **OpenAPI 3.0.x** (JSON and YAML)
|
|
114
|
-
- ✅ **OpenAPI 3.1.x** (JSON and YAML)
|
|
114
|
+
- ✅ **OpenAPI 3.1.x** (JSON and YAML, including union `type` arrays, `oneOf` / `anyOf` / `allOf`, and `const`)
|
|
115
|
+
|
|
116
|
+
> **Note:** Swagger 2.0 is commonly referred to as OpenAPI 2.0.
|
|
117
|
+
|
|
118
|
+
### Compatibility Matrix
|
|
119
|
+
|
|
120
|
+
| Feature | Swagger 2.0 | OpenAPI 3.0 | OpenAPI 3.1 |
|
|
121
|
+
| -------------------------------------- | :---------: | :---------: | :---------: |
|
|
122
|
+
| Path / query / header / cookie params | ✅ | ✅ | ✅ |
|
|
123
|
+
| `$ref` resolution (internal) | ✅ | ✅ | ✅ |
|
|
124
|
+
| Base URL from `host` + `basePath` | ✅ | — | — |
|
|
125
|
+
| Base URL from `servers[]` | — | ✅ | ✅ |
|
|
126
|
+
| Fallback base URL from spec source URL | ✅ | ✅ | ✅ |
|
|
127
|
+
| `application/json` request bodies | ✅ | ✅ | ✅ |
|
|
128
|
+
| `application/x-www-form-urlencoded` | ✅ | ✅ | ✅ |
|
|
129
|
+
| `multipart/form-data` (incl. files) | ✅ | ✅ | ✅ |
|
|
130
|
+
| `text/*` and `application/xml` | ✅ | ✅ | ✅ |
|
|
131
|
+
| Vendor `+json` media types | ✅ | ✅ | ✅ |
|
|
132
|
+
| `oneOf` / `anyOf` (as Python `Union`) | — | ✅ | ✅ |
|
|
133
|
+
| `allOf` merge into single model | — | ✅ | ✅ |
|
|
134
|
+
| `nullable: true` (3.0) | — | ✅ | — |
|
|
135
|
+
| `type: ["string", "null"]` (3.1) | — | — | ✅ |
|
|
136
|
+
| `const` | — | — | ✅ |
|
|
137
|
+
| `readOnly` / `writeOnly` / `deprecated`| ✅ | ✅ | ✅ |
|
|
138
|
+
| Security: Bearer / API key / Basic | ✅ | ✅ | ✅ |
|
|
139
|
+
| Async execution (`httpx.AsyncClient`) | ✅ | ✅ | ✅ |
|
|
140
|
+
|
|
141
|
+
Not yet supported: `discriminator`-driven union routing, `patternProperties`,
|
|
142
|
+
`$dynamicRef` / `$dynamicAnchor`, callbacks, links, webhooks, and XML request-body
|
|
143
|
+
serialization from Python dicts.
|
|
115
144
|
|
|
116
145
|
---
|
|
117
146
|
|
|
@@ -256,3 +285,27 @@ toolkit = OpenAPIToolkit.from_url(
|
|
|
256
285
|
▼
|
|
257
286
|
LangChain StructuredTools
|
|
258
287
|
```
|
|
288
|
+
|
|
289
|
+
### Example:
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
from langchain_openapi_tools import OpenAPIToolkit
|
|
293
|
+
from langchain.agents import create_agent
|
|
294
|
+
from langchain.chat_models import init_chat_model
|
|
295
|
+
|
|
296
|
+
toolkit = OpenAPIToolkit.from_url(
|
|
297
|
+
"https://api.crossref.org/swagger-docs"
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
model = init_chat_model("openrouter:qwen/qwen3-32b:free")
|
|
301
|
+
|
|
302
|
+
agent = create_agent(
|
|
303
|
+
model=model,
|
|
304
|
+
tools=toolkit.get_tools(tags=["Works"]),
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
response = agent.invoke({
|
|
308
|
+
"messages": "Find papers about Kashmir"
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
```
|
|
@@ -69,9 +69,38 @@ from langchain_openapi_tools import OpenAPIToolkit
|
|
|
69
69
|
|
|
70
70
|
## Supported Specifications
|
|
71
71
|
|
|
72
|
-
- ✅ **Swagger 2.0** (Automatically normalized to OpenAPI 3.0)
|
|
72
|
+
- ✅ **Swagger 2.0** (Automatically normalized to OpenAPI 3.0 via the built-in adapter layer)
|
|
73
73
|
- ✅ **OpenAPI 3.0.x** (JSON and YAML)
|
|
74
|
-
- ✅ **OpenAPI 3.1.x** (JSON and YAML)
|
|
74
|
+
- ✅ **OpenAPI 3.1.x** (JSON and YAML, including union `type` arrays, `oneOf` / `anyOf` / `allOf`, and `const`)
|
|
75
|
+
|
|
76
|
+
> **Note:** Swagger 2.0 is commonly referred to as OpenAPI 2.0.
|
|
77
|
+
|
|
78
|
+
### Compatibility Matrix
|
|
79
|
+
|
|
80
|
+
| Feature | Swagger 2.0 | OpenAPI 3.0 | OpenAPI 3.1 |
|
|
81
|
+
| -------------------------------------- | :---------: | :---------: | :---------: |
|
|
82
|
+
| Path / query / header / cookie params | ✅ | ✅ | ✅ |
|
|
83
|
+
| `$ref` resolution (internal) | ✅ | ✅ | ✅ |
|
|
84
|
+
| Base URL from `host` + `basePath` | ✅ | — | — |
|
|
85
|
+
| Base URL from `servers[]` | — | ✅ | ✅ |
|
|
86
|
+
| Fallback base URL from spec source URL | ✅ | ✅ | ✅ |
|
|
87
|
+
| `application/json` request bodies | ✅ | ✅ | ✅ |
|
|
88
|
+
| `application/x-www-form-urlencoded` | ✅ | ✅ | ✅ |
|
|
89
|
+
| `multipart/form-data` (incl. files) | ✅ | ✅ | ✅ |
|
|
90
|
+
| `text/*` and `application/xml` | ✅ | ✅ | ✅ |
|
|
91
|
+
| Vendor `+json` media types | ✅ | ✅ | ✅ |
|
|
92
|
+
| `oneOf` / `anyOf` (as Python `Union`) | — | ✅ | ✅ |
|
|
93
|
+
| `allOf` merge into single model | — | ✅ | ✅ |
|
|
94
|
+
| `nullable: true` (3.0) | — | ✅ | — |
|
|
95
|
+
| `type: ["string", "null"]` (3.1) | — | — | ✅ |
|
|
96
|
+
| `const` | — | — | ✅ |
|
|
97
|
+
| `readOnly` / `writeOnly` / `deprecated`| ✅ | ✅ | ✅ |
|
|
98
|
+
| Security: Bearer / API key / Basic | ✅ | ✅ | ✅ |
|
|
99
|
+
| Async execution (`httpx.AsyncClient`) | ✅ | ✅ | ✅ |
|
|
100
|
+
|
|
101
|
+
Not yet supported: `discriminator`-driven union routing, `patternProperties`,
|
|
102
|
+
`$dynamicRef` / `$dynamicAnchor`, callbacks, links, webhooks, and XML request-body
|
|
103
|
+
serialization from Python dicts.
|
|
75
104
|
|
|
76
105
|
---
|
|
77
106
|
|
|
@@ -216,3 +245,27 @@ toolkit = OpenAPIToolkit.from_url(
|
|
|
216
245
|
▼
|
|
217
246
|
LangChain StructuredTools
|
|
218
247
|
```
|
|
248
|
+
|
|
249
|
+
### Example:
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
from langchain_openapi_tools import OpenAPIToolkit
|
|
253
|
+
from langchain.agents import create_agent
|
|
254
|
+
from langchain.chat_models import init_chat_model
|
|
255
|
+
|
|
256
|
+
toolkit = OpenAPIToolkit.from_url(
|
|
257
|
+
"https://api.crossref.org/swagger-docs"
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
model = init_chat_model("openrouter:qwen/qwen3-32b:free")
|
|
261
|
+
|
|
262
|
+
agent = create_agent(
|
|
263
|
+
model=model,
|
|
264
|
+
tools=toolkit.get_tools(tags=["Works"]),
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
response = agent.invoke({
|
|
268
|
+
"messages": "Find papers about Kashmir"
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
```
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Architecture & Core Pipeline
|
|
2
|
+
|
|
3
|
+
`langchain-openapi` processes raw OpenAPI specs into native LangChain tools using a clean, modular pipeline.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Execution Pipeline
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
OpenAPI Spec (JSON / YAML / Dict)
|
|
11
|
+
│
|
|
12
|
+
▼
|
|
13
|
+
OpenAPILoader
|
|
14
|
+
│
|
|
15
|
+
▼
|
|
16
|
+
Spec Adapter Layer (Swagger 2.0 → 3.0 normalization; 3.0/3.1 pass-through)
|
|
17
|
+
│
|
|
18
|
+
▼
|
|
19
|
+
OpenAPIParser
|
|
20
|
+
│
|
|
21
|
+
▼
|
|
22
|
+
SchemaConverter ────► Pydantic Dynamic Models
|
|
23
|
+
│ (oneOf/anyOf → Union, allOf → merged model,
|
|
24
|
+
│ const → Literal, nullable → Optional)
|
|
25
|
+
▼
|
|
26
|
+
LangChainToolFactory
|
|
27
|
+
│
|
|
28
|
+
▼
|
|
29
|
+
AsyncHTTPExecutor
|
|
30
|
+
│
|
|
31
|
+
▼
|
|
32
|
+
Media-type dispatch (application/json, x-www-form-urlencoded,
|
|
33
|
+
multipart/form-data, text/*, application/xml,
|
|
34
|
+
vendor +json variants)
|
|
35
|
+
│
|
|
36
|
+
▼
|
|
37
|
+
Middleware Pipeline (Retry, Cache, RateLimit, Logging, Pagination)
|
|
38
|
+
│
|
|
39
|
+
▼
|
|
40
|
+
Request Providers (Authentication & Header Mutations)
|
|
41
|
+
│
|
|
42
|
+
▼
|
|
43
|
+
httpx.AsyncClient (Target HTTP API)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Pipeline Components
|
|
49
|
+
|
|
50
|
+
### 1. OpenAPILoader
|
|
51
|
+
In-gests raw JSON/YAML specifications from local disk files, remote HTTP endpoints, or in-memory dictionaries. Normalizes encoding and format. When loading from a URL, the loader retains the source URL on the resulting `OpenAPISpec` so relative `servers` entries (OpenAPI 3.x) and missing `host` values (Swagger 2.0) can be resolved to absolute request URLs downstream.
|
|
52
|
+
|
|
53
|
+
### 2. Spec Adapter Layer
|
|
54
|
+
`langchain_openapi_tools.adapters` provides `SpecAdapter` implementations for every supported family: `Swagger2Adapter` normalizes Swagger 2.0 documents into OpenAPI 3.0 shape (using the source URL to fill in a missing `host`), and `OpenAPI3Adapter` accepts both 3.0.x and 3.1.x without modification. `detect_spec_version()`, `select_adapter()`, and `normalize_spec()` provide the public entry points. The resulting family tag is preserved on `OpenAPISpec.spec_family`.
|
|
55
|
+
|
|
56
|
+
### 3. OpenAPIParser
|
|
57
|
+
Translates raw specification dictionaries into strongly-typed internal data models (`Operation`, `Parameter`, `RequestBody`, `Response`, `Schema`). Resolves internal `$ref` pointers using `ReferenceResolver`, and extracts polymorphic keywords (`oneOf`, `anyOf`, `allOf`), `const` values, and `readOnly`/`writeOnly`/`deprecated` markers.
|
|
58
|
+
|
|
59
|
+
### 4. SchemaConverter & PydanticFactory
|
|
60
|
+
Converts JSON Schema representations into dynamic Pydantic models at runtime using `pydantic.create_model()`. Union keywords become `typing.Union`s, `allOf` merges into a single model, list-form `type` becomes a `Union` of Python types, and `nullable` / `type: [X, "null"]` become `Optional[X]`. Incoming LLM tool arguments are validated against the resulting schema before dispatch.
|
|
61
|
+
|
|
62
|
+
### 5. LangChainToolFactory
|
|
63
|
+
Translates parsed `Operation` models into executable LangChain `StructuredTool` instances.
|
|
64
|
+
|
|
65
|
+
### 6. AsyncHTTPExecutor
|
|
66
|
+
Orchestrates request building, middleware execution, provider transformations, and asynchronous network transport. `RequestBuilder` inspects the operation's request-body content-types and dispatches to the correct httpx encoding (JSON, form-urlencoded, multipart with files, or raw text/XML), and coerces validated Pydantic body models back to JSON-safe primitives before serialization.
|
|
67
|
+
|
|
68
|
+
### 7. Middleware Pipeline
|
|
69
|
+
Intercepts outgoing requests and incoming responses to apply retries, rate limiting, caching, result aggregation, and sanitized telemetry.
|
|
70
|
+
|
|
71
|
+
### 8. Request Providers
|
|
72
|
+
Applies authentication schemes (Bearer tokens, API Keys, Basic Auth) and static headers before network transport.
|
|
@@ -36,6 +36,18 @@ toolkit = OpenAPIToolkit.from_dict(
|
|
|
36
36
|
|
|
37
37
|
---
|
|
38
38
|
|
|
39
|
+
## Base URL Resolution
|
|
40
|
+
|
|
41
|
+
The toolkit determines the base URL used for HTTP requests in this order of precedence:
|
|
42
|
+
|
|
43
|
+
1. Explicit `base_url` argument passed to `OpenAPIToolkit.__init__` / `from_url` / `from_file` / `from_dict`.
|
|
44
|
+
2. The first entry in the spec's `servers` block (OpenAPI 3.x) or the URL synthesized from `host`/`basePath`/`schemes` (Swagger 2.0).
|
|
45
|
+
3. When `OpenAPIToolkit.from_url(...)` is used and the spec has no `servers` (or Swagger 2.0 has no `host`), the source URL is used as a fallback: relative server URLs are resolved against it via `urljoin`, and a missing `servers` block defaults to the document's `scheme://host` origin — matching the OpenAPI 3.x specification.
|
|
46
|
+
|
|
47
|
+
This means self-describing endpoints such as `https://fakerestapi.azurewebsites.net/swagger/v1/swagger.json` (an OpenAPI 3.0.1 document that omits the `servers` block) work out of the box without a manual `base_url`. For local files or in-memory dicts with no `servers`, pass `base_url` explicitly.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
39
51
|
## Tool Filtering & Selection
|
|
40
52
|
|
|
41
53
|
Filter generated tools by HTTP methods, tags, or operation names:
|
{langchain_openapi_tools-1.1.3 → langchain_openapi_tools-2.0.0}/langchain_openapi_tools/__init__.py
RENAMED
|
@@ -5,6 +5,14 @@ executing HTTP requests asynchronously, configuring request providers,
|
|
|
5
5
|
applying production middleware, and exporting dynamic LangChain tools.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from langchain_openapi_tools.adapters import (
|
|
9
|
+
OpenAPI3Adapter,
|
|
10
|
+
SpecAdapter,
|
|
11
|
+
Swagger2Adapter,
|
|
12
|
+
detect_spec_version,
|
|
13
|
+
normalize_spec,
|
|
14
|
+
select_adapter,
|
|
15
|
+
)
|
|
8
16
|
from langchain_openapi_tools.enums import DataType, HTTPMethod, ParameterLocation
|
|
9
17
|
from langchain_openapi_tools.exceptions import (
|
|
10
18
|
AuthenticationError,
|
|
@@ -107,6 +115,7 @@ __all__ = [
|
|
|
107
115
|
"MiddlewarePipeline",
|
|
108
116
|
"NextCallable",
|
|
109
117
|
"NoAuthProvider",
|
|
118
|
+
"OpenAPI3Adapter",
|
|
110
119
|
"OpenAPIError",
|
|
111
120
|
"OpenAPILoader",
|
|
112
121
|
"OpenAPIParser",
|
|
@@ -133,15 +142,20 @@ __all__ = [
|
|
|
133
142
|
"RetryMiddleware",
|
|
134
143
|
"Schema",
|
|
135
144
|
"SchemaConverter",
|
|
145
|
+
"SpecAdapter",
|
|
136
146
|
"SpecLoadError",
|
|
137
147
|
"StaticHeadersProvider",
|
|
148
|
+
"Swagger2Adapter",
|
|
138
149
|
"SwaggerNormalizer",
|
|
139
150
|
"UnsupportedVersionError",
|
|
140
151
|
"__version__",
|
|
141
152
|
"build_tool_description",
|
|
142
153
|
"create_async_client",
|
|
154
|
+
"detect_spec_version",
|
|
143
155
|
"format_tool_name",
|
|
144
156
|
"generate_fallback_operation_name",
|
|
145
157
|
"map_schema_type_to_python",
|
|
158
|
+
"normalize_spec",
|
|
146
159
|
"sanitize_request_log",
|
|
160
|
+
"select_adapter",
|
|
147
161
|
]
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""Specification adapter layer.
|
|
2
|
+
|
|
3
|
+
This module implements the unified ingestion pipeline that transforms any
|
|
4
|
+
supported API specification (Swagger 2.0, OpenAPI 3.0, OpenAPI 3.1) into a
|
|
5
|
+
single normalized OpenAPI 3.x dictionary. Every downstream component
|
|
6
|
+
(``OpenAPIParser``, ``SchemaConverter``, ``RequestBuilder``, ...) operates
|
|
7
|
+
against the normalized shape and never inspects the original spec version.
|
|
8
|
+
|
|
9
|
+
Layering::
|
|
10
|
+
|
|
11
|
+
API Specification
|
|
12
|
+
│
|
|
13
|
+
┌────────────────┴────────────────┐
|
|
14
|
+
│ │
|
|
15
|
+
Swagger 2.0 OpenAPI 3.x
|
|
16
|
+
│ │
|
|
17
|
+
▼ ▼
|
|
18
|
+
Swagger2Adapter OpenAPI3Adapter
|
|
19
|
+
└──────────────┬──────────────────┘
|
|
20
|
+
▼
|
|
21
|
+
Normalized Internal Spec
|
|
22
|
+
|
|
23
|
+
Adapters are stateless and receive an optional ``source_url`` for base-URL
|
|
24
|
+
resolution when the spec omits ``servers`` / ``host``.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
from typing import Any, Protocol
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class SpecAdapter(Protocol):
|
|
33
|
+
"""Protocol for specification-version adapters.
|
|
34
|
+
|
|
35
|
+
An adapter accepts a raw specification dictionary and returns a
|
|
36
|
+
normalized OpenAPI 3.x dictionary. The returned document is the sole
|
|
37
|
+
input consumed by the rest of the pipeline.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def normalize(
|
|
41
|
+
self, spec_dict: dict[str, Any], source_url: str | None = None
|
|
42
|
+
) -> dict[str, Any]:
|
|
43
|
+
"""Return a normalized OpenAPI 3.x dictionary."""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class Swagger2Adapter:
|
|
47
|
+
"""Adapter for Swagger 2.0 (a.k.a. OpenAPI 2.0) specifications.
|
|
48
|
+
|
|
49
|
+
Delegates to ``SwaggerNormalizer`` for the actual field-by-field
|
|
50
|
+
translation, then relies on the pipeline's OpenAPI 3.x processing for
|
|
51
|
+
everything downstream.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def normalize(
|
|
55
|
+
self, spec_dict: dict[str, Any], source_url: str | None = None
|
|
56
|
+
) -> dict[str, Any]:
|
|
57
|
+
from langchain_openapi_tools.swagger import SwaggerNormalizer
|
|
58
|
+
|
|
59
|
+
return SwaggerNormalizer(spec_dict, source_url=source_url).normalize()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class OpenAPI3Adapter:
|
|
63
|
+
"""Adapter for OpenAPI 3.0 and 3.1 specifications.
|
|
64
|
+
|
|
65
|
+
OpenAPI 3.x documents already match the internal normalized shape, so
|
|
66
|
+
the adapter primarily performs the following:
|
|
67
|
+
|
|
68
|
+
* Ensures the document has an ``openapi`` field.
|
|
69
|
+
* Backfills ``jsonSchemaDialect`` for 3.1 documents that omit it.
|
|
70
|
+
* Leaves ``servers`` / ``components`` untouched; base-URL resolution is
|
|
71
|
+
handled by :class:`langchain_openapi_tools.parser.OpenAPISpec`.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def normalize(
|
|
75
|
+
self, spec_dict: dict[str, Any], source_url: str | None = None
|
|
76
|
+
) -> dict[str, Any]:
|
|
77
|
+
return spec_dict
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def detect_spec_version(spec_dict: dict[str, Any]) -> str:
|
|
81
|
+
"""Detect the spec version family: ``"swagger2"``, ``"openapi30"``, ``"openapi31"``.
|
|
82
|
+
|
|
83
|
+
Raises ``UnsupportedVersionError`` for anything else.
|
|
84
|
+
"""
|
|
85
|
+
from langchain_openapi_tools.exceptions import UnsupportedVersionError
|
|
86
|
+
|
|
87
|
+
swagger = str(spec_dict.get("swagger", ""))
|
|
88
|
+
if swagger == "2.0" or swagger.startswith("2."):
|
|
89
|
+
return "swagger2"
|
|
90
|
+
|
|
91
|
+
openapi = str(spec_dict.get("openapi", ""))
|
|
92
|
+
if openapi.startswith("3.0"):
|
|
93
|
+
return "openapi30"
|
|
94
|
+
if openapi.startswith("3.1"):
|
|
95
|
+
return "openapi31"
|
|
96
|
+
|
|
97
|
+
raise UnsupportedVersionError(
|
|
98
|
+
f"Unsupported specification version: swagger={swagger!r}, openapi={openapi!r}."
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def select_adapter(spec_dict: dict[str, Any]) -> SpecAdapter:
|
|
103
|
+
"""Return the correct :class:`SpecAdapter` for the given raw spec dict."""
|
|
104
|
+
family = detect_spec_version(spec_dict)
|
|
105
|
+
if family == "swagger2":
|
|
106
|
+
return Swagger2Adapter()
|
|
107
|
+
return OpenAPI3Adapter()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def normalize_spec(
|
|
111
|
+
spec_dict: dict[str, Any], source_url: str | None = None
|
|
112
|
+
) -> tuple[dict[str, Any], str]:
|
|
113
|
+
"""Normalize any supported spec into an OpenAPI 3.x dictionary.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
spec_dict: Raw specification dictionary (Swagger 2.0 or OpenAPI 3.x).
|
|
117
|
+
source_url: Optional URL the specification was fetched from.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Tuple of ``(normalized_dict, family)`` where ``family`` is one of
|
|
121
|
+
``"swagger2"``, ``"openapi30"``, ``"openapi31"``.
|
|
122
|
+
"""
|
|
123
|
+
family = detect_spec_version(spec_dict)
|
|
124
|
+
adapter = select_adapter(spec_dict)
|
|
125
|
+
normalized = adapter.normalize(spec_dict, source_url=source_url)
|
|
126
|
+
return normalized, family
|