Skip to content

Add asyncio support for LLM (OpenAI), Chain (LLMChain, LLMMathChain), and Agent #841

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

Merged
merged 21 commits into from
Feb 8, 2023

Conversation

agola11
Copy link
Collaborator

@agola11 agola11 commented Feb 2, 2023

Supporting asyncio in langchain primitives allows for users to run them concurrently and creates more seamless integration with asyncio-supported frameworks (FastAPI, etc.)

Summary of changes:

LLM

  • Add agenerate and _agenerate
  • Implement in OpenAI by leveraging client.Completions.acreate

Chain

  • Add arun, acall, _acall
  • Implement them in LLMChain and LLMMathChain for now

Agent

  • Refactor and leverage async chain and llm methods
  • Add ability for Tools to contain async coroutine
  • Implement async SerpaPI arun

Create demo notebook.

Open questions:

  • Should all the async stuff go in separate classes? I've seen both patterns (keeping the same class and having async and sync methods vs. having class separation)

@agola11 agola11 changed the base branch from master to ankush/retry-openai February 2, 2023 19:54
Base automatically changed from ankush/retry-openai to master February 3, 2023 03:56
@agola11 agola11 changed the title [WIP] async llm Add asyncio support for LLM (OpenAI), Chain (LLMChain, LLMMathChain), and Agent Feb 3, 2023
@agola11 agola11 marked this pull request as ready for review February 3, 2023 20:41
observation = (
await tool.coroutine(output.tool_input)
if tool.coroutine
else tool.func(output.tool_input)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be called with one of the asyncio methods for executing sync functions in another thread. If left like this it blocks the event loop for the duration of the tool call

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asyncio.get_event_loop().run_in_executor(None, tool.func, output.tool_input)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, great point

color = color_mapping[output.tool]
return_direct = tool.return_direct
except (KeyboardInterrupt, Exception) as e:
self.callback_manager.on_tool_error(e, verbose=self.verbose)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the callback manager have async methods? If I'm using an async agent I might also want to use an async callback handler

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rn, callbacks are all synchronous but on our roadmap to change soon


def apply(self, input_list: List[Dict[str, Any]]) -> List[Dict[str, str]]:
"""Utilize the LLM generate method for speed gains."""
response = self.generate(input_list)
return self.create_outputs(response)

async def aapply(self, input_list: List[Dict[str, Any]]) -> List[Dict[str, str]]:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An unfortunate name ha, but I guess it is what it is

url = "https://serpapi.com/search"
return url, params

async with aiohttp.ClientSession() as session:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not recommended, sessions should be reused if possible

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the docs for this feature should probably mention this part of the openai python docs https://github.com/openai/openai-python#async-api

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do -- thanks

@nfcampos
Copy link
Collaborator

nfcampos commented Feb 3, 2023

Making the async methods live in the same class makes reusing helper methods across both implementations easier

@agola11 agola11 requested a review from nfcampos February 5, 2023 19:50
Copy link
Collaborator

@nfcampos nfcampos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good

@agola11 agola11 merged commit bc7e56e into master Feb 8, 2023
@agola11 agola11 deleted the ankush/async-llm branch February 8, 2023 05:21
@tmbo
Copy link
Contributor

tmbo commented Feb 8, 2023

How is this compatible with the global OpenAICallbackHandler? e.g. let's say you have multiple of the contexts mentioned in https://langchain.readthedocs.io/en/latest/modules/llms/examples/token_usage_tracking.html

import time
import asyncio
from langchain.callbacks import get_openai_callback
from langchain.llms import OpenAI

async def async_generate(llm):
    with get_openai_callback() as cb:
        resp = await llm.agenerate(["Hello, how are you?"])
        print(resp.generations[0][0].text)
        # not consistent as async execution will share the global handler
        print(cb.total_tokens)

async def generate_concurrently():
    llm = OpenAI(temperature=0.9)
    tasks = [async_generate(llm) for _ in range(10)]
    await asyncio.gather(*tasks)

I am not sure if this implementation is complete without handling callbacks properly

@blob42 blob42 mentioned this pull request Feb 21, 2023
zachschillaci27 pushed a commit to zachschillaci27/langchain that referenced this pull request Mar 8, 2023
… and Agent (langchain-ai#841)

Supporting asyncio in langchain primitives allows for users to run them
concurrently and creates more seamless integration with
asyncio-supported frameworks (FastAPI, etc.)

Summary of changes:

**LLM**
* Add `agenerate` and `_agenerate`
* Implement in OpenAI by leveraging `client.Completions.acreate`

**Chain**
* Add `arun`, `acall`, `_acall`
* Implement them in `LLMChain` and `LLMMathChain` for now

**Agent**
* Refactor and leverage async chain and llm methods
* Add ability for `Tools` to contain async coroutine
* Implement async SerpaPI `arun`

Create demo notebook.

Open questions:
* Should all the async stuff go in separate classes? I've seen both
patterns (keeping the same class and having async and sync methods vs.
having class separation)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants