Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenAPI Spec - reduce_openapi_spec - maximum recursion depth exceeded #12163

Open
Cyb3rWard0g opened this issue Oct 23, 2023 · 6 comments
Open
Labels
Ɑ: agent Related to agents module 🤖:bug Related to a bug, vulnerability, unexpected error with an existing feature 🤖:question A specific question about the codebase, product, project, or how to use a feature stale Issue has not had recent activity or appears to be solved. Stale issues will be automatically closed

Comments

@Cyb3rWard0g
Copy link

Issue you'd like to raise.

Hello team!

I am new to the OpenAPI Specs Toolkit. I am trying to build an OpenAPI Agent Planner for the Microsoft Graph API. The OpenAPI specs can be found here: https://github.com/microsoftgraph/msgraph-metadata/blob/master/openapi/v1.0/openapi.yaml . I downloaded the YAML file and followed this notebook: https://python.langchain.com/docs/integrations/toolkits/openapi#1st-example-hierarchical-planning-agent .

import os
import yaml
from langchain.agents.agent_toolkits.openapi.spec import reduce_openapi_spec

current_directory = os.path.abspath('')
data_directory = os.path.join(current_directory, "data")
msgraph_api_file = os.path.join(data_directory, "msgraph-openapi.yaml")

raw_msgraph_api_spec = yaml.load(open(msgraph_api_file,encoding='utf-8').read(), Loader=yaml.Loader)
msgraph_api_spec = reduce_openapi_spec(raw_msgraph_api_spec)

Does anyone know how to handle large OpenAPI Specs? I ran the following to read my OpenAPI YAML spec and when using the reduce_openapi_spec module, I get the following error below:

RecursionError: maximum recursion depth exceeded while calling a Python object

Is there a setting I need to change in LangChain? Please and thank you for your help in advance. I believe this issue is before I run into the following token limit, right? #2786

Suggestion:

No response

@dosubot dosubot bot added Ɑ: agent Related to agents module 🤖:bug Related to a bug, vulnerability, unexpected error with an existing feature 🤖:question A specific question about the codebase, product, project, or how to use a feature labels Oct 23, 2023
@dosubot
Copy link

dosubot bot commented Oct 23, 2023

🤖

Hello,

Thank you for reaching out and providing detailed information about the issue you're facing. The RecursionError: maximum recursion depth exceeded while calling a Python object error you're encountering is likely due to the dereference_refs function, which is called within the reduce_openapi_spec function. If the OpenAPI spec has a deep structure, it could cause a recursion depth exceeded error.

To handle large OpenAPI Specs in LangChain without exceeding the maximum recursion depth, you can modify the reduce_openapi_spec function to use an iterative approach instead of a recursive one. Here's a general example of how you might convert a recursive function to an iterative one:

def recursive_function(node):
    if base_case(node):
        return some_value
    else:
        return recursive_function(next_node(node))

# can be converted to:

def iterative_function(start_node):
    stack = [start_node]
    while stack:
        node = stack.pop()
        if base_case(node):
            return some_value
        else:
            stack.append(next_node(node))

In this case, you would need to apply a similar transformation to the dereference_refs function. If you provide the implementation of dereference_refs, I can give a more specific solution.

I hope this helps! If you have any other questions or need further clarification, please don't hesitate to ask.

Best,
Dosu

Sources


This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.

Help me be more useful! Please leave a 👍 if this is helpful and 👎 if it is irrelevant.

If you want to continue the conversation, start your reply with @dosu-bot.

@Cyb3rWard0g
Copy link
Author

I guess just to test the other capabilities under reduce_openapi_spec, if I set dereference to False in the function, this works:

msgraph_api_spec = reduce_openapi_spec(raw_msgraph_api_spec, dereference=False)

However, I believe now I have this massive spec with over 9K endpoints :) . Any help on how to handle those massive OpenAPI specs?

Copy link

dosubot bot commented Feb 3, 2024

Hi, @Cyb3rWard0g,

