mirror of
https://github.com/hwchase17/langchain.git
synced 2025-08-14 07:07:34 +00:00
make openapi_schema_pydantic opt (#9408)
This commit is contained in:
parent
8f2d321dd0
commit
8c986221e4
@ -1,10 +1,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
from collections import defaultdict
|
||||
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
||||
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Union
|
||||
|
||||
import requests
|
||||
from openapi_schema_pydantic import Parameter
|
||||
from requests import Response
|
||||
|
||||
from langchain import LLMChain
|
||||
@ -20,6 +21,9 @@ from langchain.tools import APIOperation
|
||||
from langchain.utilities.openapi import OpenAPISpec
|
||||
from langchain.utils.input import get_colored_text
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from openapi_schema_pydantic import Parameter
|
||||
|
||||
|
||||
def _get_description(o: Any, prefer_short: bool) -> Optional[str]:
|
||||
summary = getattr(o, "summary", None)
|
||||
|
@ -1,7 +1,19 @@
|
||||
"""Pydantic models for parsing an OpenAPI spec."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, List, Optional, Sequence, Tuple, Type, Union
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Dict,
|
||||
List,
|
||||
Optional,
|
||||
Sequence,
|
||||
Tuple,
|
||||
Type,
|
||||
Union,
|
||||
)
|
||||
|
||||
from langchain.pydantic_v1 import _PYDANTIC_MAJOR_VERSION, BaseModel, Field
|
||||
from langchain.tools.openapi.utils.openapi_utils import HTTPVerb, OpenAPISpec
|
||||
@ -84,10 +96,10 @@ class APIPropertyBase(BaseModel):
|
||||
|
||||
|
||||
if _PYDANTIC_MAJOR_VERSION == 1:
|
||||
if TYPE_CHECKING:
|
||||
from openapi_schema_pydantic import (
|
||||
MediaType,
|
||||
Parameter,
|
||||
Reference,
|
||||
RequestBody,
|
||||
Schema,
|
||||
)
|
||||
@ -118,6 +130,11 @@ if _PYDANTIC_MAJOR_VERSION == 1:
|
||||
def _get_schema_type_for_array(
|
||||
schema: Schema,
|
||||
) -> Optional[Union[str, Tuple[str, ...]]]:
|
||||
from openapi_schema_pydantic import (
|
||||
Reference,
|
||||
Schema,
|
||||
)
|
||||
|
||||
items = schema.items
|
||||
if isinstance(items, Schema):
|
||||
schema_type = APIProperty._cast_schema_list_type(items)
|
||||
@ -175,6 +192,11 @@ if _PYDANTIC_MAJOR_VERSION == 1:
|
||||
|
||||
@staticmethod
|
||||
def _get_schema(parameter: Parameter, spec: OpenAPISpec) -> Optional[Schema]:
|
||||
from openapi_schema_pydantic import (
|
||||
Reference,
|
||||
Schema,
|
||||
)
|
||||
|
||||
schema = parameter.param_schema
|
||||
if isinstance(schema, Reference):
|
||||
schema = spec.get_referenced_schema(schema)
|
||||
@ -231,6 +253,10 @@ if _PYDANTIC_MAJOR_VERSION == 1:
|
||||
def _process_object_schema(
|
||||
cls, schema: Schema, spec: OpenAPISpec, references_used: List[str]
|
||||
) -> Tuple[Union[str, List[str], None], List["APIRequestBodyProperty"]]:
|
||||
from openapi_schema_pydantic import (
|
||||
Reference,
|
||||
)
|
||||
|
||||
properties = []
|
||||
required_props = schema.required or []
|
||||
if schema.properties is None:
|
||||
@ -265,6 +291,8 @@ if _PYDANTIC_MAJOR_VERSION == 1:
|
||||
spec: OpenAPISpec,
|
||||
references_used: List[str],
|
||||
) -> str:
|
||||
from openapi_schema_pydantic import Reference, Schema
|
||||
|
||||
items = schema.items
|
||||
if items is not None:
|
||||
if isinstance(items, Reference):
|
||||
@ -352,6 +380,8 @@ if _PYDANTIC_MAJOR_VERSION == 1:
|
||||
spec: OpenAPISpec,
|
||||
) -> List[APIRequestBodyProperty]:
|
||||
"""Process the media type of the request body."""
|
||||
from openapi_schema_pydantic import Reference
|
||||
|
||||
references_used = []
|
||||
schema = media_type_obj.media_type_schema
|
||||
if isinstance(schema, Reference):
|
||||
|
@ -7,7 +7,7 @@ import logging
|
||||
import re
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Union
|
||||
from typing import TYPE_CHECKING, Dict, List, Optional, Union
|
||||
|
||||
import requests
|
||||
import yaml
|
||||
@ -39,9 +39,9 @@ class HTTPVerb(str, Enum):
|
||||
|
||||
|
||||
if _PYDANTIC_MAJOR_VERSION == 1:
|
||||
if TYPE_CHECKING:
|
||||
from openapi_schema_pydantic import (
|
||||
Components,
|
||||
OpenAPI,
|
||||
Operation,
|
||||
Parameter,
|
||||
PathItem,
|
||||
@ -51,6 +51,11 @@ if _PYDANTIC_MAJOR_VERSION == 1:
|
||||
Schema,
|
||||
)
|
||||
|
||||
try:
|
||||
from openapi_schema_pydantic import OpenAPI
|
||||
except ImportError:
|
||||
OpenAPI = object
|
||||
|
||||
class OpenAPISpec(OpenAPI):
|
||||
"""OpenAPI Model that removes mis-formatted parts of the spec."""
|
||||
|
||||
@ -109,6 +114,8 @@ if _PYDANTIC_MAJOR_VERSION == 1:
|
||||
|
||||
def _get_root_referenced_parameter(self, ref: Reference) -> Parameter:
|
||||
"""Get the root reference or err."""
|
||||
from openapi_schema_pydantic import Reference
|
||||
|
||||
parameter = self._get_referenced_parameter(ref)
|
||||
while isinstance(parameter, Reference):
|
||||
parameter = self._get_referenced_parameter(parameter)
|
||||
@ -123,12 +130,16 @@ if _PYDANTIC_MAJOR_VERSION == 1:
|
||||
return schemas[ref_name]
|
||||
|
||||
def get_schema(self, schema: Union[Reference, Schema]) -> Schema:
|
||||
from openapi_schema_pydantic import Reference
|
||||
|
||||
if isinstance(schema, Reference):
|
||||
return self.get_referenced_schema(schema)
|
||||
return schema
|
||||
|
||||
def _get_root_referenced_schema(self, ref: Reference) -> Schema:
|
||||
"""Get the root reference or err."""
|
||||
from openapi_schema_pydantic import Reference
|
||||
|
||||
schema = self.get_referenced_schema(ref)
|
||||
while isinstance(schema, Reference):
|
||||
schema = self.get_referenced_schema(schema)
|
||||
@ -148,6 +159,8 @@ if _PYDANTIC_MAJOR_VERSION == 1:
|
||||
self, ref: Reference
|
||||
) -> Optional[RequestBody]:
|
||||
"""Get the root request Body or err."""
|
||||
from openapi_schema_pydantic import Reference
|
||||
|
||||
request_body = self._get_referenced_request_body(ref)
|
||||
while isinstance(request_body, Reference):
|
||||
request_body = self._get_referenced_request_body(request_body)
|
||||
@ -235,6 +248,8 @@ if _PYDANTIC_MAJOR_VERSION == 1:
|
||||
|
||||
def get_methods_for_path(self, path: str) -> List[str]:
|
||||
"""Return a list of valid methods for the specified path."""
|
||||
from openapi_schema_pydantic import Operation
|
||||
|
||||
path_item = self._get_path_strict(path)
|
||||
results = []
|
||||
for method in HTTPVerb:
|
||||
@ -244,6 +259,8 @@ if _PYDANTIC_MAJOR_VERSION == 1:
|
||||
return results
|
||||
|
||||
def get_parameters_for_path(self, path: str) -> List[Parameter]:
|
||||
from openapi_schema_pydantic import Reference
|
||||
|
||||
path_item = self._get_path_strict(path)
|
||||
parameters = []
|
||||
if not path_item.parameters:
|
||||
@ -256,6 +273,8 @@ if _PYDANTIC_MAJOR_VERSION == 1:
|
||||
|
||||
def get_operation(self, path: str, method: str) -> Operation:
|
||||
"""Get the operation object for a given path and HTTP method."""
|
||||
from openapi_schema_pydantic import Operation
|
||||
|
||||
path_item = self._get_path_strict(path)
|
||||
operation_obj = getattr(path_item, method, None)
|
||||
if not isinstance(operation_obj, Operation):
|
||||
@ -264,6 +283,8 @@ if _PYDANTIC_MAJOR_VERSION == 1:
|
||||
|
||||
def get_parameters_for_operation(self, operation: Operation) -> List[Parameter]:
|
||||
"""Get the components for a given operation."""
|
||||
from openapi_schema_pydantic import Reference
|
||||
|
||||
parameters = []
|
||||
if operation.parameters:
|
||||
for parameter in operation.parameters:
|
||||
@ -276,6 +297,8 @@ if _PYDANTIC_MAJOR_VERSION == 1:
|
||||
self, operation: Operation
|
||||
) -> Optional[RequestBody]:
|
||||
"""Get the request body for a given operation."""
|
||||
from openapi_schema_pydantic import Reference
|
||||
|
||||
request_body = operation.requestBody
|
||||
if isinstance(request_body, Reference):
|
||||
request_body = self._get_root_referenced_request_body(request_body)
|
||||
|
6
libs/langchain/poetry.lock
generated
6
libs/langchain/poetry.lock
generated
@ -5637,7 +5637,7 @@ name = "openapi-schema-pydantic"
|
||||
version = "1.2.4"
|
||||
description = "OpenAPI (v3) specification schema as pydantic class"
|
||||
category = "main"
|
||||
optional = false
|
||||
optional = true
|
||||
python-versions = ">=3.6.1"
|
||||
files = [
|
||||
{file = "openapi-schema-pydantic-1.2.4.tar.gz", hash = "sha256:3e22cf58b74a69f752cc7e5f1537f6e44164282db2700cbbcd3bb99ddd065196"},
|
||||
@ -10477,7 +10477,7 @@ clarifai = ["clarifai"]
|
||||
cohere = ["cohere"]
|
||||
docarray = ["docarray"]
|
||||
embeddings = ["sentence-transformers"]
|
||||
extended-testing = ["amazon-textract-caller", "atlassian-python-api", "beautifulsoup4", "bibtexparser", "cassio", "chardet", "esprima", "faiss-cpu", "feedparser", "geopandas", "gitpython", "gql", "html2text", "jinja2", "jq", "lxml", "mwparserfromhell", "mwxml", "newspaper3k", "openai", "openai", "pandas", "pdfminer-six", "pgvector", "psychicapi", "py-trello", "pymupdf", "pypdf", "pypdfium2", "pyspark", "rank-bm25", "rapidfuzz", "requests-toolbelt", "scikit-learn", "streamlit", "sympy", "telethon", "tqdm", "xata", "xmltodict"]
|
||||
extended-testing = ["amazon-textract-caller", "atlassian-python-api", "beautifulsoup4", "bibtexparser", "cassio", "chardet", "esprima", "faiss-cpu", "feedparser", "geopandas", "gitpython", "gql", "html2text", "jinja2", "jq", "lxml", "mwparserfromhell", "mwxml", "newspaper3k", "openai", "openai", "openapi-schema-pydantic", "pandas", "pdfminer-six", "pgvector", "psychicapi", "py-trello", "pymupdf", "pypdf", "pypdfium2", "pyspark", "rank-bm25", "rapidfuzz", "requests-toolbelt", "scikit-learn", "streamlit", "sympy", "telethon", "tqdm", "xata", "xmltodict"]
|
||||
javascript = ["esprima"]
|
||||
llms = ["clarifai", "cohere", "huggingface_hub", "manifest-ml", "nlpcloud", "openai", "openlm", "torch", "transformers"]
|
||||
openai = ["openai", "tiktoken"]
|
||||
@ -10487,4 +10487,4 @@ text-helpers = ["chardet"]
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.8.1,<4.0"
|
||||
content-hash = "594d1f6ea7a3e00f0ab6c74cab8b75245d112a84635af440df7ab1242d464140"
|
||||
content-hash = "a5e3458dd0cabcefd83caec6eb33b6fb593c2c347ca1d33c1f182341e852a9c8"
|
||||
|
@ -19,7 +19,7 @@ PyYAML = ">=5.3"
|
||||
numpy = "^1"
|
||||
azure-core = {version = "^1.26.4", optional=true}
|
||||
tqdm = {version = ">=4.48.0", optional = true}
|
||||
openapi-schema-pydantic = "^1.2"
|
||||
openapi-schema-pydantic = {version = "^1.2", optional = true}
|
||||
faiss-cpu = {version = "^1", optional = true}
|
||||
wikipedia = {version = "^1", optional = true}
|
||||
elasticsearch = {version = "^8", optional = true}
|
||||
@ -336,6 +336,7 @@ extended_testing = [
|
||||
"xata",
|
||||
"xmltodict",
|
||||
"faiss-cpu",
|
||||
"openapi-schema-pydantic",
|
||||
]
|
||||
|
||||
[tool.ruff]
|
||||
|
@ -41,7 +41,6 @@ def test_required_dependencies(poetry_conf: Mapping[str, Any]) -> None:
|
||||
"langsmith",
|
||||
"numexpr",
|
||||
"numpy",
|
||||
"openapi-schema-pydantic",
|
||||
"pydantic",
|
||||
"python",
|
||||
"requests",
|
||||
|
@ -18,14 +18,6 @@ if _PYDANTIC_MAJOR_VERSION != 1:
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
from openapi_schema_pydantic import (
|
||||
Components,
|
||||
Info,
|
||||
MediaType,
|
||||
Reference,
|
||||
RequestBody,
|
||||
Schema,
|
||||
)
|
||||
|
||||
from langchain.tools.openapi.utils.api_models import (
|
||||
APIOperation,
|
||||
@ -86,30 +78,38 @@ def http_paths_and_methods() -> List[Tuple[str, OpenAPISpec, str, str]]:
|
||||
return http_paths_and_methods
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"spec_name, spec, path, method",
|
||||
http_paths_and_methods(),
|
||||
)
|
||||
def test_parse_api_operations(
|
||||
spec_name: str, spec: OpenAPISpec, path: str, method: str
|
||||
) -> None:
|
||||
@pytest.mark.requires("openapi_schema_pydantic")
|
||||
def test_parse_api_operations() -> None:
|
||||
"""Test the APIOperation class."""
|
||||
for spec_name, spec, path, method in http_paths_and_methods():
|
||||
try:
|
||||
APIOperation.from_openapi_spec(spec, path, method)
|
||||
except Exception as e:
|
||||
raise AssertionError(f"Error processing {spec_name}: {e} ") from e
|
||||
|
||||
|
||||
@pytest.mark.requires("openapi_schema_pydantic")
|
||||
@pytest.fixture
|
||||
def raw_spec() -> OpenAPISpec:
|
||||
"""Return a raw OpenAPI spec."""
|
||||
from openapi_schema_pydantic import Info
|
||||
|
||||
return OpenAPISpec(
|
||||
info=Info(title="Test API", version="1.0.0"),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.requires("openapi_schema_pydantic")
|
||||
def test_api_request_body_from_request_body_with_ref(raw_spec: OpenAPISpec) -> None:
|
||||
"""Test instantiating APIRequestBody from RequestBody with a reference."""
|
||||
from openapi_schema_pydantic import (
|
||||
Components,
|
||||
MediaType,
|
||||
Reference,
|
||||
RequestBody,
|
||||
Schema,
|
||||
)
|
||||
|
||||
raw_spec.components = Components(
|
||||
schemas={
|
||||
"Foo": Schema(
|
||||
@ -140,8 +140,15 @@ def test_api_request_body_from_request_body_with_ref(raw_spec: OpenAPISpec) -> N
|
||||
assert api_request_body.media_type == "application/json"
|
||||
|
||||
|
||||
@pytest.mark.requires("openapi_schema_pydantic")
|
||||
def test_api_request_body_from_request_body_with_schema(raw_spec: OpenAPISpec) -> None:
|
||||
"""Test instantiating APIRequestBody from RequestBody with a schema."""
|
||||
from openapi_schema_pydantic import (
|
||||
MediaType,
|
||||
RequestBody,
|
||||
Schema,
|
||||
)
|
||||
|
||||
request_body = RequestBody(
|
||||
content={
|
||||
"application/json": MediaType(
|
||||
@ -164,7 +171,14 @@ def test_api_request_body_from_request_body_with_schema(raw_spec: OpenAPISpec) -
|
||||
assert api_request_body.media_type == "application/json"
|
||||
|
||||
|
||||
@pytest.mark.requires("openapi_schema_pydantic")
|
||||
def test_api_request_body_property_from_schema(raw_spec: OpenAPISpec) -> None:
|
||||
from openapi_schema_pydantic import (
|
||||
Components,
|
||||
Reference,
|
||||
Schema,
|
||||
)
|
||||
|
||||
raw_spec.components = Components(
|
||||
schemas={
|
||||
"Bar": Schema(
|
||||
|
Loading…
Reference in New Issue
Block a user