diff --git a/.gitignore b/.gitignore index 214a9de48..c69829e3a 100644 --- a/.gitignore +++ b/.gitignore @@ -175,3 +175,5 @@ docs/docs_skeleton/yarn.lock # Any new jupyter notebooks # not intended for the repo Untitled*.ipynb + +Chinook.db diff --git a/README.md b/README.md index 34505b1d7..e63722cfc 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,18 @@ ## Overview -[LangGraph](https://langchain-ai.github.io/langgraph/) is a library for building stateful, multi-actor applications with LLMs. -Inspired by [Pregel](https://research.google/pubs/pub37252/) and [Apache Beam](https://beam.apache.org/), LangGraph lets you coordinate and checkpoint multiple chains (or actors) across cyclic computational steps using regular python functions (or [JS](https://github.com/langchain-ai/langgraphjs)). The public interface draws inspiration from [NetworkX](https://networkx.org/documentation/latest/). +[LangGraph](https://langchain-ai.github.io/langgraph/) is a library for building stateful, multi-actor applications with LLMs, used to create agent and multi-agent workflows. Compared to other LLM frameworks, it offers these core benefits: cycles, controllability, and persistence. LangGraph allows you to define flows that involve cycles, essential for most agentic architectures, differentiating it from DAG-based solutions. As a very low-level framework, it provides fine-grained control over both the flow and state of your application, crucial for creating reliable agents. Additionally, LangGraph includes built-in persistence, enabling advanced human-in-the-loop and memory features. -The main use is for adding **cycles** and **persistence** to your LLM application. If you only need quick Directed Acyclic Graphs (DAGs), you can already accomplish this using [LangChain Expression Language](https://python.langchain.com/docs/expression_language/). +LangGraph is inspired by [Pregel](https://research.google/pubs/pub37252/) and [Apache Beam](https://beam.apache.org/). The public interface draws inspiration from [NetworkX](https://networkx.org/documentation/latest/). LangGraph is built by LangChain Inc, the creators of LangChain, but can be used without LangChain. + +### Key Features + +- **Cycles and Branching**: Implement loops and conditionals in your apps. +- **Persistence**: Automatically save state after each step in the graph. Pause and resume the graph execution at any point to support error recovery, human-in-the-loop workflows, time travel and more. +- **Human-in-the-Loop**: Interrupt graph execution to approve or edit next action planned by the agent. +- **Streaming Support**: Stream outputs as they are produced by each node (including token streaming). +- **Integration with LangChain**: LangGraph integrates seamlessly with [LangChain](https://github.com/langchain-ai/langchain/) and [LangSmith](https://docs.smith.langchain.com/) (but does not require them). -Cycles are important for agentic behaviors, where you call an LLM in a loop, asking it what action to take next. ## Installation @@ -23,196 +29,16 @@ Cycles are important for agentic behaviors, where you call an LLM in a loop, ask pip install -U langgraph ``` -## Quick start +## Example One of the central concepts of LangGraph is state. Each graph execution creates a state that is passed between nodes in the graph as they execute, and each node updates this internal state with its return value after it executes. The way that the graph updates its internal state is defined by either the type of graph chosen or a custom function. -State in LangGraph can be pretty general, but to keep things simpler to start, we'll show off an example where the graph's state is limited to a list of chat messages using the built-in `MessageGraph` class. This is convenient when using LangGraph with LangChain chat models because we can directly return chat model output. - -First, install the LangChain OpenAI integration package: - -```python -pip install langchain_openai -``` - -We also need to export some environment variables: +Let's take a look at a simple example of an agent that can search the web using [Tavily Search API](https://tavily.com/). ```shell -export OPENAI_API_KEY=sk-... +pip install langchain_openai langchain_community ``` -And now we're ready! The graph below contains a single node called `"oracle"` that executes a chat model, then returns the result: - -```python -from langchain_openai import ChatOpenAI -from langchain_core.messages import HumanMessage -from langgraph.graph import END, MessageGraph - -model = ChatOpenAI(temperature=0) - -graph = MessageGraph() - -graph.add_node("oracle", model) -graph.add_edge("oracle", END) - -graph.set_entry_point("oracle") - -runnable = graph.compile() -``` - -Let's run it! - -```python -runnable.invoke(HumanMessage("What is 1 + 1?")) -``` - -``` -[HumanMessage(content='What is 1 + 1?'), AIMessage(content='1 + 1 equals 2.')] -``` - -So what did we do here? Let's break it down step by step: - -1. First, we initialize our model and a `MessageGraph`. -2. Next, we add a single node to the graph, called `"oracle"`, which simply calls the model with the given input. -3. We add an edge from this `"oracle"` node to the special string `END` (`"__end__"`). This means that execution will end after the current node. -4. We set `"oracle"` as the entrypoint to the graph. -5. We compile the graph, translating it to low-level [pregel operations](https://research.google/pubs/pregel-a-system-for-large-scale-graph-processing/) ensuring that it can be run. - -Then, when we execute the graph: - -1. LangGraph adds the input message to the internal state, then passes the state to the entrypoint node, `"oracle"`. -2. The `"oracle"` node executes, invoking the chat model. -3. The chat model returns an `AIMessage`. LangGraph adds this to the state. -4. Execution progresses to the special `END` value and outputs the final state. - -And as a result, we get a list of two chat messages as output. - -### Interaction with LCEL - -As an aside for those already familiar with LangChain - `add_node` actually takes any function or [runnable](https://python.langchain.com/docs/expression_language/interface/) as input. In the above example, the model is used "as-is", but we could also have passed in a function: - -```python -def call_oracle(messages: list): - return model.invoke(messages) - -graph.add_node("oracle", call_oracle) -``` - -Just make sure you are mindful of the fact that the input to the [runnable](https://python.langchain.com/docs/expression_language/interface/) is the **entire current state**. So this will fail: - -```python -# This will not work with MessageGraph! -from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder - -prompt = ChatPromptTemplate.from_messages([ - ("system", "You are a helpful assistant named {name} who always speaks in pirate dialect"), - MessagesPlaceholder(variable_name="messages"), -]) - -chain = prompt | model - -# State is a list of messages, but our chain expects a dict input: -# -# { "name": some_string, "messages": [] } -# -# Therefore, the graph will throw an exception when it executes here. -graph.add_node("oracle", chain) -``` - -## Conditional edges - -Now, let's move onto something a little bit less trivial. LLMs struggle with math, so let's allow the LLM to conditionally call a `"multiply"` node using [tool calling](https://python.langchain.com/docs/modules/model_io/chat/function_calling/). - -We'll recreate our graph with an additional `"multiply"` that will take the result of the most recent message, if it is a tool call, and calculate the result. -We'll also [bind](https://api.python.langchain.com/en/latest/chat_models/langchain_openai.chat_models.base.ChatOpenAI.html#langchain_openai.chat_models.base.ChatOpenAI.bind_tools) the calculator's schema to the OpenAI model as a tool to allow the model to optionally use the tool necessary to respond to the current state: - -```python -from langchain_core.tools import tool -from langgraph.prebuilt import ToolNode - -@tool -def multiply(first_number: int, second_number: int): - """Multiplies two numbers together.""" - return first_number * second_number - -model = ChatOpenAI(temperature=0) -model_with_tools = model.bind_tools([multiply]) - -builder = MessageGraph() - -builder.add_node("oracle", model_with_tools) - -tool_node = ToolNode([multiply]) -builder.add_node("multiply", tool_node) - -builder.add_edge("multiply", END) - -builder.set_entry_point("oracle") -``` - -Now let's think - what do we want to have happened? - -- If the `"oracle"` node returns a message expecting a tool call, we want to execute the `"multiply"` node -- If not, we can just end execution - -We can achieve this using **conditional edges**, which call a function on the current state and routes execution to a node the function's output. - -Here's what that looks like: - -```python -from typing import Literal - -def router(state: List[BaseMessage]) -> Literal["multiply", "__end__"]: - tool_calls = state[-1].additional_kwargs.get("tool_calls", []) - if len(tool_calls): - return "multiply" - else: - return "__end__" - -builder.add_conditional_edges("oracle", router) -``` - -If the model output contains a tool call, we move to the `"multiply"` node. Otherwise, we end execution. - -Great! Now all that's left is to compile the graph and try it out. Math-related questions are routed to the calculator tool: - -```python -runnable = builder.compile() - -runnable.invoke(HumanMessage("What is 123 * 456?")) -``` - -``` - -[HumanMessage(content='What is 123 * 456?'), - AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_OPbdlm8Ih1mNOObGf3tMcNgb', 'function': {'arguments': '{"first_number":123,"second_number":456}', 'name': 'multiply'}, 'type': 'function'}]}), - ToolMessage(content='56088', tool_call_id='call_OPbdlm8Ih1mNOObGf3tMcNgb')] -``` - -While conversational responses are outputted directly: - -```python -runnable.invoke(HumanMessage("What is your name?")) -``` - -``` -[HumanMessage(content='What is your name?'), - AIMessage(content='My name is Assistant. How can I assist you today?')] -``` - -## Cycles - -Now, let's go over a more general cyclic example. We will recreate the `AgentExecutor` class from LangChain. The agent itself will use chat models and tool calling. -This agent will represent all its state as a list of messages. - -We will need to install some LangChain community packages, as well as [Tavily](https://app.tavily.com/sign-in) to use as an example tool. - -```shell -pip install -U langgraph langchain_openai tavily-python -``` - -We also need to export some additional environment variables for OpenAI and Tavily API access. - ```shell export OPENAI_API_KEY=sk-... export TAVILY_API_KEY=tvly-... @@ -225,114 +51,32 @@ export LANGCHAIN_TRACING_V2="true" export LANGCHAIN_API_KEY=ls__... ``` -### Set up the tools - -As above, we will first define the tools we want to use. -For this simple example, we will use a web search tool. -However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that. - ```python +from typing import Annotated, Literal, TypedDict + +from langchain_core.messages import HumanMessage from langchain_community.tools.tavily_search import TavilySearchResults - -tools = [TavilySearchResults(max_results=1)] -``` - -We can now wrap these tools in a simple LangGraph [ToolNode](https://langchain-ai.github.io/langgraph/reference/prebuilt/#toolnode). -This class receives the list of messages (containing [tool_calls](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.ai.AIMessage.html#langchain_core.messages.ai.AIMessage.tool_calls), calls the tool(s) the LLM has requested to run, and returns the output as new [ToolMessage](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.tool.ToolMessage.html#langchain_core.messages.tool.ToolMessage)(s). - - -```python +from langchain_openai import ChatOpenAI +from langgraph.checkpoint import MemorySaver +from langgraph.graph import END, StateGraph, MessagesState from langgraph.prebuilt import ToolNode + +# Define the tools for the agent to use +tools = [TavilySearchResults(max_results=1)] tool_node = ToolNode(tools) -``` -### Set up the model - -Now we need to load the chat model to use. - -```python -from langchain_openai import ChatOpenAI - -# We will set streaming=True so that we can stream tokens -# See the streaming section for more information on this. -model = ChatOpenAI(model="gpt-3.5-turbo", temperature=0, streaming=True) -``` - -After we've done this, we should make sure the model knows that it has these tools available to call. -We can do this by converting the LangChain tools into the format for OpenAI tool calling using the [bind_tools()](https://api.python.langchain.com/en/latest/chat_models/langchain_openai.chat_models.base.ChatOpenAI.html#langchain_openai.chat_models.base.ChatOpenAI.bind_tools) method. - -```python -model = model.bind_tools(tools) -``` - -### Define the agent state - -This time, we'll use the more general `StateGraph`. -This graph is parameterized by a state object that it passes around to each node. -Remember that each node then returns operations to update that state. -These operations can either SET specific attributes on the state (e.g. overwrite the existing values) or ADD to the existing attribute. -Whether to set or add is denoted by annotating the state object you construct the graph with. - -For this example, the state we will track will just be a list of messages. -We want each node to just add messages to that list. -Therefore, we will use a `TypedDict` with one key (`messages`) and annotate it so that we always **add** to the `messages` key when updating it using the is always added to with the second parameter (`operator.add`). -(Note: the state can be any [type](https://docs.python.org/3/library/stdtypes.html#type-objects), including [pydantic BaseModel's](https://docs.pydantic.dev/latest/api/base_model/)). - -```python -from typing import TypedDict, Annotated - -def add_messages(left: list, right: list): - """Add-don't-overwrite.""" - return left + right - -class AgentState(TypedDict): - # The `add_messages` function within the annotation defines - # *how* updates should be merged into the state. - messages: Annotated[list, add_messages] -``` - -You can think of the `MessageGraph` used in the initial example as a preconfigured version of this graph, where the state is directly an array of messages, -and the update step always appends the returned values of a node to the internal state. - -### Define the nodes - -We now need to define a few different nodes in our graph. -In `langgraph`, a node can be either a regular python function or a [runnable](https://python.langchain.com/docs/expression_language/). - -There are two main nodes we need for this: - -1. The agent: responsible for deciding what (if any) actions to take. -2. A function to invoke tools: if the agent decides to take an action, this node will then execute that action. We already defined this above. - -We will also need to define some edges. -Some of these edges may be conditional. -The reason they are conditional is that the destination depends on the contents of the graph's `State`. - -The path that is taken is not known until that node is run (the LLM decides). For our use case, we will need one of each type of edge: - -1. Conditional Edge: after the agent is called, we should either: - - a. Run tools if the agent said to take an action, OR - - b. Finish (respond to the user) if the agent did not ask to run tools - -2. Normal Edge: after the tools are invoked, the graph should always return to the agent to decide what to do next - -Let's define the nodes, as well as a function to define the conditional edge to take. - -```python -from typing import Literal +model = ChatOpenAI(temperature=0).bind_tools(tools) # Define the function that determines whether to continue or not -def should_continue(state: AgentState) -> Literal["tools", "__end__"]: +def should_continue(state: AgentState) -> Literal["tools", END]: messages = state['messages'] last_message = messages[-1] # If the LLM makes a tool call, then we route to the "tools" node if last_message.tool_calls: return "tools" # Otherwise, we stop (reply to the user) - return "__end__" + return END # Define the function that calls the model @@ -341,16 +85,10 @@ def call_model(state: AgentState): response = model.invoke(messages) # We return a list, because this will get added to the existing list return {"messages": [response]} -``` -### Define the graph -We can now put it all together and define the graph! - -```python -from langgraph.graph import StateGraph, END # Define a new graph -workflow = StateGraph(AgentState) +workflow = StateGraph(MessagesState) # Define the two nodes we will cycle between workflow.add_node("agent", call_model) @@ -373,130 +111,97 @@ workflow.add_conditional_edges( # This means that after `tools` is called, `agent` node is called next. workflow.add_edge("tools", 'agent') +# Initialize memory to persist state between graph runs +checkpointer = MemorySaver() + # Finally, we compile it! # This compiles it into a LangChain Runnable, -# meaning you can use it as you would any other runnable -app = workflow.compile() +# meaning you can use it as you would any other runnable. +# Note that we're (optionally) passing the memory when compiling the graph +app = workflow.compile(checkpointer=checkpointer) + +# Use the Runnable +final_state = app.invoke( + {"messages": [HumanMessage(content="what is the weather in sf")]}, + config={"configurable": {"thread_id": 42}} +) +final_state["messages"][-1].content ``` -### Use it! +``` +'The current weather in San Francisco is as follows:\n- Temperature: 60.1°F (15.6°C)\n- Condition: Partly cloudy\n- Wind: 5.6 mph (9.0 kph) from SSW\n- Humidity: 83%\n- Visibility: 9.0 miles (16.0 km)\n- UV Index: 4.0\n\nFor more details, you can visit [Weather API](https://www.weatherapi.com/).' +``` -We can now use it! -This now exposes the [same interface](https://python.langchain.com/docs/expression_language/) as all other LangChain runnables. -This [runnable](https://python.langchain.com/docs/expression_language/interface/) accepts a list of messages. +Now when we pass the same `"thread_id"`, the conversation context is retained via the saved state (i.e. stored list of messages) ```python -from langchain_core.messages import HumanMessage - -inputs = {"messages": [HumanMessage(content="what is the weather in sf")]} -app.invoke(inputs) -``` - -This may take a little bit - it's making a few calls behind the scenes. -In order to start seeing some intermediate results as they happen, we can use streaming - see below for more information on that. - -## Streaming - -LangGraph has support for several different types of streaming. - -### Streaming Node Output - -One of the benefits of using LangGraph is that it is easy to stream output as it's produced by each node. - -```python -inputs = {"messages": [HumanMessage(content="what is the weather in sf")]} -for output in app.stream(inputs, stream_mode="updates"): - # stream() yields dictionaries with output keyed by node name - for key, value in output.items(): - print(f"Output from node '{key}':") - print("---") - print(value) - print("\n---\n") +final_state = app.invoke( + {"messages": [HumanMessage(content="what about ny")]}, + config={"configurable": {"thread_id": 42}} +) +final_state["messages"][-1].content ``` ``` -Output from node 'agent': ---- -{'messages': [AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\n "query": "weather in San Francisco"\n}', 'name': 'tavily_search_results_json'}})]} - ---- - -Output from node 'tools': ---- -{'messages': [FunctionMessage(content="[{'url': 'https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States', 'content': 'January 2024 Weather History in San Francisco California, United States Daily Precipitation in January 2024 in San Francisco Observed Weather in January 2024 in San Francisco San Francisco Temperature History January 2024 Hourly Temperature in January 2024 in San Francisco Hours of Daylight and Twilight in January 2024 in San FranciscoThis report shows the past weather for San Francisco, providing a weather history for January 2024. It features all historical weather data series we have available, including the San Francisco temperature history for January 2024. You can drill down from year to month and even day level reports by clicking on the graphs.'}]", name='tavily_search_results_json')]} - ---- - -Output from node 'agent': ---- -{'messages': [AIMessage(content="I couldn't find the current weather in San Francisco. However, you can visit [WeatherSpark](https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States) to check the historical weather data for January 2024 in San Francisco.")]} - ---- - -Output from node '__end__': ---- -{'messages': [HumanMessage(content='what is the weather in sf'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\n "query": "weather in San Francisco"\n}', 'name': 'tavily_search_results_json'}}), FunctionMessage(content="[{'url': 'https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States', 'content': 'January 2024 Weather History in San Francisco California, United States Daily Precipitation in January 2024 in San Francisco Observed Weather in January 2024 in San Francisco San Francisco Temperature History January 2024 Hourly Temperature in January 2024 in San Francisco Hours of Daylight and Twilight in January 2024 in San FranciscoThis report shows the past weather for San Francisco, providing a weather history for January 2024. It features all historical weather data series we have available, including the San Francisco temperature history for January 2024. You can drill down from year to month and even day level reports by clicking on the graphs.'}]", name='tavily_search_results_json'), AIMessage(content="I couldn't find the current weather in San Francisco. However, you can visit [WeatherSpark](https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States) to check the historical weather data for January 2024 in San Francisco.")]} - ---- +'The current weather in New York is as follows:\n- Temperature: 20.3°C (68.5°F)\n- Condition: Overcast\n- Wind: 2.2 mph from the north\n- Humidity: 65%\n- Cloud Cover: 100%\n- UV Index: 5.0\n\nFor more details, you can visit [Weather API](https://www.weatherapi.com/).' ``` -### Streaming LLM Tokens +### Step-by-step Breakdown: -You can also access the LLM tokens as they are produced by each node. -In this case only the "agent" node produces LLM tokens. -In order for this to work properly, you must be using an LLM that supports streaming as well as have set it when constructing the LLM (e.g. `ChatOpenAI(model="gpt-3.5-turbo-1106", streaming=True)`) +1.
+ Initialize the model and tools. -```python -inputs = {"messages": [HumanMessage(content="what is the weather in sf")]} -async for output in app.astream_log(inputs, include_types=["llm"]): - # astream_log() yields the requested logs (here LLMs) in JSONPatch format - for op in output.ops: - if op["path"] == "/streamed_output/-": - # this is the output from .stream() - ... - elif op["path"].startswith("/logs/") and op["path"].endswith( - "/streamed_output/-" - ): - # because we chose to only include LLMs, these are LLM tokens - print(op["value"]) -``` + - we use `ChatOpenAI` as our LLM. **NOTE:** we need make sure the model knows that it has these tools available to call. We can do this by converting the LangChain tools into the format for OpenAI tool calling using the `.bind_tools()` method. + - we define the tools we want to use -- a web search tool in our case. It is really easy to create your own tools - see documentation here on how to do that [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools). +
+2.
+ Initialize graph with state. -``` -content='' additional_kwargs={'function_call': {'arguments': '', 'name': 'tavily_search_results_json'}} -content='' additional_kwargs={'function_call': {'arguments': '{\n', 'name': ''}}} -content='' additional_kwargs={'function_call': {'arguments': ' ', 'name': ''}} -content='' additional_kwargs={'function_call': {'arguments': ' "', 'name': ''}} -content='' additional_kwargs={'function_call': {'arguments': 'query', 'name': ''}} -... -``` + - we initialize graph (`StateGraph`) by passing state schema (in our case `MessagesState`) + - `MessagesState` is a prebuilt state schema that has one attribute -- a list of LangChain `Message` objects, as well as logic for merging the updates from each node into the state +
+3.
+ Define graph nodes. -## When to Use + There are two main nodes we need: + - The `agent` node: responsible for deciding what (if any) actions to take. + - The `tools` node that invokes tools: if the agent decides to take an action, this node will then execute that action. +
+4.
+ Define entry point and graph edges. -When should you use this versus [LangChain Expression Language](https://python.langchain.com/docs/expression_language/)? + First, we need to set the entry point for graph execution - `agent` node. -If you need cycles. + Then we define one normal and one conditional edge. Conditional edge means that the destination depends on the contents of the graph's state (`MessageState`). In our case, the destination is not known until the agent (LLM) decides. + + - Conditional edge: after the agent is called, we should either: + - a. Run tools if the agent said to take an action, OR + - b. Finish (respond to the user) if the agent did not ask to run tools + - Normal edge: after the tools are invoked, the graph should always return to the agent to decide what to do next +
+5.
+ Compile the graph. + + - When we compile the graph, we turn it into a LangChain [Runnable](https://python.langchain.com/v0.2/docs/concepts/#runnable-interface), which automatically enables calling `.invoke()`, `.stream()` and `.batch()` with your inputs + - We can also optionally pass checkpointer object for persisting state between graph runs, and enabling memory, human-in-the-loop workflows, time travel and more. In our case we use `MemorySaver` - a simple in-memory checkpointer +
+6.
+ Execute the graph. + + 1. LangGraph adds the input message to the internal state, then passes the state to the entrypoint node, `"agent"`. + 2. The `"agent"` node executes, invoking the chat model. + 3. The chat model returns an `AIMessage`. LangGraph adds this to the state. + 4. Graph cycles the following steps until there are no more `tool_calls` on `AIMessage`: + - If `AIMessage` has `tool_calls`, `"tools"` node executes + - The `"agent"` node executes again and returns `AIMessage` + 5. Execution progresses to the special `END` value and outputs the final state. + And as a result, we get a list of all our chat messages as output. +
-Langchain Expression Language allows you to easily define chains (DAGs) but does not have a good mechanism for adding in cycles. -`langgraph` adds that syntax. ## Documentation -We hope this gave you a taste of what you can build! Check out the rest of the docs to learn more. - -### Tutorials - -Learn to build with LangGraph through guided examples in the [LangGraph Tutorials](https://langchain-ai.github.io/langgraph/tutorials/). - -We recommend starting with the [Introduction to LangGraph](https://langchain-ai.github.io/langgraph/tutorials/introduction/) before trying out the more advanced guides. - -### How-to Guides - -The [LangGraph how-to guides](https://langchain-ai.github.io/langgraph/how-tos/) show how to accomplish specific things within LangGraph, from streaming, to adding memory & persistence, to common design patterns (branching, subgraphs, etc.), these are the place to go if you want to copy and run a specific code snippet. - -### Conceptual Guides - -The [Conceptual Guides](https://langchain-ai.github.io/langgraph/concepts/) provide in-depth explanations of the key concepts and principles behind LangGraph, such as nodes, edges, state and more. - -### Reference - -LangGraph's API has a few important classes and methods that are all covered in the [Reference Documents](https://langchain-ai.github.io/langgraph/reference/graphs/). Check these out to see the specific function arguments and simple examples of how to use the graph + checkpointing APIs or to see some of the higher-level prebuilt components. \ No newline at end of file +* [Tutorials](https://langchain-ai.github.io/langgraph/tutorials/): Learn to build with LangGraph through guided examples. +* [How-to Guides](https://langchain-ai.github.io/langgraph/how-tos/): Accomplish specific things within LangGraph, from streaming, to adding memory & persistence, to common design patterns (branching, subgraphs, etc.), these are the place to go if you want to copy and run a specific code snippet. +* [Conceptual Guides](https://langchain-ai.github.io/langgraph/concepts/): In-depth explanations of the key concepts and principles behind LangGraph, such as nodes, edges, state and more. +* [API Reference](https://langchain-ai.github.io/langgraph/reference/graphs/): Review important classes and methods, simple examples of how to use the graph and checkpointing APIs, higher-level prebuilt components and more. \ No newline at end of file diff --git a/docs/_scripts/copy_notebooks.py b/docs/_scripts/copy_notebooks.py index 4b9b42a34..0aae600bf 100644 --- a/docs/_scripts/copy_notebooks.py +++ b/docs/_scripts/copy_notebooks.py @@ -29,10 +29,13 @@ _MANUAL = { "configuration.ipynb", "map-reduce.ipynb", "extraction/retries.ipynb", + "create-react-agent.ipynb", ], "tutorials": [ "introduction.ipynb", "customer-support/customer-support.ipynb", + "tutorials/tnt-llm/tnt-llm.ipynb", + "tutorials/sql-agent.ipynb" ], } _MANUAL_INVERSE = {v: docs_dir / k for k, vs in _MANUAL.items() for v in vs} @@ -116,6 +119,10 @@ def copy_notebooks(): print(f"Overriding: {src_path} to {dst_path}") break + # Avoid double nesting. + dst_path = dst_path.replace("tutorials/tutorials", "tutorials").replace( + "how-tos/how-tos", "how-tos" + ) os.makedirs(os.path.dirname(dst_path), exist_ok=True) print(f"Copying: {src_path} to {dst_path}") shutil.copy(src_path, dst_path) diff --git a/docs/docs/how-tos/index.md b/docs/docs/how-tos/index.md index e74b9cc17..234f1fcd2 100644 --- a/docs/docs/how-tos/index.md +++ b/docs/docs/how-tos/index.md @@ -6,6 +6,7 @@ Welcome to the LangGraph how-to guides! These guides provide practical, step-by- The core guides show how to address common needs when building out AI workflows, with special focus placed on [ReAct](https://arxiv.org/abs/2210.03629)-style agents with [tool calling](https://python.langchain.com/docs/modules/model_io/chat/function_calling/). +- [ReAct agent](create-react-agent.ipynb): How to create a tool-calling agent that **Re**asons and **Act**s to accomplish tasks - [Persistence](persistence.ipynb): How to give your graph "memory" and resilience by saving and loading state - [Time travel](time-travel.ipynb): How to navigate and manipulate graph state history once it's persisted - [Async execution](async.ipynb): How to run nodes asynchronously for improved performance diff --git a/docs/docs/tutorials/index.md b/docs/docs/tutorials/index.md index 7682346e6..937575315 100644 --- a/docs/docs/tutorials/index.md +++ b/docs/docs/tutorials/index.md @@ -55,6 +55,10 @@ Learn from example implementations of graphs designed for specific scenarios and - [Agent-based](chatbot-simulation-evaluation/agent-simulation-evaluation.ipynb): Evaluating chatbots via simulated user interactions - [Within LangSmith](chatbot-simulation-evaluation/langsmith-agent-simulation-evaluation.ipynb): Evaluating chatbots in LangSmith over a dialog dataset +#### Text Mining + +- [TNT-LLM](tnt-llm/tnt-llm.ipynb): learn to build rich, interpretable taxonomies of user intentand using the classification system developed by Microsoft for their Bing Copilot application. + #### Competitive Programming - [Can Language Models Solve Olympiad Programming?](usaco/usaco.ipynb): Build an agent with few-shot "episodic memory" and human-in-the-loop collaboration to solve problems from the USA Computing Olympiad; adapted from the [paper of the same name](https://arxiv.org/abs/2404.10952v1) by Shi, Tang, Narasimhan, and Yao. diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index e8870acbd..7699f0694 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -2,7 +2,6 @@ site_name: LangGraph site_description: Build language agents as graphs site_url: https://langchain-ai.github.io/langgraph/ repo_url: https://github.com/langchain-ai/langgraph -edit_uri: edit/main/docs/docs/ theme: name: material custom_dir: overrides @@ -129,12 +128,16 @@ nav: - Chatbot Eval via Sim: - Agent-based: tutorials/chatbot-simulation-evaluation/agent-simulation-evaluation.ipynb - In LangSmith: tutorials/chatbot-simulation-evaluation/langsmith-agent-simulation-evaluation.ipynb + - Text Mining: + - TNT-LLM: tutorials/tnt-llm/tnt-llm.ipynb - Web Navigation: tutorials/web-navigation/web_voyager.ipynb - Competitive Programming: tutorials/usaco/usaco.ipynb + - SQL: tutorials/sql-agent.ipynb - "How-to Guides": - 'how-tos/index.md' - Core: + - "ReAct Agent": how-tos/create-react-agent.ipynb - "Persistence": how-tos/persistence.ipynb - "Time Travel": how-tos/time-travel.ipynb - "Async Execution": how-tos/async.ipynb diff --git a/examples/agent_executor/base.ipynb b/examples/agent_executor/base.ipynb index 561c7b4ce..31aad70a5 100644 --- a/examples/agent_executor/base.ipynb +++ b/examples/agent_executor/base.ipynb @@ -27,7 +27,7 @@ "outputs": [], "source": [ "%%capture --no-stderr\n", - "%pip install --quiet -U langchain langchain_openai langchainhub tavily-python" + "%pip install --quiet -U langgraph langchain langchain_openai langchainhub tavily-python" ] }, { @@ -45,8 +45,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")\n", "os.environ[\"TAVILY_API_KEY\"] = getpass.getpass(\"Tavily API Key:\")" @@ -78,7 +78,7 @@ "source": [ "## Create the LangChain agent\n", "\n", - "First, we will create the LangChain agent. For more information on LangChain agents, see [this documentation](https://python.langchain.com/docs/modules/agents/)" + "First, we will create the LangChain agent. For more information on LangChain agents, see [this documentation](https://python.langchain.com/v0.2/docs/concepts/#agents)" ] }, { @@ -90,8 +90,8 @@ "source": [ "from langchain import hub\n", "from langchain.agents import create_openai_functions_agent\n", - "from langchain_openai.chat_models import ChatOpenAI\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", + "from langchain_openai.chat_models import ChatOpenAI\n", "\n", "tools = [TavilySearchResults(max_results=1)]\n", "\n", @@ -127,10 +127,11 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import TypedDict, Annotated, List, Union\n", + "import operator\n", + "from typing import Annotated, TypedDict, Union\n", + "\n", "from langchain_core.agents import AgentAction, AgentFinish\n", "from langchain_core.messages import BaseMessage\n", - "import operator\n", "\n", "\n", "class AgentState(TypedDict):\n", @@ -155,7 +156,7 @@ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -182,6 +183,7 @@ "outputs": [], "source": [ "from langchain_core.agents import AgentFinish\n", + "\n", "from langgraph.prebuilt.tool_executor import ToolExecutor\n", "\n", "# This a helper class we have that is useful for running tools\n", diff --git a/examples/agent_executor/force-calling-a-tool-first.ipynb b/examples/agent_executor/force-calling-a-tool-first.ipynb index 93994e9f4..e2d464dcf 100644 --- a/examples/agent_executor/force-calling-a-tool-first.ipynb +++ b/examples/agent_executor/force-calling-a-tool-first.ipynb @@ -32,7 +32,7 @@ "outputs": [], "source": [ "%%capture --no-stderr\n", - "%pip install --quiet -U langchain langchain_openai tavily-python" + "%pip install --quiet -U langgraph langchain langchain_openai tavily-python" ] }, { @@ -50,8 +50,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")\n", "os.environ[\"TAVILY_API_KEY\"] = getpass.getpass(\"Tavily API Key:\")" @@ -83,7 +83,7 @@ "source": [ "## Create the LangChain agent\n", "\n", - "First, we will create the LangChain agent. For more information on LangChain agents, see [this documentation](https://python.langchain.com/docs/modules/agents/)" + "First, we will create the LangChain agent. For more information on LangChain agents, see [this documentation](https://python.langchain.com/v0.2/docs/concepts/#agents)" ] }, { @@ -95,8 +95,8 @@ "source": [ "from langchain import hub\n", "from langchain.agents import create_openai_functions_agent\n", - "from langchain_openai.chat_models import ChatOpenAI\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", + "from langchain_openai.chat_models import ChatOpenAI\n", "\n", "tools = [TavilySearchResults(max_results=1)]\n", "\n", @@ -132,10 +132,11 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import TypedDict, Annotated, List, Union\n", + "import operator\n", + "from typing import Annotated, TypedDict, Union\n", + "\n", "from langchain_core.agents import AgentAction, AgentFinish\n", "from langchain_core.messages import BaseMessage\n", - "import operator\n", "\n", "\n", "class AgentState(TypedDict):\n", @@ -160,7 +161,7 @@ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -187,6 +188,7 @@ "outputs": [], "source": [ "from langchain_core.agents import AgentFinish\n", + "\n", "from langgraph.prebuilt.tool_executor import ToolExecutor\n", "\n", "# This a helper class we have that is useful for running tools\n", diff --git a/examples/agent_executor/human-in-the-loop.ipynb b/examples/agent_executor/human-in-the-loop.ipynb index d89c5257a..f53930192 100644 --- a/examples/agent_executor/human-in-the-loop.ipynb +++ b/examples/agent_executor/human-in-the-loop.ipynb @@ -32,7 +32,7 @@ "outputs": [], "source": [ "%%capture --no-stderr\n", - "%pip install --quiet -U langchain langchain_openai tavily-python" + "%pip install --quiet -U langgraph langchain langchain_openai tavily-python" ] }, { @@ -50,8 +50,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")\n", "os.environ[\"TAVILY_API_KEY\"] = getpass.getpass(\"Tavily API Key:\")" @@ -83,7 +83,7 @@ "source": [ "## Create the LangChain agent\n", "\n", - "First, we will create the LangChain agent. For more information on LangChain agents, see [this documentation](https://python.langchain.com/docs/modules/agents/)" + "First, we will create the LangChain agent. For more information on LangChain agents, see [this documentation](https://python.langchain.com/v0.2/docs/concepts/#agents)" ] }, { @@ -95,8 +95,8 @@ "source": [ "from langchain import hub\n", "from langchain.agents import create_openai_functions_agent\n", - "from langchain_openai.chat_models import ChatOpenAI\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", + "from langchain_openai.chat_models import ChatOpenAI\n", "\n", "tools = [TavilySearchResults(max_results=1)]\n", "\n", @@ -132,10 +132,11 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import TypedDict, Annotated, List, Union\n", + "import operator\n", + "from typing import Annotated, TypedDict, Union\n", + "\n", "from langchain_core.agents import AgentAction, AgentFinish\n", "from langchain_core.messages import BaseMessage\n", - "import operator\n", "\n", "\n", "class AgentState(TypedDict):\n", @@ -160,7 +161,7 @@ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -187,6 +188,7 @@ "outputs": [], "source": [ "from langchain_core.agents import AgentFinish\n", + "\n", "from langgraph.prebuilt.tool_executor import ToolExecutor\n", "\n", "# This a helper class we have that is useful for running tools\n", @@ -317,7 +319,7 @@ ] }, { - "name": "stdin", + "name": "stdout", "output_type": "stream", "text": [ "[y/n] continue with: tool='tavily_search_results_json' tool_input={'query': 'weather in San Francisco'} log=\"\\nInvoking: `tavily_search_results_json` with `{'query': 'weather in San Francisco'}`\\n\\n\\n\" message_log=[AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"query\":\"weather in San Francisco\"}', 'name': 'tavily_search_results_json'}})]? y\n" diff --git a/examples/agent_executor/managing-agent-steps.ipynb b/examples/agent_executor/managing-agent-steps.ipynb index c4cac640b..24b10d9c1 100644 --- a/examples/agent_executor/managing-agent-steps.ipynb +++ b/examples/agent_executor/managing-agent-steps.ipynb @@ -32,7 +32,7 @@ "outputs": [], "source": [ "%%capture --no-stderr\n", - "%pip install --quiet -U langchain langchain_openai tavily-python" + "%pip install --quiet -U langgraph langchain langchain_openai tavily-python" ] }, { @@ -50,8 +50,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")\n", "os.environ[\"TAVILY_API_KEY\"] = getpass.getpass(\"Tavily API Key:\")" @@ -83,7 +83,7 @@ "source": [ "## Create the LangChain agent\n", "\n", - "First, we will create the LangChain agent. For more information on LangChain agents, see [this documentation](https://python.langchain.com/docs/modules/agents/)" + "First, we will create the LangChain agent. For more information on LangChain agents, see [this documentation](https://python.langchain.com/v0.2/docs/concepts/#agents)" ] }, { @@ -95,8 +95,8 @@ "source": [ "from langchain import hub\n", "from langchain.agents import create_openai_functions_agent\n", - "from langchain_openai.chat_models import ChatOpenAI\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", + "from langchain_openai.chat_models import ChatOpenAI\n", "\n", "tools = [TavilySearchResults(max_results=1)]\n", "\n", @@ -132,10 +132,11 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import TypedDict, Annotated, List, Union\n", + "import operator\n", + "from typing import Annotated, TypedDict, Union\n", + "\n", "from langchain_core.agents import AgentAction, AgentFinish\n", "from langchain_core.messages import BaseMessage\n", - "import operator\n", "\n", "\n", "class AgentState(TypedDict):\n", @@ -160,7 +161,7 @@ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -187,6 +188,7 @@ "outputs": [], "source": [ "from langchain_core.agents import AgentFinish\n", + "\n", "from langgraph.prebuilt.tool_executor import ToolExecutor\n", "\n", "# This a helper class we have that is useful for running tools\n", diff --git a/examples/async.ipynb b/examples/async.ipynb index 147ffdb68..848c48cf6 100644 --- a/examples/async.ipynb +++ b/examples/async.ipynb @@ -57,8 +57,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "\n", "def _set_env(var: str):\n", @@ -113,8 +113,10 @@ "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", "from typing import Annotated\n", + "\n", + "from typing_extensions import TypedDict\n", + "\n", "from langgraph.graph.message import add_messages\n", "\n", "# Add messages essentially does this with more\n", @@ -306,7 +308,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", + "from langgraph.graph import END, StateGraph\n", "\n", "# Define a new graph\n", "workflow = StateGraph(State)\n", @@ -584,7 +586,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.12.2" } }, "nbformat": 4, diff --git a/examples/branching.ipynb b/examples/branching.ipynb index da29a87e9..6b5a85256 100644 --- a/examples/branching.ipynb +++ b/examples/branching.ipynb @@ -40,10 +40,12 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph\n", - "from typing_extensions import TypedDict\n", - "from typing import Annotated\n", "import operator\n", + "from typing import Annotated, Any\n", + "\n", + "from typing_extensions import TypedDict\n", + "\n", + "from langgraph.graph import StateGraph\n", "\n", "\n", "class State(TypedDict):\n", @@ -51,9 +53,6 @@ " aggregate: Annotated[list, operator.add]\n", "\n", "\n", - "from typing import Any\n", - "\n", - "\n", "class ReturnNodeValue:\n", " def __init__(self, node_secret: str):\n", " self._value = node_secret\n", @@ -164,10 +163,12 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph\n", - "from typing_extensions import TypedDict\n", - "from typing import Annotated\n", "import operator\n", + "from typing import Annotated\n", + "\n", + "from typing_extensions import TypedDict\n", + "\n", + "from langgraph.graph import StateGraph\n", "\n", "\n", "class State(TypedDict):\n", @@ -264,11 +265,12 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import Sequence\n", - "from langgraph.graph import StateGraph, END, START\n", - "from typing_extensions import TypedDict\n", - "from typing import Annotated\n", "import operator\n", + "from typing import Annotated, Sequence\n", + "\n", + "from typing_extensions import TypedDict\n", + "\n", + "from langgraph.graph import END, START, StateGraph\n", "\n", "\n", "class State(TypedDict):\n", @@ -412,11 +414,12 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import Sequence\n", - "from langgraph.graph import StateGraph\n", - "from typing_extensions import TypedDict\n", - "from typing import Annotated\n", "import operator\n", + "from typing import Annotated, Sequence\n", + "\n", + "from typing_extensions import TypedDict\n", + "\n", + "from langgraph.graph import StateGraph\n", "\n", "\n", "def reduce_fanouts(left, right):\n", diff --git a/examples/chat_agent_executor_with_function_calling/anthropic.ipynb b/examples/chat_agent_executor_with_function_calling/anthropic.ipynb index f09940c3f..3c7f975b7 100644 --- a/examples/chat_agent_executor_with_function_calling/anthropic.ipynb +++ b/examples/chat_agent_executor_with_function_calling/anthropic.ipynb @@ -47,8 +47,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")\n", "os.environ[\"TAVILY_API_KEY\"] = getpass.getpass(\"Tavily API Key:\")" @@ -82,7 +82,7 @@ "\n", "We will first define the tools we want to use.\n", "For this simple example, we will use create a placeholder search engine.\n", - "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n", + "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that.\n", "\n", "**MODIFICATION**\n", "\n", @@ -165,8 +165,9 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import TypedDict, Annotated, Sequence\n", "import operator\n", + "from typing import Annotated, Sequence, TypedDict\n", + "\n", "from langchain_core.messages import BaseMessage\n", "\n", "\n", @@ -182,7 +183,7 @@ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -252,7 +253,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", + "from langgraph.graph import END, StateGraph\n", "\n", "# Define a new graph\n", "workflow = StateGraph(AgentState)\n", @@ -304,7 +305,7 @@ "## Use it!\n", "\n", "We can now use it!\n", - "This now exposes the [same interface](https://python.langchain.com/docs/expression_language/) as all other LangChain runnables." + "This now exposes the [same interface](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel) as all other LangChain runnables." ] }, { diff --git a/examples/chat_agent_executor_with_function_calling/base.ipynb b/examples/chat_agent_executor_with_function_calling/base.ipynb index f066030bd..f9384f68f 100644 --- a/examples/chat_agent_executor_with_function_calling/base.ipynb +++ b/examples/chat_agent_executor_with_function_calling/base.ipynb @@ -28,7 +28,7 @@ "outputs": [], "source": [ "%%capture --no-stderr\n", - "%pip install --quiet -U langchain langchain_openai tavily-python" + "%pip install --quiet -U langgraph langchain langchain_openai tavily-python" ] }, { @@ -46,8 +46,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")\n", "os.environ[\"TAVILY_API_KEY\"] = getpass.getpass(\"Tavily API Key:\")" @@ -81,7 +81,7 @@ "\n", "We will first define the tools we want to use.\n", "For this simple example, we will use create a placeholder search engine.\n", - "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n" + "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that.\n" ] }, { @@ -193,8 +193,9 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import TypedDict, Annotated, Sequence\n", "import operator\n", + "from typing import Annotated, Sequence, TypedDict\n", + "\n", "from langchain_core.messages import BaseMessage\n", "\n", "\n", @@ -210,7 +211,7 @@ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -236,9 +237,10 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.prebuilt import ToolInvocation\n", "from langchain_core.messages import ToolMessage\n", "\n", + "from langgraph.prebuilt import ToolInvocation\n", + "\n", "\n", "# Define the function that determines whether to continue or not\n", "def should_continue(state):\n", @@ -299,7 +301,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", + "from langgraph.graph import END, StateGraph\n", "\n", "# Define a new graph\n", "workflow = StateGraph(AgentState)\n", @@ -365,7 +367,7 @@ "\n", "try:\n", " display(Image(app.get_graph(xray=True).draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -378,7 +380,7 @@ "## Use it!\n", "\n", "We can now use it!\n", - "This now exposes the [same interface](https://python.langchain.com/docs/expression_language/) as all other LangChain runnables." + "This now exposes the [same interface](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel) as all other LangChain runnables." ] }, { @@ -643,7 +645,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.12.2" } }, "nbformat": 4, diff --git a/examples/chat_agent_executor_with_function_calling/dynamically-returning-directly.ipynb b/examples/chat_agent_executor_with_function_calling/dynamically-returning-directly.ipynb index 172aecd75..8ac2e500b 100644 --- a/examples/chat_agent_executor_with_function_calling/dynamically-returning-directly.ipynb +++ b/examples/chat_agent_executor_with_function_calling/dynamically-returning-directly.ipynb @@ -17,7 +17,7 @@ "
\n", "

Note

\n", "

\n", - " In this how-to, we will create our agent from scratch to be transparent (but verbose). You can accomplish similar functionality using the create_react_agent(model, tools=tool, interrupt_before=[\"agent\" | \"tools\"], interrupt_after=[\"agent\" | \"tools\"], checkpointer=checkpointer) (API doc) constructor. This may be more appropriate if you are used to LangChain’s AgentExecutor class.\n", + " In this how-to, we will create our agent from scratch to be transparent (but verbose). You can accomplish similar functionality using the create_react_agent(model, tools=tool, interrupt_before=[\"agent\" | \"tools\"], interrupt_after=[\"agent\" | \"tools\"], checkpointer=checkpointer) (API doc) constructor. This may be more appropriate if you are used to LangChain’s AgentExecutor class.\n", "

\n", "
" ] @@ -40,7 +40,7 @@ "outputs": [], "source": [ "%%capture --no-stderr\n", - "%pip install --quiet -U langchain langchain_openai tavily-python" + "%pip install --quiet -U langgraph langchain langchain_openai tavily-python" ] }, { @@ -58,8 +58,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")\n", "os.environ[\"TAVILY_API_KEY\"] = getpass.getpass(\"Tavily API Key:\")" @@ -93,7 +93,7 @@ "\n", "We will first define the tools we want to use.\n", "For this simple example, we will use a built-in search tool via Tavily.\n", - "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n", + "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that.\n", "\n", "**MODIFICATION**\n", "\n", @@ -230,8 +230,9 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import TypedDict, Annotated, Sequence\n", "import operator\n", + "from typing import Annotated, Sequence, TypedDict\n", + "\n", "from langchain_core.messages import BaseMessage\n", "\n", "\n", @@ -247,7 +248,7 @@ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -273,8 +274,9 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.prebuilt import ToolInvocation\n", - "from langchain_core.messages import ToolMessage" + "from langchain_core.messages import ToolMessage\n", + "\n", + "from langgraph.prebuilt import ToolInvocation" ] }, { @@ -390,7 +392,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", + "from langgraph.graph import END, StateGraph\n", "\n", "# Define a new graph\n", "workflow = StateGraph(AgentState)\n", @@ -460,7 +462,7 @@ "\n", "try:\n", " display(Image(app.get_graph(xray=True).draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -473,7 +475,7 @@ "## Use it!\n", "\n", "We can now use it!\n", - "This now exposes the [same interface](https://python.langchain.com/docs/expression_language/) as all other LangChain runnables." + "This now exposes the [same interface](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel) as all other LangChain runnables." ] }, { @@ -589,7 +591,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.12.2" } }, "nbformat": 4, diff --git a/examples/chat_agent_executor_with_function_calling/force-calling-a-tool-first.ipynb b/examples/chat_agent_executor_with_function_calling/force-calling-a-tool-first.ipynb index aa0490453..b2ae9ba1a 100644 --- a/examples/chat_agent_executor_with_function_calling/force-calling-a-tool-first.ipynb +++ b/examples/chat_agent_executor_with_function_calling/force-calling-a-tool-first.ipynb @@ -50,8 +50,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "\n", "def _set_env(var: str):\n", @@ -90,7 +90,7 @@ "\n", "We will first define the tools we want to use.\n", "For this simple example, we will use create a placeholder search engine.\n", - "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n" + "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that.\n" ] }, { @@ -208,8 +208,9 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import TypedDict, Annotated, Sequence\n", "import operator\n", + "from typing import Annotated, Sequence, TypedDict\n", + "\n", "from langchain_core.messages import BaseMessage\n", "\n", "\n", @@ -225,7 +226,7 @@ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -251,9 +252,10 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.prebuilt import ToolInvocation\n", "from langchain_core.messages import ToolMessage\n", "\n", + "from langgraph.prebuilt import ToolInvocation\n", + "\n", "\n", "# Define the function that determines whether to continue or not\n", "def should_continue(state: AgentState):\n", @@ -375,7 +377,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", + "from langgraph.graph import END, StateGraph\n", "\n", "# Define a new graph\n", "workflow = StateGraph(AgentState)\n", @@ -456,7 +458,7 @@ "## Use it!\n", "\n", "We can now use it!\n", - "This now exposes the [same interface](https://python.langchain.com/docs/expression_language/) as all other LangChain runnables." + "This now exposes the [same interface](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel) as all other LangChain runnables." ] }, { @@ -705,7 +707,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.12.2" } }, "nbformat": 4, diff --git a/examples/chat_agent_executor_with_function_calling/high-level-tools.ipynb b/examples/chat_agent_executor_with_function_calling/high-level-tools.ipynb index d97a49c4d..bf1f9c029 100644 --- a/examples/chat_agent_executor_with_function_calling/high-level-tools.ipynb +++ b/examples/chat_agent_executor_with_function_calling/high-level-tools.ipynb @@ -30,10 +30,11 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain_openai import ChatOpenAI\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", - "from langgraph.prebuilt import create_react_agent\n", - "from langchain_core.messages import HumanMessage" + "from langchain_core.messages import HumanMessage\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "from langgraph.prebuilt import create_react_agent" ] }, { diff --git a/examples/chat_agent_executor_with_function_calling/human-in-the-loop.ipynb b/examples/chat_agent_executor_with_function_calling/human-in-the-loop.ipynb index 72c91c494..9ab467887 100644 --- a/examples/chat_agent_executor_with_function_calling/human-in-the-loop.ipynb +++ b/examples/chat_agent_executor_with_function_calling/human-in-the-loop.ipynb @@ -59,8 +59,8 @@ } ], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")\n", "os.environ[\"TAVILY_API_KEY\"] = getpass.getpass(\"Tavily API Key:\")" @@ -102,7 +102,7 @@ "\n", "We will first define the tools we want to use.\n", "For this simple example, we will use a built-in search tool via Tavily.\n", - "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n" + "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that.\n" ] }, { @@ -214,8 +214,9 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import TypedDict, Annotated, Sequence\n", "import operator\n", + "from typing import Annotated, Sequence, TypedDict\n", + "\n", "from langchain_core.messages import BaseMessage\n", "\n", "\n", @@ -231,7 +232,7 @@ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -257,9 +258,10 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.prebuilt import ToolInvocation\n", "from langchain_core.messages import ToolMessage\n", "\n", + "from langgraph.prebuilt import ToolInvocation\n", + "\n", "\n", "# Define the function that determines whether to continue or not\n", "def should_continue(state):\n", @@ -344,8 +346,8 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", "from langgraph.checkpoint.memory import MemorySaver\n", + "from langgraph.graph import END, StateGraph\n", "\n", "# Define a new graph\n", "workflow = StateGraph(AgentState)\n", @@ -411,7 +413,7 @@ "\n", "try:\n", " display(Image(app.get_graph(xray=True).draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -424,7 +426,7 @@ "## Use it!\n", "\n", "We can now use it!\n", - "This now exposes the [same interface](https://python.langchain.com/docs/expression_language/) as all other LangChain runnables." + "This now exposes the [same interface](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel) as all other LangChain runnables." ] }, { diff --git a/examples/chat_agent_executor_with_function_calling/managing-agent-steps.ipynb b/examples/chat_agent_executor_with_function_calling/managing-agent-steps.ipynb index d698e4225..35f494b95 100644 --- a/examples/chat_agent_executor_with_function_calling/managing-agent-steps.ipynb +++ b/examples/chat_agent_executor_with_function_calling/managing-agent-steps.ipynb @@ -32,7 +32,7 @@ "outputs": [], "source": [ "%%capture --no-stderr\n", - "%pip install --quiet -U langchain langchain_openai tavily-python" + "%pip install --quiet -U langgraph langchain langchain_openai tavily-python" ] }, { @@ -50,8 +50,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")\n", "os.environ[\"TAVILY_API_KEY\"] = getpass.getpass(\"Tavily API Key:\")" @@ -85,7 +85,7 @@ "\n", "We will first define the tools we want to use.\n", "For this simple example, we will use a built-in search tool via Tavily.\n", - "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n" + "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that.\n" ] }, { @@ -197,8 +197,9 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import TypedDict, Annotated, Sequence\n", "import operator\n", + "from typing import Annotated, Sequence, TypedDict\n", + "\n", "from langchain_core.messages import BaseMessage\n", "\n", "\n", @@ -214,7 +215,7 @@ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -240,9 +241,10 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.prebuilt import ToolInvocation\n", "from langchain_core.messages import ToolMessage\n", "\n", + "from langgraph.prebuilt import ToolInvocation\n", + "\n", "\n", "# Define the function that determines whether to continue or not\n", "def should_continue(state):\n", @@ -340,7 +342,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", + "from langgraph.graph import END, StateGraph\n", "\n", "# Define a new graph\n", "workflow = StateGraph(AgentState)\n", @@ -406,7 +408,7 @@ "\n", "try:\n", " display(Image(app.get_graph(xray=True).draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -419,7 +421,7 @@ "## Use it!\n", "\n", "We can now use it!\n", - "This now exposes the [same interface](https://python.langchain.com/docs/expression_language/) as all other LangChain runnables." + "This now exposes the [same interface](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel) as all other LangChain runnables." ] }, { @@ -491,7 +493,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.12.2" } }, "nbformat": 4, diff --git a/examples/chat_agent_executor_with_function_calling/prebuilt-tool-node.ipynb b/examples/chat_agent_executor_with_function_calling/prebuilt-tool-node.ipynb index 2d629af4b..da0e8b3c9 100644 --- a/examples/chat_agent_executor_with_function_calling/prebuilt-tool-node.ipynb +++ b/examples/chat_agent_executor_with_function_calling/prebuilt-tool-node.ipynb @@ -29,7 +29,7 @@ "outputs": [], "source": [ "%%capture --no-stderr\n", - "%pip install --quiet -U langchain langchain_openai tavily-python" + "%pip install --quiet -U langgraph langchain langchain_openai tavily-python" ] }, { @@ -47,8 +47,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")\n", "os.environ[\"TAVILY_API_KEY\"] = getpass.getpass(\"Tavily API Key:\")" @@ -82,7 +82,7 @@ "\n", "We will first define the tools we want to use.\n", "For this simple example, we will use a built-in search tool via Tavily.\n", - "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n", + "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that.\n", "\n", "**MODIFICATION**\n", "\n", @@ -174,8 +174,9 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import TypedDict, Annotated, Sequence\n", "import operator\n", + "from typing import Annotated, Sequence, TypedDict\n", + "\n", "from langchain_core.messages import BaseMessage\n", "\n", "\n", @@ -191,7 +192,7 @@ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -261,7 +262,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", + "from langgraph.graph import END, StateGraph\n", "\n", "# Define a new graph\n", "workflow = StateGraph(AgentState)\n", @@ -313,7 +314,7 @@ "## Use it!\n", "\n", "We can now use it!\n", - "This now exposes the [same interface](https://python.langchain.com/docs/expression_language/) as all other LangChain runnables." + "This now exposes the [same interface](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel) as all other LangChain runnables." ] }, { diff --git a/examples/chat_agent_executor_with_function_calling/respond-in-format.ipynb b/examples/chat_agent_executor_with_function_calling/respond-in-format.ipynb index b77000f66..3e1f82577 100644 --- a/examples/chat_agent_executor_with_function_calling/respond-in-format.ipynb +++ b/examples/chat_agent_executor_with_function_calling/respond-in-format.ipynb @@ -39,8 +39,10 @@ "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", "from typing import Annotated\n", + "\n", + "from typing_extensions import TypedDict\n", + "\n", "from langgraph.graph.message import add_messages\n", "\n", "# Add messages essentially does this with more\n", @@ -62,7 +64,7 @@ "\n", "We will first define the tools we want to use.\n", "For this simple example, we will use create a placeholder search engine.\n", - "It is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n" + "It is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that.\n" ] }, { @@ -172,7 +174,7 @@ "outputs": [], "source": [ "%%capture --no-stderr\n", - "%pip install --quiet -U langchain langchain_openai tavily-python" + "%pip install --quiet -U langgraph langchain langchain_openai tavily-python" ] }, { @@ -190,8 +192,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")\n", "os.environ[\"TAVILY_API_KEY\"] = getpass.getpass(\"Tavily API Key:\")" @@ -225,7 +227,7 @@ "\n", "We will first define the tools we want to use.\n", "For this simple example, we will use a built-in search tool via Tavily.\n", - "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n" + "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that.\n" ] }, { @@ -352,8 +354,9 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import TypedDict, Annotated, Sequence\n", "import operator\n", + "from typing import Annotated, Sequence, TypedDict\n", + "\n", "from langchain_core.messages import BaseMessage\n", "\n", "\n", @@ -369,7 +372,7 @@ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -399,10 +402,12 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.prebuilt import ToolInvocation\n", - "from langchain_core.messages import ToolMessage\n", "from typing import Literal\n", "\n", + "from langchain_core.messages import ToolMessage\n", + "\n", + "from langgraph.prebuilt import ToolInvocation\n", + "\n", "\n", "# Define the function that determines whether to continue or not\n", "def should_continue(state) -> Literal[\"continue\", \"end\"]:\n", @@ -478,7 +483,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", + "from langgraph.graph import END, StateGraph\n", "\n", "# Define a new graph\n", "workflow = StateGraph(AgentState)\n", @@ -544,7 +549,7 @@ "\n", "try:\n", " display(Image(app.get_graph(xray=True).draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -557,7 +562,7 @@ "## Use it!\n", "\n", "We can now use it!\n", - "This now exposes the [same interface](https://python.langchain.com/docs/expression_language/) as all other LangChain runnables." + "This now exposes the [same interface](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel) as all other LangChain runnables." ] }, { @@ -629,7 +634,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.12.2" } }, "nbformat": 4, diff --git a/examples/chatbot-simulation-evaluation/agent-simulation-evaluation.ipynb b/examples/chatbot-simulation-evaluation/agent-simulation-evaluation.ipynb index cbb1d9433..044572c7b 100644 --- a/examples/chatbot-simulation-evaluation/agent-simulation-evaluation.ipynb +++ b/examples/chatbot-simulation-evaluation/agent-simulation-evaluation.ipynb @@ -40,7 +40,6 @@ "source": [ "import getpass\n", "import os\n", - "import uuid\n", "\n", "\n", "def _set_if_undefined(var: str):\n", @@ -138,7 +137,6 @@ "outputs": [], "source": [ "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", - "from langchain_core.runnables import chain\n", "from langchain_openai import ChatOpenAI\n", "\n", "system_prompt_template = \"\"\"You are a customer of an airline company. \\\n", @@ -185,7 +183,6 @@ "source": [ "from langchain_core.messages import HumanMessage\n", "\n", - "\n", "messages = [HumanMessage(content=\"Hi! How can I help you?\")]\n", "simulated_user.invoke({\"messages\": messages})" ] @@ -227,8 +224,8 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain_core.messages import AIMessage\n", "from langchain_community.adapters.openai import convert_message_to_dict\n", + "from langchain_core.messages import AIMessage\n", "\n", "\n", "def chat_bot_node(messages):\n", @@ -324,7 +321,6 @@ "source": [ "from langgraph.graph import END, MessageGraph\n", "\n", - "\n", "graph_builder = MessageGraph()\n", "graph_builder.add_node(\"user\", simulated_user_node)\n", "graph_builder.add_node(\"chat_bot\", chat_bot_node)\n", diff --git a/examples/chatbots/information-gather-prompting.ipynb b/examples/chatbots/information-gather-prompting.ipynb index 3986886ef..7255b72f1 100644 --- a/examples/chatbots/information-gather-prompting.ipynb +++ b/examples/chatbots/information-gather-prompting.ipynb @@ -33,10 +33,11 @@ "metadata": {}, "outputs": [], "source": [ + "from typing import List\n", + "\n", "from langchain_core.messages import SystemMessage\n", - "from langchain_openai import ChatOpenAI\n", "from langchain_core.pydantic_v1 import BaseModel\n", - "from typing import List" + "from langchain_openai import ChatOpenAI" ] }, { @@ -97,7 +98,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain_core.messages import HumanMessage, AIMessage, ToolMessage\n", + "from langchain_core.messages import AIMessage, HumanMessage, ToolMessage\n", "\n", "# New system prompt\n", "prompt_system = \"\"\"Based on the following requirements, write a good prompt template:\n", @@ -145,6 +146,7 @@ "outputs": [], "source": [ "from typing import Literal\n", + "\n", "from langgraph.graph import END\n", "\n", "\n", @@ -174,8 +176,8 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import MessageGraph, START\n", "from langgraph.checkpoint.sqlite import SqliteSaver\n", + "from langgraph.graph import START, MessageGraph\n", "\n", "memory = SqliteSaver.from_conn_string(\":memory:\")\n", "workflow = MessageGraph()\n", @@ -215,7 +217,7 @@ } ], "source": [ - "from IPython.display import display, Image\n", + "from IPython.display import Image, display\n", "\n", "display(Image(graph.get_graph().draw_mermaid_png()))" ] @@ -296,7 +298,6 @@ "source": [ "import uuid\n", "\n", - "\n", "config = {\"configurable\": {\"thread_id\": str(uuid.uuid4())}}\n", "while True:\n", " user = input(\"User (q/Q to quit): \")\n", diff --git a/examples/code_assistant/langgraph_code_assistant.ipynb b/examples/code_assistant/langgraph_code_assistant.ipynb index 909c0e420..c435cd3ce 100644 --- a/examples/code_assistant/langgraph_code_assistant.ipynb +++ b/examples/code_assistant/langgraph_code_assistant.ipynb @@ -18,7 +18,7 @@ "\n", "[AlphaCodium](https://github.com/Codium-ai/AlphaCodium) iteravely tests and improves an answer on public and AI-generated tests for a particular question. \n", "\n", - "We will implement some of these ideas from scratch using [LangGraph](https://python.langchain.com/docs/langgraph):\n", + "We will implement some of these ideas from scratch using [LangGraph](https://langchain-ai.github.io/langgraph/):\n", "\n", "1. We start with a set of documentation specified by a user\n", "2. We use a long context LLM to ingest it and perform RAG to answer a question based upon it\n", @@ -45,7 +45,7 @@ "source": [ "## Docs\n", "\n", - "Load [LangChain Expression Language](https://python.langchain.com/docs/expression_language/) (LCEL) docs as an example." + "Load [LangChain Expression Language](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel) (LCEL) docs as an example." ] }, { @@ -59,7 +59,7 @@ "from langchain_community.document_loaders.recursive_url_loader import RecursiveUrlLoader\n", "\n", "# LCEL docs\n", - "url = \"https://python.langchain.com/docs/expression_language/\"\n", + "url = \"https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel\"\n", "loader = RecursiveUrlLoader(\n", " url=url, max_depth=20, extractor=lambda x: Soup(x, \"html.parser\").text\n", ")\n", @@ -94,9 +94,9 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain_openai import ChatOpenAI\n", "from langchain_core.prompts import ChatPromptTemplate\n", "from langchain_core.pydantic_v1 import BaseModel, Field\n", + "from langchain_openai import ChatOpenAI\n", "\n", "### OpenAI\n", "\n", @@ -125,6 +125,7 @@ " code: str = Field(description=\"Code block not including import statements\")\n", " description = \"Schema for code solutions to questions about LCEL.\"\n", "\n", + "\n", "expt_llm = \"gpt-4-0125-preview\"\n", "llm = ChatOpenAI(temperature=0, model=expt_llm)\n", "code_gen_chain = code_gen_prompt | llm.with_structured_output(code)\n", @@ -181,6 +182,7 @@ "\n", "structured_llm_claude = llm.with_structured_output(code, include_raw=True)\n", "\n", + "\n", "# Optional: Check for errors in case tool use is flaky\n", "def check_claude_output(tool_output):\n", " \"\"\"Check for parse error or failure to call the tool\"\"\"\n", @@ -189,7 +191,7 @@ " if tool_output[\"parsing_error\"]:\n", " # Report back output and parsing errors\n", " print(\"Parsing error!\")\n", - " raw_output = str(code_output[\"raw\"].content)\n", + " raw_output = str(tool_output[\"raw\"].content)\n", " error = tool_output[\"parsing_error\"]\n", " raise ValueError(\n", " f\"Error parsing your output! Be sure to invoke the tool. Output: {raw_output}. \\n Parse error: {error}\"\n", @@ -199,15 +201,17 @@ " elif not tool_output[\"parsed\"]:\n", " print(\"Failed to invoke tool!\")\n", " raise ValueError(\n", - " f\"You did not use the provided tool! Be sure to invoke the tool to structure the output.\"\n", + " \"You did not use the provided tool! Be sure to invoke the tool to structure the output.\"\n", " )\n", " return tool_output\n", "\n", + "\n", "# Chain with output check\n", "code_chain_claude_raw = (\n", " code_gen_prompt_claude | structured_llm_claude | check_claude_output\n", ")\n", "\n", + "\n", "def insert_errors(inputs):\n", " \"\"\"Insert errors for tool parsing in the messages\"\"\"\n", "\n", @@ -240,6 +244,7 @@ "\n", " return solution[\"parsed\"]\n", "\n", + "\n", "# Optional: With re-try to correct for failure to invoke tool\n", "code_gen_chain = code_gen_chain_re_try | parse_output\n", "\n", @@ -281,7 +286,7 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import Dict, TypedDict, List\n", + "from typing import List, TypedDict\n", "\n", "\n", "class GraphState(TypedDict):\n", @@ -318,10 +323,7 @@ "metadata": {}, "outputs": [], "source": [ - "from operator import itemgetter\n", "from langchain_core.pydantic_v1 import BaseModel, Field\n", - "from langchain_core.runnables import RunnablePassthrough\n", - "from langchain_core.prompts import PromptTemplate\n", "\n", "### Parameter\n", "\n", @@ -396,7 +398,6 @@ " iterations = state[\"iterations\"]\n", "\n", " # Get solution components\n", - " prefix = code_solution.prefix\n", " imports = code_solution.imports\n", " code = code_solution.code\n", "\n", @@ -457,14 +458,6 @@ " code_solution = state[\"generation\"]\n", "\n", " # Prompt reflection\n", - " reflection_message = [\n", - " (\n", - " \"user\",\n", - " \"\"\"You tried to solve this problem and failed a unit test. Reflect on this failure\n", - " given the provided documentation. Write a few key suggestions based on the \n", - " documentation to avoid making this mistake again.\"\"\",\n", - " )\n", - " ]\n", "\n", " # Add reflection\n", " reflections = code_gen_chain.invoke(\n", @@ -613,7 +606,7 @@ " try:\n", " exec(imports)\n", " return {\"key\": \"import_check\", \"score\": 1}\n", - " except:\n", + " except Exception:\n", " return {\"key\": \"import_check\", \"score\": 0}\n", "\n", "\n", @@ -623,7 +616,7 @@ " try:\n", " exec(imports + \"\\n\" + code)\n", " return {\"key\": \"code_execution_check\", \"score\": 1}\n", - " except:\n", + " except Exception:\n", " return {\"key\": \"code_execution_check\", \"score\": 0}" ] }, @@ -754,7 +747,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.8" + "version": "3.12.2" } }, "nbformat": 4, diff --git a/examples/code_assistant/langgraph_code_assistant_mistral.ipynb b/examples/code_assistant/langgraph_code_assistant_mistral.ipynb index a2161f76b..0533444b9 100644 --- a/examples/code_assistant/langgraph_code_assistant_mistral.ipynb +++ b/examples/code_assistant/langgraph_code_assistant_mistral.ipynb @@ -18,7 +18,7 @@ "\n", "[AlphaCodium](https://github.com/Codium-ai/AlphaCodium) iteravely tests and improves an answer on public and AI-generated tests for a particular question. \n", "\n", - "We will implement some of these ideas from scratch using [LangGraph](https://python.langchain.com/docs/langgraph):\n", + "We will implement some of these ideas from scratch using [LangGraph](https://langchain-ai.github.io/langgraph/):\n", "\n", "1. We show how to route user questions to different types of documentation\n", "2. We we will show how to perform inline unit tests to confirm imports and code execution work\n", @@ -55,8 +55,9 @@ "outputs": [], "source": [ "import os\n", - "os.environ['TOKENIZERS_PARALLELISM'] = 'true'\n", - "mistral_api_key = os.getenv(\"MISTRAL_API_KEY\") # Ensure this is set" + "\n", + "os.environ[\"TOKENIZERS_PARALLELISM\"] = \"true\"\n", + "mistral_api_key = os.getenv(\"MISTRAL_API_KEY\") # Ensure this is set" ] }, { @@ -76,19 +77,9 @@ "metadata": {}, "outputs": [], "source": [ - "os.environ['LANGCHAIN_TRACING_V2'] = 'true'\n", - "os.environ['LANGCHAIN_ENDPOINT'] = 'https://api.smith.langchain.com'\n", - "os.environ['LANGCHAIN_API_KEY'] = " - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "949efd30-44c7-4a4c-a05f-eca4e2769a61", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "os.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\n", + "os.environ[\"LANGCHAIN_API_KEY\"] = \"\"\n", "os.environ[\"LANGCHAIN_PROJECT\"] = \"Mistral-code-gen-testing\"" ] }, @@ -110,18 +101,18 @@ "outputs": [], "source": [ "# Select LLM\n", - "from langchain_mistralai import ChatMistralAI\n", "from langchain_core.prompts import ChatPromptTemplate\n", "from langchain_core.pydantic_v1 import BaseModel, Field\n", + "from langchain_mistralai import ChatMistralAI\n", "\n", "mistral_model = \"mistral-large-latest\"\n", "llm = ChatMistralAI(model=mistral_model, temperature=0)\n", "\n", - "# Prompt \n", + "# Prompt\n", "code_gen_prompt_claude = ChatPromptTemplate.from_messages(\n", " [\n", " (\n", - " \"system\", \n", + " \"system\",\n", " \"\"\"You are a coding assistant. Ensure any code you provide can be executed with all required imports and variables \\n\n", " defined. Structure your answer: 1) a prefix describing the code solution, 2) the imports, 3) the functioning code block.\n", " \\n Here is the user question:\"\"\",\n", @@ -130,6 +121,7 @@ " ]\n", ")\n", "\n", + "\n", "# Data model\n", "class code(BaseModel):\n", " \"\"\"Code output\"\"\"\n", @@ -139,6 +131,7 @@ " code: str = Field(description=\"Code block not including import statements\")\n", " description = \"Schema for code solutions to questions about LCEL.\"\n", "\n", + "\n", "# LLM\n", "code_gen_chain = llm.with_structured_output(code, include_raw=False)" ] @@ -192,10 +185,11 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import Annotated\n", - "from typing import Dict, TypedDict, List\n", + "from typing import Annotated, TypedDict\n", + "\n", "from langgraph.graph.message import AnyMessage, add_messages\n", "\n", + "\n", "class GraphState(TypedDict):\n", " \"\"\"\n", " Represents the state of our graph.\n", @@ -228,14 +222,14 @@ "metadata": {}, "outputs": [], "source": [ - "from operator import itemgetter\n", + "import uuid\n", + "\n", "from langchain_core.pydantic_v1 import BaseModel, Field\n", - "from langchain_core.runnables import RunnablePassthrough\n", - "from langchain_core.prompts import PromptTemplate\n", "\n", "### Parameters\n", "max_iterations = 3\n", "\n", + "\n", "### Nodes\n", "def generate(state: GraphState):\n", " \"\"\"\n", @@ -253,7 +247,6 @@ " # State\n", " messages = state[\"messages\"]\n", " iterations = state[\"iterations\"]\n", - " error = state[\"error\"]\n", "\n", " # Solution\n", " code_solution = code_gen_chain.invoke(messages)\n", @@ -268,6 +261,7 @@ " iterations = iterations + 1\n", " return {\"generation\": code_solution, \"messages\": messages, \"iterations\": iterations}\n", "\n", + "\n", "def code_check(state: GraphState):\n", " \"\"\"\n", " Check code\n", @@ -287,7 +281,6 @@ " iterations = state[\"iterations\"]\n", "\n", " # Get solution components\n", - " prefix = code_solution.prefix\n", " imports = code_solution.imports\n", " code = code_solution.code\n", "\n", @@ -296,7 +289,12 @@ " exec(imports)\n", " except Exception as e:\n", " print(\"---CODE IMPORT CHECK: FAILED---\")\n", - " error_message = [(\"user\", f\"Your solution failed the import test. Here is the error: {e}. Reflect on this error and your prior attempt to solve the problem. (1) State what you think went wrong with the prior solution and (2) try to solve this problem again. Return the FULL SOLUTION. Use the code tool to structure the output with a prefix, imports, and code block:\")]\n", + " error_message = [\n", + " (\n", + " \"user\",\n", + " f\"Your solution failed the import test. Here is the error: {e}. Reflect on this error and your prior attempt to solve the problem. (1) State what you think went wrong with the prior solution and (2) try to solve this problem again. Return the FULL SOLUTION. Use the code tool to structure the output with a prefix, imports, and code block:\",\n", + " )\n", + " ]\n", " messages += error_message\n", " return {\n", " \"generation\": code_solution,\n", @@ -314,7 +312,12 @@ " exec(combined_code, global_scope)\n", " except Exception as e:\n", " print(\"---CODE BLOCK CHECK: FAILED---\")\n", - " error_message = [(\"user\", f\"Your solution failed the code execution test: {e}) Reflect on this error and your prior attempt to solve the problem. (1) State what you think went wrong with the prior solution and (2) try to solve this problem again. Return the FULL SOLUTION. Use the code tool to structure the output with a prefix, imports, and code block:\")]\n", + " error_message = [\n", + " (\n", + " \"user\",\n", + " f\"Your solution failed the code execution test: {e}) Reflect on this error and your prior attempt to solve the problem. (1) State what you think went wrong with the prior solution and (2) try to solve this problem again. Return the FULL SOLUTION. Use the code tool to structure the output with a prefix, imports, and code block:\",\n", + " )\n", + " ]\n", " messages += error_message\n", " return {\n", " \"generation\": code_solution,\n", @@ -332,8 +335,10 @@ " \"error\": \"no\",\n", " }\n", "\n", + "\n", "### Conditional edges\n", "\n", + "\n", "def decide_to_finish(state: GraphState):\n", " \"\"\"\n", " Determines whether to finish.\n", @@ -354,14 +359,14 @@ " print(\"---DECISION: RE-TRY SOLUTION---\")\n", " return \"generate\"\n", "\n", + "\n", "### Utilities\n", "\n", - "import uuid \n", "\n", "def _print_event(event: dict, _printed: set, max_length=1500):\n", " current_state = event.get(\"dialog_state\")\n", " if current_state:\n", - " print(f\"Currently in: \", current_state[-1])\n", + " print(\"Currently in: \", current_state[-1])\n", " message = event.get(\"messages\")\n", " if message:\n", " if isinstance(message, list):\n", @@ -428,7 +433,7 @@ "\n", "try:\n", " display(Image(graph.get_graph(xray=True).draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -554,6 +559,7 @@ "outputs": [], "source": [ "import uuid\n", + "\n", "_printed = set()\n", "thread_id = str(uuid.uuid4())\n", "config = {\n", @@ -563,7 +569,7 @@ " }\n", "}\n", "\n", - "question = '''I want to vectorize a function\n", + "question = \"\"\"I want to vectorize a function\n", "\n", " frame = np.zeros((out_h, out_w, 3), dtype=np.uint8)\n", " for i, val1 in enumerate(rows):\n", @@ -574,7 +580,7 @@ "\n", " out.write(np.array(frame))\n", "\n", - "with a simple numpy function that does something like this what is it called. Show me a test case with this working.'''\n", + "with a simple numpy function that does something like this what is it called. Show me a test case with this working.\"\"\"\n", "\n", "events = graph.stream(\n", " {\"messages\": [(\"user\", question)], \"iterations\": 0}, config, stream_mode=\"values\"\n", diff --git a/examples/configuration.ipynb b/examples/configuration.ipynb index f075aee2f..fb1bee442 100644 --- a/examples/configuration.ipynb +++ b/examples/configuration.ipynb @@ -29,12 +29,13 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", - "from langchain_anthropic import ChatAnthropic\n", - "from typing import TypedDict, Annotated, Sequence\n", "import operator\n", + "from typing import Annotated, Sequence, TypedDict\n", + "\n", + "from langchain_anthropic import ChatAnthropic\n", "from langchain_core.messages import BaseMessage, HumanMessage\n", "\n", + "from langgraph.graph import END, StateGraph\n", "\n", "model = ChatAnthropic(model_name=\"claude-2.1\")\n", "\n", diff --git a/examples/create-react-agent.ipynb b/examples/create-react-agent.ipynb new file mode 100644 index 000000000..35a3bb6e3 --- /dev/null +++ b/examples/create-react-agent.ipynb @@ -0,0 +1,651 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "992c4695-ec4f-428d-bd05-fb3b5fbd70f4", + "metadata": {}, + "source": [ + "# How to create a ReAct agent" + ] + }, + { + "cell_type": "markdown", + "id": "e0fcced0-9767-412f-90f9-7f3cd618ff90", + "metadata": {}, + "source": [ + "In this how-to we'll create a simple [ReAct](https://arxiv.org/abs/2210.03629) agent app that can check the weather. The app consists of an agent (LLM) and tools. As we interact with the app, we will first call the agent (LLM) to decide if we should use tools. Then we will run a loop: \n", + "\n", + "1. If the agent said to take an action (i.e. call tool), we'll run the tools and pass the results back to the agent\n", + "2. If the agent did not ask to run tools, we will finish (respond to the user)" + ] + }, + { + "cell_type": "markdown", + "id": "7be3889f-3c17-4fa1-bd2b-84114a2c7247", + "metadata": {}, + "source": [ + "## Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a213e11a-5c62-4ddb-a707-490d91add383", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture --no-stderr\n", + "%pip install -U langgraph langchain-openai" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "23a1885c-04ab-4750-aefa-105891fddf3e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OPENAI_API_KEY: ········\n" + ] + } + ], + "source": [ + "import getpass\n", + "import os\n", + "\n", + "\n", + "def _set_env(var: str):\n", + " if not os.environ.get(var):\n", + " os.environ[var] = getpass.getpass(f\"{var}: \")\n", + "\n", + "\n", + "_set_env(\"OPENAI_API_KEY\")\n", + "\n", + "# Recommended\n", + "_set_env(\"LANGCHAIN_API_KEY\")\n", + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "os.environ[\"LANGCHAIN_PROJECT\"] = \"Create ReAct Agent Tutorial\"" + ] + }, + { + "cell_type": "markdown", + "id": "03c0f089-070c-4cd4-87e0-6c51f2477b82", + "metadata": {}, + "source": [ + "## How to create a simple ReAct agent with `create_react_agent`" + ] + }, + { + "cell_type": "markdown", + "id": "91efaf20-711b-4b94-837e-dcef10d8abdd", + "metadata": {}, + "source": [ + "In our example we'll use `ChatOpenAI` as our agent and a custom tool that returns pre-defined values for weather in two cities (NYC & SF)" + ] + }, + { + "cell_type": "markdown", + "id": "6238f7d4-7da1-4e48-bc5e-a592280271e0", + "metadata": {}, + "source": [ + "### Define model and tools" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "029c838a-c6e7-4679-9a8b-953703fe3041", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Literal\n", + "\n", + "from langchain_core.tools import tool\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "model = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n", + "\n", + "\n", + "@tool\n", + "def get_weather(city: Literal[\"nyc\", \"sf\"]):\n", + " \"\"\"Use this to get weather information.\"\"\"\n", + " if city == \"nyc\":\n", + " return \"It might be cloudy in nyc\"\n", + " elif city == \"sf\":\n", + " return \"It's always sunny in sf\"\n", + " else:\n", + " raise AssertionError(\"Unknown city\")\n", + "\n", + "\n", + "tools = [get_weather]" + ] + }, + { + "cell_type": "markdown", + "id": "47570e6c-655b-45ae-ad4a-b278e58f1b94", + "metadata": {}, + "source": [ + "### Define the graph" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "3d2df9e7-4c2c-494a-9249-5a2ee32eca3e", + "metadata": {}, + "outputs": [], + "source": [ + "from langgraph.prebuilt import create_react_agent\n", + "\n", + "graph = create_react_agent(model, tools=tools)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "fa16de4c-aac0-4ff4-ab69-60d399f75423", + "metadata": {}, + "outputs": [ + { + "data": { + "image/jpeg": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Image, display\n", + "\n", + "display(Image(graph.get_graph().draw_mermaid_png()))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "16636975-5f2d-4dc7-ab8e-d0bea0830a28", + "metadata": {}, + "outputs": [], + "source": [ + "def print_stream(stream):\n", + " for s in stream:\n", + " message = s[\"messages\"][-1]\n", + " if isinstance(message, tuple):\n", + " print(message)\n", + " else:\n", + " message.pretty_print()" + ] + }, + { + "cell_type": "markdown", + "id": "9d187d6b-0fb6-4860-8771-160c3cf403c6", + "metadata": {}, + "source": [ + "Let's run the app with an input that needs a tool call" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "9ffff6c3-a4f5-47c9-b51d-97caaee85cd6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "what is the weather in sf\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "Tool Calls:\n", + " get_weather (call_g6w9lHn3fxYo2ABE3Ihhprbr)\n", + " Call ID: call_g6w9lHn3fxYo2ABE3Ihhprbr\n", + " Args:\n", + " city: sf\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: get_weather\n", + "\n", + "It's always sunny in sf\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "The weather in San Francisco is currently sunny.\n" + ] + } + ], + "source": [ + "inputs = {\"messages\": [(\"user\", \"what is the weather in sf\")]}\n", + "print_stream(graph.stream(inputs, stream_mode=\"values\"))" + ] + }, + { + "cell_type": "markdown", + "id": "838a043f-90ad-4e69-9d1d-6e22db2c346c", + "metadata": {}, + "source": [ + "Now let's try a question that doesn't need tools" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "187479f9-32fa-4611-9487-cf816ba2e147", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "who built you?\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "I was created by OpenAI, a research organization focused on developing and advancing artificial intelligence technology.\n" + ] + } + ], + "source": [ + "inputs = {\"messages\": [(\"user\", \"who built you?\")]}\n", + "print_stream(graph.stream(inputs, stream_mode=\"values\"))" + ] + }, + { + "cell_type": "markdown", + "id": "82b06aa3-6414-48e6-ba11-07bf065a316a", + "metadata": {}, + "source": [ + "## How to add system prompt to `create_react_agent`" + ] + }, + { + "cell_type": "markdown", + "id": "a3823699-1050-40fc-a8cc-ecbdc03c1cbe", + "metadata": {}, + "source": [ + "There are several ways to customize prompt, all of which are controlled by `messages_modifier` param. You can pass:\n", + "- system message string / `SystemMessage` that will be prepended to the list of messages\n", + "- a function that takes a list of messages and transforms them into an output that can be passed to the language model" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "23ea0a31-3e6b-433b-a832-036655579ebe", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "What's your name? And what's the weather in SF?\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "Tool Calls:\n", + " get_weather (call_PGwTiytTVAAvNKWp4nznPi4s)\n", + " Call ID: call_PGwTiytTVAAvNKWp4nznPi4s\n", + " Args:\n", + " city: sf\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: get_weather\n", + "\n", + "It's always sunny in sf\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "My name is Fred. The weather in San Francisco is currently sunny.\n" + ] + } + ], + "source": [ + "system_prompt = \"You are a helpful bot named Fred.\"\n", + "graph = create_react_agent(model, tools, messages_modifier=system_prompt)\n", + "\n", + "inputs = {\"messages\": [(\"user\", \"What's your name? And what's the weather in SF?\")]}\n", + "print_stream(graph.stream(inputs, stream_mode=\"values\"))" + ] + }, + { + "cell_type": "markdown", + "id": "d8e797ce-74bf-4a30-a388-08104138fd52", + "metadata": {}, + "source": [ + "We can also add a more complex prompt for the LLM:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "23c40ab3-c574-47be-a80f-654e9035cd6d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "What's my name? And what's the weather in SF?\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "Tool Calls:\n", + " get_weather (call_scpzIjdK3l411zcEn2T00Xm0)\n", + " Call ID: call_scpzIjdK3l411zcEn2T00Xm0\n", + " Args:\n", + " city: sf\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: get_weather\n", + "\n", + "It's always sunny in sf\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "Your name is Joe. The weather in San Francisco is always sunny.\n" + ] + } + ], + "source": [ + "from langchain_core.prompts import ChatPromptTemplate\n", + "\n", + "prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\"system\", \"You are a helpful bot named Fred.\"),\n", + " (\"user\", \"My name is Joe\"),\n", + " (\"placeholder\", \"{messages}\"),\n", + " ]\n", + ")\n", + "\n", + "\n", + "def modify_messages(messages: list):\n", + " # You can do more complex modifications here\n", + " return prompt.invoke({\"messages\": messages})\n", + "\n", + "\n", + "graph = create_react_agent(model, tools, messages_modifier=modify_messages)\n", + "\n", + "inputs = {\"messages\": [(\"user\", \"What's my name? And what's the weather in SF?\")]}\n", + "print_stream(graph.stream(inputs, stream_mode=\"values\"))" + ] + }, + { + "cell_type": "markdown", + "id": "0af418b8-bdd3-4e10-a552-9d8d2a409a57", + "metadata": {}, + "source": [ + "## How to add memory to `create_react_agent`" + ] + }, + { + "cell_type": "markdown", + "id": "4eef6365-530b-4d33-9874-41273ebee510", + "metadata": {}, + "source": [ + "We can add \"chat memory\" to the graph with LangGraph's checkpointer, to retain the chat context between interactions" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "c43d071a-a91e-4646-a035-b3fed9cd9f0c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "What's the weather in NYC?\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "Tool Calls:\n", + " get_weather (call_C5yEOD1GhlVPX9Gc5nnqgUXF)\n", + " Call ID: call_C5yEOD1GhlVPX9Gc5nnqgUXF\n", + " Args:\n", + " city: nyc\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: get_weather\n", + "\n", + "It might be cloudy in nyc\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "The weather in NYC might be cloudy.\n" + ] + } + ], + "source": [ + "from langgraph.checkpoint import MemorySaver\n", + "\n", + "graph = create_react_agent(model, tools, checkpointer=MemorySaver())\n", + "\n", + "config = {\"configurable\": {\"thread_id\": \"1\"}}\n", + "inputs = {\"messages\": [(\"user\", \"What's the weather in NYC?\")]}\n", + "\n", + "print_stream(graph.stream(inputs, config=config, stream_mode=\"values\"))" + ] + }, + { + "cell_type": "markdown", + "id": "4541ea58-ee6a-4685-a988-c915a42284e6", + "metadata": {}, + "source": [ + "Notice that when we pass the same the same thread ID, the chat history is preserved" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "b509417b-70f3-4736-b838-5d1729ed759e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "What's it known for?\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "New York City (NYC) is known for many things, including:\n", + "\n", + "1. **Landmarks and Attractions**: \n", + " - **Statue of Liberty**: A symbol of freedom and democracy.\n", + " - **Times Square**: Known for its bright lights, Broadway theaters, and bustling atmosphere.\n", + " - **Central Park**: A large urban park offering various recreational activities.\n", + " - **Empire State Building**: An iconic skyscraper with an observation deck offering panoramic views of the city.\n", + " - **Brooklyn Bridge**: A historic bridge connecting Manhattan and Brooklyn.\n", + "\n", + "2. **Cultural Diversity**: NYC is a melting pot of cultures, languages, and cuisines, making it one of the most diverse cities in the world.\n", + "\n", + "3. **Arts and Entertainment**: \n", + " - **Broadway**: Renowned for its world-class theater productions.\n", + " - **Museums**: Such as the Metropolitan Museum of Art, the Museum of Modern Art (MoMA), and the American Museum of Natural History.\n", + " - **Music and Nightlife**: A vibrant scene with numerous music venues, bars, and clubs.\n", + "\n", + "4. **Financial Hub**: \n", + " - **Wall Street**: The financial district is home to the New York Stock Exchange and numerous financial institutions.\n", + "\n", + "5. **Fashion and Shopping**: \n", + " - **Fifth Avenue**: Known for its high-end shopping.\n", + " - **Fashion Week**: One of the major fashion events held twice a year.\n", + "\n", + "6. **Cuisine**: \n", + " - **Diverse Food Scene**: From street food like hot dogs and pretzels to fine dining and international cuisines.\n", + " - **Famous Foods**: New York-style pizza, bagels, and cheesecake.\n", + "\n", + "7. **Media and Publishing**: \n", + " - Home to major media companies, newspapers like The New York Times, and numerous publishing houses.\n", + "\n", + "8. **Sports**: \n", + " - Home to several major sports teams, including the New York Yankees (baseball), New York Mets (baseball), New York Knicks (basketball), Brooklyn Nets (basketball), New York Giants (football), and New York Jets (football).\n", + "\n", + "9. **Education and Research**: \n", + " - Prestigious institutions like Columbia University, New York University (NYU), and Rockefeller University.\n", + "\n", + "10. **Public Transportation**: \n", + " - An extensive subway system, buses, and taxis that make getting around the city convenient.\n", + "\n", + "NYC is a city that never sleeps, offering endless opportunities for exploration and experiences.\n" + ] + } + ], + "source": [ + "inputs = {\"messages\": [(\"user\", \"What's it known for?\")]}\n", + "print_stream(graph.stream(inputs, config=config, stream_mode=\"values\"))" + ] + }, + { + "cell_type": "markdown", + "id": "ef765ab6-3ea0-4de0-8737-de8f9d9add40", + "metadata": {}, + "source": [ + "And if we pass a different thread ID, the chat history is reset" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "1bb09dbe-bbba-4e8f-b44c-b18bd2ed96ff", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "What's it known for?\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "Could you please specify what \"it\" refers to? Are you asking about a specific city, person, object, or something else?\n" + ] + } + ], + "source": [ + "inputs = {\"messages\": [(\"user\", \"What's it known for?\")]}\n", + "print_stream(\n", + " graph.stream(\n", + " inputs, config={\"configurable\": {\"thread_id\": 2}}, stream_mode=\"values\"\n", + " )\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "42e730aa-8fac-48a5-9c67-b51393665469", + "metadata": {}, + "source": [ + "## How to add human-in-the-loop to `create_react_agent`" + ] + }, + { + "cell_type": "markdown", + "id": "6314e49c-9143-45a8-871b-8bd3f489425c", + "metadata": {}, + "source": [ + "Let's add an interrupt to let the user confirm before LLM takes an action:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "8cd2cbcc-8a1f-443e-9cc9-dc18cd0ceac6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "What's the weather in SF?\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "Tool Calls:\n", + " get_weather (call_I7B9YW4ENth7QXYzDpiIoLCE)\n", + " Call ID: call_I7B9YW4ENth7QXYzDpiIoLCE\n", + " Args:\n", + " city: sf\n" + ] + } + ], + "source": [ + "graph = create_react_agent(\n", + " model, tools, interrupt_before=[\"tools\"], checkpointer=MemorySaver()\n", + ")\n", + "\n", + "config = {\"configurable\": {\"thread_id\": \"42\"}}\n", + "inputs = {\"messages\": [(\"user\", \"What's the weather in SF?\")]}\n", + "\n", + "print_stream(graph.stream(inputs, config, stream_mode=\"values\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "493d4963-b27b-46ef-8474-1527b08c1c03", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Next step: ('tools',)\n" + ] + } + ], + "source": [ + "snapshot = graph.get_state(config)\n", + "print(\"Next step: \", snapshot.next)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "7ad01f3a-e838-4230-ab07-2f850bb7e7cb", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: get_weather\n", + "\n", + "It's always sunny in sf\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "The weather in San Francisco is currently sunny.\n" + ] + } + ], + "source": [ + "print_stream(graph.stream(None, config, stream_mode=\"values\"))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "langgraph-example-dev", + "language": "python", + "name": "langgraph-example-dev" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/customer-support/customer-support.ipynb b/examples/customer-support/customer-support.ipynb index ee7c57086..f3828cc5b 100644 --- a/examples/customer-support/customer-support.ipynb +++ b/examples/customer-support/customer-support.ipynb @@ -903,8 +903,8 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain_core.runnables import RunnableLambda\n", "from langchain_core.messages import ToolMessage\n", + "from langchain_core.runnables import RunnableLambda\n", "\n", "from langgraph.prebuilt import ToolNode\n", "\n", @@ -932,7 +932,7 @@ "def _print_event(event: dict, _printed: set, max_length=1500):\n", " current_state = event.get(\"dialog_state\")\n", " if current_state:\n", - " print(f\"Currently in: \", current_state[-1])\n", + " print(\"Currently in: \", current_state[-1])\n", " message = event.get(\"messages\")\n", " if message:\n", " if isinstance(message, list):\n", @@ -1107,7 +1107,7 @@ "source": [ "from langgraph.checkpoint.sqlite import SqliteSaver\n", "from langgraph.graph import END, StateGraph\n", - "from langgraph.prebuilt import ToolNode, tools_condition\n", + "from langgraph.prebuilt import tools_condition\n", "\n", "builder = StateGraph(State)\n", "\n", @@ -1151,7 +1151,7 @@ "\n", "try:\n", " display(Image(part_1_graph.get_graph(xray=True).draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -1806,7 +1806,7 @@ "\n", "#### State & Assistant\n", "\n", - "Our graph state and LLM calling is nearly identical to Part 1 except:\n", + "Our graph state and LLM calling is nearly identical to Part 1 except Exception:\n", "\n", "- We've added a `user_info` field that will be eagerly populated by our graph\n", "- We can use the state directly in the `Assistant` object rather than using the configurable params" @@ -1924,8 +1924,8 @@ "outputs": [], "source": [ "from langgraph.checkpoint.sqlite import SqliteSaver\n", - "from langgraph.graph import END, StateGraph\n", - "from langgraph.prebuilt import ToolNode, tools_condition\n", + "from langgraph.graph import StateGraph\n", + "from langgraph.prebuilt import tools_condition\n", "\n", "builder = StateGraph(State)\n", "\n", @@ -1979,7 +1979,7 @@ "\n", "try:\n", " display(Image(part_2_graph.get_graph(xray=True).draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -2512,7 +2512,7 @@ "from typing import Literal\n", "\n", "from langgraph.checkpoint.sqlite import SqliteSaver\n", - "from langgraph.graph import END, StateGraph\n", + "from langgraph.graph import StateGraph\n", "from langgraph.prebuilt import tools_condition\n", "\n", "builder = StateGraph(State)\n", @@ -2588,7 +2588,7 @@ "\n", "try:\n", " display(Image(part_3_graph.get_graph(xray=True).draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -3442,7 +3442,7 @@ "from typing import Literal\n", "\n", "from langgraph.checkpoint.sqlite import SqliteSaver\n", - "from langgraph.graph import END, StateGraph\n", + "from langgraph.graph import StateGraph\n", "from langgraph.prebuilt import tools_condition\n", "\n", "builder = StateGraph(State)\n", @@ -3842,7 +3842,7 @@ "\n", "try:\n", " display(Image(part_4_graph.get_graph(xray=True).draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] diff --git a/examples/docs/quickstart.ipynb b/examples/docs/quickstart.ipynb index b42f5101d..e130c27f4 100644 --- a/examples/docs/quickstart.ipynb +++ b/examples/docs/quickstart.ipynb @@ -23,8 +23,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "if not os.environ.get(\"OPENAI_API_KEY\"):\n", " os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")" @@ -36,8 +36,9 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain_openai import ChatOpenAI\n", "from langchain_core.messages import BaseMessage, HumanMessage\n", + "from langchain_openai import ChatOpenAI\n", + "\n", "from langgraph.graph import END, MessageGraph\n", "\n", "model = ChatOpenAI(temperature=0)\n", @@ -73,7 +74,7 @@ "\n", "try:\n", " display(Image(runnable.get_graph(xray=True).draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -105,11 +106,13 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain_core.tools import tool\n", - "from langgraph.prebuilt import ToolNode\n", - "from langgraph.graph import END, START\n", "from typing import Literal\n", "\n", + "from langchain_core.tools import tool\n", + "\n", + "from langgraph.graph import END, START\n", + "from langgraph.prebuilt import ToolNode\n", + "\n", "\n", "@tool\n", "def multiply(first_number: int, second_number: int):\n", @@ -161,7 +164,7 @@ "source": [ "try:\n", " display(Image(runnable.get_graph(xray=True).draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] diff --git a/examples/dynamically-returning-directly.ipynb b/examples/dynamically-returning-directly.ipynb index e3b133af7..38b9e3b0e 100644 --- a/examples/dynamically-returning-directly.ipynb +++ b/examples/dynamically-returning-directly.ipynb @@ -48,8 +48,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "\n", "def _set_env(var: str):\n", @@ -227,8 +227,8 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import TypedDict, Annotated\n", "import operator\n", + "from typing import Annotated, TypedDict\n", "\n", "\n", "class AgentState(TypedDict):\n", @@ -269,8 +269,9 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.prebuilt import ToolInvocation\n", - "from langchain_core.messages import ToolMessage" + "from langchain_core.messages import ToolMessage\n", + "\n", + "from langgraph.prebuilt import ToolInvocation" ] }, { @@ -386,7 +387,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", + "from langgraph.graph import END, StateGraph\n", "\n", "# Define a new graph\n", "workflow = StateGraph(AgentState)\n", @@ -604,7 +605,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.12.2" } }, "nbformat": 4, diff --git a/examples/extraction/retries.ipynb b/examples/extraction/retries.ipynb index 36e4ee280..47de6529d 100644 --- a/examples/extraction/retries.ipynb +++ b/examples/extraction/retries.ipynb @@ -420,11 +420,11 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain_core.prompts import ChatPromptTemplate\n", "from langchain_anthropic import ChatAnthropic\n", + "from langchain_core.prompts import ChatPromptTemplate\n", "\n", "# Or you can use ChatGroq, ChatOpenAI, ChatGoogleGemini, ChatCohere, etc.\n", - "# See https://python.langchain.com/v0.1/docs/integrations/chat/ for more info on tool calling\n", + "# See https://python.langchain.com/v0.2/docs/integrations/chat/ for more info on tool calling\n", "llm = ChatAnthropic(model=\"claude-3-haiku-20240307\")\n", "bound_llm = bind_validator_with_retries(llm, tools=tools)\n", "prompt = ChatPromptTemplate.from_messages(\n", @@ -953,7 +953,7 @@ "\n", "try:\n", " display(Image(bound_llm.get_graph().draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " pass" ] }, @@ -1036,7 +1036,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.12.2" } }, "nbformat": 4, diff --git a/examples/force-calling-a-tool-first.ipynb b/examples/force-calling-a-tool-first.ipynb index ffc104f8a..1c1947e1b 100644 --- a/examples/force-calling-a-tool-first.ipynb +++ b/examples/force-calling-a-tool-first.ipynb @@ -28,7 +28,7 @@ "outputs": [], "source": [ "%%capture --no-stderr\n", - "%pip install --quiet -U langchain langchain_openai tavily-python" + "%pip install --quiet -U langgraph langchain langchain_openai tavily-python" ] }, { @@ -46,8 +46,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")\n", "os.environ[\"TAVILY_API_KEY\"] = getpass.getpass(\"Tavily API Key:\")" @@ -81,7 +81,7 @@ "\n", "We will first define the tools we want to use.\n", "For this simple example, we will use a built-in search tool via Tavily.\n", - "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n" + "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that.\n" ] }, { @@ -193,8 +193,9 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import TypedDict, Annotated, Sequence\n", "import operator\n", + "from typing import Annotated, Sequence, TypedDict\n", + "\n", "from langchain_core.messages import BaseMessage\n", "\n", "\n", @@ -236,9 +237,10 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.prebuilt import ToolInvocation\n", "from langchain_core.messages import ToolMessage\n", "\n", + "from langgraph.prebuilt import ToolInvocation\n", + "\n", "\n", "# Define the function that determines whether to continue or not\n", "def should_continue(state):\n", @@ -357,7 +359,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", + "from langgraph.graph import END, StateGraph\n", "\n", "# Define a new graph\n", "workflow = StateGraph(AgentState)\n", @@ -429,7 +431,7 @@ "\n", "try:\n", " display(Image(app.get_graph(xray=True).draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] diff --git a/examples/human-in-the-loop.ipynb b/examples/human-in-the-loop.ipynb index a71ae9b80..1042d714f 100644 --- a/examples/human-in-the-loop.ipynb +++ b/examples/human-in-the-loop.ipynb @@ -18,7 +18,7 @@ "
\n", "

Note

\n", "

\n", - " In this how-to, we will create our agent from scratch to be transparent (but verbose). You can accomplish similar functionality using either `interrupt_before` or `interrupt_after` in the create_react_agent(model, tools=tool, interrupt_before=[\"tools\" | \"agent\"], interrupt_after=[\"tools\" | \"agent\"]) (API doc) constructor. This may be more appropriate if you are used to LangChain’s AgentExecutor class.\n", + " In this how-to, we will create our agent from scratch to be transparent (but verbose). You can accomplish similar functionality using either `interrupt_before` or `interrupt_after` in the create_react_agent(model, tools=tool, interrupt_before=[\"tools\" | \"agent\"], interrupt_after=[\"tools\" | \"agent\"]) (API doc) constructor. This may be more appropriate if you are used to LangChain’s AgentExecutor class.\n", "

\n", "
" ] @@ -59,8 +59,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "\n", "def _set_env(var: str):\n", @@ -107,8 +107,10 @@ "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", "from typing import Annotated\n", + "\n", + "from typing_extensions import TypedDict\n", + "\n", "from langgraph.graph.message import add_messages\n", "\n", "# `add_messages`` essentially does this\n", @@ -257,9 +259,10 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.prebuilt import ToolInvocation\n", "from langchain_core.messages import ToolMessage\n", "\n", + "from langgraph.prebuilt import ToolInvocation\n", + "\n", "\n", "# Define the function that determines whether to continue or not\n", "def should_continue(state):\n", @@ -320,7 +323,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", + "from langgraph.graph import END, StateGraph\n", "\n", "# Define a new graph\n", "workflow = StateGraph(State)\n", @@ -611,7 +614,7 @@ " indent=2,\n", " )\n", " return AIMessage(\n", - " content = (\n", + " content=(\n", " \"I plan to invoke the following tools, do you approve?\\n\\n\"\n", " \"Type 'y' if you do, anything else to stop.\\n\\n\"\n", " f\"{serialized_tool_calls}\"\n", @@ -665,7 +668,7 @@ ] }, { - "name": "stdin", + "name": "stdout", "output_type": "stream", "text": [ " can you specify sf in CA?\n" @@ -696,7 +699,7 @@ ] }, { - "name": "stdin", + "name": "stdout", "output_type": "stream", "text": [ " y\n" @@ -969,7 +972,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.4" + "version": "3.12.2" } }, "nbformat": 4, diff --git a/examples/introduction.ipynb b/examples/introduction.ipynb index 6caea9cd8..5cdde3480 100644 --- a/examples/introduction.ipynb +++ b/examples/introduction.ipynb @@ -245,7 +245,7 @@ "\n", "try:\n", " display(Image(graph.get_graph().draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -398,7 +398,7 @@ "\n", "Before we start, make sure you have the necessary packages installed and API keys set up:\n", "\n", - "First, install the requirements to use the [Tavily Search Engine](https://python.langchain.com/docs/integrations/tools/tavily_search/), and set your [TAVILY_API_KEY](https://tavily.com/)." + "First, install the requirements to use the [Tavily Search Engine](https://python.langchain.com/v0.2/docs/integrations/tools/tavily_search/), and set your [TAVILY_API_KEY](https://tavily.com/)." ] }, { @@ -653,7 +653,7 @@ "\n", "try:\n", " display(Image(graph.get_graph().draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -775,7 +775,7 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import Annotated, Union\n", + "from typing import Annotated\n", "\n", "from langchain_anthropic import ChatAnthropic\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", @@ -873,7 +873,7 @@ } ], "source": [ - "from typing import Annotated, Union\n", + "from typing import Annotated\n", "\n", "from langchain_anthropic import ChatAnthropic\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", @@ -964,7 +964,7 @@ "\n", "try:\n", " display(Image(graph.get_graph().draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -1179,7 +1179,7 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import Annotated, Union\n", + "from typing import Annotated\n", "\n", "from langchain_anthropic import ChatAnthropic\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", @@ -1253,7 +1253,7 @@ } ], "source": [ - "from typing import Annotated, Union\n", + "from typing import Annotated\n", "\n", "from langchain_anthropic import ChatAnthropic\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", @@ -1443,7 +1443,7 @@ "=================================\u001b[1m Tool Message \u001b[0m=================================\n", "Name: tavily_search_results_json\n", "\n", - "[{\"url\": \"https://github.com/langchain-ai/langgraph\", \"content\": \"LangGraph is a Python package that extends LangChain Expression Language with the ability to coordinate multiple chains across multiple steps of computation in a cyclic manner. It is inspired by Pregel and Apache Beam and can be used for agent-like behaviors, such as chatbots, with LLMs.\"}, {\"url\": \"https://python.langchain.com/docs/langgraph/\", \"content\": \"LangGraph is a library for building stateful, multi-actor applications with LLMs, built on top of (and intended to be used with) LangChain . It extends the LangChain Expression Language with the ability to coordinate multiple chains (or actors) across multiple steps of computation in a cyclic manner. It is inspired by Pregel and Apache Beam .\"}]\n", + "[{\"url\": \"https://github.com/langchain-ai/langgraph\", \"content\": \"LangGraph is a Python package that extends LangChain Expression Language with the ability to coordinate multiple chains across multiple steps of computation in a cyclic manner. It is inspired by Pregel and Apache Beam and can be used for agent-like behaviors, such as chatbots, with LLMs.\"}, {\"url\": \"https://langchain-ai.github.io/langgraph//\", \"content\": \"LangGraph is a library for building stateful, multi-actor applications with LLMs, built on top of (and intended to be used with) LangChain . It extends the LangChain Expression Language with the ability to coordinate multiple chains (or actors) across multiple steps of computation in a cyclic manner. It is inspired by Pregel and Apache Beam .\"}]\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", "Based on the search results, LangGraph seems to be a Python library that extends the LangChain library to enable more complex, multi-step interactions with large language models (LLMs). Some key points:\n", @@ -1487,7 +1487,7 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import Annotated, Union\n", + "from typing import Annotated\n", "\n", "from langchain_anthropic import ChatAnthropic\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", @@ -1495,7 +1495,7 @@ "from typing_extensions import TypedDict\n", "\n", "from langgraph.checkpoint.sqlite import SqliteSaver\n", - "from langgraph.graph import MessageGraph, StateGraph\n", + "from langgraph.graph import StateGraph\n", "from langgraph.graph.message import add_messages\n", "from langgraph.prebuilt import ToolNode\n", "\n", @@ -1569,7 +1569,7 @@ } ], "source": [ - "from typing import Annotated, Union\n", + "from typing import Annotated\n", "\n", "from langchain_anthropic import ChatAnthropic\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", @@ -1577,7 +1577,7 @@ "from typing_extensions import TypedDict\n", "\n", "from langgraph.checkpoint.sqlite import SqliteSaver\n", - "from langgraph.graph import MessageGraph, StateGraph\n", + "from langgraph.graph import StateGraph\n", "from langgraph.graph.message import add_messages\n", "from langgraph.prebuilt import ToolNode, tools_condition\n", "\n", @@ -1688,7 +1688,7 @@ } ], "source": [ - "from langchain_core.messages import AIMessage, ToolMessage\n", + "from langchain_core.messages import AIMessage\n", "\n", "answer = (\n", " \"LangGraph is a library for building stateful, multi-actor applications with LLMs.\"\n", @@ -1790,7 +1790,7 @@ "\n", "try:\n", " display(Image(graph.get_graph().draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -2068,7 +2068,7 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import Annotated, Union\n", + "from typing import Annotated\n", "\n", "from langchain_anthropic import ChatAnthropic\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", @@ -2310,7 +2310,7 @@ "\n", "try:\n", " display(Image(graph.get_graph().draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -2517,7 +2517,7 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import Annotated, Union\n", + "from typing import Annotated\n", "\n", "from langchain_anthropic import ChatAnthropic\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", @@ -2644,7 +2644,7 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import Annotated, Union, Literal\n", + "from typing import Annotated, Literal\n", "\n", "from langchain_anthropic import ChatAnthropic\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", @@ -2768,7 +2768,7 @@ "\n", "try:\n", " display(Image(graph.get_graph().draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] diff --git a/examples/lats/lats.ipynb b/examples/lats/lats.ipynb index 858761d38..3e1a5cca2 100644 --- a/examples/lats/lats.ipynb +++ b/examples/lats/lats.ipynb @@ -38,8 +38,9 @@ "metadata": {}, "outputs": [], "source": [ - "# %pip install -U --quiet langchain langgraph langchain_openai\n", - "# %pip install -U --quiet tavily-python" + "%%capture --no-stderr\n", + "%pip install -U --quiet langchain langgraph langchain_openai\n", + "%pip install -U --quiet tavily-python" ] }, { @@ -49,6 +50,8 @@ "metadata": {}, "outputs": [], "source": [ + "from __future__ import annotations # noqa: F404\n", + "\n", "import getpass\n", "import os\n", "\n", @@ -91,13 +94,11 @@ "metadata": {}, "outputs": [], "source": [ - "from __future__ import annotations\n", - "\n", "import math\n", + "from collections import deque\n", "from typing import Optional\n", "\n", "from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, ToolMessage\n", - "from collections import deque\n", "\n", "\n", "class Node:\n", @@ -321,13 +322,13 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", - "from langchain_core.pydantic_v1 import BaseModel, Field\n", - "from langchain_core.runnables import chain as as_runnable\n", "from langchain_core.output_parsers.openai_tools import (\n", " JsonOutputToolsParser,\n", " PydanticToolsParser,\n", ")\n", + "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", + "from langchain_core.pydantic_v1 import BaseModel, Field\n", + "from langchain_core.runnables import chain as as_runnable\n", "\n", "\n", "class Reflection(BaseModel):\n", @@ -637,6 +638,7 @@ "outputs": [], "source": [ "from typing import Literal\n", + "\n", "from langgraph.graph import END, StateGraph\n", "\n", "\n", @@ -925,7 +927,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.12.2" } }, "nbformat": 4, diff --git a/examples/learning.ipynb b/examples/learning.ipynb index e545fb27c..0b4c27db1 100644 --- a/examples/learning.ipynb +++ b/examples/learning.ipynb @@ -9,7 +9,7 @@ "\n", "When running LangGraph agents, you can easily save good threads and use them in the future.\n", "\n", - "**Note:** this requires passing in a checkpointer." + "**Note:** this requires passing in a checkpointer.\n" ] }, { @@ -19,7 +19,7 @@ "source": [ "## Setup\n", "\n", - "First we need to install the packages required" + "First we need to install the packages required\n" ] }, { @@ -39,7 +39,7 @@ } ], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python" + "!%pip install --quiet -U langgraph langchain langchain_openai tavily-pythonvily-python" ] }, { @@ -47,7 +47,7 @@ "id": "0abe11f4-62ed-4dc4-8875-3db21e260d1d", "metadata": {}, "source": [ - "Next, we need to set API keys for OpenAI (the LLM we will use) and Tavily (the search tool we will use)" + "Next, we need to set API keys for OpenAI (the LLM we will use) and Tavily (the search tool we will use)\n" ] }, { @@ -66,8 +66,8 @@ } ], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")\n", "os.environ[\"TAVILY_API_KEY\"] = getpass.getpass(\"Tavily API Key:\")" @@ -78,7 +78,7 @@ "id": "f0ed46a8-effe-4596-b0e1-a6a29ee16f5c", "metadata": {}, "source": [ - "Optionally, we can set API key for [LangSmith tracing](https://smith.langchain.com/), which will give us best-in-class observability." + "Optionally, we can set API key for [LangSmith tracing](https://smith.langchain.com/), which will give us best-in-class observability.\n" ] }, { @@ -101,7 +101,7 @@ "\n", "We will first define the tools we want to use.\n", "For this simple example, we will use a built-in search tool via Tavily.\n", - "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n" + "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that.\n" ] }, { @@ -150,7 +150,7 @@ "1. It should work with messages. We will represent all agent state in the form of messages, so it needs to be able to work well with them.\n", "2. It should work with OpenAI function calling. This means it should either be an OpenAI model or a model that exposes a similar interface.\n", "\n", - "Note: these model requirements are not requirements for using LangGraph - they are just requirements for this one example." + "Note: these model requirements are not requirements for using LangGraph - they are just requirements for this one example.\n" ] }, { @@ -170,7 +170,6 @@ "id": "a77995c0-bae2-4cee-a036-8688a90f05b9", "metadata": {}, "source": [ - "\n", "After we've done this, we should make sure the model knows that it has these tools available to call.\n", "We can do this using the `.bind_tools()` method, common to many of LangChain's chat models.\n" ] @@ -193,7 +192,7 @@ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -209,7 +208,7 @@ " b. If the agent said that it was finished, then it should finish\n", "2. Normal Edge: after the tools are invoked, it should always go back to the agent to decide what to do next\n", "\n", - "Let's define the nodes, as well as a function to decide how what conditional edge to take." + "Let's define the nodes, as well as a function to decide how what conditional edge to take.\n" ] }, { @@ -237,7 +236,7 @@ "source": [ "## Define the graph\n", "\n", - "We can now put it all together and define the graph!" + "We can now put it all together and define the graph!\n" ] }, { @@ -247,11 +246,19 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", + "from typing import Annotated, TypedDict\n", + "\n", + "from langchain_core.messages import (\n", + " AIMessage,\n", + " AnyMessage,\n", + " HumanMessage,\n", + " SystemMessage,\n", + " ToolMessage,\n", + ")\n", + "\n", + "from langgraph.graph import END, StateGraph\n", "from langgraph.graph.message import add_messages\n", "from langgraph.managed.few_shot import FewShotExamples\n", - "from typing import TypedDict, Annotated\n", - "from langchain_core.messages import AnyMessage, HumanMessage, SystemMessage\n", "\n", "\n", "class BaseState(TypedDict):\n", @@ -259,9 +266,6 @@ " examples: Annotated[list, FewShotExamples]\n", "\n", "\n", - "from langchain_core.messages import AIMessage, ToolMessage\n", - "\n", - "\n", "def _render_message(m):\n", " if isinstance(m, HumanMessage):\n", " return \"Human: \" + m.content\n", @@ -299,9 +303,7 @@ "\n", "{examples}\n", "\n", - "Assist the user as they require!\"\"\".format(\n", - " examples=_examples\n", - " )\n", + "Assist the user as they require!\"\"\".format(examples=_examples)\n", "\n", " else:\n", " system_message = \"\"\"You are a helpful assistant\"\"\"\n", @@ -350,7 +352,7 @@ "source": [ "**Persistence**\n", "\n", - "To add in persistence, we pass in a checkpoint when compiling the graph" + "To add in persistence, we pass in a checkpoint when compiling the graph\n" ] }, { @@ -383,7 +385,7 @@ "id": "e8aff75b-563e-42b1-969b-742201514fc3", "metadata": {}, "source": [ - "## Preview the graph" + "## Preview the graph\n" ] }, { @@ -435,8 +437,6 @@ } ], "source": [ - "from langchain_core.messages import HumanMessage\n", - "\n", "thread = {\"configurable\": {\"thread_id\": \"1\"}}\n", "for event in app.stream(\n", " {\"messages\": [HumanMessage(content=\"whats the weather in sf?\")]}, thread\n", diff --git a/examples/llm-compiler/LLMCompiler.ipynb b/examples/llm-compiler/LLMCompiler.ipynb index b3d30e5dd..23a75ab57 100644 --- a/examples/llm-compiler/LLMCompiler.ipynb +++ b/examples/llm-compiler/LLMCompiler.ipynb @@ -43,8 +43,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "\n", "def _get_pass(var: str):\n", @@ -68,7 +68,7 @@ "\n", "We'll first define the tools for the agent to use in our demo. We'll give it the class search engine + calculator combo.\n", "\n", - "If you don't want to sign up for tavily, you can replace it with the free [DuckDuckGo](https://python.langchain.com/docs/integrations/tools/ddg)." + "If you don't want to sign up for tavily, you can replace it with the free [DuckDuckGo](https://python.langchain.com/v0.2/docs/integrations/tools/ddg/)." ] }, { @@ -78,8 +78,8 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain_openai import ChatOpenAI\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", + "from langchain_openai import ChatOpenAI\n", "\n", "# Imported from the https://github.com/langchain-ai/langgraph/tree/main/examples/plan-and-execute repo\n", "from math_tools import get_math_tool\n", @@ -191,21 +191,19 @@ "source": [ "from typing import Sequence\n", "\n", + "from langchain import hub\n", "from langchain_core.language_models import BaseChatModel\n", - "from langchain_core.prompts import ChatPromptTemplate\n", - "from langchain_core.runnables import RunnableBranch\n", - "from langchain_core.tools import BaseTool\n", "from langchain_core.messages import (\n", " BaseMessage,\n", " FunctionMessage,\n", " HumanMessage,\n", " SystemMessage,\n", ")\n", - "\n", - "from output_parser import LLMCompilerPlanParser, Task\n", - "from langchain import hub\n", + "from langchain_core.prompts import ChatPromptTemplate\n", + "from langchain_core.runnables import RunnableBranch\n", + "from langchain_core.tools import BaseTool\n", "from langchain_openai import ChatOpenAI\n", - "\n", + "from output_parser import LLMCompilerPlanParser, Task\n", "\n", "prompt = hub.pull(\"wfh/llm-compiler\")\n", "print(prompt.pretty_print())" @@ -340,16 +338,15 @@ }, "outputs": [], "source": [ - "from typing import Any, Union, Iterable, List, Tuple, Dict\n", - "from typing_extensions import TypedDict\n", "import re\n", + "import time\n", + "from concurrent.futures import ThreadPoolExecutor, wait\n", + "from typing import Any, Dict, Iterable, List, Union\n", "\n", "from langchain_core.runnables import (\n", " chain as as_runnable,\n", ")\n", - "\n", - "from concurrent.futures import ThreadPoolExecutor, wait\n", - "import time\n", + "from typing_extensions import TypedDict\n", "\n", "\n", "def _get_observations(messages: List[BaseMessage]) -> Dict[int, Any]:\n", @@ -472,8 +469,7 @@ " args_for_tasks[task[\"idx\"]] = task[\"args\"]\n", " if (\n", " # Depends on other tasks\n", - " deps\n", - " and (any([dep not in observations for dep in deps]))\n", + " deps and (any([dep not in observations for dep in deps]))\n", " ):\n", " futures.append(\n", " executor.submit(\n", @@ -597,9 +593,9 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain_core.pydantic_v1 import BaseModel, Field\n", "from langchain.chains.openai_functions import create_structured_output_runnable\n", "from langchain_core.messages import AIMessage\n", + "from langchain_core.pydantic_v1 import BaseModel, Field\n", "\n", "\n", "class FinalResponse(BaseModel):\n", @@ -724,9 +720,10 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import MessageGraph, END\n", "from typing import Dict\n", "\n", + "from langgraph.graph import END, MessageGraph\n", + "\n", "graph_builder = MessageGraph()\n", "\n", "# 1. Define vertices\n", diff --git a/examples/managing-agent-steps.ipynb b/examples/managing-agent-steps.ipynb index a8650defb..97c2abd05 100644 --- a/examples/managing-agent-steps.ipynb +++ b/examples/managing-agent-steps.ipynb @@ -48,8 +48,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "\n", "def _set_env(var: str):\n", @@ -104,8 +104,10 @@ "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", "from typing import Annotated\n", + "\n", + "from typing_extensions import TypedDict\n", + "\n", "from langgraph.graph.message import add_messages\n", "\n", "# Add messages essentially does this with more\n", @@ -127,7 +129,7 @@ "\n", "We will first define the tools we want to use.\n", "For this simple example, we will use create a placeholder search engine.\n", - "It is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n" + "It is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that.\n" ] }, { @@ -229,7 +231,7 @@ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -317,7 +319,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", + "from langgraph.graph import END, StateGraph\n", "\n", "# Define a new graph\n", "workflow = StateGraph(State)\n", @@ -392,7 +394,7 @@ "## Use it!\n", "\n", "We can now use it!\n", - "This now exposes the [same interface](https://python.langchain.com/docs/expression_language/) as all other LangChain runnables." + "This now exposes the [same interface](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel) as all other LangChain runnables." ] }, { @@ -675,7 +677,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.12.2" } }, "nbformat": 4, diff --git a/examples/managing-conversation-history.ipynb b/examples/managing-conversation-history.ipynb index 65719acac..8a7495a67 100644 --- a/examples/managing-conversation-history.ipynb +++ b/examples/managing-conversation-history.ipynb @@ -46,8 +46,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "\n", "def _set_env(var: str):\n", @@ -92,18 +92,18 @@ "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", - "from typing import Annotated\n", - "from langgraph.graph import MessagesState\n", - "from langchain_core.tools import tool\n", - "from langgraph.prebuilt import ToolNode\n", - "from langchain_anthropic import ChatAnthropic\n", "from typing import Literal\n", - "from langgraph.graph import StateGraph, END\n", + "\n", + "from langchain_anthropic import ChatAnthropic\n", + "from langchain_core.tools import tool\n", + "\n", "from langgraph.checkpoint.sqlite import SqliteSaver\n", + "from langgraph.graph import MessagesState, StateGraph\n", + "from langgraph.prebuilt import ToolNode\n", "\n", "memory = SqliteSaver.from_conn_string(\":memory:\")\n", "\n", + "\n", "@tool\n", "def search(query: str):\n", " \"\"\"Call to surf the web.\"\"\"\n", @@ -119,6 +119,7 @@ "model = ChatAnthropic(model_name=\"claude-3-haiku-20240307\")\n", "bound_model = model.bind_tools(tools)\n", "\n", + "\n", "def should_continue(state: MessagesState) -> Literal[\"action\", \"__end__\"]:\n", " \"\"\"Return the next node to execute.\"\"\"\n", " last_message = state[\"messages\"][-1]\n", @@ -222,18 +223,18 @@ "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", - "from typing import Annotated\n", - "from langgraph.graph import MessagesState\n", - "from langchain_core.tools import tool\n", - "from langgraph.prebuilt import ToolNode\n", - "from langchain_anthropic import ChatAnthropic\n", "from typing import Literal\n", - "from langgraph.graph import StateGraph, END\n", + "\n", + "from langchain_anthropic import ChatAnthropic\n", + "from langchain_core.tools import tool\n", + "\n", "from langgraph.checkpoint.sqlite import SqliteSaver\n", + "from langgraph.graph import MessagesState, StateGraph\n", + "from langgraph.prebuilt import ToolNode\n", "\n", "memory = SqliteSaver.from_conn_string(\":memory:\")\n", "\n", + "\n", "@tool\n", "def search(query: str):\n", " \"\"\"Call to surf the web.\"\"\"\n", @@ -249,6 +250,7 @@ "model = ChatAnthropic(model_name=\"claude-3-haiku-20240307\")\n", "bound_model = model.bind_tools(tools)\n", "\n", + "\n", "def should_continue(state: MessagesState) -> Literal[\"action\", \"__end__\"]:\n", " \"\"\"Return the next node to execute.\"\"\"\n", " last_message = state[\"messages\"][-1]\n", @@ -335,7 +337,7 @@ "for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n", " event[\"messages\"][-1].pretty_print()\n", "\n", - "# This will now not remember the previous messages \n", + "# This will now not remember the previous messages\n", "# (because we set `messages[-1:]` in the filter messages argument)\n", "input_message = HumanMessage(content=\"whats my name?\")\n", "for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n", diff --git a/examples/map-reduce.ipynb b/examples/map-reduce.ipynb index 9a386b3aa..eb9a254ef 100644 --- a/examples/map-reduce.ipynb +++ b/examples/map-reduce.ipynb @@ -7,7 +7,7 @@ "source": [ "# Map Reduce\n", "\n", - "A common pattern in agents is to generate a list of objects, do some work on each of those objects, and then combine the results. This is very similar to the common [map-reduce](https://en.wikipedia.org/wiki/MapReduce) operation. This can be tricky for a few reasons. First, it can be tought to define in structured graph ahead of time because the length of the list of objects may be unknown. Second, in order to do this map-reduce you need multiple versions of the state to exist... but the graph shares a common shared state, so how can this be?\n", + "A common pattern in agents is to generate a list of objects, do some work on each of those objects, and then combine the results. This is very similar to the common [map-reduce](https://en.wikipedia.org/wiki/MapReduce) operation. This can be tricky for a few reasons. First, it can be tough to define a structured graph ahead of time because the length of the list of objects may be unknown. Second, in order to do this map-reduce you need multiple versions of the state to exist... but the graph shares a common shared state, so how can this be?\n", "\n", "LangGraph supports this via the `Send` api. This can be used to allow a conditional edge to `Send` multiple different states to multiple nodes. The state it sends can be different from the state of the core graph.\n", "\n", @@ -35,12 +35,14 @@ } ], "source": [ + "import operator\n", + "from typing import Annotated, TypedDict\n", + "\n", + "from langchain_core.pydantic_v1 import BaseModel\n", + "from langchain_openai import ChatOpenAI\n", + "\n", "from langgraph.constants import Send\n", "from langgraph.graph import END, StateGraph\n", - "import operator\n", - "from typing import TypedDict, Annotated\n", - "from langchain_openai import ChatOpenAI\n", - "from langchain_core.pydantic_v1 import BaseModel\n", "\n", "# Model and prompts\n", "# Define model and prompts we will use\n", @@ -67,6 +69,7 @@ "\n", "# Graph components: define the components that will make up the graph\n", "\n", + "\n", "# This will be the overall state of the main graph.\n", "# It will contain a topic (which we expect the user to provide)\n", "# and then will generate a list of subjects, and then a joke for\n", @@ -90,14 +93,14 @@ "\n", "# This is the function we will use to generate the subjects of the jokes\n", "def generate_topics(state: OverallState):\n", - " prompt = subjects_prompt.format(topic=state['topic'])\n", + " prompt = subjects_prompt.format(topic=state[\"topic\"])\n", " response = model.with_structured_output(Subjects).invoke(prompt)\n", " return {\"subjects\": response.subjects}\n", "\n", "\n", "# Here we generate a joke, given a subject\n", "def generate_joke(state: JokeState):\n", - " prompt = joke_prompt.format(subject=state['subject'])\n", + " prompt = joke_prompt.format(subject=state[\"subject\"])\n", " response = model.with_structured_output(Joke).invoke(prompt)\n", " return {\"jokes\": [response.joke]}\n", "\n", @@ -108,17 +111,15 @@ " # We will return a list of `Send` objects\n", " # Each `Send` object consists of the name of a node in the graph\n", " # as well as the state to send to that node\n", - " return [Send(\"generate_joke\", {\"subject\": s}) for s in state['subjects']]\n", + " return [Send(\"generate_joke\", {\"subject\": s}) for s in state[\"subjects\"]]\n", "\n", "\n", "# Here we will judge the best joke\n", "def best_joke(state: OverallState):\n", - " jokes = \"\\n\\n\".format(\"Joke {i}: {j}\" for i, j in enumerate(state['jokes']))\n", - " prompt = best_joke_prompt.format(topic=state['topic'], jokes=jokes)\n", + " jokes = \"\\n\\n\".format()\n", + " prompt = best_joke_prompt.format(topic=state[\"topic\"], jokes=jokes)\n", " response = model.with_structured_output(BestJoke).invoke(prompt)\n", - " return {\"best_selected_joke\": state['jokes'][response.id]}\n", - " \n", - "\n", + " return {\"best_selected_joke\": state[\"jokes\"][response.id]}\n", "\n", "\n", "# Construct the graph: here we put everything together to construct our graph\n", diff --git a/examples/multi_agent/agent_supervisor.ipynb b/examples/multi_agent/agent_supervisor.ipynb index 38746a70f..3b84dc8f1 100644 --- a/examples/multi_agent/agent_supervisor.ipynb +++ b/examples/multi_agent/agent_supervisor.ipynb @@ -27,8 +27,8 @@ "metadata": {}, "outputs": [], "source": [ - "# %%capture --no-stderr\n", - "# %pip install -U langchain langchain_openai langchain_experimental langsmith pandas" + "%%capture --no-stderr\n", + "%pip install -U langgraph langchain langchain_openai langchain_experimental langsmith pandas" ] }, { @@ -73,10 +73,9 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import Annotated, List, Tuple, Union\n", + "from typing import Annotated\n", "\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", - "from langchain_core.tools import tool\n", "from langchain_experimental.tools import PythonREPLTool\n", "\n", "tavily_tool = TavilySearchResults(max_results=5)\n", @@ -161,8 +160,8 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", "from langchain_core.output_parsers.openai_functions import JsonOutputFunctionsParser\n", + "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", "\n", "members = [\"Researcher\", \"Coder\"]\n", "system_prompt = (\n", @@ -231,12 +230,13 @@ "metadata": {}, "outputs": [], "source": [ - "import operator\n", - "from typing import Annotated, Any, Dict, List, Optional, Sequence, TypedDict\n", "import functools\n", + "import operator\n", + "from typing import Sequence, TypedDict\n", "\n", "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", - "from langgraph.graph import StateGraph, END\n", + "\n", + "from langgraph.graph import END, StateGraph\n", "\n", "\n", "# The agent state is the input to each node in the graph\n", diff --git a/examples/multi_agent/hierarchical_agent_teams.ipynb b/examples/multi_agent/hierarchical_agent_teams.ipynb index 9c1e5e551..bb9609df3 100644 --- a/examples/multi_agent/hierarchical_agent_teams.ipynb +++ b/examples/multi_agent/hierarchical_agent_teams.ipynb @@ -59,7 +59,6 @@ "source": [ "import getpass\n", "import os\n", - "import uuid\n", "\n", "\n", "def _set_if_undefined(var: str):\n", @@ -105,13 +104,11 @@ }, "outputs": [], "source": [ - "from typing import Annotated, List, Tuple, Union\n", + "from typing import Annotated, List\n", "\n", - "import matplotlib.pyplot as plt\n", "from langchain_community.document_loaders import WebBaseLoader\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", "from langchain_core.tools import tool\n", - "from langsmith import trace\n", "\n", "tavily_tool = TavilySearchResults(max_results=5)\n", "\n", @@ -236,7 +233,7 @@ "\n", "@tool\n", "def python_repl(\n", - " code: Annotated[str, \"The python code to execute to generate your chart.\"]\n", + " code: Annotated[str, \"The python code to execute to generate your chart.\"],\n", "):\n", " \"\"\"Use this to execute python code. If you want to see the output of a value,\n", " you should print it out with `print(...)`. This is visible to the user.\"\"\"\n", @@ -274,13 +271,11 @@ }, "outputs": [], "source": [ - "from typing import Any, Callable, List, Optional, TypedDict, Union\n", + "from typing import List, Optional\n", "\n", "from langchain.agents import AgentExecutor, create_openai_functions_agent\n", "from langchain.output_parsers.openai_functions import JsonOutputFunctionsParser\n", "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", - "from langchain_core.runnables import Runnable\n", - "from langchain_core.tools import BaseTool\n", "from langchain_openai import ChatOpenAI\n", "\n", "from langgraph.graph import END, StateGraph\n", @@ -383,9 +378,8 @@ "import functools\n", "import operator\n", "\n", - "from langchain_core.messages import AIMessage, BaseMessage, HumanMessage\n", + "from langchain_core.messages import BaseMessage, HumanMessage\n", "from langchain_openai.chat_models import ChatOpenAI\n", - "import functools\n", "\n", "\n", "# ResearchTeam graph state\n", @@ -586,7 +580,7 @@ " written_files = [\n", " f.relative_to(WORKING_DIRECTORY) for f in WORKING_DIRECTORY.rglob(\"*\")\n", " ]\n", - " except:\n", + " except Exception:\n", " pass\n", " if not written_files:\n", " return {**state, \"current_files\": \"No files written.\"}\n", @@ -785,10 +779,9 @@ }, "outputs": [], "source": [ - "from langchain_core.messages import AIMessage, BaseMessage, HumanMessage\n", + "from langchain_core.messages import BaseMessage\n", "from langchain_openai.chat_models import ChatOpenAI\n", "\n", - "\n", "llm = ChatOpenAI(model=\"gpt-4-1106-preview\")\n", "\n", "supervisor_node = create_team_supervisor(\n", diff --git a/examples/multi_agent/multi-agent-collaboration.ipynb b/examples/multi_agent/multi-agent-collaboration.ipynb index 1bef08bfd..04603d01c 100644 --- a/examples/multi_agent/multi-agent-collaboration.ipynb +++ b/examples/multi_agent/multi-agent-collaboration.ipynb @@ -77,10 +77,11 @@ "source": [ "from langchain_core.messages import (\n", " BaseMessage,\n", - " ToolMessage,\n", " HumanMessage,\n", + " ToolMessage,\n", ")\n", "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", + "\n", "from langgraph.graph import END, StateGraph\n", "\n", "\n", @@ -123,10 +124,11 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain_core.tools import tool\n", "from typing import Annotated\n", - "from langchain_experimental.utilities import PythonREPL\n", + "\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", + "from langchain_core.tools import tool\n", + "from langchain_experimental.utilities import PythonREPL\n", "\n", "tavily_tool = TavilySearchResults(max_results=5)\n", "\n", @@ -137,7 +139,7 @@ "\n", "@tool\n", "def python_repl(\n", - " code: Annotated[str, \"The python code to execute to generate your chart.\"]\n", + " code: Annotated[str, \"The python code to execute to generate your chart.\"],\n", "):\n", " \"\"\"Use this to execute python code. If you want to see the output of a value,\n", " you should print it out with `print(...)`. This is visible to the user.\"\"\"\n", @@ -182,7 +184,6 @@ "from typing import Annotated, Sequence, TypedDict\n", "\n", "from langchain_openai import ChatOpenAI\n", - "from typing_extensions import TypedDict\n", "\n", "\n", "# This defines the object that is passed between each node\n", @@ -210,6 +211,7 @@ "outputs": [], "source": [ "import functools\n", + "\n", "from langchain_core.messages import AIMessage\n", "\n", "\n", @@ -377,7 +379,7 @@ "\n", "try:\n", " display(Image(graph.get_graph(xray=True).draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] diff --git a/examples/pass-run-time-values-to-tools.ipynb b/examples/pass-run-time-values-to-tools.ipynb index 8e1a3a0c2..ebeae58ef 100644 --- a/examples/pass-run-time-values-to-tools.ipynb +++ b/examples/pass-run-time-values-to-tools.ipynb @@ -37,7 +37,7 @@ "outputs": [], "source": [ "%%capture --no-stderr\n", - "%pip install --quiet -U langchain langchain_openai" + "%pip install --quiet -U langgraph langchain langchain_openai" ] }, { @@ -55,10 +55,10 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", - "if 'OPENAI_API_KEY' not in os.environ:\n", + "if \"OPENAI_API_KEY\" not in os.environ:\n", " os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")" ] }, @@ -79,7 +79,7 @@ "source": [ "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", "\n", - "if 'LANGCHAIN_API_KEY' not in os.environ:\n", + "if \"LANGCHAIN_API_KEY\" not in os.environ:\n", " os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass(\"LangSmith API Key:\")" ] }, @@ -90,7 +90,7 @@ "source": [ "## Set up the tools\n", "\n", - "Here, we will make a function that dynamically creates 3 [custom-tools](https://python.langchain.com/docs/modules/agents/tools/custom_tools).\n", + "Here, we will make a function that dynamically creates 3 [custom-tools](https://python.langchain.com/v0.2/docs/how_to/custom_tools).\n", "\n", "This function will bind to the tools the correct `user_id`, allowing the LLM to only fill in the other relevant values. Importantly,\n", "the LLM will be **unaware** that a user ID even exists!" @@ -104,25 +104,27 @@ "outputs": [], "source": [ "from typing import List\n", - "from langchain_core.tools import tool, BaseTool\n", + "\n", + "from langchain_core.tools import BaseTool, tool\n", "\n", "# A global dict that the tools will be updating in this example.\n", "user_to_pets = {}\n", "\n", + "\n", "def generate_tools_for_user(user_id: str) -> List[BaseTool]:\n", " \"\"\"Generate a set of tools that have a user id associated with them.\"\"\"\n", - " \n", + "\n", " @tool\n", " def update_favorite_pets(pets: List[str]) -> None:\n", " \"\"\"Add the list of favorite pets.\"\"\"\n", " user_to_pets[user_id] = pets\n", - " \n", + "\n", " @tool\n", " def delete_favorite_pets() -> None:\n", " \"\"\"Delete the list of favorite pets.\"\"\"\n", " if user_id in user_to_pets:\n", " del user_to_pets[user_id]\n", - " \n", + "\n", " @tool\n", " def list_favorite_pets() -> None:\n", " \"\"\"List favorite pets if any.\"\"\"\n", @@ -186,8 +188,9 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import TypedDict, Annotated, Sequence\n", "import operator\n", + "from typing import Annotated, Sequence, TypedDict\n", + "\n", "from langchain_core.messages import BaseMessage\n", "\n", "\n", @@ -203,7 +206,7 @@ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -229,9 +232,9 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.prebuilt import ToolInvocation\n", "from langchain_core.messages import ToolMessage\n", - "from langgraph.prebuilt import ToolExecutor\n", + "\n", + "from langgraph.prebuilt import ToolExecutor, ToolInvocation\n", "\n", "\n", "# Define the function that determines whether to continue or not\n", @@ -249,7 +252,7 @@ "# Define the function that calls the model\n", "def call_model(state, config):\n", " messages = state[\"messages\"]\n", - " tools = generate_tools_for_user(config['user_id'])\n", + " tools = generate_tools_for_user(config[\"user_id\"])\n", " model_with_tools = model.bind_tools(tools)\n", " response = model_with_tools.invoke(messages)\n", " # We return a list, because this will get added to the existing list\n", @@ -275,7 +278,7 @@ " # We can now wrap these tools in a simple ToolExecutor.\n", " # This is a real simple class that takes in a ToolInvocation and calls that tool, returning the output.\n", " # A ToolInvocation is any class with `tool` and `tool_input` attribute.\n", - " tools = generate_tools_for_user(config['user_id'])\n", + " tools = generate_tools_for_user(config[\"user_id\"])\n", " tool_executor = ToolExecutor(tools)\n", " responses = tool_executor.batch(tool_invocations, return_exceptions=True)\n", " # We use the response to create tool messages\n", @@ -309,7 +312,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", + "from langgraph.graph import END, StateGraph\n", "\n", "# Define a new graph\n", "workflow = StateGraph(AgentState)\n", @@ -375,7 +378,7 @@ "\n", "try:\n", " display(Image(app.get_graph(xray=True).draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -388,7 +391,7 @@ "## Use it!\n", "\n", "We can now use it!\n", - "This now exposes the [same interface](https://python.langchain.com/docs/expression_language/) as all other LangChain runnables." + "This now exposes the [same interface](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel) as all other LangChain runnables." ] }, { @@ -427,12 +430,12 @@ "source": [ "from langchain_core.messages import HumanMessage\n", "\n", - "user_to_pets.clear() # Clear the state\n", + "user_to_pets.clear() # Clear the state\n", "\n", - "print(f'User information prior to run: {user_to_pets}')\n", + "print(f\"User information prior to run: {user_to_pets}\")\n", "\n", "inputs = {\"messages\": [HumanMessage(content=\"my favorite pets are cats and dogs\")]}\n", - "for output in app.stream(inputs, {'user_id': 'eugene'}):\n", + "for output in app.stream(inputs, {\"user_id\": \"eugene\"}):\n", " # stream() yields dictionaries with output keyed by node name\n", " for key, value in output.items():\n", " print(f\"Output from node '{key}':\")\n", @@ -440,7 +443,7 @@ " print(value)\n", " print(\"\\n---\\n\")\n", "\n", - "print(f'User information prior to run: {user_to_pets}')" + "print(f\"User information prior to run: {user_to_pets}\")" ] }, { @@ -477,11 +480,11 @@ } ], "source": [ - "print(f'User information prior to run: {user_to_pets}')\n", + "print(f\"User information prior to run: {user_to_pets}\")\n", "\n", "\n", "inputs = {\"messages\": [HumanMessage(content=\"what are my favorite pets?\")]}\n", - "for output in app.stream(inputs, {'user_id': 'eugene'}):\n", + "for output in app.stream(inputs, {\"user_id\": \"eugene\"}):\n", " # stream() yields dictionaries with output keyed by node name\n", " for key, value in output.items():\n", " print(f\"Output from node '{key}':\")\n", @@ -490,7 +493,7 @@ " print(\"\\n---\\n\")\n", "\n", "\n", - "print(f'User information prior to run: {user_to_pets}')" + "print(f\"User information prior to run: {user_to_pets}\")" ] }, { @@ -527,11 +530,15 @@ } ], "source": [ - "print(f'User information prior to run: {user_to_pets}')\n", + "print(f\"User information prior to run: {user_to_pets}\")\n", "\n", "\n", - "inputs = {\"messages\": [HumanMessage(content=\"please forget what i told you about my favorite animals\")]}\n", - "for output in app.stream(inputs, {'user_id': 'eugene'}):\n", + "inputs = {\n", + " \"messages\": [\n", + " HumanMessage(content=\"please forget what i told you about my favorite animals\")\n", + " ]\n", + "}\n", + "for output in app.stream(inputs, {\"user_id\": \"eugene\"}):\n", " # stream() yields dictionaries with output keyed by node name\n", " for key, value in output.items():\n", " print(f\"Output from node '{key}':\")\n", @@ -540,7 +547,7 @@ " print(\"\\n---\\n\")\n", "\n", "\n", - "print(f'User information prior to run: {user_to_pets}')" + "print(f\"User information prior to run: {user_to_pets}\")" ] } ], diff --git a/examples/persistence.ipynb b/examples/persistence.ipynb index c8f8bf913..32d7b2e5d 100644 --- a/examples/persistence.ipynb +++ b/examples/persistence.ipynb @@ -33,7 +33,7 @@ "
\n", "

Note

\n", "

\n", - " In this how-to, we will create our agent from scratch to be transparent (but verbose). You can accomplish similar functionality using the create_react_agent(model, tools=tool, checkpointer=checkpointer) (API doc) constructor. This may be more appropriate if you are used to LangChain’s AgentExecutor class.\n", + " In this how-to, we will create our agent from scratch to be transparent (but verbose). You can accomplish similar functionality using the create_react_agent(model, tools=tool, checkpointer=checkpointer) (API doc) constructor. This may be more appropriate if you are used to LangChain’s AgentExecutor class.\n", "

\n", "
" ] @@ -74,8 +74,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "\n", "def _set_env(var: str):\n", @@ -122,8 +122,10 @@ "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", "from typing import Annotated\n", + "\n", + "from typing_extensions import TypedDict\n", + "\n", "from langgraph.graph.message import add_messages\n", "\n", "# Add messages essentially does this with more\n", @@ -145,7 +147,7 @@ "\n", "We will first define the tools we want to use.\n", "For this simple example, we will use create a placeholder search engine.\n", - "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n" + "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that.\n" ] }, { @@ -200,7 +202,7 @@ "For the design below, it must satisfy two criteria:\n", "\n", "1. It should work with **messages** (since our state contains a list of chat messages)\n", - "2. It should work with [**tool calling**](https://python.langchain.com/v0.1/docs/modules/model_io/chat/function_calling/).\n", + "2. It should work with [**tool calling**](https://python.langchain.com/v0.2/docs/concepts/#functiontool-calling).\n", "\n", "
\n", "

Note

\n", @@ -253,7 +255,7 @@ "## Define the graph \n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -315,7 +317,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", + "from langgraph.graph import StateGraph\n", "\n", "# Define a new graph\n", "workflow = StateGraph(State)\n", @@ -399,7 +401,7 @@ "\n", "try:\n", " display(Image(app.get_graph().draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -566,7 +568,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.12.2" } }, "nbformat": 4, diff --git a/examples/plan-and-execute/plan-and-execute.ipynb b/examples/plan-and-execute/plan-and-execute.ipynb index 9ead0eac4..5f044c5eb 100644 --- a/examples/plan-and-execute/plan-and-execute.ipynb +++ b/examples/plan-and-execute/plan-and-execute.ipynb @@ -47,7 +47,7 @@ "outputs": [], "source": [ "%%capture --no-stderr\n", - "%pip install --quiet -U langchain-community langchain-openai tavily-python" + "%pip install --quiet -U langgraph langchain-community langchain-openai tavily-python" ] }, { @@ -65,8 +65,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "\n", "def _set_env(var: str):\n", @@ -105,7 +105,7 @@ "source": [ "## Define Tools\n", "\n", - "We will first define the tools we want to use. For this simple example, we will use a built-in search tool via Tavily. However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that." + "We will first define the tools we want to use. For this simple example, we will use a built-in search tool via Tavily. However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that." ] }, { @@ -154,6 +154,7 @@ "source": [ "from langchain import hub\n", "from langchain_openai import ChatOpenAI\n", + "\n", "from langgraph.prebuilt import create_react_agent\n", "\n", "# Get the prompt to use - you can modify this!\n", @@ -212,8 +213,8 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import List, Tuple, Annotated, TypedDict\n", "import operator\n", + "from typing import Annotated, List, Tuple, TypedDict\n", "\n", "\n", "class PlanExecute(TypedDict):\n", diff --git a/examples/rag/langgraph_adaptive_rag.ipynb b/examples/rag/langgraph_adaptive_rag.ipynb index 51326f9d8..c4da78907 100644 --- a/examples/rag/langgraph_adaptive_rag.ipynb +++ b/examples/rag/langgraph_adaptive_rag.ipynb @@ -60,9 +60,10 @@ "source": [ "### LLMs\n", "import os\n", - "os.environ['OPENAI_API_KEY'] = \n", - "os.environ['COHERE_API_KEY'] = \n", - "os.environ['TAVILY_API_KEY'] = " + "\n", + "os.environ[\"OPENAI_API_KEY\"] = \"\"\n", + "os.environ[\"COHERE_API_KEY\"] = \"\"\n", + "os.environ[\"TAVILY_API_KEY\"] = \"\"" ] }, { @@ -83,9 +84,9 @@ "outputs": [], "source": [ "### Tracing (optional)\n", - "os.environ['LANGCHAIN_TRACING_V2'] = 'true'\n", - "os.environ['LANGCHAIN_ENDPOINT'] = 'https://api.smith.langchain.com'\n", - "os.environ['LANGCHAIN_API_KEY'] = " + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "os.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\n", + "os.environ[\"LANGCHAIN_API_KEY\"] = \"\"" ] }, { @@ -475,9 +476,10 @@ "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", "from typing import List\n", "\n", + "from typing_extensions import TypedDict\n", + "\n", "\n", "class GraphState(TypedDict):\n", " \"\"\"\n", @@ -659,7 +661,7 @@ " \"\"\"\n", "\n", " print(\"---ASSESS GRADED DOCUMENTS---\")\n", - " question = state[\"question\"]\n", + " state[\"question\"]\n", " filtered_documents = state[\"documents\"]\n", "\n", " if not filtered_documents:\n", diff --git a/examples/rag/langgraph_adaptive_rag_cohere.ipynb b/examples/rag/langgraph_adaptive_rag_cohere.ipynb index 241db3f6a..a95fe0ab1 100644 --- a/examples/rag/langgraph_adaptive_rag_cohere.ipynb +++ b/examples/rag/langgraph_adaptive_rag_cohere.ipynb @@ -68,7 +68,8 @@ "source": [ "### LLMs\n", "import os\n", - "os.environ['COHERE_API_KEY'] = " + "\n", + "os.environ[\"COHERE_API_KEY\"] = \"\"" ] }, { @@ -83,7 +84,7 @@ "# ### Tracing (optional)\n", "# os.environ['LANGCHAIN_TRACING_V2'] = 'true'\n", "# os.environ['LANGCHAIN_ENDPOINT'] = 'https://api.smith.langchain.com'\n", - "# os.environ['LANGCHAIN_API_KEY'] = " + "# os.environ['LANGCHAIN_API_KEY'] =''" ] }, { @@ -108,9 +109,9 @@ "### Build Index\n", "\n", "from langchain.text_splitter import RecursiveCharacterTextSplitter\n", + "from langchain_cohere import CohereEmbeddings\n", "from langchain_community.document_loaders import WebBaseLoader\n", "from langchain_community.vectorstores import Chroma\n", - "from langchain_cohere import CohereEmbeddings\n", "\n", "# Set embeddings\n", "embd = CohereEmbeddings()\n", @@ -187,11 +188,10 @@ ], "source": [ "### Router\n", - "from typing import Literal\n", "\n", + "from langchain_cohere import ChatCohere\n", "from langchain_core.prompts import ChatPromptTemplate\n", "from langchain_core.pydantic_v1 import BaseModel, Field\n", - "from langchain_cohere import ChatCohere\n", "\n", "\n", "# Data model\n", @@ -329,11 +329,8 @@ "source": [ "### Generate\n", "\n", - "from langchain import hub\n", - "from langchain_core.output_parsers import StrOutputParser\n", - "import langchain\n", "from langchain_core.messages import HumanMessage\n", - "\n", + "from langchain_core.output_parsers import StrOutputParser\n", "\n", "# Preamble\n", "preamble = \"\"\"You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.\"\"\"\n", @@ -341,15 +338,18 @@ "# LLM\n", "llm = ChatCohere(model_name=\"command-r\", temperature=0).bind(preamble=preamble)\n", "\n", + "\n", "# Prompt\n", - "prompt = lambda x: ChatPromptTemplate.from_messages(\n", - " [\n", - " HumanMessage(\n", - " f\"Question: {x['question']} \\nAnswer: \",\n", - " additional_kwargs={\"documents\": x[\"documents\"]},\n", - " )\n", - " ]\n", - ")\n", + "def prompt(x):\n", + " return ChatPromptTemplate.from_messages(\n", + " [\n", + " HumanMessage(\n", + " f\"Question: {x['question']} \\nAnswer: \",\n", + " additional_kwargs={\"documents\": x[\"documents\"]},\n", + " )\n", + " ]\n", + " )\n", + "\n", "\n", "# Chain\n", "rag_chain = prompt | llm | StrOutputParser()\n", @@ -376,11 +376,7 @@ "source": [ "### LLM fallback\n", "\n", - "from langchain import hub\n", "from langchain_core.output_parsers import StrOutputParser\n", - "import langchain\n", - "from langchain_core.messages import HumanMessage\n", - "\n", "\n", "# Preamble\n", "preamble = \"\"\"You are an assistant for question-answering tasks. Answer the question based upon your knowledge. Use three sentences maximum and keep the answer concise.\"\"\"\n", @@ -388,10 +384,13 @@ "# LLM\n", "llm = ChatCohere(model_name=\"command-r\", temperature=0).bind(preamble=preamble)\n", "\n", + "\n", "# Prompt\n", - "prompt = lambda x: ChatPromptTemplate.from_messages(\n", - " [HumanMessage(f\"Question: {x['question']} \\nAnswer: \")]\n", - ")\n", + "def prompt(x):\n", + " return ChatPromptTemplate.from_messages(\n", + " [HumanMessage(f\"Question: {x['question']} \\nAnswer: \")]\n", + " )\n", + "\n", "\n", "# Chain\n", "llm_chain = prompt | llm | StrOutputParser()\n", @@ -535,7 +534,7 @@ "outputs": [], "source": [ "### Search\n", - "# os.environ['TAVILY_API_KEY'] = \n", + "# os.environ['TAVILY_API_KEY'] =''\n", "\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", "\n", @@ -565,9 +564,10 @@ }, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", "from typing import List\n", "\n", + "from typing_extensions import TypedDict\n", + "\n", "\n", "class GraphState(TypedDict):\n", " \"\"\"|\n", @@ -764,7 +764,7 @@ " \"\"\"\n", "\n", " print(\"---ASSESS GRADED DOCUMENTS---\")\n", - " question = state[\"question\"]\n", + " state[\"question\"]\n", " filtered_documents = state[\"documents\"]\n", "\n", " if not filtered_documents:\n", diff --git a/examples/rag/langgraph_adaptive_rag_local.ipynb b/examples/rag/langgraph_adaptive_rag_local.ipynb index 5f7570de1..d82c2198c 100644 --- a/examples/rag/langgraph_adaptive_rag_local.ipynb +++ b/examples/rag/langgraph_adaptive_rag_local.ipynb @@ -45,7 +45,8 @@ "metadata": {}, "outputs": [], "source": [ - "! pip install -U langchain-nomic langchain_community tiktoken langchainhub chromadb langchain langgraph tavily-python nomic[local]" + "%capture --no-stderr\n", + "%pip install -U langchain-nomic langchain_community tiktoken langchainhub chromadb langchain langgraph tavily-python nomic[local]" ] }, { @@ -100,9 +101,11 @@ "metadata": {}, "outputs": [], "source": [ - "os.environ['LANGCHAIN_TRACING_V2'] = 'true'\n", - "os.environ['LANGCHAIN_ENDPOINT'] = 'https://api.smith.langchain.com'\n", - "os.environ['LANGCHAIN_API_KEY'] = " + "import os\n", + "\n", + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "os.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\n", + "os.environ[\"LANGCHAIN_API_KEY\"] = \"\"" ] }, { @@ -122,8 +125,8 @@ "source": [ "from langchain.text_splitter import RecursiveCharacterTextSplitter\n", "from langchain_community.document_loaders import WebBaseLoader\n", - "from langchain_community.vectorstores import Chroma\n", "from langchain_nomic.embeddings import NomicEmbeddings\n", + "from langchain_community.vectorstores import Chroma\n", "\n", "urls = [\n", " \"https://lilianweng.github.io/posts/2023-06-23-agent/\",\n", @@ -440,9 +443,10 @@ "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", "from typing import List\n", "\n", + "from typing_extensions import TypedDict\n", + "\n", "\n", "class GraphState(TypedDict):\n", " \"\"\"\n", @@ -621,7 +625,7 @@ " \"\"\"\n", "\n", " print(\"---ASSESS GRADED DOCUMENTS---\")\n", - " question = state[\"question\"]\n", + " state[\"question\"]\n", " filtered_documents = state[\"documents\"]\n", "\n", " if not filtered_documents:\n", diff --git a/examples/rag/langgraph_agentic_rag.ipynb b/examples/rag/langgraph_agentic_rag.ipynb index 646a45cda..01049aa82 100644 --- a/examples/rag/langgraph_agentic_rag.ipynb +++ b/examples/rag/langgraph_agentic_rag.ipynb @@ -7,11 +7,11 @@ "source": [ "# LangGraph Retrieval Agent\n", "\n", - "[Retrieval Agents](https://python.langchain.com/docs/use_cases/question_answering/conversational_retrieval_agents) are useful when we want to make decisions about whether to retrieve from an index.\n", + "[Retrieval Agents](https://python.langchain.com/v0.2/docs/tutorials/qa_chat_history/#agents) are useful when we want to make decisions about whether to retrieve from an index.\n", "\n", "To implement a retrieval agent, we simple need to give an LLM access to a retriever tool.\n", "\n", - "We can incorporate this into [LangGraph](https://python.langchain.com/docs/langgraph)." + "We can incorporate this into [LangGraph](https://langchain-ai.github.io/langgraph/)." ] }, { @@ -32,8 +32,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "\n", "def _set_env(key: str):\n", @@ -116,11 +116,7 @@ " \"Search and return information about Lilian Weng blog posts on LLM agents, prompt engineering, and adversarial attacks on LLMs.\",\n", ")\n", "\n", - "tools = [retriever_tool]\n", - "\n", - "from langgraph.prebuilt import ToolExecutor\n", - "\n", - "tool_executor = ToolExecutor(tools)" + "tools = [retriever_tool]" ] }, { @@ -149,6 +145,7 @@ "from typing import Annotated, Sequence, TypedDict\n", "\n", "from langchain_core.messages import BaseMessage\n", + "\n", "from langgraph.graph.message import add_messages\n", "\n", "\n", @@ -204,11 +201,12 @@ "\n", "from langchain import hub\n", "from langchain_core.messages import BaseMessage, HumanMessage\n", - "from langchain_core.pydantic_v1 import BaseModel, Field\n", - "from langchain_openai import ChatOpenAI\n", - "from langgraph.prebuilt import tools_condition\n", "from langchain_core.output_parsers import StrOutputParser\n", "from langchain_core.prompts import PromptTemplate\n", + "from langchain_core.pydantic_v1 import BaseModel, Field\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "from langgraph.prebuilt import tools_condition\n", "\n", "### Edges\n", "\n", @@ -451,7 +449,7 @@ "\n", "try:\n", " display(Image(graph.get_graph(xray=True).draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -529,7 +527,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.12.2" } }, "nbformat": 4, diff --git a/examples/rag/langgraph_crag.ipynb b/examples/rag/langgraph_crag.ipynb index 75e20a8db..fbe536e6d 100644 --- a/examples/rag/langgraph_crag.ipynb +++ b/examples/rag/langgraph_crag.ipynb @@ -23,11 +23,11 @@ "* If all documents fall below the relevance threshold or if the grader is unsure, then the framework seeks an additional datasource\n", "* It will use web search to supplement retrieval\n", " \n", - "We will implement some of these ideas from scratch using [LangGraph](https://python.langchain.com/docs/langgraph):\n", + "We will implement some of these ideas from scratch using [LangGraph](https://langchain-ai.github.io/langgraph/):\n", "\n", "* Let's skip the knowledge refinement phase as a first pass. This can be added back as a node, if desired. \n", "* If *any* documents are irrelevant, let's opt to supplement retrieval with web search. \n", - "* We'll use [Tavily Search](https://python.langchain.com/docs/integrations/tools/tavily_search) for web search.\n", + "* We'll use [Tavily Search](https://python.langchain.com/v0.2/docs/integrations/tools/tavily_search/) for web search.\n", "* Let's use query re-writing to optimize the query for web search.\n", "\n", "![Screenshot 2024-04-01 at 9.28.30 AM.png](attachment:683fae34-980f-43f0-a9c2-9894bebd9157.png)" @@ -67,7 +67,8 @@ "outputs": [], "source": [ "import os\n", - "os.environ['OPENAI_API_KEY'] = " + "\n", + "os.environ[\"OPENAI_API_KEY\"] = \"\"" ] }, { @@ -87,7 +88,7 @@ "metadata": {}, "outputs": [], "source": [ - "os.environ['TAVILY_API_KEY'] = " + "os.environ[\"TAVILY_API_KEY\"] = \"\"" ] }, { @@ -107,9 +108,9 @@ "metadata": {}, "outputs": [], "source": [ - "os.environ['LANGCHAIN_TRACING_V2'] = 'true'\n", - "os.environ['LANGCHAIN_ENDPOINT'] = 'https://api.smith.langchain.com'\n", - "os.environ['LANGCHAIN_API_KEY'] = " + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "os.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\n", + "os.environ[\"LANGCHAIN_API_KEY\"] = \"\"" ] }, { @@ -182,9 +183,9 @@ "source": [ "### Retrieval Grader\n", "\n", - "from langchain_openai import ChatOpenAI\n", "from langchain_core.prompts import ChatPromptTemplate\n", "from langchain_core.pydantic_v1 import BaseModel, Field\n", + "from langchain_openai import ChatOpenAI\n", "\n", "\n", "# Data model\n", @@ -339,9 +340,10 @@ "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", "from typing import List\n", "\n", + "from typing_extensions import TypedDict\n", + "\n", "\n", "class GraphState(TypedDict):\n", " \"\"\"\n", @@ -499,9 +501,9 @@ " \"\"\"\n", "\n", " print(\"---ASSESS GRADED DOCUMENTS---\")\n", - " question = state[\"question\"]\n", + " state[\"question\"]\n", " web_search = state[\"web_search\"]\n", - " filtered_documents = state[\"documents\"]\n", + " state[\"documents\"]\n", "\n", " if web_search == \"Yes\":\n", " # All documents have been filtered check_relevance\n", diff --git a/examples/rag/langgraph_crag_local.ipynb b/examples/rag/langgraph_crag_local.ipynb index 98ca3343a..587f4fb38 100644 --- a/examples/rag/langgraph_crag_local.ipynb +++ b/examples/rag/langgraph_crag_local.ipynb @@ -23,11 +23,11 @@ "* If all documents fall below the relevance threshold or if the grader is unsure, then the framework seeks an additional datasource\n", "* It will use web search to supplement retrieval\n", " \n", - "We will implement some of these ideas from scratch using [LangGraph](https://python.langchain.com/docs/langgraph):\n", + "We will implement some of these ideas from scratch using [LangGraph](https://langchain-ai.github.io/langgraph/):\n", "\n", "* Let's skip the knowledge refinement phase as a first pass. This can be added back as a node, if desired. \n", "* If *any* documents are irrelevant, let's opt to supplement retrieval with web search. \n", - "* We'll use [Tavily Search](https://python.langchain.com/docs/integrations/tools/tavily_search) for web search.\n", + "* We'll use [Tavily Search](https://python.langchain.com/v0.2/docs/integrations/tools/tavily_search/) for web search.\n", "* Let's use query re-writing to optimize the query for web search.\n", "\n", "![Screenshot 2024-04-01 at 12.36.05 PM.png](attachment:d3ff129f-c0ff-4951-993c-efafc7f29dce.png)\n", @@ -99,7 +99,7 @@ "outputs": [], "source": [ "# If using Mistral API\n", - "mistral_api_key = " + "mistral_api_key = \"\"" ] }, { @@ -120,7 +120,8 @@ "outputs": [], "source": [ "import os\n", - "os.environ['TAVILY_API_KEY'] = " + "\n", + "os.environ[\"TAVILY_API_KEY\"] = \"\"" ] }, { @@ -140,9 +141,9 @@ "metadata": {}, "outputs": [], "source": [ - "os.environ['LANGCHAIN_TRACING_V2'] = 'true'\n", - "os.environ['LANGCHAIN_ENDPOINT'] = 'https://api.smith.langchain.com'\n", - "os.environ['LANGCHAIN_API_KEY'] = " + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "os.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\n", + "os.environ[\"LANGCHAIN_API_KEY\"] = \"\"" ] }, { @@ -183,10 +184,10 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain_community.document_loaders import WebBaseLoader\n", - "from langchain_community.vectorstores import Chroma\n", - "from langchain_nomic.embeddings import NomicEmbeddings\n", "from langchain.text_splitter import RecursiveCharacterTextSplitter\n", + "from langchain_community.document_loaders import WebBaseLoader\n", + "from langchain_nomic.embeddings import NomicEmbeddings\n", + "from langchain_community.vectorstores import Chroma\n", "from langchain_mistralai import MistralAIEmbeddings\n", "\n", "# Load\n", @@ -244,8 +245,8 @@ "\n", "from langchain.prompts import PromptTemplate\n", "from langchain_community.chat_models import ChatOllama\n", - "from langchain_mistralai.chat_models import ChatMistralAI\n", "from langchain_core.output_parsers import JsonOutputParser\n", + "from langchain_mistralai.chat_models import ChatMistralAI\n", "\n", "# LLM\n", "if run_local == \"Yes\":\n", @@ -399,9 +400,10 @@ "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", "from typing import List\n", "\n", + "from typing_extensions import TypedDict\n", + "\n", "\n", "class GraphState(TypedDict):\n", " \"\"\"\n", @@ -559,9 +561,9 @@ " \"\"\"\n", "\n", " print(\"---ASSESS GRADED DOCUMENTS---\")\n", - " question = state[\"question\"]\n", + " state[\"question\"]\n", " web_search = state[\"web_search\"]\n", - " filtered_documents = state[\"documents\"]\n", + " state[\"documents\"]\n", "\n", " if web_search == \"Yes\":\n", " # All documents have been filtered check_relevance\n", diff --git a/examples/rag/langgraph_rag_agent_llama3_local.ipynb b/examples/rag/langgraph_rag_agent_llama3_local.ipynb index 4f4a305d7..5dc54f0b6 100644 --- a/examples/rag/langgraph_rag_agent_llama3_local.ipynb +++ b/examples/rag/langgraph_rag_agent_llama3_local.ipynb @@ -187,7 +187,6 @@ "source": [ "### Generate\n", "\n", - "from langchain import hub\n", "from langchain_core.output_parsers import StrOutputParser\n", "from langchain_core.prompts import PromptTemplate\n", "\n", @@ -350,7 +349,6 @@ "outputs": [], "source": [ "### Search\n", - "\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", "\n", "web_search_tool = TavilySearchResults(k=3)" @@ -371,9 +369,13 @@ "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", + "from pprint import pprint\n", "from typing import List\n", + "\n", "from langchain_core.documents import Document\n", + "from typing_extensions import TypedDict\n", + "\n", + "from langgraph.graph import END, StateGraph\n", "\n", "### State\n", "\n", @@ -539,9 +541,9 @@ " \"\"\"\n", "\n", " print(\"---ASSESS GRADED DOCUMENTS---\")\n", - " question = state[\"question\"]\n", + " state[\"question\"]\n", " web_search = state[\"web_search\"]\n", - " filtered_documents = state[\"documents\"]\n", + " state[\"documents\"]\n", "\n", " if web_search == \"Yes\":\n", " # All documents have been filtered check_relevance\n", @@ -598,8 +600,6 @@ " return \"not supported\"\n", "\n", "\n", - "from langgraph.graph import END, StateGraph\n", - "\n", "workflow = StateGraph(GraphState)\n", "\n", "# Define the nodes\n", @@ -704,7 +704,6 @@ "app = workflow.compile()\n", "\n", "# Test\n", - "from pprint import pprint\n", "\n", "inputs = {\"question\": \"What are the types of agent memory?\"}\n", "for output in app.stream(inputs):\n", @@ -752,12 +751,10 @@ } ], "source": [ - "# Compile\n", - "app = workflow.compile()\n", - "\n", - "# Test\n", "from pprint import pprint\n", "\n", + "# Compile\n", + "app = workflow.compile()\n", "inputs = {\"question\": \"Who are the Bears expected to draft first in the NFL draft?\"}\n", "for output in app.stream(inputs):\n", " for key, value in output.items():\n", diff --git a/examples/rag/langgraph_self_rag.ipynb b/examples/rag/langgraph_self_rag.ipynb index a10fb5928..3f309a237 100644 --- a/examples/rag/langgraph_self_rag.ipynb +++ b/examples/rag/langgraph_self_rag.ipynb @@ -40,7 +40,7 @@ "* `y (generation)` is a useful response to `x (question)`.\n", "* Output: `{5, 4, 3, 2, 1}`\n", "\n", - "We will implement some of these ideas from scratch using [LangGraph](https://python.langchain.com/docs/langgraph).\n", + "We will implement some of these ideas from scratch using [LangGraph](https://langchain-ai.github.io/langgraph/).\n", "\n", "![Screenshot 2024-04-01 at 12.41.50 PM.png](attachment:15cba0ab-a549-4909-8373-fb761e384eff.png)" ] @@ -79,7 +79,8 @@ "outputs": [], "source": [ "import os\n", - "os.environ['OPENAI_API_KEY'] = " + "\n", + "os.environ[\"OPENAI_API_KEY\"] = \"\"" ] }, { @@ -99,9 +100,9 @@ "metadata": {}, "outputs": [], "source": [ - "os.environ['LANGCHAIN_TRACING_V2'] = 'true'\n", - "os.environ['LANGCHAIN_ENDPOINT'] = 'https://api.smith.langchain.com'\n", - "os.environ['LANGCHAIN_API_KEY'] = " + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "os.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\n", + "os.environ[\"LANGCHAIN_API_KEY\"] = \"\"" ] }, { @@ -182,7 +183,6 @@ "source": [ "### Retrieval Grader\n", "\n", - "from typing import Literal\n", "\n", "from langchain_core.prompts import ChatPromptTemplate\n", "from langchain_core.pydantic_v1 import BaseModel, Field\n", @@ -416,9 +416,10 @@ "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", "from typing import List\n", "\n", + "from typing_extensions import TypedDict\n", + "\n", "\n", "class GraphState(TypedDict):\n", " \"\"\"\n", @@ -444,8 +445,6 @@ "source": [ "### Nodes\n", "\n", - "from langchain.schema import Document\n", - "\n", "\n", "def retrieve(state):\n", " \"\"\"\n", @@ -550,7 +549,7 @@ " \"\"\"\n", "\n", " print(\"---ASSESS GRADED DOCUMENTS---\")\n", - " question = state[\"question\"]\n", + " state[\"question\"]\n", " filtered_documents = state[\"documents\"]\n", "\n", " if not filtered_documents:\n", diff --git a/examples/rag/langgraph_self_rag_local.ipynb b/examples/rag/langgraph_self_rag_local.ipynb index c6e6c6d36..7e70baf6f 100644 --- a/examples/rag/langgraph_self_rag_local.ipynb +++ b/examples/rag/langgraph_self_rag_local.ipynb @@ -40,7 +40,7 @@ "* `y (generation)` is a useful response to `x (question)`.\n", "* Output: `{5, 4, 3, 2, 1}`\n", "\n", - "We will implement some of these ideas from scratch using [LangGraph](https://python.langchain.com/docs/langgraph).\n", + "We will implement some of these ideas from scratch using [LangGraph](https://langchain-ai.github.io/langgraph/).\n", "\n", "![Screenshot 2024-04-01 at 12.42.59 PM.png](attachment:5fca0a3e-d13d-4bfa-95ea-58203640cc7a.png)" ] @@ -60,7 +60,8 @@ "metadata": {}, "outputs": [], "source": [ - "! pip install -U langchain-nomic langchain_community tiktoken langchainhub chromadb langchain langgraph nomic[local]" + "%capture --no-stderr\n", + "%pip install -U langchain-nomic langchain_community tiktoken langchainhub chromadb langchain langgraph nomic[local]" ] }, { @@ -115,9 +116,11 @@ "metadata": {}, "outputs": [], "source": [ - "os.environ['LANGCHAIN_TRACING_V2'] = 'true'\n", - "os.environ['LANGCHAIN_ENDPOINT'] = 'https://api.smith.langchain.com'\n", - "os.environ['LANGCHAIN_API_KEY'] = " + "import os\n", + "\n", + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "os.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\n", + "os.environ[\"LANGCHAIN_API_KEY\"] = \"\"" ] }, { @@ -139,8 +142,9 @@ "source": [ "from langchain.text_splitter import RecursiveCharacterTextSplitter\n", "from langchain_community.document_loaders import WebBaseLoader\n", - "from langchain_community.vectorstores import Chroma\n", + "from langchain_nomic.embeddings import NomicEmbeddings\n", + "from langchain_community.vectorstores import Chroma\n", "\n", "urls = [\n", " \"https://lilianweng.github.io/posts/2023-06-23-agent/\",\n", @@ -390,9 +394,10 @@ "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", "from typing import List\n", "\n", + "from typing_extensions import TypedDict\n", + "\n", "\n", "class GraphState(TypedDict):\n", " \"\"\"\n", @@ -418,8 +423,6 @@ "source": [ "### Nodes\n", "\n", - "from langchain.schema import Document\n", - "\n", "\n", "def retrieve(state):\n", " \"\"\"\n", @@ -524,7 +527,7 @@ " \"\"\"\n", "\n", " print(\"---ASSESS GRADED DOCUMENTS---\")\n", - " question = state[\"question\"]\n", + " state[\"question\"]\n", " filtered_documents = state[\"documents\"]\n", "\n", " if not filtered_documents:\n", diff --git a/examples/rag/langgraph_self_rag_pinecone_movies.ipynb b/examples/rag/langgraph_self_rag_pinecone_movies.ipynb index 99e194c97..a21c95c5c 100644 --- a/examples/rag/langgraph_self_rag_pinecone_movies.ipynb +++ b/examples/rag/langgraph_self_rag_pinecone_movies.ipynb @@ -231,7 +231,6 @@ "\n", "from langchain import hub\n", "from langchain_core.output_parsers import StrOutputParser\n", - "from langchain_core.runnables import RunnablePassthrough\n", "\n", "# Prompt\n", "prompt = hub.pull(\"rlm/rag-prompt\")\n", @@ -404,9 +403,10 @@ "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", "from typing import List\n", "\n", + "from typing_extensions import TypedDict\n", + "\n", "\n", "class GraphState(TypedDict):\n", " \"\"\"\n", @@ -543,7 +543,7 @@ " \"\"\"\n", "\n", " print(\"---ASSESS GRADED DOCUMENTS---\")\n", - " question = state[\"question\"]\n", + " state[\"question\"]\n", " filtered_documents = state[\"documents\"]\n", "\n", " if not filtered_documents:\n", diff --git a/examples/reflexion/reflexion.ipynb b/examples/reflexion/reflexion.ipynb index b0e8e45a6..2793ec514 100644 --- a/examples/reflexion/reflexion.ipynb +++ b/examples/reflexion/reflexion.ipynb @@ -133,10 +133,10 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", - "from langchain_core.pydantic_v1 import BaseModel, Field, ValidationError\n", "from langchain_core.messages import HumanMessage, ToolMessage\n", "from langchain_core.output_parsers.openai_tools import PydanticToolsParser\n", + "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", + "from langchain_core.pydantic_v1 import BaseModel, Field, ValidationError\n", "\n", "\n", "class Reflection(BaseModel):\n", @@ -346,6 +346,7 @@ "outputs": [], "source": [ "from langchain_core.tools import StructuredTool\n", + "\n", "from langgraph.prebuilt import ToolNode\n", "\n", "\n", @@ -381,8 +382,8 @@ "outputs": [], "source": [ "from typing import Literal\n", - "from langgraph.graph import END, MessageGraph\n", "\n", + "from langgraph.graph import END, MessageGraph\n", "\n", "MAX_ITERATIONS = 5\n", "builder = MessageGraph()\n", @@ -444,7 +445,7 @@ "\n", "try:\n", " display(Image(graph.get_graph().draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -608,7 +609,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.12.2" } }, "nbformat": 4, diff --git a/examples/respond-in-format.ipynb b/examples/respond-in-format.ipynb index d60e85ea0..a77a30791 100644 --- a/examples/respond-in-format.ipynb +++ b/examples/respond-in-format.ipynb @@ -50,8 +50,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "\n", "def _set_env(var: str):\n", @@ -106,8 +106,10 @@ "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", "from typing import Annotated\n", + "\n", + "from typing_extensions import TypedDict\n", + "\n", "from langgraph.graph.message import add_messages\n", "\n", "# Add messages essentially does this with more\n", @@ -129,7 +131,7 @@ "\n", "We will first define the tools we want to use.\n", "For this simple example, we will use create a placeholder search engine.\n", - "It is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n" + "It is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that.\n" ] }, { @@ -262,8 +264,9 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import TypedDict, Annotated, Sequence\n", "import operator\n", + "from typing import Annotated, Sequence, TypedDict\n", + "\n", "from langchain_core.messages import BaseMessage\n", "\n", "\n", @@ -279,7 +282,7 @@ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -414,7 +417,7 @@ "## Use it!\n", "\n", "We can now use it!\n", - "This now exposes the [same interface](https://python.langchain.com/docs/expression_language/) as all other LangChain runnables." + "This now exposes the [same interface](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel) as all other LangChain runnables." ] }, { @@ -497,7 +500,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.1" + "version": "3.12.2" } }, "nbformat": 4, diff --git a/examples/rewoo/rewoo.ipynb b/examples/rewoo/rewoo.ipynb index c668f7513..ee9228fe1 100644 --- a/examples/rewoo/rewoo.ipynb +++ b/examples/rewoo/rewoo.ipynb @@ -36,7 +36,7 @@ "\n", "## 0. Prerequisites\n", "\n", - "For this example, we will provide the agent with a Tavily search engine tool. You can get an API key [here](https://app.tavily.com/sign-in) or replace with a free tool option (e.g., [duck duck go search](https://python.langchain.com/docs/integrations/tools/ddg)).\n", + "For this example, we will provide the agent with a Tavily search engine tool. You can get an API key [here](https://app.tavily.com/sign-in) or replace with a free tool option (e.g., [duck duck go search](https://python.langchain.com/v0.2/docs/integrations/tools/ddg/)).\n", "\n", "To see the full langsmith trace, you can s" ] @@ -58,8 +58,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "\n", "def _set_if_undefined(var: str):\n", @@ -91,7 +91,7 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import TypedDict, List\n", + "from typing import List, TypedDict\n", "\n", "\n", "class ReWOO(TypedDict):\n", @@ -233,6 +233,7 @@ "outputs": [], "source": [ "import re\n", + "\n", "from langchain_core.prompts import ChatPromptTemplate\n", "\n", "# Regex to match expressions of the form E#... = ...[...]\n", @@ -383,7 +384,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", + "from langgraph.graph import END, StateGraph\n", "\n", "graph = StateGraph(ReWOO)\n", "graph.add_node(\"plan\", get_plan)\n", diff --git a/examples/self-discover/self-discover.ipynb b/examples/self-discover/self-discover.ipynb index 3c5f44b2c..f626c0d34 100644 --- a/examples/self-discover/self-discover.ipynb +++ b/examples/self-discover/self-discover.ipynb @@ -9,7 +9,10 @@ "\n", "An implementation of the [Self-Discover paper](https://arxiv.org/pdf/2402.03620.pdf).\n", "\n", - "Based on [this implementation from @catid](https://github.com/catid/self-discover/tree/main?tab=readme-ov-file)" + "Based on [this implementation from @catid](https://github.com/catid/self-discover/tree/main?tab=readme-ov-file)\n", + "\n", + "\n", + "## Define the prompts" ] }, { @@ -17,52 +20,12 @@ "execution_count": 1, "id": "a18d8f24-5d9a-45c5-9739-6f3c4ed6c9c9", "metadata": {}, - "outputs": [], - "source": [ - "from langchain_openai import ChatOpenAI" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "9f554045-6e79-42d3-be4b-835bbbd0b78c", - "metadata": {}, - "outputs": [], - "source": [ - "model = ChatOpenAI(temperature=0, model=\"gpt-4-turbo-preview\")" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "9e9925aa-638a-4862-823e-9803402b8f82", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain import hub\n", - "from langchain_core.prompts import PromptTemplate" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "c4cc5c8c-f6a5-42c7-9ed5-780d79b3b29a", - "metadata": {}, - "outputs": [], - "source": [ - "select_prompt = hub.pull(\"hwchase17/self-discovery-select\")" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "a5b53d29-f5b6-4f39-af97-bb6b133e1d18", - "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "Self-Discovery Select Prompt:\n", "Select several reasoning modules that are crucial to utilize in order to solve the given task:\n", "\n", "All reasoning module descriptions:\n", @@ -71,34 +34,8 @@ "Task: \u001b[33;1m\u001b[1;3m{task_description}\u001b[0m\n", "\n", "Select several modules are crucial for solving the task above:\n", - "\n" - ] - } - ], - "source": [ - "select_prompt.pretty_print()" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "id": "26eaa6bc-5202-4b22-9522-33f227c8eb55", - "metadata": {}, - "outputs": [], - "source": [ - "adapt_prompt = hub.pull(\"hwchase17/self-discovery-adapt\")" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "id": "dc30afb9-180d-417b-9935-f7ef166710b8", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ + "\n", + "Self-Discovery Select Response:\n", "Rephrase and specify each reasoning module so that it better helps solving the task:\n", "\n", "SELECTED module descriptions:\n", @@ -107,34 +44,8 @@ "Task: \u001b[33;1m\u001b[1;3m{task_description}\u001b[0m\n", "\n", "Adapt each reasoning module description to better solve the task:\n", - "\n" - ] - } - ], - "source": [ - "adapt_prompt.pretty_print()" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "id": "a93253a9-8f50-49dd-8815-c3927bae1905", - "metadata": {}, - "outputs": [], - "source": [ - "structured_prompt = hub.pull(\"hwchase17/self-discovery-structure\")" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "id": "8ea8dd78-4285-400b-83d2-c4a241903a79", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ + "\n", + "Self-Discovery Structured Prompt:\n", "Operationalize the reasoning modules into a step-by-step reasoning plan in JSON format:\n", "\n", "Here's an example:\n", @@ -159,34 +70,8 @@ "\n", "Implement a reasoning structure for solvers to follow step-by-step and arrive at correct answer.\n", "\n", - "Note: do NOT actually arrive at a conclusion in this pass. Your job is to generate a PLAN so that in the future you can fill it out and arrive at the correct conclusion for tasks like this\n" - ] - } - ], - "source": [ - "structured_prompt.pretty_print()" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "id": "f3d4d79d-f414-4588-b476-4a35b3ba6fbf", - "metadata": {}, - "outputs": [], - "source": [ - "reasoning_prompt = hub.pull(\"hwchase17/self-discovery-reasoning\")" - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "id": "23d1e32e-d12e-454a-8484-c08e250e3262", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ + "Note: do NOT actually arrive at a conclusion in this pass. Your job is to generate a PLAN so that in the future you can fill it out and arrive at the correct conclusion for tasks like this\n", + "Self-Discovery Structured Response:\n", "Follow the step-by-step reasoning plan in JSON to correctly solve the task. Fill in the values following the keys by reasoning specifically about the task given. Do not simply rephrase the keys.\n", " \n", "Reasoning Structure:\n", @@ -197,163 +82,113 @@ } ], "source": [ + "from langchain import hub\n", + "\n", + "select_prompt = hub.pull(\"hwchase17/self-discovery-select\")\n", + "print(\"Self-Discovery Select Prompt:\")\n", + "select_prompt.pretty_print()\n", + "print(\"Self-Discovery Select Response:\")\n", + "adapt_prompt = hub.pull(\"hwchase17/self-discovery-adapt\")\n", + "adapt_prompt.pretty_print()\n", + "structured_prompt = hub.pull(\"hwchase17/self-discovery-structure\")\n", + "print(\"Self-Discovery Structured Prompt:\")\n", + "structured_prompt.pretty_print()\n", + "reasoning_prompt = hub.pull(\"hwchase17/self-discovery-reasoning\")\n", + "print(\"Self-Discovery Structured Response:\")\n", "reasoning_prompt.pretty_print()" ] }, { - "cell_type": "code", - "execution_count": 42, - "id": "7b9af01d-da28-4785-b069-efea61905cfa", + "cell_type": "markdown", + "id": "bce1135e", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "PromptTemplate(input_variables=['reasoning_structure', 'task_instance'], template='Follow the step-by-step reasoning plan in JSON to correctly solve the task. Fill in the values following the keys by reasoning specifically about the task given. Do not simply rephrase the keys.\\n \\nReasoning Structure:\\n{reasoning_structure}\\n\\nTask: {task_instance}')" - ] - }, - "execution_count": 42, - "metadata": {}, - "output_type": "execute_result" - } - ], "source": [ - "reasoning_prompt" + "## Define the graph" ] }, { "cell_type": "code", - "execution_count": 43, - "id": "399bf160-e257-429f-b27e-66d4063f195f", - "metadata": {}, - "outputs": [], - "source": [ - "_prompt = \"\"\"Follow the step-by-step reasoning plan in JSON to correctly solve the task. Fill in the values following the keys by reasoning specifically about the task given. Do not simply rephrase the keys.\\n \\nReasoning Structure:\\n{reasoning_structure}\\n\\nTask: {task_description}\"\"\"\n", - "_prompt = PromptTemplate.from_template(_prompt)" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "id": "6e047e3a-b1c2-4a3b-abc7-eb84de6874e4", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'https://smith.langchain.com/hub/hwchase17/self-discovery-reasoning/48340707'" - ] - }, - "execution_count": 44, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "hub.push(\"hwchase17/self-discovery-reasoning\", _prompt)" - ] - }, - { - "cell_type": "code", - "execution_count": 72, - "id": "7f66d61e-6dcd-4462-b67b-29c5de56e238", + "execution_count": 2, + "id": "9f554045-6e79-42d3-be4b-835bbbd0b78c", "metadata": {}, "outputs": [], "source": [ + "from typing import Optional, TypedDict\n", + "\n", + "from langchain_core.output_parsers import StrOutputParser\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "from langgraph.graph import END, START, StateGraph\n", + "\n", + "\n", "class SelfDiscoverState(TypedDict):\n", " reasoning_modules: str\n", " task_description: str\n", " selected_modules: Optional[str]\n", " adapted_modules: Optional[str]\n", " reasoning_structure: Optional[str]\n", - " answer: Optional[str]" - ] - }, - { - "cell_type": "code", - "execution_count": 73, - "id": "5c3bd203-7dc1-457e-813f-283aaf059ec0", - "metadata": {}, - "outputs": [], - "source": [ + " answer: Optional[str]\n", + "\n", + "\n", + "model = ChatOpenAI(temperature=0, model=\"gpt-4-turbo-preview\")\n", + "\n", + "\n", "def select(inputs):\n", " select_chain = select_prompt | model | StrOutputParser()\n", - " return {\"selected_modules\": select_chain.invoke(inputs)}" - ] - }, - { - "cell_type": "code", - "execution_count": 74, - "id": "86420da0-7cc2-4659-853e-9c3ef808e47c", - "metadata": {}, - "outputs": [], - "source": [ + " return {\"selected_modules\": select_chain.invoke(inputs)}\n", + "\n", + "\n", "def adapt(inputs):\n", " adapt_chain = adapt_prompt | model | StrOutputParser()\n", - " return {\"adapted_modules\": adapt_chain.invoke(inputs)}" - ] - }, - { - "cell_type": "code", - "execution_count": 75, - "id": "270a3905-58a3-4650-96ca-e8254040285f", - "metadata": {}, - "outputs": [], - "source": [ + " return {\"adapted_modules\": adapt_chain.invoke(inputs)}\n", + "\n", + "\n", "def structure(inputs):\n", " structure_chain = structured_prompt | model | StrOutputParser()\n", - " return {\"reasoning_structure\": structure_chain.invoke(inputs)}" - ] - }, - { - "cell_type": "code", - "execution_count": 76, - "id": "55b486cc-36be-497e-9eba-9c8dc228f2d1", - "metadata": {}, - "outputs": [], - "source": [ + " return {\"reasoning_structure\": structure_chain.invoke(inputs)}\n", + "\n", + "\n", "def reason(inputs):\n", " reasoning_chain = reasoning_prompt | model | StrOutputParser()\n", - " return {\"answer\": reasoning_chain.invoke(inputs)}" - ] - }, - { - "cell_type": "code", - "execution_count": 77, - "id": "d9e2e4ce-dc9d-45a5-a218-34ab4fb62bc4", - "metadata": {}, - "outputs": [], - "source": [ - "from langgraph.graph import StateGraph, END\n", - "from typing import TypedDict, Optional" - ] - }, - { - "cell_type": "code", - "execution_count": 79, - "id": "26276404-0134-40a0-a796-cc9927c153ee", - "metadata": {}, - "outputs": [], - "source": [ + " return {\"answer\": reasoning_chain.invoke(inputs)}\n", + "\n", + "\n", "graph = StateGraph(SelfDiscoverState)\n", - "graph.add_node(\"select\", select)\n", - "graph.add_node(\"adapt\", adapt)\n", - "graph.add_node(\"structure\", structure)\n", - "graph.add_node(\"reason\", reason)\n", + "graph.add_node(select)\n", + "graph.add_node(adapt)\n", + "graph.add_node(structure)\n", + "graph.add_node(reason)\n", + "graph.add_edge(START, \"select\")\n", "graph.add_edge(\"select\", \"adapt\")\n", "graph.add_edge(\"adapt\", \"structure\")\n", "graph.add_edge(\"structure\", \"reason\")\n", "graph.add_edge(\"reason\", END)\n", - "graph.set_entry_point(\"select\")\n", "app = graph.compile()" ] }, { - "cell_type": "code", - "execution_count": 80, + "cell_type": "markdown", "id": "29fe385b-cf5d-4581-80e7-55462f5628bb", "metadata": {}, - "outputs": [], + "source": [ + "## Invoke the graph" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "6cbfbe81-f751-42da-843a-f9003ace663d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'select': {'selected_modules': 'To solve the task of identifying the shape drawn by the SVG path element, the following reasoning modules are crucial:\\n\\n1. **Critical Thinking (10):** This involves analyzing the provided SVG path commands to understand how they contribute to forming a shape. It requires questioning assumptions (e.g., not assuming the shape is simple or common) and evaluating the information given in the path data.\\n\\n2. **Creative Thinking (11):** While the task seems straightforward, creative thinking can help in visualizing the shape described by the path commands without immediately drawing it. This involves imagining the transitions and connections between the points defined in the path.\\n\\n3. **Systems Thinking (13):** Understanding the SVG path as a system of coordinates and lines that connect to form a shape. This includes recognizing the interconnectedness of the start and end points of each line segment and how they contribute to the overall shape.\\n\\n4. **Analytical Problem Solving (29):** This task requires data analysis skills to interpret the SVG path commands and deduce the shape they form. Analyzing the coordinates and the movements (lines and moves) can reveal the structure of the shape.\\n\\n5. **Design Challenge (30):** Interpreting and visualizing SVG paths can be seen as a design challenge, requiring an understanding of how individual parts (line segments) come together to create a whole (shape).\\n\\n6. **Step-by-Step Planning and Implementation (39):** Formulating a plan to sequentially interpret each segment of the SVG path and understanding how each segment contributes to the overall shape. This could involve sketching the path based on the commands to better visualize the shape.\\n\\nThese modules collectively enable a comprehensive approach to solving the task, from understanding and analyzing the SVG path data to creatively and systematically deducing the shape it represents.'}}\n", + "{'adapt': {'adapted_modules': \"To enhance the process of identifying the shape drawn by the SVG path element, the reasoning modules can be adapted and specified as follows:\\n\\n1. **Enhanced Critical Analysis (10):** This module focuses on a detailed examination of the SVG path commands, challenging initial perceptions and critically assessing each command's role in shaping the figure. It involves a deep dive into the syntax and semantics of the path data, ensuring no detail is overlooked, especially in recognizing less obvious or complex shapes.\\n\\n2. **Visual Creative Thinking (11):** Leveraging imagination to mentally construct the shape from the path commands, this module emphasizes the ability to visualize the sequential flow and connection of points without physical drawing. It encourages innovative approaches to mentally piecing together the described shape, enhancing the ability to predict the outcome based on abstract data.\\n\\n3. **Integrated Systems Analysis (13):** This module treats the SVG path as a complex system where each command and coordinate plays a critical role in the final shape. It focuses on understanding the relationship between individual path segments and their collective contribution to forming a coherent structure, emphasizing the holistic view of the path's construction.\\n\\n4. **Targeted Analytical Problem Solving (29):** Specializing in dissecting the SVG path's commands to systematically uncover the represented shape, this module applies precise analytical techniques to decode the sequence of movements and coordinates. It involves a methodical breakdown of the path data to reveal the underlying geometric figure.\\n\\n5. **Design Synthesis Challenge (30):** Approaching the task as a problem of synthesizing a coherent design from segmented inputs, this module requires an adept understanding of how discrete line segments interconnect to form a unified shape. It challenges one to think like a designer, piecing together the puzzle of path commands into a complete and recognizable form.\\n\\n6. **Sequential Interpretation and Visualization (39):** This module involves developing a step-by-step strategy for interpreting and visualizing the SVG path, focusing on the incremental construction of the shape from the path commands. It advocates for a systematic approach to translating the abstract commands into a tangible visual representation, potentially through sketching or mentally mapping the path's progression.\\n\\nBy refining these modules, the approach to solving the task becomes more targeted, enhancing the ability to accurately identify the shape described by the SVG path element.\"}}\n" + ] + } + ], "source": [ "reasoning_modules = [\n", " \"1. How could I devise an experiment to help solve that problem?\",\n", @@ -402,38 +237,10 @@ "\n", "task_example = \"\"\"This SVG path element draws a:\n", - "(A) circle (B) heptagon (C) hexagon (D) kite (E) line (F) octagon (G) pentagon(H) rectangle (I) sector (J) triangle\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": 81, - "id": "6cbfbe81-f751-42da-843a-f9003ace663d", - "metadata": {}, - "outputs": [], - "source": [ - "reasoning_modules_str = \"\\n\".join(reasoning_modules)" - ] - }, - { - "cell_type": "code", - "execution_count": 82, - "id": "d411c7aa-7017-4d67-88b5-43b5d161c34c", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'select': {'selected_modules': \"To solve the task of identifying the shape drawn by the given SVG path element, the following reasoning modules are crucial:\\n\\n1. **Critical Thinking (10)**: This involves analyzing the SVG path commands and coordinates logically to understand the shape they form. It requires questioning assumptions (e.g., not assuming the shape based on a quick glance at the coordinates but rather analyzing the path commands) and evaluating the information given in the SVG path data.\\n\\n2. **Simplification (4)**: Simplifying the problem by breaking down the SVG path commands can make it easier to visualize and understand the shape being drawn. This might involve sketching the path based on the commands and coordinates or using a tool to render the SVG path.\\n\\n3. **Systems Thinking (13)**: Understanding the SVG path as part of a larger system (in this case, the SVG coordinate system and how path commands work) helps in comprehending how the individual commands come together to form a complete shape.\\n\\n4. **Analytical Problem Solving (29)**: This task requires data analysis skills to interpret the SVG path commands and coordinates. Understanding how 'M' (moveto), 'L' (lineto), and other commands work is essential for determining the shape.\\n\\n5. **Creative Thinking (11)**: While not as directly applicable as the other modules, creative thinking can aid in visualizing the shape that the path commands are intended to draw, especially if the shape is complex or if the path commands are not immediately clear.\\n\\n6. **Visualization (30)**: Although not explicitly listed, a module focused on visualization would be highly relevant here. Visualizing the path that the 'M' and 'L' commands create from the given coordinates can directly lead to identifying the shape.\\n\\nGiven the task's nature, modules focused on experimentation, risk analysis, stakeholder perspectives, and long-term implications (e.g., 1, 14, 21, 8) are less relevant. The task is primarily analytical and technical, requiring an understanding of SVG path syntax and geometry rather than broader problem-solving or decision-making strategies.\"}}\n", - "{'adapt': {'adapted_modules': \"1. **Detailed Path Analysis (10)**: This module focuses on a thorough examination of the SVG path commands and their corresponding coordinates to accurately deduce the shape they outline. It involves a critical approach where assumptions are set aside in favor of a detailed analysis of each command (e.g., 'M' for moveto, 'L' for lineto) and how these commands connect points in the SVG coordinate system to form a specific shape.\\n\\n2. **Path Decomposition (4)**: This involves breaking down the SVG path into more manageable segments or components to facilitate a clearer understanding of the overall shape. Techniques might include manually sketching the path as described by the commands and coordinates or utilizing digital tools to render the SVG path, thereby making the shape more apparent and easier to identify.\\n\\n3. **SVG System Analysis (13)**: Emphasizes the importance of understanding the SVG coordinate system and the functionality of path commands within this framework. This module is about seeing the SVG path not just as a series of commands but as part of the broader system of SVG graphics, where each command plays a specific role in shaping the final image.\\n\\n4. **Command Interpretation and Geometry (29)**: This module requires a deep dive into the syntax and semantics of SVG path commands, coupled with geometric reasoning to interpret the shape formed by these commands. Knowledge of how different commands like 'M' (moveto) and 'L' (lineto) contribute to the construction of geometric shapes is crucial for accurately identifying the shape in question.\\n\\n5. **Imaginative Visualization (11)**: While analytical skills are paramount, this module recognizes the role of creative thinking in visualizing the potential shapes that complex or ambiguous path commands might represent. It encourages thinking beyond the obvious and considering multiple geometric possibilities that fit the given path data.\\n\\n6. **Explicit Visualization (30)**: Directly focuses on the ability to visualize the trajectory formed by executing the SVG path commands, particularly 'M' and 'L'. This module is about using visualization techniques, whether mental or through software tools, to trace the path and see the resulting shape, thereby facilitating its identification.\\n\\nBy refining these modules to more directly address the task of interpreting SVG path elements, the process of identifying the drawn shape becomes more structured and focused on the specific skills and knowledge areas that are most relevant to the task.\"}}\n", - "{'structure': {'reasoning_structure': '```json\\n{\\n \"Step 1: Detailed Path Analysis\": {\\n \"Description\": \"Examine each SVG path command and its coordinates to understand the shape outline.\",\\n \"Actions\": [\\n {\\n \"Command\": \"M\",\\n \"Coordinate\": \"55.57,80.69\",\\n \"Purpose\": \"Move to starting point without drawing.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"57.38,65.80\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"M\",\\n \"Coordinate\": \"57.38,65.80\",\\n \"Purpose\": \"Move to this point without drawing.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"48.90,57.46\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"M\",\\n \"Coordinate\": \"48.90,57.46\",\\n \"Purpose\": \"Move to this point without drawing.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"45.58,47.78\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"M\",\\n \"Coordinate\": \"45.58,47.78\",\\n \"Purpose\": \"Move to this point without drawing.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"53.25,36.07\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"66.29,48.90\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"78.69,61.09\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"55.57,80.69\",\\n \"Purpose\": \"Draw line to this point to close the shape.\"\\n }\\n ]\\n },\\n \"Step 2: Path Decomposition\": {\\n \"Description\": \"Break down the path into segments to simplify analysis.\",\\n \"Segments\": [\\n \"Segment 1: Move from (55.57,80.69) to (57.38,65.80)\",\\n \"Segment 2: Move from (57.38,65.80) to (48.90,57.46)\",\\n \"Segment 3: Move from (48.90,57.46) to (45.58,47.78)\",\\n \"Segment 4: Move from (45.58,47.78) to (53.25,36.07)\",\\n \"Segment 5: Move from (53.25,36.07) to (66.29,48.90)\",\\n \"Segment 6: Move from (66.29,48.90) to (78.69,61.09)\",\\n \"Segment 7: Move from (78.69,61.09) to (55.57,80.69)\"\\n ]\\n },\\n \"Step 3: SVG System Analysis\": {\\n \"Description\": \"Understand the role of each command within the SVG coordinate system.\",\\n \"Analysis\": [\\n {\\n \"Command\": \"M\",\\n \"Role\": \"Defines starting points for new sub-paths.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Role\": \"Creates straight lines between points.\"\\n }\\n ]\\n },\\n \"Step 4: Command Interpretation and Geometry\": {\\n \"Description\": \"Interpret the geometric shape formed by the path commands.\",\\n \"Geometric Principles\": [\\n \"Identify angles and lines created by \\'L\\' commands.\",\\n \"Determine the number of sides from the number of \\'L\\' commands.\"\\n ]\\n },\\n \"Step 5: Imaginative Visualization\": {\\n \"Description\": \"Visualize potential shapes that the path commands might represent.\",\\n \"Visualization Techniques\": [\\n \"Sketching the path based on command coordinates.\",\\n \"Mentally visualizing the path progression.\"\\n ]\\n },\\n \"Step 6: Explicit Visualization\": {\\n \"Description\": \"Use visualization tools to trace the path and see the resulting shape.\",\\n \"Tools\": [\\n \"Digital drawing software\",\\n \"SVG rendering tools\"\\n ]\\n },\\n \"Conclusion\": {\\n \"Description\": \"Based on the analysis and visualization, identify the shape.\",\\n \"Options\": [\\n \"Circle\",\\n \"Heptagon\",\\n \"Hexagon\",\\n \"Kite\",\\n \"Line\",\\n \"Octagon\",\\n \"Pentagon\",\\n \"Rectangle\",\\n \"Sector\",\\n \"Triangle\"\\n ],\\n \"Selected Option\": \"\"\\n }\\n}\\n```'}}\n", - "{'reason': {'answer': '```json\\n{\\n \"Step 1: Detailed Path Analysis\": {\\n \"Description\": \"Examine each SVG path command and its coordinates to understand the shape outline.\",\\n \"Actions\": [\\n {\\n \"Command\": \"M\",\\n \"Coordinate\": \"55.57,80.69\",\\n \"Purpose\": \"Move to starting point without drawing.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"57.38,65.80\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"M\",\\n \"Coordinate\": \"57.38,65.80\",\\n \"Purpose\": \"Move to this point without drawing.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"48.90,57.46\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"M\",\\n \"Coordinate\": \"48.90,57.46\",\\n \"Purpose\": \"Move to this point without drawing.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"45.58,47.78\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"M\",\\n \"Coordinate\": \"45.58,47.78\",\\n \"Purpose\": \"Move to this point without drawing.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"53.25,36.07\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"66.29,48.90\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"78.69,61.09\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"55.57,80.69\",\\n \"Purpose\": \"Draw line to this point to close the shape.\"\\n }\\n ]\\n },\\n \"Step 2: Path Decomposition\": {\\n \"Description\": \"Break down the path into segments to simplify analysis.\",\\n \"Segments\": [\\n \"Segment 1: Move from (55.57,80.69) to (57.38,65.80)\",\\n \"Segment 2: Move from (57.38,65.80) to (48.90,57.46)\",\\n \"Segment 3: Move from (48.90,57.46) to (45.58,47.78)\",\\n \"Segment 4: Move from (45.58,47.78) to (53.25,36.07)\",\\n \"Segment 5: Move from (53.25,36.07) to (66.29,48.90)\",\\n \"Segment 6: Move from (66.29,48.90) to (78.69,61.09)\",\\n \"Segment 7: Move from (78.69,61.09) to (55.57,80.69)\"\\n ]\\n },\\n \"Step 3: SVG System Analysis\": {\\n \"Description\": \"Understand the role of each command within the SVG coordinate system.\",\\n \"Analysis\": [\\n {\\n \"Command\": \"M\",\\n \"Role\": \"Defines starting points for new sub-paths.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Role\": \"Creates straight lines between points.\"\\n }\\n ]\\n },\\n \"Step 4: Command Interpretation and Geometry\": {\\n \"Description\": \"Interpret the geometric shape formed by the path commands.\",\\n \"Geometric Principles\": [\\n \"Identify angles and lines created by \\'L\\' commands.\",\\n \"Determine the number of sides from the number of \\'L\\' commands.\"\\n ]\\n },\\n \"Step 5: Imaginative Visualization\": {\\n \"Description\": \"Visualize potential shapes that the path commands might represent.\",\\n \"Visualization Techniques\": [\\n \"Sketching the path based on command coordinates.\",\\n \"Mentally visualizing the path progression.\"\\n ]\\n },\\n \"Step 6: Explicit Visualization\": {\\n \"Description\": \"Use visualization tools to trace the path and see the resulting shape.\",\\n \"Tools\": [\\n \"Digital drawing software\",\\n \"SVG rendering tools\"\\n ]\\n },\\n \"Conclusion\": {\\n \"Description\": \"Based on the analysis and visualization, identify the shape.\",\\n \"Options\": [\\n \"Circle\",\\n \"Heptagon\",\\n \"Hexagon\",\\n \"Kite\",\\n \"Line\",\\n \"Octagon\",\\n \"Pentagon\",\\n \"Rectangle\",\\n \"Sector\",\\n \"Triangle\"\\n ],\\n \"Selected Option\": \"Pentagon\"\\n }\\n}\\n```'}}\n", - "{'__end__': {'reasoning_modules': '1. How could I devise an experiment to help solve that problem?\\n2. Make a list of ideas for solving this problem, and apply them one by one to the problem to see if any progress can be made.\\n4. How can I simplify the problem so that it is easier to solve?\\n5. What are the key assumptions underlying this problem?\\n6. What are the potential risks and drawbacks of each solution?\\n7. What are the alternative perspectives or viewpoints on this problem?\\n8. What are the long-term implications of this problem and its solutions?\\n9. How can I break down this problem into smaller, more manageable parts?\\n10. Critical Thinking: This style involves analyzing the problem from different perspectives, questioning assumptions, and evaluating the evidence or information available. It focuses on logical reasoning, evidence-based decision-making, and identifying potential biases or flaws in thinking.\\n11. Try creative thinking, generate innovative and out-of-the-box ideas to solve the problem. Explore unconventional solutions, thinking beyond traditional boundaries, and encouraging imagination and originality.\\n13. Use systems thinking: Consider the problem as part of a larger system and understanding the interconnectedness of various elements. Focuses on identifying the underlying causes, feedback loops, and interdependencies that influence the problem, and developing holistic solutions that address the system as a whole.\\n14. Use Risk Analysis: Evaluate potential risks, uncertainties, and tradeoffs associated with different solutions or approaches to a problem. Emphasize assessing the potential consequences and likelihood of success or failure, and making informed decisions based on a balanced analysis of risks and benefits.\\n16. What is the core issue or problem that needs to be addressed?\\n17. What are the underlying causes or factors contributing to the problem?\\n18. Are there any potential solutions or strategies that have been tried before? If yes, what were the outcomes and lessons learned?\\n19. What are the potential obstacles or challenges that might arise in solving this problem?\\n20. Are there any relevant data or information that can provide insights into the problem? If yes, what data sources are available, and how can they be analyzed?\\n21. Are there any stakeholders or individuals who are directly affected by the problem? What are their perspectives and needs?\\n22. What resources (financial, human, technological, etc.) are needed to tackle the problem effectively?\\n23. How can progress or success in solving the problem be measured or evaluated?\\n24. What indicators or metrics can be used?\\n25. Is the problem a technical or practical one that requires a specific expertise or skill set? Or is it more of a conceptual or theoretical problem?\\n26. Does the problem involve a physical constraint, such as limited resources, infrastructure, or space?\\n27. Is the problem related to human behavior, such as a social, cultural, or psychological issue?\\n28. Does the problem involve decision-making or planning, where choices need to be made under uncertainty or with competing objectives?\\n29. Is the problem an analytical one that requires data analysis, modeling, or optimization techniques?\\n30. Is the problem a design challenge that requires creative solutions and innovation?\\n31. Does the problem require addressing systemic or structural issues rather than just individual instances?\\n32. Is the problem time-sensitive or urgent, requiring immediate attention and action?\\n33. What kinds of solution typically are produced for this kind of problem specification?\\n34. Given the problem specification and the current best solution, have a guess about other possible solutions.35. Let’s imagine the current best solution is totally wrong, what other ways are there to think about the problem specification?36. What is the best way to modify this current best solution, given what you know about these kinds of problem specification?37. Ignoring the current best solution, create an entirely new solution to the problem.39. Let’s make a step by step plan and implement it with good notation and explanation.', 'task_description': 'This SVG path element draws a:\\n(A) circle (B) heptagon (C) hexagon (D) kite (E) line (F) octagon (G) pentagon(H) rectangle (I) sector (J) triangle', 'selected_modules': \"To solve the task of identifying the shape drawn by the given SVG path element, the following reasoning modules are crucial:\\n\\n1. **Critical Thinking (10)**: This involves analyzing the SVG path commands and coordinates logically to understand the shape they form. It requires questioning assumptions (e.g., not assuming the shape based on a quick glance at the coordinates but rather analyzing the path commands) and evaluating the information given in the SVG path data.\\n\\n2. **Simplification (4)**: Simplifying the problem by breaking down the SVG path commands can make it easier to visualize and understand the shape being drawn. This might involve sketching the path based on the commands and coordinates or using a tool to render the SVG path.\\n\\n3. **Systems Thinking (13)**: Understanding the SVG path as part of a larger system (in this case, the SVG coordinate system and how path commands work) helps in comprehending how the individual commands come together to form a complete shape.\\n\\n4. **Analytical Problem Solving (29)**: This task requires data analysis skills to interpret the SVG path commands and coordinates. Understanding how 'M' (moveto), 'L' (lineto), and other commands work is essential for determining the shape.\\n\\n5. **Creative Thinking (11)**: While not as directly applicable as the other modules, creative thinking can aid in visualizing the shape that the path commands are intended to draw, especially if the shape is complex or if the path commands are not immediately clear.\\n\\n6. **Visualization (30)**: Although not explicitly listed, a module focused on visualization would be highly relevant here. Visualizing the path that the 'M' and 'L' commands create from the given coordinates can directly lead to identifying the shape.\\n\\nGiven the task's nature, modules focused on experimentation, risk analysis, stakeholder perspectives, and long-term implications (e.g., 1, 14, 21, 8) are less relevant. The task is primarily analytical and technical, requiring an understanding of SVG path syntax and geometry rather than broader problem-solving or decision-making strategies.\", 'adapted_modules': \"1. **Detailed Path Analysis (10)**: This module focuses on a thorough examination of the SVG path commands and their corresponding coordinates to accurately deduce the shape they outline. It involves a critical approach where assumptions are set aside in favor of a detailed analysis of each command (e.g., 'M' for moveto, 'L' for lineto) and how these commands connect points in the SVG coordinate system to form a specific shape.\\n\\n2. **Path Decomposition (4)**: This involves breaking down the SVG path into more manageable segments or components to facilitate a clearer understanding of the overall shape. Techniques might include manually sketching the path as described by the commands and coordinates or utilizing digital tools to render the SVG path, thereby making the shape more apparent and easier to identify.\\n\\n3. **SVG System Analysis (13)**: Emphasizes the importance of understanding the SVG coordinate system and the functionality of path commands within this framework. This module is about seeing the SVG path not just as a series of commands but as part of the broader system of SVG graphics, where each command plays a specific role in shaping the final image.\\n\\n4. **Command Interpretation and Geometry (29)**: This module requires a deep dive into the syntax and semantics of SVG path commands, coupled with geometric reasoning to interpret the shape formed by these commands. Knowledge of how different commands like 'M' (moveto) and 'L' (lineto) contribute to the construction of geometric shapes is crucial for accurately identifying the shape in question.\\n\\n5. **Imaginative Visualization (11)**: While analytical skills are paramount, this module recognizes the role of creative thinking in visualizing the potential shapes that complex or ambiguous path commands might represent. It encourages thinking beyond the obvious and considering multiple geometric possibilities that fit the given path data.\\n\\n6. **Explicit Visualization (30)**: Directly focuses on the ability to visualize the trajectory formed by executing the SVG path commands, particularly 'M' and 'L'. This module is about using visualization techniques, whether mental or through software tools, to trace the path and see the resulting shape, thereby facilitating its identification.\\n\\nBy refining these modules to more directly address the task of interpreting SVG path elements, the process of identifying the drawn shape becomes more structured and focused on the specific skills and knowledge areas that are most relevant to the task.\", 'reasoning_structure': '```json\\n{\\n \"Step 1: Detailed Path Analysis\": {\\n \"Description\": \"Examine each SVG path command and its coordinates to understand the shape outline.\",\\n \"Actions\": [\\n {\\n \"Command\": \"M\",\\n \"Coordinate\": \"55.57,80.69\",\\n \"Purpose\": \"Move to starting point without drawing.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"57.38,65.80\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"M\",\\n \"Coordinate\": \"57.38,65.80\",\\n \"Purpose\": \"Move to this point without drawing.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"48.90,57.46\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"M\",\\n \"Coordinate\": \"48.90,57.46\",\\n \"Purpose\": \"Move to this point without drawing.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"45.58,47.78\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"M\",\\n \"Coordinate\": \"45.58,47.78\",\\n \"Purpose\": \"Move to this point without drawing.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"53.25,36.07\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"66.29,48.90\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"78.69,61.09\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"55.57,80.69\",\\n \"Purpose\": \"Draw line to this point to close the shape.\"\\n }\\n ]\\n },\\n \"Step 2: Path Decomposition\": {\\n \"Description\": \"Break down the path into segments to simplify analysis.\",\\n \"Segments\": [\\n \"Segment 1: Move from (55.57,80.69) to (57.38,65.80)\",\\n \"Segment 2: Move from (57.38,65.80) to (48.90,57.46)\",\\n \"Segment 3: Move from (48.90,57.46) to (45.58,47.78)\",\\n \"Segment 4: Move from (45.58,47.78) to (53.25,36.07)\",\\n \"Segment 5: Move from (53.25,36.07) to (66.29,48.90)\",\\n \"Segment 6: Move from (66.29,48.90) to (78.69,61.09)\",\\n \"Segment 7: Move from (78.69,61.09) to (55.57,80.69)\"\\n ]\\n },\\n \"Step 3: SVG System Analysis\": {\\n \"Description\": \"Understand the role of each command within the SVG coordinate system.\",\\n \"Analysis\": [\\n {\\n \"Command\": \"M\",\\n \"Role\": \"Defines starting points for new sub-paths.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Role\": \"Creates straight lines between points.\"\\n }\\n ]\\n },\\n \"Step 4: Command Interpretation and Geometry\": {\\n \"Description\": \"Interpret the geometric shape formed by the path commands.\",\\n \"Geometric Principles\": [\\n \"Identify angles and lines created by \\'L\\' commands.\",\\n \"Determine the number of sides from the number of \\'L\\' commands.\"\\n ]\\n },\\n \"Step 5: Imaginative Visualization\": {\\n \"Description\": \"Visualize potential shapes that the path commands might represent.\",\\n \"Visualization Techniques\": [\\n \"Sketching the path based on command coordinates.\",\\n \"Mentally visualizing the path progression.\"\\n ]\\n },\\n \"Step 6: Explicit Visualization\": {\\n \"Description\": \"Use visualization tools to trace the path and see the resulting shape.\",\\n \"Tools\": [\\n \"Digital drawing software\",\\n \"SVG rendering tools\"\\n ]\\n },\\n \"Conclusion\": {\\n \"Description\": \"Based on the analysis and visualization, identify the shape.\",\\n \"Options\": [\\n \"Circle\",\\n \"Heptagon\",\\n \"Hexagon\",\\n \"Kite\",\\n \"Line\",\\n \"Octagon\",\\n \"Pentagon\",\\n \"Rectangle\",\\n \"Sector\",\\n \"Triangle\"\\n ],\\n \"Selected Option\": \"\"\\n }\\n}\\n```', 'answer': '```json\\n{\\n \"Step 1: Detailed Path Analysis\": {\\n \"Description\": \"Examine each SVG path command and its coordinates to understand the shape outline.\",\\n \"Actions\": [\\n {\\n \"Command\": \"M\",\\n \"Coordinate\": \"55.57,80.69\",\\n \"Purpose\": \"Move to starting point without drawing.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"57.38,65.80\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"M\",\\n \"Coordinate\": \"57.38,65.80\",\\n \"Purpose\": \"Move to this point without drawing.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"48.90,57.46\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"M\",\\n \"Coordinate\": \"48.90,57.46\",\\n \"Purpose\": \"Move to this point without drawing.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"45.58,47.78\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"M\",\\n \"Coordinate\": \"45.58,47.78\",\\n \"Purpose\": \"Move to this point without drawing.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"53.25,36.07\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"66.29,48.90\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"78.69,61.09\",\\n \"Purpose\": \"Draw line to this point.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Coordinate\": \"55.57,80.69\",\\n \"Purpose\": \"Draw line to this point to close the shape.\"\\n }\\n ]\\n },\\n \"Step 2: Path Decomposition\": {\\n \"Description\": \"Break down the path into segments to simplify analysis.\",\\n \"Segments\": [\\n \"Segment 1: Move from (55.57,80.69) to (57.38,65.80)\",\\n \"Segment 2: Move from (57.38,65.80) to (48.90,57.46)\",\\n \"Segment 3: Move from (48.90,57.46) to (45.58,47.78)\",\\n \"Segment 4: Move from (45.58,47.78) to (53.25,36.07)\",\\n \"Segment 5: Move from (53.25,36.07) to (66.29,48.90)\",\\n \"Segment 6: Move from (66.29,48.90) to (78.69,61.09)\",\\n \"Segment 7: Move from (78.69,61.09) to (55.57,80.69)\"\\n ]\\n },\\n \"Step 3: SVG System Analysis\": {\\n \"Description\": \"Understand the role of each command within the SVG coordinate system.\",\\n \"Analysis\": [\\n {\\n \"Command\": \"M\",\\n \"Role\": \"Defines starting points for new sub-paths.\"\\n },\\n {\\n \"Command\": \"L\",\\n \"Role\": \"Creates straight lines between points.\"\\n }\\n ]\\n },\\n \"Step 4: Command Interpretation and Geometry\": {\\n \"Description\": \"Interpret the geometric shape formed by the path commands.\",\\n \"Geometric Principles\": [\\n \"Identify angles and lines created by \\'L\\' commands.\",\\n \"Determine the number of sides from the number of \\'L\\' commands.\"\\n ]\\n },\\n \"Step 5: Imaginative Visualization\": {\\n \"Description\": \"Visualize potential shapes that the path commands might represent.\",\\n \"Visualization Techniques\": [\\n \"Sketching the path based on command coordinates.\",\\n \"Mentally visualizing the path progression.\"\\n ]\\n },\\n \"Step 6: Explicit Visualization\": {\\n \"Description\": \"Use visualization tools to trace the path and see the resulting shape.\",\\n \"Tools\": [\\n \"Digital drawing software\",\\n \"SVG rendering tools\"\\n ]\\n },\\n \"Conclusion\": {\\n \"Description\": \"Based on the analysis and visualization, identify the shape.\",\\n \"Options\": [\\n \"Circle\",\\n \"Heptagon\",\\n \"Hexagon\",\\n \"Kite\",\\n \"Line\",\\n \"Octagon\",\\n \"Pentagon\",\\n \"Rectangle\",\\n \"Sector\",\\n \"Triangle\"\\n ],\\n \"Selected Option\": \"Pentagon\"\\n }\\n}\\n```'}}\n" - ] - } - ], - "source": [ + "(A) circle (B) heptagon (C) hexagon (D) kite (E) line (F) octagon (G) pentagon(H) rectangle (I) sector (J) triangle\"\"\"\n", + "\n", + "reasoning_modules_str = \"\\n\".join(reasoning_modules)\n", + "\n", "for s in app.stream(\n", " {\"task_description\": task_example, \"reasoning_modules\": reasoning_modules_str}\n", "):\n", @@ -443,15 +250,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ea8568d5-bdb6-45cd-8d04-1ab305786caa", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c14a291c-7c1b-43bc-807e-11180290985e", + "id": "20cac598", "metadata": {}, "outputs": [], "source": [] @@ -473,7 +272,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.1" + "version": "3.12.2" } }, "nbformat": 4, diff --git a/examples/state-model.ipynb b/examples/state-model.ipynb index 01f993d6c..5a1bca9fe 100644 --- a/examples/state-model.ipynb +++ b/examples/state-model.ipynb @@ -52,8 +52,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "\n", "def _set_env(var: str):\n", @@ -92,7 +92,7 @@ "\n", "We will first define the tools we want to use.\n", "For this simple example, we will use create a placeholder search engine.\n", - "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n" + "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that.\n" ] }, { @@ -212,8 +212,9 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import Annotated, Sequence\n", "import operator\n", + "from typing import Annotated, Sequence\n", + "\n", "from langchain_core.messages import BaseMessage\n", "from langchain_core.pydantic_v1 import BaseModel\n", "\n", @@ -230,7 +231,7 @@ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -260,9 +261,10 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.prebuilt import ToolInvocation\n", "from langchain_core.messages import ToolMessage\n", "\n", + "from langgraph.prebuilt import ToolInvocation\n", + "\n", "\n", "# Define the function that determines whether to continue or not\n", "def should_continue(state):\n", @@ -323,7 +325,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", + "from langgraph.graph import END, StateGraph\n", "\n", "# Define a new graph\n", "workflow = StateGraph(AgentState)\n", @@ -398,7 +400,7 @@ "## Use it!\n", "\n", "We can now use it!\n", - "This now exposes the [same interface](https://python.langchain.com/docs/expression_language/) as all other LangChain runnables." + "This now exposes the [same interface](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel) as all other LangChain runnables." ] }, { diff --git a/examples/storm/storm.ipynb b/examples/storm/storm.ipynb index 32c060627..5d99703bb 100644 --- a/examples/storm/storm.ipynb +++ b/examples/storm/storm.ipynb @@ -48,9 +48,10 @@ "metadata": {}, "outputs": [], "source": [ - "# %pip install -U langchain_community langchain_openai langgraph wikipedia scikit-learn langchain_fireworks\n", + "%%capture --no-stderr\n", + "%pip install -U langchain_community langchain_openai langgraph wikipedia scikit-learn langchain_fireworks\n", "# We use one or the other search engine below\n", - "# %pip install -U duckduckgo tavily-python" + "%pip install -U duckduckgo tavily-python" ] }, { @@ -71,8 +72,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "\n", "def _set_env(var: str):\n", @@ -104,7 +105,6 @@ "outputs": [], "source": [ "from langchain_openai import ChatOpenAI\n", - "from langchain_fireworks import ChatFireworks\n", "\n", "fast_llm = ChatOpenAI(model=\"gpt-3.5-turbo\")\n", "# Uncomment for a Fireworks model\n", @@ -137,9 +137,10 @@ } ], "source": [ - "from langchain_core.pydantic_v1 import BaseModel, Field\n", "from typing import List, Optional\n", + "\n", "from langchain_core.prompts import ChatPromptTemplate\n", + "from langchain_core.pydantic_v1 import BaseModel, Field\n", "\n", "direct_gen_outline_prompt = ChatPromptTemplate.from_messages(\n", " [\n", @@ -360,7 +361,8 @@ "outputs": [], "source": [ "from langchain_community.retrievers import WikipediaRetriever\n", - "from langchain_core.runnables import RunnableLambda, chain as as_runnable\n", + "from langchain_core.runnables import RunnableLambda\n", + "from langchain_core.runnables import chain as as_runnable\n", "\n", "wikipedia_retriever = WikipediaRetriever(load_all_available_meta=True, top_k_results=1)\n", "\n", @@ -455,10 +457,12 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", - "from typing_extensions import TypedDict\n", + "from typing import Annotated\n", + "\n", "from langchain_core.messages import AnyMessage\n", - "from typing import Annotated, Sequence\n", + "from typing_extensions import TypedDict\n", + "\n", + "from langgraph.graph import END, StateGraph\n", "\n", "\n", "def add_messages(left, right):\n", @@ -504,9 +508,8 @@ "metadata": {}, "outputs": [], "source": [ + "from langchain_core.messages import AIMessage, HumanMessage, ToolMessage\n", "from langchain_core.prompts import MessagesPlaceholder\n", - "from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, ToolMessage\n", - "\n", "\n", "gen_qn_prompt = ChatPromptTemplate.from_messages(\n", " [\n", @@ -692,7 +695,6 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain_community.tools.tavily_search import TavilySearchResults\n", "from langchain_community.utilities.duckduckgo_search import DuckDuckGoSearchAPIWrapper\n", "from langchain_core.tools import tool\n", "\n", @@ -724,9 +726,10 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain_core.runnables import RunnableConfig\n", "import json\n", "\n", + "from langchain_core.runnables import RunnableConfig\n", + "\n", "\n", "async def gen_answer(\n", " state: InterviewState,\n", @@ -1047,9 +1050,8 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain_core.documents import Document\n", - "\n", "from langchain_community.vectorstores import SKLearnVectorStore\n", + "from langchain_core.documents import Document\n", "from langchain_openai import OpenAIEmbeddings\n", "\n", "embeddings = OpenAIEmbeddings(model=\"text-embedding-3-small\")\n", @@ -1565,7 +1567,7 @@ " {\n", " \"topic\": \"Groq, NVIDIA, Llamma.cpp and the future of LLM Inference\",\n", " },\n", - " config\n", + " config,\n", "):\n", " name = next(iter(step))\n", " print(name)\n", diff --git a/examples/streaming-tokens.ipynb b/examples/streaming-tokens.ipynb index bed3dbe4e..28e786648 100644 --- a/examples/streaming-tokens.ipynb +++ b/examples/streaming-tokens.ipynb @@ -14,7 +14,7 @@ "
\n", "

Note

\n", "

\n", - " In this how-to, we will create our agent from scratch to be transparent (but verbose). You can accomplish similar functionality using the create_react_agent(model, tools=tool) (API doc) constructor. This may be more appropriate if you are used to LangChain’s AgentExecutor class.\n", + " In this how-to, we will create our agent from scratch to be transparent (but verbose). You can accomplish similar functionality using the create_react_agent(model, tools=tool) (API doc) constructor. This may be more appropriate if you are used to LangChain’s AgentExecutor class.\n", "

\n", "
\n", "\n", @@ -63,8 +63,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "\n", "def _set_env(var: str):\n", @@ -119,8 +119,10 @@ "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", "from typing import Annotated\n", + "\n", + "from typing_extensions import TypedDict\n", + "\n", "from langgraph.graph.message import add_messages\n", "\n", "# Add messages essentially does this with more\n", @@ -142,7 +144,7 @@ "\n", "We will first define the tools we want to use.\n", "For this simple example, we will use create a placeholder search engine.\n", - "It is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n" + "It is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that.\n" ] }, { @@ -242,7 +244,7 @@ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -278,10 +280,12 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END, START\n", - "from langchain_core.runnables import RunnableConfig\n", "from typing import Literal\n", "\n", + "from langchain_core.runnables import RunnableConfig\n", + "\n", + "from langgraph.graph import END, START, StateGraph\n", + "\n", "\n", "# Define the function that determines whether to continue or not\n", "def should_continue(state: State) -> Literal[\"__end__\", \"tools\"]:\n", @@ -370,7 +374,6 @@ "source": [ "from IPython.display import Image, display\n", "\n", - "\n", "display(Image(app.get_graph().draw_mermaid_png()))" ] }, @@ -458,18 +461,31 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END, START\n", - "from langchain_core.runnables import RunnableGenerator\n", "from langchain_core.messages import AIMessage\n", + "from langchain_core.runnables import RunnableGenerator\n", + "\n", + "from langgraph.graph import START, StateGraph\n", "\n", "# Define a new graph\n", "workflow = StateGraph(State)\n", "\n", + "\n", "async def my_generator(state: State):\n", - " messages = [\"Four\", \"score\", \"and\", \"seven\", \"years\", \"ago\", \"our\", \"fathers\", \"...\"]\n", + " messages = [\n", + " \"Four\",\n", + " \"score\",\n", + " \"and\",\n", + " \"seven\",\n", + " \"years\",\n", + " \"ago\",\n", + " \"our\",\n", + " \"fathers\",\n", + " \"...\",\n", + " ]\n", " for message in messages:\n", " yield message\n", "\n", + "\n", "async def my_node(state: State, config: RunnableConfig):\n", " messages = []\n", " # Tagging a node makes it easy to filter out which events to include in your stream\n", diff --git a/examples/subgraph.ipynb b/examples/subgraph.ipynb index 826ac2c06..8df91b956 100644 --- a/examples/subgraph.ipynb +++ b/examples/subgraph.ipynb @@ -40,8 +40,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "\n", "def _set_env(var: str):\n", @@ -70,10 +70,12 @@ "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", - "from langgraph.graph import StateGraph\n", "from typing import Annotated\n", "\n", + "from typing_extensions import TypedDict\n", + "\n", + "from langgraph.graph import StateGraph\n", + "\n", "\n", "def reduce_list(left: list | None, right: list | None) -> list:\n", " if not left:\n", diff --git a/examples/time-travel.ipynb b/examples/time-travel.ipynb index 4cc6a70c6..598623a9e 100644 --- a/examples/time-travel.ipynb +++ b/examples/time-travel.ipynb @@ -25,7 +25,7 @@ "
\n", "

Note:

\n", "

\n", - " In this how-to, we will create our agent from scratch to be transparent (but verbose). You can accomplish similar functionality using the create_react_agent(model, tools=tool, checkpointer=checkpointer) (API doc) constructor. This may be more appropriate if you are used to LangChain’s AgentExecutor class.\n", + " In this how-to, we will create our agent from scratch to be transparent (but verbose). You can accomplish similar functionality using the create_react_agent(model, tools=tool, checkpointer=checkpointer) (API doc) constructor. This may be more appropriate if you are used to LangChain’s AgentExecutor class.\n", "

\n", "
" ] @@ -66,8 +66,8 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import getpass\n", + "import os\n", "\n", "\n", "def _set_env(var: str):\n", @@ -114,8 +114,10 @@ "metadata": {}, "outputs": [], "source": [ - "from typing_extensions import TypedDict\n", "from typing import Annotated\n", + "\n", + "from typing_extensions import TypedDict\n", + "\n", "from langgraph.graph.message import add_messages\n", "\n", "# `add_messages`` essentially does this\n", @@ -137,7 +139,7 @@ "\n", "We will first define the tools we want to use.\n", "For this simple example, we will use create a placeholder search engine.\n", - "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n" + "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that.\n" ] }, { @@ -237,7 +239,7 @@ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", - "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", @@ -294,7 +296,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langgraph.graph import StateGraph, END\n", + "from langgraph.graph import END, StateGraph\n", "\n", "# Define a new graph\n", "workflow = StateGraph(State)\n", @@ -403,7 +405,7 @@ "\n", "try:\n", " display(Image(app.get_graph().draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -1112,7 +1114,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.12.2" } }, "nbformat": 4, diff --git a/examples/tutorials/sql-agent-diagram.png b/examples/tutorials/sql-agent-diagram.png new file mode 100644 index 000000000..9c8d8a27c Binary files /dev/null and b/examples/tutorials/sql-agent-diagram.png differ diff --git a/examples/tutorials/sql-agent.ipynb b/examples/tutorials/sql-agent.ipynb new file mode 100644 index 000000000..c59e5071a --- /dev/null +++ b/examples/tutorials/sql-agent.ipynb @@ -0,0 +1,1032 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "9f853e403eabd4f8", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "# An agent for interacting with a SQL database\n", + "\n", + "In this tutorial, we will walk through how to build an agent that can answer questions about a SQL database. \n", + "\n", + "At a high level, the agent will:\n", + "1. Fetch the available tables from the database\n", + "2. Decide which tables are relevant to the question\n", + "3. Fetch the DDL for the relevant tables\n", + "4. Generate a query based on the question and information from the DDL\n", + "5. Double-check the query for common mistakes using an LLM\n", + "6. Execute the query and return the results\n", + "7. Correct mistakes surfaced by the database engine until the query is successful\n", + "8. Formulate a response based on the results\n", + "\n", + "The end-to-end workflow will look something like below:\n", + "\n", + "![](sql-agent-diagram.png)" + ] + }, + { + "cell_type": "markdown", + "id": "b5a87813ffe7e4d2", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "## Set up environment\n", + "\n", + "We'll set up our environment variables for OpenAI, and optionally, to enable tracing with [LangSmith](https://smith.langchain.com)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "6c05a600f1afb5b6", + "metadata": { + "ExecuteTime": { + "end_time": "2024-06-12T21:24:00.532147Z", + "start_time": "2024-06-12T21:24:00.526043Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "os.environ[\"OPENAI_API_KEY\"] = \"sk-...\"\n", + "os.environ[\"LANGSMITH_API_KEY\"] = \"lsv2_pt_...\"\n", + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "04d73c39-1cc9-4b94-a454-b0a4f604713c", + "metadata": {}, + "outputs": [], + "source": [ + "os.environ[\"LANGCHAIN_PROJECT\"] = \"sql-agent\"" + ] + }, + { + "cell_type": "markdown", + "id": "877d8c85825089d8", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "## Configure the database\n", + "\n", + "We will be creating a SQLite database for this tutorial. SQLite is a lightweight database that is easy to set up and use. We will be loading the `chinook` database, which is a sample database that represents a digital media store.\n", + "Find more information about the database [here](https://www.sqlitetutorial.net/sqlite-sample-database/).\n", + "\n", + "For convenience, we have hosted the database (`Chinook.db`) on a public GCS bucket." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "64b0bf1b14c2e902", + "metadata": { + "ExecuteTime": { + "end_time": "2024-06-12T21:24:09.918436Z", + "start_time": "2024-06-12T21:24:09.608563Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "File downloaded and saved as Chinook.db\n" + ] + } + ], + "source": [ + "import requests\n", + "\n", + "url = \"https://storage.googleapis.com/benchmarks-artifacts/chinook/Chinook.db\"\n", + "\n", + "response = requests.get(url)\n", + "\n", + "if response.status_code == 200:\n", + " # Open a local file in binary write mode\n", + " with open(\"Chinook.db\", \"wb\") as file:\n", + " # Write the content of the response (the file) to the local file\n", + " file.write(response.content)\n", + " print(\"File downloaded and saved as Chinook.db\")\n", + "else:\n", + " print(f\"Failed to download the file. Status code: {response.status_code}\")" + ] + }, + { + "cell_type": "markdown", + "id": "61c8304aa5ceb6a5", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "We will use a handy SQL database wrapper available in the `langchain_community` package to interact with the database. The wrapper provides a simple interface to execute SQL queries and fetch results. We will also use the `langchain_openai` package to interact with the OpenAI API for language models later in the tutorial." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "a60191bd3489f278", + "metadata": { + "ExecuteTime": { + "end_time": "2024-06-12T21:24:14.663745Z", + "start_time": "2024-06-12T21:24:13.527958Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "%%capture --no-stderr --no-display\n", + "!pip install langchain_community langchain_openai" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "1f1e1f4f86ed54", + "metadata": { + "ExecuteTime": { + "end_time": "2024-06-12T21:24:15.891582Z", + "start_time": "2024-06-12T21:24:15.289782Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sqlite\n", + "['Album', 'Artist', 'Customer', 'Employee', 'Genre', 'Invoice', 'InvoiceLine', 'MediaType', 'Playlist', 'PlaylistTrack', 'Track']\n" + ] + }, + { + "data": { + "text/plain": [ + "\"[(1, 'AC/DC'), (2, 'Accept'), (3, 'Aerosmith'), (4, 'Alanis Morissette'), (5, 'Alice In Chains'), (6, 'Antônio Carlos Jobim'), (7, 'Apocalyptica'), (8, 'Audioslave'), (9, 'BackBeat'), (10, 'Billy Cobham')]\"" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_community.utilities import SQLDatabase\n", + "\n", + "db = SQLDatabase.from_uri(\"sqlite:///Chinook.db\")\n", + "print(db.dialect)\n", + "print(db.get_usable_table_names())\n", + "db.run(\"SELECT * FROM Artist LIMIT 10;\")" + ] + }, + { + "cell_type": "markdown", + "id": "6959e93141d8099c", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "## Utility functions\n", + "\n", + "We will define a few utility functions to help us with the agent implementation. Specifically, we will wrap a `ToolNode` with a fallback to handle errors and surface them to the agent." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "deae8460e4cf72b1", + "metadata": { + "ExecuteTime": { + "end_time": "2024-06-12T21:24:17.557848Z", + "start_time": "2024-06-12T21:24:17.508550Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "from typing import Any\n", + "\n", + "from langchain_core.messages import ToolMessage\n", + "from langchain_core.runnables import RunnableLambda, RunnableWithFallbacks\n", + "\n", + "\n", + "def create_tool_node_with_fallback(tools: list) -> RunnableWithFallbacks[Any, dict]:\n", + " \"\"\"\n", + " Create a ToolNode with a fallback to handle errors and surface them to the agent.\n", + " \"\"\"\n", + " return ToolNode(tools).with_fallbacks(\n", + " [RunnableLambda(handle_tool_error)], exception_key=\"error\"\n", + " )\n", + "\n", + "def handle_tool_error(state) -> dict:\n", + " error = state.get(\"error\")\n", + " tool_calls = state[\"messages\"][-1].tool_calls\n", + " return {\n", + " \"messages\": [\n", + " ToolMessage(\n", + " content=f\"Error: {repr(error)}\\n please fix your mistakes.\",\n", + " tool_call_id=tc[\"id\"],\n", + " )\n", + " for tc in tool_calls\n", + " ]\n", + " }" + ] + }, + { + "cell_type": "markdown", + "id": "d0196604f8cbb07b", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "## Define tools for the agent\n", + "\n", + "We will define a few tools that the agent will use to interact with the database.\n", + "\n", + "1. `list_tables_tool`: Fetch the available tables from the database\n", + "2. `get_schema_tool`: Fetch the DDL for a table\n", + "3. `db_query_tool`: Execute the query and fetch the results OR return an error message if the query fails\n", + "\n", + "For the first two tools, we will grab them from the `SQLDatabaseToolkit`, also available in the `langchain_community` package." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "452d049a3d2a4406", + "metadata": { + "ExecuteTime": { + "end_time": "2024-06-12T20:18:15.838940Z", + "start_time": "2024-06-12T20:18:15.734199Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Album, Artist, Customer, Employee, Genre, Invoice, InvoiceLine, MediaType, Playlist, PlaylistTrack, Track\n", + "\n", + "CREATE TABLE \"Artist\" (\n", + "\t\"ArtistId\" INTEGER NOT NULL, \n", + "\t\"Name\" NVARCHAR(120), \n", + "\tPRIMARY KEY (\"ArtistId\")\n", + ")\n", + "\n", + "/*\n", + "3 rows from Artist table:\n", + "ArtistId\tName\n", + "1\tAC/DC\n", + "2\tAccept\n", + "3\tAerosmith\n", + "*/\n" + ] + } + ], + "source": [ + "from langchain_community.agent_toolkits import SQLDatabaseToolkit\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "toolkit = SQLDatabaseToolkit(db=db, llm=ChatOpenAI(model=\"gpt-4o\"))\n", + "tools = toolkit.get_tools()\n", + "\n", + "list_tables_tool = next(tool for tool in tools if tool.name == \"sql_db_list_tables\")\n", + "get_schema_tool = next(tool for tool in tools if tool.name == \"sql_db_schema\")\n", + "\n", + "print(list_tables_tool.invoke(\"\"))\n", + "\n", + "print(get_schema_tool.invoke(\"Artist\"))" + ] + }, + { + "cell_type": "markdown", + "id": "c16359edada327fa", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "The third will be defined manually. For the `db_query_tool`, we will execute the query against the database and return the results." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "f7eb708ecb4c7cfc", + "metadata": { + "ExecuteTime": { + "end_time": "2024-06-12T20:39:35.759834Z", + "start_time": "2024-06-12T20:39:35.740255Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[(1, 'AC/DC'), (2, 'Accept'), (3, 'Aerosmith'), (4, 'Alanis Morissette'), (5, 'Alice In Chains'), (6, 'Antônio Carlos Jobim'), (7, 'Apocalyptica'), (8, 'Audioslave'), (9, 'BackBeat'), (10, 'Billy Cobham')]\n" + ] + } + ], + "source": [ + "from langchain.agents import tool\n", + "\n", + "\n", + "@tool\n", + "def db_query_tool(query: str) -> str:\n", + " \"\"\"\n", + " Execute a SQL query against the database and get back the result.\n", + " If the query is not correct, an error message will be returned.\n", + " If an error is returned, rewrite the query, check the query, and try again.\n", + " \"\"\"\n", + " result = db.run_no_throw(query)\n", + " if not result:\n", + " return \"Error: Query failed. Please rewrite your query and try again.\"\n", + " return result\n", + "\n", + "print(db_query_tool.invoke(\"SELECT * FROM Artist LIMIT 10;\"))" + ] + }, + { + "cell_type": "markdown", + "id": "f1d66db8b8621639", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "While not strictly a tool, we will prompt an LLM to check for common mistakes in the query and later add this as a node in the workflow." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "293017e8f05ac2b3", + "metadata": { + "ExecuteTime": { + "end_time": "2024-06-12T20:18:19.658322Z", + "start_time": "2024-06-12T20:18:18.756256Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_5zdRt3uWwY23FSYmKZT7crGF', 'function': {'arguments': '{\"query\":\"SELECT * FROM Artist LIMIT 10;\"}', 'name': 'db_query_tool'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 20, 'prompt_tokens': 222, 'total_tokens': 242}, 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_319be4768e', 'finish_reason': 'stop', 'logprobs': None}, id='run-a062c91e-084e-4a91-bba8-fdbb957e2a5c-0', tool_calls=[{'name': 'db_query_tool', 'args': {'query': 'SELECT * FROM Artist LIMIT 10;'}, 'id': 'call_5zdRt3uWwY23FSYmKZT7crGF'}], usage_metadata={'input_tokens': 222, 'output_tokens': 20, 'total_tokens': 242})" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.prompts import ChatPromptTemplate\n", + "\n", + "query_check_system = \"\"\"You are a SQL expert with a strong attention to detail.\n", + "Double check the SQLite query for common mistakes, including:\n", + "- Using NOT IN with NULL values\n", + "- Using UNION when UNION ALL should have been used\n", + "- Using BETWEEN for exclusive ranges\n", + "- Data type mismatch in predicates\n", + "- Properly quoting identifiers\n", + "- Using the correct number of arguments for functions\n", + "- Casting to the correct data type\n", + "- Using the proper columns for joins\n", + "\n", + "If there are any of the above mistakes, rewrite the query. If there are no mistakes, just reproduce the original query.\n", + "\n", + "You will call the appropriate tool to execute the query after running this check.\"\"\"\n", + "\n", + "query_check_prompt = ChatPromptTemplate.from_messages([(\"system\", query_check_system),(\"placeholder\", \"{messages}\")])\n", + "query_check = query_check_prompt | ChatOpenAI(model=\"gpt-4o\", temperature=0).bind_tools([db_query_tool], tool_choice=\"required\")\n", + "\n", + "query_check.invoke({\"messages\": [(\"user\", \"SELET * FROM Artist LIMIT 10;\")]})" + ] + }, + { + "cell_type": "markdown", + "id": "66f88452151e8188", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "## Define the workflow\n", + "\n", + "We will then define the workflow for the agent. The agent will first force-call the `list_tables_tool` to fetch the available tables from the database, then follow the steps mentioned at the beginning of the tutorial." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "90d04ceea7b6b010", + "metadata": { + "ExecuteTime": { + "end_time": "2024-06-12T20:21:09.799829Z", + "start_time": "2024-06-12T20:21:09.765928Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "from typing import Annotated, Literal\n", + "\n", + "from langchain_core.messages import AIMessage\n", + "from langchain_core.pydantic_v1 import BaseModel, Field\n", + "from langchain_openai import ChatOpenAI\n", + "from typing_extensions import TypedDict\n", + "\n", + "from langgraph.graph import END, StateGraph\n", + "from langgraph.graph.message import AnyMessage, add_messages\n", + "from langgraph.prebuilt.tool_node import ToolNode\n", + "\n", + "\n", + "# Define the state for the agent\n", + "class State(TypedDict):\n", + " messages: Annotated[list[AnyMessage], add_messages]\n", + "\n", + "# Define a new graph\n", + "workflow = StateGraph(State)\n", + "\n", + "# Add a node for the first tool call\n", + "def first_tool_call(state: State) -> dict[str, list[AIMessage]]:\n", + " return {\n", + " \"messages\": [\n", + " AIMessage(\n", + " content=\"\",\n", + " tool_calls=[\n", + " {\n", + " \"name\": \"sql_db_list_tables\",\n", + " \"args\": {\n", + " },\n", + " \"id\": \"tool_abcd123\",\n", + " }\n", + " ],\n", + " )\n", + " ]\n", + " }\n", + "\n", + "def model_check_query(state: State) -> dict[str, list[AIMessage]]:\n", + " \"\"\"\n", + " Use this tool to double-check if your query is correct before executing it.\n", + " \"\"\"\n", + " return {\n", + " \"messages\": [\n", + " query_check.invoke({\"messages\": [state[\"messages\"][-1]]})\n", + " ]\n", + " }\n", + "\n", + "workflow.add_node(\"first_tool_call\", first_tool_call)\n", + "\n", + "# Add nodes for the first two tools\n", + "workflow.add_node(\"list_tables_tool\", create_tool_node_with_fallback([list_tables_tool]))\n", + "workflow.add_node(\"get_schema_tool\", create_tool_node_with_fallback([get_schema_tool]))\n", + "\n", + "# Add a node for a model to choose the relevant tables based on the question and available tables\n", + "model_get_schema = ChatOpenAI(model=\"gpt-4o\", temperature=0).bind_tools([get_schema_tool])\n", + "workflow.add_node(\"model_get_schema\", lambda state: {\"messages\": [model_get_schema.invoke(state[\"messages\"])],})\n", + "\n", + "# Describe a tool to represent the end state\n", + "class SubmitFinalAnswer(BaseModel):\n", + " \"\"\"Submit the final answer to the user based on the query results.\"\"\"\n", + " final_answer: str = Field(..., description=\"The final answer to the user\")\n", + "\n", + "# Add a node for a model to generate a query based on the question and schema\n", + "query_gen_system = \"\"\"You are a SQL expert with a strong attention to detail.\n", + "\n", + "Given an input question, output a syntactically correct SQLite query to run, then look at the results of the query and return the answer.\n", + "\n", + "DO NOT call any tool besides SubmitFinalAnswer to submit the final answer.\n", + "\n", + "When generating the query:\n", + "\n", + "Output the SQL query that answers the input question without a tool call.\n", + "\n", + "Unless the user specifies a specific number of examples they wish to obtain, always limit your query to at most 5 results.\n", + "You can order the results by a relevant column to return the most interesting examples in the database.\n", + "Never query for all the columns from a specific table, only ask for the relevant columns given the question.\n", + "\n", + "If you get an error while executing a query, rewrite the query and try again.\n", + "\n", + "If you get an empty result set, you should try to rewrite the query to get a non-empty result set. \n", + "NEVER make stuff up if you don't have enough information to answer the query... just say you don't have enough information.\n", + "\n", + "If you have enough information to answer the input question, simply invoke the appropriate tool to submit the final answer to the user.\n", + "\n", + "DO NOT make any DML statements (INSERT, UPDATE, DELETE, DROP etc.) to the database.\"\"\"\n", + "query_gen_prompt = ChatPromptTemplate.from_messages([(\"system\", query_gen_system),(\"placeholder\", \"{messages}\")])\n", + "query_gen = query_gen_prompt | ChatOpenAI(model=\"gpt-4o\", temperature=0).bind_tools([SubmitFinalAnswer])\n", + "def query_gen_node(state: State):\n", + " message = query_gen.invoke(state)\n", + " \n", + " # Sometimes, the LLM will hallucinate and call the wrong tool. We need to catch this and return an error message.\n", + " tool_messages = []\n", + " if message.tool_calls:\n", + " for tc in message.tool_calls:\n", + " if tc[\"name\"] != \"SubmitFinalAnswer\":\n", + " tool_messages.append(\n", + " ToolMessage(\n", + " content=f\"Error: The wrong tool was called: {tc['name']}. Please fix your mistakes. Remember to only call SubmitFinalAnswer to submit the final answer. Generated queries should be outputted WITHOUT a tool call.\",\n", + " tool_call_id=tc[\"id\"],\n", + " )\n", + " )\n", + " else:\n", + " tool_messages = []\n", + " return {\"messages\": [message] + tool_messages}\n", + "\n", + "workflow.add_node(\"query_gen\", query_gen_node)\n", + "\n", + "# Add a node for the model to check the query before executing it\n", + "workflow.add_node(\"correct_query\", model_check_query)\n", + "\n", + "# Add node for executing the query\n", + "workflow.add_node(\"execute_query\", create_tool_node_with_fallback([db_query_tool]))\n", + "\n", + "# Define a conditional edge to decide whether to continue or end the workflow\n", + "def should_continue(state: State) -> Literal[END, \"correct_query\", \"query_gen\"]:\n", + " messages = state[\"messages\"]\n", + " last_message = messages[-1]\n", + " # If there is a tool call, then we finish\n", + " if getattr(last_message, \"tool_calls\", None):\n", + " return END\n", + " if last_message.content.startswith(\"Error:\"):\n", + " return \"query_gen\"\n", + " else:\n", + " return \"correct_query\"\n", + "\n", + "# Specify the edges between the nodes\n", + "workflow.set_entry_point(\"first_tool_call\")\n", + "workflow.add_edge(\"first_tool_call\", \"list_tables_tool\")\n", + "workflow.add_edge(\"list_tables_tool\", \"model_get_schema\")\n", + "workflow.add_edge(\"model_get_schema\", \"get_schema_tool\")\n", + "workflow.add_edge(\"get_schema_tool\", \"query_gen\")\n", + "workflow.add_conditional_edges(\n", + " \"query_gen\",\n", + " should_continue,\n", + ")\n", + "workflow.add_edge(\"correct_query\", \"execute_query\")\n", + "workflow.add_edge(\"execute_query\", \"query_gen\")\n", + "\n", + "# Compile the workflow into a runnable\n", + "app = workflow.compile()" + ] + }, + { + "cell_type": "markdown", + "id": "6c344ae086ba8d22", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "## Visualize the graph" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "4f200d1813897000", + "metadata": { + "ExecuteTime": { + "end_time": "2024-06-12T20:21:11.813905Z", + "start_time": "2024-06-12T20:21:11.712945Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Image, display\n", + "from langchain_core.runnables.graph import MermaidDrawMethod\n", + "\n", + "display(\n", + " Image(\n", + " app.get_graph().draw_mermaid_png(\n", + " draw_method=MermaidDrawMethod.API,\n", + " )\n", + " )\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "bdf78dc68548522c", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "## Run the agent" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "85958809-03c5-4e52-97cc-e7c0ae986f60", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'The sales agent who made the most in sales in 2009 is Steve Johnson with total sales of 164.34.'" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import json\n", + "\n", + "messages = app.invoke({\"messages\": [(\"user\", \"Which sales agent made the most in sales in 2009?\")]}) \n", + "json_str = messages['messages'][-1].additional_kwargs['tool_calls'][0]['function']['arguments']\n", + "json.loads(json_str)['final_answer']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3bf7709f-500c-4f28-bb85-dda317286c63", + "metadata": {}, + "outputs": [], + "source": [ + "for event in app.stream({\"messages\": [(\"user\", \"Which sales agent made the most in sales in 2009?\")]}):\n", + " print(event)" + ] + }, + { + "attachments": { + "b92325b1-2c9a-4efa-94f5-49a75b1ffb64.png": { + "image/png": "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LyI+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj45Njg8L2V4aWY6UGl4ZWxZRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+MTg4ODwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOlVzZXJDb21tZW50PlNjcmVlbnNob3Q8L2V4aWY6VXNlckNvbW1lbnQ+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgqL2GBRAAAAHGlET1QAAAACAAAAAAAAAeQAAAAoAAAB5AAAAeQAAgByTaF3wAAAQABJREFUeAHsvQeAXFd59n926vai3nu1miW5y7bcK6YZGz5KQgmkESAESEIqhJbko/OREPhDQjABDAEbG2Pc5CYXybLVm9V7We1q++zuzP5/73t3VitpJauMbNl+brLa2Zk7557zu+eca85znvct6uIIOkRABERABERABERABERABERABERABERABERABERABERABERABERABERABE6bQJEE2NNmqAJEQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREwAlIgFVHEAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREIECEZAAWyCQKkYEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEJMCqD4iACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIhAgQhIgC0QSBUjAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAhJg1QdEQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREoEAEJMAWCKSKEQEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEJsOoDIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIlAgAhJgCwRSxYiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiABFj1AREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREoEAEJsAUCqWJEQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAKs+oAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIFIiABNgCgVQxIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiABVn1ABERABERABERABERABERABERABERABERABERABERABERABERABERABApEQAJsgUCqGBEQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQARGQAKs+IAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIFIiABtkAgVYwIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAISIBVHxABERABERABERABERABERABERABERABERABERABERABERABERABERCBAhGQAFsgkCpGBERABERABERABERABERABERABERABERABERABERABERABERABERABCTAqg+IgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIQIEISIAtEEgVIwIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAISYNUHREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAERKBABCTAFgikihEBERABERABERABERABERABERABERABERABERABERABERABERABERABCbDqAyIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiJQIAISYAsEUsWIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgARY9QEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAERKBABCbAFAqliREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREACrPqACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACBSIgATYAoFUMSIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAVZ9QAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAQKREACbIFAqhgREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAERkACrPiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACBSIgAbZAIFWMCIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACEiAVR8QAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQgQIRkABbIJAqRgREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAQkwKoPiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiECBCEiALRBIFSMCIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACEmDVB0RABERABERABERABERABERABERABERABERABERABERABERABERABESgQAQkwBYIpIoRAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAQmw6gMiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiUCACEmALBFLFiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAEWPUBERABERABERABERABERABERABERABERABERABERABERABERABERABESgQAQmwBQKpYkRABERABERABERABERABERABERABERABERABERABERABERABERABERAAqz6gAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAgUiIAE2AKBVDEiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAFWfUAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAECkRAAmyBQKoYERABERABERABERABERABERABERABERABERABERABERABERABERABEZAAqz4gAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAgUiIAG2QCBVjAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAhIgFUfEAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREIECEZAAWyCQKkYEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEJMCqD4iACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIhAgQhIgC0QSBUjAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAhJg1QdEQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREoEAEJMAWCKSKEQEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEJsOoDIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIlAgAhJgCwRSxYiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiABFj1AREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREoEAEJsAUCqWJEQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAKs+oAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIFIiABNgCgVQxIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiACIiABVn1ABERABERABERABERABERABERABERABERABERABERABERABERABERABApEQAJsgUCqGBEQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQARGQAKs+IAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIFIiABtkAgVYwIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAISIBVHxABERABERABERABERABERABERABERABERABERABERABERABERABERCBAhGQAFsgkCpGBERABERABERABERABERABETg9Ah05TpDyLZHhcSToSiWPL0CX8XfPoxFLBGK4qmjW9OVDV3ZTn4yoShR3Pc5R39L74iACIiACIiACIiACIiACIiACJxhAhJgzzBgFS8CIiACIiACIiACIiACIiACInBiBLIte0Nn4/YQcrmQqBgW4uXDTuyLr8Gzsq21obNhSwhduRAvHRwSlSOPamUu0xCyzbtDZ/2mkBw4DWYjjjpHb4iACIiACIiACIiACIiACIiACLz8BCTAvvzMdUUREAEREAEREAEREAEREAEREIE+CLRteSK0rLsXR2cuFI+6MBSPmRfixQNCiMX7OPu1/VZmx+LQvOp/YZEN6WHnhpLxV4RYyYDDXMFt2xeGzLanQkftlpAeMTukh58XUgOnA6botQ1HrRMBERABERABERABERABERCBs5yABNiz/AapeiIgAiIgAiIgAiIgAiIgAiLweiHQtPTH4eDCb4RcZy6UTb0+lM14a0gNOIfQuunXC4KedjavvjvUL/giLLKhZML8UDH79pDqD4tkWc85Tct+GJoQaTv27wrpkTNC6cRrQvnUt6O/SoDtgaQXIiACIiACIiACIiACIiACIvAKEJAA+wpA1yVFQAREQAREQAREQAREQAREQASOJuAC7JMIsDhgS8ZdEsrOuTkUj5iH6Fh69Mmv8XciAfYLLkYXjzovlE03FpeGWLqqp+VNS/8zNK38Reg4sBfn6yiE2itD5Zw/lgDbQ0gvREAEREAEREAEREAEREAEROCVISAB9pXhrquKgAiIgAiIgAiIwBklkNn5bGjftyZkD+4JyUETQmrQOSHZb/IZvaYKF4HXIoEceTg76l4Mme1LybVZ600sSsRDcgDjasgM8nKOel26M8/UvTYBtv7Jr0chiEefH0pxwZaMvjrEUuVn6pJnbbkmwNY98nlYdBFeeKY7gkvGwKK4X0+dm5b+IDStwAFbtzckawa7AFt1wcckwPYQ0gsREAEREAEREAEREAEREAEReGUISIB9ZbjrqiIgAiIgAiIgAiJwRgk0PPfvofXFR3BFbSMnIGEpp9wcyia98YxeU4WLwGuOQK6TjQwrQ/Pae0LbpqdCZ8Neb6IJsKkh00Lx6ItDeuiskKgeG+Il/V9zzX8lGtT4AiGITYDN5QipOzuUTr4mlI6/EQG24pWozit6zebVdyHAfiESYIed4yxKYNG7rzW+8IPQbA7Yun0hUdUfAfaKUH3RJyXAvqJ3ThcXAREQAREQAREQAREQAREQ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67LKqUAqkAqkAqlAKpAKpAKpQCqQCqQCqcDAUyAB7MDr82xxKpAKpAKpQCqQCqQCqUB/UiAA7EsArBywjyeA7UtdC7K++OKLzT333NOMHj26GTFiRPPCCy80iy66aLPWWms1+++/f7PZZps1SyyxRF9qVtY1FUgFUoFUIBVIBVKBVCAVSAVSgVQgFRjwCiSAHfBDIAVIBVKBVCAV6EgBoSDbl/afvetd72q8Oiq2ra/O/l4/F05yWscySd+dUo/V0ba1/vVnR9vUz7qqT92mO8exbWca1ePUn47X3W3rPvkzFUgFQoECYM+cAmDvSwdsHxkU7nl/+9vfmvvvv7/5zW9+0/z+978vNZ933nmbjTfeuPnmN7/ZLL/88s18883XR1qU1UwFUoFUIBVIBVKBVCAVSAVSgVQgFUgFUoGqQALYqkT+TAVSgVQgFUgFpijw+uuvF0fSpEmTyid///vfyyS531977bXm3e9+d7P44os3K6ywQrPssst2qNuzzz5bHE1jxoxp6nHqho43YcKEkuuPy2mrrbZq1l133WbQoEF1k7f9NDk/cuTIt33W0S+OteOOOzbLLLNMYwK/fXn66afLecePH9/+T2/7XduWW265MvE/11xzve1v9RcOLa4tr66KfIXq1VWhqZfchwsuuGAz99xzd7V5/i0VSAXaKwDA3h0AVgjixxPAtpent/4+ceLE5o477ij399tvv708KxZYYIFmn332aXbfffdmzTXXLPdyz5wsqUAqkAqkAqlAKpAKpAKpQCqQCqQCqUAq0LcUSADbt/ora5sKpAKpQCowgwpwGgGfwOgzzzxTgN/LL7/ceL300kvNG2+8UfLtObz3PrOtYj+AcPLkyQ04C0ouvfTSzQ477FDgadmo3T+PP/54c9VVVzWXXXZZ8+CDD77tr4735JNPFpC74oorNnvvvXezzTbbNGusscbbtqu/3HDDDc3RRx9d6qpuHRUT9JxSRx55ZJm07wh6CnF54403Ntdcc01Hh5j6GagMCO+6664NGNBRuemmm5p77723eeyxxzr689TPHMcL0G0PEQBXeQ/lO3Qcusw///xF36kH6OSNPIhe733ve5v3ve99zSKLLNIstthi3d6/k8Pmx6lA31SgANizAsBengC2D/Sge51FOO6jwg57VnDCWjyz5ZZbNnvssUez0UYbdete2Aeam1VM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USAKRIEoEAWiQBSIAlEgCkSBKBAFhkOBANjhULGXOozu5yL605/+VN4tYiS+1I8gQtKi/b1o5iP87W9/W2ABx5e5DqPT3+uUb6LAeFIAqLz99tuLIxSIlA5XkL2bCwjx2muvNeeff36Z99W+cJiCpNIP1/L00083p59+elkW/Pz4xz9e4Gx1wdbl+nq3/nHHHVfmdV1mmWUGBGDdt15//fUyV+P999/fzDLLLAV6L7jggo069M1jAYD3pVm+jwJRYPoU4Hp94403ioP+2muvba644oryXGcwzYwoMgm4R+y0007lfiE1cdIRzwjls40oEAWiQBSIAlEgCkSBKBAFokAUiAJRoF2BANh2RYbpfw4io/+lgDSPnzLHHHM0Sy21VAGLA0kfOUxN6Zpq7rzzzuaCCy4oun3oQx9qpkyZEljdNUcvDY0CI6OAQSzHH398c88995Qg+te//vXicBqZrc2YWl9++eXm0UcfbS655JICNFdfffVGiuDZZ5/9b9xa7iFSEF911VUFQE+YMKGkIl522WUbQHRaxfyxp5xySmN7iy22WLPvvvv264AFSICTW2+9tThg3btWWmmlxuAYYBYE5oadf/75p7Xp/B4FosA4VeC5nvmkb7nllgJef/azn5WU3sOdcrg/aQ3ckzKc+/XDH/5ws8suuzSgbNIR96dafosCUSAKRIEoEAWiQBSIAlEgCkSBKBAFRkKBANhhVlUA2+j/u+++uwTOBZ/++Mc/lq2YR85IfBCW02neeectjthhbkLXVifVpnSZ9Npkk02aHXbYoegVt1XXHtI0PApMtwL6A/2CgL4gvrkEl1xyyemud0ZXoO0G5oCaDzzwQEnJ+fOf/7wA1a233rqRgrh9YI75WC1vLlbzwQIb4OsKK6xQXtPahxdffLEMavnpT39anLObbrppcbeq13yJiy++eDPPPPMUlytIK2WxQUPPPPNMmQtWxgb3LHM4ArPvete7irOMu8yAIuubr5YjNyUKRIEooP8wmO6aa64p81rrN0ar6JekdJeOeOLEieXzrLPOOlrNyXajQBSIAlEgCkSBKBAFokAUiAJRIApEgXGoQADsMB90sECQ/Oyzz25uuummMndhHflvVL6AtjSO6667bnEWcRIJakuPNt7LmWee2Rx88MFFBg7YT37ykwVUS4HZX6HvL3/5yzJnonSlc801V9EU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4k5JXZRpyy23LGuvvXanQ2r07COPPFKuuOKKKsT7E7B4217YNsvaATOS1bMUm3ztJK87XjQfhkAIhMD7SOCuu+4qngd7/fXX1+eXWwi0wAILlM0337xsvfXWde7NdsTvYwPl0iEQAiEQAiEQAiEQAiEQAiEQAiEwiQhEwE4ikMkmBEIgBEJgcBAQjdpeAymxZ5l2Su3z9t73mPG5Rn/PWW15DjQvZemvPPIaaD6OHWhe8uwvtTwGUqax5dPK067TN7++v7fj8h4CIRACUxoBc53tiI8//vi6+OS+++6r861oftu+f+tb3yrzzTfflFbslCcEQiAEQiAEQiAEQiAEQiAEQiAEQmA8CUTAjiewHB4CIRACIRACIRACIRACIRACE0rAM2Efe+yxur38pZdeWmwLbwcA0f0rr7xy2XHHHYst4G27nhQCIRACIRACIRACIRACIRACIRACITA4CUTADs52S6lDIARCIARCIARCIARCIAQGMQHby1933XV1W/wHH3ywbsvuOde2bvdaddVViy3lI2IHcSOn6CEQAiEQAiEQAiEQAiEQAiEQAkOWQATskG36VDwEQiAEQiAE3hsCvbc+btsSvzdXzlVCIARCYMom8OKLL5YLLrigXHjhhfVZ36JjzZkrrrhiOfTQQ+uzvT3zelxb1U/ZtUzpQiAEQiAEQiAEQiAEQiAEQiAEQmDoEYiAHXptnhqHQAiEQAiEwHtK4LXXXiujRo0qM888c5ltttlqRNd7WoBcLARCIASmUAKE6+jRo8tvf/vb+kzYa665przyyitllllmKUsssUTZaaedyvrrr18WXHDBKbQGKVYIhEAIhEAIhEAIhEAIhEAIhEAIhEAnAhGwnajksxAIgRAIgRAIgUlCgFx4/PHHy9lnn12mm266stRSS5WNNtqoTD311JMk/2QSAiEQAt1A4Iknnii33357ueiii8pdd91VnnrqqVqtDTbYoM6Zw4cPL/PMM0+ZccYZu6G6qUMIhEAIhEAIhEAIhEAIhEAIhEAIdD2BCNiub+JUMARCIARCIATePwKvvvpqufHGG8v+++9f3njjjSoSjjzyyCpj379S5cohEAIhMOURsB3xbbfdVs4666xy7bXXlpdffrluR7zMMsuUXXfdtayxxhplkUUWKdNOO+2UV/iUKARCIARCIARCIARCIARCIARCIARC4B0EImDfgSO/hEAIhEAIhEAITEoCttK0teZXvvKVQi6I4jrmmGNqFFeeaTgpSSevEAiBwU7grbfeqtJVJOyVV15ZfvGLX5TXX3+9zpfzzjtv2W677cqGG25YVlpppcFe1ZQ/BEIgBEIgBEIgBEIgBEIgBEIgBLqeQARs1zdxKhgCITAYCPztb38rtmqdaqqpyjTTTDMYipwyhsCACIjgImAPPPDA8vzzz5f11luvfO973yuzzjpr+cAHPjCgPHJQCIRACAwlAs8++2y59957yyWXXFKuv/76MnLkyFr9FVZYoayzzjp1J4Ell1yyPlN7KHFJXUMgBEIgBEIgBEIgBEIgBEIgBEJgMBGIgB1MrZWyhkAIdCWBP//5z8U2rU8//XQVUnPPPXeZeeaZ84zMrmztoVepl156qQrYr371q1XAkge2IJ5zzjmzDfHQ6w6pcQiEwAAJ+H/BqFGjyo9//OMaDTt69Oh65qKLLlo8F/bjH/94+chHPlJmmWWWAeaYw0IgBEIgBEIgBEIgBEIgBEIgBEIgBN5LAhGw7yXtXCsEQiAEOhB46qmnyq233lqf+eZG6sYbb1xvrhJUSSEw2An86U9/GiNgX3jhhbL22muXI444oswzzzxl+umnH+zVS/lDIARCYLIQ+Otf/1os0HrsscfKpZdeWo4++uj6PFjPf51tttnK+uuvX7bYYouyySabTJbrJ9MQCIEQCIEQCIEQCIEQCIEQCIEQCIGJIxABO3H8cnYIhEAITDSBu+++u5x77rnl4osvrtuyupm68847lwUWWGCi804GUz6Btv30H//4x/Laa6+V+eabr0aGTj311FN+4QdQQgL2hhtuKAcddFAhYEXAfve73y1zzTVXImAHwC+HhEAIDG0CvhceeeSRGgV73nnnlYcffrh4fvZCCy1UVltttfKxj32szqtZtDW0+0lqHwIhEAIhEAIhEAIhEAIhEAIhMOURiICd8tokJQqBEBgEBN566636zFbvIlTefPPN8sYbb9TPFF9k3wwzzFBfnnPp2a79JXLqxBNPLLfffnuZY445avTrnnvuWRZeeOH+TsnnXURA37H99IgRIwoJu8oqq9Qb6yKcuiG9+OKL9RmGX/va16qA9QxYkVyzzz57ngHbDQ2cOoRACEx2Av6v4bvC3HnZZZfVrYl95pEFyy67bNl1113LiiuuWHcWmOyFyQVCIARCIARCIARCIARCIARCIARCIAQGRCACdkCYclAIhEAIvE1AxKJns5FlL7/8cnn22WeLbYS9nn/++WLbQM9o82y2xRZbrEY0ji2a8ZprrinHHXdcue+++8qCCy5YtxNMBOzbvLv9p+eee66cffbZY57xZ0vJzTffvCy33HJdUXVj4vrrry8HH3xwFbCeXXjMMcfkOcdd0bqpRAiEwHtFwP897Chw4YUXlsMPP7y88sor9f8bFnstvvji5TOf+UzZcccd36vi5DohEAIhEAIhEAIhEAIhEAIhEAIhEALjIBABOw5A+XMIhEAINAJufnrdfPPNNVr13nvvrREptgckYr1EwTpG9KLtAG0jTMSuuuqq9QbpdNNN965o2F//+tc1quXRRx+twnarrbYq22+/fZl33nnbpfPexQQI/FNPPbVcfvnl5cknn6x9ZbfddquR0N1QbQsVrrvuuvLNb36zCtjhw4eXH/3oRzVKfGyR4d1Q99QhBEIgBCY1gccff7zOqeecc065//77q4idaaaZyvLLL1823HDDst1229XIWNsUJ4VACIRACIRACIRACIRACIRACIRACLx/BCJg3z/2uXIIhMAgIiCqVeTJE088Uc4///zym9/8pkastioQSaJc2w1P2xITsW6KikxxU3SttdYqSy65ZI38m3baadup5dJLLy1HHHFEjaAV9Ui+brrppvUG6piD8kPXEvjDH/5QTjnllHLFFVeUkSNH1ujpL37xi4WI74Y0evToKgsOPfTQ8tJLL9WxQMD2HgPdUM/UIQRCIATeKwIWe/nesIDrnnvuqRJ2mmmmKR/60IeKRxh4NuwiiyxSt3nPQpf3qlVynRAIgRAIgRAIgRAIgRAIgRAIgRB4J4EI2HfyyG8hEAIh0JGAm5133nlnOemkk8pdd91Vnnnmmbr1XzuYeJ155pmLCFfi9YUXXqjPg3Xj099mmWWW8tGPfrTeGF166aXfIVcvuOCCcthhhxVb0a6++url05/+dFlnnXXq82Bb/nnvXgIE7E9/+tMqYB977LG6DfV+++1Xo5i6oda///3vy7XXXlv7uGjxjTbaqHz/+9+PgO2Gxk0dQiAE3jcCFnpZwHXCCSeUhx56qEpY/9/w3Pl999237L777vX/EW1h2PtW0Fw4BEIgBEIgBEIgBEIgBEIgBEIgBIYogQjYIdrwqXYIhMD4EbjhhhvqFrEXX3xxfc7r66+/PiaDpZZaqgzv2VZVpCvRKlr2xRdfrBGypK2tin02xxxz1GN22WWXsvbaa9ctimXi+Z9f+9rX6nbGImXdNCVrZ5111jHXyA/dS4CAJfZFMj388MPF8/w222yzstJKK9Ub6qJGPXPYzXaC39bU+trKK688pg9NyXQ8G5mA/Y//+I9aBwL2qKOOioCdkhstZQuBEBgUBJ5++ulyxx13lF/96lf13e+S3TbWW2+9stNOO5WFF1647sYxKCqUQoZACIRACIRACIRACIRACIRACIRAFxGIgO2ixkxVQiAEJj0B0osA84zOiy66qDzwwAM1wpUII1vnn3/+su6665ZtttmmDBs2rN7kJFtF+t13333lxhtvLNdcc00Va56FKe2www5l6623rlGuImR/+ctfloMOOqj+bYsttih77713IXVtXzwpk8jct956q2YpImZKj4rBUXnJblsrEpODOeFPttrKWp30Lf1ENPUll1xSI6vbzfNFF120Rkm/8sor9fjeAtYWkyKlP/WpT9U+N6Uz8VxbY+Db3/52LerGG29cDj/88CErYPWDv/zlL/VZ0LYtz/ag7+zB2GDU2ITPO/nktxDoS8D3hC3sLeK56aab6veM75eFFlqofPKTn6z/R/Eseou6Mp760svvIRACIRACIRACIRACIRACIRACITD5CETATj62yTkEQqALCIhkHTFiRPHMShJJIi7nmWeesswyy5RNNtmkyrAllliiY21tK3zdddeVn//85+W3v/1tjYSda6656jasol5tFShy5eCDD67nb7vttsXzPz/4wQ9OtHAkMXq/yEwizw1Yz98kkdXlvbwhqzxSe6+/9PzTytDeff7mm29W+U1Qkt1uJg+0vORtu8ZAz2llmRzvykIs6UPEPNFqm2rRof/7v/9bo6r9vW/CozFp7wsssEBZZZVVyhe+8IUq6vueM6X9PmrUqFpv0lWfI2Btua3vT67U2r69t+s0hu29fT653tv1vbeXdraowxicfvrpKwd9dCimxqT3e1tsMOOMM1ZGTcQORT6pcwiMDwHPprejBhHr/y7GlbTHHnuUT3ziE2XZZZcds7BhfPLNsSEQAiEQAiEQAiEQAiEQAiEQAiEQAhNGIAJ2wrjlrBAIgSFAgCh58MEH6/PVRLKK5JMIJFGvtoglYm0t3F+0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s2fbpEmT3FqxDz74oGmN2CJFiuRWAvqNAAIIIIBApgS+++4727dvn+XLl8/Kli1rxYsXz9T1nIxAZgT0B3KapaRMmTJ22223Wa9evTJzOecigAACCCCAAAIIIIBAAggQwCbAQ2QICCCAAALhElCgumDBAlMF7Lx582zYsGHWoUMHy5s3r6uA1dqw5cqVSw1gCxYsaFu3brVnnnnGDh06ZDfccIO1b9/erRMbLjlGiwACCCAQVoEJEybYzJkz3dro/fr1M62dzoZArAQ084hep06dshEjRtiQIUNidSvaRQABBBBAAAEEEEAAgTgVIICN0wdDtxBAAAEEEEhL4MSJE676VVWt33//vT366KOmqYb37t1rWut1+fLlrrpHa8Dq+5IlS9rRo0ft+eefd9MTV6lSxe68806rW7duWrfgewQQQAABBBJK4IUXXjCFsFWrVrWf/exn7g+REmqADCauBN5++217/fXXbfPmzXbPPffYzTffbEWLFrWkpKS46iedQQABBBBAAAEEEEAAgdgJEMDGzpaWEUAAAQQQiInAkSNH7Nlnn7Uvv/zSBa0PP/ywVa9e3VW5/vrXv7Zly5a5Ke98AKtqWG2aCk+h7fbt203XtGjRwooVKxaTPtIoAggggAAC8SCgddM1db9mgRg/frz746OhQ4dap06d4qF79CFBBT788ENTCPvpp5/aLbfcYjfddJNVqFDB8uTJk6AjZlgIIIAAAggggAACCCAQKUAAGynCZwQQQAABBOJY4OzZs3bw4EF74IEH7PDhw9axY0cbOHCg+6Wephl+6qmnXABbunTp1CmIfQCr6YonT55s7733nt19991u7dh69erF8WjpGgIIIIAAAhcnoH83jx07Zv/7v//rpu5v0KCB3X777dauXbuLa5irEUhHQMHrlClT3Kt///5u+YeUlBS3XEQ6l3EIAQQQQAABBBBAAAEEEkiAADaBHiZDQQABBBBIfIF9+/a5yldV8pQpU8Z++tOf2tVXX20lSpSwr7/+2v7nf/7Hli5d6ta4i6yA3bZtm02bNs00DaOC2969e1v37t0TH40RIoAAAgiEVuDkyZNu5oe//OUvNnfuXFf5qj9cuuaaa0JrwsBjL7BmzRqbMWOGjR492lq3bm0DBgywli1bWv78+WN/c+6AAAIIIIAAAggggAACcSFAABsXj4FOIIAAAggg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gAACCFykAAHsRQJyOQIIIIAAAvEiEAxg8+XLZ0OGDLFOnTpZrVq14qWL9AMBBBBAAIEcESCAzRF2booAAggggAACCCCAAAIIhFaAADa0j56BI4AAAggkmkAwgNUad7feequ1a9fOatSokWhDZTwIIIAAAghkSoAANlNcnIwAAggggAACCCCAAAIIIHCRAgSwFwnI5QgggAACCMSLQDCALViwoN15553WqlUrq1q1arx0kX4ggAACCCCQIwIEsDnCHuqbnjt3zk6fPm3JycmmpSGSkpJC7cHgEUAAAQQQQAABBBAImwABbNieOONFAAEEEEhYgWAAW6hQIbv77rutefPmVrly5YQdMwNDAAEEEEAgIwIEsBlR4pysFDh06JAtXbrUKlWq5P4YrkCBAoSwWQlMWwgggAACCCCAAAIIxLkAAWycPyC6hwACCCCAQEYFggFskSJFbPjw4dakSROrWLFiRpvgPAQQQAABBBJSgAA2IR9r3A7q8OHDtnHjRnvjjTesdu3a1qJFC7v88sstb968cdtnOoYAAggggAACCCCAAAJZK0AAm7WetIYAAggggECOCQQD2KJFi9qIESOsUaNGVr58+RzrEzdGAAEEEEAgHgQIYOPhKYSnDzt27LD58+fbyJEjrW7dutanTx/30hIRbAgggAACCCCAAAIIIBAOAQLYcDxnRokAAgggEAKBYABbrFgxe+ihh6xhw4ZWrly5EIyeISKAAAIIIJC2AAFs2jYcyXoBAtisN6VFBBBAAAEEEEAAAQRymwABbG57YvQXAQQQQACBNAQIYNOA4WsEEEAAgdALjB8/3j744AMrXbq0DR482E0JG3oUAGImQAAbM1oaRgABBBBAAAEEEEAg1wgQwOaaR0VHEUAAAQQQSF+AADZ9H44igAACCIRXYNmyZbZmzRoH0KxZM7cuZ3g1GHmsBQhgYy1M+wgggAACCCCAAAIIxL8AAWz8PyN6iAACCCCAQIYECGAzxMRJCCCAAAIhFDhx4oSdOnXKjVzrcObPnz+ECgw5uwQIYLNLmvsggAACCCCAAAIIIBC/AgSw8fts6BkCCCCAAAKZEiCAzRQXJyOAAAIIIIAAAjER2Lp1q82dO9f++Mc/Wr169axPnz7upfCfDQEEEEAAAQQQQAABBMIhQAAbjufMKBFAAAEEQiCwe/due/31123FihVWpEgR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BLBx90joEAIIIIAAAggggAACCCCAQHYLKIhduHChvfHGG7Z+/Xrr16+fjRgxIs1uaArj4JaUlBT8yPtcKBB8pmk9z2+//dZUBavqV01TXaNGjVw4UrqMAAIIIIAAAggggAACsRYggI21MO0jgAACCCCAAAIIIIAAAgjEvcCZM2ds37599s0339iRI0esYsWKVrNmzaj91nFNXZwnTx4rUKCAe5UuXdqFclEv4Mu4F9i7d69pCupTp06ZnmWZMmXcc40MYk+ePGmHDh1yz75gwYI887h/snQQAQQQQAABBBBAAIGcESCAzRl37ooAAggggAACCCCAAAIIIJBLBbZs2WJvvvmm/fDDD5Y/f373KlWqlCmQ81u+fPnc9/p8+vRpF+z5Ywr18ubN6yooK1eu7PbJycn+cOpe161atcp27drlQuHUA4E3PgS+6qqrTG2p3cjt+PHjbprl1atXu/Aw8rj/rGuLFi1q9evXdwG0/z6437p1q61du9aOHj1qCq3T2kqUKGHVq1e3Sy+91LUZPE+VpidOnLCNGze6KZ/lGKw+DZ6r8WlcV199tQs9I510rapS9UwUnkfbdI3aKVKkiHsmutfBgwfPeyYHDhxwRgpgNbVww4YNrVatWqbnyIYAAggggAACCCCAAAIIZFaAADazYpyPAAIIIIAAAggggAACCCAQaoHFixfb8OHDbf/+/Xb27FkXHirUC4aImqK2WLFi7jtVzKpqUkGgD18LFSpkHTt2tO7du1unTp2iBqeHDx+25557zubMmWPbtm2Laq4KXIW/Dz74oGtL943cFFAuW7bMtbVp06bIw6mfFb4q7HzkkUesXbt2qd8H30yaNMlefvll1x+FqGltKSkpdv3117sxVq1a9bzTfLXxuHHjbPTo0S7MlWO0TePp0qWLPfHEEyazyIBZQbCmjp4wYYLNmDEjWhMufFVQrrHpmSi0VQWzfH2Fq56Nf7Vs2dJ69uzp1neN5hn1JnyJAAIIIIAAAggggAACCAQECGADGLxFAAEEEEAAAQQQQAABBBBA4EICu3fvtpkzZ7qqS4WbCldVUanpaf2mNWVVVanvVMmqcLZ48eKmylBVYqois0mTJta2bVtr06aN++yv9XsFt3/729/siy++cNWZ/vvgXhWaCk5vueUW1060wFB9U/CqtlTBmtamdqpUqWI33XSTNWrUKOpp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eQv/3P21fq/jemmTl15sCCCAAAIIIIAAAgggEC4BAthwPW9GiwACCCCAAAIIIIAAAggggECuFFDVqypUNT2wKnNVzavphbU+bFaF37kShk4jgAACCCCAAAIIIIBA3AkQwMbdI6FDCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQWwUIYHPrk6PfCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCAQdwIEsHH3SOgQAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgjkVgEC2Nz65Og3AggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgjEnQABbNw9EjqEAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAK5VYAANrc+OfqNAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAJxJ0AAG3ePhA4hgAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEBuFSCAza1Pjn4jgAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEDcCRDAxt0joUMIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIJBbBQhgc+uTo98IIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIBB3AgSwcfdI6BACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCORWAQLY3Prk6DcCCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCMSdAAFs3D0SOoQAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAArlVgAA2tz45+o0AAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAnEnQAAbd4+EDiGAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAQG4VIIDNrU+OfiOAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAQNwJEMDG3SOhQwgggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgggkFsFCGBz65Oj3wgggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgggEHcCBLBx90joEAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAII5FYBAtjc+uToNwII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E9FSdmvMonzg3/6lbImqzs6obDz2b99dZp757FpV+2vfVLaHjTByItsLR/C6Y2vMRI3PyrmpWSWbMySnZ8vemANa2+52RZviqKzMCtYnSzhZZ/tmO9m4p2zLOhEzfrMF885s4RoBcle2hI77mry/uLe4v5xRWmeMHqDS9td2dyABAgQIECBAgAABAgQI7CEggN2Dwx8IECBAgAABAgQIECBA4LEWyOBwe7QHXvGB95fhm5fVObJHveOvy6wlzy1ds2cfuo+PNquT8zbHYwTljghbI5hswtZD9ylP2JnqfMwInHeGZ0znjHubXqZn+1hB6xP2THwwAQIECBAgQIAAAQIEUkAAax0QIECAAAECBAgQIECAwOMqUKtUY37q8ve/twz94KbS0dtXFrz598vg2eeUvmOPfVyvxYcRIECAAAECBAgQIECAAIFDLSCAPdSizkeAAAECBAgQIECAAAECBxTIVrrL3/++sunG79bq1IGlLyxzL7iozF56zgGP9QMECBAgQIAAAQIECBAgQOBwFhDAHs5Px7URIECAAAECBAgQIEDgKSqQVbArP/HxsumG68vYffeWrkWLy/xLXl0WvO4NT5p5rE/RR+O2CBAgQIAAAQIECBAgQKClgAC2JaDDCRAgQIAAAQIECBAgQODgBXJ+6Ybrv102fOvasvm7N9TZrPNf+Zqy6Pf+oEyfOXMyhD340zqCAAECBAgQIECAAAECBAg84QIC2Cf8EbgAAgQIECBAgAABAgQI/AYK7NxZRu75ZVn/9a+WNVd8ugLMufDisuDSt5TexYtLR0/PbyCKWyZAgAABAgQIECBAgACBp4KAAPap8BTdAwECBAgQIECAAAECBJ6EAhPDw2XT928qa79yVcnfz156bpn/268tnbNmqYB9Ej5Pl0yAAAECBAgQIECAAAECkwICWCuBAAECBAgQIECAAAECBJ4YgaiC3XrfijJ0661lYstwmXHiSWXWmc+q7YjLtGlPzDX5VAIECBAgQIAAAQIECBAg0FJAANsS0OEECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBBoBASwjYQ9AQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEWgoIYFsCOpwAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQKNgAC2kbAnQIAAAQIECBAgQIAAAQIE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" + } + }, + "cell_type": "markdown", + "id": "490f07a9-d816-4b53-b784-b33dbc6d74b1", + "metadata": {}, + "source": [ + "## Eval\n", + "\n", + "Now, we can evaluate this agent! We previously defined [simple SQL agent](https://github.com/langchain-ai/langsmith-cookbook/blob/main/testing-examples/agent-evals-with-langgraph/langgraph_sql_agent_eval.ipynb) as part of our LangSmith evaluation cookbooks, and evaluated responses to 5 questions about our database. We can compare this agent to our prior one on the same dataset. [Agent evaluation](https://docs.smith.langchain.com/concepts/evaluation#agents) can focus on 3 things:\n", + "\n", + "* `Response`: The inputs are a prompt and a list of tools. The output is the agent response.\n", + "* `Single tool`: As before, the inputs are a prompt and a list of tools. The output the tool call.\n", + "* `Trajectory`: As before, the inputs are a prompt and a list of tools. The output is the list of tool calls\n", + "\n", + "![Screenshot 2024-06-13 at 2.13.30 PM.png](attachment:b92325b1-2c9a-4efa-94f5-49a75b1ffb64.png)\n", + "\n", + "### Response\n", + "\n", + "We'll evaluate end-to-end responses of our agent relative to reference answers. Let's run [response evaluation](https://docs.smith.langchain.com/concepts/evaluation#evaluating-an-agents-final-response) [on the same dataset](https://smith.langchain.com/public/20808486-67c3-4e30-920b-6d49d6f2b6b8/d)." + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "a80f4adc-a8dc-403c-9bef-6de5e873b9bc", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "\n", + "\n", + "def predict_sql_agent_answer(example: dict):\n", + " \"\"\"Use this for answer evaluation\"\"\"\n", + " msg = {\"messages\": (\"user\", example[\"input\"])}\n", + " messages = app.invoke(msg)\n", + " json_str = messages['messages'][-1].additional_kwargs['tool_calls'][0]['function']['arguments']\n", + " response = json.loads(json_str)['final_answer']\n", + " return {\"response\": response}" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "1040233f-3751-4bd3-902f-709fc2e1ecf5", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain import hub\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "# Grade prompt\n", + "grade_prompt_answer_accuracy = prompt = hub.pull(\"langchain-ai/rag-answer-vs-reference\")\n", + "\n", + "def answer_evaluator(run, example) -> dict:\n", + " \"\"\"\n", + " A simple evaluator for RAG answer accuracy\n", + " \"\"\"\n", + "\n", + " # Get question, ground truth answer, chain\n", + " input_question = example.inputs[\"input\"] \n", + " reference = example.outputs[\"output\"]\n", + " prediction = run.outputs[\"response\"]\n", + "\n", + " # LLM grader\n", + " llm = ChatOpenAI(model=\"gpt-4-turbo\", temperature=0)\n", + "\n", + " # Structured prompt\n", + " answer_grader = grade_prompt_answer_accuracy | llm\n", + "\n", + " # Run evaluator\n", + " score = answer_grader.invoke({\"question\": input_question,\n", + " \"correct_answer\": reference,\n", + " \"student_answer\": prediction})\n", + " score = score[\"Score\"]\n", + "\n", + " return {\"key\": \"answer_v_reference_score\", \"score\": score}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb814b85-70ba-4699-9038-20266b53efbd", + "metadata": {}, + "outputs": [], + "source": [ + "from langsmith.evaluation import evaluate\n", + "\n", + "dataset_name = \"SQL Agent Response\"\n", + "experiment_results = evaluate(\n", + " predict_sql_agent_answer,\n", + " data=dataset_name,\n", + " evaluators=[answer_evaluator],\n", + " num_repetitions=3,\n", + " experiment_prefix=\"sql-agent-multi-step-response-v-reference\",\n", + " metadata={\"version\": \"Chinook, gpt-4o multi-step-agent\"},\n", + ")" + ] + }, + { + "attachments": { + "e9a91890-3299-4b71-9ab2-21d737a120ba.png": { + "image/png": "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AggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAghkK0AAPls5lkMAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQCAQIAAfYPAWAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQACBbAUIwGcrx3IIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggEAgTgAwzeIoAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgggkK0AAfhs5VgOAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQACBQIAAfIDBWwQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBLIVIACfrRzLIYAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgggEAgQgA8weIsAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEC2AgTgs5VjOQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBAIBAvABBm8RQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBDIVoAAfLZyLIcAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEAgQAA+wOAtAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAAC2QoQgM9WjuUQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBAIBAjABxi8RQABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAIFsBAvDZyrEcAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACgQAB+ACDtwgggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCGQrQAA+WzmWQwABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAIBAgAB9g8BYBBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAIFsBQjAZyvHcggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCAQCBOADDN4igAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAA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77ynm1HD3ey3394VL8QclSqg1uS9evV2wWwFqxVYV/fscclfQ/XdoYce5ALK4XzvvPOeC+Jq2rvvvmHatVsp/Dqt94MHv+Naamtmjal+5pn/iV3u9dffNCef/H/uu759dygz7vkee+xrhg370Y0T/+KLT8d2qb5gwQKz774HmR9++NGt49VXn0/01hC7wZiJFQXg77vvQdsF/rVuyWuuudzsscduMWsx1vES8+STg9z17a23XknaxX/swssmpgrAa9z5fv32d0F+dXd/xRUDYlf14ouvmNNPP9t9p+7999qrfC8HsQsysUYJEICvUT83O4tAXgXyec+R14yxMgQQqNEC2QY9Feh++KnXzNTpM8v4qfeuA/fqY3pu2r3M9Ew/jB470Tw86DUzctTYcou2atHMHH3IbmaVlcs/b11+4/3mzzETyi2TasK63VY1Jx7ZL9UsfFcEgWyPxSJkjU0ggAACWQlUywD8Cy+8bM444xzXuletfH3q3/8CW6j6kvnuu8+zar2qMSt3221vo8L1Dh3am5dffs4W0Dbwq+e1QAIE4PMLe+aZ/d3fAS3g/3FV4EXj46ZqAf/P3JX37qmnnnGtBGlxVXm/AVtGoCKB4cNHuBa/mu/qqy+L7aa8onXwfXEFPv/8S9dFurZ6zDFH2nvI+IC3vldL7j59dtFbs/XWvcydd97q3vt/Bg161nX7rs/ffDM0q/vESy65wjzyyGOuq/uhQz809erV86sv86rW4+ptQa3gO3dexbz++ovu+ylTprreUvT96aefaisEHFVmufBDGCC/8cZrbOWRncKvK3yfKgCvAH/Pntu5FuXdu69rBg16NOn6tA/qnefvv/+2rf/72UD9rknnTfZFqgC8up5XxQZ12f/hh4NdxYRk67nwwgFGw1ats87aZXrSSjY/02ueAAH4mvebs8cI5Esgn/cc+coT60EAAQSyCXr+OOIPc+u9g8zChYtMg/rLmTW6djKNGzUwP/3yh5kydYZD7dt7M7PbDltmBfyXDaDfeMcTriW7ysDWXqOzWbF1S/P7n2PNb3+McRVr69qeKf990K5mg3VXL7ONux563oydMLnMtLgPyvuESVPdV93X7mpOOHyvuNmYVkSBbI7FImaPTSGAAAIZC1TLALy6EFW3Xh98MNisuGKbBMr++x/qCgF9AWXiizTf3HPP/bYw/Xo3d67d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2Lf/gKzbtMNumlbwuXEE2KZr/a4SVSqUkqJF0kBBAAEEUkKAAHxKHEZ2AgEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQACB+ARcK/i0woWlYrkSdEUfHx+5ExDQ4PvGLTvNMAh/0/o9AUcWRQCBvClAAD5vHhdKhQACCCCAAAIIIIAAAggggAACCCCAAAIIIIBAtgts2LxT9uzbb7ej3dGXKFZE0goXkkMOOSTbt80GCpZAIBCwAfdde/bZbud174sVLSKVypcsWBDsLQIIpLwAAfiUP8TsIAIIIIAAAggggAACCCCAAAIIIIAAAggggAAC0QVcS/joOZiDQPIFGPc9+aasEQEE8oYAAfi8cRwoBQIIIIAAAggggAACCCCAAAIIIIAAAggggAACuSagY8Lv3L1P9u47aFopH8y1crDh1BZISysshxYpbLudZ8z31D7W7B0CBVmAAHxBPvrsOwIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIBA0gQIwCeNkhUhgAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCBRkAQLwBfnos+8IIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAkkTIACfNEpWhAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCBQkAUIwBfko8++I4AAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggkTYAAfNIoWRECCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAQEEWIABfkI8++44AAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgggkDQBAvBJo2RFCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIFWYAAfEE++uw7AggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEDSBAjAJ42SFSGAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIFGQBAvAF+eiz7wgggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACSRMgAJ80SlaEAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIFCQBQjAF+Sjz74jgAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCRNgAB80ihZEQIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIBAQRYgAF+Qjz77jgACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQNAEC8EmjZEUIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgVZgAB8QT767DsCCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAQNIECMAnjZIVIYAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggUZAEC8AX56LPvCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAJJEyAAnzRKVoQAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgggUJAFCMAX5KPPviOAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIJE2AAHzSKFkRAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEBBFiAAX5CPPvuOAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIJA0AQLwSaNkRQgggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACBVmAAHxBPvrsOwIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIBA0gQIwCeNkhUhgAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCBRkAQLwBfnos+8IIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAkkTIACfNEpWhAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCBQkAUIwBfko8++I4AAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggkTYAAfNIoWRECCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAQEEWIABfkI8++44AAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgggkDQBAvBJo2RFCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIFWYAAfEE++uw7AggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEDSBAjA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CMIn7dCwIgQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQACB/CSQlAC87vDe/Qdk45adNgiv3dGXK12cMeHz05lAWRFAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEhJIWgBeS6Et4Tdv22W7o9fXpYofKqVKFJW0tML6koQAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEDKCiQ1AO+Utm7fLdt37XUvpVjRNCl2aBHTIr6wpBUuLIUKHeLN4wkCCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAKpIJAtAXiF0dbwO0wQfufufangxD4ggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQoUC2BeDdVgOBgOzeu1/27jtgg/IHDv5tx4l383lEAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAgFQSyPQCfCkjsAwIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAApkJEIDPTIj5CCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIxCBAAD4GJLIggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQmQAB+MyEmI8AAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEAMAgTgY0AiCwIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAApkJEIDPTIj5CCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIxCBAAD4GJLIggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQmQAB+MyEmI8AAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEAMAgTgY0AiCwIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAApkJEIDPTIj5CCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIxCBAAD4GJLIggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQmQAB+MyEmI8AAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEAMAgTgY0AiCwIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAApkJEIDPTIj5CCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIxCBAAD4GJLIggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQmQAB+MyEmI8AAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEAMAgTgY0AiCwIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAApkJEIDPTIj5CCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIxCDwf9KTCJQlRRRbAAAAAElFTkSuQmCC" + } + }, + "cell_type": "markdown", + "id": "23ddb587-d52c-45d5-9316-ff0e86f90ef6", + "metadata": {}, + "source": [ + "Summary metrics (see dataset [here](https://smith.langchain.com/public/20808486-67c3-4e30-920b-6d49d6f2b6b8/d)):\n", + "\n", + "* The `multi-step` agent here out performs the previously defined [base case SQL agent](https://github.com/langchain-ai/langsmith-cookbook/blob/main/testing-examples/agent-evals-with-langgraph/langgraph_sql_agent_eval.ipynb)\n", + "\n", + "![Screenshot 2024-06-13 at 2.09.57 PM.png](attachment:e9a91890-3299-4b71-9ab2-21d737a120ba.png)" + ] + }, + { + "cell_type": "markdown", + "id": "5fb7f738-11d4-4a55-8b28-58727ead9838", + "metadata": {}, + "source": [ + "### Trajectory\n", + "\n", + "Let's run [trajectory evaluation](https://docs.smith.langchain.com/concepts/evaluation#evaluating-an-agents-trajectory) on this same dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "id": "ef84d2f7-fa52-46ca-8939-616e5ac4d101", + "metadata": {}, + "outputs": [], + "source": [ + "# These are the tools that we expect the agent to use\n", + "expected_trajectory=['sql_db_list_tables', # first: list_tables_tool node\n", + " 'sql_db_schema', # second: get_schema_tool node\n", + " 'db_query_tool', # third: execute_query node\n", + " 'SubmitFinalAnswer'] # fourth: query_gen" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "id": "1b7b007a-1dd2-4f3e-b157-b9d7ec2ea0a2", + "metadata": {}, + "outputs": [], + "source": [ + "def predict_sql_agent_messages(example: dict):\n", + " \"\"\"Use this for answer evaluation\"\"\"\n", + " msg = {\"messages\": (\"user\", example[\"input\"])}\n", + " messages = app.invoke(msg)\n", + " return {\"response\": messages}" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "id": "ae2fe538-1c6d-4186-80dd-1d240d253f40", + "metadata": {}, + "outputs": [], + "source": [ + "from langsmith.schemas import Example, Run\n", + "\n", + "\n", + "def find_tool_calls(messages):\n", + " \"\"\" \n", + " Find all tool calls in the messages returned \n", + " \"\"\"\n", + " tool_calls = [tc['name'] for m in messages['messages'] for tc in getattr(m, 'tool_calls', [])]\n", + " return tool_calls\n", + "\n", + "def contains_all_tool_calls_in_order_exact_match(root_run: Run, example: Example) -> dict:\n", + " \"\"\"\n", + " Check if all expected tools are called in exact order and without any additional tool calls.\n", + " \"\"\"\n", + " expected_trajectory = ['sql_db_list_tables', 'sql_db_schema', 'db_query_tool', 'SubmitFinalAnswer']\n", + " messages = root_run.outputs[\"response\"]\n", + " tool_calls = find_tool_calls(messages)\n", + " \n", + " # Print the tool calls for debugging\n", + " print(\"Here are my tool calls:\")\n", + " print(tool_calls)\n", + " \n", + " # Check if the tool calls match the expected trajectory exactly\n", + " if tool_calls == expected_trajectory:\n", + " score = 1\n", + " else:\n", + " score = 0\n", + " \n", + " return {\"score\": int(score), \"key\": \"multi_tool_call_in_exact_order\"}\n", + "\n", + "def contains_all_tool_calls_in_order(root_run: Run, example: Example) -> dict:\n", + " \"\"\"\n", + " Check if all expected tools are called in order, \n", + " but it allows for other tools to be called in between the expected ones.\n", + " \"\"\"\n", + " messages = root_run.outputs[\"response\"]\n", + " tool_calls = find_tool_calls(messages)\n", + " \n", + " # Print the tool calls for debugging\n", + " print(\"Here are my tool calls:\")\n", + " print(tool_calls)\n", + " \n", + " it = iter(tool_calls)\n", + " if all(elem in it for elem in expected_trajectory):\n", + " score = 1\n", + " else: \n", + " score = 0\n", + " return {\"score\": int(score), \"key\": \"multi_tool_call_in_order\"}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cf02e843-438d-4168-a27a-8f1e0266f8d7", + "metadata": {}, + "outputs": [], + "source": [ + "experiment_results = evaluate(\n", + " predict_sql_agent_messages,\n", + " data=dataset_name,\n", + " evaluators=[contains_all_tool_calls_in_order,contains_all_tool_calls_in_order_exact_match],\n", + " num_repetitions=3,\n", + " experiment_prefix=\"sql-agent-multi-step-tool-calling-trajecory-in-order\",\n", + " metadata={\"version\": \"Chinook, gpt-4o multi-step-agent\"},\n", + ")" + ] + }, + { + "attachments": { + "9a1084c0-4c7c-4e6f-8329-80499d293e0a.png": { + "image/png": "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eGlmOlBpeGVsWERpbWVuc2lvbj4xNTk4PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6VXNlckNvbW1lbnQ+U2NyZWVuc2hvdDwvZXhpZjpVc2VyQ29tbWVudD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CrqY7EIAAEAASURBVHgB7Z0HmBTF1oZLJOeco4BKBgFBARFRkoAiiIgJM+Z8vVf0N6CYI4KCIElAUQQkI1lAQMmg5JxBck7+5zuzNfQ2M9Mzs4Gd3e/wsNOxuvrt7urqk+qSf0UMhQRIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARSAYF0qeAceAokQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkoARo+OCNQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkkGoI0PCRai4lT4QESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESICGD94DJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACqYYADR+p5lLyREiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABGj44D1AAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiSQagjQ8JFqLiVPhARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgIYP3gMkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAKphgANH6nmUvJESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEaPjgPUACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJJBqCNDwkWouJU+EBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEggfSQITp0+a44eP2VOnjptzpw9F8mu3JYESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEkpzAJf+KhHOU/YeOmxNi8MieJZPJmPFSky4dg0XC4cZtSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEko9AWIaPvfuPmEsvvdRkz5Yp+WrGI5EACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZBAhAQ8DR+I9IDQ6BEhWW5OAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiSQ7ARC5qvCmB6a3oqRHsl+YXhAEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiCByAmENHxgIHOM6UEhARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIggVggENLwcVIGM8dA5hQSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESiAUCIQ0fZ86eM+nShdwkFs6RdSQBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEkgjBGjVSCMXmqdJAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAmmBAA0faeEq8xxJgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIII0QoOEjjVxoniYJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJpAUCNHykhavMcyQBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiCBNEKAho80cqF5miRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiSQFgjQ8JEWrjLPkQRIgARIgARIgARIgARIgARIgARIgARIgARIgARIgATSCAEaPtLIheZpkgAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkEBaIEDDR1q4yjxHEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEkgjBNKnkfPkaZIACZAACZAACSQjgTXrN5vyl5XUI46fPMeM+3WOKV+2hCwrocua33htMtaGhyIBEiABEiABEiABEiABEiABEiABEkhLBJLF8HHy5ClzYN8Bc+zYCfPvv/+G5HvJJZeYrFkzm9x5c5tMmTKG3JYrSYAESIAESIAEUhYBGDzGT/7drFm3xTz9aHs1fqxZv0UrCaMHDCBWaPywJPjrJLD3nwNm45YdZv+BQ9JvNCZ3ruymdIkipmCBvM7NOE0CURFAGwWxhtmoCuFOJEACqZoA24lUfXl5ciQQkwTYLsXkZWOlUwCBJDd8wOixfetOU7BgPlO0aEGTLl3o7Frnzp0zhw4d1X2KFi9M40cKuEnSahVWrFwvp/6vqXRl2bSKgOdNAiRAAhER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SIAESIAESIAESIAESIAESIAESIAESIAESIAESCAVE6DhIxVfXJ4aCZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACaQ1AjR8pLUrzvMlARIgARIgARIgARIgARIgARIgARIgARIgARIgARIggVRMgIaPVHxxeWokQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkkNYI0PCR1q44z5cESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEUjEBGj5S8cXlqZEACZAACZAACZAACZAACZAACZAACZAACZAACZAACZBAWiNAw0dau+I8XxIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARJIxQTSe53brr2HvDbhehIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARJIEQQ8DR/FC+VOERVlJUiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEjAiwBTXXkR4noSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIIGYIUDDR8xcKlaUBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEjAiwANH16EuJ4ESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESCBmCNDwETOXihUlARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARLwIkDDhxchricBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEogZAjR8xMylYkVJgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgAS8CNDw4UWI60mABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABGKGAA0fMXOpWFESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEvAjR8eBHiehIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIggZghQMNHzFwqVpQESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESMCLAA0fXoS4ngRIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIIGYI0PARM5eKFSUBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEvAiQMOHFyGuJwESIAESIAESIAESIAES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R44bKMngIUwJnwB4gRv4gSOFBEggcQiwXYqeI9ul6NmlpT0Z8ZGWrjbPlQRIgARIgARIgARIgARIgARIgATSKIFTp8+aw0dPmBOnztD4EcE9gLHWMmdMr0aPjBkujWBPbkoCJOBFgO2SF6HA69kuBebCpfEJpLjBzeNXj3MkQAIkQAIkQAIkQAIkQAIkQAIkQAIkkHACUNozTVPCObIEEiCBxCPAdinxWLIkEnATYKorNxHOkwAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJxCwBGj5i9tKx4iRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAm4CNHy4iXCeBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEggZgnQ8BGzl44VJwESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEScBOg4cNNhPMkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIxS4CGj5i9dKw4CZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZCAmwANH24inCcBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEohZAjR8xOylY8VJgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgATcBGj4cBPhPAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQQMwSoOEjZi8dK04CJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJOAmQMOHmwjnSYAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEYpYADR8xe+lYcRIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgATcBGj7cRDhPAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiQQswRo+IjZS8eKkwAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJuAnQ8OEmwnkSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIIGYJUDDR8xeOlacBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEjATSC9e4F7fuuuA+5FnCcBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiCBFEnA0/BRvFDuFFlxVooESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAE3ASY6spNhPMkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIxS4CGj5i9dKw4CZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZCAmwANH24inCcBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEohZAjR8xOylY8VJgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgATcBGj4cBPhPAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQQMwSoOEjZi8dK04CJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJOAmQMOHmwjnSYAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEYpYADR8xe+lYcRIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgATcBGj7cRDhPAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiQQswT+H0W+a/zjPdUwAAAAAElFTkSuQmCC" + } + }, + "cell_type": "markdown", + "id": "a34d5022-c0c5-4037-afc5-75c173b882f6", + "metadata": {}, + "source": [ + "The aggregate scores show that we never correctly call the tools in exact order:\n", + "\n", + "![Screenshot 2024-06-13 at 2.46.34 PM.png](attachment:9a1084c0-4c7c-4e6f-8329-80499d293e0a.png)" + ] + }, + { + "cell_type": "markdown", + "id": "a9e4a0e6-fc0f-4101-bda5-d96d1294cbb9", + "metadata": {}, + "source": [ + "Looking at the logging, we can see something interesting - \n", + "\n", + "```\n", + "['sql_db_list_tables', 'sql_db_schema', 'sql_db_query', 'db_query_tool', 'SubmitFinalAnswer']\n", + "```\n", + "\n", + "We appear to inject a hallucinated tool call, `sql_db_query`, into our trajectory for most of the runs.\n", + "\n", + "This is why `multi_tool_call_in_exact_order` fails, but `multi_tool_call_in_order` still passes. \n", + "\n", + "We will explore ways to resolve this using LangGraph in future cookbooks!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0681b6e0-196e-440c-ab16-1a530411719e", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/tnt-llm/img/tnt_llm.png b/examples/tutorials/tnt-llm/img/tnt_llm.png similarity index 100% rename from examples/tnt-llm/img/tnt_llm.png rename to examples/tutorials/tnt-llm/img/tnt_llm.png diff --git a/examples/tnt-llm/tnt-llm.ipynb b/examples/tutorials/tnt-llm/tnt-llm.ipynb similarity index 98% rename from examples/tnt-llm/tnt-llm.ipynb rename to examples/tutorials/tnt-llm/tnt-llm.ipynb index c98bf5598..e07a07840 100644 --- a/examples/tnt-llm/tnt-llm.ipynb +++ b/examples/tutorials/tnt-llm/tnt-llm.ipynb @@ -7,7 +7,7 @@ "source": [ "# TNT-LLM: Text Mining at Scale\n", "\n", - "[TNT-LLM](https://arxiv.org/abs/2403.12173) by Wan, et. al describes a taxonomy generation and classification system developed by Microsoft for their Bing Copilot application.\n", + "[TNT-LLM](https://arxiv.org/abs/2403.12173) by Wan, et. al describes a taxonomy generation and classification system developed by Microsoft for their Bing Copilot application.\n", "\n", "It generates a rich, interpretable taxonomy of user intents (or other categories) from raw conversation logs. This taxonomy can then be used downstream by LLMs to label logs, which in turn can be used as training data to adapt a cheap classifier (such as logistic regression classifier on embeddings) that can be deployed in your app.\n", "\n", @@ -17,7 +17,6 @@ "2. Label Training Data\n", "3. Finetune classifier + deploy\n", "\n", - "\n", "When applying LangGraph in this notebook, we will focus on the first phase: taxonomy generation (blue in the diagram below). We then show how to label and fit the classifier in subsequent steps below.\n", "\n", "![TNT LLM Diagram](./img/tnt_llm.png)\n", @@ -30,8 +29,7 @@ "4. **Update** the taxonomy on each subsequent minibatch via a ritique and revise prompt\n", "5. **Review** the final taxonomy, scoring its quality and generating a final value using a final sample.\n", "\n", - "\n", - "## Prerequisites" + "## Prerequisites\n" ] }, { @@ -77,7 +75,7 @@ "\n", "Since each node of a StateGraph accepts the state (and returns an updated state), we'll define that at the outset.\n", "\n", - "Our flow takes in a list of documents, batches them, and then generates and refines candidate taxonomies as interpretable \"clusters\"." + "Our flow takes in a list of documents, batches them, and then generates and refines candidate taxonomies as interpretable \"clusters\".\n" ] }, { @@ -119,7 +117,7 @@ "source": [ "#### 1. Summarize Docs\n", "\n", - "Chat logs can get quite long. Our taxonomy generation step needs to see large, diverse minibatches to be able to adequately capture the distribution of categories. To ensure they can all fit efficiently into the context window, we first summarize each chat log. Downstream steps will use these summaries instead of the raw doc content." + "Chat logs can get quite long. Our taxonomy generation step needs to see large, diverse minibatches to be able to adequately capture the distribution of categories. To ensure they can all fit efficiently into the context window, we first summarize each chat log. Downstream steps will use these summaries instead of the raw doc content.\n" ] }, { @@ -155,9 +153,7 @@ "\n", "\n", "summary_llm_chain = (\n", - " summary_prompt\n", - " | ChatAnthropic(model=\"claude-3-haiku-20240307\")\n", - " | StrOutputParser()\n", + " summary_prompt | ChatAnthropic(model=\"claude-3-haiku-20240307\") | StrOutputParser()\n", " # Customize the tracing name for easier organization\n", ").with_config(run_name=\"GenerateSummary\")\n", "summary_chain = summary_llm_chain | parse_summary\n", @@ -207,7 +203,7 @@ "source": [ "#### 2. Split into Minibatches\n", "\n", - "Each minibatch contains a random sample of docs. This lets the flow identify inadequacies in the current taxonomy using new data." + "Each minibatch contains a random sample of docs. This lets the flow identify inadequacies in the current taxonomy using new data.\n" ] }, { @@ -217,6 +213,9 @@ "metadata": {}, "outputs": [], "source": [ + "import random\n", + "\n", + "\n", "def get_minibatches(state: TaxonomyGenerationState, config: RunnableConfig):\n", " batch_size = config[\"configurable\"].get(\"batch_size\", 200)\n", " original = state[\"documents\"]\n", @@ -251,8 +250,7 @@ "source": [ "#### 3.a Taxonomy Generation Utilities\n", "\n", - "\n", - "This section of the graph is a generate -> update 🔄 -> review cycle. Each node shares a LOT of logic, which we have factored out into the shared functions below." + "This section of the graph is a generate -> update 🔄 -> review cycle. Each node shares a LOT of logic, which we have factored out into the shared functions below.\n" ] }, { @@ -262,7 +260,6 @@ "metadata": {}, "outputs": [], "source": [ - "import random\n", "from typing import Dict\n", "\n", "from langchain_core.runnables import Runnable\n", @@ -342,7 +339,7 @@ "id": "2e2a2723-d350-4871-83e8-88f081ab4c8b", "metadata": {}, "source": [ - "#### 3. Generate initial taxonomy" + "#### 3. Generate initial taxonomy\n" ] }, { @@ -387,7 +384,7 @@ "source": [ "#### 4. Update Taxonomy\n", "\n", - "This is a \"critique -> revise\" step that is repeated N times." + "This is a \"critique -> revise\" step that is repeated N times.\n" ] }, { @@ -423,7 +420,7 @@ "source": [ "#### 5. Review Taxonomy\n", "\n", - "This runs once we've processed all the minibatches." + "This runs once we've processed all the minibatches.\n" ] }, { @@ -462,7 +459,7 @@ "source": [ "## Define the Graph\n", "\n", - "With all the functionality defined, we can define the graph!" + "With all the functionality defined, we can define the graph!\n" ] }, { @@ -537,11 +534,11 @@ "source": [ "## Usage\n", "\n", - "The docs can contain __any__ content, but we've found it works really well on chat bot logs, such as those captured by [LangSmith](https://smith.langchain.com).\n", + "The docs can contain **any** content, but we've found it works really well on chat bot logs, such as those captured by [LangSmith](https://smith.langchain.com).\n", "\n", "We will use that as an example below. Update the `project_name` to your own LangSmith project.\n", "\n", - "You will likely have to customize the `run_to_doc` function below, since your expected keys may differ from those of this notebook's author." + "You will likely have to customize the `run_to_doc` function below, since your expected keys may differ from those of this notebook's author.\n" ] }, { @@ -551,7 +548,6 @@ "metadata": {}, "outputs": [], "source": [ - "import random\n", "from datetime import datetime, timedelta\n", "\n", "from langsmith import Client\n", @@ -604,7 +600,7 @@ "source": [ "#### Invoke\n", "\n", - "Now convert the runs to docs and kick off your graph flow. This will take some time! The summary step takes the longest. If you want to speed things up, you could try splitting the load across model providers." + "Now convert the runs to docs and kick off your graph flow. This will take some time! The summary step takes the longest. If you want to speed things up, you could try splitting the load across model providers.\n" ] }, { @@ -614,11 +610,12 @@ "metadata": {}, "outputs": [], "source": [ - "# Optional. If you are running into errors or rate limits and want to avoid repeated computation,\n", - "# you can set this while debugging\n", "from langchain.cache import InMemoryCache\n", "from langchain.globals import set_llm_cache\n", "\n", + "# Optional. If you are running into errors or rate limits and want to avoid repeated computation,\n", + "# you can set this while debugging\n", + "\n", "set_llm_cache(InMemoryCache())" ] }, @@ -638,31 +635,28 @@ " \" that would benefit the user.\"\n", ")\n", "\n", - "from langchain_core.tracers.context import tracing_v2_enabled\n", - "\n", - "with tracing_v2_enabled(client=Client(api_key=\"ls__eaec6db115fe4ad2af4fdf26fa553645\")):\n", - " stream = app.stream(\n", - " {\"documents\": docs},\n", - " {\n", - " \"configurable\": {\n", - " \"use_case\": use_case,\n", - " # Optional:\n", - " \"batch_size\": 400,\n", - " \"suggestion_length\": 30,\n", - " \"cluster_name_length\": 10,\n", - " \"cluster_description_length\": 30,\n", - " \"explanation_length\": 20,\n", - " \"max_num_clusters\": 25,\n", - " },\n", - " # We batch summarize the docs. To avoid getting errors, we will limit the\n", - " # degree of parallelism to permit.\n", - " \"max_concurrency\": 2,\n", + "stream = app.stream(\n", + " {\"documents\": docs},\n", + " {\n", + " \"configurable\": {\n", + " \"use_case\": use_case,\n", + " # Optional:\n", + " \"batch_size\": 400,\n", + " \"suggestion_length\": 30,\n", + " \"cluster_name_length\": 10,\n", + " \"cluster_description_length\": 30,\n", + " \"explanation_length\": 20,\n", + " \"max_num_clusters\": 25,\n", " },\n", - " )\n", + " # We batch summarize the docs. To avoid getting errors, we will limit the\n", + " # degree of parallelism to permit.\n", + " \"max_concurrency\": 2,\n", + " },\n", + ")\n", "\n", - " for step in stream:\n", - " node, state = next(iter(step.items()))\n", - " print(node, str(state)[:20] + \" ...\")" + "for step in stream:\n", + " node, state = next(iter(step.items()))\n", + " print(node, str(state)[:20] + \" ...\")" ] }, { @@ -672,7 +666,7 @@ "source": [ "## Final Result\n", "\n", - "Below, render the final result as markdown:" + "Below, render the final result as markdown:\n" ] }, { @@ -724,6 +718,9 @@ } ], "source": [ + "from IPython.display import Markdown\n", + "\n", + "\n", "def format_taxonomy_md(clusters):\n", " md = \"## Final Taxonomy\\n\\n\"\n", " md += \"| ID | Name | Description |\\n\"\n", @@ -743,8 +740,6 @@ " return md\n", "\n", "\n", - "from IPython.display import Markdown\n", - "\n", "Markdown(format_taxonomy_md(step[\"__end__\"][\"clusters\"][-1]))" ] }, @@ -761,13 +756,13 @@ "\n", "The problem is that LLM-based tagging can be expensive.\n", "\n", - "Embeddings can be ~100x cheaper to compute, and a simple logistic regression classifier on top of that would add negligible cost. \n", + "Embeddings can be ~100x cheaper to compute, and a simple logistic regression classifier on top of that would add negligible cost.\n", "\n", "Let's tag and train a classifier!\n", "\n", "#### Label Training Data\n", "\n", - "Use an LLM to label the data in a fully-automated fashion. For beter accuracy, you can sample a portion of the results to label by hand as well to verify the quality." + "Use an LLM to label the data in a fully-automated fashion. For beter accuracy, you can sample a portion of the results to label by hand as well to verify the quality.\n" ] }, { @@ -860,7 +855,7 @@ "source": [ "#### Train Classifier\n", "\n", - "Now that we've extracted the features from the text, we can generate the classifier on them." + "Now that we've extracted the features from the text, we can generate the classifier on them.\n" ] }, { @@ -883,9 +878,8 @@ "source": [ "import numpy as np\n", "from sklearn.linear_model import LogisticRegression\n", - "from sklearn.metrics import accuracy_score, f1_score, roc_auc_score\n", + "from sklearn.metrics import accuracy_score, f1_score\n", "from sklearn.model_selection import train_test_split\n", - "from sklearn.preprocessing import label_binarize\n", "from sklearn.utils import class_weight\n", "\n", "# Create a dictionary mapping category names to their indices in the taxonomy\n", @@ -934,7 +928,7 @@ "source": [ "## Phase 3: Deploy\n", "\n", - "Now that you have your classifier, you can easily deploy it and apply to future runs! All you need is to embed the input and apply your LogisticRegression classifier. Let's try it. We will use python's [joblib](https://joblib.readthedocs.io/en/stable/) library to serialize our sklearn classifier. Below is an example:" + "Now that you have your classifier, you can easily deploy it and apply to future runs! All you need is to embed the input and apply your LogisticRegression classifier. Let's try it. We will use python's [joblib](https://joblib.readthedocs.io/en/stable/) library to serialize our sklearn classifier. Below is an example:\n" ] }, { @@ -960,7 +954,7 @@ "source": [ "#### To deploy\n", "\n", - "When deploying, you can load the classifier and initialize your embeddings encoder. They fit together easily using LCEL:" + "When deploying, you can load the classifier and initialize your embeddings encoder. They fit together easily using LCEL:\n" ] }, { @@ -995,7 +989,7 @@ "source": [ "#### Example:\n", "\n", - "Assuming you've had some more data come in, you can fetch it and apply it below" + "Assuming you've had some more data come in, you can fetch it and apply it below\n" ] }, { @@ -1056,7 +1050,7 @@ "\n", "Congrats on implementing TNT-LLM! While most folks use clustering-based approachs like LDA, k-means, etc. it can often be hard to really interpret what each cluster represents. TNT-LLM generates human-interpretable labels you can use downstream to monitor and improve your application.\n", "\n", - "The technique also lends itself to hierarchical sub-categorizing: once you have the above taxonomy, use it to label your data, then on each sub-category, generate a new taxonomy using a similar technique to the one described above!" + "The technique also lends itself to hierarchical sub-categorizing: once you have the above taxonomy, use it to label your data, then on each sub-category, generate a new taxonomy using a similar technique to the one described above!\n" ] } ], diff --git a/examples/usaco/usaco.ipynb b/examples/usaco/usaco.ipynb index 68382db2c..f647abc3c 100644 --- a/examples/usaco/usaco.ipynb +++ b/examples/usaco/usaco.ipynb @@ -168,7 +168,7 @@ " except subprocess.TimeoutExpired:\n", " process.kill()\n", " q.put(\"timed out\")\n", - " except Exception as e:\n", + " except Exception:\n", " q.put(f\"failed: {traceback.format_exc()}\")\n", "\n", "\n", @@ -266,7 +266,7 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import Annotated, Optional\n", + "from typing import Annotated\n", "\n", "from typing_extensions import TypedDict\n", "\n", @@ -321,7 +321,7 @@ "source": [ "#### Node 1: Solver\n", "\n", - "Create a `solver` node that prompts an LLM \"agent\" to use a [writePython tool](https://python.langchain.com/docs/integrations/chat/anthropic/#beta-tool-calling) to generate the submitted code." + "Create a `solver` node that prompts an LLM \"agent\" to use a [writePython tool](https://python.langchain.com/v0.2/docs/integrations/chat/anthropic/#integration-details) to generate the submitted code." ] }, { @@ -483,7 +483,6 @@ "\n", "def evaluate(state: State):\n", " test_cases = state[\"test_cases\"]\n", - " runtime_limit = state[\"runtime_limit\"]\n", " ai_message: AIMessage = state[\"messages\"][-1]\n", " if not ai_message.tool_calls:\n", " return {\n", @@ -586,7 +585,7 @@ "\n", "try:\n", " display(Image(graph.get_graph().draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -736,7 +735,6 @@ } ], "source": [ - "from langchain_core.messages import BaseMessage\n", "from langchain_core.tracers.context import tracing_v2_enabled\n", "from langsmith import Client\n", "\n", @@ -745,7 +743,7 @@ "def _hide_test_cases(inputs):\n", " copied = inputs.copy()\n", " # These are tens of MB in size. No need to send them up\n", - " copied[\"test_cases\"] = f\"...\"\n", + " copied[\"test_cases\"] = \"...\"\n", " return copied\n", "\n", "\n", @@ -830,7 +828,7 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import Annotated, Optional\n", + "from typing import Annotated\n", "\n", "from typing_extensions import TypedDict\n", "\n", @@ -1059,7 +1057,7 @@ "\n", "try:\n", " display(Image(graph.get_graph().draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -1379,7 +1377,7 @@ "\n", "try:\n", " display(Image(graph.get_graph().draw_mermaid_png()))\n", - "except:\n", + "except Exception:\n", " # This requires some extra dependencies and is optional\n", " pass" ] @@ -1664,7 +1662,7 @@ " \"messages\": [\n", " (\n", " \"user\",\n", - " f\"\"\"Consider breaking down the algorithm into separate parts: reading inputs, detecting cycles using the tortoise and hare algorithm, and determining Bessie's final position by skipping ahead K steps.\n", + " \"\"\"Consider breaking down the algorithm into separate parts: reading inputs, detecting cycles using the tortoise and hare algorithm, and determining Bessie's final position by skipping ahead K steps.\n", "\n", "Read the inputs into three arrays:\n", "- Two arrays L and R for the ports (adjust for 0-based indexing)\n", diff --git a/examples/visualization.ipynb b/examples/visualization.ipynb index 1dafc6667..2c8ae3853 100644 --- a/examples/visualization.ipynb +++ b/examples/visualization.ipynb @@ -39,10 +39,12 @@ "outputs": [], "source": [ "import random\n", + "from typing import Annotated, Literal\n", + "\n", + "from typing_extensions import TypedDict\n", + "\n", "from langgraph.graph import StateGraph\n", "from langgraph.graph.message import add_messages\n", - "from typing_extensions import TypedDict\n", - "from typing import Annotated, Literal\n", "\n", "\n", "class State(TypedDict):\n", @@ -265,8 +267,8 @@ } ], "source": [ - "from langchain_core.runnables.graph import CurveStyle, NodeColors, MermaidDrawMethod\n", - "from IPython.display import display, HTML, Image\n", + "from IPython.display import Image, display\n", + "from langchain_core.runnables.graph import CurveStyle, MermaidDrawMethod, NodeColors\n", "\n", "display(\n", " Image(\n", @@ -421,7 +423,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.12.2" } }, "nbformat": 4, diff --git a/examples/web-navigation/web_voyager.ipynb b/examples/web-navigation/web_voyager.ipynb index 4c4937a0e..728003605 100644 --- a/examples/web-navigation/web_voyager.ipynb +++ b/examples/web-navigation/web_voyager.ipynb @@ -29,24 +29,12 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "af83b042", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n", - "langchain-experimental 0.0.47 requires langchain<0.1,>=0.0.350, but you have langchain 0.1.4 which is incompatible.\u001b[0m\u001b[31m\n", - "\u001b[0m\n", - "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.3.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.0\u001b[0m\n", - "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], + "outputs": [], "source": [ + "%%capture --no-stderr\n", "%pip install -U --quiet langgraph langsmith langchain_openai" ] }, @@ -199,10 +187,10 @@ " bbox_id = int(bbox_id)\n", " try:\n", " bbox = state[\"bboxes\"][bbox_id]\n", - " except:\n", + " except Exception:\n", " return f\"Error: no bbox for : {bbox_id}\"\n", " x, y = bbox[\"x\"], bbox[\"y\"]\n", - " res = await page.mouse.click(x, y)\n", + " await page.mouse.click(x, y)\n", " # TODO: In the paper, they automatically parse any downloaded PDFs\n", " # We could add something similar here as well and generally\n", " # improve response format.\n", @@ -308,7 +296,6 @@ "metadata": {}, "outputs": [], "source": [ - "import asyncio\n", "import base64\n", "\n", "from langchain_core.runnables import chain as chain_decorator\n", @@ -327,7 +314,7 @@ " try:\n", " bboxes = await page.evaluate(\"markPage()\")\n", " break\n", - " except:\n", + " except Exception:\n", " # May be loading...\n", " asyncio.sleep(3)\n", " screenshot = await page.screenshot()\n", @@ -358,7 +345,6 @@ "source": [ "from langchain import hub\n", "from langchain_core.output_parsers import StrOutputParser\n", - "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", "from langchain_core.runnables import RunnablePassthrough\n", "from langchain_openai import ChatOpenAI\n", "\n", @@ -470,6 +456,7 @@ "outputs": [], "source": [ "from langchain_core.runnables import RunnableLambda\n", + "\n", "from langgraph.graph import END, StateGraph\n", "\n", "graph_builder = StateGraph(AgentState)\n", @@ -538,7 +525,6 @@ "metadata": {}, "outputs": [], "source": [ - "import playwright\n", "from IPython import display\n", "from playwright.async_api import async_playwright\n", "\n", diff --git a/langgraph/channels/any_value.py b/langgraph/channels/any_value.py index caddc05bf..23602bcfe 100644 --- a/langgraph/channels/any_value.py +++ b/langgraph/channels/any_value.py @@ -45,15 +45,16 @@ class AnyValue(Generic[Value], BaseChannel[Value, Value, Value]): except AttributeError: pass - def update(self, values: Sequence[Value]) -> None: + def update(self, values: Sequence[Value]) -> bool: if len(values) == 0: try: del self.value + return True except AttributeError: - pass - return + return False self.value = values[-1] + return True def get(self) -> Value: try: diff --git a/langgraph/channels/base.py b/langgraph/channels/base.py index 01d595464..bb88ba2c6 100644 --- a/langgraph/channels/base.py +++ b/langgraph/channels/base.py @@ -1,12 +1,10 @@ from abc import ABC, abstractmethod from contextlib import asynccontextmanager, contextmanager -from datetime import datetime, timezone from typing import ( Any, AsyncGenerator, Generator, Generic, - Mapping, Optional, Sequence, TypeVar, @@ -14,8 +12,6 @@ from typing import ( from typing_extensions import Self -from langgraph.checkpoint.base import Checkpoint -from langgraph.checkpoint.id import uuid6 from langgraph.errors import EmptyChannelError, InvalidUpdateError Value = TypeVar("Value") @@ -62,12 +58,13 @@ class BaseChannel(Generic[Value, Update, C], ABC): # state methods @abstractmethod - def update(self, values: Sequence[Update]) -> None: + def update(self, values: Sequence[Update]) -> bool: """Update the channel's value with the given sequence of updates. The order of the updates in the sequence is arbitrary. This method is called by Pregel for all channels at the end of each step. If there are no updates, it is called with an empty sequence. - Raises InvalidUpdateError if the sequence of updates is invalid.""" + Raises InvalidUpdateError if the sequence of updates is invalid. + Returns True if the channel was updated, False otherwise.""" @abstractmethod def get(self) -> Value: @@ -75,76 +72,16 @@ class BaseChannel(Generic[Value, Update, C], ABC): Raises EmptyChannelError if the channel is empty (never updated yet).""" - def consume(self) -> None: + def consume(self) -> bool: """Mark the current value of the channel as consumed. By default, no-op. This is called by Pregel before the start of the next step, for all - channels that triggered a node. + channels that triggered a node. If the channel was updated, return True. """ - pass - - -@contextmanager -def ChannelsManager( - channels: Mapping[str, BaseChannel], - checkpoint: Checkpoint, -) -> Generator[Mapping[str, BaseChannel], None, None]: - """Manage channels for the lifetime of a Pregel invocation (multiple steps).""" - # TODO use https://docs.python.org/3/library/contextlib.html#contextlib.ExitStack - empty = { - k: v.from_checkpoint(checkpoint["channel_values"].get(k)) - for k, v in channels.items() - } - try: - yield {k: v.__enter__() for k, v in empty.items()} - finally: - for v in empty.values(): - v.__exit__(None, None, None) - - -@asynccontextmanager -async def AsyncChannelsManager( - channels: Mapping[str, BaseChannel], - checkpoint: Checkpoint, -) -> AsyncGenerator[Mapping[str, BaseChannel], None]: - """Manage channels for the lifetime of a Pregel invocation (multiple steps).""" - empty = { - k: v.afrom_checkpoint(checkpoint["channel_values"].get(k)) - for k, v in channels.items() - } - try: - yield {k: await v.__aenter__() for k, v in empty.items()} - finally: - for v in empty.values(): - await v.__aexit__(None, None, None) - - -def create_checkpoint( - checkpoint: Checkpoint, channels: Mapping[str, BaseChannel], step: int -) -> Checkpoint: - """Create a checkpoint for the given channels.""" - ts = datetime.now(timezone.utc).isoformat() - values: dict[str, Any] = {} - for k, v in channels.items(): - try: - values[k] = v.checkpoint() - except EmptyChannelError: - pass - return Checkpoint( - v=1, - ts=ts, - id=str(uuid6(clock_seq=step)), - channel_values=values, - channel_versions=checkpoint["channel_versions"], - versions_seen=checkpoint["versions_seen"], - pending_sends=checkpoint.get("pending_sends", []), - ) + return False __all__ = [ "BaseChannel", - "ChannelsManager", - "AsyncChannelsManager", - "create_checkpoint", "EmptyChannelError", "InvalidUpdateError", ] diff --git a/langgraph/channels/binop.py b/langgraph/channels/binop.py index 03b4b62d8..b51b8efe1 100644 --- a/langgraph/channels/binop.py +++ b/langgraph/channels/binop.py @@ -85,15 +85,15 @@ class BinaryOperatorAggregate(Generic[Value], BaseChannel[Value, Value, Value]): except AttributeError: pass - def update(self, values: Sequence[Value]) -> None: + def update(self, values: Sequence[Value]) -> bool: if not values: - return + return False if not hasattr(self, "value"): self.value = values[0] values = values[1:] - for value in values: self.value = self.operator(self.value, value) + return True def get(self) -> Value: try: diff --git a/langgraph/channels/context.py b/langgraph/channels/context.py index 205f9d6d3..dbcfa1627 100644 --- a/langgraph/channels/context.py +++ b/langgraph/channels/context.py @@ -95,9 +95,10 @@ class Context(Generic[Value], BaseChannel[Value, None, None]): with self.from_checkpoint() as empty: yield empty - def update(self, values: Sequence[None]) -> None: + def update(self, values: Sequence[None]) -> bool: if values: raise InvalidUpdateError() + return False def get(self) -> Value: try: diff --git a/langgraph/channels/dynamic_barrier_value.py b/langgraph/channels/dynamic_barrier_value.py index 45cd83a7c..875c55c45 100644 --- a/langgraph/channels/dynamic_barrier_value.py +++ b/langgraph/channels/dynamic_barrier_value.py @@ -59,27 +59,34 @@ class DynamicBarrierValue( finally: pass - def update(self, values: Sequence[Union[Value, WaitForNames]]) -> None: + def update(self, values: Sequence[Union[Value, WaitForNames]]) -> bool: if wait_for_names := [v for v in values if isinstance(v, WaitForNames)]: if len(wait_for_names) > 1: raise InvalidUpdateError( "Received multiple WaitForNames updates in the same step." ) self.names = wait_for_names[0].names + return True elif self.names is not None: + updated = False for value in values: assert not isinstance(value, WaitForNames) if value in self.names: - self.seen.add(value) + if value not in self.seen: + self.seen.add(value) + updated = True else: raise InvalidUpdateError(f"Value {value} not in {self.names}") + return updated def get(self) -> Value: if self.seen != self.names: raise EmptyChannelError() return None - def consume(self) -> None: + def consume(self) -> bool: if self.seen == self.names: self.seen = set() self.names = None + return True + return False diff --git a/langgraph/channels/ephemeral_value.py b/langgraph/channels/ephemeral_value.py index 17a11f552..eb1dd9d36 100644 --- a/langgraph/channels/ephemeral_value.py +++ b/langgraph/channels/ephemeral_value.py @@ -45,20 +45,20 @@ class EphemeralValue(Generic[Value], BaseChannel[Value, Value, Value]): except AttributeError: pass - def update(self, values: Sequence[Value]) -> None: + def update(self, values: Sequence[Value]) -> bool: if len(values) == 0: try: del self.value + return True except AttributeError: - pass - finally: - return + return False if len(values) != 1 and self.guard: raise InvalidUpdateError( "EphemeralValue can only receive one value per step." ) self.value = values[-1] + return True def get(self) -> Value: try: diff --git a/langgraph/channels/last_value.py b/langgraph/channels/last_value.py index c3748b7f9..dbae1306d 100644 --- a/langgraph/channels/last_value.py +++ b/langgraph/channels/last_value.py @@ -44,13 +44,14 @@ class LastValue(Generic[Value], BaseChannel[Value, Value, Value]): except AttributeError: pass - def update(self, values: Sequence[Value]) -> None: + def update(self, values: Sequence[Value]) -> bool: if len(values) == 0: - return + return False if len(values) != 1: raise InvalidUpdateError("LastValue can only receive one value per step.") self.value = values[-1] + return True def get(self) -> Value: try: diff --git a/langgraph/channels/manager.py b/langgraph/channels/manager.py new file mode 100644 index 000000000..1c230ac00 --- /dev/null +++ b/langgraph/channels/manager.py @@ -0,0 +1,65 @@ +from contextlib import asynccontextmanager, contextmanager +from datetime import datetime, timezone +from typing import Any, AsyncGenerator, Generator, Mapping + +from langgraph.channels.base import BaseChannel +from langgraph.checkpoint.base import Checkpoint +from langgraph.checkpoint.id import uuid6 +from langgraph.errors import EmptyChannelError + + +@contextmanager +def ChannelsManager( + channels: Mapping[str, BaseChannel], + checkpoint: Checkpoint, +) -> Generator[Mapping[str, BaseChannel], None, None]: + """Manage channels for the lifetime of a Pregel invocation (multiple steps).""" + # TODO use https://docs.python.org/3/library/contextlib.html#contextlib.ExitStack + empty = { + k: v.from_checkpoint(checkpoint["channel_values"].get(k)) + for k, v in channels.items() + } + try: + yield {k: v.__enter__() for k, v in empty.items()} + finally: + for v in empty.values(): + v.__exit__(None, None, None) + + +@asynccontextmanager +async def AsyncChannelsManager( + channels: Mapping[str, BaseChannel], + checkpoint: Checkpoint, +) -> AsyncGenerator[Mapping[str, BaseChannel], None]: + """Manage channels for the lifetime of a Pregel invocation (multiple steps).""" + empty = { + k: v.afrom_checkpoint(checkpoint["channel_values"].get(k)) + for k, v in channels.items() + } + try: + yield {k: await v.__aenter__() for k, v in empty.items()} + finally: + for v in empty.values(): + await v.__aexit__(None, None, None) + + +def create_checkpoint( + checkpoint: Checkpoint, channels: Mapping[str, BaseChannel], step: int +) -> Checkpoint: + """Create a checkpoint for the given channels.""" + ts = datetime.now(timezone.utc).isoformat() + values: dict[str, Any] = {} + for k, v in channels.items(): + try: + values[k] = v.checkpoint() + except EmptyChannelError: + pass + return Checkpoint( + v=1, + ts=ts, + id=str(uuid6(clock_seq=step)), + channel_values=values, + channel_versions=checkpoint["channel_versions"], + versions_seen=checkpoint["versions_seen"], + pending_sends=checkpoint.get("pending_sends", []), + ) diff --git a/langgraph/channels/named_barrier_value.py b/langgraph/channels/named_barrier_value.py index 8181d0a02..bd97754bf 100644 --- a/langgraph/channels/named_barrier_value.py +++ b/langgraph/channels/named_barrier_value.py @@ -41,18 +41,24 @@ class NamedBarrierValue(Generic[Value], BaseChannel[Value, Value, set[Value]]): finally: pass - def update(self, values: Sequence[Value]) -> None: + def update(self, values: Sequence[Value]) -> bool: + updated = False for value in values: if value in self.names: - self.seen.add(value) + if value not in self.seen: + self.seen.add(value) + updated = True else: raise InvalidUpdateError(f"Value {value} not in {self.names}") + return updated def get(self) -> Value: if self.seen != self.names: raise EmptyChannelError() return None - def consume(self) -> None: + def consume(self) -> bool: if self.seen == self.names: self.seen = set() + return True + return False diff --git a/langgraph/channels/topic.py b/langgraph/channels/topic.py index c9bebdbfd..960cd0dfa 100644 --- a/langgraph/channels/topic.py +++ b/langgraph/channels/topic.py @@ -4,6 +4,7 @@ from typing import Any, Generator, Generic, Iterator, Optional, Sequence, Type, from typing_extensions import Self from langgraph.channels.base import BaseChannel, Value +from langgraph.errors import EmptyChannelError def flatten(values: Sequence[Union[Value, list[Value]]]) -> Iterator[Value]: @@ -66,6 +67,7 @@ class Topic( pass def update(self, values: Sequence[Union[Value, list[Value]]]) -> None: + current = list(self.values) if not self.accumulate: self.values = list[Value]() if flat_values := flatten(values): @@ -76,6 +78,10 @@ class Topic( self.values.append(value) else: self.values.extend(flat_values) + return self.values != current def get(self) -> Sequence[Value]: - return list(self.values) + if self.values: + return list(self.values) + else: + raise EmptyChannelError diff --git a/langgraph/checkpoint/base.py b/langgraph/checkpoint/base.py index a639bc336..fab38fa99 100644 --- a/langgraph/checkpoint/base.py +++ b/langgraph/checkpoint/base.py @@ -11,15 +11,20 @@ from typing import ( NamedTuple, Optional, TypedDict, + TypeVar, + Union, ) from langchain_core.runnables import ConfigurableFieldSpec, RunnableConfig +from langgraph.channels.base import BaseChannel from langgraph.checkpoint.id import uuid6 from langgraph.constants import Send from langgraph.serde.base import SerializerProtocol from langgraph.serde.jsonplus import JsonPlusSerializer +V = TypeVar("V", int, float, str) + # Marked as total=False to allow for future expansion. class CheckpointMetadata(TypedDict, total=False): @@ -62,13 +67,13 @@ class Checkpoint(TypedDict): Mapping from channel name to channel snapshot value. """ - channel_versions: defaultdict[str, int] + channel_versions: dict[str, Union[str, int, float]] """The versions of the channels at the time of the checkpoint. The keys are channel names and the values are the logical time step at which the channel was last updated. """ - versions_seen: defaultdict[str, defaultdict[str, int]] + versions_seen: defaultdict[str, dict[str, Union[str, int, float]]] """Map from node ID to map from channel name to version seen. This keeps track of the versions of the channels that each node has seen. @@ -80,18 +85,14 @@ class Checkpoint(TypedDict): Cleared by the next checkpoint.""" -def _seen_dict(): - return defaultdict(int) - - def empty_checkpoint() -> Checkpoint: return Checkpoint( v=1, id=str(uuid6(clock_seq=-2)), ts=datetime.now(timezone.utc).isoformat(), channel_values={}, - channel_versions=defaultdict(int), - versions_seen=defaultdict(_seen_dict), + channel_versions={}, + versions_seen=defaultdict(dict), pending_sends=[], ) @@ -102,10 +103,10 @@ def copy_checkpoint(checkpoint: Checkpoint) -> Checkpoint: ts=checkpoint["ts"], id=checkpoint["id"], channel_values=checkpoint["channel_values"].copy(), - channel_versions=defaultdict(int, checkpoint["channel_versions"]), + channel_versions=checkpoint["channel_versions"].copy(), versions_seen=defaultdict( - _seen_dict, - {k: defaultdict(int, v) for k, v in checkpoint["versions_seen"].items()}, + dict, + {k: v.copy() for k, v in checkpoint["versions_seen"].items()}, ), pending_sends=checkpoint.get("pending_sends", []).copy(), ) @@ -187,6 +188,7 @@ class BaseCheckpointSaver(ABC): self, config: Optional[RunnableConfig], *, + filter: Optional[Dict[str, Any]] = None, before: Optional[RunnableConfig] = None, limit: Optional[int] = None, ) -> AsyncIterator[CheckpointTuple]: @@ -200,3 +202,8 @@ class BaseCheckpointSaver(ABC): metadata: CheckpointMetadata, ) -> RunnableConfig: raise NotImplementedError + + def get_next_version(self, current: Optional[V], channel: BaseChannel) -> V: + """Get the next version of a channel. Default is to use int versions, incrementing by 1. If you override, you can use str/int/float versions, + as long as they are monotonically increasing.""" + return current + 1 if current is not None else 1 diff --git a/langgraph/checkpoint/sqlite.py b/langgraph/checkpoint/sqlite.py index 3836a041f..e13f14bf9 100644 --- a/langgraph/checkpoint/sqlite.py +++ b/langgraph/checkpoint/sqlite.py @@ -3,12 +3,14 @@ import pickle import sqlite3 import threading from contextlib import AbstractContextManager, contextmanager +from hashlib import md5 from types import TracebackType from typing import Any, AsyncIterator, Dict, Iterator, Optional, Sequence, Tuple from langchain_core.runnables import RunnableConfig from typing_extensions import Self +from langgraph.channels.base import BaseChannel from langgraph.checkpoint.base import ( BaseCheckpointSaver, Checkpoint, @@ -16,6 +18,7 @@ from langgraph.checkpoint.base import ( CheckpointTuple, SerializerProtocol, ) +from langgraph.errors import EmptyChannelError from langgraph.serde.jsonplus import JsonPlusSerializer @@ -431,6 +434,18 @@ class SqliteSaver(BaseCheckpointSaver, AbstractContextManager): """ raise NotImplementedError(_AIO_ERROR_MSG) + def get_next_version(self, current: Optional[str], channel: BaseChannel) -> str: + if current is None: + current_v = 0 + else: + current_v = int(current.split(".")[0]) + next_v = current_v + 1 + try: + next_h = md5(self.serde.dumps(channel.checkpoint())).hexdigest() + except EmptyChannelError: + next_h = "" + return f"{next_v:032}.{next_h}" + def _metadata_predicate( metadata_filter: Dict[str, Any], diff --git a/langgraph/graph/state.py b/langgraph/graph/state.py index 1af5d3ae3..0a88711a2 100644 --- a/langgraph/graph/state.py +++ b/langgraph/graph/state.py @@ -155,7 +155,14 @@ class StateGraph(Graph): ) -> None: if not isinstance(node, str): action = node - node = getattr(action, "name", action.__name__) + if isinstance(action, Runnable): + node = action.name + else: + node = getattr(action, "__name__", action.__class__.__name__) + if node is None: + raise ValueError( + "Node name must be provided if action is not a function" + ) if node in self.channels: raise ValueError(f"'{node}' is already being used as a state key") return super().add_node(node, action) diff --git a/langgraph/managed/few_shot.py b/langgraph/managed/few_shot.py index 9d46739eb..a8ba4da82 100644 --- a/langgraph/managed/few_shot.py +++ b/langgraph/managed/few_shot.py @@ -17,7 +17,7 @@ from typing import ( from langchain_core.runnables import RunnableConfig from typing_extensions import Self -from langgraph.channels.base import AsyncChannelsManager, ChannelsManager +from langgraph.channels.manager import AsyncChannelsManager, ChannelsManager from langgraph.managed.base import ConfiguredManagedValue, ManagedValue, V from langgraph.pregel import Pregel from langgraph.pregel.io import read_channels diff --git a/langgraph/prebuilt/tool_node.py b/langgraph/prebuilt/tool_node.py index b747b5091..ecb051581 100644 --- a/langgraph/prebuilt/tool_node.py +++ b/langgraph/prebuilt/tool_node.py @@ -22,11 +22,28 @@ def str_output(output: Any) -> str: class ToolNode(RunnableCallable): - """ - A node that runs the tools requested in the last AIMessage. It can be used + """A node that runs the tools requested in the last AIMessage. It can be used either in StateGraph with a "messages" key or in MessageGraph. If multiple tool calls are requested, they will be run in parallel. The output will be a list of ToolMessages, one for each tool call. + + The `ToolNode` is roughly analogous to: + + ```python + tools_by_name = {tool.name: tool for tool in tools} + def tool_node(state: dict): + result = [] + for tool_call in state["messages"][-1].tool_calls: + tool = tools_by_name[tool_call["name"]] + observation = tool.invoke(tool_call["args"]) + result.append(ToolMessage(content=observation, tool_call_id=tool_call["id"])) + return {"messages": result} + ``` + + Important: + - The state MUST contain a list of messages. + - The last message MUST be an `AIMessage`. + - The `AIMessage` MUST have `tool_calls` populated. """ def __init__( diff --git a/langgraph/pregel/__init__.py b/langgraph/pregel/__init__.py index 4d53c6be9..f87126b3d 100644 --- a/langgraph/pregel/__init__.py +++ b/langgraph/pregel/__init__.py @@ -52,10 +52,12 @@ from langchain_core.tracers._streaming import _StreamingCallbackHandler from typing_extensions import Self from langgraph.channels.base import ( - AsyncChannelsManager, BaseChannel, - ChannelsManager, EmptyChannelError, +) +from langgraph.channels.manager import ( + AsyncChannelsManager, + ChannelsManager, create_checkpoint, ) from langgraph.checkpoint.base import ( @@ -564,7 +566,9 @@ class Pregel( ), ) # apply to checkpoint and save - _apply_writes(checkpoint, channels, task.writes) + _apply_writes( + checkpoint, channels, task.writes, self.checkpointer.get_next_version + ) step = saved.metadata.get("step", -2) + 1 if saved else -1 # merge configurable fields with previous checkpoint config @@ -650,7 +654,9 @@ class Pregel( ), ) # apply to checkpoint and save - _apply_writes(checkpoint, channels, task.writes) + _apply_writes( + checkpoint, channels, task.writes, self.checkpointer.get_next_version + ) step = saved.metadata.get("step", -2) + 1 if saved else -1 # merge configurable fields with previous checkpoint config @@ -847,9 +853,19 @@ class Pregel( config, -1, for_execution=True, + get_next_version=self.checkpointer.get_next_version + if self.checkpointer + else _increment, ) # apply input writes - _apply_writes(checkpoint, channels, input_writes) + _apply_writes( + checkpoint, + channels, + input_writes, + self.checkpointer.get_next_version + if self.checkpointer + else _increment, + ) # save input checkpoint yield from put_checkpoint( { @@ -865,8 +881,9 @@ class Pregel( # past previous interrupt, if any checkpoint = copy_checkpoint(checkpoint) for k in self.stream_channels_list: - version = checkpoint["channel_versions"][k] - checkpoint["versions_seen"][INTERRUPT][k] = version + if k in checkpoint["channel_versions"]: + version = checkpoint["channel_versions"][k] + checkpoint["versions_seen"][INTERRUPT][k] = version # Similarly to Bulk Synchronous Parallel / Pregel model # computation proceeds in steps, while there are channel updates @@ -884,6 +901,9 @@ class Pregel( step, for_execution=True, manager=run_manager, + get_next_version=self.checkpointer.get_next_version + if self.checkpointer + else _increment, ) # if no more tasks, we're done @@ -975,7 +995,14 @@ class Pregel( ) # apply writes to channels - _apply_writes(checkpoint, channels, pending_writes) + _apply_writes( + checkpoint, + channels, + pending_writes, + self.checkpointer.get_next_version + if self.checkpointer + else _increment, + ) # yield values output if "values" in stream_modes: @@ -1176,9 +1203,19 @@ class Pregel( config, -1, for_execution=True, + get_next_version=self.checkpointer.get_next_version + if self.checkpointer + else _increment, ) # apply input writes - _apply_writes(checkpoint, channels, input_writes) + _apply_writes( + checkpoint, + channels, + input_writes, + self.checkpointer.get_next_version + if self.checkpointer + else _increment, + ) # save input checkpoint for chunk in put_checkpoint( {"source": "input", "step": start, "writes": input} @@ -1191,8 +1228,9 @@ class Pregel( # past previous interrupt, if any checkpoint = copy_checkpoint(checkpoint) for k in self.stream_channels_list: - version = checkpoint["channel_versions"][k] - checkpoint["versions_seen"][INTERRUPT][k] = version + if k in checkpoint["channel_versions"]: + version = checkpoint["channel_versions"][k] + checkpoint["versions_seen"][INTERRUPT][k] = version # Similarly to Bulk Synchronous Parallel / Pregel model # computation proceeds in steps, while there are channel updates @@ -1210,6 +1248,9 @@ class Pregel( step, for_execution=True, manager=run_manager, + get_next_version=self.checkpointer.get_next_version + if self.checkpointer + else _increment, ) # if no more tasks, we're done @@ -1304,7 +1345,14 @@ class Pregel( ) # apply writes to channels - _apply_writes(checkpoint, channels, pending_writes) + _apply_writes( + checkpoint, + channels, + pending_writes, + self.checkpointer.get_next_version + if self.checkpointer + else _increment, + ) # yield current values if "values" in stream_modes: @@ -1360,7 +1408,7 @@ class Pregel( except NameError: pass # wait for all background tasks to finish - await asyncio.gather(*bg) + await asyncio.shield(asyncio.gather(*bg)) def invoke( self, @@ -1503,12 +1551,15 @@ def _should_interrupt( snapshot_channels: Sequence[str], tasks: list[PregelExecutableTask], ) -> bool: + version_type = type(next(iter(checkpoint["channel_versions"].values()), None)) + null_version = version_type() # defaultdicts are mutated on access :( so we need to copy - seen = checkpoint["versions_seen"].copy()[INTERRUPT].copy() + seen = checkpoint["versions_seen"].copy()[INTERRUPT] return ( # interrupt if any of snapshopt_channels has been updated since last interrupt any( - checkpoint["channel_versions"][chan] > seen[chan] + checkpoint["channel_versions"].get(chan, null_version) + > seen.get(chan, null_version) for chan in snapshot_channels ) # and any triggered node is in interrupt_nodes list @@ -1534,7 +1585,7 @@ def _local_read( if fresh: checkpoint = create_checkpoint(checkpoint, channels, -1) with ChannelsManager(channels, checkpoint) as channels: - _apply_writes(copy_checkpoint(checkpoint), channels, writes) + _apply_writes(copy_checkpoint(checkpoint), channels, writes, None) return read_channels(channels, select) else: return read_channels(channels, select) @@ -1559,10 +1610,15 @@ def _local_write( commit(writes) +def _increment(current: Optional[int], channel: BaseChannel) -> int: + return current + 1 if current is not None else 1 + + def _apply_writes( checkpoint: Checkpoint, channels: Mapping[str, BaseChannel], pending_writes: Sequence[tuple[str, Any]], + get_next_version: Optional[Callable[[int, BaseChannel], int]], ) -> None: if checkpoint["pending_sends"]: checkpoint["pending_sends"].clear() @@ -1579,24 +1635,30 @@ def _apply_writes( if checkpoint["channel_versions"]: max_version = max(checkpoint["channel_versions"].values()) else: - max_version = 0 + max_version = None updated_channels: set[str] = set() # Apply writes to channels for chan, vals in pending_writes_by_channel.items(): if chan in channels: try: - channels[chan].update(vals) + updated = channels[chan].update(vals) except InvalidUpdateError as e: raise InvalidUpdateError( f"Invalid update for channel {chan} with values {vals}" ) from e - checkpoint["channel_versions"][chan] = max_version + 1 + if updated and get_next_version is not None: + checkpoint["channel_versions"][chan] = get_next_version( + max_version, channels[chan] + ) updated_channels.add(chan) # Channels that weren't updated in this step are notified of a new step for chan in channels: if chan not in updated_channels: - channels[chan].update([]) + if channels[chan].update([]) and get_next_version is not None: + checkpoint["channel_versions"][chan] = get_next_version( + max_version, channels[chan] + ) @overload @@ -1608,6 +1670,7 @@ def _prepare_next_tasks( config: RunnableConfig, step: int, for_execution: Literal[False], + get_next_version: Literal[None] = None, manager: Literal[None] = None, ) -> tuple[Checkpoint, list[PregelTaskDescription]]: ... @@ -1622,7 +1685,8 @@ def _prepare_next_tasks( config: RunnableConfig, step: int, for_execution: Literal[True], - manager: Union[ParentRunManager, AsyncParentRunManager], + get_next_version: Callable[[int, BaseChannel], int], + manager: Union[None, ParentRunManager, AsyncParentRunManager], ) -> tuple[Checkpoint, list[PregelExecutableTask]]: ... @@ -1636,6 +1700,7 @@ def _prepare_next_tasks( step: int, *, for_execution: bool, + get_next_version: Union[None, Callable[[int, BaseChannel], int]] = None, manager: Union[None, ParentRunManager, AsyncParentRunManager] = None, ) -> tuple[Checkpoint, Union[list[PregelTaskDescription], list[PregelExecutableTask]]]: checkpoint = copy_checkpoint(checkpoint) @@ -1689,6 +1754,10 @@ def _prepare_next_tasks( channels_to_consume = set() # Check if any processes should be run in next step # If so, prepare the values to be passed to them + version_type = type(next(iter(checkpoint["channel_versions"].values()), None)) + null_version = version_type() + if null_version is None: + return checkpoint, tasks for name, proc in processes.items(): seen = checkpoint["versions_seen"][name] # If any of the channels read by this process were updated @@ -1698,7 +1767,8 @@ def _prepare_next_tasks( if not isinstance( read_channel(channels, chan, return_exception=True), EmptyChannelError ) - and checkpoint["channel_versions"][chan] > seen[chan] + and checkpoint["channel_versions"].get(chan, null_version) + > seen.get(chan, null_version) ]: channels_to_consume.update(triggers) try: @@ -1712,6 +1782,7 @@ def _prepare_next_tasks( { chan: checkpoint["channel_versions"][chan] for chan in proc.triggers + if chan in checkpoint["channel_versions"] } ) @@ -1757,10 +1828,18 @@ def _prepare_next_tasks( ) else: tasks.append(PregelTaskDescription(name, val)) + # Find the highest version of all channels + if checkpoint["channel_versions"]: + max_version = max(checkpoint["channel_versions"].values()) + else: + max_version = None # Consume all channels that were read if for_execution: for chan in channels_to_consume: - channels[chan].consume() + if channels[chan].consume(): + checkpoint["channel_versions"][chan] = get_next_version( + max_version, channels[chan] + ) return checkpoint, tasks diff --git a/langgraph/pregel/retry.py b/langgraph/pregel/retry.py index 24d660e94..6ec224012 100644 --- a/langgraph/pregel/retry.py +++ b/langgraph/pregel/retry.py @@ -13,6 +13,8 @@ logger = logging.getLogger(__name__) def default_retry_on(exc: Exception) -> bool: + if isinstance(exc, ConnectionError): + return True if isinstance( exc, ( @@ -24,6 +26,10 @@ def default_retry_on(exc: Exception) -> bool: NameError, SyntaxError, RuntimeError, + ReferenceError, + StopIteration, + StopAsyncIteration, + OSError, ), ): return False @@ -90,7 +96,7 @@ def run_with_retry( ) # log the retry logger.info( - f"Retrying task {task.name} after {interval:.2f} seconds (attempt {attempts})" + f"Retrying task {task.name} after {interval:.2f} seconds (attempt {attempts}) after {exc.__class__.__name__} {exc}" ) @@ -138,5 +144,5 @@ async def arun_with_retry( ) # log the retry logger.info( - f"Retrying task {task.name} after {interval:.2f} seconds (attempt {attempts})" + f"Retrying task {task.name} after {interval:.2f} seconds (attempt {attempts}) after {exc.__class__.__name__} {exc}" ) diff --git a/poetry.lock b/poetry.lock index 29d0c0671..1d7c2fd97 100644 --- a/poetry.lock +++ b/poetry.lock @@ -127,30 +127,30 @@ docs = ["sphinx (==6.1.3)", "sphinx-mdinclude (==0.5.3)"] [[package]] name = "annotated-types" -version = "0.6.0" +version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" files = [ - {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, - {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, ] [[package]] name = "anthropic" -version = "0.26.0" +version = "0.28.0" description = "The official Python library for the anthropic API" optional = false python-versions = ">=3.7" files = [ - {file = "anthropic-0.26.0-py3-none-any.whl", hash = "sha256:38fc415561d71dcf263b89da0cc6ecec498379b56256fc4242e9128bc707b283"}, - {file = "anthropic-0.26.0.tar.gz", hash = "sha256:6aaffeb05d515cf9788eef57150a5f827f3786883628ccac71dbe5671ab6f44e"}, + {file = "anthropic-0.28.0-py3-none-any.whl", hash = "sha256:2b620b21aee3d20c5d8005483c34df239d53ae895687113b26b8a36892a7e20f"}, ] [package.dependencies] anyio = ">=3.5.0,<5" distro = ">=1.7.0,<2" httpx = ">=0.23.0,<1" +jiter = ">=0.4.0,<1" pydantic = ">=1.9.0,<3" sniffio = "*" tokenizers = ">=0.13.0" @@ -162,13 +162,13 @@ vertex = ["google-auth (>=2,<3)"] [[package]] name = "anyio" -version = "4.3.0" +version = "4.4.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.8" files = [ - {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, - {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, + {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, + {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, ] [package.dependencies] @@ -333,13 +333,13 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p [[package]] name = "babel" -version = "2.14.0" +version = "2.15.0" description = "Internationalization utilities" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, - {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, + {file = "Babel-2.15.0-py3-none-any.whl", hash = "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb"}, + {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"}, ] [package.extras] @@ -386,13 +386,13 @@ css = ["tinycss2 (>=1.1.0,<1.3)"] [[package]] name = "certifi" -version = "2024.2.2" +version = "2024.6.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"}, + {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"}, ] [[package]] @@ -588,63 +588,63 @@ test = ["pytest"] [[package]] name = "coverage" -version = "7.5.0" +version = "7.5.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:432949a32c3e3f820af808db1833d6d1631664d53dd3ce487aa25d574e18ad1c"}, - {file = "coverage-7.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2bd7065249703cbeb6d4ce679c734bef0ee69baa7bff9724361ada04a15b7e3b"}, - {file = "coverage-7.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbfe6389c5522b99768a93d89aca52ef92310a96b99782973b9d11e80511f932"}, - {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39793731182c4be939b4be0cdecde074b833f6171313cf53481f869937129ed3"}, - {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85a5dbe1ba1bf38d6c63b6d2c42132d45cbee6d9f0c51b52c59aa4afba057517"}, - {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:357754dcdfd811462a725e7501a9b4556388e8ecf66e79df6f4b988fa3d0b39a"}, - {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a81eb64feded34f40c8986869a2f764f0fe2db58c0530d3a4afbcde50f314880"}, - {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:51431d0abbed3a868e967f8257c5faf283d41ec882f58413cf295a389bb22e58"}, - {file = "coverage-7.5.0-cp310-cp310-win32.whl", hash = "sha256:f609ebcb0242d84b7adeee2b06c11a2ddaec5464d21888b2c8255f5fd6a98ae4"}, - {file = "coverage-7.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:6782cd6216fab5a83216cc39f13ebe30adfac2fa72688c5a4d8d180cd52e8f6a"}, - {file = "coverage-7.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e768d870801f68c74c2b669fc909839660180c366501d4cc4b87efd6b0eee375"}, - {file = "coverage-7.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84921b10aeb2dd453247fd10de22907984eaf80901b578a5cf0bb1e279a587cb"}, - {file = "coverage-7.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710c62b6e35a9a766b99b15cdc56d5aeda0914edae8bb467e9c355f75d14ee95"}, - {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c379cdd3efc0658e652a14112d51a7668f6bfca7445c5a10dee7eabecabba19d"}, - {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea9d3ca80bcf17edb2c08a4704259dadac196fe5e9274067e7a20511fad1743"}, - {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:41327143c5b1d715f5f98a397608f90ab9ebba606ae4e6f3389c2145410c52b1"}, - {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:565b2e82d0968c977e0b0f7cbf25fd06d78d4856289abc79694c8edcce6eb2de"}, - {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cf3539007202ebfe03923128fedfdd245db5860a36810136ad95a564a2fdffff"}, - {file = "coverage-7.5.0-cp311-cp311-win32.whl", hash = "sha256:bf0b4b8d9caa8d64df838e0f8dcf68fb570c5733b726d1494b87f3da85db3a2d"}, - {file = "coverage-7.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c6384cc90e37cfb60435bbbe0488444e54b98700f727f16f64d8bfda0b84656"}, - {file = "coverage-7.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fed7a72d54bd52f4aeb6c6e951f363903bd7d70bc1cad64dd1f087980d309ab9"}, - {file = "coverage-7.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cbe6581fcff7c8e262eb574244f81f5faaea539e712a058e6707a9d272fe5b64"}, - {file = "coverage-7.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad97ec0da94b378e593ef532b980c15e377df9b9608c7c6da3506953182398af"}, - {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd4bacd62aa2f1a1627352fe68885d6ee694bdaebb16038b6e680f2924a9b2cc"}, - {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf032b6c105881f9d77fa17d9eebe0ad1f9bfb2ad25777811f97c5362aa07f2"}, - {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ba01d9ba112b55bfa4b24808ec431197bb34f09f66f7cb4fd0258ff9d3711b1"}, - {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f0bfe42523893c188e9616d853c47685e1c575fe25f737adf473d0405dcfa7eb"}, - {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a9a7ef30a1b02547c1b23fa9a5564f03c9982fc71eb2ecb7f98c96d7a0db5cf2"}, - {file = "coverage-7.5.0-cp312-cp312-win32.whl", hash = "sha256:3c2b77f295edb9fcdb6a250f83e6481c679335ca7e6e4a955e4290350f2d22a4"}, - {file = "coverage-7.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:427e1e627b0963ac02d7c8730ca6d935df10280d230508c0ba059505e9233475"}, - {file = "coverage-7.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9dd88fce54abbdbf4c42fb1fea0e498973d07816f24c0e27a1ecaf91883ce69e"}, - {file = "coverage-7.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a898c11dca8f8c97b467138004a30133974aacd572818c383596f8d5b2eb04a9"}, - {file = "coverage-7.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07dfdd492d645eea1bd70fb1d6febdcf47db178b0d99161d8e4eed18e7f62fe7"}, - {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3d117890b6eee85887b1eed41eefe2e598ad6e40523d9f94c4c4b213258e4a4"}, - {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6afd2e84e7da40fe23ca588379f815fb6dbbb1b757c883935ed11647205111cb"}, - {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9960dd1891b2ddf13a7fe45339cd59ecee3abb6b8326d8b932d0c5da208104f"}, - {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ced268e82af993d7801a9db2dbc1d2322e786c5dc76295d8e89473d46c6b84d4"}, - {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7c211f25777746d468d76f11719e64acb40eed410d81c26cefac641975beb88"}, - {file = "coverage-7.5.0-cp38-cp38-win32.whl", hash = "sha256:262fffc1f6c1a26125d5d573e1ec379285a3723363f3bd9c83923c9593a2ac25"}, - {file = "coverage-7.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:eed462b4541c540d63ab57b3fc69e7d8c84d5957668854ee4e408b50e92ce26a"}, - {file = "coverage-7.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0194d654e360b3e6cc9b774e83235bae6b9b2cac3be09040880bb0e8a88f4a1"}, - {file = "coverage-7.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33c020d3322662e74bc507fb11488773a96894aa82a622c35a5a28673c0c26f5"}, - {file = "coverage-7.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbdf2cae14a06827bec50bd58e49249452d211d9caddd8bd80e35b53cb04631"}, - {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3235d7c781232e525b0761730e052388a01548bd7f67d0067a253887c6e8df46"}, - {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2de4e546f0ec4b2787d625e0b16b78e99c3e21bc1722b4977c0dddf11ca84e"}, - {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0e206259b73af35c4ec1319fd04003776e11e859936658cb6ceffdeba0f5be"}, - {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2055c4fb9a6ff624253d432aa471a37202cd8f458c033d6d989be4499aed037b"}, - {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:075299460948cd12722a970c7eae43d25d37989da682997687b34ae6b87c0ef0"}, - {file = "coverage-7.5.0-cp39-cp39-win32.whl", hash = "sha256:280132aada3bc2f0fac939a5771db4fbb84f245cb35b94fae4994d4c1f80dae7"}, - {file = "coverage-7.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:c58536f6892559e030e6924896a44098bc1290663ea12532c78cef71d0df8493"}, - {file = "coverage-7.5.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:2b57780b51084d5223eee7b59f0d4911c31c16ee5aa12737c7a02455829ff067"}, - {file = "coverage-7.5.0.tar.gz", hash = "sha256:cf62d17310f34084c59c01e027259076479128d11e4661bb6c9acb38c5e19bb8"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, + {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, + {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, + {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, + {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, + {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, + {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, + {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, + {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, + {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, + {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, + {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, + {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, ] [package.dependencies] @@ -655,13 +655,13 @@ toml = ["tomli"] [[package]] name = "dataclasses-json" -version = "0.6.4" +version = "0.6.7" description = "Easily serialize dataclasses to and from JSON." optional = false -python-versions = ">=3.7,<4.0" +python-versions = "<4.0,>=3.7" files = [ - {file = "dataclasses_json-0.6.4-py3-none-any.whl", hash = "sha256:f90578b8a3177f7552f4e1a6e535e84293cd5da421fcce0642d49c0d7bdf8df2"}, - {file = "dataclasses_json-0.6.4.tar.gz", hash = "sha256:73696ebf24936560cca79a2430cbc4f3dd23ac7bf46ed17f38e5e5e7657a6377"}, + {file = "dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a"}, + {file = "dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0"}, ] [package.dependencies] @@ -776,13 +776,13 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc [[package]] name = "filelock" -version = "3.13.4" +version = "3.14.0" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.13.4-py3-none-any.whl", hash = "sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f"}, - {file = "filelock-3.13.4.tar.gz", hash = "sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4"}, + {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"}, + {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"}, ] [package.extras] @@ -889,13 +889,13 @@ files = [ [[package]] name = "fsspec" -version = "2024.3.1" +version = "2024.6.0" description = "File-system specification" optional = false python-versions = ">=3.8" files = [ - {file = "fsspec-2024.3.1-py3-none-any.whl", hash = "sha256:918d18d41bf73f0e2b261824baeb1b124bcf771767e3a26425cd7dec3332f512"}, - {file = "fsspec-2024.3.1.tar.gz", hash = "sha256:f39780e282d7d117ffb42bb96992f8a90795e4d0fb0f661a70ca39fe9c43ded9"}, + {file = "fsspec-2024.6.0-py3-none-any.whl", hash = "sha256:58d7122eb8a1a46f7f13453187bfea4972d66bf01618d37366521b1998034cee"}, + {file = "fsspec-2024.6.0.tar.gz", hash = "sha256:f579960a56e6d8038a9efc8f9c77279ec12e6299aa86b0769a7e9c46b94527c2"}, ] [package.extras] @@ -903,7 +903,8 @@ abfs = ["adlfs"] adl = ["adlfs"] arrow = ["pyarrow (>=1)"] dask = ["dask", "distributed"] -devel = ["pytest", "pytest-cov"] +dev = ["pre-commit", "ruff"] +doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"] dropbox = ["dropbox", "dropboxdrivefs", "requests"] full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] fuse = ["fusepy"] @@ -920,6 +921,9 @@ s3 = ["s3fs"] sftp = ["paramiko"] smb = ["smbprotocol"] ssh = ["paramiko"] +test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"] +test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask-expr", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"] +test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"] tqdm = ["tqdm"] [[package]] @@ -1068,13 +1072,13 @@ socks = ["socksio (==1.*)"] [[package]] name = "huggingface-hub" -version = "0.22.2" +version = "0.23.3" description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" optional = false python-versions = ">=3.8.0" files = [ - {file = "huggingface_hub-0.22.2-py3-none-any.whl", hash = "sha256:3429e25f38ccb834d310804a3b711e7e4953db5a9e420cc147a5e194ca90fd17"}, - {file = "huggingface_hub-0.22.2.tar.gz", hash = "sha256:32e9a9a6843c92f253ff9ca16b9985def4d80a93fb357af5353f770ef74a81be"}, + {file = "huggingface_hub-0.23.3-py3-none-any.whl", hash = "sha256:22222c41223f1b7c209ae5511d2d82907325a0e3cdbce5f66949d43c598ff3bc"}, + {file = "huggingface_hub-0.23.3.tar.gz", hash = "sha256:1a1118a0b3dea3bab6c325d71be16f5ffe441d32f3ac7c348d6875911b694b5b"}, ] [package.dependencies] @@ -1087,16 +1091,16 @@ tqdm = ">=4.42.1" typing-extensions = ">=3.7.4.3" [package.extras] -all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.3.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.3.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] cli = ["InquirerPy (==0.3.4)"] -dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.3.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.3.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] hf-transfer = ["hf-transfer (>=0.1.4)"] inference = ["aiohttp", "minijinja (>=1.0)"] quality = ["mypy (==1.5.1)", "ruff (>=0.3.0)"] tensorflow = ["graphviz", "pydot", "tensorflow"] tensorflow-testing = ["keras (<3.0)", "tensorflow"] -testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] +testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] torch = ["safetensors", "torch"] typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] @@ -1213,21 +1217,21 @@ test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pa [[package]] name = "ipywidgets" -version = "8.1.2" +version = "8.1.3" description = "Jupyter interactive widgets" optional = false python-versions = ">=3.7" files = [ - {file = "ipywidgets-8.1.2-py3-none-any.whl", hash = "sha256:bbe43850d79fb5e906b14801d6c01402857996864d1e5b6fa62dd2ee35559f60"}, - {file = "ipywidgets-8.1.2.tar.gz", hash = "sha256:d0b9b41e49bae926a866e613a39b0f0097745d2b9f1f3dd406641b4a57ec42c9"}, + {file = "ipywidgets-8.1.3-py3-none-any.whl", hash = "sha256:efafd18f7a142248f7cb0ba890a68b96abd4d6e88ddbda483c9130d12667eaf2"}, + {file = "ipywidgets-8.1.3.tar.gz", hash = "sha256:f5f9eeaae082b1823ce9eac2575272952f40d748893972956dc09700a6392d9c"}, ] [package.dependencies] comm = ">=0.1.3" ipython = ">=6.1.0" -jupyterlab-widgets = ">=3.0.10,<3.1.0" +jupyterlab-widgets = ">=3.0.11,<3.1.0" traitlets = ">=4.3.1" -widgetsnbextension = ">=4.0.10,<4.1.0" +widgetsnbextension = ">=4.0.11,<4.1.0" [package.extras] test = ["ipykernel", "jsonschema", "pytest (>=3.6.0)", "pytest-cov", "pytz"] @@ -1282,6 +1286,76 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "jiter" +version = "0.4.1" +description = "Fast iterable JSON parser." +optional = false +python-versions = ">=3.8" +files = [ + {file = "jiter-0.4.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3c2370cd8826b484f3fc6ed729cb58510ba24b4bc277c92323a57d35cf4df223"}, + {file = "jiter-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3587af23140a2eb282bba980010dae60f3b8b1579a034c5e869e9b94220a5972"}, + {file = "jiter-0.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df8788d34545d47de864032a78bae49a14b66b67196c73cd95f1c1e3081d9c73"}, + {file = "jiter-0.4.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91bf2d31e906a3ca26fc8ee0cb979e0e51b12aa7e83999c6afea047538f95e5c"}, + {file = "jiter-0.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8586e68702666b6acd919c65f718a09603adcfd8b4c7026bade2441d9e7bd34e"}, + {file = "jiter-0.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:639b766bde088546b5205fd31608502b5b42abee3294b43cc95c6ea8f9a257c3"}, + {file = "jiter-0.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cb32457296351c98da289d21a092a6c53c75beb80e7127c8e16224ee342c7c7"}, + {file = "jiter-0.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:583263bd81bce5426806cf27ba85e4b97746797fae13c71e50a8689e06e57f81"}, + {file = "jiter-0.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c0e413999a819ccef9b5fd22ef4b9b8c48a98e49da4d09b43ebce286d0d80e26"}, + {file = "jiter-0.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5e50468d5acfef335ba8bc3892bb304354c38ba18acb3f7ae428451e47136e49"}, + {file = "jiter-0.4.1-cp310-none-win32.whl", hash = "sha256:b2ac90b94dd717644c61c8ed0c2ec6e9505bd7314b91a1549680d7f1cb8f1da4"}, + {file = "jiter-0.4.1-cp310-none-win_amd64.whl", hash = "sha256:2509868b8dacf4f65d04b4d951d390f30f403a87a997a14e2db2d232c7a468a7"}, + {file = "jiter-0.4.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b56e4f2fa5767976f2332e9e067010ddfe1379b6456b5458123ba50657c33e02"}, + {file = "jiter-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f813b49db21c946aa010accc54b8e5c9d0007be252bda4738159fa6c65d6d396"}, + {file = "jiter-0.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2933c04ebd77b3e9cf34f80ba45c093739c687c9c5a4fd0a8c701a3bfd90940"}, + {file = "jiter-0.4.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b02ddd65513705ec38211ea48ffc0fde41aa46166d9f7706972daf97b57c8599"}, + {file = "jiter-0.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:88d06af883524e5429d75395bb4ee6ddeda4c30818b2f3e3b8f4afa2dd8f28c0"}, + {file = "jiter-0.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd333eca1090cf21e6359721eecbb2a7fe031cc4db3dd595081430b4a59371c5"}, + {file = "jiter-0.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdc90017cd22dca6b40f2f8518b38363e78aee3cb32f84e1cb08900a598ca91b"}, + {file = "jiter-0.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aedce5b11ca58853d46461e1880079836bfab4e132be2b7d2093ec193081bbc8"}, + {file = "jiter-0.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e55b2f4d2d5066979b0e0e58d85e3fffd0f6e6a0523aab7e0ce75950259387da"}, + {file = "jiter-0.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b3c85c586f1cd71c2a1e78756f6857119947b532379bd9be4338bf3dacf1e87f"}, + {file = "jiter-0.4.1-cp311-none-win32.whl", hash = "sha256:37875f56222f2bb61410e15196d9b91510ccca322c391f3d20c91d667130d15e"}, + {file = "jiter-0.4.1-cp311-none-win_amd64.whl", hash = "sha256:b71758befea8dbdc10e0fb40a776e085eed0e74afef42468ebb58562289e9190"}, + {file = "jiter-0.4.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:93a8869c18a3721e41d7adb289c5c71aea8887eb368a3411219a0afb62955cbe"}, + {file = "jiter-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0ffbc61349f2f27676d40d68e8ef83fc2a9dd2c1464962b1d1b1d8504bccbf85"}, + {file = "jiter-0.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5f1f33e9fd4de4369a8d00fdf2571a8246a942095fb2a9d4cd25135ee675c85"}, + {file = "jiter-0.4.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d8f91a19eba23b4a1bb1e5b64c19cfdbf46604180e5dee40548b53ca13afd2d9"}, + {file = "jiter-0.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a60f8e495448d8e02d291fa9a8522cfe775a10210ba428994f383965e6f6e65"}, + {file = "jiter-0.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7387998c6585ce0f02ae4f5338fabf72b99494860c347f27bc34720290eafb15"}, + {file = "jiter-0.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7cbf41da6506b42db21a1a0befa48e16384591e84e80db002a826ccf07668f1"}, + {file = "jiter-0.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:793ae2499722b9fc31e300abd07418902512109bca17f617598a31a9e17bddce"}, + {file = "jiter-0.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:19f7953b8ada7ee109764ad91d4afb1a9f69b77cde0b890844744c513612dbf8"}, + {file = "jiter-0.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:dcd3d6a142d7b267a8c5f1e28d02759e2e29343b095f6d8aaf463333a842e1f8"}, + {file = "jiter-0.4.1-cp312-none-win32.whl", hash = "sha256:fffdf137c3ab7f0c5facb7c478b57ad3e1eb9b149daff48687844de77b78ab70"}, + {file = "jiter-0.4.1-cp312-none-win_amd64.whl", hash = "sha256:fde004e47a801512c4167f188a6372960374fbd59e635753b3ee536e81953eb3"}, + {file = "jiter-0.4.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b429ba25e05ca28d5e7efa4249032746ac28ec6ad68017ed3ea009989c597911"}, + {file = "jiter-0.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:27df9925d0282c80bdd41613ace7cd799bd6355acdfe25cc48ec16843541999e"}, + {file = "jiter-0.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb68736a0e2b00eda83937c1937f999e8d7dab68820c04343ac2e2eb2c5c2193"}, + {file = "jiter-0.4.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c218458ac32ce0b495f013293867649b40c067a4d7533fa0d70a46f7194febae"}, + {file = "jiter-0.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ebead86e80e352753f6e6f78ca96c12d764a8dbbc7c4b25938ce657ab0e4e879"}, + {file = "jiter-0.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf58f878d43294bea400a9df86ef7796dd2e67969109bce22d337ca77372c69"}, + {file = "jiter-0.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba671e60570cd99b8ed83ce0d82703040dc34c793229ac607f09683ba1981163"}, + {file = "jiter-0.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef0bd8b68ad14f045544989b6ad3758bee6dc01f6924bce5b4fd7060b0a09b1b"}, + {file = "jiter-0.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7888f165a0fe285e015ee18cfcb8b5432c4fa389235b4c24c339ca0cc51ba979"}, + {file = "jiter-0.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7d9c443b2a71a8c3ab6578f5faf7725ad5f63dbb92d87f820eec56de9da0560f"}, + {file = "jiter-0.4.1-cp38-none-win32.whl", hash = "sha256:6f618d1b04493bc9196e466ef59e0a6388eb85e936d1a61833449677643bbdd9"}, + {file = "jiter-0.4.1-cp38-none-win_amd64.whl", hash = "sha256:46b6364a0b2a81cc259768bda131e8528aa3af4312f23f7e10aa04d24f54bbb1"}, + {file = "jiter-0.4.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:6680785a9273a87e463c86a962042d620c00c7bb8100dde1a4c78b2184cdd613"}, + {file = "jiter-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:36b10d945b9ccd2e9f2720e37395daf9e63cfa47e5e0e2887c4931888f0800cd"}, + {file = "jiter-0.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78820599693bda34be17119abf9fad1f02e501b4816e47addbee9c5c768fb361"}, + {file = "jiter-0.4.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68697317170d8f851dfe978ba278b886e54e837ecd2a80c4a33ae780a0f19526"}, + {file = "jiter-0.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d126ffc3876cfc1fba6ae2be37f2532b5db593a96cf4b845724b50b44339c4fd"}, + {file = "jiter-0.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b082223f2e7e6f506d837df935f58f25cabf0a2b35902b4ec73fb561fbf2694a"}, + {file = "jiter-0.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13314287782782be8439dfafca50f13fcab18046227068a3a8e8d8ac888f092b"}, + {file = "jiter-0.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3da1346375605926f1ca4604d154ff41f5e3b933c6e01005e534bca2197d919f"}, + {file = "jiter-0.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9b67a97fbce3ec35ee97439c8b786393f71ecbe7458d5e9279d4c172772eac36"}, + {file = "jiter-0.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7b0f34544923bff0f3393aa3d60087686d86089c9361f6530bb5d19ebfb3db47"}, + {file = "jiter-0.4.1-cp39-none-win32.whl", hash = "sha256:b0c93ef95b896a4ce5edff23071e4dcad77c9e9262fcb6ca2b050f781e8335a9"}, + {file = "jiter-0.4.1-cp39-none-win_amd64.whl", hash = "sha256:3db5c83c8655ce031943b6f08434dac1a91e1477b0df452de0c44f3390a9b22c"}, + {file = "jiter-0.4.1.tar.gz", hash = "sha256:741851cf5f37cf3583f2a56829d734c9fd17334770c9a326e6d25291603d4278"}, +] + [[package]] name = "json5" version = "0.9.25" @@ -1309,24 +1383,24 @@ jsonpointer = ">=1.9" [[package]] name = "jsonpointer" -version = "2.4" +version = "3.0.0" description = "Identify specific nodes in a JSON document (RFC 6901)" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" +python-versions = ">=3.7" files = [ - {file = "jsonpointer-2.4-py2.py3-none-any.whl", hash = "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a"}, - {file = "jsonpointer-2.4.tar.gz", hash = "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88"}, + {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"}, + {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"}, ] [[package]] name = "jsonschema" -version = "4.21.1" +version = "4.22.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" files = [ - {file = "jsonschema-4.21.1-py3-none-any.whl", hash = "sha256:7996507afae316306f9e2290407761157c6f78002dcf7419acb99822143d1c6f"}, - {file = "jsonschema-4.21.1.tar.gz", hash = "sha256:85727c00279f5fa6bedbe6238d2aa6403bedd8b4864ab11207d07df3cc1b2ee5"}, + {file = "jsonschema-4.22.0-py3-none-any.whl", hash = "sha256:ff4cfd6b1367a40e7bc6411caec72effadd3db0bbe5017de188f2d6108335802"}, + {file = "jsonschema-4.22.0.tar.gz", hash = "sha256:5b22d434a45935119af990552c862e5d6d564e8f6601206b305a61fdf661a2b7"}, ] [package.dependencies] @@ -1383,13 +1457,13 @@ qtconsole = "*" [[package]] name = "jupyter-client" -version = "8.6.1" +version = "8.6.2" description = "Jupyter protocol implementation and client libraries" optional = false python-versions = ">=3.8" files = [ - {file = "jupyter_client-8.6.1-py3-none-any.whl", hash = "sha256:3b7bd22f058434e3b9a7ea4b1500ed47de2713872288c0d511d19926f99b459f"}, - {file = "jupyter_client-8.6.1.tar.gz", hash = "sha256:e842515e2bab8e19186d89fdfea7abd15e39dd581f94e399f00e2af5a1652d3f"}, + {file = "jupyter_client-8.6.2-py3-none-any.whl", hash = "sha256:50cbc5c66fd1b8f65ecb66bc490ab73217993632809b6e505687de18e9dea39f"}, + {file = "jupyter_client-8.6.2.tar.gz", hash = "sha256:2bda14d55ee5ba58552a8c53ae43d215ad9868853489213f37da060ced54d8df"}, ] [package.dependencies] @@ -1402,7 +1476,7 @@ traitlets = ">=5.3" [package.extras] docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] +test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest (<8.2.0)", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] [[package]] name = "jupyter-console" @@ -1490,13 +1564,13 @@ jupyter-server = ">=1.1.2" [[package]] name = "jupyter-server" -version = "2.14.0" +version = "2.14.1" description = "The backend—i.e. core services, APIs, and REST endpoints—to Jupyter web applications." optional = false python-versions = ">=3.8" files = [ - {file = "jupyter_server-2.14.0-py3-none-any.whl", hash = "sha256:fb6be52c713e80e004fac34b35a0990d6d36ba06fd0a2b2ed82b899143a64210"}, - {file = "jupyter_server-2.14.0.tar.gz", hash = "sha256:659154cea512083434fd7c93b7fe0897af7a2fd0b9dd4749282b42eaac4ae677"}, + {file = "jupyter_server-2.14.1-py3-none-any.whl", hash = "sha256:16f7177c3a4ea8fe37784e2d31271981a812f0b2874af17339031dc3510cc2a5"}, + {file = "jupyter_server-2.14.1.tar.gz", hash = "sha256:12558d158ec7a0653bf96cc272bc7ad79e0127d503b982ed144399346694f726"}, ] [package.dependencies] @@ -1521,7 +1595,7 @@ traitlets = ">=5.6.0" websocket-client = ">=1.7" [package.extras] -docs = ["ipykernel", "jinja2", "jupyter-client", "jupyter-server", "myst-parser", "nbformat", "prometheus-client", "pydata-sphinx-theme", "send2trash", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-openapi (>=0.8.0)", "sphinxcontrib-spelling", "sphinxemoji", "tornado", "typing-extensions"] +docs = ["ipykernel", "jinja2", "jupyter-client", "myst-parser", "nbformat", "prometheus-client", "pydata-sphinx-theme", "send2trash", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-openapi (>=0.8.0)", "sphinxcontrib-spelling", "sphinxemoji", "tornado", "typing-extensions"] test = ["flaky", "ipykernel", "pre-commit", "pytest (>=7.0,<9)", "pytest-console-scripts", "pytest-jupyter[server] (>=0.7)", "pytest-timeout", "requests"] [[package]] @@ -1545,13 +1619,13 @@ test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (> [[package]] name = "jupyterlab" -version = "4.1.6" +version = "4.2.2" description = "JupyterLab computational environment" optional = false python-versions = ">=3.8" files = [ - {file = "jupyterlab-4.1.6-py3-none-any.whl", hash = "sha256:cf3e862bc10dbf4331e4eb37438634f813c238cfc62c71c640b3b3b2caa089a8"}, - {file = "jupyterlab-4.1.6.tar.gz", hash = "sha256:7935f36ba26eb615183a4f5c2bbca5791b5108ce2a00b5505f8cfd100d53648e"}, + {file = "jupyterlab-4.2.2-py3-none-any.whl", hash = "sha256:59ee9b839f43308c3dfd55d72d1f1a299ed42a7f91f2d1afe9c12a783f9e525f"}, + {file = "jupyterlab-4.2.2.tar.gz", hash = "sha256:a534b6a25719a92a40d514fb133a9fe8f0d9981b0bbce5d8a5fcaa33344a3038"}, ] [package.dependencies] @@ -1563,19 +1637,20 @@ jinja2 = ">=3.0.3" jupyter-core = "*" jupyter-lsp = ">=2.0.0" jupyter-server = ">=2.4.0,<3" -jupyterlab-server = ">=2.19.0,<3" +jupyterlab-server = ">=2.27.1,<3" notebook-shim = ">=0.2" packaging = "*" +setuptools = ">=40.1.0" tomli = {version = ">=1.2.2", markers = "python_version < \"3.11\""} tornado = ">=6.2.0" traitlets = "*" [package.extras] -dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.2.0)"] +dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.3.5)"] docs = ["jsx-lexer", "myst-parser", "pydata-sphinx-theme (>=0.13.0)", "pytest", "pytest-check-links", "pytest-jupyter", "sphinx (>=1.8,<7.3.0)", "sphinx-copybutton"] -docs-screenshots = ["altair (==5.2.0)", "ipython (==8.16.1)", "ipywidgets (==8.1.1)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.0.post6)", "matplotlib (==3.8.2)", "nbconvert (>=7.0.0)", "pandas (==2.2.0)", "scipy (==1.12.0)", "vega-datasets (==0.9.0)"] +docs-screenshots = ["altair (==5.3.0)", "ipython (==8.16.1)", "ipywidgets (==8.1.2)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.1.post2)", "matplotlib (==3.8.3)", "nbconvert (>=7.0.0)", "pandas (==2.2.1)", "scipy (==1.12.0)", "vega-datasets (==0.9.0)"] test = ["coverage", "pytest (>=7.0)", "pytest-check-links (>=0.7)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter (>=0.5.3)", "pytest-timeout", "pytest-tornasync", "requests", "requests-cache", "virtualenv"] -upgrade-extension = ["copier (>=8.0,<9.0)", "jinja2-time (<0.3)", "pydantic (<2.0)", "pyyaml-include (<2.0)", "tomli-w (<2.0)"] +upgrade-extension = ["copier (>=8,<10)", "jinja2-time (<0.3)", "pydantic (<2.0)", "pyyaml-include (<2.0)", "tomli-w (<2.0)"] [[package]] name = "jupyterlab-pygments" @@ -1590,13 +1665,13 @@ files = [ [[package]] name = "jupyterlab-server" -version = "2.27.1" +version = "2.27.2" description = "A set of server components for JupyterLab and JupyterLab like applications." optional = false python-versions = ">=3.8" files = [ - {file = "jupyterlab_server-2.27.1-py3-none-any.whl", hash = "sha256:f5e26156e5258b24d532c84e7c74cc212e203bff93eb856f81c24c16daeecc75"}, - {file = "jupyterlab_server-2.27.1.tar.gz", hash = "sha256:097b5ac709b676c7284ac9c5e373f11930a561f52cd5a86e4fc7e5a9c8a8631d"}, + {file = "jupyterlab_server-2.27.2-py3-none-any.whl", hash = "sha256:54aa2d64fd86383b5438d9f0c032f043c4d8c0264b8af9f60bd061157466ea43"}, + {file = "jupyterlab_server-2.27.2.tar.gz", hash = "sha256:15cbb349dc45e954e09bacf81b9f9bcb10815ff660fb2034ecd7417db3a7ea27"}, ] [package.dependencies] @@ -1616,30 +1691,29 @@ test = ["hatch", "ipykernel", "openapi-core (>=0.18.0,<0.19.0)", "openapi-spec-v [[package]] name = "jupyterlab-widgets" -version = "3.0.10" +version = "3.0.11" description = "Jupyter interactive widgets for JupyterLab" optional = false python-versions = ">=3.7" files = [ - {file = "jupyterlab_widgets-3.0.10-py3-none-any.whl", hash = "sha256:dd61f3ae7a5a7f80299e14585ce6cf3d6925a96c9103c978eda293197730cb64"}, - {file = "jupyterlab_widgets-3.0.10.tar.gz", hash = "sha256:04f2ac04976727e4f9d0fa91cdc2f1ab860f965e504c29dbd6a65c882c9d04c0"}, + {file = "jupyterlab_widgets-3.0.11-py3-none-any.whl", hash = "sha256:78287fd86d20744ace330a61625024cf5521e1c012a352ddc0a3cdc2348becd0"}, + {file = "jupyterlab_widgets-3.0.11.tar.gz", hash = "sha256:dd5ac679593c969af29c9bed054c24f26842baa51352114736756bc035deee27"}, ] [[package]] name = "langchain" -version = "0.2.0" +version = "0.2.3" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain-0.2.0-py3-none-any.whl", hash = "sha256:25c43d97daaf8539c10c77d028f88782f48649e6a01e77938014a1fbaf68bb62"}, - {file = "langchain-0.2.0.tar.gz", hash = "sha256:3d980e92d8635bb7fa45751ee159c56643d1f0e56221e378b45d81cf6dc7d453"}, + {file = "langchain-0.2.3-py3-none-any.whl", hash = "sha256:5dc33cd9c8008693d328b7cb698df69073acecc89ad9c2a95f243b3314f8d834"}, + {file = "langchain-0.2.3.tar.gz", hash = "sha256:81962cc72cce6515f7bd71e01542727870789bf8b666c6913d85559080c1a201"}, ] [package.dependencies] aiohttp = ">=3.8.3,<4.0.0" async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""} -dataclasses-json = ">=0.5.7,<0.7" langchain-core = ">=0.2.0,<0.3.0" langchain-text-splitters = ">=0.2.0,<0.3.0" langsmith = ">=0.1.17,<0.2.0" @@ -1650,83 +1724,66 @@ requests = ">=2,<3" SQLAlchemy = ">=1.4,<3" tenacity = ">=8.1.0,<9.0.0" -[package.extras] -azure = ["azure-ai-formrecognizer (>=3.2.1,<4.0.0)", "azure-ai-textanalytics (>=5.3.0,<6.0.0)", "azure-cognitiveservices-speech (>=1.28.0,<2.0.0)", "azure-core (>=1.26.4,<2.0.0)", "azure-cosmos (>=4.4.0b1,<5.0.0)", "azure-identity (>=1.12.0,<2.0.0)", "azure-search-documents (==11.4.0b8)", "openai (<2)"] -clarifai = ["clarifai (>=9.1.0)"] -cli = ["typer (>=0.9.0,<0.10.0)"] -cohere = ["cohere (>=4,<6)"] -docarray = ["docarray[hnswlib] (>=0.32.0,<0.33.0)"] -embeddings = ["sentence-transformers (>=2,<3)"] -extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cohere (>=4,<6)", "couchbase (>=4.1.9,<5.0.0)", "dashvector (>=1.0.1,<2.0.0)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "langchain-openai (>=0.1,<0.2)", "lxml (>=4.9.3,<6.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "rdflib (==7.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "upstash-redis (>=0.15.0,<0.16.0)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)"] -javascript = ["esprima (>=4.0.1,<5.0.0)"] -llms = ["clarifai (>=9.1.0)", "cohere (>=4,<6)", "huggingface_hub (>=0,<1)", "manifest-ml (>=0.0.1,<0.0.2)", "nlpcloud (>=1,<2)", "openai (<2)", "openlm (>=0.0.5,<0.0.6)", "torch (>=1,<3)", "transformers (>=4,<5)"] -openai = ["openai (<2)", "tiktoken (>=0.7,<1.0)"] -qdrant = ["qdrant-client (>=1.3.1,<2.0.0)"] -text-helpers = ["chardet (>=5.1.0,<6.0.0)"] - [[package]] name = "langchain-anthropic" -version = "0.1.13" +version = "0.1.15" description = "An integration package connecting AnthropicMessages and LangChain" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_anthropic-0.1.13-py3-none-any.whl", hash = "sha256:121f6f480da7685c239573d98322adb94fe486d40651ac341637f65da36881de"}, - {file = "langchain_anthropic-0.1.13.tar.gz", hash = "sha256:32e7ac51e1874c47e1a20493e75f5bfc88b0ffeaf5f1aed6091547e1ae44bb85"}, + {file = "langchain_anthropic-0.1.15-py3-none-any.whl", hash = "sha256:7cceea526f473e4d514f39295dc128eec57da628a4bbb54850d11dda7aa959fc"}, + {file = "langchain_anthropic-0.1.15.tar.gz", hash = "sha256:c5c3c6eaccb11ed99a63886e50873ac21eaf8e9441e0f75c7ae7cd8cdef65155"}, ] [package.dependencies] -anthropic = ">=0.26.0,<1" +anthropic = ">=0.28.0,<1" defusedxml = ">=0.7.1,<0.8.0" -langchain-core = ">=0.1.43,<0.3" +langchain-core = ">=0.2.2rc1,<0.3" [[package]] name = "langchain-core" -version = "0.2.0" +version = "0.2.7" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_core-0.2.0-py3-none-any.whl", hash = "sha256:973cf3402d428018dc68313d8be0e721cea5b18d4ae82149ad659dbce34dc296"}, - {file = "langchain_core-0.2.0.tar.gz", hash = "sha256:22c84e88aa7e98a0b98fadb3238343152641afce6ff27e8f2d9f5ff421e35a35"}, + {file = "langchain_core-0.2.7-py3-none-any.whl", hash = "sha256:fd02e153c898486dd728d634684ffc64bc257ff2ba443dc7e53d017ac0bf4658"}, + {file = "langchain_core-0.2.7.tar.gz", hash = "sha256:b0b1b6dfbdedb39426fcb8bd3f07e40eec7964856e3fc384c420ca6dba61b34e"}, ] [package.dependencies] jsonpatch = ">=1.33,<2.0" -langsmith = ">=0.1.0,<0.2.0" -packaging = ">=23.2,<24.0" +langsmith = ">=0.1.75,<0.2.0" +packaging = ">=23.2,<25" pydantic = ">=1,<3" PyYAML = ">=5.3" tenacity = ">=8.1.0,<9.0.0" -[package.extras] -extended-testing = ["jinja2 (>=3,<4)"] - [[package]] name = "langchain-openai" -version = "0.1.7" +version = "0.1.8" description = "An integration package connecting OpenAI and LangChain" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_openai-0.1.7-py3-none-any.whl", hash = "sha256:39c3cb22bb739900ae8294d4d9939a6138c0ca7ad11198e57038eb14c08d04ec"}, - {file = "langchain_openai-0.1.7.tar.gz", hash = "sha256:fd7e1c33ba8e2cab4b2154f3a2fd4a0d9cc6518b41cf49bb87255f9f732a4896"}, + {file = "langchain_openai-0.1.8-py3-none-any.whl", hash = "sha256:8125c84223e9f43b05defbca64eedbcf362fd78a680de6c25e64f973b34a8063"}, + {file = "langchain_openai-0.1.8.tar.gz", hash = "sha256:a11fcce15def7917c44232abda6baaa63dfc79fe44be1531eea650d39a44cd95"}, ] [package.dependencies] -langchain-core = ">=0.1.46,<0.3" -openai = ">=1.24.0,<2.0.0" +langchain-core = ">=0.2.2,<0.3" +openai = ">=1.26.0,<2.0.0" tiktoken = ">=0.7,<1" [[package]] name = "langchain-text-splitters" -version = "0.2.0" +version = "0.2.1" description = "LangChain text splitting utilities" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_text_splitters-0.2.0-py3-none-any.whl", hash = "sha256:7b4c6a45f8471630a882b321e138329b6897102a5bc62f4c12be1c0b05bb9199"}, - {file = "langchain_text_splitters-0.2.0.tar.gz", hash = "sha256:b32ab4f7397f7d42c1fa3283fefc2547ba356bd63a68ee9092865e5ad83c82f9"}, + {file = "langchain_text_splitters-0.2.1-py3-none-any.whl", hash = "sha256:c2774a85f17189eaca50339629d2316d13130d4a8d9f1a1a96f3a03670c4a138"}, + {file = "langchain_text_splitters-0.2.1.tar.gz", hash = "sha256:06853d17d7241ecf5c97c7b6ef01f600f9b0fb953dd997838142a527a4f32ea4"}, ] [package.dependencies] @@ -1737,13 +1794,13 @@ extended-testing = ["beautifulsoup4 (>=4.12.3,<5.0.0)", "lxml (>=4.9.3,<6.0)"] [[package]] name = "langchainhub" -version = "0.1.15" +version = "0.1.18" description = "The LangChain Hub API client" optional = false -python-versions = ">=3.8.1,<4.0" +python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchainhub-0.1.15-py3-none-any.whl", hash = "sha256:89a0951abd1db255e91c6d545d092a598fc255aa865d1ffc3ce8f93bbeae60e7"}, - {file = "langchainhub-0.1.15.tar.gz", hash = "sha256:fa3ff81a31946860f84c119f1e2f6b7c7707e2bd7ed2394a7313b286d59f3bda"}, + {file = "langchainhub-0.1.18-py3-none-any.whl", hash = "sha256:11501f15e7f34715ecc8892587daa35c6f2a3005e1f2926c9bcabd31fc2c100c"}, + {file = "langchainhub-0.1.18.tar.gz", hash = "sha256:f2d0d8bf3abe4ca5e70511d8220bdc9ccea28d5267bcfd0e5ef9c53bd5bd3bad"}, ] [package.dependencies] @@ -1752,13 +1809,13 @@ types-requests = ">=2.31.0.2,<3.0.0.0" [[package]] name = "langsmith" -version = "0.1.65" +version = "0.1.77" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langsmith-0.1.65-py3-none-any.whl", hash = "sha256:ab4487029240e69cca30da1065f1e9138e5a7ca2bbe8c697f0bd7d5839f71cf7"}, - {file = "langsmith-0.1.65.tar.gz", hash = "sha256:d3c2eb2391478bd79989f02652cf66e29a7959d677614b6993a47cef43f7f43b"}, + {file = "langsmith-0.1.77-py3-none-any.whl", hash = "sha256:2202cc21b1ed7e7b9e5d2af2694be28898afa048c09fdf09f620cbd9301755ae"}, + {file = "langsmith-0.1.77.tar.gz", hash = "sha256:4ace09077a9a4e412afeb4b517ca68e7de7b07f36e4792dc8236ac5207c0c0c7"}, ] [package.dependencies] @@ -1837,13 +1894,13 @@ files = [ [[package]] name = "marshmallow" -version = "3.21.1" +version = "3.21.3" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.8" files = [ - {file = "marshmallow-3.21.1-py3-none-any.whl", hash = "sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633"}, - {file = "marshmallow-3.21.1.tar.gz", hash = "sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3"}, + {file = "marshmallow-3.21.3-py3-none-any.whl", hash = "sha256:86ce7fb914aa865001a4b2092c4c2872d13bc347f3d42673272cabfdbad386f1"}, + {file = "marshmallow-3.21.3.tar.gz", hash = "sha256:4f57c5e050a54d66361e826f94fba213eb10b67b2fdb02c3e0343ce207ba1662"}, ] [package.dependencies] @@ -1851,7 +1908,7 @@ packaging = ">=17.0" [package.extras] dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] -docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)", "sphinx-version-warning (==1.1.2)"] +docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.3.7)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"] tests = ["pytest", "pytz", "simplejson"] [[package]] @@ -2060,13 +2117,13 @@ test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>= [[package]] name = "nbconvert" -version = "7.16.3" +version = "7.16.4" description = "Converting Jupyter Notebooks (.ipynb files) to other formats. Output formats include asciidoc, html, latex, markdown, pdf, py, rst, script. nbconvert can be used both as a Python library (`import nbconvert`) or as a command line tool (invoked as `jupyter nbconvert ...`)." optional = false python-versions = ">=3.8" files = [ - {file = "nbconvert-7.16.3-py3-none-any.whl", hash = "sha256:ddeff14beeeedf3dd0bc506623e41e4507e551736de59df69a91f86700292b3b"}, - {file = "nbconvert-7.16.3.tar.gz", hash = "sha256:a6733b78ce3d47c3f85e504998495b07e6ea9cf9bf6ec1c98dda63ec6ad19142"}, + {file = "nbconvert-7.16.4-py3-none-any.whl", hash = "sha256:05873c620fe520b6322bf8a5ad562692343fe3452abda5765c7a34b7d1aa3eb3"}, + {file = "nbconvert-7.16.4.tar.gz", hash = "sha256:86ca91ba266b0a448dc96fa6c5b9d98affabde2867b363258703536807f9f7f4"}, ] [package.dependencies] @@ -2088,9 +2145,9 @@ tinycss2 = "*" traitlets = ">=5.1" [package.extras] -all = ["nbconvert[docs,qtpdf,serve,test,webpdf]"] +all = ["flaky", "ipykernel", "ipython", "ipywidgets (>=7.5)", "myst-parser", "nbsphinx (>=0.2.12)", "playwright", "pydata-sphinx-theme", "pyqtwebengine (>=5.15)", "pytest (>=7)", "sphinx (==5.0.2)", "sphinxcontrib-spelling", "tornado (>=6.1)"] docs = ["ipykernel", "ipython", "myst-parser", "nbsphinx (>=0.2.12)", "pydata-sphinx-theme", "sphinx (==5.0.2)", "sphinxcontrib-spelling"] -qtpdf = ["nbconvert[qtpng]"] +qtpdf = ["pyqtwebengine (>=5.15)"] qtpng = ["pyqtwebengine (>=5.15)"] serve = ["tornado (>=6.1)"] test = ["flaky", "ipykernel", "ipywidgets (>=7.5)", "pytest (>=7)"] @@ -2130,26 +2187,26 @@ files = [ [[package]] name = "notebook" -version = "7.1.3" +version = "7.2.1" description = "Jupyter Notebook - A web-based notebook environment for interactive computing" optional = false python-versions = ">=3.8" files = [ - {file = "notebook-7.1.3-py3-none-any.whl", hash = "sha256:919b911e59f41f6e3857ce93c9d93535ba66bb090059712770e5968c07e1004d"}, - {file = "notebook-7.1.3.tar.gz", hash = "sha256:41fcebff44cf7bb9377180808bcbae066629b55d8c7722f1ebbe75ca44f9cfc1"}, + {file = "notebook-7.2.1-py3-none-any.whl", hash = "sha256:f45489a3995746f2195a137e0773e2130960b51c9ac3ce257dbc2705aab3a6ca"}, + {file = "notebook-7.2.1.tar.gz", hash = "sha256:4287b6da59740b32173d01d641f763d292f49c30e7a51b89c46ba8473126341e"}, ] [package.dependencies] jupyter-server = ">=2.4.0,<3" -jupyterlab = ">=4.1.1,<4.2" -jupyterlab-server = ">=2.22.1,<3" +jupyterlab = ">=4.2.0,<4.3" +jupyterlab-server = ">=2.27.1,<3" notebook-shim = ">=0.2,<0.3" tornado = ">=6.2.0" [package.extras] dev = ["hatch", "pre-commit"] docs = ["myst-parser", "nbsphinx", "pydata-sphinx-theme", "sphinx (>=1.3.6)", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["importlib-resources (>=5.0)", "ipykernel", "jupyter-server[test] (>=2.4.0,<3)", "jupyterlab-server[test] (>=2.22.1,<3)", "nbval", "pytest (>=7.0)", "pytest-console-scripts", "pytest-timeout", "pytest-tornasync", "requests"] +test = ["importlib-resources (>=5.0)", "ipykernel", "jupyter-server[test] (>=2.4.0,<3)", "jupyterlab-server[test] (>=2.27.1,<3)", "nbval", "pytest (>=7.0)", "pytest-console-scripts", "pytest-timeout", "pytest-tornasync", "requests"] [[package]] name = "notebook-shim" @@ -2215,13 +2272,13 @@ files = [ [[package]] name = "openai" -version = "1.30.1" +version = "1.33.0" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.30.1-py3-none-any.whl", hash = "sha256:c9fb3c3545c118bbce8deb824397b9433a66d0d0ede6a96f7009c95b76de4a46"}, - {file = "openai-1.30.1.tar.gz", hash = "sha256:4f85190e577cba0b066e1950b8eb9b11d25bc7ebcc43a86b326ce1bfa564ec74"}, + {file = "openai-1.33.0-py3-none-any.whl", hash = "sha256:621163b56570897ab8389d187f686a53d4771fd6ce95d481c0a9611fe8bc4229"}, + {file = "openai-1.33.0.tar.gz", hash = "sha256:1169211a7b326ecbc821cafb427c29bfd0871f9a3e0947dd9e51acb3b0f1df78"}, ] [package.dependencies] @@ -2238,62 +2295,57 @@ datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] [[package]] name = "orjson" -version = "3.10.1" +version = "3.10.4" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.10.1-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:8ec2fc456d53ea4a47768f622bb709be68acd455b0c6be57e91462259741c4f3"}, - {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e900863691d327758be14e2a491931605bd0aded3a21beb6ce133889830b659"}, - {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab6ecbd6fe57785ebc86ee49e183f37d45f91b46fc601380c67c5c5e9c0014a2"}, - {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8af7c68b01b876335cccfb4eee0beef2b5b6eae1945d46a09a7c24c9faac7a77"}, - {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:915abfb2e528677b488a06eba173e9d7706a20fdfe9cdb15890b74ef9791b85e"}, - {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe3fd4a36eff9c63d25503b439531d21828da9def0059c4f472e3845a081aa0b"}, - {file = "orjson-3.10.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d229564e72cfc062e6481a91977a5165c5a0fdce11ddc19ced8471847a67c517"}, - {file = "orjson-3.10.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9e00495b18304173ac843b5c5fbea7b6f7968564d0d49bef06bfaeca4b656f4e"}, - {file = "orjson-3.10.1-cp310-none-win32.whl", hash = "sha256:fd78ec55179545c108174ba19c1795ced548d6cac4d80d014163033c047ca4ea"}, - {file = "orjson-3.10.1-cp310-none-win_amd64.whl", hash = "sha256:50ca42b40d5a442a9e22eece8cf42ba3d7cd4cd0f2f20184b4d7682894f05eec"}, - {file = "orjson-3.10.1-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b345a3d6953628df2f42502297f6c1e1b475cfbf6268013c94c5ac80e8abc04c"}, - {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:caa7395ef51af4190d2c70a364e2f42138e0e5fcb4bc08bc9b76997659b27dab"}, - {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b01d701decd75ae092e5f36f7b88a1e7a1d3bb7c9b9d7694de850fb155578d5a"}, - {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5028981ba393f443d8fed9049211b979cadc9d0afecf162832f5a5b152c6297"}, - {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31ff6a222ea362b87bf21ff619598a4dc1106aaafaea32b1c4876d692891ec27"}, - {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e852a83d7803d3406135fb7a57cf0c1e4a3e73bac80ec621bd32f01c653849c5"}, - {file = "orjson-3.10.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2567bc928ed3c3fcd90998009e8835de7c7dc59aabcf764b8374d36044864f3b"}, - {file = "orjson-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4ce98cac60b7bb56457bdd2ed7f0d5d7f242d291fdc0ca566c83fa721b52e92d"}, - {file = "orjson-3.10.1-cp311-none-win32.whl", hash = "sha256:813905e111318acb356bb8029014c77b4c647f8b03f314e7b475bd9ce6d1a8ce"}, - {file = "orjson-3.10.1-cp311-none-win_amd64.whl", hash = "sha256:03a3ca0b3ed52bed1a869163a4284e8a7b0be6a0359d521e467cdef7e8e8a3ee"}, - {file = "orjson-3.10.1-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f02c06cee680b1b3a8727ec26c36f4b3c0c9e2b26339d64471034d16f74f4ef5"}, - {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1aa2f127ac546e123283e437cc90b5ecce754a22306c7700b11035dad4ccf85"}, - {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2cf29b4b74f585225196944dffdebd549ad2af6da9e80db7115984103fb18a96"}, - {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1b130c20b116f413caf6059c651ad32215c28500dce9cd029a334a2d84aa66f"}, - {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d31f9a709e6114492136e87c7c6da5e21dfedebefa03af85f3ad72656c493ae9"}, - {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d1d169461726f271ab31633cf0e7e7353417e16fb69256a4f8ecb3246a78d6e"}, - {file = "orjson-3.10.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:57c294d73825c6b7f30d11c9e5900cfec9a814893af7f14efbe06b8d0f25fba9"}, - {file = "orjson-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d7f11dbacfa9265ec76b4019efffabaabba7a7ebf14078f6b4df9b51c3c9a8ea"}, - {file = "orjson-3.10.1-cp312-none-win32.whl", hash = "sha256:d89e5ed68593226c31c76ab4de3e0d35c760bfd3fbf0a74c4b2be1383a1bf123"}, - {file = "orjson-3.10.1-cp312-none-win_amd64.whl", hash = "sha256:aa76c4fe147fd162107ce1692c39f7189180cfd3a27cfbc2ab5643422812da8e"}, - {file = "orjson-3.10.1-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a2c6a85c92d0e494c1ae117befc93cf8e7bca2075f7fe52e32698da650b2c6d1"}, - {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9813f43da955197d36a7365eb99bed42b83680801729ab2487fef305b9ced866"}, - {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ec917b768e2b34b7084cb6c68941f6de5812cc26c6f1a9fecb728e36a3deb9e8"}, - {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5252146b3172d75c8a6d27ebca59c9ee066ffc5a277050ccec24821e68742fdf"}, - {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:536429bb02791a199d976118b95014ad66f74c58b7644d21061c54ad284e00f4"}, - {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7dfed3c3e9b9199fb9c3355b9c7e4649b65f639e50ddf50efdf86b45c6de04b5"}, - {file = "orjson-3.10.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2b230ec35f188f003f5b543644ae486b2998f6afa74ee3a98fc8ed2e45960afc"}, - {file = "orjson-3.10.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:01234249ba19c6ab1eb0b8be89f13ea21218b2d72d496ef085cfd37e1bae9dd8"}, - {file = "orjson-3.10.1-cp38-none-win32.whl", hash = "sha256:8a884fbf81a3cc22d264ba780920d4885442144e6acaa1411921260416ac9a54"}, - {file = "orjson-3.10.1-cp38-none-win_amd64.whl", hash = "sha256:dab5f802d52b182163f307d2b1f727d30b1762e1923c64c9c56dd853f9671a49"}, - {file = "orjson-3.10.1-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a51fd55d4486bc5293b7a400f9acd55a2dc3b5fc8420d5ffe9b1d6bb1a056a5e"}, - {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53521542a6db1411b3bfa1b24ddce18605a3abdc95a28a67b33f9145f26aa8f2"}, - {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:27d610df96ac18ace4931411d489637d20ab3b8f63562b0531bba16011998db0"}, - {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79244b1456e5846d44e9846534bd9e3206712936d026ea8e6a55a7374d2c0694"}, - {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d751efaa8a49ae15cbebdda747a62a9ae521126e396fda8143858419f3b03610"}, - {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27ff69c620a4fff33267df70cfd21e0097c2a14216e72943bd5414943e376d77"}, - {file = "orjson-3.10.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ebc58693464146506fde0c4eb1216ff6d4e40213e61f7d40e2f0dde9b2f21650"}, - {file = "orjson-3.10.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5be608c3972ed902e0143a5b8776d81ac1059436915d42defe5c6ae97b3137a4"}, - {file = "orjson-3.10.1-cp39-none-win32.whl", hash = "sha256:4ae10753e7511d359405aadcbf96556c86e9dbf3a948d26c2c9f9a150c52b091"}, - {file = "orjson-3.10.1-cp39-none-win_amd64.whl", hash = "sha256:fb5bc4caa2c192077fdb02dce4e5ef8639e7f20bec4e3a834346693907362932"}, - {file = "orjson-3.10.1.tar.gz", hash = "sha256:a883b28d73370df23ed995c466b4f6c708c1f7a9bdc400fe89165c96c7603204"}, + {file = "orjson-3.10.4-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:afca963f19ca60c7aedadea9979f769139127288dd58ccf3f7c5e8e6dc62cabf"}, + {file = "orjson-3.10.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b112eff36ba7ccc7a9d6b87e17b9d6bde4312d05e3ddf66bf5662481dee846"}, + {file = "orjson-3.10.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02b192eaba048b1039eca9a0cef67863bd5623042f5c441889a9957121d97e14"}, + {file = "orjson-3.10.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:827c3d0e4fc44242c82bfdb1a773235b8c0575afee99a9fa9a8ce920c14e440f"}, + {file = "orjson-3.10.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca8ec09724f10ec209244caeb1f9f428b6bb03f2eda9ed5e2c4dd7f2b7fabd44"}, + {file = "orjson-3.10.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8eaa5d531a8fde11993cbcb27e9acf7d9c457ba301adccb7fa3a021bfecab46c"}, + {file = "orjson-3.10.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e112aa7fc4ea67367ec5e86c39a6bb6c5719eddc8f999087b1759e765ddaf2d4"}, + {file = "orjson-3.10.4-cp310-none-win32.whl", hash = "sha256:1538844fb88446c42da3889f8c4ecce95a630b5a5ba18ecdfe5aea596f4dff21"}, + {file = "orjson-3.10.4-cp310-none-win_amd64.whl", hash = "sha256:de02811903a2e434127fba5389c3cc90f689542339a6e52e691ab7f693407b5a"}, + {file = "orjson-3.10.4-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:358afaec75de7237dfea08e6b1b25d226e33a1e3b6dc154fc99eb697f24a1ffa"}, + {file = "orjson-3.10.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb4e292c3198ab3d93e5f877301d2746be4ca0ba2d9c513da5e10eb90e19ff52"}, + {file = "orjson-3.10.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c39e57cf6323a39238490092985d5d198a7da4a3be013cc891a33fef13a536e"}, + {file = "orjson-3.10.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f86df433fc01361ff9270ad27455ce1ad43cd05e46de7152ca6adb405a16b2f6"}, + {file = "orjson-3.10.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c9966276a2c97e93e6cbe8286537f88b2a071827514f0d9d47a0aefa77db458"}, + {file = "orjson-3.10.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c499a14155a1f5a1e16e0cd31f6cf6f93965ac60a0822bc8340e7e2d3dac1108"}, + {file = "orjson-3.10.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3087023ce904a327c29487eb7e1f2c060070e8dbb9a3991b8e7952a9c6e62f38"}, + {file = "orjson-3.10.4-cp311-none-win32.whl", hash = "sha256:f965893244fe348b59e5ce560693e6dd03368d577ce26849b5d261ce31c70101"}, + {file = "orjson-3.10.4-cp311-none-win_amd64.whl", hash = "sha256:c212f06fad6aa6ce85d5665e91a83b866579f29441a47d3865c57329c0857357"}, + {file = "orjson-3.10.4-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d0965a8b0131959833ca8a65af60285995d57ced0de2fd8f16fc03235975d238"}, + {file = "orjson-3.10.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27b64695d9f2aef3ae15a0522e370ec95c946aaea7f2c97a1582a62b3bdd9169"}, + {file = "orjson-3.10.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:867d882ddee6a20be4c8b03ae3d2b0333894d53ad632d32bd9b8123649577171"}, + {file = "orjson-3.10.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a0667458f8a8ceb6dee5c08fec0b46195f92c474cbbec71dca2a6b7fd5b67b8d"}, + {file = "orjson-3.10.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3eac9befc4eaec1d1ff3bba6210576be4945332dde194525601c5ddb5c060d3"}, + {file = "orjson-3.10.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4343245443552eae240a33047a6d1bcac7a754ad4b1c57318173c54d7efb9aea"}, + {file = "orjson-3.10.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:30153e269eea43e98918d4d462a36a7065031d9246407dfff2579a4e457515c1"}, + {file = "orjson-3.10.4-cp312-none-win32.whl", hash = "sha256:1a7d092ee043abf3db19c2183115e80676495c9911843fdb3ebd48ca7b73079e"}, + {file = "orjson-3.10.4-cp312-none-win_amd64.whl", hash = "sha256:07a2adbeb8b9efe6d68fc557685954a1f19d9e33f5cc018ae1a89e96647c1b65"}, + {file = "orjson-3.10.4-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f5a746f3d908bce1a1e347b9ca89864047533bdfab5a450066a0315f6566527b"}, + {file = "orjson-3.10.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:465b4a8a3e459f8d304c19071b4badaa9b267c59207a005a7dd9dfe13d3a423f"}, + {file = "orjson-3.10.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35858d260728c434a3d91b60685ab32418318567e8902039837e1c2af2719e0b"}, + {file = "orjson-3.10.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8a5ba090d40c4460312dd69c232b38c2ff67a823185cfe667e841c9dd5c06841"}, + {file = "orjson-3.10.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dde86755d064664e62e3612a166c28298aa8dfd35a991553faa58855ae739cc"}, + {file = "orjson-3.10.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:020a9e9001cfec85c156ef3b185ff758b62ef986cefdb8384c4579facd5ce126"}, + {file = "orjson-3.10.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3bf8e6e3388a2e83a86466c912387e0f0a765494c65caa7e865f99969b76ba0d"}, + {file = "orjson-3.10.4-cp38-none-win32.whl", hash = "sha256:c5a1cca6a4a3129db3da68a25dc0a459a62ae58e284e363b35ab304202d9ba9e"}, + {file = "orjson-3.10.4-cp38-none-win_amd64.whl", hash = "sha256:ecd97d98d7bee3e3d51d0b51c92c457f05db4993329eea7c69764f9820e27eb3"}, + {file = "orjson-3.10.4-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:71362daa330a2fc85553a1469185ac448547392a8f83d34e67779f8df3a52743"}, + {file = "orjson-3.10.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d24b59d1fecb0fd080c177306118a143f7322335309640c55ed9580d2044e363"}, + {file = "orjson-3.10.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e906670aea5a605b083ebb58d575c35e88cf880fa372f7cedaac3d51e98ff164"}, + {file = "orjson-3.10.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ce32ed4bc4d632268e4978e595fe5ea07e026b751482b4a0feec48f66a90abc"}, + {file = "orjson-3.10.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1dcd34286246e0c5edd0e230d1da2daab2c1b465fcb6bac85b8d44057229d40a"}, + {file = "orjson-3.10.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c45d4b8c403e50beedb1d006a8916d9910ed56bceaf2035dc253618b44d0a161"}, + {file = "orjson-3.10.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:aaed3253041b5002a4f5bfdf6f7b5cce657d974472b0699a469d439beba40381"}, + {file = "orjson-3.10.4-cp39-none-win32.whl", hash = "sha256:9a4f41b7dbf7896f8dbf559b9b43dcd99e31e0d49ac1b59d74f52ce51ab10eb9"}, + {file = "orjson-3.10.4-cp39-none-win_amd64.whl", hash = "sha256:6c4eb7d867ed91cb61e6514cb4f457aa01d7b0fd663089df60a69f3d38b69d4c"}, + {file = "orjson-3.10.4.tar.gz", hash = "sha256:c912ed25b787c73fe994a5decd81c3f3b256599b8a87d410d799d5d52013af2a"}, ] [[package]] @@ -2360,13 +2412,13 @@ ptyprocess = ">=0.5" [[package]] name = "platformdirs" -version = "4.2.1" +version = "4.2.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, - {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] @@ -2405,13 +2457,13 @@ twisted = ["twisted"] [[package]] name = "prompt-toolkit" -version = "3.0.43" +version = "3.0.47" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.7.0" files = [ - {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"}, - {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"}, + {file = "prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10"}, + {file = "prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360"}, ] [package.dependencies] @@ -2483,18 +2535,18 @@ files = [ [[package]] name = "pydantic" -version = "2.7.1" +version = "2.7.3" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"}, - {file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"}, + {file = "pydantic-2.7.3-py3-none-any.whl", hash = "sha256:ea91b002777bf643bb20dd717c028ec43216b24a6001a280f83877fd2655d0b4"}, + {file = "pydantic-2.7.3.tar.gz", hash = "sha256:c46c76a40bb1296728d7a8b99aa73dd70a48c3510111ff290034f860c99c419e"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.18.2" +pydantic-core = "2.18.4" typing-extensions = ">=4.6.1" [package.extras] @@ -2502,90 +2554,90 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.18.2" +version = "2.18.4" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"}, - {file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"}, - {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"}, - {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"}, - {file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"}, - {file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"}, - {file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"}, - {file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"}, - {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"}, - {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"}, - {file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"}, - {file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"}, - {file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"}, - {file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"}, - {file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"}, - {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"}, - {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"}, - {file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"}, - {file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"}, - {file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"}, - {file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"}, - {file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"}, - {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"}, - {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"}, - {file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"}, - {file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"}, - {file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"}, - {file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"}, - {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"}, - {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"}, - {file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"}, - {file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"}, - {file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"}, + {file = "pydantic_core-2.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4"}, + {file = "pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb"}, + {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c"}, + {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e"}, + {file = "pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc"}, + {file = "pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0"}, + {file = "pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d"}, + {file = "pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951"}, + {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2"}, + {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9"}, + {file = "pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558"}, + {file = "pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b"}, + {file = "pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805"}, + {file = "pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2"}, + {file = "pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9"}, + {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c"}, + {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8"}, + {file = "pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07"}, + {file = "pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a"}, + {file = "pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f"}, + {file = "pydantic_core-2.18.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2"}, + {file = "pydantic_core-2.18.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057"}, + {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b"}, + {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af"}, + {file = "pydantic_core-2.18.4-cp38-none-win32.whl", hash = "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2"}, + {file = "pydantic_core-2.18.4-cp38-none-win_amd64.whl", hash = "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443"}, + {file = "pydantic_core-2.18.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528"}, + {file = "pydantic_core-2.18.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23"}, + {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b"}, + {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a"}, + {file = "pydantic_core-2.18.4-cp39-none-win32.whl", hash = "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d"}, + {file = "pydantic_core-2.18.4-cp39-none-win_amd64.whl", hash = "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9"}, + {file = "pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864"}, ] [package.dependencies] @@ -2593,17 +2645,16 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pygments" -version = "2.17.2" +version = "2.18.0" description = "Pygments is a syntax highlighting package written in Python." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, - {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, ] [package.extras] -plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] [[package]] @@ -2864,99 +2915,99 @@ files = [ [[package]] name = "pyzmq" -version = "26.0.2" +version = "26.0.3" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.7" files = [ - {file = "pyzmq-26.0.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:1a60a03b01e8c9c58932ec0cca15b1712d911c2800eb82d4281bc1ae5b6dad50"}, - {file = "pyzmq-26.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:949067079e14ea1973bd740255e0840118c163d4bce8837f539d749f145cf5c3"}, - {file = "pyzmq-26.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37e7edfa6cf96d036a403775c96afa25058d1bb940a79786a9a2fc94a783abe3"}, - {file = "pyzmq-26.0.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:903cc7a84a7d4326b43755c368780800e035aa3d711deae84a533fdffa8755b0"}, - {file = "pyzmq-26.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6cb2e41af165e5f327d06fbdd79a42a4e930267fade4e9f92d17f3ccce03f3a7"}, - {file = "pyzmq-26.0.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:55353b8189adcfc4c125fc4ce59d477744118e9c0ec379dd0999c5fa120ac4f5"}, - {file = "pyzmq-26.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f961423ff6236a752ced80057a20e623044df95924ed1009f844cde8b3a595f9"}, - {file = "pyzmq-26.0.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ba77fe84fe4f5f3dc0ef681a6d366685c8ffe1c8439c1d7530997b05ac06a04b"}, - {file = "pyzmq-26.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:52589f0a745ef61b9c75c872cf91f8c1f7c0668eb3dd99d7abd639d8c0fb9ca7"}, - {file = "pyzmq-26.0.2-cp310-cp310-win32.whl", hash = "sha256:b7b6d2a46c7afe2ad03ec8faf9967090c8ceae85c4d8934d17d7cae6f9062b64"}, - {file = "pyzmq-26.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:86531e20de249d9204cc6d8b13d5a30537748c78820215161d8a3b9ea58ca111"}, - {file = "pyzmq-26.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:f26a05029ecd2bd306b941ff8cb80f7620b7901421052bc429d238305b1cbf2f"}, - {file = "pyzmq-26.0.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:70770e296a9cb03d955540c99360aab861cbb3cba29516abbd106a15dbd91268"}, - {file = "pyzmq-26.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2740fd7161b39e178554ebf21aa5667a1c9ef0cd2cb74298fd4ef017dae7aec4"}, - {file = "pyzmq-26.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5e3706c32dea077faa42b1c92d825b7f86c866f72532d342e0be5e64d14d858"}, - {file = "pyzmq-26.0.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fa1416876194927f7723d6b7171b95e1115602967fc6bfccbc0d2d51d8ebae1"}, - {file = "pyzmq-26.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ef9a79a48794099c57dc2df00340b5d47c5caa1792f9ddb8c7a26b1280bd575"}, - {file = "pyzmq-26.0.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1c60fcdfa3229aeee4291c5d60faed3a813b18bdadb86299c4bf49e8e51e8605"}, - {file = "pyzmq-26.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e943c39c206b04df2eb5d71305761d7c3ca75fd49452115ea92db1b5b98dbdef"}, - {file = "pyzmq-26.0.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8da0ed8a598693731c76659880a668f4748b59158f26ed283a93f7f04d47447e"}, - {file = "pyzmq-26.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7bf51970b11d67096bede97cdbad0f4333f7664f4708b9b2acb352bf4faa3140"}, - {file = "pyzmq-26.0.2-cp311-cp311-win32.whl", hash = "sha256:6f8e6bd5d066be605faa9fe5ec10aa1a46ad9f18fc8646f2b9aaefc8fb575742"}, - {file = "pyzmq-26.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:6d03da3a0ae691b361edcb39530075461202f699ce05adbb15055a0e1c9bcaa4"}, - {file = "pyzmq-26.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:f84e33321b68ff00b60e9dbd1a483e31ab6022c577c8de525b8e771bd274ce68"}, - {file = "pyzmq-26.0.2-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:44c33ebd1c62a01db7fbc24e18bdda569d6639217d13d5929e986a2b0f69070d"}, - {file = "pyzmq-26.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ac04f904b4fce4afea9cdccbb78e24d468cb610a839d5a698853e14e2a3f9ecf"}, - {file = "pyzmq-26.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2133de5ba9adc5f481884ccb699eac9ce789708292945c05746880f95b241c0"}, - {file = "pyzmq-26.0.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7753c67c570d7fc80c2dc59b90ca1196f1224e0e2e29a548980c95fe0fe27fc1"}, - {file = "pyzmq-26.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d4e51632e6b12e65e8d9d7612446ecda2eda637a868afa7bce16270194650dd"}, - {file = "pyzmq-26.0.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d6c38806f6ecd0acf3104b8d7e76a206bcf56dadd6ce03720d2fa9d9157d5718"}, - {file = "pyzmq-26.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:48f496bbe14686b51cec15406323ae6942851e14022efd7fc0e2ecd092c5982c"}, - {file = "pyzmq-26.0.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e84a3161149c75bb7a7dc8646384186c34033e286a67fec1ad1bdedea165e7f4"}, - {file = "pyzmq-26.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:dabf796c67aa9f5a4fcc956d47f0d48b5c1ed288d628cf53aa1cf08e88654343"}, - {file = "pyzmq-26.0.2-cp312-cp312-win32.whl", hash = "sha256:3eee4c676af1b109f708d80ef0cf57ecb8aaa5900d1edaf90406aea7e0e20e37"}, - {file = "pyzmq-26.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:26721fec65846b3e4450dad050d67d31b017f97e67f7e0647b5f98aa47f828cf"}, - {file = "pyzmq-26.0.2-cp312-cp312-win_arm64.whl", hash = "sha256:653955c6c233e90de128a1b8e882abc7216f41f44218056bd519969c8c413a15"}, - {file = "pyzmq-26.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:becd8d8fb068fbb5a52096efd83a2d8e54354383f691781f53a4c26aee944542"}, - {file = "pyzmq-26.0.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7a15e5465e7083c12517209c9dd24722b25e9b63c49a563922922fc03554eb35"}, - {file = "pyzmq-26.0.2-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e8158ac8616941f874841f9fa0f6d2f1466178c2ff91ea08353fdc19de0d40c2"}, - {file = "pyzmq-26.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea2c6a53e28c7066ea7db86fcc0b71d78d01b818bb11d4a4341ec35059885295"}, - {file = "pyzmq-26.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:bdbc7dab0b0e9c62c97b732899c4242e3282ba803bad668e03650b59b165466e"}, - {file = "pyzmq-26.0.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e74b6d5ef57bb65bf1b4a37453d8d86d88550dde3fb0f23b1f1a24e60c70af5b"}, - {file = "pyzmq-26.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ed4c6ee624ecbc77b18aeeb07bf0700d26571ab95b8f723f0d02e056b5bce438"}, - {file = "pyzmq-26.0.2-cp37-cp37m-win32.whl", hash = "sha256:8a98b3cb0484b83c19d8fb5524c8a469cd9f10e743f5904ac285d92678ee761f"}, - {file = "pyzmq-26.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:aa5f95d71b6eca9cec28aa0a2f8310ea53dea313b63db74932879ff860c1fb8d"}, - {file = "pyzmq-26.0.2-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:5ff56c76ce77b9805378a7a73032c17cbdb1a5b84faa1df03c5d3e306e5616df"}, - {file = "pyzmq-26.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bab697fc1574fee4b81da955678708567c43c813c84c91074e452bda5346c921"}, - {file = "pyzmq-26.0.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0c0fed8aa9ba0488ee1cbdaa304deea92d52fab43d373297002cfcc69c0a20c5"}, - {file = "pyzmq-26.0.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:606b922699fcec472ed814dda4dc3ff7c748254e0b26762a0ba21a726eb1c107"}, - {file = "pyzmq-26.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45f0fd82bad4d199fa993fbf0ac586a7ac5879addbe436a35a389df7e0eb4c91"}, - {file = "pyzmq-26.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:166c5e41045939a52c01e6f374e493d9a6a45dfe677360d3e7026e38c42e8906"}, - {file = "pyzmq-26.0.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d566e859e8b8d5bca08467c093061774924b3d78a5ba290e82735b2569edc84b"}, - {file = "pyzmq-26.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:264ee0e72b72ca59279dc320deab5ae0fac0d97881aed1875ce4bde2e56ffde0"}, - {file = "pyzmq-26.0.2-cp38-cp38-win32.whl", hash = "sha256:3152bbd3a4744cbdd83dfb210ed701838b8b0c9065cef14671d6d91df12197d0"}, - {file = "pyzmq-26.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:bf77601d75ca692c179154b7e5943c286a4aaffec02c491afe05e60493ce95f2"}, - {file = "pyzmq-26.0.2-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:c770a7545b3deca2db185b59175e710a820dd4ed43619f4c02e90b0e227c6252"}, - {file = "pyzmq-26.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d47175f0a380bfd051726bc5c0054036ae4a5d8caf922c62c8a172ccd95c1a2a"}, - {file = "pyzmq-26.0.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9bce298c1ce077837e110367c321285dc4246b531cde1abfc27e4a5bbe2bed4d"}, - {file = "pyzmq-26.0.2-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c40b09b7e184d6e3e1be1c8af2cc320c0f9f610d8a5df3dd866e6e6e4e32b235"}, - {file = "pyzmq-26.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d420d856bf728713874cefb911398efe69e1577835851dd297a308a78c14c249"}, - {file = "pyzmq-26.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d792d3cab987058451e55c70c5926e93e2ceb68ca5a2334863bb903eb860c9cb"}, - {file = "pyzmq-26.0.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:83ec17729cf6d3464dab98a11e98294fcd50e6b17eaabd3d841515c23f6dbd3a"}, - {file = "pyzmq-26.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47c17d5ebfa88ae90f08960c97b49917098665b8cd8be31f2c24e177bcf37a0f"}, - {file = "pyzmq-26.0.2-cp39-cp39-win32.whl", hash = "sha256:d509685d1cd1d018705a811c5f9d5bc237790936ead6d06f6558b77e16cc7235"}, - {file = "pyzmq-26.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:c7cc8cc009e8f6989a6d86c96f87dae5f5fb07d6c96916cdc7719d546152c7db"}, - {file = "pyzmq-26.0.2-cp39-cp39-win_arm64.whl", hash = "sha256:3ada31cb879cd7532f4a85b501f4255c747d4813ab76b35c49ed510ce4865b45"}, - {file = "pyzmq-26.0.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0a6ceaddc830dd3ca86cb8451cf373d1f05215368e11834538c2902ed5205139"}, - {file = "pyzmq-26.0.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a967681463aa7a99eb9a62bb18229b653b45c10ff0947b31cc0837a83dfb86f"}, - {file = "pyzmq-26.0.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6472a73bc115bc40a2076609a90894775abe6faf19a78375675a2f889a613071"}, - {file = "pyzmq-26.0.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d6aea92bcccfe5e5524d3c70a6f16ffdae548390ddad26f4207d55c55a40593"}, - {file = "pyzmq-26.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e025f6351e49d48a5aa2f5a09293aa769b0ee7369c25bed551647234b7fa0c75"}, - {file = "pyzmq-26.0.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:40bd7ebe4dbb37d27f0c56e2a844f360239343a99be422085e13e97da13f73f9"}, - {file = "pyzmq-26.0.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1dd40d586ad6f53764104df6e01810fe1b4e88fd353774629a5e6fe253813f79"}, - {file = "pyzmq-26.0.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f2aca15e9ad8c8657b5b3d7ae3d1724dc8c1c1059c06b4b674c3aa36305f4930"}, - {file = "pyzmq-26.0.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:450ec234736732eb0ebeffdb95a352450d4592f12c3e087e2a9183386d22c8bf"}, - {file = "pyzmq-26.0.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:f43be2bebbd09360a2f23af83b243dc25ffe7b583ea8c722e6df03e03a55f02f"}, - {file = "pyzmq-26.0.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:867f55e54aff254940bcec5eec068e7c0ac1e6bf360ab91479394a8bf356b0e6"}, - {file = "pyzmq-26.0.2-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b4dbc033c5ad46f8c429bf238c25a889b8c1d86bfe23a74e1031a991cb3f0000"}, - {file = "pyzmq-26.0.2-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6e8dd2961462e337e21092ec2da0c69d814dcb1b6e892955a37444a425e9cfb8"}, - {file = "pyzmq-26.0.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35391e72df6c14a09b697c7b94384947c1dd326aca883ff98ff137acdf586c33"}, - {file = "pyzmq-26.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:1c3d3c92fa54eda94ab369ca5b8d35059987c326ba5e55326eb068862f64b1fc"}, - {file = "pyzmq-26.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e7aa61a9cc4f0523373e31fc9255bf4567185a099f85ca3598e64de484da3ab2"}, - {file = "pyzmq-26.0.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee53a8191271f144cc20b12c19daa9f1546adc84a2f33839e3338039b55c373c"}, - {file = "pyzmq-26.0.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac60a980f07fa988983f7bfe6404ef3f1e4303f5288a01713bc1266df6d18783"}, - {file = "pyzmq-26.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88896b1b4817d7b2fe1ec7205c4bbe07bf5d92fb249bf2d226ddea8761996068"}, - {file = "pyzmq-26.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:18dfffe23751edee917764ffa133d5d3fef28dfd1cf3adebef8c90bc854c74c4"}, - {file = "pyzmq-26.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:6926dd14cfe6967d3322640b6d5c3c3039db71716a5e43cca6e3b474e73e0b36"}, - {file = "pyzmq-26.0.2.tar.gz", hash = "sha256:f0f9bb370449158359bb72a3e12c658327670c0ffe6fbcd1af083152b64f9df0"}, + {file = "pyzmq-26.0.3-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:44dd6fc3034f1eaa72ece33588867df9e006a7303725a12d64c3dff92330f625"}, + {file = "pyzmq-26.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:acb704195a71ac5ea5ecf2811c9ee19ecdc62b91878528302dd0be1b9451cc90"}, + {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dbb9c997932473a27afa93954bb77a9f9b786b4ccf718d903f35da3232317de"}, + {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bcb34f869d431799c3ee7d516554797f7760cb2198ecaa89c3f176f72d062be"}, + {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ece17ec5f20d7d9b442e5174ae9f020365d01ba7c112205a4d59cf19dc38ee"}, + {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ba6e5e6588e49139a0979d03a7deb9c734bde647b9a8808f26acf9c547cab1bf"}, + {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3bf8b000a4e2967e6dfdd8656cd0757d18c7e5ce3d16339e550bd462f4857e59"}, + {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2136f64fbb86451dbbf70223635a468272dd20075f988a102bf8a3f194a411dc"}, + {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e8918973fbd34e7814f59143c5f600ecd38b8038161239fd1a3d33d5817a38b8"}, + {file = "pyzmq-26.0.3-cp310-cp310-win32.whl", hash = "sha256:0aaf982e68a7ac284377d051c742610220fd06d330dcd4c4dbb4cdd77c22a537"}, + {file = "pyzmq-26.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:f1a9b7d00fdf60b4039f4455afd031fe85ee8305b019334b72dcf73c567edc47"}, + {file = "pyzmq-26.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:80b12f25d805a919d53efc0a5ad7c0c0326f13b4eae981a5d7b7cc343318ebb7"}, + {file = "pyzmq-26.0.3-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:a72a84570f84c374b4c287183debc776dc319d3e8ce6b6a0041ce2e400de3f32"}, + {file = "pyzmq-26.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7ca684ee649b55fd8f378127ac8462fb6c85f251c2fb027eb3c887e8ee347bcd"}, + {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e222562dc0f38571c8b1ffdae9d7adb866363134299264a1958d077800b193b7"}, + {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f17cde1db0754c35a91ac00b22b25c11da6eec5746431d6e5092f0cd31a3fea9"}, + {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b7c0c0b3244bb2275abe255d4a30c050d541c6cb18b870975553f1fb6f37527"}, + {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ac97a21de3712afe6a6c071abfad40a6224fd14fa6ff0ff8d0c6e6cd4e2f807a"}, + {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:88b88282e55fa39dd556d7fc04160bcf39dea015f78e0cecec8ff4f06c1fc2b5"}, + {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:72b67f966b57dbd18dcc7efbc1c7fc9f5f983e572db1877081f075004614fcdd"}, + {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4b6cecbbf3b7380f3b61de3a7b93cb721125dc125c854c14ddc91225ba52f83"}, + {file = "pyzmq-26.0.3-cp311-cp311-win32.whl", hash = "sha256:eed56b6a39216d31ff8cd2f1d048b5bf1700e4b32a01b14379c3b6dde9ce3aa3"}, + {file = "pyzmq-26.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:3191d312c73e3cfd0f0afdf51df8405aafeb0bad71e7ed8f68b24b63c4f36500"}, + {file = "pyzmq-26.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:b6907da3017ef55139cf0e417c5123a84c7332520e73a6902ff1f79046cd3b94"}, + {file = "pyzmq-26.0.3-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:068ca17214038ae986d68f4a7021f97e187ed278ab6dccb79f837d765a54d753"}, + {file = "pyzmq-26.0.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7821d44fe07335bea256b9f1f41474a642ca55fa671dfd9f00af8d68a920c2d4"}, + {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eeb438a26d87c123bb318e5f2b3d86a36060b01f22fbdffd8cf247d52f7c9a2b"}, + {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69ea9d6d9baa25a4dc9cef5e2b77b8537827b122214f210dd925132e34ae9b12"}, + {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7daa3e1369355766dea11f1d8ef829905c3b9da886ea3152788dc25ee6079e02"}, + {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6ca7a9a06b52d0e38ccf6bca1aeff7be178917893f3883f37b75589d42c4ac20"}, + {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1b7d0e124948daa4d9686d421ef5087c0516bc6179fdcf8828b8444f8e461a77"}, + {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e746524418b70f38550f2190eeee834db8850088c834d4c8406fbb9bc1ae10b2"}, + {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:6b3146f9ae6af82c47a5282ac8803523d381b3b21caeae0327ed2f7ecb718798"}, + {file = "pyzmq-26.0.3-cp312-cp312-win32.whl", hash = "sha256:2b291d1230845871c00c8462c50565a9cd6026fe1228e77ca934470bb7d70ea0"}, + {file = "pyzmq-26.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:926838a535c2c1ea21c903f909a9a54e675c2126728c21381a94ddf37c3cbddf"}, + {file = "pyzmq-26.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:5bf6c237f8c681dfb91b17f8435b2735951f0d1fad10cc5dfd96db110243370b"}, + {file = "pyzmq-26.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c0991f5a96a8e620f7691e61178cd8f457b49e17b7d9cfa2067e2a0a89fc1d5"}, + {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dbf012d8fcb9f2cf0643b65df3b355fdd74fc0035d70bb5c845e9e30a3a4654b"}, + {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:01fbfbeb8249a68d257f601deb50c70c929dc2dfe683b754659569e502fbd3aa"}, + {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c8eb19abe87029c18f226d42b8a2c9efdd139d08f8bf6e085dd9075446db450"}, + {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5344b896e79800af86ad643408ca9aa303a017f6ebff8cee5a3163c1e9aec987"}, + {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:204e0f176fd1d067671157d049466869b3ae1fc51e354708b0dc41cf94e23a3a"}, + {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a42db008d58530efa3b881eeee4991146de0b790e095f7ae43ba5cc612decbc5"}, + {file = "pyzmq-26.0.3-cp37-cp37m-win32.whl", hash = "sha256:8d7a498671ca87e32b54cb47c82a92b40130a26c5197d392720a1bce1b3c77cf"}, + {file = "pyzmq-26.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:3b4032a96410bdc760061b14ed6a33613ffb7f702181ba999df5d16fb96ba16a"}, + {file = "pyzmq-26.0.3-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2cc4e280098c1b192c42a849de8de2c8e0f3a84086a76ec5b07bfee29bda7d18"}, + {file = "pyzmq-26.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5bde86a2ed3ce587fa2b207424ce15b9a83a9fa14422dcc1c5356a13aed3df9d"}, + {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:34106f68e20e6ff253c9f596ea50397dbd8699828d55e8fa18bd4323d8d966e6"}, + {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ebbbd0e728af5db9b04e56389e2299a57ea8b9dd15c9759153ee2455b32be6ad"}, + {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6b1d1c631e5940cac5a0b22c5379c86e8df6a4ec277c7a856b714021ab6cfad"}, + {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e891ce81edd463b3b4c3b885c5603c00141151dd9c6936d98a680c8c72fe5c67"}, + {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9b273ecfbc590a1b98f014ae41e5cf723932f3b53ba9367cfb676f838038b32c"}, + {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b32bff85fb02a75ea0b68f21e2412255b5731f3f389ed9aecc13a6752f58ac97"}, + {file = "pyzmq-26.0.3-cp38-cp38-win32.whl", hash = "sha256:f6c21c00478a7bea93caaaef9e7629145d4153b15a8653e8bb4609d4bc70dbfc"}, + {file = "pyzmq-26.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:3401613148d93ef0fd9aabdbddb212de3db7a4475367f49f590c837355343972"}, + {file = "pyzmq-26.0.3-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:2ed8357f4c6e0daa4f3baf31832df8a33334e0fe5b020a61bc8b345a3db7a606"}, + {file = "pyzmq-26.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c1c8f2a2ca45292084c75bb6d3a25545cff0ed931ed228d3a1810ae3758f975f"}, + {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b63731993cdddcc8e087c64e9cf003f909262b359110070183d7f3025d1c56b5"}, + {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b3cd31f859b662ac5d7f4226ec7d8bd60384fa037fc02aee6ff0b53ba29a3ba8"}, + {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:115f8359402fa527cf47708d6f8a0f8234f0e9ca0cab7c18c9c189c194dbf620"}, + {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:715bdf952b9533ba13dfcf1f431a8f49e63cecc31d91d007bc1deb914f47d0e4"}, + {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e1258c639e00bf5e8a522fec6c3eaa3e30cf1c23a2f21a586be7e04d50c9acab"}, + {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:15c59e780be8f30a60816a9adab900c12a58d79c1ac742b4a8df044ab2a6d920"}, + {file = "pyzmq-26.0.3-cp39-cp39-win32.whl", hash = "sha256:d0cdde3c78d8ab5b46595054e5def32a755fc028685add5ddc7403e9f6de9879"}, + {file = "pyzmq-26.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:ce828058d482ef860746bf532822842e0ff484e27f540ef5c813d516dd8896d2"}, + {file = "pyzmq-26.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:788f15721c64109cf720791714dc14afd0f449d63f3a5487724f024345067381"}, + {file = "pyzmq-26.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2c18645ef6294d99b256806e34653e86236eb266278c8ec8112622b61db255de"}, + {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e6bc96ebe49604df3ec2c6389cc3876cabe475e6bfc84ced1bf4e630662cb35"}, + {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:971e8990c5cc4ddcff26e149398fc7b0f6a042306e82500f5e8db3b10ce69f84"}, + {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8416c23161abd94cc7da80c734ad7c9f5dbebdadfdaa77dad78244457448223"}, + {file = "pyzmq-26.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:082a2988364b60bb5de809373098361cf1dbb239623e39e46cb18bc035ed9c0c"}, + {file = "pyzmq-26.0.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d57dfbf9737763b3a60d26e6800e02e04284926329aee8fb01049635e957fe81"}, + {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:77a85dca4c2430ac04dc2a2185c2deb3858a34fe7f403d0a946fa56970cf60a1"}, + {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4c82a6d952a1d555bf4be42b6532927d2a5686dd3c3e280e5f63225ab47ac1f5"}, + {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4496b1282c70c442809fc1b151977c3d967bfb33e4e17cedbf226d97de18f709"}, + {file = "pyzmq-26.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:e4946d6bdb7ba972dfda282f9127e5756d4f299028b1566d1245fa0d438847e6"}, + {file = "pyzmq-26.0.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:03c0ae165e700364b266876d712acb1ac02693acd920afa67da2ebb91a0b3c09"}, + {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3e3070e680f79887d60feeda051a58d0ac36622e1759f305a41059eff62c6da7"}, + {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6ca08b840fe95d1c2bd9ab92dac5685f949fc6f9ae820ec16193e5ddf603c3b2"}, + {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e76654e9dbfb835b3518f9938e565c7806976c07b37c33526b574cc1a1050480"}, + {file = "pyzmq-26.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:871587bdadd1075b112e697173e946a07d722459d20716ceb3d1bd6c64bd08ce"}, + {file = "pyzmq-26.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d0a2d1bd63a4ad79483049b26514e70fa618ce6115220da9efdff63688808b17"}, + {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0270b49b6847f0d106d64b5086e9ad5dc8a902413b5dbbb15d12b60f9c1747a4"}, + {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:703c60b9910488d3d0954ca585c34f541e506a091a41930e663a098d3b794c67"}, + {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74423631b6be371edfbf7eabb02ab995c2563fee60a80a30829176842e71722a"}, + {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4adfbb5451196842a88fda3612e2c0414134874bffb1c2ce83ab4242ec9e027d"}, + {file = "pyzmq-26.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3516119f4f9b8671083a70b6afaa0a070f5683e431ab3dc26e9215620d7ca1ad"}, + {file = "pyzmq-26.0.3.tar.gz", hash = "sha256:dba7d9f2e047dfa2bca3b01f4f84aa5246725203d6284e3790f2ca15fba6b40a"}, ] [package.dependencies] @@ -2964,13 +3015,13 @@ cffi = {version = "*", markers = "implementation_name == \"pypy\""} [[package]] name = "qtconsole" -version = "5.5.1" +version = "5.5.2" description = "Jupyter Qt console" optional = false -python-versions = ">= 3.8" +python-versions = ">=3.8" files = [ - {file = "qtconsole-5.5.1-py3-none-any.whl", hash = "sha256:8c75fa3e9b4ed884880ff7cea90a1b67451219279ec33deaee1d59e3df1a5d2b"}, - {file = "qtconsole-5.5.1.tar.gz", hash = "sha256:a0e806c6951db9490628e4df80caec9669b65149c7ba40f9bf033c025a5b56bc"}, + {file = "qtconsole-5.5.2-py3-none-any.whl", hash = "sha256:42d745f3d05d36240244a04e1e1ec2a86d5d9b6edb16dbdef582ccb629e87e0b"}, + {file = "qtconsole-5.5.2.tar.gz", hash = "sha256:6b5fb11274b297463706af84dcbbd5c92273b1f619e6d25d08874b0a88516989"}, ] [package.dependencies] @@ -3006,13 +3057,13 @@ test = ["pytest (>=6,!=7.0.0,!=7.0.1)", "pytest-cov (>=3.0.0)", "pytest-qt"] [[package]] name = "referencing" -version = "0.35.0" +version = "0.35.1" description = "JSON Referencing + Python" optional = false python-versions = ">=3.8" files = [ - {file = "referencing-0.35.0-py3-none-any.whl", hash = "sha256:8080727b30e364e5783152903672df9b6b091c926a146a759080b62ca3126cd6"}, - {file = "referencing-0.35.0.tar.gz", hash = "sha256:191e936b0c696d0af17ad7430a3dc68e88bc11be6514f4757dc890f04ab05889"}, + {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, + {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, ] [package.dependencies] @@ -3021,115 +3072,101 @@ rpds-py = ">=0.7.0" [[package]] name = "regex" -version = "2024.4.16" +version = "2024.5.15" description = "Alternative regular expression module, to replace re." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "regex-2024.4.16-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb83cc090eac63c006871fd24db5e30a1f282faa46328572661c0a24a2323a08"}, - {file = "regex-2024.4.16-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c91e1763696c0eb66340c4df98623c2d4e77d0746b8f8f2bee2c6883fd1fe18"}, - {file = "regex-2024.4.16-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:10188fe732dec829c7acca7422cdd1bf57d853c7199d5a9e96bb4d40db239c73"}, - {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:956b58d692f235cfbf5b4f3abd6d99bf102f161ccfe20d2fd0904f51c72c4c66"}, - {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a70b51f55fd954d1f194271695821dd62054d949efd6368d8be64edd37f55c86"}, - {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c02fcd2bf45162280613d2e4a1ca3ac558ff921ae4e308ecb307650d3a6ee51"}, - {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4ed75ea6892a56896d78f11006161eea52c45a14994794bcfa1654430984b22"}, - {file = "regex-2024.4.16-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd727ad276bb91928879f3aa6396c9a1d34e5e180dce40578421a691eeb77f47"}, - {file = "regex-2024.4.16-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7cbc5d9e8a1781e7be17da67b92580d6ce4dcef5819c1b1b89f49d9678cc278c"}, - {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:78fddb22b9ef810b63ef341c9fcf6455232d97cfe03938cbc29e2672c436670e"}, - {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:445ca8d3c5a01309633a0c9db57150312a181146315693273e35d936472df912"}, - {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:95399831a206211d6bc40224af1c635cb8790ddd5c7493e0bd03b85711076a53"}, - {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:7731728b6568fc286d86745f27f07266de49603a6fdc4d19c87e8c247be452af"}, - {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4facc913e10bdba42ec0aee76d029aedda628161a7ce4116b16680a0413f658a"}, - {file = "regex-2024.4.16-cp310-cp310-win32.whl", hash = "sha256:911742856ce98d879acbea33fcc03c1d8dc1106234c5e7d068932c945db209c0"}, - {file = "regex-2024.4.16-cp310-cp310-win_amd64.whl", hash = "sha256:e0a2df336d1135a0b3a67f3bbf78a75f69562c1199ed9935372b82215cddd6e2"}, - {file = "regex-2024.4.16-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1210365faba7c2150451eb78ec5687871c796b0f1fa701bfd2a4a25420482d26"}, - {file = "regex-2024.4.16-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9ab40412f8cd6f615bfedea40c8bf0407d41bf83b96f6fc9ff34976d6b7037fd"}, - {file = "regex-2024.4.16-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fd80d1280d473500d8086d104962a82d77bfbf2b118053824b7be28cd5a79ea5"}, - {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bb966fdd9217e53abf824f437a5a2d643a38d4fd5fd0ca711b9da683d452969"}, - {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:20b7a68444f536365af42a75ccecb7ab41a896a04acf58432db9e206f4e525d6"}, - {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b74586dd0b039c62416034f811d7ee62810174bb70dffcca6439f5236249eb09"}, - {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c8290b44d8b0af4e77048646c10c6e3aa583c1ca67f3b5ffb6e06cf0c6f0f89"}, - {file = "regex-2024.4.16-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2d80a6749724b37853ece57988b39c4e79d2b5fe2869a86e8aeae3bbeef9eb0"}, - {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3a1018e97aeb24e4f939afcd88211ace472ba566efc5bdf53fd8fd7f41fa7170"}, - {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8d015604ee6204e76569d2f44e5a210728fa917115bef0d102f4107e622b08d5"}, - {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:3d5ac5234fb5053850d79dd8eb1015cb0d7d9ed951fa37aa9e6249a19aa4f336"}, - {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:0a38d151e2cdd66d16dab550c22f9521ba79761423b87c01dae0a6e9add79c0d"}, - {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:159dc4e59a159cb8e4e8f8961eb1fa5d58f93cb1acd1701d8aff38d45e1a84a6"}, - {file = "regex-2024.4.16-cp311-cp311-win32.whl", hash = "sha256:ba2336d6548dee3117520545cfe44dc28a250aa091f8281d28804aa8d707d93d"}, - {file = "regex-2024.4.16-cp311-cp311-win_amd64.whl", hash = "sha256:8f83b6fd3dc3ba94d2b22717f9c8b8512354fd95221ac661784df2769ea9bba9"}, - {file = "regex-2024.4.16-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:80b696e8972b81edf0af2a259e1b2a4a661f818fae22e5fa4fa1a995fb4a40fd"}, - {file = "regex-2024.4.16-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d61ae114d2a2311f61d90c2ef1358518e8f05eafda76eaf9c772a077e0b465ec"}, - {file = "regex-2024.4.16-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ba6745440b9a27336443b0c285d705ce73adb9ec90e2f2004c64d95ab5a7598"}, - {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6295004b2dd37b0835ea5c14a33e00e8cfa3c4add4d587b77287825f3418d310"}, - {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4aba818dcc7263852aabb172ec27b71d2abca02a593b95fa79351b2774eb1d2b"}, - {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0800631e565c47520aaa04ae38b96abc5196fe8b4aa9bd864445bd2b5848a7a"}, - {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08dea89f859c3df48a440dbdcd7b7155bc675f2fa2ec8c521d02dc69e877db70"}, - {file = "regex-2024.4.16-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eeaa0b5328b785abc344acc6241cffde50dc394a0644a968add75fcefe15b9d4"}, - {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4e819a806420bc010489f4e741b3036071aba209f2e0989d4750b08b12a9343f"}, - {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:c2d0e7cbb6341e830adcbfa2479fdeebbfbb328f11edd6b5675674e7a1e37730"}, - {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:91797b98f5e34b6a49f54be33f72e2fb658018ae532be2f79f7c63b4ae225145"}, - {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:d2da13568eff02b30fd54fccd1e042a70fe920d816616fda4bf54ec705668d81"}, - {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:370c68dc5570b394cbaadff50e64d705f64debed30573e5c313c360689b6aadc"}, - {file = "regex-2024.4.16-cp312-cp312-win32.whl", hash = "sha256:904c883cf10a975b02ab3478bce652f0f5346a2c28d0a8521d97bb23c323cc8b"}, - {file = "regex-2024.4.16-cp312-cp312-win_amd64.whl", hash = "sha256:785c071c982dce54d44ea0b79cd6dfafddeccdd98cfa5f7b86ef69b381b457d9"}, - {file = "regex-2024.4.16-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e2f142b45c6fed48166faeb4303b4b58c9fcd827da63f4cf0a123c3480ae11fb"}, - {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e87ab229332ceb127a165612d839ab87795972102cb9830e5f12b8c9a5c1b508"}, - {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:81500ed5af2090b4a9157a59dbc89873a25c33db1bb9a8cf123837dcc9765047"}, - {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b340cccad138ecb363324aa26893963dcabb02bb25e440ebdf42e30963f1a4e0"}, - {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c72608e70f053643437bd2be0608f7f1c46d4022e4104d76826f0839199347a"}, - {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a01fe2305e6232ef3e8f40bfc0f0f3a04def9aab514910fa4203bafbc0bb4682"}, - {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:03576e3a423d19dda13e55598f0fd507b5d660d42c51b02df4e0d97824fdcae3"}, - {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:549c3584993772e25f02d0656ac48abdda73169fe347263948cf2b1cead622f3"}, - {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:34422d5a69a60b7e9a07a690094e824b66f5ddc662a5fc600d65b7c174a05f04"}, - {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:5f580c651a72b75c39e311343fe6875d6f58cf51c471a97f15a938d9fe4e0d37"}, - {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3399dd8a7495bbb2bacd59b84840eef9057826c664472e86c91d675d007137f5"}, - {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8d1f86f3f4e2388aa3310b50694ac44daefbd1681def26b4519bd050a398dc5a"}, - {file = "regex-2024.4.16-cp37-cp37m-win32.whl", hash = "sha256:dd5acc0a7d38fdc7a3a6fd3ad14c880819008ecb3379626e56b163165162cc46"}, - {file = "regex-2024.4.16-cp37-cp37m-win_amd64.whl", hash = "sha256:ba8122e3bb94ecda29a8de4cf889f600171424ea586847aa92c334772d200331"}, - {file = "regex-2024.4.16-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:743deffdf3b3481da32e8a96887e2aa945ec6685af1cfe2bcc292638c9ba2f48"}, - {file = "regex-2024.4.16-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7571f19f4a3fd00af9341c7801d1ad1967fc9c3f5e62402683047e7166b9f2b4"}, - {file = "regex-2024.4.16-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:df79012ebf6f4efb8d307b1328226aef24ca446b3ff8d0e30202d7ebcb977a8c"}, - {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e757d475953269fbf4b441207bb7dbdd1c43180711b6208e129b637792ac0b93"}, - {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4313ab9bf6a81206c8ac28fdfcddc0435299dc88cad12cc6305fd0e78b81f9e4"}, - {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d83c2bc678453646f1a18f8db1e927a2d3f4935031b9ad8a76e56760461105dd"}, - {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9df1bfef97db938469ef0a7354b2d591a2d438bc497b2c489471bec0e6baf7c4"}, - {file = "regex-2024.4.16-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62120ed0de69b3649cc68e2965376048793f466c5a6c4370fb27c16c1beac22d"}, - {file = "regex-2024.4.16-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c2ef6f7990b6e8758fe48ad08f7e2f66c8f11dc66e24093304b87cae9037bb4a"}, - {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8fc6976a3395fe4d1fbeb984adaa8ec652a1e12f36b56ec8c236e5117b585427"}, - {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:03e68f44340528111067cecf12721c3df4811c67268b897fbe695c95f860ac42"}, - {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ec7e0043b91115f427998febaa2beb82c82df708168b35ece3accb610b91fac1"}, - {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:c21fc21a4c7480479d12fd8e679b699f744f76bb05f53a1d14182b31f55aac76"}, - {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:12f6a3f2f58bb7344751919a1876ee1b976fe08b9ffccb4bbea66f26af6017b9"}, - {file = "regex-2024.4.16-cp38-cp38-win32.whl", hash = "sha256:479595a4fbe9ed8f8f72c59717e8cf222da2e4c07b6ae5b65411e6302af9708e"}, - {file = "regex-2024.4.16-cp38-cp38-win_amd64.whl", hash = "sha256:0534b034fba6101611968fae8e856c1698da97ce2efb5c2b895fc8b9e23a5834"}, - {file = "regex-2024.4.16-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a7ccdd1c4a3472a7533b0a7aa9ee34c9a2bef859ba86deec07aff2ad7e0c3b94"}, - {file = "regex-2024.4.16-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f2f017c5be19984fbbf55f8af6caba25e62c71293213f044da3ada7091a4455"}, - {file = "regex-2024.4.16-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:803b8905b52de78b173d3c1e83df0efb929621e7b7c5766c0843704d5332682f"}, - {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:684008ec44ad275832a5a152f6e764bbe1914bea10968017b6feaecdad5736e0"}, - {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65436dce9fdc0aeeb0a0effe0839cb3d6a05f45aa45a4d9f9c60989beca78b9c"}, - {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea355eb43b11764cf799dda62c658c4d2fdb16af41f59bb1ccfec517b60bcb07"}, - {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c1165f3809ce7774f05cb74e5408cd3aa93ee8573ae959a97a53db3ca3180d"}, - {file = "regex-2024.4.16-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cccc79a9be9b64c881f18305a7c715ba199e471a3973faeb7ba84172abb3f317"}, - {file = "regex-2024.4.16-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00169caa125f35d1bca6045d65a662af0202704489fada95346cfa092ec23f39"}, - {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6cc38067209354e16c5609b66285af17a2863a47585bcf75285cab33d4c3b8df"}, - {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:23cff1b267038501b179ccbbd74a821ac4a7192a1852d1d558e562b507d46013"}, - {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:b9d320b3bf82a39f248769fc7f188e00f93526cc0fe739cfa197868633d44701"}, - {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:89ec7f2c08937421bbbb8b48c54096fa4f88347946d4747021ad85f1b3021b3c"}, - {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4918fd5f8b43aa7ec031e0fef1ee02deb80b6afd49c85f0790be1dc4ce34cb50"}, - {file = "regex-2024.4.16-cp39-cp39-win32.whl", hash = "sha256:684e52023aec43bdf0250e843e1fdd6febbe831bd9d52da72333fa201aaa2335"}, - {file = "regex-2024.4.16-cp39-cp39-win_amd64.whl", hash = "sha256:e697e1c0238133589e00c244a8b676bc2cfc3ab4961318d902040d099fec7483"}, - {file = "regex-2024.4.16.tar.gz", hash = "sha256:fa454d26f2e87ad661c4f0c5a5fe4cf6aab1e307d1b94f16ffdfcb089ba685c0"}, + {file = "regex-2024.5.15-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a81e3cfbae20378d75185171587cbf756015ccb14840702944f014e0d93ea09f"}, + {file = "regex-2024.5.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b59138b219ffa8979013be7bc85bb60c6f7b7575df3d56dc1e403a438c7a3f6"}, + {file = "regex-2024.5.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0bd000c6e266927cb7a1bc39d55be95c4b4f65c5be53e659537537e019232b1"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eaa7ddaf517aa095fa8da0b5015c44d03da83f5bd49c87961e3c997daed0de7"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba68168daedb2c0bab7fd7e00ced5ba90aebf91024dea3c88ad5063c2a562cca"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6e8d717bca3a6e2064fc3a08df5cbe366369f4b052dcd21b7416e6d71620dca1"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1337b7dbef9b2f71121cdbf1e97e40de33ff114801263b275aafd75303bd62b5"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9ebd0a36102fcad2f03696e8af4ae682793a5d30b46c647eaf280d6cfb32796"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9efa1a32ad3a3ea112224897cdaeb6aa00381627f567179c0314f7b65d354c62"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1595f2d10dff3d805e054ebdc41c124753631b6a471b976963c7b28543cf13b0"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b802512f3e1f480f41ab5f2cfc0e2f761f08a1f41092d6718868082fc0d27143"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a0981022dccabca811e8171f913de05720590c915b033b7e601f35ce4ea7019f"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:19068a6a79cf99a19ccefa44610491e9ca02c2be3305c7760d3831d38a467a6f"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1b5269484f6126eee5e687785e83c6b60aad7663dafe842b34691157e5083e53"}, + {file = "regex-2024.5.15-cp310-cp310-win32.whl", hash = "sha256:ada150c5adfa8fbcbf321c30c751dc67d2f12f15bd183ffe4ec7cde351d945b3"}, + {file = "regex-2024.5.15-cp310-cp310-win_amd64.whl", hash = "sha256:ac394ff680fc46b97487941f5e6ae49a9f30ea41c6c6804832063f14b2a5a145"}, + {file = "regex-2024.5.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f5b1dff3ad008dccf18e652283f5e5339d70bf8ba7c98bf848ac33db10f7bc7a"}, + {file = "regex-2024.5.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c6a2b494a76983df8e3d3feea9b9ffdd558b247e60b92f877f93a1ff43d26656"}, + {file = "regex-2024.5.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a32b96f15c8ab2e7d27655969a23895eb799de3665fa94349f3b2fbfd547236f"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10002e86e6068d9e1c91eae8295ef690f02f913c57db120b58fdd35a6bb1af35"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ec54d5afa89c19c6dd8541a133be51ee1017a38b412b1321ccb8d6ddbeb4cf7d"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10e4ce0dca9ae7a66e6089bb29355d4432caed736acae36fef0fdd7879f0b0cb"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e507ff1e74373c4d3038195fdd2af30d297b4f0950eeda6f515ae3d84a1770f"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1f059a4d795e646e1c37665b9d06062c62d0e8cc3c511fe01315973a6542e40"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0721931ad5fe0dda45d07f9820b90b2148ccdd8e45bb9e9b42a146cb4f695649"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:833616ddc75ad595dee848ad984d067f2f31be645d603e4d158bba656bbf516c"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:287eb7f54fc81546346207c533ad3c2c51a8d61075127d7f6d79aaf96cdee890"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:19dfb1c504781a136a80ecd1fff9f16dddf5bb43cec6871778c8a907a085bb3d"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:119af6e56dce35e8dfb5222573b50c89e5508d94d55713c75126b753f834de68"}, + {file = "regex-2024.5.15-cp311-cp311-win32.whl", hash = "sha256:1c1c174d6ec38d6c8a7504087358ce9213d4332f6293a94fbf5249992ba54efa"}, + {file = "regex-2024.5.15-cp311-cp311-win_amd64.whl", hash = "sha256:9e717956dcfd656f5055cc70996ee2cc82ac5149517fc8e1b60261b907740201"}, + {file = "regex-2024.5.15-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:632b01153e5248c134007209b5c6348a544ce96c46005d8456de1d552455b014"}, + {file = "regex-2024.5.15-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e64198f6b856d48192bf921421fdd8ad8eb35e179086e99e99f711957ffedd6e"}, + {file = "regex-2024.5.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68811ab14087b2f6e0fc0c2bae9ad689ea3584cad6917fc57be6a48bbd012c49"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8ec0c2fea1e886a19c3bee0cd19d862b3aa75dcdfb42ebe8ed30708df64687a"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0c0c0003c10f54a591d220997dd27d953cd9ccc1a7294b40a4be5312be8797b"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2431b9e263af1953c55abbd3e2efca67ca80a3de8a0437cb58e2421f8184717a"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a605586358893b483976cffc1723fb0f83e526e8f14c6e6614e75919d9862cf"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:391d7f7f1e409d192dba8bcd42d3e4cf9e598f3979cdaed6ab11288da88cb9f2"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9ff11639a8d98969c863d4617595eb5425fd12f7c5ef6621a4b74b71ed8726d5"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4eee78a04e6c67e8391edd4dad3279828dd66ac4b79570ec998e2155d2e59fd5"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8fe45aa3f4aa57faabbc9cb46a93363edd6197cbc43523daea044e9ff2fea83e"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d0a3d8d6acf0c78a1fff0e210d224b821081330b8524e3e2bc5a68ef6ab5803d"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c486b4106066d502495b3025a0a7251bf37ea9540433940a23419461ab9f2a80"}, + {file = "regex-2024.5.15-cp312-cp312-win32.whl", hash = "sha256:c49e15eac7c149f3670b3e27f1f28a2c1ddeccd3a2812cba953e01be2ab9b5fe"}, + {file = "regex-2024.5.15-cp312-cp312-win_amd64.whl", hash = "sha256:673b5a6da4557b975c6c90198588181029c60793835ce02f497ea817ff647cb2"}, + {file = "regex-2024.5.15-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:87e2a9c29e672fc65523fb47a90d429b70ef72b901b4e4b1bd42387caf0d6835"}, + {file = "regex-2024.5.15-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c3bea0ba8b73b71b37ac833a7f3fd53825924165da6a924aec78c13032f20850"}, + {file = "regex-2024.5.15-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bfc4f82cabe54f1e7f206fd3d30fda143f84a63fe7d64a81558d6e5f2e5aaba9"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5bb9425fe881d578aeca0b2b4b3d314ec88738706f66f219c194d67179337cb"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64c65783e96e563103d641760664125e91bd85d8e49566ee560ded4da0d3e704"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf2430df4148b08fb4324b848672514b1385ae3807651f3567871f130a728cc3"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5397de3219a8b08ae9540c48f602996aa6b0b65d5a61683e233af8605c42b0f2"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:455705d34b4154a80ead722f4f185b04c4237e8e8e33f265cd0798d0e44825fa"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2b6f1b3bb6f640c1a92be3bbfbcb18657b125b99ecf141fb3310b5282c7d4ed"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3ad070b823ca5890cab606c940522d05d3d22395d432f4aaaf9d5b1653e47ced"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5b5467acbfc153847d5adb21e21e29847bcb5870e65c94c9206d20eb4e99a384"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:e6662686aeb633ad65be2a42b4cb00178b3fbf7b91878f9446075c404ada552f"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:2b4c884767504c0e2401babe8b5b7aea9148680d2e157fa28f01529d1f7fcf67"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3cd7874d57f13bf70078f1ff02b8b0aa48d5b9ed25fc48547516c6aba36f5741"}, + {file = "regex-2024.5.15-cp38-cp38-win32.whl", hash = "sha256:e4682f5ba31f475d58884045c1a97a860a007d44938c4c0895f41d64481edbc9"}, + {file = "regex-2024.5.15-cp38-cp38-win_amd64.whl", hash = "sha256:d99ceffa25ac45d150e30bd9ed14ec6039f2aad0ffa6bb87a5936f5782fc1569"}, + {file = "regex-2024.5.15-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13cdaf31bed30a1e1c2453ef6015aa0983e1366fad2667657dbcac7b02f67133"}, + {file = "regex-2024.5.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cac27dcaa821ca271855a32188aa61d12decb6fe45ffe3e722401fe61e323cd1"}, + {file = "regex-2024.5.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7dbe2467273b875ea2de38ded4eba86cbcbc9a1a6d0aa11dcf7bd2e67859c435"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64f18a9a3513a99c4bef0e3efd4c4a5b11228b48aa80743be822b71e132ae4f5"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d347a741ea871c2e278fde6c48f85136c96b8659b632fb57a7d1ce1872547600"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1878b8301ed011704aea4c806a3cadbd76f84dece1ec09cc9e4dc934cfa5d4da"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4babf07ad476aaf7830d77000874d7611704a7fcf68c9c2ad151f5d94ae4bfc4"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35cb514e137cb3488bce23352af3e12fb0dbedd1ee6e60da053c69fb1b29cc6c"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cdd09d47c0b2efee9378679f8510ee6955d329424c659ab3c5e3a6edea696294"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:72d7a99cd6b8f958e85fc6ca5b37c4303294954eac1376535b03c2a43eb72629"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:a094801d379ab20c2135529948cb84d417a2169b9bdceda2a36f5f10977ebc16"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c0c18345010870e58238790a6779a1219b4d97bd2e77e1140e8ee5d14df071aa"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:16093f563098448ff6b1fa68170e4acbef94e6b6a4e25e10eae8598bb1694b5d"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e38a7d4e8f633a33b4c7350fbd8bad3b70bf81439ac67ac38916c4a86b465456"}, + {file = "regex-2024.5.15-cp39-cp39-win32.whl", hash = "sha256:71a455a3c584a88f654b64feccc1e25876066c4f5ef26cd6dd711308aa538694"}, + {file = "regex-2024.5.15-cp39-cp39-win_amd64.whl", hash = "sha256:cab12877a9bdafde5500206d1020a584355a97884dfd388af3699e9137bf7388"}, + {file = "regex-2024.5.15.tar.gz", hash = "sha256:d3ee02d9e5f482cc8309134a91eeaacbdd2261ba111b0fef3748eeb4913e6a2c"}, ] [[package]] name = "requests" -version = "2.32.0" +version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" files = [ - {file = "requests-2.32.0-py3-none-any.whl", hash = "sha256:f2c3881dddb70d056c5bd7600a4fae312b2a300e39be6a118d30b90bd27262b5"}, - {file = "requests-2.32.0.tar.gz", hash = "sha256:fa5490319474c82ef1d2c9bc459d3652e3ae4ef4c4ebdd18a21145a47ca4b6b8"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] @@ -3169,110 +3206,110 @@ files = [ [[package]] name = "rpds-py" -version = "0.18.0" +version = "0.18.1" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.8" files = [ - {file = "rpds_py-0.18.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e"}, - {file = "rpds_py-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88"}, - {file = "rpds_py-0.18.0-cp310-none-win32.whl", hash = "sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337"}, - {file = "rpds_py-0.18.0-cp310-none-win_amd64.whl", hash = "sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66"}, - {file = "rpds_py-0.18.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4"}, - {file = "rpds_py-0.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836"}, - {file = "rpds_py-0.18.0-cp311-none-win32.whl", hash = "sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1"}, - {file = "rpds_py-0.18.0-cp311-none-win_amd64.whl", hash = "sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa"}, - {file = "rpds_py-0.18.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0"}, - {file = "rpds_py-0.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7"}, - {file = "rpds_py-0.18.0-cp312-none-win32.whl", hash = "sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98"}, - {file = "rpds_py-0.18.0-cp312-none-win_amd64.whl", hash = "sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec"}, - {file = "rpds_py-0.18.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e"}, - {file = "rpds_py-0.18.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594"}, - {file = "rpds_py-0.18.0-cp38-none-win32.whl", hash = "sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e"}, - {file = "rpds_py-0.18.0-cp38-none-win_amd64.whl", hash = "sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1"}, - {file = "rpds_py-0.18.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33"}, - {file = "rpds_py-0.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20"}, - {file = "rpds_py-0.18.0-cp39-none-win32.whl", hash = "sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7"}, - {file = "rpds_py-0.18.0-cp39-none-win_amd64.whl", hash = "sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f"}, - {file = "rpds_py-0.18.0.tar.gz", hash = "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d"}, + {file = "rpds_py-0.18.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:d31dea506d718693b6b2cffc0648a8929bdc51c70a311b2770f09611caa10d53"}, + {file = "rpds_py-0.18.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:732672fbc449bab754e0b15356c077cc31566df874964d4801ab14f71951ea80"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a98a1f0552b5f227a3d6422dbd61bc6f30db170939bd87ed14f3c339aa6c7c9"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f1944ce16401aad1e3f7d312247b3d5de7981f634dc9dfe90da72b87d37887d"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38e14fb4e370885c4ecd734f093a2225ee52dc384b86fa55fe3f74638b2cfb09"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08d74b184f9ab6289b87b19fe6a6d1a97fbfea84b8a3e745e87a5de3029bf944"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d70129cef4a8d979caa37e7fe957202e7eee8ea02c5e16455bc9808a59c6b2f0"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce0bb20e3a11bd04461324a6a798af34d503f8d6f1aa3d2aa8901ceaf039176d"}, + {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81c5196a790032e0fc2464c0b4ab95f8610f96f1f2fa3d4deacce6a79852da60"}, + {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f3027be483868c99b4985fda802a57a67fdf30c5d9a50338d9db646d590198da"}, + {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d44607f98caa2961bab4fa3c4309724b185b464cdc3ba6f3d7340bac3ec97cc1"}, + {file = "rpds_py-0.18.1-cp310-none-win32.whl", hash = "sha256:c273e795e7a0f1fddd46e1e3cb8be15634c29ae8ff31c196debb620e1edb9333"}, + {file = "rpds_py-0.18.1-cp310-none-win_amd64.whl", hash = "sha256:8352f48d511de5f973e4f2f9412736d7dea76c69faa6d36bcf885b50c758ab9a"}, + {file = "rpds_py-0.18.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6b5ff7e1d63a8281654b5e2896d7f08799378e594f09cf3674e832ecaf396ce8"}, + {file = "rpds_py-0.18.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8927638a4d4137a289e41d0fd631551e89fa346d6dbcfc31ad627557d03ceb6d"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:154bf5c93d79558b44e5b50cc354aa0459e518e83677791e6adb0b039b7aa6a7"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07f2139741e5deb2c5154a7b9629bc5aa48c766b643c1a6750d16f865a82c5fc"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c7672e9fba7425f79019db9945b16e308ed8bc89348c23d955c8c0540da0a07"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:489bdfe1abd0406eba6b3bb4fdc87c7fa40f1031de073d0cfb744634cc8fa261"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c20f05e8e3d4fc76875fc9cb8cf24b90a63f5a1b4c5b9273f0e8225e169b100"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:967342e045564cef76dfcf1edb700b1e20838d83b1aa02ab313e6a497cf923b8"}, + {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cc7c1a47f3a63282ab0f422d90ddac4aa3034e39fc66a559ab93041e6505da7"}, + {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f7afbfee1157e0f9376c00bb232e80a60e59ed716e3211a80cb8506550671e6e"}, + {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e6934d70dc50f9f8ea47081ceafdec09245fd9f6032669c3b45705dea096b88"}, + {file = "rpds_py-0.18.1-cp311-none-win32.whl", hash = "sha256:c69882964516dc143083d3795cb508e806b09fc3800fd0d4cddc1df6c36e76bb"}, + {file = "rpds_py-0.18.1-cp311-none-win_amd64.whl", hash = "sha256:70a838f7754483bcdc830444952fd89645569e7452e3226de4a613a4c1793fb2"}, + {file = "rpds_py-0.18.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3dd3cd86e1db5aadd334e011eba4e29d37a104b403e8ca24dcd6703c68ca55b3"}, + {file = "rpds_py-0.18.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:05f3d615099bd9b13ecf2fc9cf2d839ad3f20239c678f461c753e93755d629ee"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35b2b771b13eee8729a5049c976197ff58a27a3829c018a04341bcf1ae409b2b"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ee17cd26b97d537af8f33635ef38be873073d516fd425e80559f4585a7b90c43"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b646bf655b135ccf4522ed43d6902af37d3f5dbcf0da66c769a2b3938b9d8184"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19ba472b9606c36716062c023afa2484d1e4220548751bda14f725a7de17b4f6"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e30ac5e329098903262dc5bdd7e2086e0256aa762cc8b744f9e7bf2a427d3f8"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d58ad6317d188c43750cb76e9deacf6051d0f884d87dc6518e0280438648a9ac"}, + {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e1735502458621921cee039c47318cb90b51d532c2766593be6207eec53e5c4c"}, + {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f5bab211605d91db0e2995a17b5c6ee5edec1270e46223e513eaa20da20076ac"}, + {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2fc24a329a717f9e2448f8cd1f960f9dac4e45b6224d60734edeb67499bab03a"}, + {file = "rpds_py-0.18.1-cp312-none-win32.whl", hash = "sha256:1805d5901779662d599d0e2e4159d8a82c0b05faa86ef9222bf974572286b2b6"}, + {file = "rpds_py-0.18.1-cp312-none-win_amd64.whl", hash = "sha256:720edcb916df872d80f80a1cc5ea9058300b97721efda8651efcd938a9c70a72"}, + {file = "rpds_py-0.18.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:c827576e2fa017a081346dce87d532a5310241648eb3700af9a571a6e9fc7e74"}, + {file = "rpds_py-0.18.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:aa3679e751408d75a0b4d8d26d6647b6d9326f5e35c00a7ccd82b78ef64f65f8"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0abeee75434e2ee2d142d650d1e54ac1f8b01e6e6abdde8ffd6eeac6e9c38e20"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed402d6153c5d519a0faf1bb69898e97fb31613b49da27a84a13935ea9164dfc"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:338dee44b0cef8b70fd2ef54b4e09bb1b97fc6c3a58fea5db6cc083fd9fc2724"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7750569d9526199c5b97e5a9f8d96a13300950d910cf04a861d96f4273d5b104"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:607345bd5912aacc0c5a63d45a1f73fef29e697884f7e861094e443187c02be5"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:207c82978115baa1fd8d706d720b4a4d2b0913df1c78c85ba73fe6c5804505f0"}, + {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6d1e42d2735d437e7e80bab4d78eb2e459af48c0a46e686ea35f690b93db792d"}, + {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5463c47c08630007dc0fe99fb480ea4f34a89712410592380425a9b4e1611d8e"}, + {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:06d218939e1bf2ca50e6b0ec700ffe755e5216a8230ab3e87c059ebb4ea06afc"}, + {file = "rpds_py-0.18.1-cp38-none-win32.whl", hash = "sha256:312fe69b4fe1ffbe76520a7676b1e5ac06ddf7826d764cc10265c3b53f96dbe9"}, + {file = "rpds_py-0.18.1-cp38-none-win_amd64.whl", hash = "sha256:9437ca26784120a279f3137ee080b0e717012c42921eb07861b412340f85bae2"}, + {file = "rpds_py-0.18.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:19e515b78c3fc1039dd7da0a33c28c3154458f947f4dc198d3c72db2b6b5dc93"}, + {file = "rpds_py-0.18.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7b28c5b066bca9a4eb4e2f2663012debe680f097979d880657f00e1c30875a0"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:673fdbbf668dd958eff750e500495ef3f611e2ecc209464f661bc82e9838991e"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d960de62227635d2e61068f42a6cb6aae91a7fe00fca0e3aeed17667c8a34611"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:352a88dc7892f1da66b6027af06a2e7e5d53fe05924cc2cfc56495b586a10b72"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e0ee01ad8260184db21468a6e1c37afa0529acc12c3a697ee498d3c2c4dcaf3"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4c39ad2f512b4041343ea3c7894339e4ca7839ac38ca83d68a832fc8b3748ab"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aaa71ee43a703c321906813bb252f69524f02aa05bf4eec85f0c41d5d62d0f4c"}, + {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6cd8098517c64a85e790657e7b1e509b9fe07487fd358e19431cb120f7d96338"}, + {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4adec039b8e2928983f885c53b7cc4cda8965b62b6596501a0308d2703f8af1b"}, + {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:32b7daaa3e9389db3695964ce8e566e3413b0c43e3394c05e4b243a4cd7bef26"}, + {file = "rpds_py-0.18.1-cp39-none-win32.whl", hash = "sha256:2625f03b105328729f9450c8badda34d5243231eef6535f80064d57035738360"}, + {file = "rpds_py-0.18.1-cp39-none-win_amd64.whl", hash = "sha256:bf18932d0003c8c4d51a39f244231986ab23ee057d235a12b2684ea26a353590"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cbfbea39ba64f5e53ae2915de36f130588bba71245b418060ec3330ebf85678e"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a3d456ff2a6a4d2adcdf3c1c960a36f4fd2fec6e3b4902a42a384d17cf4e7a65"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7700936ef9d006b7ef605dc53aa364da2de5a3aa65516a1f3ce73bf82ecfc7ae"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:51584acc5916212e1bf45edd17f3a6b05fe0cbb40482d25e619f824dccb679de"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:942695a206a58d2575033ff1e42b12b2aece98d6003c6bc739fbf33d1773b12f"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b906b5f58892813e5ba5c6056d6a5ad08f358ba49f046d910ad992196ea61397"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6f8e3fecca256fefc91bb6765a693d96692459d7d4c644660a9fff32e517843"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7732770412bab81c5a9f6d20aeb60ae943a9b36dcd990d876a773526468e7163"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:bd1105b50ede37461c1d51b9698c4f4be6e13e69a908ab7751e3807985fc0346"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:618916f5535784960f3ecf8111581f4ad31d347c3de66d02e728de460a46303c"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:17c6d2155e2423f7e79e3bb18151c686d40db42d8645e7977442170c360194d4"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c4c4c3f878df21faf5fac86eda32671c27889e13570645a9eea0a1abdd50922"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:fab6ce90574645a0d6c58890e9bcaac8d94dff54fb51c69e5522a7358b80ab64"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:531796fb842b53f2695e94dc338929e9f9dbf473b64710c28af5a160b2a8927d"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:740884bc62a5e2bbb31e584f5d23b32320fd75d79f916f15a788d527a5e83644"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:998125738de0158f088aef3cb264a34251908dd2e5d9966774fdab7402edfab7"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e2be6e9dd4111d5b31ba3b74d17da54a8319d8168890fbaea4b9e5c3de630ae5"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0cee71bc618cd93716f3c1bf56653740d2d13ddbd47673efa8bf41435a60daa"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c3caec4ec5cd1d18e5dd6ae5194d24ed12785212a90b37f5f7f06b8bedd7139"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:27bba383e8c5231cd559affe169ca0b96ec78d39909ffd817f28b166d7ddd4d8"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:a888e8bdb45916234b99da2d859566f1e8a1d2275a801bb8e4a9644e3c7e7909"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6031b25fb1b06327b43d841f33842b383beba399884f8228a6bb3df3088485ff"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48c2faaa8adfacefcbfdb5f2e2e7bdad081e5ace8d182e5f4ade971f128e6bb3"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:d85164315bd68c0806768dc6bb0429c6f95c354f87485ee3593c4f6b14def2bd"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6afd80f6c79893cfc0574956f78a0add8c76e3696f2d6a15bca2c66c415cf2d4"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa242ac1ff583e4ec7771141606aafc92b361cd90a05c30d93e343a0c2d82a89"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21be4770ff4e08698e1e8e0bce06edb6ea0626e7c8f560bc08222880aca6a6f"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c45a639e93a0c5d4b788b2613bd637468edd62f8f95ebc6fcc303d58ab3f0a8"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910e71711d1055b2768181efa0a17537b2622afeb0424116619817007f8a2b10"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9bb1f182a97880f6078283b3505a707057c42bf55d8fca604f70dedfdc0772a"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d54f74f40b1f7aaa595a02ff42ef38ca654b1469bef7d52867da474243cc633"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:8d2e182c9ee01135e11e9676e9a62dfad791a7a467738f06726872374a83db49"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:636a15acc588f70fda1661234761f9ed9ad79ebed3f2125d44be0862708b666e"}, + {file = "rpds_py-0.18.1.tar.gz", hash = "sha256:dc48b479d540770c811fbd1eb9ba2bb66951863e448efec2e2c102625328e92f"}, ] [[package]] @@ -3317,6 +3354,21 @@ nativelib = ["pyobjc-framework-Cocoa", "pywin32"] objc = ["pyobjc-framework-Cocoa"] win32 = ["pywin32"] +[[package]] +name = "setuptools" +version = "70.0.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] + [[package]] name = "six" version = "1.16.0" @@ -3352,60 +3404,60 @@ files = [ [[package]] name = "sqlalchemy" -version = "2.0.29" +version = "2.0.30" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" files = [ - {file = "SQLAlchemy-2.0.29-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4c142852ae192e9fe5aad5c350ea6befe9db14370b34047e1f0f7cf99e63c63b"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:99a1e69d4e26f71e750e9ad6fdc8614fbddb67cfe2173a3628a2566034e223c7"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ef3fbccb4058355053c51b82fd3501a6e13dd808c8d8cd2561e610c5456013c"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d6753305936eddc8ed190e006b7bb33a8f50b9854823485eed3a886857ab8d1"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0f3ca96af060a5250a8ad5a63699180bc780c2edf8abf96c58af175921df847a"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c4520047006b1d3f0d89e0532978c0688219857eb2fee7c48052560ae76aca1e"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-win32.whl", hash = "sha256:b2a0e3cf0caac2085ff172c3faacd1e00c376e6884b5bc4dd5b6b84623e29e4f"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-win_amd64.whl", hash = "sha256:01d10638a37460616708062a40c7b55f73e4d35eaa146781c683e0fa7f6c43fb"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:308ef9cb41d099099fffc9d35781638986870b29f744382904bf9c7dadd08513"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:296195df68326a48385e7a96e877bc19aa210e485fa381c5246bc0234c36c78e"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a13b917b4ffe5a0a31b83d051d60477819ddf18276852ea68037a144a506efb9"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f6d971255d9ddbd3189e2e79d743ff4845c07f0633adfd1de3f63d930dbe673"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:61405ea2d563407d316c63a7b5271ae5d274a2a9fbcd01b0aa5503635699fa1e"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:de7202ffe4d4a8c1e3cde1c03e01c1a3772c92858837e8f3879b497158e4cb44"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-win32.whl", hash = "sha256:b5d7ed79df55a731749ce65ec20d666d82b185fa4898430b17cb90c892741520"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-win_amd64.whl", hash = "sha256:205f5a2b39d7c380cbc3b5dcc8f2762fb5bcb716838e2d26ccbc54330775b003"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d96710d834a6fb31e21381c6d7b76ec729bd08c75a25a5184b1089141356171f"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:52de4736404e53c5c6a91ef2698c01e52333988ebdc218f14c833237a0804f1b"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c7b02525ede2a164c5fa5014915ba3591730f2cc831f5be9ff3b7fd3e30958e"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dfefdb3e54cd15f5d56fd5ae32f1da2d95d78319c1f6dfb9bcd0eb15d603d5d"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a88913000da9205b13f6f195f0813b6ffd8a0c0c2bd58d499e00a30eb508870c"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fecd5089c4be1bcc37c35e9aa678938d2888845a134dd016de457b942cf5a758"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-win32.whl", hash = "sha256:8197d6f7a3d2b468861ebb4c9f998b9df9e358d6e1cf9c2a01061cb9b6cf4e41"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-win_amd64.whl", hash = "sha256:9b19836ccca0d321e237560e475fd99c3d8655d03da80c845c4da20dda31b6e1"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:87a1d53a5382cdbbf4b7619f107cc862c1b0a4feb29000922db72e5a66a5ffc0"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a0732dffe32333211801b28339d2a0babc1971bc90a983e3035e7b0d6f06b93"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90453597a753322d6aa770c5935887ab1fc49cc4c4fdd436901308383d698b4b"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ea311d4ee9a8fa67f139c088ae9f905fcf0277d6cd75c310a21a88bf85e130f5"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5f20cb0a63a3e0ec4e169aa8890e32b949c8145983afa13a708bc4b0a1f30e03"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-win32.whl", hash = "sha256:e5bbe55e8552019c6463709b39634a5fc55e080d0827e2a3a11e18eb73f5cdbd"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-win_amd64.whl", hash = "sha256:c2f9c762a2735600654c654bf48dad388b888f8ce387b095806480e6e4ff6907"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e614d7a25a43a9f54fcce4675c12761b248547f3d41b195e8010ca7297c369c"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:471fcb39c6adf37f820350c28aac4a7df9d3940c6548b624a642852e727ea586"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:988569c8732f54ad3234cf9c561364221a9e943b78dc7a4aaf35ccc2265f1930"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dddaae9b81c88083e6437de95c41e86823d150f4ee94bf24e158a4526cbead01"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:334184d1ab8f4c87f9652b048af3f7abea1c809dfe526fb0435348a6fef3d380"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:38b624e5cf02a69b113c8047cf7f66b5dfe4a2ca07ff8b8716da4f1b3ae81567"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-win32.whl", hash = "sha256:bab41acf151cd68bc2b466deae5deeb9e8ae9c50ad113444151ad965d5bf685b"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-win_amd64.whl", hash = "sha256:52c8011088305476691b8750c60e03b87910a123cfd9ad48576d6414b6ec2a1d"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3071ad498896907a5ef756206b9dc750f8e57352113c19272bdfdc429c7bd7de"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dba622396a3170974f81bad49aacebd243455ec3cc70615aeaef9e9613b5bca5"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b184e3de58009cc0bf32e20f137f1ec75a32470f5fede06c58f6c355ed42a72"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c37f1050feb91f3d6c32f864d8e114ff5545a4a7afe56778d76a9aec62638ba"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bda7ce59b06d0f09afe22c56714c65c957b1068dee3d5e74d743edec7daba552"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:25664e18bef6dc45015b08f99c63952a53a0a61f61f2e48a9e70cec27e55f699"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-win32.whl", hash = "sha256:77d29cb6c34b14af8a484e831ab530c0f7188f8efed1c6a833a2c674bf3c26ec"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-win_amd64.whl", hash = "sha256:04c487305ab035a9548f573763915189fc0fe0824d9ba28433196f8436f1449c"}, - {file = "SQLAlchemy-2.0.29-py3-none-any.whl", hash = "sha256:dc4ee2d4ee43251905f88637d5281a8d52e916a021384ec10758826f5cbae305"}, - {file = "SQLAlchemy-2.0.29.tar.gz", hash = "sha256:bd9566b8e58cabd700bc367b60e90d9349cd16f0984973f98a9a09f9c64e86f0"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3b48154678e76445c7ded1896715ce05319f74b1e73cf82d4f8b59b46e9c0ddc"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2753743c2afd061bb95a61a51bbb6a1a11ac1c44292fad898f10c9839a7f75b2"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7bfc726d167f425d4c16269a9a10fe8630ff6d14b683d588044dcef2d0f6be7"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4f61ada6979223013d9ab83a3ed003ded6959eae37d0d685db2c147e9143797"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a365eda439b7a00732638f11072907c1bc8e351c7665e7e5da91b169af794af"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bba002a9447b291548e8d66fd8c96a6a7ed4f2def0bb155f4f0a1309fd2735d5"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-win32.whl", hash = "sha256:0138c5c16be3600923fa2169532205d18891b28afa817cb49b50e08f62198bb8"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-win_amd64.whl", hash = "sha256:99650e9f4cf3ad0d409fed3eec4f071fadd032e9a5edc7270cd646a26446feeb"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:955991a09f0992c68a499791a753523f50f71a6885531568404fa0f231832aa0"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f69e4c756ee2686767eb80f94c0125c8b0a0b87ede03eacc5c8ae3b54b99dc46"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69c9db1ce00e59e8dd09d7bae852a9add716efdc070a3e2068377e6ff0d6fdaa"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1429a4b0f709f19ff3b0cf13675b2b9bfa8a7e79990003207a011c0db880a13"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:efedba7e13aa9a6c8407c48facfdfa108a5a4128e35f4c68f20c3407e4376aa9"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:16863e2b132b761891d6c49f0a0f70030e0bcac4fd208117f6b7e053e68668d0"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-win32.whl", hash = "sha256:2ecabd9ccaa6e914e3dbb2aa46b76dede7eadc8cbf1b8083c94d936bcd5ffb49"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-win_amd64.whl", hash = "sha256:0b3f4c438e37d22b83e640f825ef0f37b95db9aa2d68203f2c9549375d0b2260"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5a79d65395ac5e6b0c2890935bad892eabb911c4aa8e8015067ddb37eea3d56c"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a5baf9267b752390252889f0c802ea13b52dfee5e369527da229189b8bd592e"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cb5a646930c5123f8461f6468901573f334c2c63c795b9af350063a736d0134"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:296230899df0b77dec4eb799bcea6fbe39a43707ce7bb166519c97b583cfcab3"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c62d401223f468eb4da32627bffc0c78ed516b03bb8a34a58be54d618b74d472"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3b69e934f0f2b677ec111b4d83f92dc1a3210a779f69bf905273192cf4ed433e"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-win32.whl", hash = "sha256:77d2edb1f54aff37e3318f611637171e8ec71472f1fdc7348b41dcb226f93d90"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-win_amd64.whl", hash = "sha256:b6c7ec2b1f4969fc19b65b7059ed00497e25f54069407a8701091beb69e591a5"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a8e3b0a7e09e94be7510d1661339d6b52daf202ed2f5b1f9f48ea34ee6f2d57"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b60203c63e8f984df92035610c5fb76d941254cf5d19751faab7d33b21e5ddc0"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1dc3eabd8c0232ee8387fbe03e0a62220a6f089e278b1f0aaf5e2d6210741ad"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:40ad017c672c00b9b663fcfcd5f0864a0a97828e2ee7ab0c140dc84058d194cf"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e42203d8d20dc704604862977b1470a122e4892791fe3ed165f041e4bf447a1b"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-win32.whl", hash = "sha256:2a4f4da89c74435f2bc61878cd08f3646b699e7d2eba97144030d1be44e27584"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-win_amd64.whl", hash = "sha256:b6bf767d14b77f6a18b6982cbbf29d71bede087edae495d11ab358280f304d8e"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc0c53579650a891f9b83fa3cecd4e00218e071d0ba00c4890f5be0c34887ed3"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:311710f9a2ee235f1403537b10c7687214bb1f2b9ebb52702c5aa4a77f0b3af7"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:408f8b0e2c04677e9c93f40eef3ab22f550fecb3011b187f66a096395ff3d9fd"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37a4b4fb0dd4d2669070fb05b8b8824afd0af57587393015baee1cf9890242d9"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a943d297126c9230719c27fcbbeab57ecd5d15b0bd6bfd26e91bfcfe64220621"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0a089e218654e740a41388893e090d2e2c22c29028c9d1353feb38638820bbeb"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-win32.whl", hash = "sha256:fa561138a64f949f3e889eb9ab8c58e1504ab351d6cf55259dc4c248eaa19da6"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-win_amd64.whl", hash = "sha256:7d74336c65705b986d12a7e337ba27ab2b9d819993851b140efdf029248e818e"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae8c62fe2480dd61c532ccafdbce9b29dacc126fe8be0d9a927ca3e699b9491a"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2383146973a15435e4717f94c7509982770e3e54974c71f76500a0136f22810b"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8409de825f2c3b62ab15788635ccaec0c881c3f12a8af2b12ae4910a0a9aeef6"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0094c5dc698a5f78d3d1539853e8ecec02516b62b8223c970c86d44e7a80f6c7"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:edc16a50f5e1b7a06a2dcc1f2205b0b961074c123ed17ebda726f376a5ab0953"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f7703c2010355dd28f53deb644a05fc30f796bd8598b43f0ba678878780b6e4c"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-win32.whl", hash = "sha256:1f9a727312ff6ad5248a4367358e2cf7e625e98b1028b1d7ab7b806b7d757513"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-win_amd64.whl", hash = "sha256:a0ef36b28534f2a5771191be6edb44cc2673c7b2edf6deac6562400288664221"}, + {file = "SQLAlchemy-2.0.30-py3-none-any.whl", hash = "sha256:7108d569d3990c71e26a42f60474b4c02c8586c4681af5fd67e51a044fdea86a"}, + {file = "SQLAlchemy-2.0.30.tar.gz", hash = "sha256:2b1708916730f4830bc69d6f49d37f7698b5bd7530aca7f04f785f8849e95255"}, ] [package.dependencies] @@ -3472,17 +3524,18 @@ pytest = ">=7.0.0,<9.0.0" [[package]] name = "tenacity" -version = "8.2.3" +version = "8.3.0" description = "Retry code until it succeeds" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"}, - {file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"}, + {file = "tenacity-8.3.0-py3-none-any.whl", hash = "sha256:3649f6443dbc0d9b01b9d8020a9c4ec7a1ff5f6f3c6c8a036ef371f573fe9185"}, + {file = "tenacity-8.3.0.tar.gz", hash = "sha256:953d4e6ad24357bceffbc9707bc74349aca9d245f68eb65419cf0c249a1949a2"}, ] [package.extras] -doc = ["reno", "sphinx", "tornado (>=4.5)"] +doc = ["reno", "sphinx"] +test = ["pytest", "tornado (>=4.5)", "typeguard"] [[package]] name = "terminado" @@ -3725,13 +3778,13 @@ files = [ [[package]] name = "tqdm" -version = "4.66.3" +version = "4.66.4" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.3-py3-none-any.whl", hash = "sha256:4f41d54107ff9a223dca80b53efe4fb654c67efaba7f47bada3ee9d50e05bd53"}, - {file = "tqdm-4.66.3.tar.gz", hash = "sha256:23097a41eba115ba99ecae40d06444c15d1c0c698d527a01c6c8bd1c5d0647e5"}, + {file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"}, + {file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"}, ] [package.dependencies] @@ -3771,13 +3824,13 @@ files = [ [[package]] name = "types-requests" -version = "2.31.0.20240406" +version = "2.32.0.20240602" description = "Typing stubs for requests" optional = false python-versions = ">=3.8" files = [ - {file = "types-requests-2.31.0.20240406.tar.gz", hash = "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1"}, - {file = "types_requests-2.31.0.20240406-py3-none-any.whl", hash = "sha256:6216cdac377c6b9a040ac1c0404f7284bd13199c0e1bb235f4324627e8898cf5"}, + {file = "types-requests-2.32.0.20240602.tar.gz", hash = "sha256:3f98d7bbd0dd94ebd10ff43a7fbe20c3b8528acace6d8efafef0b6a184793f06"}, + {file = "types_requests-2.32.0.20240602-py3-none-any.whl", hash = "sha256:ed3946063ea9fbc6b5fc0c44fa279188bae42d582cb63760be6cb4b9d06c3de8"}, ] [package.dependencies] @@ -3785,13 +3838,13 @@ urllib3 = ">=2" [[package]] name = "typing-extensions" -version = "4.11.0" +version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, - {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] @@ -3825,13 +3878,13 @@ dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake [[package]] name = "urllib3" -version = "2.2.1" +version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] @@ -3842,40 +3895,43 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "watchdog" -version = "4.0.0" +version = "4.0.1" description = "Filesystem events monitoring" optional = false python-versions = ">=3.8" files = [ - {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:39cb34b1f1afbf23e9562501673e7146777efe95da24fab5707b88f7fb11649b"}, - {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c522392acc5e962bcac3b22b9592493ffd06d1fc5d755954e6be9f4990de932b"}, - {file = "watchdog-4.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6c47bdd680009b11c9ac382163e05ca43baf4127954c5f6d0250e7d772d2b80c"}, - {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8350d4055505412a426b6ad8c521bc7d367d1637a762c70fdd93a3a0d595990b"}, - {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c17d98799f32e3f55f181f19dd2021d762eb38fdd381b4a748b9f5a36738e935"}, - {file = "watchdog-4.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4986db5e8880b0e6b7cd52ba36255d4793bf5cdc95bd6264806c233173b1ec0b"}, - {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:11e12fafb13372e18ca1bbf12d50f593e7280646687463dd47730fd4f4d5d257"}, - {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5369136a6474678e02426bd984466343924d1df8e2fd94a9b443cb7e3aa20d19"}, - {file = "watchdog-4.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76ad8484379695f3fe46228962017a7e1337e9acadafed67eb20aabb175df98b"}, - {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:45cc09cc4c3b43fb10b59ef4d07318d9a3ecdbff03abd2e36e77b6dd9f9a5c85"}, - {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eed82cdf79cd7f0232e2fdc1ad05b06a5e102a43e331f7d041e5f0e0a34a51c4"}, - {file = "watchdog-4.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba30a896166f0fee83183cec913298151b73164160d965af2e93a20bbd2ab605"}, - {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d18d7f18a47de6863cd480734613502904611730f8def45fc52a5d97503e5101"}, - {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2895bf0518361a9728773083908801a376743bcc37dfa252b801af8fd281b1ca"}, - {file = "watchdog-4.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87e9df830022488e235dd601478c15ad73a0389628588ba0b028cb74eb72fed8"}, - {file = "watchdog-4.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6e949a8a94186bced05b6508faa61b7adacc911115664ccb1923b9ad1f1ccf7b"}, - {file = "watchdog-4.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6a4db54edea37d1058b08947c789a2354ee02972ed5d1e0dca9b0b820f4c7f92"}, - {file = "watchdog-4.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d31481ccf4694a8416b681544c23bd271f5a123162ab603c7d7d2dd7dd901a07"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8fec441f5adcf81dd240a5fe78e3d83767999771630b5ddfc5867827a34fa3d3"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:6a9c71a0b02985b4b0b6d14b875a6c86ddea2fdbebd0c9a720a806a8bbffc69f"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:557ba04c816d23ce98a06e70af6abaa0485f6d94994ec78a42b05d1c03dcbd50"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0f9bd1fd919134d459d8abf954f63886745f4660ef66480b9d753a7c9d40927"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:f9b2fdca47dc855516b2d66eef3c39f2672cbf7e7a42e7e67ad2cbfcd6ba107d"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:73c7a935e62033bd5e8f0da33a4dcb763da2361921a69a5a95aaf6c93aa03a87"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6a80d5cae8c265842c7419c560b9961561556c4361b297b4c431903f8c33b269"}, - {file = "watchdog-4.0.0-py3-none-win32.whl", hash = "sha256:8f9a542c979df62098ae9c58b19e03ad3df1c9d8c6895d96c0d51da17b243b1c"}, - {file = "watchdog-4.0.0-py3-none-win_amd64.whl", hash = "sha256:f970663fa4f7e80401a7b0cbeec00fa801bf0287d93d48368fc3e6fa32716245"}, - {file = "watchdog-4.0.0-py3-none-win_ia64.whl", hash = "sha256:9a03e16e55465177d416699331b0f3564138f1807ecc5f2de9d55d8f188d08c7"}, - {file = "watchdog-4.0.0.tar.gz", hash = "sha256:e3e7065cbdabe6183ab82199d7a4f6b3ba0a438c5a512a68559846ccb76a78ec"}, + {file = "watchdog-4.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:da2dfdaa8006eb6a71051795856bedd97e5b03e57da96f98e375682c48850645"}, + {file = "watchdog-4.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e93f451f2dfa433d97765ca2634628b789b49ba8b504fdde5837cdcf25fdb53b"}, + {file = "watchdog-4.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ef0107bbb6a55f5be727cfc2ef945d5676b97bffb8425650dadbb184be9f9a2b"}, + {file = "watchdog-4.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:17e32f147d8bf9657e0922c0940bcde863b894cd871dbb694beb6704cfbd2fb5"}, + {file = "watchdog-4.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:03e70d2df2258fb6cb0e95bbdbe06c16e608af94a3ffbd2b90c3f1e83eb10767"}, + {file = "watchdog-4.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:123587af84260c991dc5f62a6e7ef3d1c57dfddc99faacee508c71d287248459"}, + {file = "watchdog-4.0.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:093b23e6906a8b97051191a4a0c73a77ecc958121d42346274c6af6520dec175"}, + {file = "watchdog-4.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:611be3904f9843f0529c35a3ff3fd617449463cb4b73b1633950b3d97fa4bfb7"}, + {file = "watchdog-4.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:62c613ad689ddcb11707f030e722fa929f322ef7e4f18f5335d2b73c61a85c28"}, + {file = "watchdog-4.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d4925e4bf7b9bddd1c3de13c9b8a2cdb89a468f640e66fbfabaf735bd85b3e35"}, + {file = "watchdog-4.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cad0bbd66cd59fc474b4a4376bc5ac3fc698723510cbb64091c2a793b18654db"}, + {file = "watchdog-4.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a3c2c317a8fb53e5b3d25790553796105501a235343f5d2bf23bb8649c2c8709"}, + {file = "watchdog-4.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c9904904b6564d4ee8a1ed820db76185a3c96e05560c776c79a6ce5ab71888ba"}, + {file = "watchdog-4.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:667f3c579e813fcbad1b784db7a1aaa96524bed53437e119f6a2f5de4db04235"}, + {file = "watchdog-4.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d10a681c9a1d5a77e75c48a3b8e1a9f2ae2928eda463e8d33660437705659682"}, + {file = "watchdog-4.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0144c0ea9997b92615af1d94afc0c217e07ce2c14912c7b1a5731776329fcfc7"}, + {file = "watchdog-4.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:998d2be6976a0ee3a81fb8e2777900c28641fb5bfbd0c84717d89bca0addcdc5"}, + {file = "watchdog-4.0.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e7921319fe4430b11278d924ef66d4daa469fafb1da679a2e48c935fa27af193"}, + {file = "watchdog-4.0.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:f0de0f284248ab40188f23380b03b59126d1479cd59940f2a34f8852db710625"}, + {file = "watchdog-4.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bca36be5707e81b9e6ce3208d92d95540d4ca244c006b61511753583c81c70dd"}, + {file = "watchdog-4.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ab998f567ebdf6b1da7dc1e5accfaa7c6992244629c0fdaef062f43249bd8dee"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:dddba7ca1c807045323b6af4ff80f5ddc4d654c8bce8317dde1bd96b128ed253"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_armv7l.whl", hash = "sha256:4513ec234c68b14d4161440e07f995f231be21a09329051e67a2118a7a612d2d"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_i686.whl", hash = "sha256:4107ac5ab936a63952dea2a46a734a23230aa2f6f9db1291bf171dac3ebd53c6"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_ppc64.whl", hash = "sha256:6e8c70d2cd745daec2a08734d9f63092b793ad97612470a0ee4cbb8f5f705c57"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:f27279d060e2ab24c0aa98363ff906d2386aa6c4dc2f1a374655d4e02a6c5e5e"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_s390x.whl", hash = "sha256:f8affdf3c0f0466e69f5b3917cdd042f89c8c63aebdb9f7c078996f607cdb0f5"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ac7041b385f04c047fcc2951dc001671dee1b7e0615cde772e84b01fbf68ee84"}, + {file = "watchdog-4.0.1-py3-none-win32.whl", hash = "sha256:206afc3d964f9a233e6ad34618ec60b9837d0582b500b63687e34011e15bb429"}, + {file = "watchdog-4.0.1-py3-none-win_amd64.whl", hash = "sha256:7577b3c43e5909623149f76b099ac49a1a01ca4e167d1785c76eb52fa585745a"}, + {file = "watchdog-4.0.1-py3-none-win_ia64.whl", hash = "sha256:d7b9f5f3299e8dd230880b6c55504a1f69cf1e4316275d1b215ebdd8187ec88d"}, + {file = "watchdog-4.0.1.tar.gz", hash = "sha256:eebaacf674fa25511e8867028d281e602ee6500045b57f43b08778082f7f8b44"}, ] [package.extras] @@ -3894,18 +3950,18 @@ files = [ [[package]] name = "webcolors" -version = "1.13" +version = "24.6.0" description = "A library for working with the color formats defined by HTML and CSS." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "webcolors-1.13-py3-none-any.whl", hash = "sha256:29bc7e8752c0a1bd4a1f03c14d6e6a72e93d82193738fa860cbff59d0fcc11bf"}, - {file = "webcolors-1.13.tar.gz", hash = "sha256:c225b674c83fa923be93d235330ce0300373d02885cef23238813b0d5668304a"}, + {file = "webcolors-24.6.0-py3-none-any.whl", hash = "sha256:8cf5bc7e28defd1d48b9e83d5fc30741328305a8195c29a8e668fa45586568a1"}, + {file = "webcolors-24.6.0.tar.gz", hash = "sha256:1d160d1de46b3e81e58d0a280d0c78b467dc80f47294b91b1ad8029d2cedb55b"}, ] [package.extras] docs = ["furo", "sphinx", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-notfound-page", "sphinxext-opengraph"] -tests = ["pytest", "pytest-cov"] +tests = ["coverage[toml]"] [[package]] name = "webencodings" @@ -3936,13 +3992,13 @@ test = ["websockets"] [[package]] name = "widgetsnbextension" -version = "4.0.10" +version = "4.0.11" description = "Jupyter interactive widgets for Jupyter Notebook" optional = false python-versions = ">=3.7" files = [ - {file = "widgetsnbextension-4.0.10-py3-none-any.whl", hash = "sha256:d37c3724ec32d8c48400a435ecfa7d3e259995201fbefa37163124a9fcb393cc"}, - {file = "widgetsnbextension-4.0.10.tar.gz", hash = "sha256:64196c5ff3b9a9183a8e699a4227fb0b7002f252c814098e66c4d1cd0644688f"}, + {file = "widgetsnbextension-4.0.11-py3-none-any.whl", hash = "sha256:55d4d6949d100e0d08b94948a42efc3ed6dfdc0e9468b2c4b128c9a2ce3a7a36"}, + {file = "widgetsnbextension-4.0.11.tar.gz", hash = "sha256:8b22a8f1910bfd188e596fe7fc05dcbd87e810c8a4ba010bdb3da86637398474"}, ] [[package]] @@ -4050,20 +4106,20 @@ multidict = ">=4.0" [[package]] name = "zipp" -version = "3.18.1" +version = "3.19.2" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, - {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, + {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, + {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [metadata] lock-version = "2.0" python-versions = ">=3.9.0,<4.0" -content-hash = "47f653979a2e0bcb14264ceedef65670ff2ddce7e70c6de46043194aa4688c1a" +content-hash = "169aa0ae0415b6b959bd86040113f5f3b0f4f299eee059e46c0fcb33ba5ff0f4" diff --git a/pyproject.toml b/pyproject.toml index 3b27e36f2..7ff4ceb0d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langgraph" -version = "0.0.66" +version = "0.0.69" description = "langgraph" authors = [] license = "MIT" @@ -30,13 +30,25 @@ jupyter = "^1.0.0" langchainhub = "^0.1.14" langchain-openai = ">=0.1.2" langchain-anthropic = ">=0.1.8" +dataclasses-json = "^0.6.7" [tool.poetry.group.test] optional = true [tool.ruff] -select = [ "E", "F", "I" ] -ignore = [ "E501" ] +lint.select = [ "E", "F", "I" ] +lint.ignore = [ "E501" ] +line-length = 88 +indent-width = 4 +extend-include = ["*.ipynb"] + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +skip-magic-trailing-comma = false +line-ending = "auto" +docstring-code-format = false +docstring-code-line-length = "dynamic" [tool.mypy] ignore_missing_imports = "True" diff --git a/tests/__snapshots__/test_pregel.ambr b/tests/__snapshots__/test_pregel.ambr index 26381cdbf..3e972872f 100644 --- a/tests/__snapshots__/test_pregel.ambr +++ b/tests/__snapshots__/test_pregel.ambr @@ -606,10 +606,10 @@ ''' # --- # name: test_message_graph - '{"title": "LangGraphInput", "type": "array", "items": {"anyOf": [{"$ref": "#/definitions/AIMessage"}, {"$ref": "#/definitions/HumanMessage"}, {"$ref": "#/definitions/ChatMessage"}, {"$ref": "#/definitions/SystemMessage"}, {"$ref": "#/definitions/FunctionMessage"}, {"$ref": "#/definitions/ToolMessage"}]}, "definitions": {"ToolCall": {"title": "ToolCall", "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"title": "Args", "type": "object"}, "id": {"title": "Id", "type": "string"}}, "required": ["name", "args", "id"]}, "InvalidToolCall": {"title": "InvalidToolCall", "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"title": "Args", "type": "string"}, "id": {"title": "Id", "type": "string"}, "error": {"title": "Error", "type": "string"}}, "required": ["name", "args", "id", "error"]}, "AIMessage": {"title": "AIMessage", "description": "Message from an AI.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "ai", "enum": ["ai"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}, "tool_calls": {"title": "Tool Calls", "default": [], "type": "array", "items": {"$ref": "#/definitions/ToolCall"}}, "invalid_tool_calls": {"title": "Invalid Tool Calls", "default": [], "type": "array", "items": {"$ref": "#/definitions/InvalidToolCall"}}}, "required": ["content"]}, "HumanMessage": {"title": "HumanMessage", "description": "Message from a human.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "human", "enum": ["human"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "ChatMessage": {"title": "ChatMessage", "description": "Message that can be assigned an arbitrary speaker (i.e. role).", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "chat", "enum": ["chat"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"]}, "SystemMessage": {"title": "SystemMessage", "description": "Message for priming AI behavior, usually passed in as the first of a sequence\\nof input messages.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "system", "enum": ["system"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content"]}, "FunctionMessage": {"title": "FunctionMessage", "description": "Message for passing the result of executing a function back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "function", "enum": ["function"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content", "name"]}, "ToolMessage": {"title": "ToolMessage", "description": "Message for passing the result of executing a tool back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "tool", "enum": ["tool"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}}, "required": ["content", "tool_call_id"]}}}' + '{"title": "LangGraphInput", "type": "array", "items": {"anyOf": [{"$ref": "#/definitions/AIMessage"}, {"$ref": "#/definitions/HumanMessage"}, {"$ref": "#/definitions/ChatMessage"}, {"$ref": "#/definitions/SystemMessage"}, {"$ref": "#/definitions/FunctionMessage"}, {"$ref": "#/definitions/ToolMessage"}]}, "definitions": {"ToolCall": {"title": "ToolCall", "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"title": "Args", "type": "object"}, "id": {"title": "Id", "type": "string"}}, "required": ["name", "args", "id"]}, "InvalidToolCall": {"title": "InvalidToolCall", "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"title": "Args", "type": "string"}, "id": {"title": "Id", "type": "string"}, "error": {"title": "Error", "type": "string"}}, "required": ["name", "args", "id", "error"]}, "UsageMetadata": {"title": "UsageMetadata", "type": "object", "properties": {"input_tokens": {"title": "Input Tokens", "type": "integer"}, "output_tokens": {"title": "Output Tokens", "type": "integer"}, "total_tokens": {"title": "Total Tokens", "type": "integer"}}, "required": ["input_tokens", "output_tokens", "total_tokens"]}, "AIMessage": {"title": "AIMessage", "description": "Message from an AI.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "ai", "enum": ["ai"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}, "tool_calls": {"title": "Tool Calls", "default": [], "type": "array", "items": {"$ref": "#/definitions/ToolCall"}}, "invalid_tool_calls": {"title": "Invalid Tool Calls", "default": [], "type": "array", "items": {"$ref": "#/definitions/InvalidToolCall"}}, "usage_metadata": {"$ref": "#/definitions/UsageMetadata"}}, "required": ["content"]}, "HumanMessage": {"title": "HumanMessage", "description": "Message from a human.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "human", "enum": ["human"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "ChatMessage": {"title": "ChatMessage", "description": "Message that can be assigned an arbitrary speaker (i.e. role).", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "chat", "enum": ["chat"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"]}, "SystemMessage": {"title": "SystemMessage", "description": "Message for priming AI behavior, usually passed in as the first of a sequence\\nof input messages.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "system", "enum": ["system"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content"]}, "FunctionMessage": {"title": "FunctionMessage", "description": "Message for passing the result of executing a function back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "function", "enum": ["function"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content", "name"]}, "ToolMessage": {"title": "ToolMessage", "description": "Message for passing the result of executing a tool back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "tool", "enum": ["tool"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}}, "required": ["content", "tool_call_id"]}}}' # --- # name: test_message_graph.1 - '{"title": "LangGraphOutput", "type": "array", "items": {"anyOf": [{"$ref": "#/definitions/AIMessage"}, {"$ref": "#/definitions/HumanMessage"}, {"$ref": "#/definitions/ChatMessage"}, {"$ref": "#/definitions/SystemMessage"}, {"$ref": "#/definitions/FunctionMessage"}, {"$ref": "#/definitions/ToolMessage"}]}, "definitions": {"ToolCall": {"title": "ToolCall", "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"title": "Args", "type": "object"}, "id": {"title": "Id", "type": "string"}}, "required": ["name", "args", "id"]}, "InvalidToolCall": {"title": "InvalidToolCall", "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"title": "Args", "type": "string"}, "id": {"title": "Id", "type": "string"}, "error": {"title": "Error", "type": "string"}}, "required": ["name", "args", "id", "error"]}, "AIMessage": {"title": "AIMessage", "description": "Message from an AI.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "ai", "enum": ["ai"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}, "tool_calls": {"title": "Tool Calls", "default": [], "type": "array", "items": {"$ref": "#/definitions/ToolCall"}}, "invalid_tool_calls": {"title": "Invalid Tool Calls", "default": [], "type": "array", "items": {"$ref": "#/definitions/InvalidToolCall"}}}, "required": ["content"]}, "HumanMessage": {"title": "HumanMessage", "description": "Message from a human.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "human", "enum": ["human"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "ChatMessage": {"title": "ChatMessage", "description": "Message that can be assigned an arbitrary speaker (i.e. role).", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "chat", "enum": ["chat"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"]}, "SystemMessage": {"title": "SystemMessage", "description": "Message for priming AI behavior, usually passed in as the first of a sequence\\nof input messages.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "system", "enum": ["system"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content"]}, "FunctionMessage": {"title": "FunctionMessage", "description": "Message for passing the result of executing a function back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "function", "enum": ["function"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content", "name"]}, "ToolMessage": {"title": "ToolMessage", "description": "Message for passing the result of executing a tool back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "tool", "enum": ["tool"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}}, "required": ["content", "tool_call_id"]}}}' + '{"title": "LangGraphOutput", "type": "array", "items": {"anyOf": [{"$ref": "#/definitions/AIMessage"}, {"$ref": "#/definitions/HumanMessage"}, {"$ref": "#/definitions/ChatMessage"}, {"$ref": "#/definitions/SystemMessage"}, {"$ref": "#/definitions/FunctionMessage"}, {"$ref": "#/definitions/ToolMessage"}]}, "definitions": {"ToolCall": {"title": "ToolCall", "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"title": "Args", "type": "object"}, "id": {"title": "Id", "type": "string"}}, "required": ["name", "args", "id"]}, "InvalidToolCall": {"title": "InvalidToolCall", "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"title": "Args", "type": "string"}, "id": {"title": "Id", "type": "string"}, "error": {"title": "Error", "type": "string"}}, "required": ["name", "args", "id", "error"]}, "UsageMetadata": {"title": "UsageMetadata", "type": "object", "properties": {"input_tokens": {"title": "Input Tokens", "type": "integer"}, "output_tokens": {"title": "Output Tokens", "type": "integer"}, "total_tokens": {"title": "Total Tokens", "type": "integer"}}, "required": ["input_tokens", "output_tokens", "total_tokens"]}, "AIMessage": {"title": "AIMessage", "description": "Message from an AI.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "ai", "enum": ["ai"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}, "tool_calls": {"title": "Tool Calls", "default": [], "type": "array", "items": {"$ref": "#/definitions/ToolCall"}}, "invalid_tool_calls": {"title": "Invalid Tool Calls", "default": [], "type": "array", "items": {"$ref": "#/definitions/InvalidToolCall"}}, "usage_metadata": {"$ref": "#/definitions/UsageMetadata"}}, "required": ["content"]}, "HumanMessage": {"title": "HumanMessage", "description": "Message from a human.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "human", "enum": ["human"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "ChatMessage": {"title": "ChatMessage", "description": "Message that can be assigned an arbitrary speaker (i.e. role).", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "chat", "enum": ["chat"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"]}, "SystemMessage": {"title": "SystemMessage", "description": "Message for priming AI behavior, usually passed in as the first of a sequence\\nof input messages.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "system", "enum": ["system"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content"]}, "FunctionMessage": {"title": "FunctionMessage", "description": "Message for passing the result of executing a function back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "function", "enum": ["function"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content", "name"]}, "ToolMessage": {"title": "ToolMessage", "description": "Message for passing the result of executing a tool back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "tool", "enum": ["tool"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}}, "required": ["content", "tool_call_id"]}}}' # --- # name: test_message_graph.2 ''' diff --git a/tests/checkpoint/test_aiosqlite.py b/tests/checkpoint/test_aiosqlite.py index 99e24b722..abcf6cb71 100644 --- a/tests/checkpoint/test_aiosqlite.py +++ b/tests/checkpoint/test_aiosqlite.py @@ -1,7 +1,7 @@ import pytest from langchain_core.runnables import RunnableConfig -from langgraph.channels.base import create_checkpoint +from langgraph.channels.manager import create_checkpoint from langgraph.checkpoint.aiosqlite import AsyncSqliteSaver from langgraph.checkpoint.base import Checkpoint, CheckpointMetadata, empty_checkpoint diff --git a/tests/checkpoint/test_memory.py b/tests/checkpoint/test_memory.py index e41e53528..bac957180 100644 --- a/tests/checkpoint/test_memory.py +++ b/tests/checkpoint/test_memory.py @@ -1,7 +1,7 @@ import pytest from langchain_core.runnables import RunnableConfig -from langgraph.channels.base import create_checkpoint +from langgraph.channels.manager import create_checkpoint from langgraph.checkpoint.base import Checkpoint, CheckpointMetadata, empty_checkpoint from langgraph.checkpoint.memory import MemorySaver diff --git a/tests/checkpoint/test_sqlite.py b/tests/checkpoint/test_sqlite.py index 6dd7dcbae..cd2aa8e1e 100644 --- a/tests/checkpoint/test_sqlite.py +++ b/tests/checkpoint/test_sqlite.py @@ -1,7 +1,7 @@ import pytest from langchain_core.runnables import RunnableConfig -from langgraph.channels.base import create_checkpoint +from langgraph.channels.manager import create_checkpoint from langgraph.checkpoint.base import Checkpoint, CheckpointMetadata, empty_checkpoint from langgraph.checkpoint.sqlite import ( _AIO_ERROR_MSG, diff --git a/tests/test_channels.py b/tests/test_channels.py index 313b4373a..bfa48f55d 100644 --- a/tests/test_channels.py +++ b/tests/test_channels.py @@ -56,13 +56,15 @@ def test_topic() -> None: assert channel.ValueType is Sequence[str] assert channel.UpdateType is Union[str, list[str]] - channel.update(["a", "b"]) + assert channel.update(["a", "b"]) assert channel.get() == ["a", "b"] - channel.update([["c", "d"], "d"]) + assert channel.update([["c", "d"], "d"]) assert channel.get() == ["c", "d", "d"] - channel.update([]) - assert channel.get() == [] - channel.update(["e"]) + assert channel.update([]) + with pytest.raises(EmptyChannelError): + channel.get() + assert not channel.update([]), "channel already empty" + assert channel.update(["e"]) assert channel.get() == ["e"] checkpoint = channel.checkpoint() with Topic(str).from_checkpoint(checkpoint) as channel: @@ -78,13 +80,15 @@ async def test_topic_async() -> None: assert channel.ValueType is Sequence[str] assert channel.UpdateType is Union[str, list[str]] - channel.update(["a", "b"]) + assert channel.update(["a", "b"]) assert channel.get() == ["a", "b"] - channel.update(["b", ["c", "d"], "d"]) + assert channel.update(["b", ["c", "d"], "d"]) assert channel.get() == ["b", "c", "d", "d"] - channel.update([]) - assert channel.get() == [] - channel.update(["e"]) + assert channel.update([]) + with pytest.raises(EmptyChannelError): + channel.get() + assert not channel.update([]), "channel already empty" + assert channel.update(["e"]) assert channel.get() == ["e"] checkpoint = channel.checkpoint() async with Topic(str).afrom_checkpoint(checkpoint) as channel: @@ -96,18 +100,20 @@ def test_topic_unique() -> None: assert channel.ValueType is Sequence[str] assert channel.UpdateType is Union[str, list[str]] - channel.update(["a", "b"]) + assert channel.update(["a", "b"]) assert channel.get() == ["a", "b"] - channel.update(["b", ["c", "d"], "d"]) + assert channel.update(["b", ["c", "d"], "d"]) assert channel.get() == ["c", "d"], "de-dupes from current and previous steps" - channel.update([]) - assert channel.get() == [] - channel.update(["e"]) + assert channel.update([]) + with pytest.raises(EmptyChannelError): + channel.get() + assert not channel.update([]), "channel already empty" + assert channel.update(["e"]) assert channel.get() == ["e"] checkpoint = channel.checkpoint() with Topic(str, unique=True).from_checkpoint(checkpoint) as channel: assert channel.get() == ["e"] - channel.update(["d", "f"]) + assert channel.update(["d", "f"]) assert channel.get() == ["f"], "de-dupes from checkpoint" @@ -116,18 +122,20 @@ async def test_topic_unique_async() -> None: assert channel.ValueType is Sequence[str] assert channel.UpdateType is Union[str, list[str]] - channel.update(["a", "b"]) + assert channel.update(["a", "b"]) assert channel.get() == ["a", "b"] - channel.update(["b", ["c", "d"], "d"]) + assert channel.update(["b", ["c", "d"], "d"]) assert channel.get() == ["c", "d"], "de-dupes from current and previous steps" - channel.update([]) - assert channel.get() == [] - channel.update(["e"]) + assert channel.update([]) + with pytest.raises(EmptyChannelError): + channel.get() + assert not channel.update([]), "channel already empty" + assert channel.update(["e"]) assert channel.get() == ["e"] checkpoint = channel.checkpoint() async with Topic(str, unique=True).afrom_checkpoint(checkpoint) as channel: assert channel.get() == ["e"] - channel.update(["d", "f"]) + assert channel.update(["d", "f"]) assert channel.get() == ["f"], "de-dupes from checkpoint" @@ -136,16 +144,16 @@ def test_topic_accumulate() -> None: assert channel.ValueType is Sequence[str] assert channel.UpdateType is Union[str, list[str]] - channel.update(["a", "b"]) + assert channel.update(["a", "b"]) assert channel.get() == ["a", "b"] - channel.update(["b", ["c", "d"], "d"]) + assert channel.update(["b", ["c", "d"], "d"]) assert channel.get() == ["a", "b", "b", "c", "d", "d"] - channel.update([]) + assert not channel.update([]) assert channel.get() == ["a", "b", "b", "c", "d", "d"] checkpoint = channel.checkpoint() with Topic(str, accumulate=True).from_checkpoint(checkpoint) as channel: assert channel.get() == ["a", "b", "b", "c", "d", "d"] - channel.update(["e"]) + assert channel.update(["e"]) assert channel.get() == ["a", "b", "b", "c", "d", "d", "e"] @@ -154,16 +162,16 @@ async def test_topic_accumulate_async() -> None: assert channel.ValueType is Sequence[str] assert channel.UpdateType is Union[str, list[str]] - channel.update(["a", "b"]) + assert channel.update(["a", "b"]) assert channel.get() == ["a", "b"] - channel.update(["b", ["c", "d"], "d"]) + assert channel.update(["b", ["c", "d"], "d"]) assert channel.get() == ["a", "b", "b", "c", "d", "d"] - channel.update([]) + assert not channel.update([]) assert channel.get() == ["a", "b", "b", "c", "d", "d"] checkpoint = channel.checkpoint() async with Topic(str, accumulate=True).afrom_checkpoint(checkpoint) as channel: assert channel.get() == ["a", "b", "b", "c", "d", "d"] - channel.update(["e"]) + assert channel.update(["e"]) assert channel.get() == ["a", "b", "b", "c", "d", "d", "e"] @@ -172,18 +180,19 @@ def test_topic_unique_accumulate() -> None: assert channel.ValueType is Sequence[str] assert channel.UpdateType is Union[str, list[str]] - channel.update(["a", "b"]) + assert channel.update(["a", "b"]) assert channel.get() == ["a", "b"] - channel.update(["b", ["c", "d"], "d"]) + assert channel.update(["b", ["c", "d"], "d"]) assert channel.get() == ["a", "b", "c", "d"] - channel.update([]) + assert not channel.update(["c"]), "no new values" + assert not channel.update([]) assert channel.get() == ["a", "b", "c", "d"] checkpoint = channel.checkpoint() with Topic(str, unique=True, accumulate=True).from_checkpoint( checkpoint ) as channel: assert channel.get() == ["a", "b", "c", "d"] - channel.update(["d", "e"]) + assert channel.update(["d", "e"]) assert channel.get() == ["a", "b", "c", "d", "e"] diff --git a/tests/test_pregel.py b/tests/test_pregel.py index 712d260f5..55953a18f 100644 --- a/tests/test_pregel.py +++ b/tests/test_pregel.py @@ -30,10 +30,17 @@ from langsmith import traceable from pytest_mock import MockerFixture from syrupy import SnapshotAssertion +from langgraph.channels.base import BaseChannel from langgraph.channels.binop import BinaryOperatorAggregate from langgraph.channels.context import Context from langgraph.channels.last_value import LastValue from langgraph.channels.topic import Topic +from langgraph.checkpoint.base import ( + Checkpoint, + CheckpointMetadata, + CheckpointTuple, +) +from langgraph.checkpoint.memory import MemorySaver from langgraph.checkpoint.sqlite import SqliteSaver from langgraph.constants import Send from langgraph.errors import InvalidUpdateError @@ -171,6 +178,45 @@ def test_graph_validation() -> None: graph.compile() +def test_checkpoint_errors() -> None: + class FaultyGetCheckpointer(MemorySaver): + def get_tuple(self, config: RunnableConfig) -> Optional[CheckpointTuple]: + raise ValueError("Faulty get_tuple") + + class FaultyPutCheckpointer(MemorySaver): + def put( + self, + config: RunnableConfig, + checkpoint: Checkpoint, + metadata: CheckpointMetadata, + ) -> RunnableConfig: + raise ValueError("Faulty put") + + class FaultyVersionCheckpointer(MemorySaver): + def get_next_version(self, current: Optional[int], channel: BaseChannel) -> int: + raise ValueError("Faulty get_next_version") + + def logic(inp: str) -> str: + return "" + + builder = Graph() + builder.add_node("agent", logic) + builder.set_entry_point("agent") + builder.set_finish_point("agent") + + graph = builder.compile(checkpointer=FaultyGetCheckpointer()) + with pytest.raises(ValueError, match="Faulty get_tuple"): + graph.invoke("", {"configurable": {"thread_id": "thread-1"}}) + + graph = builder.compile(checkpointer=FaultyPutCheckpointer()) + with pytest.raises(ValueError, match="Faulty put"): + graph.invoke("", {"configurable": {"thread_id": "thread-1"}}) + + graph = builder.compile(checkpointer=FaultyVersionCheckpointer()) + with pytest.raises(ValueError, match="Faulty get_next_version"): + graph.invoke("", {"configurable": {"thread_id": "thread-1"}}) + + def test_reducer_before_first_node() -> None: from langchain_core.messages import HumanMessage @@ -619,7 +665,7 @@ def test_invoke_two_processes_in_dict_out(mocker: MockerFixture) -> None: ] assert [*app.stream({"input": 2, "inbox": 12})] == [ {"inbox": [3], "output": 13}, - {"inbox": [], "output": 4}, + {"output": 4}, ] assert [*app.stream({"input": 2, "inbox": 12}, stream_mode="debug")] == [ { @@ -842,7 +888,7 @@ def test_invoke_checkpoint(mocker: MockerFixture) -> None: pass else: errored_once = True - raise OSError("I will be retried") + raise ConnectionError("I will be retried") if input > 10: raise ValueError("Input is too large") return input @@ -1216,7 +1262,7 @@ def test_channel_enter_exit_timing(mocker: MockerFixture) -> None: if i == 0: assert chunk == {"inbox": [3]} elif i == 1: - assert chunk == {"inbox": [], "output": 4} + assert chunk == {"output": 4} else: assert False, "Expected only two chunks" assert cleanup.call_count == 1, "Expected cleanup to be called once" @@ -6048,7 +6094,6 @@ def test_branch_then(snapshot: SnapshotAssertion) -> None: "metadata": {"thread_id": "10"}, "callbacks": None, "recursion_limit": 25, - "run_id": None, "configurable": { "thread_id": "10", "thread_ts": AnyStr(), @@ -6072,7 +6117,6 @@ def test_branch_then(snapshot: SnapshotAssertion) -> None: "metadata": {"thread_id": "10"}, "callbacks": None, "recursion_limit": 25, - "run_id": None, "configurable": { "thread_id": "10", "thread_ts": AnyStr(), @@ -6120,7 +6164,6 @@ def test_branch_then(snapshot: SnapshotAssertion) -> None: "metadata": {"thread_id": "10"}, "callbacks": None, "recursion_limit": 25, - "run_id": None, "configurable": { "thread_id": "10", "thread_ts": AnyStr(), @@ -6168,7 +6211,6 @@ def test_branch_then(snapshot: SnapshotAssertion) -> None: "metadata": {"thread_id": "10"}, "callbacks": None, "recursion_limit": 25, - "run_id": None, "configurable": { "thread_id": "10", "thread_ts": AnyStr(), @@ -6216,7 +6258,6 @@ def test_branch_then(snapshot: SnapshotAssertion) -> None: "metadata": {"thread_id": "10"}, "callbacks": None, "recursion_limit": 25, - "run_id": None, "configurable": { "thread_id": "10", "thread_ts": AnyStr(), diff --git a/tests/test_pregel_async.py b/tests/test_pregel_async.py index 51a024fe3..b5cfb3a80 100644 --- a/tests/test_pregel_async.py +++ b/tests/test_pregel_async.py @@ -1,6 +1,7 @@ import asyncio import json import operator +import time from collections import Counter from contextlib import asynccontextmanager, contextmanager from typing import ( @@ -24,14 +25,19 @@ from langchain_core.runnables import ( RunnablePassthrough, RunnablePick, ) +from langchain_core.utils.aiter import aclosing from pytest_mock import MockerFixture from syrupy import SnapshotAssertion +from langgraph.channels.base import BaseChannel from langgraph.channels.binop import BinaryOperatorAggregate from langgraph.channels.context import Context from langgraph.channels.last_value import LastValue from langgraph.channels.topic import Topic +from langgraph.checkpoint import BaseCheckpointSaver from langgraph.checkpoint.aiosqlite import AsyncSqliteSaver +from langgraph.checkpoint.base import Checkpoint, CheckpointMetadata, CheckpointTuple +from langgraph.checkpoint.memory import MemorySaver from langgraph.constants import Send from langgraph.errors import InvalidUpdateError from langgraph.graph import END, Graph, StateGraph @@ -53,6 +59,69 @@ from tests.memory_assert import ( ) +async def test_checkpoint_errors() -> None: + class FaultyGetCheckpointer(MemorySaver): + async def aget_tuple(self, config: RunnableConfig) -> Optional[CheckpointTuple]: + raise ValueError("Faulty get_tuple") + + class FaultyPutCheckpointer(MemorySaver): + async def aput( + self, + config: RunnableConfig, + checkpoint: Checkpoint, + metadata: CheckpointMetadata, + ) -> RunnableConfig: + raise ValueError("Faulty put") + + class FaultyVersionCheckpointer(MemorySaver): + def get_next_version(self, current: Optional[int], channel: BaseChannel) -> int: + raise ValueError("Faulty get_next_version") + + def logic(inp: str) -> str: + return "" + + builder = Graph() + builder.add_node("agent", logic) + builder.set_entry_point("agent") + builder.set_finish_point("agent") + + graph = builder.compile(checkpointer=FaultyGetCheckpointer()) + with pytest.raises(ValueError, match="Faulty get_tuple"): + await graph.ainvoke("", {"configurable": {"thread_id": "thread-1"}}) + with pytest.raises(ValueError, match="Faulty get_tuple"): + async for _ in graph.astream("", {"configurable": {"thread_id": "thread-2"}}): + pass + with pytest.raises(ValueError, match="Faulty get_tuple"): + async for _ in graph.astream_events( + "", {"configurable": {"thread_id": "thread-3"}}, version="v2" + ): + pass + + graph = builder.compile(checkpointer=FaultyPutCheckpointer()) + with pytest.raises(ValueError, match="Faulty put"): + await graph.ainvoke("", {"configurable": {"thread_id": "thread-1"}}) + with pytest.raises(ValueError, match="Faulty put"): + async for _ in graph.astream("", {"configurable": {"thread_id": "thread-2"}}): + pass + with pytest.raises(ValueError, match="Faulty put"): + async for _ in graph.astream_events( + "", {"configurable": {"thread_id": "thread-3"}}, version="v2" + ): + pass + + graph = builder.compile(checkpointer=FaultyVersionCheckpointer()) + with pytest.raises(ValueError, match="Faulty get_next_version"): + await graph.ainvoke("", {"configurable": {"thread_id": "thread-1"}}) + with pytest.raises(ValueError, match="Faulty get_next_version"): + async for _ in graph.astream("", {"configurable": {"thread_id": "thread-2"}}): + pass + with pytest.raises(ValueError, match="Faulty get_next_version"): + async for _ in graph.astream_events( + "", {"configurable": {"thread_id": "thread-3"}}, version="v2" + ): + pass + + async def test_node_cancellation_on_external_cancel() -> None: inner_task_cancelled = False @@ -135,6 +204,158 @@ async def test_step_timeout_on_stream_hang() -> None: assert inner_task_cancelled +@pytest.mark.parametrize( + "checkpointer", + [ + MemorySaverAssertImmutable(), + AsyncSqliteSaver.from_conn_string(":memory:"), + None, + ], +) +async def test_cancel_graph_astream( + checkpointer: Optional[BaseCheckpointSaver], +) -> None: + try: + + class State(TypedDict): + value: int + + class AwhileMaker: + def __init__(self) -> None: + self.reset() + + async def __call__(self, input: State) -> Any: + self.started = True + try: + await asyncio.sleep(1.5) + except asyncio.CancelledError: + self.cancelled = True + raise + + def reset(self): + self.started = False + self.cancelled = False + + async def alittlewhile(input: State) -> None: + await asyncio.sleep(0.6) + return {"value": 2} + + awhile = AwhileMaker() + builder = StateGraph(State) + builder.add_node("awhile", awhile) + builder.add_node(alittlewhile) + builder.add_edge(START, "alittlewhile") + builder.add_edge("alittlewhile", "awhile") + graph = builder.compile(checkpointer=checkpointer) + + # test interrupting astream + thread1: RunnableConfig = {"configurable": {"thread_id": 1}} + async with aclosing(graph.astream({"value": 1}, thread1)) as stream: + async for chunk in stream: + assert chunk == {"alittlewhile": {"value": 2}} + break + + # node "awhile" should never start + assert awhile.started is False + + # checkpoint with output of "alittlewhile" should not be saved + if checkpointer is not None: + state = await graph.aget_state(thread1) + assert state is not None + assert state.values == {"value": 1} + assert state.next == ("alittlewhile",) + assert state.metadata == {"source": "loop", "step": 0, "writes": None} + finally: + if getattr(checkpointer, "__aexit__", None): + await checkpointer.__aexit__(None, None, None) + + +@pytest.mark.parametrize( + "checkpointer", + [ + MemorySaverAssertImmutable(), + AsyncSqliteSaver.from_conn_string(":memory:"), + None, + ], +) +async def test_cancel_graph_astream_events_v2( + checkpointer: Optional[BaseCheckpointSaver], +) -> None: + try: + + class State(TypedDict): + value: int + + class AwhileMaker: + def __init__(self) -> None: + self.reset() + + async def __call__(self, input: State) -> Any: + self.started = True + try: + await asyncio.sleep(1.5) + except asyncio.CancelledError: + self.cancelled = True + raise + + def reset(self): + self.started = False + self.cancelled = False + + async def alittlewhile(input: State) -> None: + await asyncio.sleep(0.6) + return {"value": 2} + + awhile = AwhileMaker() + anotherwhile = AwhileMaker() + builder = StateGraph(State) + builder.add_node(alittlewhile) + builder.add_node("awhile", awhile) + builder.add_node("anotherwhile", anotherwhile) + builder.add_edge(START, "alittlewhile") + builder.add_edge("alittlewhile", "awhile") + builder.add_edge("awhile", "anotherwhile") + graph = builder.compile(checkpointer=checkpointer) + + # test interrupting astream_events v2 + got_event = False + thread2: RunnableConfig = {"configurable": {"thread_id": 2}} + async with aclosing( + graph.astream_events({"value": 1}, thread2, version="v2") + ) as stream: + async for chunk in stream: + if chunk["event"] == "on_chain_stream" and not chunk["parent_ids"]: + print(time.perf_counter(), "got event out here", chunk) + got_event = True + assert chunk["data"]["chunk"] == {"alittlewhile": {"value": 2}} + break + + # did break + assert got_event + + # node "awhile" starts but is cancelled + assert awhile.started is True + assert awhile.cancelled is True + + # node "anotherwhile" should never start + assert anotherwhile.started is False + + # checkpoint with output of "alittlewhile" should not be saved + if checkpointer is not None: + state = await graph.aget_state(thread2) + assert state is not None + assert state.values == {"value": 2} + assert state.next == ("awhile",) + assert state.metadata == { + "source": "loop", + "step": 1, + "writes": {"alittlewhile": {"value": 2}}, + } + finally: + if getattr(checkpointer, "__aexit__", None): + await checkpointer.__aexit__(None, None, None) + + async def test_invoke_single_process_in_out(mocker: MockerFixture) -> None: add_one = mocker.Mock(side_effect=lambda x: x + 1) chain = Channel.subscribe_to("input") | add_one | Channel.write_to("output") @@ -529,7 +750,7 @@ async def test_invoke_two_processes_in_dict_out(mocker: MockerFixture) -> None: ] assert [c async for c in app.astream({"input": 2, "inbox": 12})] == [ {"inbox": [3], "output": 13}, - {"inbox": [], "output": 4}, + {"output": 4}, ] assert [ c async for c in app.astream({"input": 2, "inbox": 12}, stream_mode="debug") @@ -758,7 +979,7 @@ async def test_invoke_checkpoint(mocker: MockerFixture) -> None: pass else: errored_once = True - raise OSError("I will be retried") + raise ConnectionError("I will be retried") if input > 10: raise ValueError("Input is too large") return input @@ -1146,7 +1367,7 @@ async def test_channel_enter_exit_timing(mocker: MockerFixture) -> None: if i == 0: assert chunk == {"inbox": [3]} elif i == 1: - assert chunk == {"inbox": [], "output": 4} + assert chunk == {"output": 4} else: assert False, "Expected only two chunks" assert setup_sync.call_count == 0 @@ -4428,7 +4649,6 @@ async def test_branch_then() -> None: "metadata": {"thread_id": "10"}, "callbacks": None, "recursion_limit": 25, - "run_id": None, "configurable": {"thread_id": "10", "thread_ts": AnyStr()}, }, "values": {"my_key": ""}, @@ -4449,7 +4669,6 @@ async def test_branch_then() -> None: "metadata": {"thread_id": "10"}, "callbacks": None, "recursion_limit": 25, - "run_id": None, "configurable": { "thread_id": "10", "thread_ts": AnyStr(), @@ -4493,7 +4712,6 @@ async def test_branch_then() -> None: "metadata": {"thread_id": "10"}, "callbacks": None, "recursion_limit": 25, - "run_id": None, "configurable": { "thread_id": "10", "thread_ts": AnyStr(), @@ -4538,7 +4756,6 @@ async def test_branch_then() -> None: "metadata": {"thread_id": "10"}, "callbacks": None, "recursion_limit": 25, - "run_id": None, "configurable": { "thread_id": "10", "thread_ts": AnyStr(), @@ -4586,7 +4803,6 @@ async def test_branch_then() -> None: "metadata": {"thread_id": "10"}, "callbacks": None, "recursion_limit": 25, - "run_id": None, "configurable": { "thread_id": "10", "thread_ts": AnyStr(),