I'm helping the LangChain team manage their backlog and am marking this issue as stale. From what I understand, you encountered a "maximum recursion depth exceeded" error when using the reduce_openapi_spec module with a large OpenAPI YAML spec for the Microsoft Graph API. Dosu-bot provided a detailed response, suggesting a modification to the reduce_openapi_spec function to use an iterative approach instead of a recursive one to handle large OpenAPI specs. You later tested setting dereference to False in the function, which worked for a massive spec with over 9K endpoints, and requested further assistance on handling such large OpenAPI specs. Yazdipour reacted positively to your comment.

Is this issue still relevant to the latest version of the LangChain repository? If it is, please let the LangChain team know by commenting on the issue. Otherwise, feel free to close the issue yourself, or the issue will be automatically closed in 7 days.

Thank you for your understanding and contributions to LangChain! If you have any further questions or need assistance, feel free to reach out.

@dosubot dosubot bot added the stale Issue has not had recent activity or appears to be solved. Stale issues will be automatically closed label Feb 3, 2024
@nischalj10
Copy link

Hey @Cyb3rWard0g were you able to resolve this or try out the solution by the bot? I am facing the same issue.

@dosubot dosubot bot removed the stale Issue has not had recent activity or appears to be solved. Stale issues will be automatically closed label Feb 5, 2024
@nischalj10
Copy link

nischalj10 commented Feb 5, 2024

@dosubot

In this case, you would need to apply a similar transformation to the dereference_refs function. If you provide the implementation of dereference_refs, I can give a more specific solution.

Here is the implementation of the dereference_refs in the /langchain_core/utils/json_schema.py file

from __future__ import annotations

from copy import deepcopy
from typing import Any, List, Optional, Sequence

def _retrieve_ref(path: str, schema: dict) -> dict:
    components = path.split("/")
    if components[0] != "#":
        raise ValueError(
            "ref paths are expected to be URI fragments, meaning they should start "
            "with #."
        )
    out = schema
    for component in components[1:]:
        if component.isdigit():
            out = out[int(component)]
        else:
            out = out[component]
    return deepcopy(out)


def _dereference_refs_helper(
    obj: Any, full_schema: dict, skip_keys: Sequence[str]
) -> Any:
    if isinstance(obj, dict):
        obj_out = {}
        for k, v in obj.items():
            if k in skip_keys:
                obj_out[k] = v
            elif k == "$ref":
                ref = _retrieve_ref(v, full_schema)
                return _dereference_refs_helper(ref, full_schema, skip_keys)
            elif isinstance(v, (list, dict)):
                obj_out[k] = _dereference_refs_helper(v, full_schema, skip_keys)
            else:
                obj_out[k] = v
        return obj_out
    elif isinstance(obj, list):
        return [_dereference_refs_helper(el, full_schema, skip_keys) for el in obj]
    else:
        return obj


def _infer_skip_keys(obj: Any, full_schema: dict) -> List[str]:
    keys = []
    if isinstance(obj, dict):
        for k, v in obj.items():
            if k == "$ref":
                ref = _retrieve_ref(v, full_schema)
                keys.append(v.split("/")[1])
                keys += _infer_skip_keys(ref, full_schema)
            elif isinstance(v, (list, dict)):
                keys += _infer_skip_keys(v, full_schema)
    elif isinstance(obj, list):
        for el in obj:
            keys += _infer_skip_keys(el, full_schema)
    return keys


def dereference_refs(
    schema_obj: dict,
    *,
    full_schema: Optional[dict] = None,
    skip_keys: Optional[Sequence[str]] = None,
) -> dict:
    """Try to substitute $refs in JSON Schema."""

    full_schema = full_schema or schema_obj
    skip_keys = (
        skip_keys
        if skip_keys is not None
        else _infer_skip_keys(schema_obj, full_schema)
    )
    return _dereference_refs_helper(schema_obj, full_schema, skip_keys)

@thebhulawat
Copy link
Contributor

I noticed the same issue for my open api spec. I think this was an issue of circular dependency. With proposed changes here I was also able to run MS Graph openapi specs.

baskaryan added a commit that referenced this issue Mar 12, 2024
**Description:** Circular dependencies when parsing references leading
to `RecursionError: maximum recursion depth exceeded` issue. This PR
address the issue by handling previously seen refs as in any typical DFS
to avoid infinite depths.

