Compare commits

..

3 Commits

Author SHA1 Message Date
Eugene Yurtsev 1bdcb70e73 x 2024-03-14 13:30:28 -04:00
Eugene Yurtsev 1ad5619598 x 2024-03-14 13:30:15 -04:00
Eugene Yurtsev 588e7975c2 x 2024-03-14 13:19:37 -04:00
86 changed files with 5739 additions and 7158 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
{
"contributors": ["eyurtsev", "hwchase17", "nfcampos", "efriis", "jacoblee93", "dqbd", "kreneskyp", "adarsh-jha-dev", "harris", "baskaryan", "hinthornw", "bracesproul", "jakerachleff", "craigsdennis", "anhi", "169", "LarchLiu", "PaulLockett", "RCMatthias", "jwynia", "majiayu000", "mpskex", "shivachittamuru", "sinashaloudegi", "sowsan", "akira", "lucianotonet", "JGalego", "nat-n", "dirien", "donbr", "rahilvora", "WarrenTheRabbit", "StreetLamb", "ccurme", "dennisrall", "Mingqi2", "xxsl", "joaquin-borggio-lc"],
"contributors": ["eyurtsev", "hwchase17", "nfcampos", "efriis", "jacoblee93", "dqbd", "kreneskyp", "adarsh-jha-dev", "harris", "baskaryan", "hinthornw", "bracesproul", "jakerachleff", "craigsdennis", "anhi", "169", "LarchLiu", "PaulLockett", "RCMatthias", "jwynia", "majiayu000", "mpskex", "shivachittamuru", "sinashaloudegi", "sowsan", "akira", "lucianotonet", "JGalego", "nat-n", "dirien"],
"message": "Thank you for your pull request and welcome to our community. We require contributors to sign our Contributor License Agreement, and we don't seem to have the username {{usersWithoutCLA}} on file. In order for us to review and merge your code, please complete the Individual Contributor License Agreement here https://forms.gle/AQFbtkWRoHXUgipM6 .\n\nThis process is done manually on our side, so after signing the form one of the maintainers will add you to the contributors list.\n\nFor more details about why we have a CLA and other contribution guidelines please see: https://github.com/langchain-ai/langserve/blob/main/CONTRIBUTING.md."
}
-19
View File
@@ -1,19 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "pip" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
- package-ecosystem: "github-actions" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
- package-ecosystem: "npm" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
+4 -6
View File
@@ -1,6 +1,4 @@
name: lint
permissions:
contents: read
on:
workflow_call:
@@ -33,10 +31,10 @@ jobs:
# Starting new jobs is also relatively slow,
# so linting on fewer versions makes CI faster.
python-version:
- "3.10"
- "3.8"
- "3.11"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v3
with:
# Fetch the last FETCH_DEPTH commits, so the mtime-changing script
# can accurately set the mtimes of files modified in the last FETCH_DEPTH commits.
@@ -117,7 +115,7 @@ jobs:
poetry install --with dev,lint,test,typing
- name: Restore black cache
uses: actions/cache@v5
uses: actions/cache@v3
env:
CACHE_BASE: black-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python-version }}-${{ inputs.working-directory }}-${{ hashFiles(format('{0}/poetry.lock', env.WORKDIR)) }}
SEGMENT_DOWNLOAD_TIMEOUT_MIN: "1"
@@ -130,7 +128,7 @@ jobs:
${{ env.CACHE_BASE }}-
- name: Get .mypy_cache to speed up mypy
uses: actions/cache@v5
uses: actions/cache@v3
env:
SEGMENT_DOWNLOAD_TIMEOUT_MIN: "2"
with:
@@ -0,0 +1,94 @@
name: pydantic v1/v2 compatibility
on:
workflow_call:
inputs:
working-directory:
required: true
type: string
description: "From which folder this pipeline executes"
env:
POETRY_VERSION: "1.5.1"
jobs:
build:
timeout-minutes: 10
defaults:
run:
working-directory: ${{ inputs.working-directory }}
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- "3.8"
- "3.9"
- "3.10"
- "3.11"
name: Pydantic v1/v2 compatibility - Python ${{ matrix.python-version }}
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_setup"
with:
python-version: ${{ matrix.python-version }}
poetry-version: ${{ env.POETRY_VERSION }}
working-directory: ${{ inputs.working-directory }}
cache-key: pydantic-cross-compat
- name: Install dependencies
shell: bash
run: poetry install
- name: Install the opposite major version of pydantic
# If normal tests use pydantic v1, here we'll use v2, and vice versa.
shell: bash
run: |
# Determine the major part of pydantic version
REGULAR_VERSION=$(poetry run python -c "import pydantic; print(pydantic.__version__)" | cut -d. -f1)
if [[ "$REGULAR_VERSION" == "1" ]]; then
PYDANTIC_DEP=">=2.1,<3"
TEST_WITH_VERSION="2"
elif [[ "$REGULAR_VERSION" == "2" ]]; then
PYDANTIC_DEP="<2"
TEST_WITH_VERSION="1"
else
echo "Unexpected pydantic major version '$REGULAR_VERSION', cannot determine which version to use for cross-compatibility test."
exit 1
fi
# Install via `pip` instead of `poetry add` to avoid changing lockfile,
# which would prevent caching from working: the cache would get saved
# to a different key than where it gets loaded from.
poetry run pip install "pydantic${PYDANTIC_DEP}"
# Ensure that the correct pydantic is installed now.
echo "Checking pydantic version... Expecting ${TEST_WITH_VERSION}"
# Determine the major part of pydantic version
CURRENT_VERSION=$(poetry run python -c "import pydantic; print(pydantic.__version__)" | cut -d. -f1)
# Check that the major part of pydantic version is as expected, if not
# raise an error
if [[ "$CURRENT_VERSION" != "$TEST_WITH_VERSION" ]]; then
echo "Error: expected pydantic version ${CURRENT_VERSION} to have been installed, but found: ${TEST_WITH_VERSION}"
exit 1
fi
echo "Found pydantic version ${CURRENT_VERSION}, as expected"
- name: Run pydantic compatibility tests
shell: bash
run: make test
- name: Ensure the tests did not create any additional files
shell: bash
run: |
set -eu
STATUS="$(git status)"
echo "$STATUS"
# grep will exit non-zero if the target message isn't found,
# and `set -e` above will cause the step to fail.
echo "$STATUS" | grep 'nothing to commit, working tree clean'
+1 -7
View File
@@ -7,12 +7,6 @@ on:
required: true
type: string
description: "From which folder this pipeline executes"
workflow_dispatch: # Allows to trigger the workflow manually in GitHub UI
inputs:
working-directory:
required: true
type: string
description: "From which folder this pipeline executes"
env:
POETRY_VERSION: "1.5.1"
@@ -36,7 +30,7 @@ jobs:
run:
working-directory: ${{ inputs.working-directory }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v3
- name: Set up Python + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_setup"
+3 -1
View File
@@ -20,11 +20,13 @@ jobs:
strategy:
matrix:
python-version:
- "3.8"
- "3.9"
- "3.10"
- "3.11"
name: Python ${{ matrix.python-version }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_setup"
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
run:
working-directory: ${{ inputs.working-directory }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v3
- name: Set up Python + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_setup"
+10 -5
View File
@@ -18,10 +18,6 @@ on:
- 'Makefile'
workflow_dispatch: # Allows to trigger the workflow manually in GitHub UI
# This workflow only needs to read the repo contents.
permissions:
contents: read
# If another push to the same PR or branch happens while this workflow is still running,
# cancel the earlier run in favor of the next run.
#
@@ -43,6 +39,13 @@ jobs:
with:
working-directory: .
secrets: inherit
pydantic-compatibility:
uses:
./.github/workflows/_pydantic_compatibility.yml
with:
working-directory: .
secrets: inherit
test:
timeout-minutes: 10
runs-on: ubuntu-latest
@@ -52,11 +55,13 @@ jobs:
strategy:
matrix:
python-version:
- "3.8"
- "3.9"
- "3.10"
- "3.11"
name: Python ${{ matrix.python-version }} tests
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_setup"
-8
View File
@@ -4,18 +4,10 @@ name: Release
on:
workflow_dispatch: # Allows to trigger the workflow manually in GitHub UI
permissions:
contents: read
jobs:
release:
uses:
./.github/workflows/_release.yml
with:
working-directory: .
permissions:
# Trusted publishing to PyPI
id-token: write
# Creating GitHub releases
contents: write
secrets: inherit
@@ -4,16 +4,10 @@ name: Test Release
on:
workflow_dispatch: # Allows to trigger the workflow manually in GitHub UI
permissions:
contents: read
jobs:
release:
uses:
./.github/workflows/_test_release.yml
with:
working-directory: .
permissions:
# Trusted publishing to TestPyPI
id-token: write
secrets: inherit
-29
View File
@@ -49,32 +49,3 @@ To run linting for this project:
```sh
make lint
```
## Frontend Playground Development
Here are a few tips to keep in mind when developing the LangServe playgrounds:
### Setup
Switch directories to `langserve/playground` or `langserve/chat_playground`, then run `yarn` to install required
dependencies. `yarn dev` will start the playground at `http://localhost:5173/____LANGSERVE_BASE_URL/` in dev mode.
You can run one of the chains in the `examples/` repo using `poetry run python path/to/file.py`.
### Setting CORS
You may need to add the following to an example route when developing the playground in dev mode to handle CORS:
```python
from fastapi.middleware.cors import CORSMiddleware
# Set all CORS enabled origins
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
expose_headers=["*"],
)
```
-222
View File
@@ -1,222 +0,0 @@
# LangGraph Platform Migration Guide
We have [recently announced](https://blog.langchain.dev/langgraph-platform-announce/) LangGraph Platform, a ***significantly*** enhanced solution for deploying agentic applications at scale.
LangGraph Platform incorporates [key design patterns and capabilities](https://langchain-ai.github.io/langgraph/concepts/langgraph_platform/#option-2-leveraging-langgraph-platform-for-complex-deployments) essential for production-level deployment of large language model (LLM) applications.
In contrast to LangServe, LangGraph Platform provides comprehensive, out-of-the-box support for [persistence](https://langchain-ai.github.io/langgraph/concepts/application_structure/), [memory](https://langchain-ai.github.io/langgraph/concepts/assistants/), [double-texting handling](https://langchain-ai.github.io/langgraph/concepts/double_texting/), [human-in-the-loop workflows](https://langchain-ai.github.io/langgraph/concepts/assistants/), [cron job scheduling](https://langchain-ai.github.io/langgraph/concepts/langgraph_server/#cron-jobs), [webhooks](https://langchain-ai.github.io/langgraph/concepts/langgraph_server/#webhooks), high-load management, advanced streaming, support for long-running tasks, background task processing, and much more.
The LangGraph Platform ecosystem includes the following components:
- [LangGraph Server](https://langchain-ai.github.io/langgraph/concepts/langgraph_server/): Provides an [Assistants API](https://langchain-ai.github.io/langgraph/cloud/reference/api/api_ref.html) for LLM applications (graphs) built with [LangGraph](https://langchain-ai.github.io/langgraph/). Available in both Python and JavaScript/TypeScript.
- [LangGraph Studio](https://langchain-ai.github.io/langgraph/concepts/langgraph_studio/): A specialized IDE for real-time visualization, debugging, and interaction via a graphical interface. Available as a web application or macOS desktop app, it's a substantial improvement over LangServe's playground.
- [SDK](https://langchain-ai.github.io/langgraph/concepts/sdk/): Enables programmatic interaction with the server, available in Python and JavaScript/TypeScript.
- [RemoteGraph](https://langchain-ai.github.io/langgraph/how-tos/use-remote-graph/): Allows interaction with a remote graph as if it were running locally, serving as LangGraph's equivalent to LangServe's RemoteRunnable. Available in both Python and JavaScript/TypeScript.
## Context
LangServe was built as a deployment solution for LangChain Runnables created using the [LangChain Expression Language (LCEL)](https://python.langchain.com/docs/concepts/lcel). In LangServe, the LCEL was the orchestration layer that managed the execution of the Runnable.
[LangGraph](https://langchain-ai.github.io/langgraph/) is an open source library created by the LangChain team that provides a more flexible orchestration layer that's better suited for creating more complex LLM applications. LangGraph Platform
is the deployment solution for LangGraph applications.
## LangServe Support
We recommend using LangGraph Platform rather than LangServe for new projects.
We will continue to accept bug fixes for LangServe from the community; however, we will not be accepting new feature contributions.
## Migration
If you would like to migrate an existing LangServe application to LangGraph Platform, you have two options:
1. You can wrap the existing `Runnable` that you expose in the LangServe application via `add_routes` in a `LangGraph` node. This is the quickest way to migrate your application to LangGraph Platform.
2. You can do a larger refactor to break up the existing LCEL into appropriate `LangGraph` nodes. This is recommended if you want to take advantage of more advanced features in LangGraph Platform.
### Option 1: Wrap Runnable in LangGraph Node
This option is the quickest way to migrate your application to LangGraph Platform. You can wrap the existing `Runnable` that you expose in the LangServe application via `add_routes` in a `LangGraph` node.
Original LangServe code:
```python
from langserve import add_routes
app = FastAPI()
# Some input schema
class Input(BaseModel):
input: str
foo: Optional[str]
# Some output schema
class Output(BaseModel):
output: Any
runnable = .... # Your existing Runnable
runnable_with_types = runnable.with_types(input_type=Input, output_type=Output)
# Adds routes to the app for using the chain under:
add_routes(
app,
runnable_with_types,
)
```
Migrated LangGraph Platform code:
```python
@dataclass
class InputState: # Equivalent to Input in the original code
"""Defines the input state, representing a narrower interface to the outside world.
This class is used to define the initial state and structure of incoming data.
See: https://langchain-ai.github.io/langgraph/concepts/low_level/#state
for more information.
"""
input: str
foo: Optional[str] = None
@dataclass
class OutputState: # Equivalent to Output in the original code
"""Defines the output state, representing a narrower interface to the outside world.
https://langchain-ai.github.io/langgraph/concepts/low_level/#state
"""
output: Any
@dataclass
class SharedState:
"""The full graph state.
https://langchain-ai.github.io/langgraph/concepts/low_level/#state
"""
input: str
foo: Optional[str] = None
output: Any
runnable = ... # Same code as before
async def my_node(state: InputState, config: RunnableConfig) -> OutputState:
"""Each node does work."""
return await runnable.ainvoke({"input": state.input, "foo": state.foo})
# Define a new graph
builder = StateGraph(
SharedState, config_schema=Configuration, input=InputState, output=OutputState
)
# Add the node to the graph
builder.add_node("my_node", my_node)
# Set the entrypoint as `call_model`
builder.add_edge("__start__", "my_node")
# Compile the workflow into an executable graph
graph = builder.compile()
graph.name = "New Graph" # This defines the custom name in LangSmith
```
### 2. Refactor LCEL into LangGraph Nodes
This option is recommended if you want to take advantage of more advanced features in LangGraph Platform.
#### Memory (alternative to `RunnableWithMessageHistory`)
For example, LangGraph comes with built-in persistence that is more general than LangChain's `RunnableWithMessageHistory`.
Please refer to the guide on [upgrading to LangGraph memory](https://python.langchain.com/docs/versions/migrating_memory/) for more details.
#### Agents
If you're relying on legacy LangChain agents, you can migrate them into the pre-built
LangGraph agents. Please refer to the guide on [migrating agents](https://python.langchain.com/docs/how_to/migrate_agent/) for more details.
#### Custom Chains
If you created a custom chain and used LCEL to orchestrate it, you will usually be able to refactor it into a LangGraph without too much difficulty.
There isn't a one-size-fits-all guide for this, but generally speaking, consider creating
a separate node for any long-running step in your LCEL chain or any step that you would
want to be able to monitor or debug separately.
For example, if you have a simple Retrieval Augmented Generation (RAG) pipeline, you might have a node for the retrieval step and a node for the generation step.
Original LCEL code:
```python
...
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
rag_chain.with_types(input_type=Input, output_type=Output)
```
Using LangGraph for the same pipeline:
```python
@dataclass
class InputState: # Equivalent to Input in the original code
"""Input question from the user."""
question: str
@dataclass
class OutputState: # Equivalent to Output in the original code
"""The output from the graph."""
answer: str
@dataclass
class SharedState:
question: str
docs: List[str]
response: str
async def retriever_node(state: InputState) -> SharedState:
"""Rettrieve documents based on the user's question."""
documents = await retriever.ainvoke({"context": state.question})
return {
"docs": documents
}
async def generator_node(state: SharedState) -> OutputState:
"""Generate an answer using an LLM based on the retrieved documents and question."""
context = " -- DOCUMENT -- ".join(state.docs)
prompt = [
SystemMessage(
content=(
"Answer the user's question based on the list of documents "
"that were retrieved. Here are the documents: \n\n"
f"{context}"
)
),
HumanMessage(content=state.question),
]
ai_message = await llm.ainvoke(prompt)
return {"answer": ai_message.content}
# Define a new graph
builder = StateGraph(
SharedState, config_schema=Configuration, input=InputState, output=OutputState
)
builder.add_node("retriever", retriever_node)
builder.add_node("generator", generator_node)
builder.add_edge("__start__", "retriever")
builder.add_edge("retriever", "generator")
graph = builder.compile()
graph.name = "RAG Graph"
```
Please see the [LangGraph tutorials](https://langchain-ai.github.io/langgraph/tutorials/)
for tutorials and examples that will help you get started with LangGraph
and LangGraph Platform.
+2 -2
View File
@@ -32,12 +32,12 @@ lint format: PYTHON_FILES=.
lint_diff format_diff: PYTHON_FILES=$(shell git diff --relative=. --name-only --diff-filter=d master | grep -E '\.py$$|\.ipynb$$')
lint lint_diff:
poetry run ruff check .
poetry run ruff .
poetry run ruff format $(PYTHON_FILES) --check
format format_diff:
poetry run ruff format $(PYTHON_FILES)
poetry run ruff check --select I --fix $(PYTHON_FILES)
poetry run ruff --select I --fix $(PYTHON_FILES)
spell_check:
poetry run codespell --toml pyproject.toml
+96 -116
View File
@@ -5,16 +5,9 @@
[![Open Issues](https://img.shields.io/github/issues-raw/langchain-ai/langserve)](https://github.com/langchain-ai/langserve/issues)
[![](https://dcbadge.vercel.app/api/server/6adMQxSpJS?compact=true&style=flat)](https://discord.com/channels/1038097195422978059/1170024642245832774)
> [!WARNING]
> **DEPRECATED** This project has been deprecated since Nov 18, 2024 (https://github.com/langchain-ai/langserve/issues/791).
>
> We recommend using LangGraph Platform rather than LangServe for new projects.
>
> Please see the [LangGraph Platform Migration Guide](./MIGRATION.md) for more information.
>
> We will continue to accept bug fixes for LangServe from the community; however, we
> will not be accepting new feature contributions.
🚩 We will be releasing a hosted version of LangServe for one-click deployments of
LangChain applications. [Sign up here](https://airtable.com/app0hN6sd93QcKubv/shrAjst60xXa6quV2)
to get on the waitlist.
## Overview
@@ -35,11 +28,11 @@ in [LangChain.js](https://js.langchain.com/docs/ecosystem/langserve).
- Input and Output schemas automatically inferred from your LangChain object, and
enforced on every API call, with rich error messages
- API docs page with JSONSchema and Swagger (insert example link)
- Efficient `/invoke`, `/batch` and `/stream` endpoints with support for many
- Efficient `/invoke/`, `/batch/` and `/stream/` endpoints with support for many
concurrent requests on a single server
- `/stream_log` endpoint for streaming all (or some) intermediate steps from your
- `/stream_log/` endpoint for streaming all (or some) intermediate steps from your
chain/agent
- **new** as of 0.0.40, supports `/stream_events` to make it easier to stream without needing to parse the output of `/stream_log`.
- **new** as of 0.0.40, supports `astream_events` to make it easier to stream without needing to parse the output of `stream_log`.
- Playground page at `/playground/` with streaming output and intermediate steps
- Built-in (optional) tracing to [LangSmith](https://www.langchain.com/langsmith), just
add your API key (see [Instructions](https://docs.smith.langchain.com/))
@@ -49,22 +42,23 @@ in [LangChain.js](https://js.langchain.com/docs/ecosystem/langserve).
locally (or call the HTTP API directly)
- [LangServe Hub](https://github.com/langchain-ai/langchain/blob/master/templates/README.md)
## ⚠️ LangGraph Compatibility
LangServe is designed to primarily deploy simple Runnables and work with well-known primitives in langchain-core.
If you need a deployment option for LangGraph, you should instead be looking at [LangGraph Cloud (beta)](https://langchain-ai.github.io/langgraph/cloud/) which will
be better suited for deploying LangGraph applications.
## Limitations
- Client callbacks are not yet supported for events that originate on the server
- Versions of LangServe <= 0.2.0, will not generate OpenAPI docs properly when using Pydantic V2 as Fast API does not support [mixing pydantic v1 and v2 namespaces](https://github.com/tiangolo/fastapi/issues/10360).
See section below for more details. Either upgrade to LangServe>=0.3.0 or downgrade Pydantic to pydantic 1.
- OpenAPI docs will not be generated when using Pydantic V2. Fast API does not
support [mixing pydantic v1 and v2 namespaces](https://github.com/tiangolo/fastapi/issues/10360).
See section below for more details.
## Hosted LangServe
We will be releasing a hosted version of LangServe for one-click deployments of
LangChain
applications. [Sign up here](https://airtable.com/apppQ9p5XuujRl3wJ/shrABpHWdxry8Bacm)
to get on the waitlist.
## Security
- Vulnerability in Versions 0.0.13 - 0.0.15 -- playground endpoint allows accessing
* Vulnerability in Versions 0.0.13 - 0.0.15 -- playground endpoint allows accessing
arbitrary files on
server. [Resolved in 0.0.16](https://github.com/langchain-ai/langserve/pull/98).
@@ -86,64 +80,39 @@ Use the `LangChain` CLI to bootstrap a `LangServe` project quickly.
To use the langchain CLI make sure that you have a recent version of `langchain-cli`
installed. You can install it with `pip install -U langchain-cli`.
## Setup
**Note**: We use `poetry` for dependency management. Please follow poetry [doc](https://python-poetry.org/docs/) to learn more about it.
### 1. Create new app using langchain cli command
```sh
langchain app new my-app
```
### 2. Define the runnable in add_routes. Go to server.py and edit
```sh
add_routes(app. NotImplemented)
```
### 3. Use `poetry` to add 3rd party packages (e.g., langchain-openai, langchain-anthropic, langchain-mistral, etc).
```sh
poetry add [package-name] // e.g `poetry add langchain-openai`
```
### 4. Set up relevant env variables. For example,
```sh
export OPENAI_API_KEY="sk-..."
```
### 5. Serve your app
```sh
poetry run langchain serve --port=8100
langchain app new ../path/to/directory
```
## Examples
Get your LangServe instances started quickly with the [examples](https://github.com/langchain-ai/langserve/tree/main/examples)
Get your LangServe instance started quickly with
[LangChain Templates](https://github.com/langchain-ai/langchain/blob/master/templates/README.md).
For more examples, see the templates
[index](https://github.com/langchain-ai/langchain/blob/master/templates/docs/INDEX.md)
or the [examples](https://github.com/langchain-ai/langserve/tree/main/examples)
directory.
| Description | Links |
| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **LLMs** Minimal example that reserves OpenAI and Anthropic chat models. Uses async, supports batching and streaming. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/llm/server.py), [client](https://github.com/langchain-ai/langserve/blob/main/examples/llm/client.ipynb) |
| **Retriever** Simple server that exposes a retriever as a runnable. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/retrieval/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/retrieval/client.ipynb) |
| **Conversational Retriever** A [Conversational Retriever](https://python.langchain.com/docs/expression_language/cookbook/retrieval#conversational-retrieval-chain) exposed via LangServe | [server](https://github.com/langchain-ai/langserve/tree/main/examples/conversational_retrieval_chain/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/conversational_retrieval_chain/client.ipynb) |
| **Agent** without **conversation history** based on [OpenAI tools](https://python.langchain.com/docs/modules/agents/agent_types/openai_functions_agent) | [server](https://github.com/langchain-ai/langserve/tree/main/examples/agent/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/agent/client.ipynb) |
| **Agent** with **conversation history** based on [OpenAI tools](https://python.langchain.com/docs/modules/agents/agent_types/openai_functions_agent) | [server](https://github.com/langchain-ai/langserve/blob/main/examples/agent_with_history/server.py), [client](https://github.com/langchain-ai/langserve/blob/main/examples/agent_with_history/client.ipynb) |
|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **LLMs** Minimal example that reserves OpenAI and Anthropic chat models. Uses async, supports batching and streaming. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/llm/server.py), [client](https://github.com/langchain-ai/langserve/blob/main/examples/llm/client.ipynb) |
| **Retriever** Simple server that exposes a retriever as a runnable. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/retrieval/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/retrieval/client.ipynb) |
| **Conversational Retriever** A [Conversational Retriever](https://python.langchain.com/docs/expression_language/cookbook/retrieval#conversational-retrieval-chain) exposed via LangServe | [server](https://github.com/langchain-ai/langserve/tree/main/examples/conversational_retrieval_chain/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/conversational_retrieval_chain/client.ipynb) |
| **Agent** without **conversation history** based on [OpenAI tools](https://python.langchain.com/docs/modules/agents/agent_types/openai_functions_agent) | [server](https://github.com/langchain-ai/langserve/tree/main/examples/agent/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/agent/client.ipynb) |
| **Agent** with **conversation history** based on [OpenAI tools](https://python.langchain.com/docs/modules/agents/agent_types/openai_functions_agent) | [server](https://github.com/langchain-ai/langserve/blob/main/examples/agent_with_history/server.py), [client](https://github.com/langchain-ai/langserve/blob/main/examples/agent_with_history/client.ipynb) |
| [RunnableWithMessageHistory](https://python.langchain.com/docs/expression_language/how_to/message_history) to implement chat persisted on backend, keyed off a `session_id` supplied by client. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/chat_with_persistence/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/chat_with_persistence/client.ipynb) |
| [RunnableWithMessageHistory](https://python.langchain.com/docs/expression_language/how_to/message_history) to implement chat persisted on backend, keyed off a `conversation_id` supplied by client, and `user_id` (see Auth for implementing `user_id` properly). | [server](https://github.com/langchain-ai/langserve/tree/main/examples/chat_with_persistence_and_user/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/chat_with_persistence_and_user/client.ipynb) |
| [Configurable Runnable](https://python.langchain.com/docs/expression_language/how_to/configure) to create a retriever that supports run time configuration of the index name. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/configurable_retrieval/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/configurable_retrieval/client.ipynb) |
| [Configurable Runnable](https://python.langchain.com/docs/expression_language/how_to/configure) that shows configurable fields and configurable alternatives. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/configurable_chain/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/configurable_chain/client.ipynb) |
| [RunnableWithMessageHistory](https://python.langchain.com/docs/expression_language/how_to/message_history) to implement chat persisted on backend, keyed off a `conversation_id` supplied by client, and `user_id` (see Auth for implementing `user_id` properly). | [server](https://github.com/langchain-ai/langserve/tree/main/examples/chat_with_persistence_and_user/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/chat_with_persistence_and_user/client.ipynb) |
| [Configurable Runnable](https://python.langchain.com/docs/expression_language/how_to/configure) to create a retriever that supports run time configuration of the index name. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/configurable_retrieval/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/configurable_retrieval/client.ipynb) |
| [Configurable Runnable](https://python.langchain.com/docs/expression_language/how_to/configure) that shows configurable fields and configurable alternatives. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/configurable_chain/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/configurable_chain/client.ipynb) |
| **APIHandler** Shows how to use `APIHandler` instead of `add_routes`. This provides more flexibility for developers to define endpoints. Works well with all FastAPI patterns, but takes a bit more effort. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/api_handler_examples/server.py) |
| **LCEL Example** Example that uses LCEL to manipulate a dictionary input. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/passthrough_dict/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/passthrough_dict/client.ipynb) |
| **Auth** with `add_routes`: Simple authentication that can be applied across all endpoints associated with app. (Not useful on its own for implementing per user logic.) | [server](https://github.com/langchain-ai/langserve/tree/main/examples/auth/global_deps/server.py) |
| **Auth** with `add_routes`: Simple authentication mechanism based on path dependencies. (No useful on its own for implementing per user logic.) | [server](https://github.com/langchain-ai/langserve/tree/main/examples/auth/path_dependencies/server.py) |
| **Auth** with `add_routes`: Implement per user logic and auth for endpoints that use per request config modifier. (**Note**: At the moment, does not integrate with OpenAPI docs.) | [server](https://github.com/langchain-ai/langserve/tree/main/examples/auth/per_req_config_modifier/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/auth/per_req_config_modifier/client.ipynb) |
| **Auth** with `APIHandler`: Implement per user logic and auth that shows how to search only within user owned documents. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/auth/api_handler/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/auth/api_handler/client.ipynb) |
| **Widgets** Different widgets that can be used with playground (file upload and chat) | [server](https://github.com/langchain-ai/langserve/tree/main/examples/widgets/chat/tuples/server.py) |
| **Widgets** File upload widget used for LangServe playground. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/file_processing/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/file_processing/client.ipynb) |
| **LCEL Example** Example that uses LCEL to manipulate a dictionary input. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/passthrough_dict/client.ipynb), [client](https://github.com/langchain-ai/langserve/tree/main/examples/passthrough_dict/client.ipynb) |
| **Auth** with `add_routes`: Simple authentication that can be applied across all endpoints associated with app. (Not useful on its own for implementing per user logic.) | [server](https://github.com/langchain-ai/langserve/tree/main/examples/auth/global_deps/server.py) |
| **Auth** with `add_routes`: Simple authentication mechanism based on path dependencies. (No useful on its own for implementing per user logic.) | [server](https://github.com/langchain-ai/langserve/tree/main/examples/auth/path_dependencies/server.py) |
| **Auth** with `add_routes`: Implement per user logic and auth for endpoints that use per request config modifier. (**Note**: At the moment, does not integrate with OpenAPI docs.) | [server](https://github.com/langchain-ai/langserve/tree/main/examples/auth/per_req_config_modifier/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/auth/per_req_config_modifier/client.ipynb) |
| **Auth** with `APIHandler`: Implement per user logic and auth that shows how to search only within user owned documents. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/auth/api_handler/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/auth/api_handler/client.ipynb) |
| **Widgets** Different widgets that can be used with playground (file upload and chat) | [server](https://github.com/langchain-ai/langserve/tree/main/examples/widgets/chat/tuples/server.py) |
| **Widgets** File upload widget used for LangServe playground. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/file_processing/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/file_processing/client.ipynb) |
## Sample Application
@@ -168,17 +137,17 @@ app = FastAPI(
add_routes(
app,
ChatOpenAI(model="gpt-3.5-turbo-0125"),
ChatOpenAI(),
path="/openai",
)
add_routes(
app,
ChatAnthropic(model="claude-3-haiku-20240307"),
ChatAnthropic(),
path="/anthropic",
)
model = ChatAnthropic(model="claude-3-haiku-20240307")
model = ChatAnthropic()
prompt = ChatPromptTemplate.from_template("tell me a joke about {topic}")
add_routes(
app,
@@ -213,9 +182,8 @@ app.add_middleware(
If you've deployed the server above, you can view the generated OpenAPI docs using:
> ⚠️ If using LangServe <= 0.2.0 and pydantic v2, docs will not be generated for _invoke_, _batch_, _stream_,
> _stream_log_. See [Pydantic](#pydantic) section below for more details.
> To resolve please upgrade to LangServe 0.3.0.
> ⚠️ If using pydantic v2, docs will not be generated for *invoke*, *batch*, *stream*,
*stream_log*. See [Pydantic](#pydantic) section below for more details.
```sh
curl localhost:8000/docs
@@ -275,10 +243,10 @@ In TypeScript (requires LangChain.js version 0.0.166 or later):
import { RemoteRunnable } from "@langchain/core/runnables/remote";
const chain = new RemoteRunnable({
url: `http://localhost:8000/joke/`,
url: `http://localhost:8000/joke/`,
});
const result = await chain.invoke({
topic: "cats",
topic: "cats",
});
```
@@ -327,7 +295,7 @@ adds of these endpoints to the server:
- `POST /my_runnable/stream_log` - invoke on a single input and stream the output,
including output of intermediate steps as it's generated
- `POST /my_runnable/astream_events` - invoke on a single input and stream events as they are generated,
including from intermediate steps.
including from intermediate steps.
- `GET /my_runnable/input_schema` - json schema for input to the runnable
- `GET /my_runnable/output_schema` - json schema for output of the runnable
- `GET /my_runnable/config_schema` - json schema for config of the runnable
@@ -366,14 +334,8 @@ runnable and share a link with the configuration:
LangServe also supports a chat-focused playground that opt into and use under `/my_runnable/playground/`.
Unlike the general playground, only certain types of runnables are supported - the runnable's input schema must
be a `dict` with either:
- a single key, and that key's value must be a list of chat messages.
- two keys, one whose value is a list of messages, and the other representing the most recent message.
We recommend you use the first format.
The runnable must also return either an `AIMessage` or a string.
be a `dict` with a single key, and that key's value must be a list of chat messages. The runnable
can return either an `AIMessage` or a string.
To enable it, you must set `playground_type="chat",` when adding your route. Here's an example:
@@ -386,7 +348,7 @@ prompt = ChatPromptTemplate.from_messages(
]
)
chain = prompt | ChatAnthropic(model="claude-2.1")
chain = prompt | ChatAnthropic(model="claude-2")
class InputChat(BaseModel):
@@ -468,6 +430,27 @@ You can deploy to GCP Cloud Run using the following command:
gcloud run deploy [your-service-name] --source . --port 8001 --allow-unauthenticated --region us-central1 --set-env-vars=OPENAI_API_KEY=your_key
```
### Deploy using Infrastructure as Code
#### Pulumi
You can deploy your LangServe server with [Pulumi](https://www.pulumi.com/) using your preferred general purpose language. Below are some quickstart
examples for deploying LangServe to different cloud providers.
These examples are a good starting point for your own infrastructure as code (IaC) projects. You can easily modify them to suit your needs.
| Cloud | Language | Repository | Quickstart |
|-------|------------|-----------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
| AWS | dotnet | https://github.com/pulumi/examples/aws-cs-langserve | [![Deploy](https://get.pulumi.com/new/button.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/aws-cs-langserve) |
| AWS | golang | https://github.com/pulumi/examples/aws-go-langserve | [![Deploy](https://get.pulumi.com/new/button.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/aws-go-langserve) |
| AWS | python | https://github.com/pulumi/examples/aws-py-langserve | [![Deploy](https://get.pulumi.com/new/button.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/aws-py-langserve) |
| AWS | typescript | https://github.com/pulumi/examples/aws-ts-langserve | [![Deploy](https://get.pulumi.com/new/button.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/aws-ts-langserve) |
| AWS | javascript | https://github.com/pulumi/examples/aws-js-langserve | [![Deploy](https://get.pulumi.com/new/button.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/aws-js-langserve) |
### Community Contributed
#### Deploy to Railway
@@ -478,12 +461,10 @@ gcloud run deploy [your-service-name] --source . --port 8001 --allow-unauthentic
## Pydantic
LangServe>=0.3 fully supports Pydantic 2.
If you're using an earlier version of LangServe (<= 0.2), then please note that support for Pydantic 2 has the following limitations:
LangServe provides support for Pydantic 2 with some limitations.
1. OpenAPI docs will not be generated for invoke/batch/stream/stream_log when using
Pydantic V2. Fast API does not support [mixing pydantic v1 and v2 namespaces]. To fix this, use `pip install pydantic==1.10.17`.
Pydantic V2. Fast API does not support [mixing pydantic v1 and v2 namespaces].
2. LangChain uses the v1 namespace in Pydantic v2. Please read
the [following guidelines to ensure compatibility with LangChain](https://github.com/langchain-ai/langchain/discussions/9337)
@@ -500,7 +481,7 @@ and [security](https://fastapi.tiangolo.com/tutorial/security/).
The below examples show how to wire up authentication logic LangServe endpoints using FastAPI primitives.
You are responsible for providing the actual authentication logic, the users table etc.
You are responsible for providing the actual authentication logic, the users table etc.
If you're not sure what you're doing, you could try using an existing solution [Auth0](https://auth0.com/).
@@ -509,11 +490,11 @@ If you're not sure what you're doing, you could try using an existing solution [
If you're using `add_routes`, see
examples [here](https://github.com/langchain-ai/langserve/tree/main/examples/auth).
| Description | Links |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Auth** with `add_routes`: Simple authentication that can be applied across all endpoints associated with app. (Not useful on its own for implementing per user logic.) | [server](https://github.com/langchain-ai/langserve/tree/main/examples/auth/global_deps/server.py) |
| **Auth** with `add_routes`: Simple authentication mechanism based on path dependencies. (No useful on its own for implementing per user logic.) | [server](https://github.com/langchain-ai/langserve/tree/main/examples/auth/path_dependencies/server.py) |
| **Auth** with `add_routes`: Implement per user logic and auth for endpoints that use per request config modifier. (**Note**: At the moment, does not integrate with OpenAPI docs.) | [server](https://github.com/langchain-ai/langserve/tree/main/examples/auth/per_req_config_modifier/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/auth/per_req_config_modifier/client.ipynb) |
| Description | Links |
|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Auth** with `add_routes`: Simple authentication that can be applied across all endpoints associated with app. (Not useful on its own for implementing per user logic.) | [server](https://github.com/langchain-ai/langserve/tree/main/examples/auth/global_deps/server.py) |
| **Auth** with `add_routes`: Simple authentication mechanism based on path dependencies. (No useful on its own for implementing per user logic.) | [server](https://github.com/langchain-ai/langserve/tree/main/examples/auth/path_dependencies/server.py) |
| **Auth** with `add_routes`: Implement per user logic and auth for endpoints that use per request config modifier. (**Note**: At the moment, does not integrate with OpenAPI docs.) | [server](https://github.com/langchain-ai/langserve/tree/main/examples/auth/per_req_config_modifier/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/auth/per_req_config_modifier/client.ipynb) |
Alternatively, you can use FastAPI's [middleware](https://fastapi.tiangolo.com/tutorial/middleware/).
@@ -533,10 +514,10 @@ authorization purposes.
If you feel comfortable with FastAPI and python, you can use LangServe's [APIHandler](https://github.com/langchain-ai/langserve/blob/main/examples/api_handler_examples/server.py).
| Description | Links |
| :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Auth** with `APIHandler`: Implement per user logic and auth that shows how to search only within user owned documents. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/auth/api_handler/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/auth/api_handler/client.ipynb) |
| **APIHandler** Shows how to use `APIHandler` instead of `add_routes`. This provides more flexibility for developers to define endpoints. Works well with all FastAPI patterns, but takes a bit more effort. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/api_handler_examples/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/api_handler_examples/client.ipynb) |
| Description | Links |
|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Auth** with `APIHandler`: Implement per user logic and auth that shows how to search only within user owned documents. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/auth/api_handler/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/auth/api_handler/client.ipynb) |
| **APIHandler** Shows how to use `APIHandler` instead of `add_routes`. This provides more flexibility for developers to define endpoints. Works well with all FastAPI patterns, but takes a bit more effort. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/api_handler_examples/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/api_handler_examples/client.ipynb) |
It's a bit more work, but gives you complete control over the endpoint definitions, so
you can do whatever custom logic you need for auth.
@@ -605,8 +586,8 @@ add_routes(app, runnable)
Inherit from `CustomUserType` if you want the data to de-serialize into a
pydantic model rather than the equivalent dict representation.
At the moment, this type only works _server_ side and is used
to specify desired _decoding_ behavior. If inheriting from this type
At the moment, this type only works *server* side and is used
to specify desired *decoding* behavior. If inheriting from this type
the server will keep the decoded type as a pydantic model instead
of converting it into a dict.
@@ -645,10 +626,10 @@ The playground allows you to define custom widgets for your runnable from the ba
Here are a few examples:
| Description | Links |
| :------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Widgets** Different widgets that can be used with playground (file upload and chat) | [server](https://github.com/langchain-ai/langserve/tree/main/examples/widgets/chat/tuples/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/widgets/client.ipynb) |
| **Widgets** File upload widget used for LangServe playground. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/file_processing/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/file_processing/client.ipynb) |
| Description | Links |
|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Widgets** Different widgets that can be used with playground (file upload and chat) | [server](https://github.com/langchain-ai/langserve/tree/main/examples/widgets/chat/tuples/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/widgets/client.ipynb) |
| **Widgets** File upload widget used for LangServe playground. | [server](https://github.com/langchain-ai/langserve/tree/main/examples/file_processing/server.py), [client](https://github.com/langchain-ai/langserve/tree/main/examples/file_processing/client.ipynb) |
#### Schema
@@ -664,8 +645,8 @@ type NameSpacedPath = { title: string; path: JsonPath }; // Using title to mimic
type OneOfPath = { oneOf: JsonPath[] };
type Widget = {
type: string; // Some well known type (e.g., base64file, chat etc.)
[key: string]: JsonPath | NameSpacedPath | OneOfPath;
type: string // Some well known type (e.g., base64file, chat etc.)
[key: string]: JsonPath | NameSpacedPath | OneOfPath;
};
```
@@ -723,9 +704,9 @@ at the [widget example](https://github.com/langchain-ai/langserve/tree/main/exam
To define a chat widget, make sure that you pass "type": "chat".
- "input" is JSONPath to the field in the _Request_ that has the new input message.
- "output" is JSONPath to the field in the _Response_ that has new output message(s).
- Don't specify these fields if the entire input or output should be used as they are (
* "input" is JSONPath to the field in the *Request* that has the new input message.
* "output" is JSONPath to the field in the *Response* that has new output message(s).
* Don't specify these fields if the entire input or output should be used as they are (
e.g., if the output is a list of chat messages.)
Here's a snippet:
@@ -765,7 +746,6 @@ add_routes(
```
Example widget:
<p align="center">
<img src="https://github.com/langchain-ai/langserve/assets/3205522/a71ff37b-a6a9-4857-a376-cf27c41d3ca4" width="50%"/>
</p>
@@ -780,7 +760,7 @@ prompt = ChatPromptTemplate.from_messages(
]
)
chain = prompt | ChatAnthropic(model="claude-2.1")
chain = prompt | ChatAnthropic(model="claude-2")
class MessageListInput(BaseModel):
+3 -58
View File
@@ -1,61 +1,6 @@
# Security Policy
## Reporting OSS Vulnerabilities
## Reporting a Vulnerability
LangChain is partnered with [huntr by Protect AI](https://huntr.com/) to provide
a bounty program for our open source projects.
Please report security vulnerabilities associated with the LangChain
open source projects by visiting the following link:
[https://huntr.com/bounties/disclose/](https://huntr.com/bounties/disclose/?target=https%3A%2F%2Fgithub.com%2Flangchain-ai%2Flangchain&validSearch=true)
Before reporting a vulnerability, please review:
1) In-Scope Targets and Out-of-Scope Targets below.
2) The [langchain-ai/langchain](https://python.langchain.com/docs/contributing/repo_structure) monorepo structure.
3) LangChain [security guidelines](https://python.langchain.com/docs/security) to
understand what we consider to be a security vulnerability vs. developer
responsibility.
### In-Scope Targets
The following packages and repositories are eligible for bug bounties:
- langchain-core
- langchain (see exceptions)
- langchain-community (see exceptions)
- langgraph
- langserve
### Out of Scope Targets
All out of scope targets defined by huntr as well as:
- **langchain-experimental**: This repository is for experimental code and is not
eligible for bug bounties, bug reports to it will be marked as interesting or waste of
time and published with no bounty attached.
- **tools**: Tools in either langchain or langchain-community are not eligible for bug
bounties. This includes the following directories
- langchain/tools
- langchain-community/tools
- Please review our [security guidelines](https://python.langchain.com/docs/security)
for more details, but generally tools interact with the real world. Developers are
expected to understand the security implications of their code and are responsible
for the security of their tools.
- Code documented with security notices. This will be decided done on a case by
case basis, but likely will not be eligible for a bounty as the code is already
documented with guidelines for developers that should be followed for making their
application secure.
- Any LangSmith related repositories or APIs see below.
## Reporting LangSmith Vulnerabilities
Please report security vulnerabilities associated with LangSmith by email to `security@langchain.dev`.
- LangSmith site: https://smith.langchain.com
- SDK client: https://github.com/langchain-ai/langsmith-sdk
### Other Security Concerns
For any other security concerns, please contact us at `security@langchain.dev`.
Please report security vulnerabilities by email to `security@langchain.dev`.
This email is an alias to a subset of our maintainers, and will ensure the issue is promptly triaged and acted upon as needed.
+7 -7
View File
@@ -20,15 +20,15 @@ Relevant LangChain documentation:
from typing import Any
from fastapi import FastAPI
from langchain.agents import AgentExecutor
from langchain.agents import AgentExecutor, tool
from langchain.agents.format_scratchpad import format_to_openai_functions
from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser
from langchain_community.vectorstores import FAISS
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.tools import tool
from langchain_core.utils.function_calling import format_tool_to_openai_function
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from pydantic import BaseModel
from langchain.chat_models import ChatOpenAI
from langchain.embeddings import OpenAIEmbeddings
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.pydantic_v1 import BaseModel
from langchain.tools.render import format_tool_to_openai_function
from langchain.vectorstores import FAISS
from langserve import add_routes
+5 -5
View File
@@ -47,19 +47,19 @@ Relevant LangChain documentation:
from typing import Any, AsyncIterator, List, Literal
from fastapi import FastAPI
from langchain.agents import AgentExecutor
from langchain.agents import AgentExecutor, tool
from langchain.agents.format_scratchpad.openai_tools import (
format_to_openai_tool_messages,
)
from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.prompts import MessagesPlaceholder
from langchain_community.tools.convert_to_openai import format_tool_to_openai_tool
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableLambda
from langchain_core.tools import tool
from langchain_core.utils.function_calling import format_tool_to_openai_tool
from langchain_openai import ChatOpenAI
from pydantic import BaseModel
from langserve import add_routes
from langserve.pydantic_v1 import BaseModel
prompt = ChatPromptTemplate.from_messages(
[
+5 -5
View File
@@ -26,19 +26,19 @@ Relevant LangChain documentation:
from typing import Any, List, Union
from fastapi import FastAPI
from langchain.agents import AgentExecutor
from langchain.agents import AgentExecutor, tool
from langchain.agents.format_scratchpad.openai_tools import (
format_to_openai_tool_messages,
)
from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser
from langchain.prompts import MessagesPlaceholder
from langchain_community.tools.convert_to_openai import format_tool_to_openai_tool
from langchain_core.messages import AIMessage, FunctionMessage, HumanMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.tools import tool
from langchain_core.utils.function_calling import format_tool_to_openai_tool
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
from langserve import add_routes
from langserve.pydantic_v1 import BaseModel, Field
prompt = ChatPromptTemplate.from_messages(
[
+7 -6
View File
@@ -35,7 +35,8 @@ from typing import Any, List, Optional, Union
from fastapi import Depends, FastAPI, HTTPException, Request, Response, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from langchain_chroma import Chroma
from langchain_community.embeddings.openai import OpenAIEmbeddings
from langchain_community.vectorstores.chroma import Chroma
from langchain_core.documents import Document
from langchain_core.runnables import (
ConfigurableField,
@@ -43,11 +44,10 @@ from langchain_core.runnables import (
RunnableSerializable,
)
from langchain_core.vectorstores import VectorStore
from langchain_openai import OpenAIEmbeddings
from pydantic import BaseModel, ConfigDict
from typing_extensions import Annotated
from langserve import APIHandler
from langserve.pydantic_v1 import BaseModel
class User(BaseModel):
@@ -150,9 +150,10 @@ class PerUserVectorstore(RunnableSerializable):
user_id: Optional[str]
vectorstore: VectorStore
model_config = ConfigDict(
arbitrary_types_allowed=True,
)
class Config:
# Allow arbitrary types since VectorStore is an abstract interface
# and not a pydantic model
arbitrary_types_allowed = True
def _invoke(
self, input: str, config: Optional[RunnableConfig] = None, **kwargs: Any
+5 -3
View File
@@ -13,14 +13,16 @@ See:
* https://fastapi.tiangolo.com/tutorial/security/
"""
from fastapi import Depends, FastAPI, Header, HTTPException
from fastapi import Depends, FastAPI, HTTPException
from fastapi.security import APIKeyHeader
from langchain_core.runnables import RunnableLambda
from typing_extensions import Annotated
from langserve import add_routes
XToken = APIKeyHeader(name="x-token")
async def verify_token(x_token: Annotated[str, Header()]) -> None:
async def verify_token(x_token: str = Depends(XToken)) -> None:
"""Verify the token is valid."""
# Replace this with your actual authentication logic
if x_token != "secret-token":
+5 -3
View File
@@ -13,14 +13,16 @@ To implement proper auth, please see the FastAPI docs:
* https://fastapi.tiangolo.com/tutorial/security/
""" # noqa: E501
from fastapi import Depends, FastAPI, Header, HTTPException
from fastapi import Depends, FastAPI, HTTPException
from fastapi.security import APIKeyHeader
from langchain_core.runnables import RunnableLambda
from typing_extensions import Annotated
from langserve import add_routes
XToken = APIKeyHeader(name="x-token")
async def verify_token(x_token: Annotated[str, Header()]) -> None:
async def verify_token(x_token: str = Depends(XToken)) -> None:
"""Verify the token is valid."""
# Replace this with your actual authentication logic
if x_token != "secret-token":
@@ -36,7 +36,8 @@ from typing import Any, Dict, List, Optional, Union
from fastapi import Depends, FastAPI, HTTPException, Request, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from langchain_chroma import Chroma
from langchain_community.embeddings.openai import OpenAIEmbeddings
from langchain_community.vectorstores.chroma import Chroma
from langchain_core.documents import Document
from langchain_core.runnables import (
ConfigurableField,
@@ -44,11 +45,10 @@ from langchain_core.runnables import (
RunnableSerializable,
)
from langchain_core.vectorstores import VectorStore
from langchain_openai import OpenAIEmbeddings
from pydantic import BaseModel, ConfigDict
from typing_extensions import Annotated
from langserve import add_routes
from langserve.pydantic_v1 import BaseModel
class User(BaseModel):
@@ -147,9 +147,10 @@ class PerUserVectorstore(RunnableSerializable):
user_id: Optional[str]
vectorstore: VectorStore
model_config = ConfigDict(
arbitrary_types_allowed=True,
)
class Config:
# Allow arbitrary types since VectorStore is an abstract interface
# and not a pydantic model
arbitrary_types_allowed = True
def _invoke(
self, input: str, config: Optional[RunnableConfig] = None, **kwargs: Any
@@ -1,56 +0,0 @@
#!/usr/bin/env python
"""Example of a simple chatbot that just passes current conversation
state back and forth between server and client.
"""
from typing import List, Union
from fastapi import FastAPI
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from pydantic import BaseModel, Field
from langserve import add_routes
app = FastAPI(
title="LangChain Server",
version="1.0",
description="Spin up a simple api server using Langchain's Runnable interfaces",
)
# Declare a chain
prompt = ChatPromptTemplate.from_messages(
[
("system", "You are a helpful, professional assistant named Cob."),
MessagesPlaceholder(variable_name="messages"),
("human", "{input}"),
]
)
chain = prompt | ChatAnthropic(model="claude-2.1")
class InputChat(BaseModel):
"""Input for the chat endpoint."""
messages: List[Union[HumanMessage, AIMessage, SystemMessage]] = Field(
...,
description="The chat messages representing the current conversation.",
)
input: str
add_routes(
app,
chain.with_types(input_type=InputChat),
enable_feedback_endpoint=True,
enable_public_trace_link_endpoint=True,
playground_type="chat",
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="localhost", port=8000)
+15 -3
View File
@@ -5,12 +5,13 @@ state back and forth between server and client.
from typing import List, Union
from fastapi import FastAPI
from langchain_anthropic.chat_models import ChatAnthropic
from fastapi.middleware.cors import CORSMiddleware
from langchain.chat_models import ChatAnthropic
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from pydantic import BaseModel, Field
from langserve import add_routes
from langserve.pydantic_v1 import BaseModel, Field
app = FastAPI(
title="LangChain Server",
@@ -19,6 +20,17 @@ app = FastAPI(
)
# Set all CORS enabled origins
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
expose_headers=["*"],
)
# Declare a chain
prompt = ChatPromptTemplate.from_messages(
[
@@ -27,7 +39,7 @@ prompt = ChatPromptTemplate.from_messages(
]
)
chain = prompt | ChatAnthropic(model_name="claude-3-sonnet-20240229")
chain = prompt | ChatAnthropic(model="claude-2")
class InputChat(BaseModel):
+4 -4
View File
@@ -13,14 +13,14 @@ from pathlib import Path
from typing import Callable, Union
from fastapi import FastAPI, HTTPException
from langchain_anthropic import ChatAnthropic
from langchain_community.chat_message_histories import FileChatMessageHistory
from langchain.chat_models import ChatAnthropic
from langchain.memory import FileChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from pydantic import BaseModel, Field
from langserve import add_routes
from langserve.pydantic_v1 import BaseModel, Field
def _is_valid_identifier(value: str) -> bool:
@@ -76,7 +76,7 @@ prompt = ChatPromptTemplate.from_messages(
]
)
chain = prompt | ChatAnthropic(model="claude-2.1")
chain = prompt | ChatAnthropic(model="claude-2")
class InputChat(BaseModel):
@@ -13,13 +13,13 @@ from pathlib import Path
from typing import Any, Callable, Dict, Union
from fastapi import FastAPI, HTTPException, Request
from langchain_community.chat_message_histories import FileChatMessageHistory
from langchain.chat_models import ChatOpenAI
from langchain.memory import FileChatMessageHistory
from langchain.schema.runnable.utils import ConfigurableFieldSpec
from langchain_core import __version__
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import ConfigurableFieldSpec
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAI
from typing_extensions import TypedDict
from langserve import add_routes
@@ -17,11 +17,15 @@ on the LLM and use the stream_log endpoint rather than stream endpoint.
from typing import Any, AsyncIterator, Dict, List, Optional, cast
from fastapi import FastAPI
from langchain.agents import AgentExecutor
from langchain.agents import AgentExecutor, tool
from langchain.agents.format_scratchpad import format_to_openai_functions
from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser
from langchain_community.vectorstores import FAISS
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.chat_models import ChatOpenAI
from langchain.embeddings import OpenAIEmbeddings
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.pydantic_v1 import BaseModel
from langchain.tools.render import format_tool_to_openai_function
from langchain.vectorstores import FAISS
from langchain_core.runnables import (
ConfigurableField,
ConfigurableFieldSpec,
@@ -29,10 +33,6 @@ from langchain_core.runnables import (
RunnableConfig,
)
from langchain_core.runnables.utils import Input, Output
from langchain_core.tools import tool
from langchain_core.utils.function_calling import format_tool_to_openai_function
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from pydantic import BaseModel
from langserve import add_routes
+53 -9
View File
@@ -23,7 +23,14 @@
"tags": []
},
"outputs": [],
"source": ["import requests\n\ninputs = {\"input\": {\"topic\": \"sports\"}}\nresponse = requests.post(\"http://localhost:8000/configurable_temp/invoke\", json=inputs)\n\nresponse.json()"]
"source": [
"import requests\n",
"\n",
"inputs = {\"input\": {\"topic\": \"sports\"}}\n",
"response = requests.post(\"http://localhost:8000/configurable_temp/invoke\", json=inputs)\n",
"\n",
"response.json()"
]
},
{
"cell_type": "markdown",
@@ -39,7 +46,11 @@
"tags": []
},
"outputs": [],
"source": ["from langserve import RemoteRunnable\n\nremote_runnable = RemoteRunnable(\"http://localhost:8000/configurable_temp\")"]
"source": [
"from langserve import RemoteRunnable\n",
"\n",
"remote_runnable = RemoteRunnable(\"http://localhost:8000/configurable_temp\")"
]
},
{
"cell_type": "markdown",
@@ -55,7 +66,9 @@
"tags": []
},
"outputs": [],
"source": ["response = await remote_runnable.ainvoke({\"topic\": \"sports\"})"]
"source": [
"response = await remote_runnable.ainvoke({\"topic\": \"sports\"})"
]
},
{
"cell_type": "markdown",
@@ -71,7 +84,11 @@
"tags": []
},
"outputs": [],
"source": ["from langchain_core.runnables import RunnableConfig\n\nremote_runnable.batch([{\"topic\": \"sports\"}, {\"topic\": \"cars\"}])"]
"source": [
"from langchain.schema.runnable.config import RunnableConfig\n",
"\n",
"remote_runnable.batch([{\"topic\": \"sports\"}, {\"topic\": \"cars\"}])"
]
},
{
"cell_type": "markdown",
@@ -87,7 +104,10 @@
"tags": []
},
"outputs": [],
"source": ["async for chunk in remote_runnable.astream({\"topic\": \"bears, but a bit verbose\"}):\n print(chunk, end=\"\", flush=True)"]
"source": [
"async for chunk in remote_runnable.astream({\"topic\": \"bears, but a bit verbose\"}):\n",
" print(chunk, end=\"\", flush=True)"
]
},
{
"cell_type": "markdown",
@@ -137,7 +157,14 @@
"tags": []
},
"outputs": [],
"source": ["await remote_runnable.ainvoke(\n {\"topic\": \"sports\"},\n config={\n \"configurable\": {\"prompt\": \"how to say {topic} in french\", \"llm\": \"low_temp\"}\n },\n)"]
"source": [
"await remote_runnable.ainvoke(\n",
" {\"topic\": \"sports\"},\n",
" config={\n",
" \"configurable\": {\"prompt\": \"how to say {topic} in french\", \"llm\": \"low_temp\"}\n",
" },\n",
")"
]
},
{
"cell_type": "markdown",
@@ -194,7 +221,13 @@
"execution_count": null,
"metadata": {},
"outputs": [],
"source": ["# The model will fail with an auth error\nunauthenticated_response = requests.post(\n \"http://localhost:8000/auth_from_header/invoke\", json={\"input\": \"hello\"}\n)\nunauthenticated_response.json()"]
"source": [
"# The model will fail with an auth error\n",
"unauthenticated_response = requests.post(\n",
" \"http://localhost:8000/auth_from_header/invoke\", json={\"input\": \"hello\"}\n",
")\n",
"unauthenticated_response.json()"
]
},
{
"cell_type": "markdown",
@@ -211,14 +244,25 @@
"execution_count": null,
"metadata": {},
"outputs": [],
"source": ["# The model will succeed as long as the above shell script is run previously\nimport os\n\ntest_key = os.environ[\"TEST_API_KEY\"]\nauthenticated_response = requests.post(\n \"http://localhost:8000/auth_from_header/invoke\",\n json={\"input\": \"hello\"},\n headers={\"x-api-key\": test_key},\n)\nauthenticated_response.json()"]
"source": [
"# The model will succeed as long as the above shell script is run previously\n",
"import os\n",
"\n",
"test_key = os.environ[\"TEST_API_KEY\"]\n",
"authenticated_response = requests.post(\n",
" \"http://localhost:8000/auth_from_header/invoke\",\n",
" json={\"input\": \"hello\"},\n",
" headers={\"x-api-key\": test_key},\n",
")\n",
"authenticated_response.json()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [""]
"source": []
}
],
"metadata": {
+4 -4
View File
@@ -10,10 +10,10 @@ from typing import Any, Dict
from fastapi import FastAPI, HTTPException, Request
from fastapi.middleware.cors import CORSMiddleware
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import ConfigurableField
from langchain_openai import ChatOpenAI
from langchain.chat_models import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.schema.output_parser import StrOutputParser
from langchain.schema.runnable import ConfigurableField
from langserve import add_routes
+8 -9
View File
@@ -3,21 +3,20 @@
from typing import Any, Iterable, List, Optional, Type
from fastapi import FastAPI
from langchain.schema.vectorstore import VST
from langchain_community.vectorstores import FAISS
from langchain_core.documents import Document
from langchain_core.embeddings import Embeddings
from langchain_core.retrievers import BaseRetriever
from langchain_core.runnables import (
from langchain.embeddings import OpenAIEmbeddings
from langchain.schema import Document
from langchain.schema.embeddings import Embeddings
from langchain.schema.retriever import BaseRetriever
from langchain.schema.runnable import (
ConfigurableFieldSingleOption,
RunnableConfig,
RunnableSerializable,
)
from langchain_core.vectorstores import VectorStore
from langchain_openai import OpenAIEmbeddings
from pydantic import BaseModel, Field
from langchain.schema.vectorstore import VST
from langchain.vectorstores import FAISS, VectorStore
from langserve import add_routes
from langserve.pydantic_v1 import BaseModel, Field
vectorstore1 = FAISS.from_texts(
["cats like fish", "dogs like sticks"], embedding=OpenAIEmbeddings()
@@ -13,14 +13,17 @@ from operator import itemgetter
from typing import List, Tuple
from fastapi import FastAPI
from langchain_community.vectorstores import FAISS
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate, format_document
from langchain_core.runnables import RunnableMap, RunnablePassthrough
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from pydantic import BaseModel, Field
from langchain.chat_models import ChatOpenAI
from langchain.embeddings import OpenAIEmbeddings
from langchain.prompts import ChatPromptTemplate
from langchain.prompts.prompt import PromptTemplate
from langchain.schema import format_document
from langchain.schema.output_parser import StrOutputParser
from langchain.schema.runnable import RunnableMap, RunnablePassthrough
from langchain.vectorstores import FAISS
from langserve import add_routes
from langserve.pydantic_v1 import BaseModel, Field
_TEMPLATE = """Given the following conversation and a follow up question, rephrase the
follow up question to be a standalone question, in its original language.
+4 -4
View File
@@ -15,10 +15,10 @@ allowing one to upload a binary file using the langserve playground UI.
import base64
from fastapi import FastAPI
from langchain_community.document_loaders.parsers.pdf import PDFMinerParser
from langchain_core.document_loaders import Blob
from langchain_core.runnables import RunnableLambda
from pydantic import Field
from langchain.document_loaders.blob_loaders import Blob
from langchain.document_loaders.parsers.pdf import PDFMinerParser
from langchain.pydantic_v1 import Field
from langchain.schema.runnable import RunnableLambda
from langserve import CustomUserType, add_routes
+81 -13
View File
@@ -16,7 +16,9 @@
"tags": []
},
"outputs": [],
"source": ["from langchain_core.prompts import ChatPromptTemplate"]
"source": [
"from langchain.prompts.chat import ChatPromptTemplate"
]
},
{
"cell_type": "code",
@@ -25,7 +27,12 @@
"tags": []
},
"outputs": [],
"source": ["from langserve import RemoteRunnable\n\nopenai_llm = RemoteRunnable(\"http://localhost:8000/openai/\")\nanthropic = RemoteRunnable(\"http://localhost:8000/anthropic/\")"]
"source": [
"from langserve import RemoteRunnable\n",
"\n",
"openai_llm = RemoteRunnable(\"http://localhost:8000/openai/\")\n",
"anthropic = RemoteRunnable(\"http://localhost:8000/anthropic/\")"
]
},
{
"cell_type": "markdown",
@@ -41,7 +48,18 @@
"tags": []
},
"outputs": [],
"source": ["prompt = ChatPromptTemplate.from_messages(\n [\n (\n \"system\",\n \"You are a highly educated person who loves to use big words. \"\n + \"You are also concise. Never answer in more than three sentences.\",\n ),\n (\"human\", \"Tell me about your favorite novel\"),\n ]\n).format_messages()"]
"source": [
"prompt = ChatPromptTemplate.from_messages(\n",
" [\n",
" (\n",
" \"system\",\n",
" \"You are a highly educated person who loves to use big words. \"\n",
" + \"You are also concise. Never answer in more than three sentences.\",\n",
" ),\n",
" (\"human\", \"Tell me about your favorite novel\"),\n",
" ]\n",
").format_messages()"
]
},
{
"cell_type": "markdown",
@@ -68,7 +86,9 @@
"output_type": "execute_result"
}
],
"source": ["anthropic.invoke(prompt)"]
"source": [
"anthropic.invoke(prompt)"
]
},
{
"cell_type": "code",
@@ -77,7 +97,9 @@
"tags": []
},
"outputs": [],
"source": ["openai_llm.invoke(prompt)"]
"source": [
"openai_llm.invoke(prompt)"
]
},
{
"cell_type": "markdown",
@@ -104,7 +126,9 @@
"output_type": "execute_result"
}
],
"source": ["await openai_llm.ainvoke(prompt)"]
"source": [
"await openai_llm.ainvoke(prompt)"
]
},
{
"cell_type": "code",
@@ -125,7 +149,9 @@
"output_type": "execute_result"
}
],
"source": ["anthropic.batch([prompt, prompt])"]
"source": [
"anthropic.batch([prompt, prompt])"
]
},
{
"cell_type": "code",
@@ -146,7 +172,9 @@
"output_type": "execute_result"
}
],
"source": ["await anthropic.abatch([prompt, prompt])"]
"source": [
"await anthropic.abatch([prompt, prompt])"
]
},
{
"cell_type": "markdown",
@@ -170,7 +198,10 @@
]
}
],
"source": ["for chunk in anthropic.stream(prompt):\n print(chunk.content, end=\"\", flush=True)"]
"source": [
"for chunk in anthropic.stream(prompt):\n",
" print(chunk.content, end=\"\", flush=True)"
]
},
{
"cell_type": "code",
@@ -187,14 +218,19 @@
]
}
],
"source": ["async for chunk in anthropic.astream(prompt):\n print(chunk.content, end=\"\", flush=True)"]
"source": [
"async for chunk in anthropic.astream(prompt):\n",
" print(chunk.content, end=\"\", flush=True)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": ["from langchain_core.runnables import RunnablePassthrough"]
"source": [
"from langchain.schema.runnable import RunnablePassthrough"
]
},
{
"cell_type": "code",
@@ -203,7 +239,37 @@
"tags": []
},
"outputs": [],
"source": ["comedian_chain = (\n ChatPromptTemplate.from_messages(\n [\n (\n \"system\",\n \"You are a comedian that sometimes tells funny jokes and other times you just state facts that are not funny. Please either tell a joke or state fact now but only output one.\",\n ),\n ]\n )\n | openai_llm\n)\n\njoke_classifier_chain = (\n ChatPromptTemplate.from_messages(\n [\n (\n \"system\",\n \"Please determine if the joke is funny. Say `funny` if it's funny and `not funny` if not funny. Then repeat the first five words of the joke for reference...\",\n ),\n (\"human\", \"{joke}\"),\n ]\n )\n | anthropic\n)\n\n\nchain = {\"joke\": comedian_chain} | RunnablePassthrough.assign(\n classification=joke_classifier_chain\n)"]
"source": [
"comedian_chain = (\n",
" ChatPromptTemplate.from_messages(\n",
" [\n",
" (\n",
" \"system\",\n",
" \"You are a comedian that sometimes tells funny jokes and other times you just state facts that are not funny. Please either tell a joke or state fact now but only output one.\",\n",
" ),\n",
" ]\n",
" )\n",
" | openai_llm\n",
")\n",
"\n",
"joke_classifier_chain = (\n",
" ChatPromptTemplate.from_messages(\n",
" [\n",
" (\n",
" \"system\",\n",
" \"Please determine if the joke is funny. Say `funny` if it's funny and `not funny` if not funny. Then repeat the first five words of the joke for reference...\",\n",
" ),\n",
" (\"human\", \"{joke}\"),\n",
" ]\n",
" )\n",
" | anthropic\n",
")\n",
"\n",
"\n",
"chain = {\"joke\": comedian_chain} | RunnablePassthrough.assign(\n",
" classification=joke_classifier_chain\n",
")"
]
},
{
"cell_type": "code",
@@ -224,7 +290,9 @@
"output_type": "execute_result"
}
],
"source": ["chain.invoke({})"]
"source": [
"chain.invoke({})"
]
}
],
"metadata": {
+3 -4
View File
@@ -2,8 +2,7 @@
"""Example LangChain server exposes multiple runnables (LLMs in this case)."""
from fastapi import FastAPI
from langchain_anthropic import ChatAnthropic
from langchain_openai import ChatOpenAI
from langchain.chat_models import ChatAnthropic, ChatOpenAI
from langserve import add_routes
@@ -15,12 +14,12 @@ app = FastAPI(
add_routes(
app,
ChatOpenAI(model="gpt-3.5-turbo-0125"),
ChatOpenAI(),
path="/openai",
)
add_routes(
app,
ChatAnthropic(model="claude-3-haiku-20240307"),
ChatAnthropic(),
path="/anthropic",
)
+46 -11
View File
@@ -18,7 +18,9 @@
"tags": []
},
"outputs": [],
"source": ["from langchain_core.prompts import ChatPromptTemplate"]
"source": [
"from langchain.prompts.chat import ChatPromptTemplate"
]
},
{
"cell_type": "code",
@@ -27,7 +29,11 @@
"tags": []
},
"outputs": [],
"source": ["from langserve import RemoteRunnable\n\nmodel = RemoteRunnable(\"http://localhost:8000/ollama/\")"]
"source": [
"from langserve import RemoteRunnable\n",
"\n",
"model = RemoteRunnable(\"http://localhost:8000/ollama/\")"
]
},
{
"cell_type": "markdown",
@@ -43,7 +49,9 @@
"tags": []
},
"outputs": [],
"source": ["prompt = \"Tell me a 3 sentence story about a cat.\""]
"source": [
"prompt = \"Tell me a 3 sentence story about a cat.\""
]
},
{
"cell_type": "code",
@@ -63,7 +71,9 @@
"output_type": "execute_result"
}
],
"source": ["model.invoke(prompt)"]
"source": [
"model.invoke(prompt)"
]
},
{
"cell_type": "code",
@@ -83,7 +93,9 @@
"output_type": "execute_result"
}
],
"source": ["await model.ainvoke(prompt)"]
"source": [
"await model.ainvoke(prompt)"
]
},
{
"cell_type": "markdown",
@@ -119,7 +131,10 @@
"output_type": "execute_result"
}
],
"source": ["%%time\nmodel.batch([prompt, prompt])"]
"source": [
"%%time\n",
"model.batch([prompt, prompt])"
]
},
{
"cell_type": "code",
@@ -137,7 +152,11 @@
]
}
],
"source": ["%%time\nfor _ in range(2):\n model.invoke(prompt)"]
"source": [
"%%time\n",
"for _ in range(2):\n",
" model.invoke(prompt)"
]
},
{
"cell_type": "code",
@@ -158,7 +177,9 @@
"output_type": "execute_result"
}
],
"source": ["await model.abatch([prompt, prompt])"]
"source": [
"await model.abatch([prompt, prompt])"
]
},
{
"cell_type": "markdown",
@@ -185,7 +206,10 @@
]
}
],
"source": ["for chunk in model.stream(prompt):\n print(chunk.content, end=\"|\", flush=True)"]
"source": [
"for chunk in model.stream(prompt):\n",
" print(chunk.content, end=\"|\", flush=True)"
]
},
{
"cell_type": "code",
@@ -203,7 +227,10 @@
]
}
],
"source": ["async for chunk in model.astream(prompt):\n print(chunk.content, end=\"|\", flush=True)"]
"source": [
"async for chunk in model.astream(prompt):\n",
" print(chunk.content, end=\"|\", flush=True)"
]
},
{
"cell_type": "markdown",
@@ -239,7 +266,15 @@
]
}
],
"source": ["i = 0\nasync for event in model.astream_events(prompt, version='v1'):\n print(event)\n if i > 10:\n print('...')\n break\n i += 1"]
"source": [
"i = 0\n",
"async for event in model.astream_events(prompt, version='v1'):\n",
" print(event)\n",
" if i > 10:\n",
" print('...')\n",
" break\n",
" i += 1"
]
}
],
"metadata": {
+23 -6
View File
@@ -16,7 +16,9 @@
"tags": []
},
"outputs": [],
"source": ["from langchain_core.prompts import ChatPromptTemplate"]
"source": [
"from langchain.prompts.chat import ChatPromptTemplate"
]
},
{
"cell_type": "code",
@@ -25,7 +27,11 @@
"tags": []
},
"outputs": [],
"source": ["from langserve import RemoteRunnable\n\nchain = RemoteRunnable(\"http://localhost:8000/v1/\")"]
"source": [
"from langserve import RemoteRunnable\n",
"\n",
"chain = RemoteRunnable(\"http://localhost:8000/v1/\")"
]
},
{
"cell_type": "markdown",
@@ -53,7 +59,9 @@
"output_type": "execute_result"
}
],
"source": ["chain.invoke({'thing': 'apple', 'language': 'italian', 'info': {\"user_id\": 42, \"user_info\": {\"address\": 42}}})"]
"source": [
"chain.invoke({'thing': 'apple', 'language': 'italian', 'info': {\"user_id\": 42, \"user_info\": {\"address\": 42}}})"
]
},
{
"cell_type": "code",
@@ -74,7 +82,10 @@
]
}
],
"source": ["for chunk in chain.stream({'thing': 'apple', 'language': 'italian', 'info': {\"user_id\": 42, \"user_info\": {\"address\": 42}}}):\n print(chunk)"]
"source": [
"for chunk in chain.stream({'thing': 'apple', 'language': 'italian', 'info': {\"user_id\": 42, \"user_info\": {\"address\": 42}}}):\n",
" print(chunk)"
]
},
{
"cell_type": "code",
@@ -83,7 +94,11 @@
"tags": []
},
"outputs": [],
"source": ["from langserve import RemoteRunnable\n\nchain = RemoteRunnable(\"http://localhost:8000/v2/\")"]
"source": [
"from langserve import RemoteRunnable\n",
"\n",
"chain = RemoteRunnable(\"http://localhost:8000/v2/\")"
]
},
{
"cell_type": "code",
@@ -104,7 +119,9 @@
"output_type": "execute_result"
}
],
"source": ["chain.invoke({'thing': 'apple', 'language': 'italian', 'info': {\"user_id\": 42, \"user_info\": {\"address\": 42}}})"]
"source": [
"chain.invoke({'thing': 'apple', 'language': 'italian', 'info': {\"user_id\": 42, \"user_info\": {\"address\": 42}}})"
]
}
],
"metadata": {
+4 -4
View File
@@ -4,9 +4,9 @@
from typing import Any, Callable, Dict, List, Optional, TypedDict
from fastapi import FastAPI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableParallel, RunnablePassthrough
from langchain_openai import ChatOpenAI
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema.runnable import RunnableMap, RunnablePassthrough
from langserve import add_routes
@@ -43,7 +43,7 @@ model = ChatOpenAI()
underlying_chain = prompt | model
wrapped_chain = RunnableParallel(
wrapped_chain = RunnableMap(
{
"output": _create_projection(exclude_keys=["info"]) | underlying_chain,
"info": _create_projection(include_keys=["info"]),
+2 -2
View File
@@ -1,8 +1,8 @@
#!/usr/bin/env python
"""Example LangChain server exposes a retriever."""
from fastapi import FastAPI
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langserve import add_routes
+3 -4
View File
@@ -9,8 +9,7 @@ See more documentation at:
https://fastapi.tiangolo.com/tutorial/bigger-applications/
"""
from fastapi import APIRouter, FastAPI
from langchain_anthropic import ChatAnthropic
from langchain_openai import ChatOpenAI
from langchain.chat_models import ChatAnthropic, ChatOpenAI
from langserve import add_routes
@@ -21,13 +20,13 @@ router = APIRouter(prefix="/models")
# Invocations to this router will appear in trace logs as /models/openai
add_routes(
router,
ChatOpenAI(model="gpt-3.5-turbo-0125"),
ChatOpenAI(),
path="/openai",
)
# Invocations to this router will appear in trace logs as /models/anthropic
add_routes(
router,
ChatAnthropic(model="claude-3-haiku-20240307"),
ChatAnthropic(),
path="/anthropic",
)
+3 -3
View File
@@ -6,13 +6,13 @@ from typing import List, Union
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from langchain_anthropic import ChatAnthropic
from langchain.chat_models import ChatAnthropic
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from pydantic import BaseModel, Field
from langserve import add_routes
from langserve.pydantic_v1 import BaseModel, Field
app = FastAPI(
title="LangChain Server",
@@ -40,7 +40,7 @@ prompt = ChatPromptTemplate.from_messages(
]
)
chain = prompt | ChatAnthropic(model="claude-2.1") | StrOutputParser()
chain = prompt | ChatAnthropic(model="claude-2") | StrOutputParser()
class InputChat(BaseModel):
+7 -6
View File
@@ -6,17 +6,18 @@ from typing import Any, Dict, List, Tuple
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from langchain_community.document_loaders.parsers.pdf import PDFMinerParser
from langchain_core.document_loaders import Blob
from langchain_core.messages import (
from langchain.chat_models.openai import ChatOpenAI
from langchain.document_loaders.blob_loaders import Blob
from langchain.document_loaders.parsers.pdf import PDFMinerParser
from langchain.pydantic_v1 import BaseModel, Field
from langchain.schema.messages import (
AIMessage,
BaseMessage,
FunctionMessage,
HumanMessage,
)
from langchain_core.runnables import RunnableLambda, RunnableParallel
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
from langchain.schema.runnable import RunnableLambda
from langchain_core.runnables import RunnableParallel
from langserve import CustomUserType
from langserve.server import add_routes
-53
View File
@@ -1,53 +0,0 @@
from typing import Any, Dict, Type, cast
from pydantic import BaseModel, ConfigDict, RootModel
from pydantic.json_schema import (
DEFAULT_REF_TEMPLATE,
GenerateJsonSchema,
JsonSchemaMode,
)
def _create_root_model(name: str, type_: Any) -> Type[RootModel]:
"""Create a base class."""
def schema(
cls: Type[BaseModel],
by_alias: bool = True,
ref_template: str = DEFAULT_REF_TEMPLATE,
) -> Dict[str, Any]:
# Complains about schema not being defined in superclass
schema_ = super(cls, cls).schema( # type: ignore[misc]
by_alias=by_alias, ref_template=ref_template
)
schema_["title"] = name
return schema_
def model_json_schema(
cls: Type[BaseModel],
by_alias: bool = True,
ref_template: str = DEFAULT_REF_TEMPLATE,
schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
mode: JsonSchemaMode = "validation",
) -> Dict[str, Any]:
# Complains about model_json_schema not being defined in superclass
schema_ = super(cls, cls).model_json_schema( # type: ignore[misc]
by_alias=by_alias,
ref_template=ref_template,
schema_generator=schema_generator,
mode=mode,
)
schema_["title"] = name
return schema_
base_class_attributes = {
"__annotations__": {"root": type_},
"model_config": ConfigDict(arbitrary_types_allowed=True),
"schema": classmethod(schema),
"model_json_schema": classmethod(model_json_schema),
# Should replace __module__ with caller based on stack frame.
"__module__": "langserve._pydantic",
}
custom_root_type = type(name, (RootModel,), base_class_attributes)
return cast(Type[RootModel], custom_root_type)
+212 -451
View File
File diff suppressed because it is too large Load Diff
+10 -27
View File
@@ -4,16 +4,13 @@ import uuid
from typing import Any, Dict, List, Optional, Sequence
from uuid import UUID
from langchain_core.agents import AgentAction, AgentFinish
from langchain_core.callbacks import AsyncCallbackHandler
from langchain_core.callbacks.manager import (
from langchain.callbacks.base import AsyncCallbackHandler
from langchain.callbacks.manager import (
BaseRunManager,
ahandle_event,
handle_event,
)
from langchain_core.documents import Document
from langchain_core.messages import BaseMessage
from langchain_core.outputs import LLMResult
from langchain.schema import AgentAction, AgentFinish, BaseMessage, Document, LLMResult
from typing_extensions import TypedDict
@@ -48,7 +45,7 @@ class AsyncEventAggregatorCallback(AsyncCallbackHandler):
async def on_chat_model_start(
self,
serialized: Optional[Dict[str, Any]],
serialized: Dict[str, Any],
messages: List[List[BaseMessage]],
*,
run_id: UUID,
@@ -73,7 +70,7 @@ class AsyncEventAggregatorCallback(AsyncCallbackHandler):
async def on_chain_start(
self,
serialized: Optional[Dict[str, Any]],
serialized: Dict[str, Any],
inputs: Dict[str, Any],
*,
run_id: UUID,
@@ -138,7 +135,7 @@ class AsyncEventAggregatorCallback(AsyncCallbackHandler):
async def on_retriever_start(
self,
serialized: Optional[Dict[str, Any]],
serialized: Dict[str, Any],
query: str,
*,
run_id: UUID,
@@ -202,7 +199,7 @@ class AsyncEventAggregatorCallback(AsyncCallbackHandler):
async def on_tool_start(
self,
serialized: Optional[Dict[str, Any]],
serialized: Dict[str, Any],
input_str: str,
*,
run_id: UUID,
@@ -306,7 +303,7 @@ class AsyncEventAggregatorCallback(AsyncCallbackHandler):
async def on_llm_start(
self,
serialized: Optional[Dict[str, Any]],
serialized: Dict[str, Any],
prompts: List[str],
*,
run_id: UUID,
@@ -445,14 +442,7 @@ async def ahandle_callbacks(
if event["parent_run_id"] is None: # How do we make sure it's None!?
event["parent_run_id"] = callback_manager.run_id
event_data = {
key: value
for key, value in event.items()
if key != "type" and key != "kwargs"
}
if "kwargs" in event:
event_data.update(event["kwargs"])
event_data = {key: value for key, value in event.items() if key != "type"}
await ahandle_event(
# Unpacking like this may not work
@@ -474,14 +464,7 @@ def handle_callbacks(
if event["parent_run_id"] is None: # How do we make sure it's None!?
event["parent_run_id"] = callback_manager.run_id
event_data = {
key: value
for key, value in event.items()
if key != "type" and key != "kwargs"
}
if "kwargs" in event:
event_data.update(event["kwargs"])
event_data = {key: value for key, value in event.items() if key != "type"}
handle_event(
# Unpacking like this may not work
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3 -20
View File
@@ -5,35 +5,18 @@
<link rel="icon" href="/____LANGSERVE_BASE_URL/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Chat Playground</title>
<script type="module" crossorigin src="/____LANGSERVE_BASE_URL/assets/index-53ad47d4.js"></script>
<link rel="stylesheet" href="/____LANGSERVE_BASE_URL/assets/index-434ff580.css">
<script type="module" crossorigin src="/____LANGSERVE_BASE_URL/assets/index-9018861a.js"></script>
<link rel="stylesheet" href="/____LANGSERVE_BASE_URL/assets/index-b47ed17e.css">
</head>
<body>
<div id="root"></div>
<script>
try {
window.CONFIG_SCHEMA = ____LANGSERVE_CONFIG_SCHEMA;
} catch (e) {
// pass
}
try {
window.INPUT_SCHEMA = ____LANGSERVE_INPUT_SCHEMA;
} catch (e) {
// pass
}
try {
window.OUTPUT_SCHEMA = ____LANGSERVE_OUTPUT_SCHEMA;
} catch (e) {
// pass
}
try {
window.FEEDBACK_ENABLED = ____LANGSERVE_FEEDBACK_ENABLED;
} catch (e) {
// pass
}
try {
window.PUBLIC_TRACE_LINK_ENABLED = ____LANGSERVE_PUBLIC_TRACE_LINK_ENABLED;
} catch (e) {
} catch (error) {
// pass
}
</script>
+1 -18
View File
@@ -11,27 +11,10 @@
<script>
try {
window.CONFIG_SCHEMA = ____LANGSERVE_CONFIG_SCHEMA;
} catch (e) {
// pass
}
try {
window.INPUT_SCHEMA = ____LANGSERVE_INPUT_SCHEMA;
} catch (e) {
// pass
}
try {
window.OUTPUT_SCHEMA = ____LANGSERVE_OUTPUT_SCHEMA;
} catch (e) {
// pass
}
try {
window.FEEDBACK_ENABLED = ____LANGSERVE_FEEDBACK_ENABLED;
} catch (e) {
// pass
}
try {
window.PUBLIC_TRACE_LINK_ENABLED = ____LANGSERVE_PUBLIC_TRACE_LINK_ENABLED;
} catch (e) {
} catch (error) {
// pass
}
</script>
+2 -9
View File
@@ -22,7 +22,7 @@
"clsx": "^2.0.0",
"dayjs": "^1.11.10",
"fast-json-patch": "^3.1.1",
"lodash": "^4.18.1",
"lodash": "^4.17.21",
"lz-string": "^1.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -46,14 +46,7 @@
"postcss": "^8.4.31",
"tailwindcss": "^3.3.3",
"typescript": "^5.0.2",
"vite": "^6.4.2",
"vite": "^4.4.5",
"vite-plugin-svgr": "^4.1.0"
},
"resolutions": {
"braces": "^3.0.3",
"cross-spawn": "^7.0.5",
"rollup": "^3.30.0",
"ajv": "^8.18.0",
"esbuild": "0.25.0"
}
}
+13 -48
View File
@@ -2,67 +2,32 @@ import "./App.css";
import { ChatWindow } from "./components/ChatWindow";
import { AppCallbackContext, useAppStreamCallbacks } from "./useStreamCallback";
import { useInputSchema, useOutputSchema } from "./useSchemas";
import { useInputSchema } from "./useSchemas";
import { useStreamLog } from "./useStreamLog";
import { resolveApiUrl } from "./utils/url";
export function App() {
const { context, callbacks } = useAppStreamCallbacks();
const { startStream, stopStream } = useStreamLog(callbacks);
const inputSchema = useInputSchema({});
const outputSchema = useOutputSchema({});
const inputProps = inputSchema?.data?.schema?.properties;
const outputDataSchema = outputSchema?.data?.schema;
const isLoading = inputProps === undefined || outputDataSchema === undefined;
const isLoading = inputProps === undefined;
const inputKeys = Object.keys(inputProps ?? {});
const inputSchemaSupported = (
inputKeys.length === 1 &&
inputProps?.[inputKeys[0]].type === "array"
) || (
inputKeys.length === 2 && (
(
inputProps?.[inputKeys[0]].type === "array" ||
inputProps?.[inputKeys[1]].type === "string"
) || (
inputProps?.[inputKeys[0]].type === "string" ||
inputProps?.[inputKeys[1]].type === "array"
)
)
);
const outputSchemaSupported = (
outputDataSchema?.anyOf?.find((option) => option.properties?.type?.enum?.includes("ai")) ||
outputDataSchema?.oneOf?.find((option) => option.properties?.type?.enum?.includes("ai")) ||
outputDataSchema?.type === "string"
);
const isSupported = isLoading || (inputSchemaSupported && outputSchemaSupported);
const isSupported = isLoading || (inputKeys.length === 1 && inputProps[inputKeys[0]].type === "array");
return (
<div className="flex items-center flex-col text-ls-black bg-background">
<AppCallbackContext.Provider value={context}>
{isSupported
? <ChatWindow
{isSupported
? <ChatWindow
startStream={startStream}
stopStream={stopStream}
messagesInputKey={inputProps?.[inputKeys[0]].type === "array" ? inputKeys[0] : inputKeys[1]}
inputKey={inputProps?.[inputKeys[0]].type === "string" ? inputKeys[0] : inputKeys[1]}
></ChatWindow>
: <div className="h-[100vh] w-[100vw] flex justify-center items-center text-xl p-16">
<span>
The chat playground is only supported for chains that take one of the following as input:
<ul className="mt-8 list-disc ml-6">
<li>
a dict with a single key containing a list of messages
</li>
<li>
a dict with two keys: one a string input, one an list of messages
</li>
</ul>
<br />
and which return either an <code>AIMessage</code> or a string.
<br />
<br />
You can test this chain in the default LangServe playground instead.
<br />
<br />
To use the default playground, set <code>playground_type="default"</code> when adding the route in your backend.
inputKey={inputKeys[0]}
></ChatWindow>
: <div className="h-[100vh] w-[100vw] flex justify-center items-center text-xl">
<span className="text-center">
The chat playground is only supported for chains that take a single array of messages as input.
<br/>
You can test this chain in the standard <a href={resolveApiUrl("/playground").toString()}>LangServe playground</a>.
</span>
</div>}
</AppCallbackContext.Provider>
@@ -33,10 +33,9 @@ export function isAIMessage(x: unknown): x is AIMessage {
export function ChatWindow(props: {
startStream: (input: unknown, config: unknown) => Promise<void>;
stopStream: (() => void) | undefined;
messagesInputKey: string;
inputKey?: string;
inputKey: string;
}) {
const { startStream, messagesInputKey, inputKey } = props;
const { startStream, inputKey } = props;
const [currentInputValue, setCurrentInputValue] = useState("");
const [isLoading, setIsLoading] = useState(false);
@@ -59,14 +58,7 @@ export function ChatWindow(props: {
setMessages(newMessages);
setCurrentInputValue("");
// TODO: Add config schema support
if (inputKey === undefined) {
startStream({ [messagesInputKey]: newMessages }, {});
} else {
startStream({
[messagesInputKey]: newMessages.slice(0, -1),
[inputKey]: newMessages[newMessages.length - 1].content
}, {});
}
startStream({ [inputKey]: newMessages }, {});
};
const regenerateMessages = () => {
@@ -75,14 +67,7 @@ export function ChatWindow(props: {
}
setIsLoading(true);
// TODO: Add config schema support
if (inputKey === undefined) {
startStream({ [messagesInputKey]: messages }, {});
} else {
startStream({
[messagesInputKey]: messages.slice(0, -1),
[inputKey]: messages[messages.length - 1].content
}, {});
}
startStream({ [inputKey]: messages }, {});
};
useStreamCallback("onStart", () => {
+2 -35
View File
@@ -1,7 +1,7 @@
import { JsonSchema } from "@jsonforms/core";
import { compressToEncodedURIComponent } from "lz-string";
import { resolveApiUrl } from "./utils/url";
import { simplifySchema } from "./utils/simplifySchema";
import { JsonSchema } from "@jsonforms/core";
import { compressToEncodedURIComponent } from "lz-string";
import useSWR from "swr";
import defaults from "./utils/defaults";
@@ -13,8 +13,6 @@ declare global {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
INPUT_SCHEMA?: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
OUTPUT_SCHEMA?: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
FEEDBACK_ENABLED?: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
PUBLIC_TRACE_LINK_ENABLED?: any;
@@ -98,34 +96,3 @@ export function useInputSchema(configData?: unknown) {
{ keepPreviousData: true }
);
}
export function useOutputSchema(configData?: unknown) {
return useSWR(
["/output_schema", configData],
async ([, configData]) => {
// TODO: this won't work if we're already seeing a prefixed URL
const prefix = configData
? `/c/${compressToEncodedURIComponent(JSON.stringify(configData))}`
: "";
let schema: JsonSchema | null = null;
if (!prefix && !import.meta.env.DEV && window.OUTPUT_SCHEMA) {
schema = await simplifySchema(window.OUTPUT_SCHEMA);
} else {
const response = await fetch(resolveApiUrl(`${prefix}/output_schema`));
if (!response.ok) throw new Error(await response.text());
const json = await response.json();
schema = await simplifySchema(json);
}
if (schema == null) return null;
return {
schema,
defaults: defaults(schema),
};
},
{ keepPreviousData: true }
);
}
+1 -1
View File
@@ -8,7 +8,7 @@ export default defineConfig({
plugins: [svgr(), react()],
server: {
proxy: {
"^/____LANGSERVE_BASE_URL.*/(config_schema|input_schema|output_schema|stream_log|feedback|public_trace_link)(/[a-zA-Z0-9-]*)?$": {
"^/____LANGSERVE_BASE_URL.*/(config_schema|input_schema|stream_log|feedback|public_trace_link)(/[a-zA-Z0-9-]*)?$": {
target: "http://127.0.0.1:8000",
changeOrigin: true,
rewrite: (path) => path.replace("/____LANGSERVE_BASE_URL", ""),
+239 -291
View File
@@ -28,15 +28,6 @@
"@babel/highlight" "^7.22.13"
chalk "^2.4.2"
"@babel/code-frame@^7.28.6":
version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c"
integrity sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==
dependencies:
"@babel/helper-validator-identifier" "^7.28.5"
js-tokens "^4.0.0"
picocolors "^1.1.1"
"@babel/compat-data@^7.22.9":
version "7.23.2"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.2.tgz#6a12ced93455827037bfb5ed8492820d60fc32cc"
@@ -146,33 +137,24 @@
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f"
integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==
"@babel/helper-string-parser@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
"@babel/helper-validator-identifier@^7.22.20":
version "7.22.20"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
"@babel/helper-validator-identifier@^7.28.5":
version "7.28.5"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4"
integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==
"@babel/helper-validator-option@^7.22.15":
version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040"
integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==
"@babel/helpers@^7.23.2":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.6.tgz#fca903a313ae675617936e8998b814c415cbf5d7"
integrity sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==
version "7.23.2"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.2.tgz#2832549a6e37d484286e15ba36a5330483cac767"
integrity sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==
dependencies:
"@babel/template" "^7.28.6"
"@babel/types" "^7.28.6"
"@babel/template" "^7.22.15"
"@babel/traverse" "^7.23.2"
"@babel/types" "^7.23.0"
"@babel/highlight@^7.22.13":
version "7.22.20"
@@ -188,13 +170,6 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719"
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
"@babel/parser@^7.28.6":
version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.0.tgz#669ef345add7d057e92b7ed15f0bac07611831b6"
integrity sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==
dependencies:
"@babel/types" "^7.29.0"
"@babel/plugin-transform-react-jsx-self@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.22.5.tgz#ca2fdc11bc20d4d46de01137318b13d04e481d8e"
@@ -210,9 +185,11 @@
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.18.3", "@babel/runtime@^7.23.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.6.tgz#d267a43cb1836dc4d182cce93ae75ba954ef6d2b"
integrity sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==
version "7.23.2"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885"
integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==
dependencies:
regenerator-runtime "^0.14.0"
"@babel/template@^7.22.15":
version "7.22.15"
@@ -223,15 +200,6 @@
"@babel/parser" "^7.22.15"
"@babel/types" "^7.22.15"
"@babel/template@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57"
integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==
dependencies:
"@babel/code-frame" "^7.28.6"
"@babel/parser" "^7.28.6"
"@babel/types" "^7.28.6"
"@babel/traverse@^7.23.2":
version "7.23.2"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8"
@@ -257,14 +225,6 @@
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"
"@babel/types@^7.28.6", "@babel/types@^7.29.0":
version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7"
integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==
dependencies:
"@babel/helper-string-parser" "^7.27.1"
"@babel/helper-validator-identifier" "^7.28.5"
"@emotion/babel-plugin@^11.11.0":
version "11.11.0"
resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz#c2d872b6a7767a9d176d007f5b31f7d504bb5d6c"
@@ -372,130 +332,115 @@
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6"
integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==
"@esbuild/aix-ppc64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz#499600c5e1757a524990d5d92601f0ac3ce87f64"
integrity sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==
"@esbuild/android-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622"
integrity sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==
"@esbuild/android-arm64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz#b9b8231561a1dfb94eb31f4ee056b92a985c324f"
integrity sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==
"@esbuild/android-arm@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682"
integrity sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==
"@esbuild/android-arm@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.0.tgz#ca6e7888942505f13e88ac9f5f7d2a72f9facd2b"
integrity sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==
"@esbuild/android-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2"
integrity sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==
"@esbuild/android-x64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.0.tgz#e765ea753bac442dfc9cb53652ce8bd39d33e163"
integrity sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==
"@esbuild/darwin-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1"
integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==
"@esbuild/darwin-arm64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz#fa394164b0d89d4fdc3a8a21989af70ef579fa2c"
integrity sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==
"@esbuild/darwin-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d"
integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==
"@esbuild/darwin-x64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz#91979d98d30ba6e7d69b22c617cc82bdad60e47a"
integrity sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==
"@esbuild/freebsd-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54"
integrity sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==
"@esbuild/freebsd-arm64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz#b97e97073310736b430a07b099d837084b85e9ce"
integrity sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==
"@esbuild/freebsd-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e"
integrity sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==
"@esbuild/freebsd-x64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz#f3b694d0da61d9910ec7deff794d444cfbf3b6e7"
integrity sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==
"@esbuild/linux-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0"
integrity sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==
"@esbuild/linux-arm64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz#f921f699f162f332036d5657cad9036f7a993f73"
integrity sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==
"@esbuild/linux-arm@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0"
integrity sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==
"@esbuild/linux-arm@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz#cc49305b3c6da317c900688995a4050e6cc91ca3"
integrity sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==
"@esbuild/linux-ia32@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7"
integrity sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==
"@esbuild/linux-ia32@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz#3e0736fcfab16cff042dec806247e2c76e109e19"
integrity sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==
"@esbuild/linux-loong64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d"
integrity sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==
"@esbuild/linux-loong64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz#ea2bf730883cddb9dfb85124232b5a875b8020c7"
integrity sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==
"@esbuild/linux-mips64el@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231"
integrity sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==
"@esbuild/linux-mips64el@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz#4cababb14eede09248980a2d2d8b966464294ff1"
integrity sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==
"@esbuild/linux-ppc64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb"
integrity sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==
"@esbuild/linux-ppc64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz#8860a4609914c065373a77242e985179658e1951"
integrity sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==
"@esbuild/linux-riscv64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6"
integrity sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==
"@esbuild/linux-riscv64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz#baf26e20bb2d38cfb86ee282dff840c04f4ed987"
integrity sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==
"@esbuild/linux-s390x@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071"
integrity sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==
"@esbuild/linux-s390x@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz#8323afc0d6cb1b6dc6e9fd21efd9e1542c3640a4"
integrity sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==
"@esbuild/linux-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz#c7690b3417af318a9b6f96df3031a8865176d338"
integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==
"@esbuild/linux-x64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz#08fcf60cb400ed2382e9f8e0f5590bac8810469a"
integrity sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==
"@esbuild/netbsd-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1"
integrity sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==
"@esbuild/netbsd-arm64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz#935c6c74e20f7224918fbe2e6c6fe865b6c6ea5b"
integrity sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==
"@esbuild/openbsd-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae"
integrity sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==
"@esbuild/netbsd-x64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz#414677cef66d16c5a4d210751eb2881bb9c1b62b"
integrity sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==
"@esbuild/sunos-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d"
integrity sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==
"@esbuild/openbsd-arm64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz#8fd55a4d08d25cdc572844f13c88d678c84d13f7"
integrity sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==
"@esbuild/win32-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9"
integrity sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==
"@esbuild/openbsd-x64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz#0c48ddb1494bbc2d6bcbaa1429a7f465fa1dedde"
integrity sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==
"@esbuild/win32-ia32@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102"
integrity sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==
"@esbuild/sunos-x64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz#86ff9075d77962b60dd26203d7352f92684c8c92"
integrity sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==
"@esbuild/win32-arm64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz#849c62327c3229467f5b5cd681bf50588442e96c"
integrity sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==
"@esbuild/win32-ia32@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz#f62eb480cd7cca088cb65bb46a6db25b725dc079"
integrity sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==
"@esbuild/win32-x64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz#c8e119a30a7c8d60b9d2e22d2073722dde3b710b"
integrity sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==
"@esbuild/win32-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d"
integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
version "4.4.0"
@@ -1253,15 +1198,25 @@ ajv-formats@^2.1.0:
dependencies:
ajv "^8.0.0"
ajv@^6.12.4, ajv@^8.0.0, ajv@^8.18.0, ajv@^8.6.1:
version "8.18.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.18.0.tgz#8864186b6738d003eb3a933172bb3833e10cefbc"
integrity sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==
ajv@^6.12.4:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
dependencies:
fast-deep-equal "^3.1.3"
fast-uri "^3.0.1"
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ajv@^8.0.0, ajv@^8.6.1:
version "8.12.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1"
integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==
dependencies:
fast-deep-equal "^3.1.1"
json-schema-traverse "^1.0.0"
require-from-string "^2.0.2"
uri-js "^4.2.2"
ansi-regex@^5.0.1:
version "5.0.1"
@@ -1349,19 +1304,19 @@ binary-extensions@^2.0.0:
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
brace-expansion@^1.1.7:
version "1.1.13"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.13.tgz#d37875c01dc9eff988dd49d112a57cb67b54efe6"
integrity sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
braces@^3.0.3, braces@~3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
braces@^3.0.2, braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.1.1"
fill-range "^7.0.1"
browserslist@^4.21.10, browserslist@^4.21.9:
version "4.22.1"
@@ -1505,10 +1460,10 @@ cosmiconfig@^8.1.3:
parse-json "^5.2.0"
path-type "^4.0.0"
cross-spawn@^7.0.2, cross-spawn@^7.0.5:
version "7.0.6"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
cross-spawn@^7.0.2:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
dependencies:
path-key "^3.1.0"
shebang-command "^2.0.0"
@@ -1603,36 +1558,33 @@ error-ex@^1.3.1:
dependencies:
is-arrayish "^0.2.1"
esbuild@0.25.0, esbuild@^0.25.0:
version "0.25.0"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.0.tgz#0de1787a77206c5a79eeb634a623d39b5006ce92"
integrity sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==
esbuild@^0.18.10:
version "0.18.20"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.18.20.tgz#4709f5a34801b43b799ab7d6d82f7284a9b7a7a6"
integrity sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==
optionalDependencies:
"@esbuild/aix-ppc64" "0.25.0"
"@esbuild/android-arm" "0.25.0"
"@esbuild/android-arm64" "0.25.0"
"@esbuild/android-x64" "0.25.0"
"@esbuild/darwin-arm64" "0.25.0"
"@esbuild/darwin-x64" "0.25.0"
"@esbuild/freebsd-arm64" "0.25.0"
"@esbuild/freebsd-x64" "0.25.0"
"@esbuild/linux-arm" "0.25.0"
"@esbuild/linux-arm64" "0.25.0"
"@esbuild/linux-ia32" "0.25.0"
"@esbuild/linux-loong64" "0.25.0"
"@esbuild/linux-mips64el" "0.25.0"
"@esbuild/linux-ppc64" "0.25.0"
"@esbuild/linux-riscv64" "0.25.0"
"@esbuild/linux-s390x" "0.25.0"
"@esbuild/linux-x64" "0.25.0"
"@esbuild/netbsd-arm64" "0.25.0"
"@esbuild/netbsd-x64" "0.25.0"
"@esbuild/openbsd-arm64" "0.25.0"
"@esbuild/openbsd-x64" "0.25.0"
"@esbuild/sunos-x64" "0.25.0"
"@esbuild/win32-arm64" "0.25.0"
"@esbuild/win32-ia32" "0.25.0"
"@esbuild/win32-x64" "0.25.0"
"@esbuild/android-arm" "0.18.20"
"@esbuild/android-arm64" "0.18.20"
"@esbuild/android-x64" "0.18.20"
"@esbuild/darwin-arm64" "0.18.20"
"@esbuild/darwin-x64" "0.18.20"
"@esbuild/freebsd-arm64" "0.18.20"
"@esbuild/freebsd-x64" "0.18.20"
"@esbuild/linux-arm" "0.18.20"
"@esbuild/linux-arm64" "0.18.20"
"@esbuild/linux-ia32" "0.18.20"
"@esbuild/linux-loong64" "0.18.20"
"@esbuild/linux-mips64el" "0.18.20"
"@esbuild/linux-ppc64" "0.18.20"
"@esbuild/linux-riscv64" "0.18.20"
"@esbuild/linux-s390x" "0.18.20"
"@esbuild/linux-x64" "0.18.20"
"@esbuild/netbsd-x64" "0.18.20"
"@esbuild/openbsd-x64" "0.18.20"
"@esbuild/sunos-x64" "0.18.20"
"@esbuild/win32-arm64" "0.18.20"
"@esbuild/win32-ia32" "0.18.20"
"@esbuild/win32-x64" "0.18.20"
escalade@^3.1.1:
version "3.1.1"
@@ -1753,7 +1705,7 @@ esutils@^2.0.2:
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
fast-deep-equal@^3.1.3:
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
@@ -1774,16 +1726,16 @@ fast-json-patch@^3.1.1:
resolved "https://registry.yarnpkg.com/fast-json-patch/-/fast-json-patch-3.1.1.tgz#85064ea1b1ebf97a3f7ad01e23f9337e72c66947"
integrity sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==
fast-json-stable-stringify@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
fast-levenshtein@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
fast-uri@^3.0.1:
version "3.1.0"
resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.0.tgz#66eecff6c764c0df9b762e62ca7edcfb53b4edfa"
integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==
fastq@^1.6.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a"
@@ -1791,11 +1743,6 @@ fastq@^1.6.0:
dependencies:
reusify "^1.0.4"
fdir@^6.4.4, fdir@^6.5.0:
version "6.5.0"
resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350"
integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==
file-entry-cache@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
@@ -1803,10 +1750,10 @@ file-entry-cache@^6.0.1:
dependencies:
flat-cache "^3.0.4"
fill-range@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
dependencies:
to-regex-range "^5.0.1"
@@ -1833,9 +1780,9 @@ flat-cache@^3.0.4:
rimraf "^3.0.2"
flatted@^3.2.9:
version "3.4.2"
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.2.tgz#f5c23c107f0f37de8dbdf24f13722b3b98d52726"
integrity sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==
version "3.2.9"
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf"
integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==
fraction.js@^4.3.6:
version "4.3.7"
@@ -1847,7 +1794,7 @@ fs.realpath@^1.0.0:
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
fsevents@~2.3.2, fsevents@~2.3.3:
fsevents@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
@@ -2046,9 +1993,9 @@ jiti@^1.18.2:
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
js-yaml@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b"
integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
dependencies:
argparse "^2.0.1"
@@ -2067,6 +2014,11 @@ json-parse-even-better-errors@^2.3.0:
resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
json-schema-traverse@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
@@ -2119,10 +2071,10 @@ lodash.merge@^4.6.2:
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash@^4.17.21, lodash@^4.18.1:
version "4.18.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c"
integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==
lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0"
@@ -2163,17 +2115,17 @@ merge2@^1.3.0, merge2@^1.4.1:
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
micromatch@^4.0.4, micromatch@^4.0.5:
version "4.0.8"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
version "4.0.5"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
dependencies:
braces "^3.0.3"
braces "^3.0.2"
picomatch "^2.3.1"
minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.5"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e"
integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies:
brace-expansion "^1.1.7"
@@ -2191,10 +2143,10 @@ mz@^2.7.0:
object-assign "^4.0.1"
thenify-all "^1.0.0"
nanoid@^3.3.11:
version "3.3.11"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b"
integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
nanoid@^3.3.6:
version "3.3.6"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==
natural-compare@^1.4.0:
version "1.4.0"
@@ -2314,20 +2266,10 @@ picocolors@^1.0.0:
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
picocolors@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
version "2.3.2"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.2.tgz#5a942915e26b372dc0f0e6753149a16e6b1c5601"
integrity sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==
picomatch@^4.0.2, picomatch@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589"
integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
pify@^2.3.0:
version "2.3.0"
@@ -2383,14 +2325,14 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0:
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
postcss@^8.4.23, postcss@^8.4.31, postcss@^8.5.3:
version "8.5.9"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.9.tgz#f6ee9e0b94f0f19c97d2f172bfbd7fc71fe1cca4"
integrity sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==
postcss@^8.4.23, postcss@^8.4.27, postcss@^8.4.31:
version "8.4.31"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d"
integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==
dependencies:
nanoid "^3.3.11"
picocolors "^1.1.1"
source-map-js "^1.2.1"
nanoid "^3.3.6"
picocolors "^1.0.0"
source-map-js "^1.0.2"
prelude-ls@^1.2.1:
version "1.2.1"
@@ -2406,6 +2348,11 @@ prop-types@^15.6.2, prop-types@^15.8.1:
object-assign "^4.1.1"
react-is "^16.13.1"
punycode@^2.1.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
queue-microtask@^1.2.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
@@ -2500,6 +2447,11 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
regenerator-runtime@^0.14.0:
version "0.14.0"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==
require-from-string@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
@@ -2531,10 +2483,10 @@ rimraf@^3.0.2:
dependencies:
glob "^7.1.3"
rollup@^3.30.0, rollup@^4.34.9:
version "3.30.0"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.30.0.tgz#3fa506fee2c5ba9d540a38da87067376cd55966d"
integrity sha512-kQvGasUgN+AlWGliFn2POSajRQEsULVYFGTvOZmK06d7vCD+YhZztt70kGk3qaeAXeWYL5eO7zx+rAubBc55eA==
rollup@^3.27.1:
version "3.29.4"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.4.tgz#4d70c0f9834146df8705bfb69a9a19c9e1109981"
integrity sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==
optionalDependencies:
fsevents "~2.3.2"
@@ -2589,10 +2541,10 @@ snake-case@^3.0.4:
dot-case "^3.0.4"
tslib "^2.0.3"
source-map-js@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
source-map-js@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
source-map@^0.5.7:
version "0.5.7"
@@ -2713,14 +2665,6 @@ thenify-all@^1.0.0:
dependencies:
any-promise "^1.0.0"
tinyglobby@^0.2.13:
version "0.2.16"
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.16.tgz#1c3b7eb953fce42b226bc5a1ee06428281aff3d6"
integrity sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==
dependencies:
fdir "^6.5.0"
picomatch "^4.0.4"
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
@@ -2773,6 +2717,13 @@ update-browserslist-db@^1.0.13:
escalade "^3.1.1"
picocolors "^1.0.0"
uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
dependencies:
punycode "^2.1.0"
use-callback-ref@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.0.tgz#772199899b9c9a50526fedc4993fc7fa1f7e32d5"
@@ -2819,19 +2770,16 @@ vite-plugin-svgr@^4.1.0:
"@svgr/core" "^8.1.0"
"@svgr/plugin-jsx" "^8.1.0"
vite@^6.4.2:
version "6.4.2"
resolved "https://registry.yarnpkg.com/vite/-/vite-6.4.2.tgz#a4e548ca3a90ca9f3724582cab35e1ba15efc6f2"
integrity sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==
vite@^4.4.5:
version "4.5.0"
resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.0.tgz#ec406295b4167ac3bc23e26f9c8ff559287cff26"
integrity sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==
dependencies:
esbuild "^0.25.0"
fdir "^6.4.4"
picomatch "^4.0.2"
postcss "^8.5.3"
rollup "^4.34.9"
tinyglobby "^0.2.13"
esbuild "^0.18.10"
postcss "^8.4.27"
rollup "^3.27.1"
optionalDependencies:
fsevents "~2.3.3"
fsevents "~2.3.2"
which@^2.0.1:
version "2.0.2"
@@ -2856,14 +2804,14 @@ yallist@^4.0.0:
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
yaml@^1.10.0:
version "1.10.3"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.3.tgz#76e407ed95c42684fb8e14641e5de62fe65bbcb3"
integrity sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==
version "1.10.2"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
yaml@^2.1.1:
version "2.8.3"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.3.tgz#a0d6bd2efb3dd03c59370223701834e60409bd7d"
integrity sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==
version "2.3.3"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.3.tgz#01f6d18ef036446340007db8e016810e5d64aad9"
integrity sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==
yocto-queue@^0.1.0:
version "0.1.0"
+70 -75
View File
@@ -8,7 +8,6 @@ import weakref
from concurrent.futures import ThreadPoolExecutor
from functools import lru_cache
from typing import (
TYPE_CHECKING,
Any,
AsyncIterator,
Dict,
@@ -22,22 +21,22 @@ from typing import (
from urllib.parse import urljoin
import httpx
from httpx._types import AuthTypes, CertTypes, CookieTypes, HeaderTypes
from langchain_core.callbacks import (
from httpx._types import AuthTypes, CertTypes, CookieTypes, HeaderTypes, VerifyTypes
from langchain.callbacks.manager import (
AsyncCallbackManagerForChainRun,
CallbackManagerForChainRun,
)
from langchain_core.load.dump import dumpd
from langchain_core.runnables import Runnable
from langchain_core.runnables.config import (
from langchain.callbacks.tracers.log_stream import RunLogPatch
from langchain.load.dump import dumpd
from langchain.schema.runnable import Runnable
from langchain.schema.runnable.config import (
RunnableConfig,
ensure_config,
get_async_callback_manager_for_config,
get_callback_manager_for_config,
)
from langchain.schema.runnable.utils import AddableDict, Input, Output
from langchain_core.runnables.schema import StreamEvent
from langchain_core.runnables.utils import AddableDict, Input, Output
from langchain_core.tracers.log_stream import RunLogPatch
from typing_extensions import Literal
from langserve.callbacks import CallbackEventDict, ahandle_callbacks, handle_callbacks
@@ -46,14 +45,9 @@ from langserve.serialization import (
WellKnownLCSerializer,
load_events,
)
from langserve.server_sent_events import aconnect_sse, connect_sse
logger = logging.getLogger(__name__)
if TYPE_CHECKING:
# For type checking httpx types
import ssl
def _is_json_serializable(obj: Any) -> bool:
"""Return True if the object is json serializable."""
@@ -125,12 +119,6 @@ def _log_error_message_once(error_message: str) -> None:
logger.error(error_message)
@lru_cache(maxsize=1_000) # Will accommodate up to 1_000 different error messages
def _log_info_message_once(error_message: str) -> None:
"""Log an error message once."""
logger.info(error_message)
def _sanitize_request(request: httpx.Request) -> httpx.Request:
"""Remove sensitive headers from the request."""
accept_headers = {
@@ -286,11 +274,10 @@ class RemoteRunnable(Runnable[Input, Output]):
auth: Optional[AuthTypes] = None,
headers: Optional[HeaderTypes] = None,
cookies: Optional[CookieTypes] = None,
verify: ssl.SSLContext | str | bool = True,
verify: VerifyTypes = True,
cert: Optional[CertTypes] = None,
client_kwargs: Optional[Dict[str, Any]] = None,
use_server_callback_events: bool = True,
serializer: Optional[Serializer] = None,
) -> None:
"""Initialize the client.
@@ -306,8 +293,6 @@ class RemoteRunnable(Runnable[Input, Output]):
and async httpx clients
use_server_callback_events: Whether to invoke callbacks on any
callback events returned by the server.
serializer: The serializer to use for serializing and deserializing
data. If not provided, a default serializer will be used.
"""
_client_kwargs = client_kwargs or {}
# Enforce trailing slash
@@ -335,7 +320,7 @@ class RemoteRunnable(Runnable[Input, Output]):
# Register cleanup handler once RemoteRunnable is garbage collected
weakref.finalize(self, _close_clients, self.sync_client, self.async_client)
self._lc_serializer = serializer or WellKnownLCSerializer()
self._lc_serializer = WellKnownLCSerializer()
self._use_server_callback_events = use_server_callback_events
def _invoke(
@@ -445,15 +430,11 @@ class RemoteRunnable(Runnable[Input, Output]):
self,
inputs: List[Input],
config: Optional[RunnableConfig] = None,
*,
return_exceptions: bool = False,
**kwargs: Any,
) -> List[Output]:
if kwargs:
raise NotImplementedError(f"kwargs not implemented yet. Got {kwargs}")
return self._batch_with_config(
self._batch, inputs, config, return_exceptions=return_exceptions
)
raise NotImplementedError("kwargs not implemented yet.")
return self._batch_with_config(self._batch, inputs, config)
async def _abatch(
self,
@@ -542,17 +523,25 @@ class RemoteRunnable(Runnable[Input, Output]):
}
endpoint = urljoin(self.url, "stream")
try:
from httpx_sse import connect_sse
except ImportError:
raise ImportError(
"Missing `httpx_sse` dependency to use the stream method. "
"Install via `pip install httpx_sse`'"
)
try:
with connect_sse(
self.sync_client, "POST", endpoint, json=data
) as event_source:
for sse in event_source.iter_sse():
if sse["event"] == "data":
chunk = self._lc_serializer.loads(sse["data"])
if sse.event == "data":
chunk = self._lc_serializer.loads(sse.data)
if isinstance(chunk, dict):
# Any dict returned from streaming end point
# is assumed to follow additive semantics
# and will be coverted to an AddableDict
# and will be converted to an AddableDict
# automatically
chunk = AddableDict(chunk)
yield chunk
@@ -574,21 +563,21 @@ class RemoteRunnable(Runnable[Input, Output]):
except TypeError:
final_output = None
final_output_supported = False
elif sse["event"] == "error":
elif sse.event == "error":
# This can only be a server side error
_raise_exception_from_data(
sse["data"], httpx.Request(method="POST", url=endpoint)
sse.data, httpx.Request(method="POST", url=endpoint)
)
elif sse["event"] == "metadata":
elif sse.event == "metadata":
# Nothing to do for metadata for the regular remote client.
continue
elif sse["event"] == "end":
elif sse.event == "end":
break
else:
_log_error_message_once(
f"Encountered an unsupported event type: `{sse['event']}`. "
f"Encountered an unsupported event type: `{sse.event}`. "
f"Try upgrading the remote client to the latest version."
f"Ignoring events of type `{sse['event']}`."
f"Ignoring events of type `{sse.event}`."
)
except BaseException as e:
run_manager.on_chain_error(e)
@@ -620,13 +609,18 @@ class RemoteRunnable(Runnable[Input, Output]):
}
endpoint = urljoin(self.url, "stream")
try:
from httpx_sse import aconnect_sse
except ImportError:
raise ImportError("You must install `httpx_sse` to use the stream method.")
try:
async with aconnect_sse(
self.async_client, "POST", endpoint, json=data
) as event_source:
async for sse in event_source.aiter_sse():
if sse["event"] == "data":
chunk = self._lc_serializer.loads(sse["data"])
if sse.event == "data":
chunk = self._lc_serializer.loads(sse.data)
if isinstance(chunk, dict):
# Any dict returned from streaming end point
# is assumed to follow additive semantics
@@ -653,21 +647,21 @@ class RemoteRunnable(Runnable[Input, Output]):
final_output = None
final_output_supported = False
elif sse["event"] == "error":
elif sse.event == "error":
# This can only be a server side error
_raise_exception_from_data(
sse["data"], httpx.Request(method="POST", url=endpoint)
sse.data, httpx.Request(method="POST", url=endpoint)
)
elif sse["event"] == "metadata":
elif sse.event == "metadata":
# Nothing to do for metadata for the regular remote client.
continue
elif sse["event"] == "end":
elif sse.event == "end":
break
else:
_log_error_message_once(
f"Encountered an unsupported event type: `{sse['event']}`. "
f"Encountered an unsupported event type: `{sse.event}`. "
f"Try upgrading the remote client to the latest version."
f"Ignoring events of type `{sse['event']}`."
f"Ignoring events of type `{sse.event}`."
)
except BaseException as e:
await run_manager.on_chain_error(e)
@@ -724,13 +718,18 @@ class RemoteRunnable(Runnable[Input, Output]):
}
endpoint = urljoin(self.url, "stream_log")
try:
from httpx_sse import aconnect_sse
except ImportError:
raise ImportError("You must install `httpx_sse` to use the stream method.")
try:
async with aconnect_sse(
self.async_client, "POST", endpoint, json=data
) as event_source:
async for sse in event_source.aiter_sse():
if sse["event"] == "data":
data = self._lc_serializer.loads(sse["data"])
if sse.event == "data":
data = self._lc_serializer.loads(sse.data)
# Create a copy of the data to yield since underlying
# code is using jsonpatch which does some stuff in-place
# that can cause unexpected consequences.
@@ -742,18 +741,18 @@ class RemoteRunnable(Runnable[Input, Output]):
final_output += chunk
else:
final_output = chunk
elif sse["event"] == "error":
elif sse.event == "error":
# This can only be a server side error
_raise_exception_from_data(
sse["data"], httpx.Request(method="POST", url=endpoint)
sse.data, httpx.Request(method="POST", url=endpoint)
)
elif sse["event"] == "end":
elif sse.event == "end":
break
else:
_log_error_message_once(
f"Encountered an unsupported event type: `{sse['event']}`. "
f"Encountered an unsupported event type: `{sse.event}`. "
f"Try upgrading the remote client to the latest version."
f"Ignoring events of type `{sse['event']}`."
f"Ignoring events of type `{sse.event}`."
)
except BaseException as e:
await run_manager.on_chain_error(e)
@@ -766,7 +765,7 @@ class RemoteRunnable(Runnable[Input, Output]):
input: Any,
config: Optional[RunnableConfig] = None,
*,
version: Literal["v1", "v2", None] = None,
version: Literal["v1"],
include_names: Optional[Sequence[str]] = None,
include_types: Optional[Sequence[str]] = None,
include_tags: Optional[Sequence[str]] = None,
@@ -789,8 +788,7 @@ class RemoteRunnable(Runnable[Input, Output]):
input: The input to the runnable
config: The config to use for the runnable
version: The version of the astream_events to use.
Currently, this input is IGNORED on the client.
The server will return whatever format it's configured with.
Currently only "v1" is supported.
include_names: The names of the events to include
include_types: The types of the events to include
include_tags: The tags of the events to include
@@ -798,18 +796,13 @@ class RemoteRunnable(Runnable[Input, Output]):
exclude_types: The types of the events to exclude
exclude_tags: The tags of the events to exclude
"""
if version != "v1":
raise ValueError(f"Unsupported version: {version}. Use 'v1'")
# Create a stream handler that will emit Log objects
config = ensure_config(config)
callback_manager = get_async_callback_manager_for_config(config)
if version is not None:
_log_info_message_once(
"Versioning of the astream_events API is not supported on the client "
"side currently. The server will return events in whatever format "
"it was configured with in add_routes or APIHandler. "
"To stop seeing this message, remove the `version` argument."
)
events = []
run_manager = await callback_manager.on_chain_start(
@@ -829,34 +822,36 @@ class RemoteRunnable(Runnable[Input, Output]):
"exclude_tags": exclude_tags,
}
endpoint = urljoin(self.url, "stream_events")
headers = kwargs.pop("headers", {})
headers["Accept"] = "text/event-stream"
headers["Cache-Control"] = "no-store"
try:
from httpx_sse import aconnect_sse
except ImportError:
raise ImportError("You must install `httpx_sse` to use the stream method.")
try:
async with aconnect_sse(
self.async_client, "POST", endpoint, json=data
) as event_source:
async for sse in event_source.aiter_sse():
if sse["event"] == "data":
event = self._lc_serializer.loads(sse["data"])
if sse.event == "data":
event = self._lc_serializer.loads(sse.data)
# Create a copy of the data to yield since underlying
# code is using jsonpatch which does some stuff in-place
# that can cause unexpected consequences.
yield event
events.append(event)
elif sse["event"] == "error":
elif sse.event == "error":
# This can only be a server side error
_raise_exception_from_data(
sse["data"], httpx.Request(method="POST", url=endpoint)
sse.data, httpx.Request(method="POST", url=endpoint)
)
elif sse["event"] == "end":
elif sse.event == "end":
break
else:
_log_error_message_once(
f"Encountered an unsupported event type: `{sse['event']}`. "
f"Encountered an unsupported event type: `{sse.event}`. "
f"Try upgrading the remote client to the latest version."
f"Ignoring events of type `{sse['event']}`."
f"Ignoring events of type `{sse.event}`."
)
except BaseException as e:
await run_manager.on_chain_error(e)
+32 -9
View File
@@ -2,11 +2,14 @@ import json
import mimetypes
import os
from string import Template
from typing import Literal, Sequence, Type
from typing import Literal, Optional, Sequence, Type, Union
from fastapi.responses import Response
from langchain_core.runnables import Runnable
from pydantic import BaseModel
from fastapi.security import APIKeyCookie, APIKeyHeader, APIKeyQuery
from langchain.schema.runnable import Runnable
from typing_extensions import TypedDict
from langserve.pydantic_v1 import BaseModel
class PlaygroundTemplate(Template):
@@ -46,18 +49,38 @@ def _get_mimetype(path: str) -> str:
return mime_type
SupportedSecurityScheme = Union[APIKeyHeader, APIKeyQuery, APIKeyCookie]
class PlaygroundConfig(TypedDict, total=False):
"""Configuration for the playground."""
security_scheme: Optional[SupportedSecurityScheme]
async def serve_playground(
runnable: Runnable,
input_schema: Type[BaseModel],
output_schema: Type[BaseModel],
config_keys: Sequence[str],
base_url: str,
file_path: str,
feedback_enabled: bool,
public_trace_link_enabled: bool,
playground_type: Literal["default", "chat"],
*,
playground_config: Optional[PlaygroundConfig] = None,
) -> Response:
"""Serve the playground."""
security_scheme = (
playground_config.get("security_scheme") if playground_config else None
)
if not isinstance(
security_scheme, (APIKeyHeader, APIKeyQuery, APIKeyCookie, type(None))
):
raise NotImplementedError(
"Only APIKeyHeader, APIKeyQuery, APIKeyCookie, and None are supported."
)
if playground_type == "default":
path_to_dist = "./playground/dist"
elif playground_type == "chat":
@@ -89,18 +112,18 @@ async def serve_playground(
if base_url.startswith("/")
else base_url,
LANGSERVE_CONFIG_SCHEMA=json.dumps(
runnable.config_schema(include=config_keys).model_json_schema()
),
LANGSERVE_INPUT_SCHEMA=json.dumps(input_schema.model_json_schema()),
LANGSERVE_OUTPUT_SCHEMA=json.dumps(
output_schema.model_json_schema()
runnable.config_schema(include=config_keys).schema()
),
LANGSERVE_INPUT_SCHEMA=json.dumps(input_schema.schema()),
LANGSERVE_FEEDBACK_ENABLED=json.dumps(
"true" if feedback_enabled else "false"
),
LANGSERVE_PUBLIC_TRACE_LINK_ENABLED=json.dumps(
"true" if public_trace_link_enabled else "false"
),
SECURITY_SCHEME=security_scheme.model.json()
if security_scheme
else json.dumps({}),
)
else:
response = f.buffer.read()
File diff suppressed because one or more lines are too long
+1 -2
View File
@@ -5,7 +5,7 @@
<link rel="icon" href="/____LANGSERVE_BASE_URL/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Playground</title>
<script type="module" crossorigin src="/____LANGSERVE_BASE_URL/assets/index-400979f0.js"></script>
<script type="module" crossorigin src="/____LANGSERVE_BASE_URL/assets/index-dbc96538.js"></script>
<link rel="stylesheet" href="/____LANGSERVE_BASE_URL/assets/index-52e8ab2f.css">
</head>
<body>
@@ -14,7 +14,6 @@
try {
window.CONFIG_SCHEMA = ____LANGSERVE_CONFIG_SCHEMA;
window.INPUT_SCHEMA = ____LANGSERVE_INPUT_SCHEMA;
window.OUTPUT_SCHEMA = ____LANGSERVE_OUTPUT_SCHEMA;
window.FEEDBACK_ENABLED = ____LANGSERVE_FEEDBACK_ENABLED;
window.PUBLIC_TRACE_LINK_ENABLED = ____LANGSERVE_PUBLIC_TRACE_LINK_ENABLED;
} catch (error) {
-1
View File
@@ -12,7 +12,6 @@
try {
window.CONFIG_SCHEMA = ____LANGSERVE_CONFIG_SCHEMA;
window.INPUT_SCHEMA = ____LANGSERVE_INPUT_SCHEMA;
window.OUTPUT_SCHEMA = ____LANGSERVE_OUTPUT_SCHEMA;
window.FEEDBACK_ENABLED = ____LANGSERVE_FEEDBACK_ENABLED;
window.PUBLIC_TRACE_LINK_ENABLED = ____LANGSERVE_PUBLIC_TRACE_LINK_ENABLED;
} catch (error) {
+2 -9
View File
@@ -25,7 +25,7 @@
"clsx": "^2.0.0",
"dayjs": "^1.11.10",
"fast-json-patch": "^3.1.1",
"lodash": "^4.18.1",
"lodash": "^4.17.21",
"lz-string": "^1.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -48,14 +48,7 @@
"postcss": "^8.4.31",
"tailwindcss": "^3.3.3",
"typescript": "^5.0.2",
"vite": "^6.4.2",
"vite": "^4.4.5",
"vite-plugin-svgr": "^4.1.0"
},
"resolutions": {
"braces": "^3.0.3",
"cross-spawn": "^7.0.5",
"rollup": "^3.30.0",
"ajv": "^8.18.0",
"esbuild": "0.25.0"
}
}
@@ -6,30 +6,12 @@ import {
schemaMatches,
Paths,
isControl,
JsonSchema,
} from "@jsonforms/core";
import { useStreamCallback } from "../useStreamCallback";
import { isJsonSchemaExtra } from "../utils/schema";
import { MessageFields, ChatMessageInput } from "./ChatMessageInput";
import { useEffect } from "react";
function checkItemSchema(schema: JsonSchema) {
const isObjectMessage =
schema.type === "object" &&
(schema.title?.endsWith("Message") ||
schema.title?.endsWith("MessageChunk"));
const isTupleMessage =
schema.type === "array" &&
schema.minItems === 2 &&
schema.maxItems === 2 &&
Array.isArray(schema.items) &&
schema.items.length === 2 &&
schema.items.every((schema) => schema.type === "string");
return isObjectMessage || isTupleMessage;
}
export const chatMessagesTester = rankWith(
12,
and(
@@ -52,11 +34,22 @@ export const chatMessagesTester = rankWith(
}
if ("anyOf" in schema.items && schema.items.anyOf != null) {
return schema.items.anyOf.every(checkItemSchema);
}
return schema.items.anyOf.every((schema) => {
const isObjectMessage =
schema.type === "object" &&
(schema.title?.endsWith("Message") ||
schema.title?.endsWith("MessageChunk"));
if ("oneOf" in schema.items && schema.items.oneOf != null) {
return schema.items.oneOf.every(checkItemSchema);
const isTupleMessage =
schema.type === "array" &&
schema.minItems === 2 &&
schema.maxItems === 2 &&
Array.isArray(schema.items) &&
schema.items.length === 2 &&
schema.items.every((schema) => schema.type === "string");
return isObjectMessage || isTupleMessage;
});
}
return false;
@@ -71,14 +64,10 @@ export const ChatMessagesControlRenderer = withJsonFormsControlProps(
useEffect(() => {
if (!isJsonSchemaExtra(props.schema)) return;
if (props.schema.extra.widget.type !== "chat") return;
setTimeout(
() =>
props.handleChange(props.path, [
...data,
{ content: "", type: "human" },
]),
10
);
setTimeout(() => props.handleChange(props.path, [
...data,
{ content: "", type: "human" },
]), 10);
}, []);
useStreamCallback("onStart", () => {
@@ -92,10 +81,7 @@ export const ChatMessagesControlRenderer = withJsonFormsControlProps(
if (props.schema.extra.widget.type !== "chat") return;
if (aggregatedState?.final_output !== undefined) {
const msgPath = Paths.compose(props.path, `${data.length - 1}`);
if (
(aggregatedState.final_output as MessageFields)?.type ===
"AIMessageChunk"
) {
if ((aggregatedState.final_output as MessageFields)?.type === "AIMessageChunk") {
props.handleChange(
Paths.compose(msgPath, "content"),
(aggregatedState.final_output as MessageFields)?.content
@@ -154,7 +140,7 @@ export const ChatMessagesControlRenderer = withJsonFormsControlProps(
props.path,
data.filter((_, i) => i !== index)
);
};
}
return (
<ChatMessageInput
message={message}
@@ -162,7 +148,7 @@ export const ChatMessagesControlRenderer = withJsonFormsControlProps(
handleRemoval={handleChatMessageRemoval}
path={props.path}
key={index}
></ChatMessageInput>
></ChatMessageInput>
);
})}
</div>
-4
View File
@@ -13,11 +13,7 @@ declare global {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
INPUT_SCHEMA?: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
OUTPUT_SCHEMA?: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
FEEDBACK_ENABLED?: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
PUBLIC_TRACE_LINK_ENABLED?: any;
}
}
+239 -291
View File
@@ -28,15 +28,6 @@
"@babel/highlight" "^7.22.13"
chalk "^2.4.2"
"@babel/code-frame@^7.28.6":
version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c"
integrity sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==
dependencies:
"@babel/helper-validator-identifier" "^7.28.5"
js-tokens "^4.0.0"
picocolors "^1.1.1"
"@babel/compat-data@^7.22.9":
version "7.23.2"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.2.tgz#6a12ced93455827037bfb5ed8492820d60fc32cc"
@@ -146,33 +137,24 @@
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f"
integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==
"@babel/helper-string-parser@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
"@babel/helper-validator-identifier@^7.22.20":
version "7.22.20"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
"@babel/helper-validator-identifier@^7.28.5":
version "7.28.5"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4"
integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==
"@babel/helper-validator-option@^7.22.15":
version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040"
integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==
"@babel/helpers@^7.23.2":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.6.tgz#fca903a313ae675617936e8998b814c415cbf5d7"
integrity sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==
version "7.23.2"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.2.tgz#2832549a6e37d484286e15ba36a5330483cac767"
integrity sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==
dependencies:
"@babel/template" "^7.28.6"
"@babel/types" "^7.28.6"
"@babel/template" "^7.22.15"
"@babel/traverse" "^7.23.2"
"@babel/types" "^7.23.0"
"@babel/highlight@^7.22.13":
version "7.22.20"
@@ -188,13 +170,6 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719"
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
"@babel/parser@^7.28.6":
version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.0.tgz#669ef345add7d057e92b7ed15f0bac07611831b6"
integrity sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==
dependencies:
"@babel/types" "^7.29.0"
"@babel/plugin-transform-react-jsx-self@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.22.5.tgz#ca2fdc11bc20d4d46de01137318b13d04e481d8e"
@@ -210,9 +185,11 @@
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.18.3", "@babel/runtime@^7.23.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.6.tgz#d267a43cb1836dc4d182cce93ae75ba954ef6d2b"
integrity sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==
version "7.23.2"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885"
integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==
dependencies:
regenerator-runtime "^0.14.0"
"@babel/template@^7.22.15":
version "7.22.15"
@@ -223,15 +200,6 @@
"@babel/parser" "^7.22.15"
"@babel/types" "^7.22.15"
"@babel/template@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57"
integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==
dependencies:
"@babel/code-frame" "^7.28.6"
"@babel/parser" "^7.28.6"
"@babel/types" "^7.28.6"
"@babel/traverse@^7.23.2":
version "7.23.2"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8"
@@ -257,14 +225,6 @@
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"
"@babel/types@^7.28.6", "@babel/types@^7.29.0":
version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7"
integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==
dependencies:
"@babel/helper-string-parser" "^7.27.1"
"@babel/helper-validator-identifier" "^7.28.5"
"@date-io/core@^1.3.13":
version "1.3.13"
resolved "https://registry.yarnpkg.com/@date-io/core/-/core-1.3.13.tgz#90c71da493f20204b7a972929cc5c482d078b3fa"
@@ -384,130 +344,115 @@
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6"
integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==
"@esbuild/aix-ppc64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz#499600c5e1757a524990d5d92601f0ac3ce87f64"
integrity sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==
"@esbuild/android-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622"
integrity sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==
"@esbuild/android-arm64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz#b9b8231561a1dfb94eb31f4ee056b92a985c324f"
integrity sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==
"@esbuild/android-arm@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682"
integrity sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==
"@esbuild/android-arm@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.0.tgz#ca6e7888942505f13e88ac9f5f7d2a72f9facd2b"
integrity sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==
"@esbuild/android-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2"
integrity sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==
"@esbuild/android-x64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.0.tgz#e765ea753bac442dfc9cb53652ce8bd39d33e163"
integrity sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==
"@esbuild/darwin-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1"
integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==
"@esbuild/darwin-arm64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz#fa394164b0d89d4fdc3a8a21989af70ef579fa2c"
integrity sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==
"@esbuild/darwin-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d"
integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==
"@esbuild/darwin-x64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz#91979d98d30ba6e7d69b22c617cc82bdad60e47a"
integrity sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==
"@esbuild/freebsd-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54"
integrity sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==
"@esbuild/freebsd-arm64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz#b97e97073310736b430a07b099d837084b85e9ce"
integrity sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==
"@esbuild/freebsd-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e"
integrity sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==
"@esbuild/freebsd-x64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz#f3b694d0da61d9910ec7deff794d444cfbf3b6e7"
integrity sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==
"@esbuild/linux-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0"
integrity sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==
"@esbuild/linux-arm64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz#f921f699f162f332036d5657cad9036f7a993f73"
integrity sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==
"@esbuild/linux-arm@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0"
integrity sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==
"@esbuild/linux-arm@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz#cc49305b3c6da317c900688995a4050e6cc91ca3"
integrity sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==
"@esbuild/linux-ia32@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7"
integrity sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==
"@esbuild/linux-ia32@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz#3e0736fcfab16cff042dec806247e2c76e109e19"
integrity sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==
"@esbuild/linux-loong64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d"
integrity sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==
"@esbuild/linux-loong64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz#ea2bf730883cddb9dfb85124232b5a875b8020c7"
integrity sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==
"@esbuild/linux-mips64el@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231"
integrity sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==
"@esbuild/linux-mips64el@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz#4cababb14eede09248980a2d2d8b966464294ff1"
integrity sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==
"@esbuild/linux-ppc64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb"
integrity sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==
"@esbuild/linux-ppc64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz#8860a4609914c065373a77242e985179658e1951"
integrity sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==
"@esbuild/linux-riscv64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6"
integrity sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==
"@esbuild/linux-riscv64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz#baf26e20bb2d38cfb86ee282dff840c04f4ed987"
integrity sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==
"@esbuild/linux-s390x@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071"
integrity sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==
"@esbuild/linux-s390x@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz#8323afc0d6cb1b6dc6e9fd21efd9e1542c3640a4"
integrity sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==
"@esbuild/linux-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz#c7690b3417af318a9b6f96df3031a8865176d338"
integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==
"@esbuild/linux-x64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz#08fcf60cb400ed2382e9f8e0f5590bac8810469a"
integrity sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==
"@esbuild/netbsd-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1"
integrity sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==
"@esbuild/netbsd-arm64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz#935c6c74e20f7224918fbe2e6c6fe865b6c6ea5b"
integrity sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==
"@esbuild/openbsd-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae"
integrity sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==
"@esbuild/netbsd-x64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz#414677cef66d16c5a4d210751eb2881bb9c1b62b"
integrity sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==
"@esbuild/sunos-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d"
integrity sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==
"@esbuild/openbsd-arm64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz#8fd55a4d08d25cdc572844f13c88d678c84d13f7"
integrity sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==
"@esbuild/win32-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9"
integrity sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==
"@esbuild/openbsd-x64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz#0c48ddb1494bbc2d6bcbaa1429a7f465fa1dedde"
integrity sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==
"@esbuild/win32-ia32@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102"
integrity sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==
"@esbuild/sunos-x64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz#86ff9075d77962b60dd26203d7352f92684c8c92"
integrity sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==
"@esbuild/win32-arm64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz#849c62327c3229467f5b5cd681bf50588442e96c"
integrity sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==
"@esbuild/win32-ia32@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz#f62eb480cd7cca088cb65bb46a6db25b725dc079"
integrity sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==
"@esbuild/win32-x64@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz#c8e119a30a7c8d60b9d2e22d2073722dde3b710b"
integrity sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==
"@esbuild/win32-x64@0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d"
integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
version "4.4.0"
@@ -1280,15 +1225,25 @@ ajv-formats@^2.1.0:
dependencies:
ajv "^8.0.0"
ajv@^6.12.4, ajv@^8.0.0, ajv@^8.18.0, ajv@^8.6.1:
version "8.18.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.18.0.tgz#8864186b6738d003eb3a933172bb3833e10cefbc"
integrity sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==
ajv@^6.12.4:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
dependencies:
fast-deep-equal "^3.1.3"
fast-uri "^3.0.1"
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ajv@^8.0.0, ajv@^8.6.1:
version "8.12.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1"
integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==
dependencies:
fast-deep-equal "^3.1.1"
json-schema-traverse "^1.0.0"
require-from-string "^2.0.2"
uri-js "^4.2.2"
ansi-regex@^5.0.1:
version "5.0.1"
@@ -1376,19 +1331,19 @@ binary-extensions@^2.0.0:
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
brace-expansion@^1.1.7:
version "1.1.13"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.13.tgz#d37875c01dc9eff988dd49d112a57cb67b54efe6"
integrity sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
braces@^3.0.3, braces@~3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
braces@^3.0.2, braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.1.1"
fill-range "^7.0.1"
browserslist@^4.21.10, browserslist@^4.21.9:
version "4.22.1"
@@ -1527,10 +1482,10 @@ cosmiconfig@^8.1.3:
parse-json "^5.2.0"
path-type "^4.0.0"
cross-spawn@^7.0.2, cross-spawn@^7.0.5:
version "7.0.6"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
cross-spawn@^7.0.2:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
dependencies:
path-key "^3.1.0"
shebang-command "^2.0.0"
@@ -1630,36 +1585,33 @@ error-ex@^1.3.1:
dependencies:
is-arrayish "^0.2.1"
esbuild@0.25.0, esbuild@^0.25.0:
version "0.25.0"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.0.tgz#0de1787a77206c5a79eeb634a623d39b5006ce92"
integrity sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==
esbuild@^0.18.10:
version "0.18.20"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.18.20.tgz#4709f5a34801b43b799ab7d6d82f7284a9b7a7a6"
integrity sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==
optionalDependencies:
"@esbuild/aix-ppc64" "0.25.0"
"@esbuild/android-arm" "0.25.0"
"@esbuild/android-arm64" "0.25.0"
"@esbuild/android-x64" "0.25.0"
"@esbuild/darwin-arm64" "0.25.0"
"@esbuild/darwin-x64" "0.25.0"
"@esbuild/freebsd-arm64" "0.25.0"
"@esbuild/freebsd-x64" "0.25.0"
"@esbuild/linux-arm" "0.25.0"
"@esbuild/linux-arm64" "0.25.0"
"@esbuild/linux-ia32" "0.25.0"
"@esbuild/linux-loong64" "0.25.0"
"@esbuild/linux-mips64el" "0.25.0"
"@esbuild/linux-ppc64" "0.25.0"
"@esbuild/linux-riscv64" "0.25.0"
"@esbuild/linux-s390x" "0.25.0"
"@esbuild/linux-x64" "0.25.0"
"@esbuild/netbsd-arm64" "0.25.0"
"@esbuild/netbsd-x64" "0.25.0"
"@esbuild/openbsd-arm64" "0.25.0"
"@esbuild/openbsd-x64" "0.25.0"
"@esbuild/sunos-x64" "0.25.0"
"@esbuild/win32-arm64" "0.25.0"
"@esbuild/win32-ia32" "0.25.0"
"@esbuild/win32-x64" "0.25.0"
"@esbuild/android-arm" "0.18.20"
"@esbuild/android-arm64" "0.18.20"
"@esbuild/android-x64" "0.18.20"
"@esbuild/darwin-arm64" "0.18.20"
"@esbuild/darwin-x64" "0.18.20"
"@esbuild/freebsd-arm64" "0.18.20"
"@esbuild/freebsd-x64" "0.18.20"
"@esbuild/linux-arm" "0.18.20"
"@esbuild/linux-arm64" "0.18.20"
"@esbuild/linux-ia32" "0.18.20"
"@esbuild/linux-loong64" "0.18.20"
"@esbuild/linux-mips64el" "0.18.20"
"@esbuild/linux-ppc64" "0.18.20"
"@esbuild/linux-riscv64" "0.18.20"
"@esbuild/linux-s390x" "0.18.20"
"@esbuild/linux-x64" "0.18.20"
"@esbuild/netbsd-x64" "0.18.20"
"@esbuild/openbsd-x64" "0.18.20"
"@esbuild/sunos-x64" "0.18.20"
"@esbuild/win32-arm64" "0.18.20"
"@esbuild/win32-ia32" "0.18.20"
"@esbuild/win32-x64" "0.18.20"
escalade@^3.1.1:
version "3.1.1"
@@ -1780,7 +1732,7 @@ esutils@^2.0.2:
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
fast-deep-equal@^3.1.3:
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
@@ -1801,16 +1753,16 @@ fast-json-patch@^3.1.1:
resolved "https://registry.yarnpkg.com/fast-json-patch/-/fast-json-patch-3.1.1.tgz#85064ea1b1ebf97a3f7ad01e23f9337e72c66947"
integrity sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==
fast-json-stable-stringify@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
fast-levenshtein@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
fast-uri@^3.0.1:
version "3.1.0"
resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.0.tgz#66eecff6c764c0df9b762e62ca7edcfb53b4edfa"
integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==
fastq@^1.6.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a"
@@ -1818,11 +1770,6 @@ fastq@^1.6.0:
dependencies:
reusify "^1.0.4"
fdir@^6.4.4, fdir@^6.5.0:
version "6.5.0"
resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350"
integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==
file-entry-cache@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
@@ -1830,10 +1777,10 @@ file-entry-cache@^6.0.1:
dependencies:
flat-cache "^3.0.4"
fill-range@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
dependencies:
to-regex-range "^5.0.1"
@@ -1860,9 +1807,9 @@ flat-cache@^3.0.4:
rimraf "^3.0.2"
flatted@^3.2.9:
version "3.4.2"
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.2.tgz#f5c23c107f0f37de8dbdf24f13722b3b98d52726"
integrity sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==
version "3.2.9"
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf"
integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==
fraction.js@^4.3.6:
version "4.3.7"
@@ -1874,7 +1821,7 @@ fs.realpath@^1.0.0:
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
fsevents@~2.3.2, fsevents@~2.3.3:
fsevents@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
@@ -2073,9 +2020,9 @@ jiti@^1.18.2:
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
js-yaml@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b"
integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
dependencies:
argparse "^2.0.1"
@@ -2094,6 +2041,11 @@ json-parse-even-better-errors@^2.3.0:
resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
json-schema-traverse@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
@@ -2146,10 +2098,10 @@ lodash.merge@^4.6.2:
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash@^4.17.15, lodash@^4.18.1:
version "4.18.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c"
integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==
lodash@^4.17.15, lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0"
@@ -2190,17 +2142,17 @@ merge2@^1.3.0, merge2@^1.4.1:
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
micromatch@^4.0.4, micromatch@^4.0.5:
version "4.0.8"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
version "4.0.5"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
dependencies:
braces "^3.0.3"
braces "^3.0.2"
picomatch "^2.3.1"
minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.5"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e"
integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies:
brace-expansion "^1.1.7"
@@ -2218,10 +2170,10 @@ mz@^2.7.0:
object-assign "^4.0.1"
thenify-all "^1.0.0"
nanoid@^3.3.11:
version "3.3.11"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b"
integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
nanoid@^3.3.6:
version "3.3.6"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==
natural-compare@^1.4.0:
version "1.4.0"
@@ -2341,20 +2293,10 @@ picocolors@^1.0.0:
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
picocolors@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
version "2.3.2"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.2.tgz#5a942915e26b372dc0f0e6753149a16e6b1c5601"
integrity sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==
picomatch@^4.0.2, picomatch@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589"
integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
pify@^2.3.0:
version "2.3.0"
@@ -2410,14 +2352,14 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0:
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
postcss@^8.4.23, postcss@^8.4.31, postcss@^8.5.3:
version "8.5.9"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.9.tgz#f6ee9e0b94f0f19c97d2f172bfbd7fc71fe1cca4"
integrity sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==
postcss@^8.4.23, postcss@^8.4.27, postcss@^8.4.31:
version "8.4.31"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d"
integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==
dependencies:
nanoid "^3.3.11"
picocolors "^1.1.1"
source-map-js "^1.2.1"
nanoid "^3.3.6"
picocolors "^1.0.0"
source-map-js "^1.0.2"
prelude-ls@^1.2.1:
version "1.2.1"
@@ -2433,6 +2375,11 @@ prop-types@^15.6.2, prop-types@^15.8.1:
object-assign "^4.1.1"
react-is "^16.13.1"
punycode@^2.1.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
queue-microtask@^1.2.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
@@ -2520,6 +2467,11 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
regenerator-runtime@^0.14.0:
version "0.14.0"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==
require-from-string@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
@@ -2551,10 +2503,10 @@ rimraf@^3.0.2:
dependencies:
glob "^7.1.3"
rollup@^3.30.0, rollup@^4.34.9:
version "3.30.0"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.30.0.tgz#3fa506fee2c5ba9d540a38da87067376cd55966d"
integrity sha512-kQvGasUgN+AlWGliFn2POSajRQEsULVYFGTvOZmK06d7vCD+YhZztt70kGk3qaeAXeWYL5eO7zx+rAubBc55eA==
rollup@^3.27.1:
version "3.29.4"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.4.tgz#4d70c0f9834146df8705bfb69a9a19c9e1109981"
integrity sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==
optionalDependencies:
fsevents "~2.3.2"
@@ -2609,10 +2561,10 @@ snake-case@^3.0.4:
dot-case "^3.0.4"
tslib "^2.0.3"
source-map-js@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
source-map-js@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
source-map@^0.5.7:
version "0.5.7"
@@ -2733,14 +2685,6 @@ thenify-all@^1.0.0:
dependencies:
any-promise "^1.0.0"
tinyglobby@^0.2.13:
version "0.2.16"
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.16.tgz#1c3b7eb953fce42b226bc5a1ee06428281aff3d6"
integrity sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==
dependencies:
fdir "^6.5.0"
picomatch "^4.0.4"
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
@@ -2793,6 +2737,13 @@ update-browserslist-db@^1.0.13:
escalade "^3.1.1"
picocolors "^1.0.0"
uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
dependencies:
punycode "^2.1.0"
use-callback-ref@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.0.tgz#772199899b9c9a50526fedc4993fc7fa1f7e32d5"
@@ -2839,19 +2790,16 @@ vite-plugin-svgr@^4.1.0:
"@svgr/core" "^8.1.0"
"@svgr/plugin-jsx" "^8.1.0"
vite@^6.4.2:
version "6.4.2"
resolved "https://registry.yarnpkg.com/vite/-/vite-6.4.2.tgz#a4e548ca3a90ca9f3724582cab35e1ba15efc6f2"
integrity sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==
vite@^4.4.5:
version "4.5.0"
resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.0.tgz#ec406295b4167ac3bc23e26f9c8ff559287cff26"
integrity sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==
dependencies:
esbuild "^0.25.0"
fdir "^6.4.4"
picomatch "^4.0.2"
postcss "^8.5.3"
rollup "^4.34.9"
tinyglobby "^0.2.13"
esbuild "^0.18.10"
postcss "^8.4.27"
rollup "^3.27.1"
optionalDependencies:
fsevents "~2.3.3"
fsevents "~2.3.2"
which@^2.0.1:
version "2.0.2"
@@ -2876,14 +2824,14 @@ yallist@^4.0.0:
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
yaml@^1.10.0:
version "1.10.3"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.3.tgz#76e407ed95c42684fb8e14641e5de62fe65bbcb3"
integrity sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==
version "1.10.2"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
yaml@^2.1.1:
version "2.8.3"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.3.tgz#a0d6bd2efb3dd03c59370223701834e60409bd7d"
integrity sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==
version "2.3.3"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.3.tgz#01f6d18ef036446340007db8e016810e5d64aad9"
integrity sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==
yocto-queue@^0.1.0:
version "0.1.0"
+33
View File
@@ -0,0 +1,33 @@
from importlib import metadata
## Create namespaces for pydantic v1 and v2.
# This code must stay at the top of the file before other modules may
# attempt to import pydantic since it adds pydantic_v1 and pydantic_v2 to sys.modules.
#
# This hack is done for the following reasons:
# * Langchain will attempt to remain compatible with both pydantic v1 and v2 since
# both dependencies and dependents may be stuck on either version of v1 or v2.
# * Creating namespaces for pydantic v1 and v2 should allow us to write code that
# unambiguously uses either v1 or v2 API.
# * This change is easier to roll out and roll back.
try:
# F401: imported but unused
from pydantic.v1 import ( # noqa: F401
BaseModel,
Field,
ValidationError,
create_model,
)
except ImportError:
from pydantic import BaseModel, Field, ValidationError, create_model # noqa: F401
# This is not a pydantic v1 thing, but it feels too small to create a new module for.
PYDANTIC_VERSION = metadata.version("pydantic")
try:
_PYDANTIC_MAJOR_VERSION: int = int(PYDANTIC_VERSION.split(".")[0])
except metadata.PackageNotFoundError:
_PYDANTIC_MAJOR_VERSION = -1
+22 -67
View File
@@ -2,11 +2,9 @@ from datetime import datetime
from typing import Dict, List, Optional, Union
from uuid import UUID
from pydantic import (
BaseModel,
Field,
)
from pydantic import BaseModel as BaseModelV1
from pydantic import BaseModel # Floats between v1 and v2
from langserve.pydantic_v1 import BaseModel as BaseModelV1
class CustomUserType(BaseModelV1):
@@ -43,35 +41,14 @@ class SharedResponseMetadata(BaseModelV1):
pass
class FeedbackToken(BaseModelV1):
"""Represents the feedback tokens for a given request."""
key: str # The key of the feedback token
token_url: Optional[str] = None
expires_at: Optional[datetime] = None
class InvokeResponseMetadata(SharedResponseMetadata):
"""Represents response metadata used for just single input/output LangServe
class SingletonResponseMetadata(SharedResponseMetadata):
"""
Represents response metadata used for just single input/output LangServe
responses.
"""
# Represents the parent run id for a given request
run_id: UUID
feedback_tokens: List[FeedbackToken] = Field(
...,
description=(
"Feedback tokens from the given run."
"These tokens allow a user to provide feedback on the run."
"Only available if server was configured to provide feedback tokens."
),
)
# Alias for backwards compatibility
# Keep here in case clients are somehow using this for type checking
# TODO(Deprecate): This should be deprecated in 2025.
SingletonResponseMetadata = InvokeResponseMetadata
class BatchResponseMetadata(SharedResponseMetadata):
@@ -80,25 +57,17 @@ class BatchResponseMetadata(SharedResponseMetadata):
responses.
"""
# This namespace can include any additional metadata that is shared
# across all responses in the batch (e.g., if a batch run
# ID was a thing, it would go here)
# metadata for each individual response in the batch
# Parallel list of InvokeResponseMetadata objects matching
# the individual requests in the batch
responses: List[InvokeResponseMetadata]
# A list of UUIDs
# Represents each parent run id for a given request, in
# the same order in which they were received
run_ids: List[UUID] # For backwards compatibility, clients should not use this
run_ids: List[UUID]
class BaseFeedback(BaseModel):
"""Shared information between create requests of feedback and feedback objects"""
"""
Shared information between create requests of feedback and feedback objects
"""
run_id: Optional[UUID]
run_id: UUID
"""The associated run ID this feedback is logged for."""
key: str
@@ -114,34 +83,18 @@ class BaseFeedback(BaseModel):
"""Comment or explanation for the feedback."""
class FeedbackCreateRequestTokenBased(BaseModel):
"""Shared information between create requests of feedback and feedback objects."""
token_or_url: Union[UUID, str]
"""The associated run ID this feedback is logged for."""
score: Optional[Union[float, int, bool]] = None
"""Value or score to assign the run."""
value: Optional[Union[float, int, bool, str, Dict]] = None
"""The display value for the feedback if not a metric."""
comment: Optional[str] = None
"""Comment or explanation for the feedback."""
correction: Optional[Dict] = None
"""Correction for the run."""
metadata: Optional[Dict] = None
"""Metadata for the feedback."""
class FeedbackCreateRequest(BaseFeedback):
"""Represents a request that creates feedback for an individual run"""
"""
Represents a request that creates feedback for an individual run
"""
pass
class Feedback(BaseFeedback):
"""Represents feedback given on an individual run"""
"""
Represents feedback given on an individual run
"""
id: UUID
"""The unique ID of the feedback that was created."""
@@ -157,7 +110,9 @@ class Feedback(BaseFeedback):
class PublicTraceLinkCreateRequest(BaseModel):
"""Represents a request that creates a public trace for an individual run."""
"""
Represents a request that creates a public trace for an individual run
"""
run_id: UUID
"""The unique ID of the run to share."""
+75 -102
View File
@@ -10,16 +10,17 @@ By default, exceptions are serialized as a generic exception without
any information about the exception. This is done to prevent leaking
sensitive information from the server to the client.
"""
import abc
import logging
from functools import lru_cache
from typing import Annotated, Any, Dict, List, Union
from typing import Any, Dict, List, Union
import orjson
from langchain_core.agents import AgentAction, AgentActionMessageLog, AgentFinish
from langchain_core.documents import Document
from langchain_core.messages import (
from langchain.prompts.base import StringPromptValue
from langchain.prompts.chat import ChatPromptValueConcrete
from langchain.schema.agent import AgentAction, AgentActionMessageLog, AgentFinish
from langchain.schema.document import Document
from langchain.schema.messages import (
AIMessage,
AIMessageChunk,
ChatMessage,
@@ -30,19 +31,15 @@ from langchain_core.messages import (
HumanMessageChunk,
SystemMessage,
SystemMessageChunk,
ToolMessage,
ToolMessageChunk,
)
from langchain_core.outputs import (
from langchain.schema.output import (
ChatGeneration,
ChatGenerationChunk,
Generation,
LLMResult,
)
from langchain_core.prompt_values import ChatPromptValueConcrete
from langchain_core.prompts.base import StringPromptValue
from pydantic import BaseModel, Discriminator, Field, RootModel, Tag, ValidationError
from langserve.pydantic_v1 import BaseModel, ValidationError
from langserve.validation import CallbackEvent
logger = logging.getLogger(__name__)
@@ -54,58 +51,42 @@ def _log_error_message_once(error_message: str) -> None:
logger.error(error_message)
def _get_type(v: Any) -> str:
"""Get the type associated with the object for serialization purposes."""
if isinstance(v, dict) and "type" in v:
return v["type"]
elif hasattr(v, "type"):
return v.type
else:
raise TypeError(
f"Expected either a dictionary with a 'type' key or an object "
f"with a 'type' attribute. Instead got type {type(v)}."
)
class WellKnownLCObject(BaseModel):
"""A well known LangChain object.
A pydantic model that defines what constitutes a well known LangChain object.
# A well known LangChain object.
# A pydantic model that defines what constitutes a well known LangChain object.
# All well-known objects are allowed to be serialized and de-serialized.
All well-known objects are allowed to be serialized and de-serialized.
"""
WellKnownLCObject = RootModel[
Annotated[
Union[
Annotated[AIMessage, Tag(tag="ai")],
Annotated[HumanMessage, Tag(tag="human")],
Annotated[ChatMessage, Tag(tag="chat")],
Annotated[SystemMessage, Tag(tag="system")],
Annotated[FunctionMessage, Tag(tag="function")],
Annotated[ToolMessage, Tag(tag="tool")],
Annotated[AIMessageChunk, Tag(tag="AIMessageChunk")],
Annotated[HumanMessageChunk, Tag(tag="HumanMessageChunk")],
Annotated[ChatMessageChunk, Tag(tag="ChatMessageChunk")],
Annotated[SystemMessageChunk, Tag(tag="SystemMessageChunk")],
Annotated[FunctionMessageChunk, Tag(tag="FunctionMessageChunk")],
Annotated[ToolMessageChunk, Tag(tag="ToolMessageChunk")],
Annotated[Document, Tag(tag="Document")],
Annotated[StringPromptValue, Tag(tag="StringPromptValue")],
Annotated[ChatPromptValueConcrete, Tag(tag="ChatPromptValueConcrete")],
Annotated[AgentAction, Tag(tag="AgentAction")],
Annotated[AgentFinish, Tag(tag="AgentFinish")],
Annotated[AgentActionMessageLog, Tag(tag="AgentActionMessageLog")],
Annotated[ChatGeneration, Tag(tag="ChatGeneration")],
Annotated[Generation, Tag(tag="Generation")],
Annotated[ChatGenerationChunk, Tag(tag="ChatGenerationChunk")],
Annotated[LLMResult, Tag(tag="LLMResult")],
],
Field(discriminator=Discriminator(_get_type)),
__root__: Union[
Document,
HumanMessage,
SystemMessage,
ChatMessage,
FunctionMessage,
AIMessage,
HumanMessageChunk,
SystemMessageChunk,
ChatMessageChunk,
FunctionMessageChunk,
AIMessageChunk,
StringPromptValue,
ChatPromptValueConcrete,
AgentAction,
AgentFinish,
AgentActionMessageLog,
LLMResult,
ChatGeneration,
Generation,
ChatGenerationChunk,
]
]
def default(obj) -> Any:
"""Default serialization for well known objects."""
if isinstance(obj, BaseModel):
return obj.model_dump()
return obj.dict()
return super().default(obj)
@@ -115,10 +96,12 @@ def _decode_lc_objects(value: Any) -> Any:
v = {key: _decode_lc_objects(v) for key, v in value.items()}
try:
obj = WellKnownLCObject.model_validate(v)
parsed = obj.root
obj = WellKnownLCObject.parse_obj(v)
parsed = obj.__root__
if set(parsed.dict()) != set(value):
raise ValueError("Invalid object")
return parsed
except (ValidationError, ValueError, TypeError):
except (ValidationError, ValueError):
return v
elif isinstance(value, list):
return [_decode_lc_objects(item) for item in value]
@@ -140,12 +123,12 @@ def _decode_event_data(value: Any) -> Any:
"""Decode the event data from a JSON object representation."""
if isinstance(value, dict):
try:
obj = CallbackEvent.model_validate(value)
return obj.root
obj = CallbackEvent.parse_obj(value)
return obj.__root__
except ValidationError:
try:
obj = WellKnownLCObject.model_validate(value)
return obj.root
obj = WellKnownLCObject.parse_obj(value)
return obj.__root__
except ValidationError:
return {key: _decode_event_data(v) for key, v in value.items()}
elif isinstance(value, list):
@@ -158,54 +141,44 @@ def _decode_event_data(value: Any) -> Any:
class Serializer(abc.ABC):
@abc.abstractmethod
def dumpd(self, obj: Any) -> Any:
"""Convert the given object to a JSON serializable object."""
return orjson.loads(self.dumps(obj))
@abc.abstractmethod
def dumps(self, obj: Any) -> bytes:
"""Dump the given object as a JSON string."""
@abc.abstractmethod
def loads(self, s: bytes) -> Any:
"""Load the given JSON string."""
@abc.abstractmethod
def loadd(self, obj: Any) -> Any:
"""Load the given object."""
class WellKnownLCSerializer(Serializer):
def dumpd(self, obj: Any) -> Any:
"""Convert the given object to a JSON serializable object."""
return orjson.loads(orjson.dumps(obj, default=default))
def dumps(self, obj: Any) -> bytes:
"""Dump the given object as a JSON string."""
return orjson.dumps(obj, default=default)
def loadd(self, obj: Any) -> Any:
"""Load the given object."""
return _decode_lc_objects(obj)
def loads(self, s: bytes) -> Any:
"""Load the given JSON string."""
return self.loadd(orjson.loads(s))
@abc.abstractmethod
def dumps(self, obj: Any) -> bytes:
"""Dump the given object to a JSON byte string."""
@abc.abstractmethod
def loadd(self, s: bytes) -> Any:
"""Given a python object, load it into a well known object.
The obj represents content that was json loaded from a string, but
not yet validated or converted into a well known object.
"""
class WellKnownLCSerializer(Serializer):
"""A pre-defined serializer for well known LangChain objects.
This is the default serialized used by LangServe for serializing and
de-serializing well known LangChain objects.
If you need to extend the serialization capabilities for your own application,
feel free to create a new instance of the Serializer class and implement
the abstract methods dumps and loadd.
"""
def dumps(self, obj: Any) -> bytes:
"""Dump the given object to a JSON byte string."""
return orjson.dumps(obj, default=default)
def loadd(self, obj: Any) -> Any:
"""Given a python object, load it into a well known object.
The obj represents content that was json loaded from a string, but
not yet validated or converted into a well known object.
"""
return _decode_lc_objects(obj)
def _project_top_level(model: BaseModel) -> Dict[str, Any]:
"""Project the top level of the model as dict."""
return {key: getattr(model, key) for key in model.model_fields}
return {key: getattr(model, key) for key in model.__fields__}
def load_events(events: Any) -> List[Dict[str, Any]]:
@@ -236,15 +209,15 @@ def load_events(events: Any) -> List[Dict[str, Any]]:
# Then validate the event
try:
full_event = CallbackEvent.model_validate(decoded_event_data)
full_event = CallbackEvent.parse_obj(decoded_event_data)
except ValidationError as e:
msg = f"Encountered an invalid event: {e}"
if "type" in decoded_event_data:
msg += f" of type {repr(decoded_event_data['type'])}"
msg += f' of type {repr(decoded_event_data["type"])}'
_log_error_message_once(msg)
continue
decoded_event_data = _project_top_level(full_event.root)
decoded_event_data = _project_top_level(full_event.__root__)
if decoded_event_data["type"].endswith("_error"):
# Data is validated by this point, so we can assume that the shape
+362 -387
View File
@@ -5,8 +5,6 @@ This code contains integration for langchain runnables with FastAPI.
The main entry point is the `add_routes` function which adds the routes to an existing
FastAPI app or APIRouter.
"""
import warnings
import weakref
from typing import (
Any,
@@ -17,23 +15,21 @@ from typing import (
Union,
)
from langchain_core.runnables import Runnable
from pydantic import BaseModel
from langchain.schema.runnable import Runnable
from typing_extensions import Annotated
from langserve.api_handler import (
APIHandler,
PerRequestConfigModifier,
TokenFeedbackConfig,
_is_hosted,
from langserve.api_handler import APIHandler, PerRequestConfigModifier, _is_hosted
from langserve.pydantic_v1 import (
_PYDANTIC_MAJOR_VERSION,
PYDANTIC_VERSION,
BaseModel,
)
from langserve.serialization import Serializer
try:
from fastapi import APIRouter, Body, Depends, FastAPI, Request, Response
from fastapi import APIRouter, Depends, FastAPI, Request, Response
except ImportError:
# [server] extra not installed
APIRouter = Body = Depends = FastAPI = Request = Response = Any
APIRouter = Depends = FastAPI = Request = Response = Any
# A function that that takes a config and a raw request
# and updates the config based on the request.
@@ -43,15 +39,71 @@ except ImportError:
# Duplicated model names break fastapi's openapi generation.
_APP_SEEN = weakref.WeakSet()
# Keeps track of the paths that have been associated with each app.
# Each runnable registered with an APP will have a unique path.
# An APP can have multiple runnables registered with it.
# There are multiple APPs as it's common to use APIRouter in larger
# FastAPI applications.
_APP_TO_PATHS = weakref.WeakKeyDictionary()
def _setup_global_app_handlers(app: Union[FastAPI, APIRouter]) -> None:
@app.on_event("startup")
async def startup_event():
LANGSERVE = r"""
__ ___ .__ __. _______ _______. _______ .______ ____ ____ _______
| | / \ | \ | | / _____| / || ____|| _ \ \ \ / / | ____|
| | / ^ \ | \| | | | __ | (----`| |__ | |_) | \ \/ / | |__
| | / /_\ \ | . ` | | | |_ | \ \ | __| | / \ / | __|
| `----./ _____ \ | |\ | | |__| | .----) | | |____ | |\ \----. \ / | |____
|_______/__/ \__\ |__| \__| \______| |_______/ |_______|| _| `._____| \__/ |_______|
""" # noqa: E501
def green(text: str) -> str:
"""Return the given text in green."""
return "\x1b[1;32;40m" + text + "\x1b[0m"
def orange(text: str) -> str:
"""Return the given text in orange."""
return "\x1b[1;31;40m" + text + "\x1b[0m"
paths = _APP_TO_PATHS[app]
print(LANGSERVE)
for path in paths:
print(
f'{green("LANGSERVE:")} Playground for chain "{path or ""}/" is '
f"live at:"
)
print(f'{green("LANGSERVE:")}')
print(f'{green("LANGSERVE:")} └──> {path}/playground/')
print(f'{green("LANGSERVE:")}')
print(f'{green("LANGSERVE:")} See all available routes at {app.docs_url}/')
if _PYDANTIC_MAJOR_VERSION == 2:
print()
print(f'{orange("LANGSERVE:")} ', end="")
print(
f"⚠️ Using pydantic {PYDANTIC_VERSION}. "
f"OpenAPI docs for invoke, batch, stream, stream_log "
f"endpoints will not be generated. API endpoints and playground "
f"should work as expected. "
f"If you need to see the docs, you can downgrade to pydantic 1. "
"For example, `pip install pydantic==1.10.13`. "
f"See https://github.com/tiangolo/fastapi/issues/10360 for details."
)
print()
def _register_path_for_app(app: Union[FastAPI, APIRouter], path: str) -> None:
"""Register a path when its added to app. Raise if path already seen."""
if app in _APP_TO_PATHS:
seen_paths = _APP_TO_PATHS.get(app)
if path in seen_paths:
raise ValueError(
f"A runnable already exists at path: {path}. If adding "
f"multiple runnables make sure they have different paths."
)
seen_paths.add(path)
else:
_setup_global_app_handlers(app)
_APP_TO_PATHS[app] = {path}
# This is the type annotation
EndpointName = Literal[
"invoke",
@@ -78,7 +130,6 @@ KNOWN_ENDPOINTS = {
"stream_events",
"playground",
"feedback",
"token_feedback",
"public_trace_link",
"input_schema",
"config_schema",
@@ -130,7 +181,6 @@ class _EndpointConfiguration:
is_output_schema_enabled = True
is_config_schema_enabled = True
is_config_hash_enabled = True
is_token_feedback_enabled = True
else:
disabled_endpoints_ = set(name.lower() for name in disabled_endpoints)
if disabled_endpoints_ - KNOWN_ENDPOINTS:
@@ -148,12 +198,12 @@ class _EndpointConfiguration:
is_output_schema_enabled = "output_schema" not in disabled_endpoints_
is_config_schema_enabled = "config_schema" not in disabled_endpoints_
is_config_hash_enabled = "config_hashes" not in disabled_endpoints_
is_token_feedback_enabled = "token_feedback" not in disabled_endpoints_
else:
enabled_endpoints_ = set(name.lower() for name in enabled_endpoints)
if enabled_endpoints_ - KNOWN_ENDPOINTS:
unknown = enabled_endpoints_ - KNOWN_ENDPOINTS
raise ValueError(f"Got unknown endpoint names: {unknown}")
raise ValueError(
f"Got unknown endpoint names: {enabled_endpoints_- KNOWN_ENDPOINTS}"
)
is_invoke_enabled = "invoke" in enabled_endpoints_
is_batch_enabled = "batch" in enabled_endpoints_
is_stream_enabled = "stream" in enabled_endpoints_
@@ -164,7 +214,6 @@ class _EndpointConfiguration:
is_output_schema_enabled = "output_schema" in enabled_endpoints_
is_config_schema_enabled = "config_schema" in enabled_endpoints_
is_config_hash_enabled = "config_hashes" in enabled_endpoints_
is_token_feedback_enabled = "token_feedback" in enabled_endpoints_
self.is_invoke_enabled = is_invoke_enabled
self.is_batch_enabled = is_batch_enabled
@@ -178,68 +227,6 @@ class _EndpointConfiguration:
self.is_config_hash_enabled = is_config_hash_enabled
self.is_feedback_enabled = enable_feedback_endpoint
self.is_public_trace_link_enabled = enable_public_trace_link_endpoint
self.is_token_feedback_enabled = is_token_feedback_enabled
def _register_path_for_app(
app: Union[FastAPI, APIRouter],
path: str,
endpoint_configuration: _EndpointConfiguration,
) -> None:
"""Register a path when its added to app. Raise if path already seen."""
if app in _APP_TO_PATHS:
seen_paths = _APP_TO_PATHS.get(app)
if path in seen_paths:
raise ValueError(
f"A runnable already exists at path: {path}. If adding "
f"multiple runnables make sure they have different paths."
)
seen_paths.add(path)
else:
_setup_global_app_handlers(app, endpoint_configuration)
_APP_TO_PATHS[app] = {path}
def _setup_global_app_handlers(
app: Union[FastAPI, APIRouter], endpoint_configuration: _EndpointConfiguration
) -> None:
with warnings.catch_warnings():
# We are using deprecated functionality here.
# We should re-write to use lifetime events at some point, and yielding
# an APIRouter instance to the caller.
warnings.filterwarnings(
"ignore",
"[\\s.]*on_event is deprecated[\\s.]*",
category=DeprecationWarning,
)
@app.on_event("startup")
async def startup_event():
LANGSERVE = r"""
__ ___ .__ __. _______ _______. _______ .______ ____ ____ _______
| | / \ | \ | | / _____| / || ____|| _ \ \ \ / / | ____|
| | / ^ \ | \| | | | __ | (----`| |__ | |_) | \ \/ / | |__
| | / /_\ \ | . ` | | | |_ | \ \ | __| | / \ / | __|
| `----./ _____ \ | |\ | | |__| | .----) | | |____ | |\ \----. \ / | |____
|_______/__/ \__\ |__| \__| \______| |_______/ |_______|| _| `._____| \__/ |_______|
""" # noqa: E501
def green(text: str) -> str:
"""Return the given text in green."""
return "\x1b[1;32;40m" + text + "\x1b[0m"
paths = _APP_TO_PATHS[app]
print(LANGSERVE)
for path in paths:
if endpoint_configuration.is_playground_enabled:
print(
f'{green("LANGSERVE:")} Playground for chain "{path or ""}/" '
f"is live at:"
)
print(f"{green('LANGSERVE:')}")
print(f"{green('LANGSERVE:')} └──> {path}/playground/")
print(f"{green('LANGSERVE:')}")
print(f"{green('LANGSERVE:')} See all available routes at {app.docs_url}/")
# PUBLIC API
@@ -256,15 +243,12 @@ def add_routes(
include_callback_events: bool = False,
per_req_config_modifier: Optional[PerRequestConfigModifier] = None,
enable_feedback_endpoint: bool = _is_hosted(),
token_feedback_config: Optional[TokenFeedbackConfig] = None,
enable_public_trace_link_endpoint: bool = False,
disabled_endpoints: Optional[Sequence[EndpointName]] = None,
stream_log_name_allow_list: Optional[Sequence[str]] = None,
enabled_endpoints: Optional[Sequence[EndpointName]] = None,
dependencies: Optional[Sequence[Depends]] = None,
playground_type: Literal["default", "chat"] = "default",
astream_events_version: Literal["v1", "v2"] = "v2",
serializer: Optional[Serializer] = None,
) -> None:
"""Register the routes on the given FastAPI app or APIRouter.
@@ -313,16 +297,6 @@ def add_routes(
to LangSmith. Enabled by default. If this flag is disabled or LangSmith
tracing is not enabled for the runnable, then 400 errors will be thrown
when accessing the feedback endpoint.
token_feedback_config: optional configuration for token based feedback.
**Attention** this is distinct from `enable_feedback_endpoint`.
When provided, feedback tokens will be included in the response
metadata that can be used to provide feedback on the run.
In addition, an endpoint will be created for submitting feedback
using the feedback tokens. This is a safer option for public facing
APIs as they scope the feedback to a specific run id and key
and include an expiration time.
This endpoint will be created at /token_feedback
**BETA**: This feature is in beta and may change in the future.
enable_public_trace_link_endpoint: Whether to enable an endpoint for
end-users to publicly view LangSmith traces of your chain runs.
WARNING: THIS WILL EXPOSE THE INTERNAL STATE OF YOUR RUN AND CHAIN AS
@@ -378,25 +352,7 @@ def add_routes(
dependencies: list of dependencies to be applied to the *path operation*.
See [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
playground_type: The type of playground to serve. The default is "default".
- default: supports more types of inputs / outputs. Not optimized
for any particular use case.
- chat: UX is optimized for chat-like interactions. Please review
the README in langserve for more details about constraints (e.g.,
which message types are supported etc.)
astream_events_version: version of the stream events endpoint to use.
By default "v2".
serializer: The serializer to use for serializing the output. If not provided,
the default serializer will be used.
""" # noqa: E501
if not isinstance(runnable, Runnable):
raise TypeError(
f"Expected a Runnable, got {type(runnable)}. "
"The second argument to add_routes should be a Runnable instance."
"add_route(app, runnable, ...) is the correct usage."
"Please make sure that you are using a runnable which is an instance of "
"langchain_core.runnables.Runnable."
)
endpoint_configuration = _EndpointConfiguration(
enabled_endpoints=enabled_endpoints,
disabled_endpoints=disabled_endpoints,
@@ -422,7 +378,7 @@ def add_routes(
if isinstance(app, FastAPI): # type: ignore
# Cannot do this checking logic for a router since
# API routers are not hashable
_register_path_for_app(app, path, endpoint_configuration)
_register_path_for_app(app, path)
# Determine the base URL for the playground endpoint
prefix = app.prefix if isinstance(app, APIRouter) else "" # type: ignore
@@ -445,15 +401,11 @@ def add_routes(
config_keys=config_keys,
include_callback_events=include_callback_events,
enable_feedback_endpoint=enable_feedback_endpoint,
token_feedback_config=token_feedback_config,
enable_public_trace_link_endpoint=enable_public_trace_link_endpoint,
per_req_config_modifier=per_req_config_modifier,
stream_log_name_allow_list=stream_log_name_allow_list,
playground_type=playground_type,
astream_events_version=astream_events_version,
serializer=serializer,
)
namespace = path or ""
route_tags = [path.strip("/")] if path else None
@@ -472,9 +424,35 @@ def add_routes(
if hasattr(app, "openapi_tags") and (path or (app not in _APP_SEEN)):
if not path:
_APP_SEEN.add(app)
default_endpoint_tags = {
"name": route_tags[0] if route_tags else "default",
}
if _PYDANTIC_MAJOR_VERSION == 1:
# Documentation for the default endpoints
default_endpoint_tags = {
"name": route_tags[0] if route_tags else "default",
}
elif _PYDANTIC_MAJOR_VERSION == 2:
# When using pydantic v2, we cannot generate openapi docs for
# the invoke/batch/stream/stream_log endpoints since the underlying
# models are from the pydantic.v1 namespace and cannot be supported
# by FastAPI's.
# https://github.com/tiangolo/fastapi/issues/10360
default_endpoint_tags = {
"name": route_tags[0] if route_tags else "default",
"description": (
f"⚠️ Using pydantic {PYDANTIC_VERSION}. "
f"OpenAPI docs for `invoke`, `batch`, `stream`, `stream_log` "
f"endpoints will not be generated. API endpoints and playground "
f"should work as expected. "
f"If you need to see the docs, you can downgrade to pydantic 1. "
"For example, `pip install pydantic==1.10.13`"
f"See https://github.com/tiangolo/fastapi/issues/10360 for details."
),
}
else:
raise AssertionError(
f"Expected pydantic major version 1 or 2, got {_PYDANTIC_MAJOR_VERSION}"
)
if endpoint_configuration.is_config_hash_enabled:
app.openapi_tags = [
*(getattr(app, "openapi_tags", []) or []),
@@ -719,12 +697,6 @@ def add_routes(
include_in_schema=False,
)(playground)
if endpoint_configuration.is_token_feedback_enabled:
app.post(
namespace + "/token_feedback",
dependencies=dependencies,
)(api_handler.create_feedback_from_token)
if enable_feedback_endpoint:
app.post(
namespace + "/feedback",
@@ -751,326 +723,329 @@ def add_routes(
# Documentation variants of end points.
#######################################
# At the moment, we only support pydantic 1.x for documentation
InvokeRequest = api_handler.InvokeRequest
InvokeResponse = api_handler.InvokeResponse
BatchRequest = api_handler.BatchRequest
BatchResponse = api_handler.BatchResponse
StreamRequest = api_handler.StreamRequest
StreamLogRequest = api_handler.StreamLogRequest
StreamEventsRequest = api_handler.StreamEventsRequest
if _PYDANTIC_MAJOR_VERSION == 1:
InvokeRequest = api_handler.InvokeRequest
InvokeResponse = api_handler.InvokeResponse
BatchRequest = api_handler.BatchRequest
BatchResponse = api_handler.BatchResponse
StreamRequest = api_handler.StreamRequest
StreamLogRequest = api_handler.StreamLogRequest
StreamEventsRequest = api_handler.StreamEventsRequest
if endpoint_configuration.is_invoke_enabled:
if endpoint_configuration.is_invoke_enabled:
async def _invoke_docs(
invoke_request: Annotated[InvokeRequest, Body()],
config_hash: str = "",
) -> InvokeResponse:
"""Invoke the runnable with the given input and config."""
raise AssertionError("This endpoint should not be reachable.")
async def _invoke_docs(
invoke_request: Annotated[InvokeRequest, InvokeRequest],
config_hash: str = "",
) -> InvokeResponse:
"""Invoke the runnable with the given input and config."""
raise AssertionError("This endpoint should not be reachable.")
invoke_docs = app.post(
f"{namespace}/invoke",
response_model=api_handler.InvokeResponse,
tags=route_tags,
name=_route_name("invoke"),
dependencies=dependencies,
)(_invoke_docs)
if endpoint_configuration.is_config_hash_enabled:
app.post(
namespace + "/c/{config_hash}/invoke",
invoke_docs = app.post(
f"{namespace}/invoke",
response_model=api_handler.InvokeResponse,
tags=route_tags_with_config,
name=_route_name_with_config("invoke"),
tags=route_tags,
name=_route_name("invoke"),
dependencies=dependencies,
description=(
"This endpoint is to be used with share links generated by the "
"LangServe playground. "
"The hash is an LZString compressed JSON string. "
"For regular use cases, use the /invoke endpoint without "
"the `c/{config_hash}` path parameter."
),
)(invoke_docs)
)(_invoke_docs)
if endpoint_configuration.is_batch_enabled:
if endpoint_configuration.is_config_hash_enabled:
app.post(
namespace + "/c/{config_hash}/invoke",
response_model=api_handler.InvokeResponse,
tags=route_tags_with_config,
name=_route_name_with_config("invoke"),
dependencies=dependencies,
description=(
"This endpoint is to be used with share links generated by the "
"LangServe playground. "
"The hash is an LZString compressed JSON string. "
"For regular use cases, use the /invoke endpoint without "
"the `c/{config_hash}` path parameter."
),
)(invoke_docs)
async def _batch_docs(
batch_request: Annotated[BatchRequest, Body()],
config_hash: str = "",
) -> BatchResponse:
"""Batch invoke the runnable with the given inputs and config."""
raise AssertionError("This endpoint should not be reachable.")
if endpoint_configuration.is_batch_enabled:
batch_docs = app.post(
f"{namespace}/batch",
response_model=BatchResponse,
tags=route_tags,
name=_route_name("batch"),
dependencies=dependencies,
)(_batch_docs)
async def _batch_docs(
batch_request: Annotated[BatchRequest, BatchRequest],
config_hash: str = "",
) -> BatchResponse:
"""Batch invoke the runnable with the given inputs and config."""
raise AssertionError("This endpoint should not be reachable.")
if endpoint_configuration.is_config_hash_enabled:
app.post(
namespace + "/c/{config_hash}/batch",
batch_docs = app.post(
f"{namespace}/batch",
response_model=BatchResponse,
tags=route_tags_with_config,
name=_route_name_with_config("batch"),
tags=route_tags,
name=_route_name("batch"),
dependencies=dependencies,
description=(
"This endpoint is to be used with share links generated by the "
"LangServe playground. "
"The hash is an LZString compressed JSON string. "
"For regular use cases, use the /batch endpoint without "
"the `c/{config_hash}` path parameter."
),
)(batch_docs)
)(_batch_docs)
if endpoint_configuration.is_stream_enabled:
if endpoint_configuration.is_config_hash_enabled:
app.post(
namespace + "/c/{config_hash}/batch",
response_model=BatchResponse,
tags=route_tags_with_config,
name=_route_name_with_config("batch"),
dependencies=dependencies,
description=(
"This endpoint is to be used with share links generated by the "
"LangServe playground. "
"The hash is an LZString compressed JSON string. "
"For regular use cases, use the /batch endpoint without "
"the `c/{config_hash}` path parameter."
),
)(batch_docs)
async def _stream_docs(
stream_request: Annotated[StreamRequest, Body()],
config_hash: str = "",
) -> EventSourceResponse:
"""Invoke the runnable stream the output.
if endpoint_configuration.is_stream_enabled:
This endpoint allows to stream the output of the runnable.
async def _stream_docs(
stream_request: Annotated[StreamRequest, StreamRequest],
config_hash: str = "",
) -> EventSourceResponse:
"""Invoke the runnable stream the output.
The endpoint uses a server sent event stream to stream the output.
This endpoint allows to stream the output of the runnable.
https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events
The endpoint uses a server sent event stream to stream the output.
Important: Set the "text/event-stream" media type for request headers if
not using an existing SDK.
https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events
The events that the endpoint uses are the following:
* "data" -- used for streaming the output of the runnale
* "error" -- signaling an error while streaming and ends the stream.
* "end" -- used for signaling the end of the stream
* "metadata" -- used for sending metadata about the run; e.g., run id.
Important: Set the "text/event-stream" media type for request headers if
not using an existing SDK.
The event type is in the "event" field of the event.
The payload associated with the event is in the "data" field
of the event, and it is JSON encoded.
The events that the endpoint uses are the following:
* "data" -- used for streaming the output of the runnale
* "error" -- signaling an error while streaming and ends the stream.
* "end" -- used for signaling the end of the stream
* "metadata" -- used for sending metadata about the run; e.g., run id.
The event type is in the "event" field of the event.
The payload associated with the event is in the "data" field
of the event, and it is JSON encoded.
Here are some examples of events that the endpoint can send:
Regular streaming event:
{
"event": "data",
"data": {
...
}
}
Internal server error:
{
"event": "error",
"data": {
"status_code": 500,
"message": "Internal Server Error"
}
}
Streaming ended so client should stop listening for events:
{
"event": "end",
}
"""
raise AssertionError("This endpoint should not be reachable.")
stream_docs = app.post(
f"{namespace}/stream",
include_in_schema=True,
tags=route_tags,
name=_route_name("stream"),
dependencies=dependencies,
description=(
"This endpoint allows to stream the output of the runnable. "
"The endpoint uses a server sent event stream to stream the "
"output. "
"https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events"
),
)(_stream_docs)
if endpoint_configuration.is_config_hash_enabled:
app.post(
namespace + "/c/{config_hash}/stream",
include_in_schema=True,
tags=route_tags_with_config,
name=_route_name_with_config("stream"),
dependencies=dependencies,
description=(
"This endpoint is to be used with share links generated by the "
"LangServe playground. "
"The hash is an LZString compressed JSON string. "
"For regular use cases, use the /stream endpoint without "
"the `c/{config_hash}` path parameter."
),
)(stream_docs)
if endpoint_configuration.is_stream_log_enabled:
async def _stream_log_docs(
stream_log_request: Annotated[StreamLogRequest, Body()],
config_hash: str = "",
) -> EventSourceResponse:
"""Invoke the runnable stream_log the output.
This endpoint allows to stream the output of the runnable, including
the output of all intermediate steps.
The endpoint uses a server sent event stream to stream the output.
https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events
Important: Set the "text/event-stream" media type for request headers if
not using an existing SDK.
This endpoint uses two different types of events:
* data - for streaming the output of the runnable
Here are some examples of events that the endpoint can send:
Regular streaming event:
{
"event": "data",
"data": {
...
...
}
}
* error - for signaling an error in the stream, also ends the stream.
{
"event": "error",
"data": {
"status_code": 500,
"message": "Internal Server Error"
Internal server error:
{
"event": "error",
"data": {
"status_code": 500,
"message": "Internal Server Error"
}
}
}
* end - for signaling the end of the stream.
This helps the client to know when to stop listening for events and
know that the streaming has ended successfully.
Streaming ended so client should stop listening for events:
{
"event": "end",
}
"""
raise AssertionError("This endpoint should not be reachable.")
"""
raise AssertionError("This endpoint should not be reachable.")
app.post(
f"{namespace}/stream_log",
include_in_schema=True,
tags=route_tags,
name=_route_name("stream_log"),
dependencies=dependencies,
)(_stream_log_docs)
if endpoint_configuration.is_config_hash_enabled:
app.post(
namespace + "/c/{config_hash}/stream_log",
stream_docs = app.post(
f"{namespace}/stream",
include_in_schema=True,
tags=route_tags_with_config,
name=_route_name_with_config("stream_log"),
tags=route_tags,
name=_route_name("stream"),
dependencies=dependencies,
description=(
"This endpoint is to be used with share links generated by the "
"LangServe playground. "
"The hash is an LZString compressed JSON string. "
"For regular use cases, use the /stream_log endpoint without "
"the `c/{config_hash}` path parameter."
"This endpoint allows to stream the output of the runnable. "
"The endpoint uses a server sent event stream to stream the "
"output. "
"https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events"
),
)(_stream_docs)
if endpoint_configuration.is_config_hash_enabled:
app.post(
namespace + "/c/{config_hash}/stream",
include_in_schema=True,
tags=route_tags_with_config,
name=_route_name_with_config("stream"),
dependencies=dependencies,
description=(
"This endpoint is to be used with share links generated by the "
"LangServe playground. "
"The hash is an LZString compressed JSON string. "
"For regular use cases, use the /stream endpoint without "
"the `c/{config_hash}` path parameter."
),
)(stream_docs)
if endpoint_configuration.is_stream_log_enabled:
async def _stream_log_docs(
stream_log_request: Annotated[StreamLogRequest, StreamLogRequest],
config_hash: str = "",
) -> EventSourceResponse:
"""Invoke the runnable stream_log the output.
This endpoint allows to stream the output of the runnable, including
the output of all intermediate steps.
The endpoint uses a server sent event stream to stream the output.
https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events
Important: Set the "text/event-stream" media type for request headers if
not using an existing SDK.
This endpoint uses two different types of events:
* data - for streaming the output of the runnable
{
"event": "data",
"data": {
...
}
}
* error - for signaling an error in the stream, also ends the stream.
{
"event": "error",
"data": {
"status_code": 500,
"message": "Internal Server Error"
}
}
* end - for signaling the end of the stream.
This helps the client to know when to stop listening for events and
know that the streaming has ended successfully.
{
"event": "end",
}
"""
raise AssertionError("This endpoint should not be reachable.")
app.post(
f"{namespace}/stream_log",
include_in_schema=True,
tags=route_tags,
name=_route_name("stream_log"),
dependencies=dependencies,
)(_stream_log_docs)
if has_astream_events and endpoint_configuration.is_stream_events_enabled:
if endpoint_configuration.is_config_hash_enabled:
app.post(
namespace + "/c/{config_hash}/stream_log",
include_in_schema=True,
tags=route_tags_with_config,
name=_route_name_with_config("stream_log"),
description=(
"This endpoint is to be used with share links generated by the "
"LangServe playground. "
"The hash is an LZString compressed JSON string. "
"For regular use cases, use the /stream_log endpoint without "
"the `c/{config_hash}` path parameter."
),
dependencies=dependencies,
)(_stream_log_docs)
async def _stream_events_docs(
stream_events_request: Annotated[StreamEventsRequest, Body()],
config_hash: str = "",
) -> EventSourceResponse:
"""Stream events from the given runnable.
if has_astream_events and endpoint_configuration.is_stream_events_enabled:
This endpoint allows to stream events from the runnable, including
events from all intermediate steps.
async def _stream_events_docs(
stream_events_request: Annotated[
StreamEventsRequest, StreamEventsRequest
],
config_hash: str = "",
) -> EventSourceResponse:
"""Stream events from the given runnable.
**Attention**
This endpoint allows to stream events from the runnable, including
events from all intermediate steps.
This is a new endpoint that only works for langchain-core >= 0.1.14.
**Attention**
It belongs to a Beta API that may change in the future.
This is a new endpoint that only works for langchain-core >= 0.1.14.
**Important**
Specify filters to the events you want to receive by setting
the appropriate filters in the request body.
It belongs to a Beta API that may change in the future.
This will help avoid sending too much data over the network.
**Important**
Specify filters to the events you want to receive by setting
the appropriate filters in the request body.
It will also prevent serialization issues with
any unsupported types since it won't need to serialize events
that aren't transmitted.
This will help avoid sending too much data over the network.
The endpoint uses a server sent event stream to stream the output.
It will also prevent serialization issues with
any unsupported types since it won't need to serialize events
that aren't transmitted.
https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events
The endpoint uses a server sent event stream to stream the output.
The encoding of events follows the following format:
https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events
* data - for streaming the output of the runnable
The encoding of events follows the following format:
* data - for streaming the output of the runnable
{
"event": "data",
"data": {
...
}
}
* error - for signaling an error in the stream, also ends the stream.
{
"event": "data",
"event": "error",
"data": {
...
"status_code": 500,
"message": "Internal Server Error"
}
}
* error - for signaling an error in the stream, also ends the stream.
* end - for signaling the end of the stream.
{
"event": "error",
"data": {
"status_code": 500,
"message": "Internal Server Error"
}
}
This helps the client to know when to stop listening for events and
know that the streaming has ended successfully.
* end - for signaling the end of the stream.
{
"event": "end",
}
This helps the client to know when to stop listening for events and
know that the streaming has ended successfully.
`data` for the `data` event is a JSON object that corresponds
to a serialized representation of a StreamEvent.
{
"event": "end",
}
See LangChain documentation for more information about astream_events.
"""
raise AssertionError("This endpoint should not be reachable.")
`data` for the `data` event is a JSON object that corresponds
to a serialized representation of a StreamEvent.
See LangChain documentation for more information about astream_events.
"""
raise AssertionError("This endpoint should not be reachable.")
app.post(
f"{namespace}/stream_events",
include_in_schema=True,
tags=route_tags,
name=_route_name("stream_events"),
dependencies=dependencies,
)(_stream_events_docs)
if endpoint_configuration.is_config_hash_enabled:
app.post(
namespace + "/c/{config_hash}/stream_events",
f"{namespace}/stream_events",
include_in_schema=True,
tags=route_tags_with_config,
name=_route_name_with_config("stream_events"),
description=(
"This endpoint is to be used with share links generated by the "
"LangServe playground. "
"The hash is an LZString compressed JSON string. "
"For regular use cases, use the /stream_events endpoint "
"without the `c/{config_hash}` path parameter."
),
tags=route_tags,
name=_route_name("stream_events"),
dependencies=dependencies,
)(_stream_events_docs)
if endpoint_configuration.is_config_hash_enabled:
app.post(
namespace + "/c/{config_hash}/stream_events",
include_in_schema=True,
tags=route_tags_with_config,
name=_route_name_with_config("stream_events"),
description=(
"This endpoint is to be used with share links generated by the "
"LangServe playground. "
"The hash is an LZString compressed JSON string. "
"For regular use cases, use the /stream_events endpoint "
"without the `c/{config_hash}` path parameter."
),
dependencies=dependencies,
)(_stream_events_docs)
-138
View File
@@ -1,138 +0,0 @@
"""Adapted from https://github.com/florimondmanca/httpx-sse"""
from contextlib import asynccontextmanager, contextmanager
from typing import Any, AsyncIterator, Iterator, List, Optional
import httpx
from typing_extensions import TypedDict
class ServerSentEvent(TypedDict):
event: Optional[str]
data: Optional[str]
id: Optional[str]
retry: Optional[int]
class SSEDecoder:
def __init__(self) -> None:
self._event = ""
self._data: List[str] = []
self._last_event_id = ""
self._retry: Optional[int] = None
def decode(self, line: str) -> Optional[ServerSentEvent]:
# See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501
if not line:
if (
not self._event
and not self._data
and not self._last_event_id
and self._retry is None
):
return None
sse = {
"event": self._event,
"data": "\n".join(self._data),
"id": self._last_event_id,
"retry": self._retry,
}
# NOTE: as per the SSE spec, do not reset last_event_id.
self._event = ""
self._data = []
self._retry = None
return sse
if line.startswith(":"):
return None
fieldname, _, value = line.partition(":")
if value.startswith(" "):
value = value[1:]
if fieldname == "event":
self._event = value
elif fieldname == "data":
self._data.append(value)
elif fieldname == "id":
if "\0" in value:
pass
else:
self._last_event_id = value
elif fieldname == "retry":
try:
self._retry = int(value)
except (TypeError, ValueError):
pass
else:
pass # Field is ignored.
return None
class EventSource:
def __init__(self, response: httpx.Response) -> None:
self._response = response
def _check_content_type(self) -> None:
"""Check that the response content type is 'text/event-stream'."""
self._response.raise_for_status()
content_type = self._response.headers.get("content-type", "").partition(";")[0]
if "text/event-stream" not in content_type:
raise AssertionError(
"Expected response header Content-Type to contain 'text/event-stream', "
f"got {content_type!r}"
)
@property
def response(self) -> httpx.Response:
return self._response
def iter_sse(self) -> Iterator[ServerSentEvent]:
self._check_content_type()
decoder = SSEDecoder()
for line in self._response.iter_lines():
line = line.rstrip("\n")
sse = decoder.decode(line)
if sse is not None:
yield sse
async def aiter_sse(self) -> AsyncIterator[ServerSentEvent]:
self._check_content_type()
decoder = SSEDecoder()
async for line in self._response.aiter_lines():
line = line.rstrip("\n")
sse = decoder.decode(line)
if sse is not None:
yield sse
@contextmanager
def connect_sse(
client: httpx.Client, method: str, url: str, **kwargs: Any
) -> Iterator[EventSource]:
headers = kwargs.pop("headers", {})
headers["Accept"] = "text/event-stream"
headers["Cache-Control"] = "no-store"
with client.stream(method, url, headers=headers, **kwargs) as response:
yield EventSource(response)
@asynccontextmanager
async def aconnect_sse(
client: httpx.AsyncClient,
method: str,
url: str,
**kwargs: Any,
) -> AsyncIterator[EventSource]:
headers = kwargs.pop("headers", {})
headers["Accept"] = "text/event-stream"
headers["Cache-Control"] = "no-store"
async with client.stream(method, url, headers=headers, **kwargs) as response:
yield EventSource(response)
+101 -95
View File
@@ -16,17 +16,25 @@ Models are created with a namespace to avoid name collisions when hosting
multiple runnables. When present the name collisions prevent fastapi from
generating OpenAPI specs.
"""
from typing import Any, Dict, List, Literal, Optional, Sequence, Union
from uuid import UUID
from langchain_core.documents import Document
from langchain_core.messages import BaseMessage
from langchain_core.outputs import ChatGeneration, Generation, RunInfo
from pydantic import BaseModel, Field, RootModel, create_model
from langchain.schema import (
BaseMessage,
ChatGeneration,
Document,
Generation,
RunInfo,
)
from typing_extensions import Type
from langserve.schema import BatchResponseMetadata, InvokeResponseMetadata
from langserve.schema import BatchResponseMetadata, SingletonResponseMetadata
try:
from pydantic.v1 import BaseModel, Field, create_model
except ImportError:
from pydantic import BaseModel, Field, create_model
# Type that is either a python annotation or a pydantic model that can be
# used to validate the input or output of a runnable.
@@ -62,7 +70,7 @@ def create_invoke_request_model(
),
),
)
invoke_request_type.model_rebuild()
invoke_request_type.update_forward_refs()
return invoke_request_type
@@ -93,7 +101,7 @@ def create_stream_request_model(
),
),
)
stream_request_model.model_rebuild()
stream_request_model.update_forward_refs()
return stream_request_model
@@ -125,7 +133,7 @@ def create_batch_request_model(
),
),
)
batch_request_type.model_rebuild()
batch_request_type.update_forward_refs()
return batch_request_type
@@ -183,7 +191,7 @@ def create_stream_log_request_model(
),
kwargs=(dict, Field(default_factory=dict)),
)
stream_log_request.model_rebuild()
stream_log_request.update_forward_refs()
return stream_log_request
@@ -241,7 +249,7 @@ def create_stream_events_request_model(
),
kwargs=(dict, Field(default_factory=dict)),
)
stream_events_request.model_rebuild()
stream_events_request.update_forward_refs()
return stream_events_request
@@ -256,76 +264,52 @@ class BatchBaseResponse(BaseModel):
def create_invoke_response_model(
namespace: str,
output_type: Validator,
include_callbacks: bool,
) -> Type[BaseModel]:
"""Create a pydantic model for the invoke response."""
# The invoke response uses a key called `output` for the output, so
# other information can be added to the response at a later date.
fields = {
"output": (
output_type,
Field(..., description="The output of the invocation."),
invoke_response_type = create_model(
f"{namespace}InvokeResponse",
output=(output_type, Field(..., description="The output of the invocation.")),
callback_events=(
List[CallbackEvent],
Field(..., description="Callback events generated by the server side."),
),
"metadata": (
InvokeResponseMetadata,
metadata=(
SingletonResponseMetadata,
Field(
...,
description=(
"Metadata about the response that may be useful to specific clients"
"Metadata about the response that may be useful to "
"specific clients"
),
),
),
}
if include_callbacks:
fields["callback_events"] = (
List[CallbackEvent],
Field(
...,
description=("Callback events generated by the server side."),
),
)
invoke_response_type = create_model(
f"{namespace}InvokeResponse",
__base__=InvokeBaseResponse,
**fields,
)
invoke_response_type.model_rebuild()
invoke_response_type.update_forward_refs()
return invoke_response_type
def create_batch_response_model(
namespace: str,
output_type: Validator,
include_callbacks: bool,
) -> Type[BaseModel]:
"""Create a pydantic model for the batch response."""
# The response uses a key called `output` for the output, so
# other information can be added to the response at a later date.
fields = {
"output": (
batch_response_type = create_model(
f"{namespace}BatchResponse",
output=(
List[output_type],
Field(
...,
description="The outputs corresponding to the inputs the "
"batch request.",
),
),
"metadata": (
BatchResponseMetadata,
Field(
...,
description=(
"Metadata about the response that may be useful to specific clients"
"The outputs corresponding to the inputs the batch request."
),
),
),
}
if include_callbacks:
fields["callback_events"] = (
callback_events=(
List[List[CallbackEvent]],
Field(
...,
@@ -335,14 +319,19 @@ def create_batch_response_model(
"list corresponds to the callbacks generated for that input."
),
),
)
batch_response_type = create_model(
f"{namespace}BatchResponse",
),
metadata=(
BatchResponseMetadata,
Field(
...,
description=(
"Metadata about the response that may be useful to specific clients"
),
),
),
__base__=BatchBaseResponse,
**fields,
)
batch_response_type.model_rebuild()
batch_response_type.update_forward_refs()
return batch_response_type
@@ -396,29 +385,27 @@ class StreamEventsParameters(BaseModel):
# status code and a message.
class BaseCallback(BaseModel):
"""Base class for all callback events."""
class OnChainStart(BaseModel):
"""On Chain Start Callback Event."""
serialized: Dict[str, Any]
inputs: Any
run_id: UUID
parent_run_id: Optional[UUID] = None
tags: Optional[List[str]] = None
metadata: Optional[Dict[str, Any]] = None
class OnChainStart(BaseCallback):
"""On Chain Start Callback Event."""
serialized: Optional[Dict[str, Any]] = None
inputs: Any
kwargs: Optional[Dict[str, Any]] = None
kwargs: Any = None
type: Literal["on_chain_start"] = "on_chain_start"
class OnChainEnd(BaseCallback):
class OnChainEnd(BaseModel):
"""On Chain End Callback Event."""
outputs: Any
kwargs: Optional[Dict[str, Any]] = None
run_id: UUID
parent_run_id: Optional[UUID] = None
tags: Optional[List[str]] = None
kwargs: Any = None
type: Literal["on_chain_end"] = "on_chain_end"
@@ -430,35 +417,38 @@ class Error(BaseModel):
type: Literal["error"] = "error"
class OnChainError(BaseCallback):
class OnChainError(BaseModel):
"""On Chain Error Callback Event."""
error: Error
kwargs: Optional[Dict[str, Any]] = None
run_id: UUID
parent_run_id: Optional[UUID] = None
tags: Optional[List[str]] = None
kwargs: Any = None
type: Literal["on_chain_error"] = "on_chain_error"
class OnToolStart(BaseCallback):
class OnToolStart(BaseModel):
"""On Tool Start Callback Event."""
serialized: Optional[Dict[str, Any]] = None
serialized: Dict[str, Any]
input_str: str
run_id: UUID
parent_run_id: Optional[UUID] = None
tags: Optional[List[str]] = None
metadata: Optional[Dict[str, Any]] = None
kwargs: Optional[Dict[str, Any]] = None
kwargs: Any = None
type: Literal["on_tool_start"] = "on_tool_start"
class OnToolEnd(BaseCallback):
class OnToolEnd(BaseModel):
"""On Tool End Callback Event."""
output: str
run_id: UUID
parent_run_id: Optional[UUID] = None
tags: Optional[List[str]] = None
kwargs: Optional[Dict[str, Any]] = None
kwargs: Any = None
type: Literal["on_tool_end"] = "on_tool_end"
@@ -466,29 +456,36 @@ class OnToolError(BaseModel):
"""On Tool Error Callback Event."""
error: Error
kwargs: Optional[Dict[str, Any]] = None
run_id: UUID
parent_run_id: Optional[UUID] = None
tags: Optional[List[str]] = None
kwargs: Any = None
type: Literal["on_tool_error"] = "on_tool_error"
class OnChatModelStart(BaseCallback):
class OnChatModelStart(BaseModel):
"""On Chat Model Start Callback Event."""
serialized: Optional[Dict[str, Any]] = None
serialized: Dict[str, Any]
messages: List[List[BaseMessage]]
kwargs: Optional[Dict[str, Any]] = None
run_id: UUID
parent_run_id: Optional[UUID] = None
tags: Optional[List[str]] = None
metadata: Optional[Dict[str, Any]] = None
kwargs: Any = None
type: Literal["on_chat_model_start"] = "on_chat_model_start"
class OnLLMStart(BaseCallback):
class OnLLMStart(BaseModel):
"""On LLM Start Callback Event."""
serialized: Optional[Dict[str, Any]] = None
serialized: Dict[str, Any]
prompts: List[str]
run_id: UUID
parent_run_id: Optional[UUID] = None
tags: Optional[List[str]] = None
metadata: Optional[Dict[str, Any]] = None
kwargs: Optional[Dict[str, Any]] = None
kwargs: Any = None
type: Literal["on_llm_start"] = "on_llm_start"
@@ -507,44 +504,54 @@ class LLMResult(BaseModel):
"""List of metadata info for model call for each input."""
class OnLLMEnd(BaseCallback):
class OnLLMEnd(BaseModel):
"""On LLM End Callback Event."""
response: LLMResult
kwargs: Optional[Dict[str, Any]] = None
run_id: UUID
parent_run_id: Optional[UUID] = None
tags: Optional[List[str]] = None
kwargs: Any = None
type: Literal["on_llm_end"] = "on_llm_end"
class OnRetrieverStart(BaseCallback):
class OnRetrieverStart(BaseModel):
"""On Retriever Start Callback Event."""
serialized: Optional[Dict[str, Any]] = None
serialized: Dict[str, Any]
query: str
kwargs: Optional[Dict[str, Any]] = None
run_id: UUID
parent_run_id: Optional[UUID] = None
tags: Optional[List[str]] = None
metadata: Optional[Dict[str, Any]] = None
kwargs: Any = None
type: Literal["on_retriever_start"] = "on_retriever_start"
class OnRetrieverError(BaseCallback):
class OnRetrieverError(BaseModel):
"""On Retriever Error Callback Event."""
error: Error
run_id: UUID
parent_run_id: Optional[UUID] = None
tags: Optional[List[str]] = None
kwargs: Optional[Dict[str, Any]] = None
kwargs: Any = None
type: Literal["on_retriever_error"] = "on_retriever_error"
class OnRetrieverEnd(BaseCallback):
class OnRetrieverEnd(BaseModel):
"""On Retriever End Callback Event."""
documents: Sequence[Document]
kwargs: Optional[Dict[str, Any]] = None
run_id: UUID
parent_run_id: Optional[UUID] = None
tags: Optional[List[str]] = None
kwargs: Any = None
type: Literal["on_retriever_end"] = "on_retriever_end"
CallbackEvent = RootModel[
Union[
class CallbackEvent(BaseModel):
__root__: Union[
OnChainStart,
OnChainEnd,
OnChainError,
@@ -558,4 +565,3 @@ CallbackEvent = RootModel[
OnRetrieverEnd,
OnRetrieverError,
]
]
-1
View File
@@ -1,5 +1,4 @@
"""Main entrypoint into package."""
from importlib import metadata
try:
+1 -4
View File
@@ -20,14 +20,11 @@
"devDependencies": {
"@types/react": "^18.2.29",
"prettier": "^3.0.3",
"tsup": "^8.3.5",
"tsup": "^7.2.0",
"typescript": "^5.2.2"
},
"peerDependencies": {
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"resolutions": {
"esbuild": "0.25.0"
}
}
File diff suppressed because it is too large Load Diff
Generated
+2615 -2601
View File
File diff suppressed because it is too large Load Diff
+23 -26
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "langserve"
version = "0.3.3"
version = "0.0.48"
description = ""
readme = "README.md"
authors = ["LangChain"]
@@ -10,61 +10,62 @@ exclude = ["langserve/playground,langserve/chat_playground"]
include = ["langserve/playground/dist/**/*", "langserve/chat_playground/dist/**/*"]
[tool.poetry.dependencies]
python = "^3.10"
httpx = ">=0.23.0,<1.0"
python = "^3.8.1"
httpx = ">=0.23.0" # May be able to decrease this version
fastapi = {version = ">=0.90.1,<1", optional = true}
sse-starlette = {version = "^1.3.0", optional = true}
langchain-core = ">=0.3,<2"
orjson = ">=2,<4"
pydantic = "^2.13"
httpx-sse = {version = ">=0.3.1", optional = true}
pydantic = ">=1"
langchain = ">=0.0.333"
orjson = ">=2"
[tool.poetry.group.dev.dependencies]
jupyterlab = "^4.5.3"
jupyterlab = "^3.6.1"
fastapi = ">=0.90.1"
sse-starlette = "^1.3.0"
httpx-sse = ">=0.3.1"
[tool.poetry.group.typing.dependencies]
[tool.poetry.group.lint.dependencies]
ruff = "^0.15.10"
codespell = "^2.4.2"
ruff = "^0.1.4"
codespell = "^2.2.0"
[tool.poetry.group.test.dependencies]
pytest = "^9.0.3"
pytest-cov = "^7.1.0"
pytest = "^7.2.1"
pytest-cov = "^4.0.0"
pytest-asyncio = "^0.21.1"
pytest-mock = "^3.11.1"
pytest-socket = "^0.7.0"
pytest-socket = "^0.6.0"
pytest-watch = "^4.2.0"
pytest-timeout = "^2.2.0"
[tool.poetry.group.examples.dependencies]
openai = "^2.30.0"
uvicorn = {extras = ["standard"], version = "^0.44.0"}
openai = "^0.28.0"
uvicorn = {extras = ["standard"], version = "^0.23.2"}
fastapi = ">=0.90.1"
sse-starlette = "^1.3.0"
httpx-sse = ">=0.3.1"
[tool.poetry.extras]
# Extras that are used for client
client = ["fastapi"]
client = ["httpx-sse"]
# Extras that are used for server
server = ["sse-starlette", "fastapi"]
# All
all = ["sse-starlette", "fastapi"]
all = ["httpx-sse", "sse-starlette", "fastapi"]
[tool.ruff]
# Same as Black.
line-length = 88
extend-exclude = ["examples"]
[tool.ruff.lint]
select = [
"E", # pycodestyle
"F", # pyflakes
"I", # isort
]
[tool.ruff.lint.isort]
# Same as Black.
line-length = 88
[tool.ruff.isort]
# TODO(Team): Temporary to make isort work with examples.
# examples assume langserve is available as a 3rd party package
# For simplicity we'll define it as first party for now can update later.
@@ -96,7 +97,3 @@ addopts = "--strict-markers --strict-config --durations=5 -vv"
# take more than 5 seconds
timeout = 5
asyncio_mode = "auto"
filterwarnings = [
"ignore::langchain_core._api.beta_decorator.LangChainBetaWarning",
]
-114
View File
@@ -1,114 +0,0 @@
"""Test the playground API."""
import httpx
from fastapi import APIRouter, FastAPI
from httpx import AsyncClient
from langchain_core.runnables import RunnableLambda
from langserve import add_routes
async def test_serve_playground() -> None:
"""Test the server directly via HTTP requests."""
app = FastAPI()
add_routes(
app,
RunnableLambda(lambda foo: "hello"),
)
async with AsyncClient(
base_url="http://localhost:9999", transport=httpx.ASGITransport(app=app)
) as client:
response = await client.get("/playground/index.html")
assert response.status_code == 200
# Test that we can't access files that do not exist.
response = await client.get("/playground/i_do_not_exist.txt")
assert response.status_code == 404
# Test that we can't access files outside of the playground directory
response = await client.get("/playground//etc/passwd")
assert response.status_code == 404
async def test_serve_playground_with_api_router() -> None:
"""Test serving playground from an api router with a prefix."""
app = FastAPI()
# Make sure that we can add routers
# to an API router
router = APIRouter(prefix="/langserve_runnables")
add_routes(
router,
RunnableLambda(lambda foo: "hello"),
path="/chat",
)
app.include_router(router)
async with AsyncClient(
base_url="http://localhost:9999", transport=httpx.ASGITransport(app=app)
) as client:
response = await client.get("/langserve_runnables/chat/playground/index.html")
assert response.status_code == 200
async def test_serve_playground_with_api_router_in_api_router() -> None:
"""Test serving playground from an api router within an api router."""
app = FastAPI()
router = APIRouter(prefix="/foo")
add_routes(
router,
RunnableLambda(lambda foo: "hello"),
)
parent_router = APIRouter(prefix="/parent")
parent_router.include_router(router, prefix="/bar")
# Now add parent router to the app
app.include_router(parent_router)
async with AsyncClient(
base_url="http://localhost:9999", transport=httpx.ASGITransport(app=app)
) as client:
response = await client.get("/parent/bar/foo/playground/index.html")
assert response.status_code == 200
async def test_root_path_on_playground() -> None:
"""Test that the playground respects the root_path for requesting assets"""
for root_path in ("/home/root", "/home/root/"):
app = FastAPI(root_path=root_path)
add_routes(
app,
RunnableLambda(lambda foo: "hello"),
path="/chat",
)
router = APIRouter(prefix="/router")
add_routes(
router,
RunnableLambda(lambda foo: "hello"),
path="/chat",
)
app.include_router(router)
async_client = AsyncClient(
base_url="http://localhost:9999", transport=httpx.ASGITransport(app=app)
)
response = await async_client.get("/chat/playground/index.html")
assert response.status_code == 200
assert (
f'src="{root_path.rstrip("/")}/chat/playground/assets/'
in response.content.decode()
), "html should contain reference to playground assets with root_path prefix"
response = await async_client.get("/router/chat/playground/index.html")
assert response.status_code == 200
assert (
f'src="{root_path.rstrip("/")}/router/chat/playground/assets/'
in response.content.decode()
), "html should contain reference to playground assets with root_path prefix"
+4 -3
View File
@@ -1,13 +1,14 @@
import uuid
from langchain_core.prompts import ChatPromptTemplate
from langserve.callbacks import AsyncEventAggregatorCallback, replace_uuids
from tests.unit_tests.utils.llms import FakeListLLM
async def test_event_aggregator() -> None:
"""Test that the event aggregator is aggregating events."""
from langchain.llms import FakeListLLM
from langchain.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_template("{question}")
llm = FakeListLLM(responses=["hello", "world"])
+11 -17
View File
@@ -4,23 +4,19 @@ from enum import Enum
from typing import Any
import pytest
from langchain_core.documents.base import Document
from langchain_core.messages import HumanMessage, HumanMessageChunk, SystemMessage
from langchain_core.outputs import ChatGeneration
from pydantic import BaseModel
from langserve.serialization import (
WellKnownLCObject,
WellKnownLCSerializer,
load_events,
from langchain.schema.messages import (
HumanMessage,
HumanMessageChunk,
SystemMessage,
)
from langchain.schema.output import ChatGeneration
try:
from pydantic.v1 import BaseModel
except ImportError:
from pydantic import BaseModel
def test_document_serialization() -> None:
"""Simple test. Exhaustive tests follow below."""
doc = Document(page_content="hello")
d = doc.model_dump()
WellKnownLCObject.model_validate(d)
from langserve.serialization import WellKnownLCSerializer, load_events
@pytest.mark.parametrize(
@@ -31,8 +27,6 @@ def test_document_serialization() -> None:
[],
{},
{"a": 1},
Document(page_content="Hello"),
[Document(page_content="Hello")],
{"output": [HumanMessage(content="hello")]},
# Test with a single message (HumanMessage)
HumanMessage(content="Hello"),
@@ -87,7 +81,7 @@ def _get_full_representation(data: Any) -> Any:
elif isinstance(data, list):
return [_get_full_representation(value) for value in data]
elif isinstance(data, BaseModel):
return data.model_json_schema()
return data.schema()
else:
return data
File diff suppressed because it is too large Load Diff
-41
View File
@@ -1,41 +0,0 @@
"""Test utilities for streaming."""
import datetime
import json
import uuid
from langsmith.schemas import FeedbackIngestToken
from langserve.api_handler import _create_metadata_event
def test_create_metadata_event() -> None:
"""Test that the metadata event is created correctly."""
run_id = uuid.UUID(int=7)
event = _create_metadata_event(run_id, feedback_ingest_token=None)
assert event == {
"data": '{"run_id": "00000000-0000-0000-0000-000000000007"}',
"event": "metadata",
}
# Test with feedback ingest token
feedback_ingest_token = FeedbackIngestToken(
id=uuid.UUID(int=8), expires_at=datetime.datetime(2022, 1, 1), url="ingest-url"
)
event = _create_metadata_event(
run_id, feedback_ingest_token=feedback_ingest_token, feedback_key="key"
)
data = json.loads(event.pop("data"))
assert event == {
"event": "metadata",
}
assert data == {
"feedback_tokens": [
{
"expires_at": "2022-01-01T00:00:00",
"key": "key",
"token_url": "ingest-url",
}
],
"run_id": "00000000-0000-0000-0000-000000000007",
}
+10 -5
View File
@@ -3,11 +3,16 @@ from unittest.mock import MagicMock
import pytest
from fastapi import Request
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import ConfigurableField
from pydantic import BaseModel, ValidationError
from langchain.prompts import PromptTemplate
from langchain.schema.runnable.utils import ConfigurableField
from langserve.api_handler import _unpack_request_config
try:
from pydantic.v1 import BaseModel, ValidationError
except ImportError:
from pydantic import BaseModel, ValidationError
from langserve.validation import (
create_batch_request_model,
create_invoke_request_model,
@@ -170,11 +175,11 @@ async def test_invoke_request_with_runnables() -> None:
"configurable": {"template": "goodbye {name}"},
},
)
assert dict(request.input) == {"name": "bob"}
assert request.input == {"name": "bob"}
assert request.config.tags == ["hello"]
assert request.config.run_name == "run"
assert isinstance(request.config.configurable, BaseModel)
assert request.config.configurable.model_dump() == {
assert request.config.configurable.dict() == {
"template": "goodbye {name}",
}
-1
View File
@@ -1,5 +1,4 @@
"""Fake Chat Model wrapper for testing purposes."""
import asyncio
import re
import time
-27
View File
@@ -1,27 +0,0 @@
from typing import Any
def recursive_dump(obj: Any) -> Any:
"""Recursively dump the object if encountering any pydantic models."""
if isinstance(obj, dict):
return {
k: recursive_dump(v)
for k, v in obj.items()
if k != "id" # Remove the id field for testing purposes
}
if isinstance(obj, list):
return [recursive_dump(v) for v in obj]
if hasattr(obj, "model_dump"):
# if the object contains an ID field, we'll remove it for testing purposes
d = obj.model_dump()
if "id" in d:
d.pop("id")
return recursive_dump(d)
if hasattr(obj, "dict"):
# if the object contains an ID field, we'll remove it for testing purposes
if hasattr(obj, "id"):
d = obj.dict()
d.pop("id")
return recursive_dump(d)
return recursive_dump(obj.dict())
return obj
-22
View File
@@ -1,22 +0,0 @@
from typing import Any
from langchain_core.messages import AIMessage, AIMessageChunk
class AnyStr(str):
def __eq__(self, other: Any) -> bool:
return isinstance(other, str)
def _AnyIdAIMessage(**kwargs: Any) -> AIMessage:
"""Create ai message with an any id field."""
message = AIMessage(**kwargs)
message.id = AnyStr()
return message
def _AnyIdAIMessageChunk(**kwargs: Any) -> AIMessageChunk:
"""Create ai message with an any id field."""
message = AIMessageChunk(**kwargs)
message.id = AnyStr()
return message
+36 -57
View File
@@ -1,15 +1,13 @@
"""Tests for verifying that testing utility code works as expected."""
from itertools import cycle
from typing import Any, Dict, List, Optional, Union
from uuid import UUID
from langchain_core.callbacks.base import AsyncCallbackHandler
from langchain_core.messages import AIMessage, BaseMessage
from langchain_core.messages import AIMessage, AIMessageChunk, BaseMessage
from langchain_core.outputs import ChatGenerationChunk, GenerationChunk
from tests.unit_tests.utils.llms import GenericFakeChatModel
from tests.unit_tests.utils.stubs import _AnyIdAIMessage, _AnyIdAIMessageChunk
def test_generic_fake_chat_model_invoke() -> None:
@@ -17,11 +15,11 @@ def test_generic_fake_chat_model_invoke() -> None:
infinite_cycle = cycle([AIMessage(content="hello"), AIMessage(content="goodbye")])
model = GenericFakeChatModel(messages=infinite_cycle)
response = model.invoke("meow")
assert response == _AnyIdAIMessage(content="hello")
assert response == AIMessage(content="hello")
response = model.invoke("kitty")
assert response == _AnyIdAIMessage(content="goodbye")
assert response == AIMessage(content="goodbye")
response = model.invoke("meow")
assert response == _AnyIdAIMessage(content="hello")
assert response == AIMessage(content="hello")
async def test_generic_fake_chat_model_ainvoke() -> None:
@@ -29,23 +27,11 @@ async def test_generic_fake_chat_model_ainvoke() -> None:
infinite_cycle = cycle([AIMessage(content="hello"), AIMessage(content="goodbye")])
model = GenericFakeChatModel(messages=infinite_cycle)
response = await model.ainvoke("meow")
assert response == _AnyIdAIMessage(content="hello")
assert response == AIMessage(content="hello")
response = await model.ainvoke("kitty")
assert response == _AnyIdAIMessage(content="goodbye")
assert response == AIMessage(content="goodbye")
response = await model.ainvoke("meow")
assert response == _AnyIdAIMessage(content="hello")
def _filter_final_empty_chunk(chunks: list) -> list:
"""Filter out the final empty sentinel chunk emitted by langchain-core 1.x.
langchain-core 1.x emits an extra empty AIMessageChunk with
chunk_position='last' at the end of streams. Strip it for backward-compat
assertions.
"""
if chunks and chunks[-1].content == "" and not chunks[-1].additional_kwargs:
return chunks[:-1]
return chunks
assert response == AIMessage(content="hello")
async def test_generic_fake_chat_model_stream() -> None:
@@ -56,28 +42,28 @@ async def test_generic_fake_chat_model_stream() -> None:
]
)
model = GenericFakeChatModel(messages=infinite_cycle)
chunks = _filter_final_empty_chunk([chunk async for chunk in model.astream("meow")])
chunks = [chunk async for chunk in model.astream("meow")]
assert chunks == [
_AnyIdAIMessageChunk(content="hello"),
_AnyIdAIMessageChunk(content=" "),
_AnyIdAIMessageChunk(content="goodbye"),
AIMessageChunk(content="hello"),
AIMessageChunk(content=" "),
AIMessageChunk(content="goodbye"),
]
chunks = _filter_final_empty_chunk([chunk for chunk in model.stream("meow")])
chunks = [chunk for chunk in model.stream("meow")]
assert chunks == [
_AnyIdAIMessageChunk(content="hello"),
_AnyIdAIMessageChunk(content=" "),
_AnyIdAIMessageChunk(content="goodbye"),
AIMessageChunk(content="hello"),
AIMessageChunk(content=" "),
AIMessageChunk(content="goodbye"),
]
# Test streaming of additional kwargs.
# Relying on insertion order of the additional kwargs dict
message = AIMessage(content="", additional_kwargs={"foo": 42, "bar": 24}, id="1")
message = AIMessage(content="", additional_kwargs={"foo": 42, "bar": 24})
model = GenericFakeChatModel(messages=cycle([message]))
chunks = _filter_final_empty_chunk([chunk async for chunk in model.astream("meow")])
chunks = [chunk async for chunk in model.astream("meow")]
assert chunks == [
_AnyIdAIMessageChunk(content="", additional_kwargs={"foo": 42}),
_AnyIdAIMessageChunk(content="", additional_kwargs={"bar": 24}),
AIMessageChunk(content="", additional_kwargs={"foo": 42}),
AIMessageChunk(content="", additional_kwargs={"bar": 24}),
]
message = AIMessage(
@@ -91,24 +77,22 @@ async def test_generic_fake_chat_model_stream() -> None:
},
)
model = GenericFakeChatModel(messages=cycle([message]))
chunks = _filter_final_empty_chunk([chunk async for chunk in model.astream("meow")])
chunks = [chunk async for chunk in model.astream("meow")]
assert chunks == [
_AnyIdAIMessageChunk(
content="",
additional_kwargs={"function_call": {"name": "move_file"}},
AIMessageChunk(
content="", additional_kwargs={"function_call": {"name": "move_file"}}
),
_AnyIdAIMessageChunk(
AIMessageChunk(
content="",
additional_kwargs={
"function_call": {"arguments": '{\n "source_path": "foo"'}
},
),
_AnyIdAIMessageChunk(
content="",
additional_kwargs={"function_call": {"arguments": ","}},
AIMessageChunk(
content="", additional_kwargs={"function_call": {"arguments": ","}}
),
_AnyIdAIMessageChunk(
AIMessageChunk(
content="",
additional_kwargs={
"function_call": {"arguments": '\n "destination_path": "bar"\n}'}
@@ -123,7 +107,7 @@ async def test_generic_fake_chat_model_stream() -> None:
else:
accumulate_chunks += chunk
assert accumulate_chunks == _AnyIdAIMessageChunk(
assert accumulate_chunks == AIMessageChunk(
content="",
additional_kwargs={
"function_call": {
@@ -143,11 +127,10 @@ async def test_generic_fake_chat_model_astream_log() -> None:
log_patch async for log_patch in model.astream_log("meow", diff=False)
]
final = log_patches[-1]
streamed = _filter_final_empty_chunk(final.state["streamed_output"])
assert streamed == [
_AnyIdAIMessageChunk(content="hello"),
_AnyIdAIMessageChunk(content=" "),
_AnyIdAIMessageChunk(content="goodbye"),
assert final.state["streamed_output"] == [
AIMessageChunk(content="hello"),
AIMessageChunk(content=" "),
AIMessageChunk(content="goodbye"),
]
@@ -193,14 +176,10 @@ async def test_callback_handlers() -> None:
model = GenericFakeChatModel(messages=infinite_cycle)
tokens: List[str] = []
# New model
results = _filter_final_empty_chunk(
list(model.stream("meow", {"callbacks": [MyCustomAsyncHandler(tokens)]}))
)
results = list(model.stream("meow", {"callbacks": [MyCustomAsyncHandler(tokens)]}))
assert results == [
_AnyIdAIMessageChunk(content="hello"),
_AnyIdAIMessageChunk(content=" "),
_AnyIdAIMessageChunk(content="goodbye"),
AIMessageChunk(content="hello"),
AIMessageChunk(content=" "),
AIMessageChunk(content="goodbye"),
]
# Filter empty token from final sentinel chunk if present
content_tokens = [t for t in tokens if t]
assert content_tokens == ["hello", " ", "goodbye"]
assert tokens == ["hello", " ", "goodbye"]
+2 -30
View File
@@ -1,7 +1,7 @@
from typing import Any, Dict, List, Optional
from typing import Dict, List
from uuid import UUID
from langchain_core.tracers import BaseTracer
from langchain.callbacks.tracers.base import BaseTracer
from langsmith.schemas import Run
@@ -39,34 +39,6 @@ class FakeTracer(BaseTracer):
}
)
def _create_chain_run(
self,
serialized: Dict[str, Any],
inputs: Dict[str, Any],
run_id: UUID,
tags: Optional[List[str]] = None,
parent_run_id: Optional[UUID] = None,
metadata: Optional[Dict[str, Any]] = None,
run_type: Optional[str] = None,
name: Optional[str] = None,
**kwargs: Any,
) -> Run:
if name is None:
# can't raise an exception from here, but can get a breakpoint
# import pdb; pdb.set_trace()
pass
return super()._create_chain_run(
serialized,
inputs,
run_id,
tags,
parent_run_id,
metadata,
run_type,
name,
**kwargs,
)
def _persist_run(self, run: Run) -> None:
"""Persist a run."""
self.runs.append(self._copy_run(run))