**Issue:** #12163

 **Twitter handle:** https://twitter.com/theBhulawat 


- [x] **Add tests and docs**: If you're adding a new integration, please
include
1. a test for the integration, preferably unit tests that do not rely on
network access,
2. an example notebook showing its use. It lives in
`docs/docs/integrations` directory.


- [x] **Lint and test**: Run `make format`, `make lint` and `make test`
from the root of the package(s) you've modified. See contribution
guidelines for more: https://python.langchain.com/docs/contributing/

---------

Co-authored-by: Bagatur <baskaryan@gmail.com>
Co-authored-by: Bagatur <22008038+baskaryan@users.noreply.github.com>
bechbd pushed a commit to bechbd/langchain that referenced this issue Mar 29, 2024
…#18657)

**Description:** Circular dependencies when parsing references leading
to `RecursionError: maximum recursion depth exceeded` issue. This PR
address the issue by handling previously seen refs as in any typical DFS
to avoid infinite depths.

**Issue:** langchain-ai#12163

 **Twitter handle:** https://twitter.com/theBhulawat 


- [x] **Add tests and docs**: If you're adding a new integration, please
include
1. a test for the integration, preferably unit tests that do not rely on
network access,
2. an example notebook showing its use. It lives in
`docs/docs/integrations` directory.


- [x] **Lint and test**: Run `make format`, `make lint` and `make test`
from the root of the package(s) you've modified. See contribution
guidelines for more: https://python.langchain.com/docs/contributing/

---------

Co-authored-by: Bagatur <baskaryan@gmail.com>
Co-authored-by: Bagatur <22008038+baskaryan@users.noreply.github.com>
gkorland pushed a commit to FalkorDB/langchain that referenced this issue Mar 30, 2024
…#18657)

**Description:** Circular dependencies when parsing references leading
to `RecursionError: maximum recursion depth exceeded` issue. This PR
address the issue by handling previously seen refs as in any typical DFS
to avoid infinite depths.

**Issue:** langchain-ai#12163

 **Twitter handle:** https://twitter.com/theBhulawat 


- [x] **Add tests and docs**: If you're adding a new integration, please
include
1. a test for the integration, preferably unit tests that do not rely on
network access,
2. an example notebook showing its use. It lives in
`docs/docs/integrations` directory.


- [x] **Lint and test**: Run `make format`, `make lint` and `make test`
from the root of the package(s) you've modified. See contribution
guidelines for more: https://python.langchain.com/docs/contributing/

---------

Co-authored-by: Bagatur <baskaryan@gmail.com>
Co-authored-by: Bagatur <22008038+baskaryan@users.noreply.github.com>
hinthornw pushed a commit that referenced this issue Apr 26, 2024
**Description:** Circular dependencies when parsing references leading
to `RecursionError: maximum recursion depth exceeded` issue. This PR
address the issue by handling previously seen refs as in any typical DFS
to avoid infinite depths.

**Issue:** #12163

 **Twitter handle:** https://twitter.com/theBhulawat 


- [x] **Add tests and docs**: If you're adding a new integration, please
include
1. a test for the integration, preferably unit tests that do not rely on
network access,
2. an example notebook showing its use. It lives in
`docs/docs/integrations` directory.


- [x] **Lint and test**: Run `make format`, `make lint` and `make test`
from the root of the package(s) you've modified. See contribution
guidelines for more: https://python.langchain.com/docs/contributing/

---------

Co-authored-by: Bagatur <baskaryan@gmail.com>
Co-authored-by: Bagatur <22008038+baskaryan@users.noreply.github.com>
@dosubot dosubot bot added the stale Issue has not had recent activity or appears to be solved. Stale issues will be automatically closed label Jun 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Ɑ: agent Related to agents module 🤖:bug Related to a bug, vulnerability, unexpected error with an existing feature 🤖:question A specific question about the codebase, product, project, or how to use a feature stale Issue has not had recent activity or appears to be solved. Stale issues will be automatically closed
Projects
None yet
Development

No branches or pull requests

3 participants