New Component, Multiple outputs and type refactor (#2246)
This PR adds a new way of building Langflow Components, adds the possibility of defining more than one output and adds the `io` module that exposes many types of inputs. The components that inherit from `CustomComponent` should still work. Components now mostly output `Data`(former `Record`) and `Message`. `MessageInput` passes a `Message` object to the component while `TextInput` gets the `.text` from the `Message` and passes it as a string.
This commit is contained in:
commit
65e1c4230a
584 changed files with 29315 additions and 22179 deletions
20
.github/workflows/docker-build.yml
vendored
20
.github/workflows/docker-build.yml
vendored
|
|
@ -53,26 +53,6 @@ jobs:
|
|||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
# - name: Build Docker Image
|
||||
# uses: docker/build-push-action@v5
|
||||
# id: docker_build
|
||||
# with:
|
||||
# context: .
|
||||
# push: false # Do not push yet
|
||||
# platforms: "linux/amd64,linux/arm64/v8"
|
||||
# load: true
|
||||
# file: ${{ matrix.file }}
|
||||
# tags: ${{ env.TEST_TAG }}
|
||||
# - name: Run Container
|
||||
# run: |
|
||||
# docker run -d --name test_container -p 8000:8000 ${{ needs.setup.outputs.tags }}
|
||||
# - name: Wait for Container to Start and Check Health
|
||||
# run: |
|
||||
# timeout 40 bash -c 'until curl -f http://127.0.0.1:8000/health; do sleep 1; done' || (echo "Server did not start in time" && docker logs test_container && docker stop test_container && docker rm test_container && exit 1)
|
||||
# - name: Stop and Remove Container
|
||||
# run: |
|
||||
# docker stop test_container
|
||||
# docker rm test_container
|
||||
- name: Build and Push Docker Image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
|
|
|
|||
23
.github/workflows/lint-js.yml
vendored
23
.github/workflows/lint-js.yml
vendored
|
|
@ -10,8 +10,10 @@ env:
|
|||
|
||||
jobs:
|
||||
run-linters:
|
||||
name: Run linters
|
||||
name: Run Prettier
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
|
|
@ -38,16 +40,13 @@ jobs:
|
|||
npm install
|
||||
if: ${{ steps.setup-node.outputs.cache-hit != 'true' }}
|
||||
|
||||
- name: Run linters
|
||||
uses: wearerequired/lint-action@v2
|
||||
- name: Run Prettier
|
||||
run: |
|
||||
cd src/frontend
|
||||
npm run format
|
||||
- name: Commit changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
auto_fix: true
|
||||
git_email: "gabriel@langflow.org"
|
||||
# Enable linters
|
||||
# eslint: true
|
||||
# eslint_auto_fix: true
|
||||
prettier: true
|
||||
prettier_auto_fix: true
|
||||
prettier_args: '--write \"{tests,src}/**/*.{js,jsx,ts,tsx,json,md}\" --ignore-path .prettierignore'
|
||||
commit_message: Apply Prettier formatting
|
||||
branch: ${{ github.head_ref }}
|
||||
|
||||
|
|
|
|||
8
.github/workflows/style-check-py.yml
vendored
8
.github/workflows/style-check-py.yml
vendored
|
|
@ -36,5 +36,11 @@ jobs:
|
|||
- name: Run Ruff
|
||||
run: poetry run ruff check --output-format=github .
|
||||
- name: Run Ruff format
|
||||
run: poetry run ruff format --check .
|
||||
run: poetry run ruff format .
|
||||
- name: Commit changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: Apply Ruff formatting
|
||||
branch: ${{ github.head_ref }}
|
||||
|
||||
|
||||
|
|
|
|||
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -268,5 +268,7 @@ stuff/*
|
|||
src/frontend/playwright-report/index.html
|
||||
*.bak
|
||||
prof/*
|
||||
*-shm
|
||||
*-wal
|
||||
|
||||
src/frontend/temp
|
||||
*.db-shm
|
||||
*.db-wal
|
||||
|
|
|
|||
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
|
|
@ -15,7 +15,9 @@
|
|||
"--log-level",
|
||||
"debug",
|
||||
"--loop",
|
||||
"asyncio"
|
||||
"asyncio",
|
||||
"--reload-include",
|
||||
"src/backend/*"
|
||||
],
|
||||
"jinja": true,
|
||||
"justMyCode": false,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
identity and expression, level of experience, education, socioeconomic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
|
|
|
|||
1
Makefile
1
Makefile
|
|
@ -208,6 +208,7 @@ ifdef base
|
|||
endif
|
||||
|
||||
ifdef main
|
||||
make build_frontend
|
||||
make build_langflow
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -106,26 +106,26 @@ python -m langflow run
|
|||
|
||||
### Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `--help` | Displays all available options. |
|
||||
| `--host` | Defines the host to bind the server to. Can be set using the `LANGFLOW_HOST` environment variable. The default is `127.0.0.1`. |
|
||||
| `--workers` | Sets the number of worker processes. Can be set using the `LANGFLOW_WORKERS` environment variable. The default is `1`. |
|
||||
| `--timeout` | Sets the worker timeout in seconds. The default is `60`. |
|
||||
| `--port` | Sets the port to listen on. Can be set using the `LANGFLOW_PORT` environment variable. The default is `7860`. |
|
||||
| `--env-file` | Specifies the path to the .env file containing environment variables. The default is `.env`. |
|
||||
| `--log-level` | Defines the logging level. Can be set using the `LANGFLOW_LOG_LEVEL` environment variable. The default is `critical`. |
|
||||
| `--components-path` | Specifies the path to the directory containing custom components. Can be set using the `LANGFLOW_COMPONENTS_PATH` environment variable. The default is `langflow/components`. |
|
||||
| `--log-file` | Specifies the path to the log file. Can be set using the `LANGFLOW_LOG_FILE` environment variable. The default is `logs/langflow.log`. |
|
||||
| `--cache` | Select the type of cache to use. Options are `InMemoryCache` and `SQLiteCache`. Can be set using the `LANGFLOW_LANGCHAIN_CACHE` environment variable. The default is `SQLiteCache`. |
|
||||
| `--dev`/`--no-dev` | Toggles the development mode. The default is `no-dev`. |
|
||||
| `--path` | Specifies the path to the frontend directory containing build files. This option is for development purposes only. Can be set using the `LANGFLOW_FRONTEND_PATH` environment variable. |
|
||||
| `--open-browser`/`--no-open-browser` | Toggles the option to open the browser after starting the server. Can be set using the `LANGFLOW_OPEN_BROWSER` environment variable. The default is `open-browser`. |
|
||||
| `--remove-api-keys`/`--no-remove-api-keys` | Toggles the option to remove API keys from the projects saved in the database. Can be set using the `LANGFLOW_REMOVE_API_KEYS` environment variable. The default is `no-remove-api-keys`. |
|
||||
| `--install-completion [bash\|zsh\|fish\|powershell\|pwsh]` | Installs completion for the specified shell. |
|
||||
| `--show-completion [bash\|zsh\|fish\|powershell\|pwsh]` | Shows completion for the specified shell, allowing you to copy it or customize the installation. |
|
||||
| `--backend-only` | This parameter, with a default value of `False`, allows running only the backend server without the frontend. It can also be set using the `LANGFLOW_BACKEND_ONLY` environment variable. For more, see [Backend-only](../deployment/backend-only.md). |
|
||||
| `--store` | This parameter, with a default value of `True`, enables the store features, use `--no-store` to deactivate it. It can be configured using the `LANGFLOW_STORE` environment variable. |
|
||||
| Option | Description |
|
||||
| ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `--help` | Displays all available options. |
|
||||
| `--host` | Defines the host to bind the server to. Can be set using the `LANGFLOW_HOST` environment variable. The default is `127.0.0.1`. |
|
||||
| `--workers` | Sets the number of worker processes. Can be set using the `LANGFLOW_WORKERS` environment variable. The default is `1`. |
|
||||
| `--timeout` | Sets the worker timeout in seconds. The default is `60`. |
|
||||
| `--port` | Sets the port to listen on. Can be set using the `LANGFLOW_PORT` environment variable. The default is `7860`. |
|
||||
| `--env-file` | Specifies the path to the .env file containing environment variables. The default is `.env`. |
|
||||
| `--log-level` | Defines the logging level. Can be set using the `LANGFLOW_LOG_LEVEL` environment variable. The default is `critical`. |
|
||||
| `--components-path` | Specifies the path to the directory containing custom components. Can be set using the `LANGFLOW_COMPONENTS_PATH` environment variable. The default is `langflow/components`. |
|
||||
| `--log-file` | Specifies the path to the log file. Can be set using the `LANGFLOW_LOG_FILE` environment variable. The default is `logs/langflow.log`. |
|
||||
| `--cache` | Select the type of cache to use. Options are `InMemoryCache` and `SQLiteCache`. Can be set using the `LANGFLOW_LANGCHAIN_CACHE` environment variable. The default is `SQLiteCache`. |
|
||||
| `--dev`/`--no-dev` | Toggles the development mode. The default is `no-dev`. |
|
||||
| `--path` | Specifies the path to the frontend directory containing build files. This option is for development purposes only. Can be set using the `LANGFLOW_FRONTEND_PATH` environment variable. |
|
||||
| `--open-browser`/`--no-open-browser` | Toggles the option to open the browser after starting the server. Can be set using the `LANGFLOW_OPEN_BROWSER` environment variable. The default is `open-browser`. |
|
||||
| `--remove-api-keys`/`--no-remove-api-keys` | Toggles the option to remove API keys from the projects saved in the database. Can be set using the `LANGFLOW_REMOVE_API_KEYS` environment variable. The default is `no-remove-api-keys`. |
|
||||
| `--install-completion [bash\|zsh\|fish\|powershell\|pwsh]` | Installs completion for the specified shell. |
|
||||
| `--show-completion [bash\|zsh\|fish\|powershell\|pwsh]` | Shows completion for the specified shell, allowing you to copy it or customize the installation. |
|
||||
| `--backend-only` | This parameter, with a default value of `False`, allows running only the backend server without the frontend. It can also be set using the `LANGFLOW_BACKEND_ONLY` environment variable. For more, see [Backend-only](../deployment/backend-only). |
|
||||
| `--store` | This parameter, with a default value of `True`, enables the store features, use `--no-store` to deactivate it. It can be configured using the `LANGFLOW_STORE` environment variable. |
|
||||
|
||||
#### CLI environment variables
|
||||
|
||||
|
|
|
|||
|
|
@ -44,9 +44,9 @@ Adding or removing any of the below components modifies your Playground so you c
|
|||
- Text Input
|
||||
- Chat Output
|
||||
- Text Output
|
||||
- Records Output
|
||||
- Data Output
|
||||
- Inspect Memory
|
||||
|
||||
You can also select **Options** > **Logs** to see your flow's logs.
|
||||
|
||||
For more information, see [Inputs and Outputs](../components/inputs-and-outputs.mdx).
|
||||
For more information, see [Inputs and Outputs](../components/inputs-and-outputs)
|
||||
|
|
|
|||
|
|
@ -24,15 +24,15 @@ Provide the session ID to clear its message history.
|
|||
|
||||
---
|
||||
|
||||
## Extract Key From Record
|
||||
## Extract Key From Data
|
||||
|
||||
This component extracts specified keys from a record.
|
||||
|
||||
**Parameters**
|
||||
|
||||
- **Record:**
|
||||
- **Data:**
|
||||
|
||||
- **Display Name:** Record
|
||||
- **Display Name:** Data
|
||||
- **Info:** The record from which to extract keys.
|
||||
|
||||
- **Keys:**
|
||||
|
|
@ -112,18 +112,18 @@ Call this component without parameters to list all flows.
|
|||
|
||||
---
|
||||
|
||||
## Merge Records
|
||||
## Merge Data
|
||||
|
||||
This component merges a list of records.
|
||||
This component merges a list of Data.
|
||||
|
||||
**Parameters**
|
||||
|
||||
- **Records:**
|
||||
- **Display Name:** Records
|
||||
- **Data:**
|
||||
- **Display Name:** Data
|
||||
|
||||
**Usage**
|
||||
|
||||
Provide the records you want to merge.
|
||||
Provide the Data you want to merge.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -138,9 +138,9 @@ This component generates a notification.
|
|||
- **Display Name:** Name
|
||||
- **Info:** The notification's name.
|
||||
|
||||
- **Record:**
|
||||
- **Data:**
|
||||
|
||||
- **Display Name:** Record
|
||||
- **Display Name:** Data
|
||||
- **Info:** Optionally, a record to store in the notification.
|
||||
|
||||
- **Append:**
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ This component retrieves stored chat messages based on a specific session ID.
|
|||
- **Number of messages:** Number of messages to retrieve.
|
||||
- **Session ID:** The session ID of the chat history.
|
||||
- **Order:** Choose the message order, either "Ascending" or "Descending".
|
||||
- **Record template:** (Optional) Template to convert a record to text. If left empty, the system dynamically sets it to the record's text key.
|
||||
- **Data template:** (Optional) Template to convert a record to text. If left empty, the system dynamically sets it to the record's text key.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -59,13 +59,13 @@ Learn more about creating custom components at [Custom Component](http://docs.la
|
|||
|
||||
---
|
||||
|
||||
### Documents to records
|
||||
### Documents to Data
|
||||
|
||||
Convert LangChain documents into records.
|
||||
Convert LangChain documents into Data.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- **Documents:** Documents to be converted into records.
|
||||
- **Documents:** Documents to be converted into Data.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -93,14 +93,14 @@ Retrieves stored chat messages based on a specific session ID.
|
|||
|
||||
---
|
||||
|
||||
### Records to text
|
||||
### Data to text
|
||||
|
||||
Convert records into plain text following a specified template.
|
||||
Convert Data into plain text following a specified template.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- **Records:** The records to convert to text.
|
||||
- **Template:** The template used for formatting the records. It can contain keys like `{text}`, `{data}`, or any other key in the record.
|
||||
- **Data:** The Data to convert to text.
|
||||
- **Template:** The template used for formatting the Data. It can contain keys like `{text}`, `{data}`, or any other key in the record.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -124,5 +124,5 @@ Update a record with text-based key/value pairs, similar to updating a Python di
|
|||
|
||||
#### Parameters
|
||||
|
||||
- **Record:** The record to update.
|
||||
- **Data:** The record to update.
|
||||
- **New data:** The new data to update the record with.
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ They also dynamically change the Playground and can be renamed to facilitate bui
|
|||
|
||||
## Inputs
|
||||
|
||||
Inputs are components used to define where data enters your flow. They can receive data from the user, a database, or any other source that can be converted to Text or Record.
|
||||
Inputs are components used to define where data enters your flow. They can receive data from the user, a database, or any other source that can be converted to Text or Data.
|
||||
|
||||
The difference between Chat Input and other Input components is the output format, the number of configurable fields, and the way they are displayed in the Playground.
|
||||
|
||||
Chat Input components can output `Text` or `Record`. When you want to pass the sender name or sender to the next component, use the `Record` output. To pass only the message, use the `Text` output, useful when saving the message to a database or memory system like Zep.
|
||||
Chat Input components can output `Text` or `Data`. When you want to pass the sender name or sender to the next component, use the `Data` output. To pass only the message, use the `Text` output, useful when saving the message to a database or memory system like Zep.
|
||||
|
||||
You can find out more about Chat Input and other Inputs [here](#chat-input).
|
||||
|
||||
|
|
@ -38,8 +38,8 @@ This component collects user input from the chat.
|
|||
|
||||
<Admonition type="note" title="Note">
|
||||
<p>
|
||||
If `As Record` is `true` and the `Message` is a `Record`, the data of the
|
||||
`Record` will be updated with the `Sender`, `Sender Name`, and `Session ID`.
|
||||
If `As Data` is `true` and the `Message` is a `Data`, the data of the `Data`
|
||||
will be updated with the `Sender`, `Sender Name`, and `Session ID`.
|
||||
</p>
|
||||
</Admonition>
|
||||
|
||||
|
|
@ -70,11 +70,11 @@ The **Text Input** component adds an **Input** field on the Playground. This ena
|
|||
**Parameters**
|
||||
|
||||
- **Value:** Specifies the text input value. This is where the user inputs text data that will be passed to the next component in the sequence. If no value is provided, it defaults to an empty string.
|
||||
- **Record Template:** Specifies how a `Record` should be converted into `Text`.
|
||||
- **Data Template:** Specifies how a `Data` should be converted into `Text`.
|
||||
|
||||
The **Record Template** field is used to specify how a `Record` should be converted into `Text`. This is particularly useful when you want to extract specific information from a `Record` and pass it as text to the next component in the sequence.
|
||||
The **Data Template** field is used to specify how a `Data` should be converted into `Text`. This is particularly useful when you want to extract specific information from a `Data` and pass it as text to the next component in the sequence.
|
||||
|
||||
For example, if you have a `Record` with the following structure:
|
||||
For example, if you have a `Data` with the following structure:
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -84,9 +84,9 @@ For example, if you have a `Record` with the following structure:
|
|||
}
|
||||
```
|
||||
|
||||
A template with `Name: {name}, Age: {age}` will convert the `Record` into a text string of `Name: John Doe, Age: 30`.
|
||||
A template with `Name: {name}, Age: {age}` will convert the `Data` into a text string of `Name: John Doe, Age: 30`.
|
||||
|
||||
If you pass more than one `Record`, the text will be concatenated with a new line separator.
|
||||
If you pass more than one `Data`, the text will be concatenated with a new line separator.
|
||||
|
||||
## Outputs
|
||||
|
||||
|
|
@ -112,8 +112,8 @@ This component sends a message to the chat.
|
|||
|
||||
<Admonition type="note" title="Note">
|
||||
<p>
|
||||
If `As Record` is `true` and the `Message` is a `Record`, the data in the
|
||||
`Record` is updated with the `Sender`, `Sender Name`, and `Session ID`.
|
||||
If `As Data` is `true` and the `Message` is a `Data`, the data in the `Data`
|
||||
is updated with the `Sender`, `Sender Name`, and `Session ID`.
|
||||
</p>
|
||||
</Admonition>
|
||||
|
||||
|
|
@ -154,4 +154,5 @@ The `PromptTemplate` component enables users to create prompts and define variab
|
|||
|
||||
After defining a variable in the prompt template, it acts as its own component input. See [Prompt Customization](../administration/prompt-customization) for more details.
|
||||
|
||||
- **template:** The template used to format an individual request.
|
||||
- **template:** The template used to format an individual request.import Admonition from "@theme/Admonition";
|
||||
import ZoomableImage from "/src/theme/ZoomableImage.js";
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
# Text and Record
|
||||
# Text and Data
|
||||
|
||||
In Langflow 1.0, we added two main input and output types: `Text` and `Record`.
|
||||
In Langflow 1.0, we added two main input and output types: `Text` and `Data`.
|
||||
|
||||
`Text` is a simple string input and output type, while `Record` is a structure very similar to a dictionary in Python. It is a key-value pair data structure.
|
||||
`Text` is a simple string input and output type, while `Data` is a structure very similar to a dictionary in Python. It is a key-value pair data structure.
|
||||
|
||||
We've created a few components to help you work with these types. Let's see how a few of them work.
|
||||
|
||||
## Records To Text
|
||||
## Data To Text
|
||||
|
||||
This is a component that takes in Records and outputs a `Text`. It does this using a template string and concatenating the values of the `Record`, one per line.
|
||||
This is a component that takes in Data and outputs a `Text`. It does this using a template string and concatenating the values of the `Data`, one per line.
|
||||
|
||||
If we have the following Records:
|
||||
If we have the following Data:
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -32,13 +32,13 @@ Alice: Hello!
|
|||
John: Hi!
|
||||
```
|
||||
|
||||
## Create Record
|
||||
## Create Data
|
||||
|
||||
This component allows you to create a `Record` from a number of inputs. You can add as many key-value pairs as you want (as long as it is less than 15). Once you've picked that number you'll need to write the name of the Key and can pass `Text` values from other components to it.
|
||||
This component allows you to create a `Data` from a number of inputs. You can add as many key-value pairs as you want (as long as it is less than 15). Once you've picked that number you'll need to write the name of the Key and can pass `Text` values from other components to it.
|
||||
|
||||
## Documents To Records
|
||||
## Documents To Data
|
||||
|
||||
This component takes in a LangChain `Document` and outputs a `Record`. It does this by extracting the `page_content` and the `metadata` from the `Document` and adding them to the `Record` as text and data respectively.
|
||||
This component takes in a LangChain `Document` and outputs a `Data`. It does this by extracting the `page_content` and the `metadata` from the `Document` and adding them to the `Data` as text and data respectively.
|
||||
|
||||
## Why is this useful?
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ import Admonition from "@theme/Admonition";
|
|||
|
||||
### Astra DB
|
||||
|
||||
The `Astra DB` initializes a vector store using Astra DB from records. It creates Astra DB-based vector indexes to efficiently store and retrieve documents.
|
||||
The `Astra DB` initializes a vector store using Astra DB from Data. It creates Astra DB-based vector indexes to efficiently store and retrieve documents.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **Input:** Documents or records for input.
|
||||
- **Input:** Documents or Data for input.
|
||||
- **Embedding:** Embedding model Astra DB uses.
|
||||
- **Collection Name:** Name of the Astra DB collection.
|
||||
- **Token:** Authentication token for Astra DB.
|
||||
|
|
@ -99,12 +99,12 @@ For detailed documentation and integration guides, please refer to the [Chroma C
|
|||
|
||||
### Couchbase
|
||||
|
||||
`Couchbase` builds a Couchbase vector store from records, streamlining the storage and retrieval of documents.
|
||||
`Couchbase` builds a Couchbase vector store from Data, streamlining the storage and retrieval of documents.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **Embedding:** Model used by Couchbase.
|
||||
- **Input:** Documents or records.
|
||||
- **Input:** Documents or Data.
|
||||
- **Couchbase Cluster Connection String:** Cluster Connection string.
|
||||
- **Couchbase Cluster Username:** Cluster Username.
|
||||
- **Couchbase Cluster Password:** Cluster Password.
|
||||
|
|
@ -165,12 +165,12 @@ For more details, see the [FAISS Component Documentation](https://faiss.ai/index
|
|||
|
||||
### MongoDB Atlas
|
||||
|
||||
`MongoDBAtlas` builds a MongoDB Atlas-based vector store from records, streamlining the storage and retrieval of documents.
|
||||
`MongoDBAtlas` builds a MongoDB Atlas-based vector store from Data, streamlining the storage and retrieval of documents.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **Embedding:** Model used by MongoDB Atlas.
|
||||
- **Input:** Documents or records.
|
||||
- **Input:** Documents or Data.
|
||||
- **Collection Name:** Collection identifier in MongoDB Atlas.
|
||||
- **Database Name:** Database identifier.
|
||||
- **Index Name:** Index identifier.
|
||||
|
|
@ -235,11 +235,11 @@ For more details, see the [PGVector Component Documentation](https://python.lang
|
|||
|
||||
### Pinecone
|
||||
|
||||
`Pinecone` constructs a Pinecone wrapper from records, setting up Pinecone-based vector indexes for document storage and retrieval.
|
||||
`Pinecone` constructs a Pinecone wrapper from Data, setting up Pinecone-based vector indexes for document storage and retrieval.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **Input:** Documents or records.
|
||||
- **Input:** Documents or Data.
|
||||
- **Embedding:** Model used.
|
||||
- **Index Name:** Index identifier.
|
||||
- **Namespace:** Namespace used.
|
||||
|
|
@ -278,7 +278,7 @@ For more details, see the [PGVector Component Documentation](https://python.lang
|
|||
|
||||
**Parameters:**
|
||||
|
||||
- **Input:** Documents or records.
|
||||
- **Input:** Documents or Data.
|
||||
- **Embedding:** Model used.
|
||||
- **API Key:** Qdrant API key.
|
||||
- **Collection Name:** Collection identifier.
|
||||
|
|
@ -345,7 +345,7 @@ For detailed documentation, refer to the [Redis Documentation](https://python.la
|
|||
|
||||
**Parameters:**
|
||||
|
||||
- **Input:** Documents or records.
|
||||
- **Input:** Documents or data.
|
||||
- **Embedding:** Model used.
|
||||
- **Query Name:** Optional query name.
|
||||
- **Search Kwargs:** Advanced search parameters.
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ You have a new document loader called **MyCustomDocumentLoader** and it would lo
|
|||
5. Add the code to the [/components/documentloaders](https://github.com/langflow-ai/langflow/tree/dev/src/backend/base/langflow/components) folder.
|
||||
6. Add the dependency to [/documentloaders/\_\_init\_\_.py](https://github.com/langflow-ai/langflow/blob/dev/src/backend/base/langflow/components/documentloaders/__init__.py) as `from .MyCustomDocumentLoader import MyCustomDocumentLoader`.
|
||||
7. Add any new dependencies to the outer [pyproject.toml](https://github.com/langflow-ai/langflow/blob/dev/pyproject.toml#L27) file.
|
||||
8. Submit documentation for your component. For this example, you'd submit documentation to the [loaders page](https://github.com/langflow-ai/langflow/blob/dev/docs/docs/components/loaders.mdx).
|
||||
8. Submit documentation for your component. For this example, you'd submit documentation to the [loaders page](https://github.com/langflow-ai/langflow/blob/dev/docs/docs/components/loaders).
|
||||
9. Submit your changes as a pull request. The Langflow team will have a look, suggest changes, and add your component to Langflow.
|
||||
|
||||
## User Sharing
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ Langflow will now serve requests to its API without the frontend running.
|
|||
|
||||
## Prerequisites
|
||||
|
||||
- [Langflow installed](../getting-started/install-langflow.mdx)
|
||||
- [Langflow installed](../getting-started/install-langflow)
|
||||
|
||||
- [OpenAI API key](https://platform.openai.com)
|
||||
|
||||
- [A Langflow flow created](../starter-projects/basic-prompting.mdx)
|
||||
- [A Langflow flow created](../starter-projects/basic-prompting)
|
||||
|
||||
## Download your flow's curl call
|
||||
|
||||
|
|
@ -120,4 +120,4 @@ The result is similar to the curl call:
|
|||
|
||||
Your Python app POSTs to your Langflow server, and the server runs the flow and returns the result.
|
||||
|
||||
See [API](../administration/api.mdx) for more ways to interact with your headless Langflow server.
|
||||
See [API](../administration/api) for more ways to interact with your headless Langflow server.
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ import ZoomableImage from "/src/theme/ZoomableImage.js";
|
|||
import ReactPlayer from "react-player";
|
||||
import Admonition from "@theme/Admonition";
|
||||
|
||||
# Create Record
|
||||
# Create Data
|
||||
|
||||
In Langflow, a `Record` has a structure very similar to a Python dictionary. It is a key-value pair data structure.
|
||||
In Langflow, a `Data` has a structure very similar to a Python dictionary. It is a key-value pair data structure.
|
||||
|
||||
The **Create Record** component allows you to dynamically create a `Record` from a specified number of inputs. You can add as many key-value pairs as you want (as long as it is less than 15 😅). Once you've chosen the number of `Records`, add keys and fill up values, or pass on values from other components to the component using the input handles.
|
||||
The **Create Data** component allows you to dynamically create a `Data` from a specified number of inputs. You can add as many key-value pairs as you want (as long as it is less than 15 😅). Once you've chosen the number of `Data`, add keys and fill up values, or pass on values from other components to the component using the input handles.
|
||||
|
||||
<div
|
||||
style={{ marginBottom: "20px", display: "flex", justifyContent: "center" }}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ For example, the **OpenAI LLM** is a **component** of the **Basic prompting** fl
|
|||
|
||||
A **flow** is a pipeline of components connected together in the Langflow canvas.
|
||||
|
||||
For example, the [Basic prompting](../starter-projects/basic-prompting.mdx) flow is a pipeline of four components:
|
||||
For example, the [Basic prompting](../starter-projects/basic-prompting) flow is a pipeline of four components:
|
||||
|
||||
<ZoomableImage
|
||||
alt="Docusaurus themed image"
|
||||
|
|
@ -117,7 +117,7 @@ You can modify the code and save it.
|
|||
|
||||
#### Save
|
||||
|
||||
Save your component to the **Saved** components folder for re-use.
|
||||
Save your component to the **Saved** components folder for reuse.
|
||||
|
||||
#### Duplicate
|
||||
|
||||
|
|
@ -143,19 +143,19 @@ Duplicate your component in the canvas.
|
|||
|
||||
### Group multiple components
|
||||
|
||||
Components without input or output nodes can be grouped into a single component for re-use.
|
||||
Components without input or output nodes can be grouped into a single component for reuse.
|
||||
This is useful for combining large flows into single components (like RAG with a vector database, for example) and saves space in the canvas.
|
||||
|
||||
1. Hold **Shift** and drag to select the **Prompt** and **OpenAI** components.
|
||||
2. Select **Group**.
|
||||
3. The components merge into a single component.
|
||||
4. To save the new component, select **Save**. It can now be re-used from the **Saved** components folder.
|
||||
4. To save the new component, select **Save**. It can now be reused from the **Saved** components folder.
|
||||
|
||||
## Playground
|
||||
|
||||
Run your flow by clicking the **Playground** button.
|
||||
|
||||
For more, see [Playground](../administration/playground.mdx).
|
||||
For more, see [Playground](../administration/playground).
|
||||
|
||||
## API
|
||||
|
||||
|
|
@ -210,7 +210,7 @@ The **Python Code** tab displays code to interact with your flow's `.json` file
|
|||
### Chat Widget HTML
|
||||
|
||||
The **Chat Widget HTML** tab displays code that can be inserted in the `<body>` of your HTML to interact with your flow.
|
||||
For more, see the [Chat widget documentation](../administration/chat-widget.mdx).
|
||||
For more, see the [Chat widget documentation](../administration/chat-widget).
|
||||
|
||||
### Tweaks
|
||||
|
||||
|
|
@ -250,7 +250,7 @@ Select **Download Collection** to save your project to your local machine. This
|
|||
|
||||
Select **Upload Collection** to upload a flow or component `.json` file from your local machine.
|
||||
|
||||
Select **New Project** to create a new project. In addition to a blank canvas, [starter projects](../starter-projects/basic-prompting.mdx) are also available.
|
||||
Select **New Project** to create a new project. In addition to a blank canvas, [starter projects](../starter-projects/basic-prompting) are also available.
|
||||
|
||||
## Project options menu
|
||||
|
||||
|
|
@ -275,4 +275,8 @@ To see options for your project, in the upper left corner of the canvas, select
|
|||
|
||||
**Export** - Download your current project to your local machine as a `.json` file.
|
||||
|
||||
**Undo** or **Redo** - Undo or redo your last action.
|
||||
**Undo** or **Redo** - Undo or redo your last action.import ThemedImage from "@theme/ThemedImage";
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
import ZoomableImage from "/src/theme/ZoomableImage.js";
|
||||
import ReactPlayer from "react-player";
|
||||
import Admonition from "@theme/Admonition";
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ python -m langflow run
|
|||
│ Collaborate, and contribute at our GitHub Repo 🚀 │
|
||||
```
|
||||
|
||||
3. Continue on to the [Quickstart](./quickstart.mdx).
|
||||
3. Continue on to the [Quickstart](./quickstart).
|
||||
|
||||
## HuggingFace Spaces
|
||||
|
||||
|
|
@ -89,4 +89,7 @@ You'll be presented with the following screen:
|
|||
style={{ width: "80%", maxWidth: "800px", margin: "0 auto" }}
|
||||
/>
|
||||
|
||||
Name your Space, define the visibility (Public or Private), and click on **Duplicate Space** to start the installation process. When installation is finished, you'll be redirected to the Space's main page to start using Langflow right away!
|
||||
Name your Space, define the visibility (Public or Private), and click on **Duplicate Space** to start the installation process. When installation is finished, you'll be redirected to the Space's main page to start using Langflow right away!import ThemedImage from "@theme/ThemedImage";
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
import ZoomableImage from "/src/theme/ZoomableImage.js";
|
||||
import Admonition from "@theme/Admonition";
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
Large Language Models, or LLMs, are part of an exciting new world in computing.
|
||||
|
||||
We made Langflow for anyone to create with LLMs, and hope you'll feel comfortable installing Langflow and [getting started](./quickstart.mdx).
|
||||
We made Langflow for anyone to create with LLMs, and hope you'll feel comfortable installing Langflow and [getting started](./quickstart).
|
||||
|
||||
If you want to learn more about LLMs, prompt engineering, and AI models, Langflow recommends [promptingguide.ai](https://promptingguide.ai), an open-source repository of prompt engineering content maintained by AI experts.
|
||||
PromptingGuide offers content for [beginners](https://www.promptingguide.ai/introduction/basics) and [experts](https://www.promptingguide.ai/techniques/cot), as well as the latest [research papers](https://www.promptingguide.ai/papers) and [test results](https://www.promptingguide.ai/research) fueling AI's progress.
|
||||
|
||||
Wherever you are on your AI journey, it's helpful to keep Prompting Guide open in a tab.
|
||||
Wherever you are on your AI journey, it's helpful to keep Prompting Guide open in a tab.
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ This guide demonstrates how to build a basic prompt flow and modify that prompt
|
|||
|
||||
- [Python >=3.10](https://www.python.org/downloads/release/python-3100/) and [pip](https://pypi.org/project/pip/) or [pipx](https://pipx.pypa.io/stable/installation/)
|
||||
|
||||
- [Langflow installed and running](./install-langflow.mdx)
|
||||
- [Langflow installed and running](./install-langflow)
|
||||
|
||||
- [OpenAI API key](https://platform.openai.com)
|
||||
|
||||
|
|
@ -77,6 +77,10 @@ By adding Langflow components to your flow, you can create all sorts of interest
|
|||
|
||||
Here are a couple of examples:
|
||||
|
||||
- [Memory chatbot](/starter-projects/memory-chatbot.mdx)
|
||||
- [Blog writer](/starter-projects/blog-writer.mdx)
|
||||
- [Document QA](/starter-projects/document-qa.mdx)
|
||||
- [Memory chatbot](/starter-projects/memory-chatbot)
|
||||
- [Blog writer](/starter-projects/blog-writer)
|
||||
- [Document QA](/starter-projects/document-qa)import ThemedImage from "@theme/ThemedImage";
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
import ZoomableImage from "/src/theme/ZoomableImage.js";
|
||||
import ReactPlayer from "react-player";
|
||||
import Admonition from "@theme/Admonition";
|
||||
|
|
|
|||
|
|
@ -136,10 +136,10 @@ The RAG flow is a bit more complex. It consists of:
|
|||
|
||||
- **Chat Input** component that defines where to put the user input coming from the Playground
|
||||
- **OpenAI Embeddings** component that generates embeddings from the user input
|
||||
- **Astra DB Search** component that retrieves the most relevant Records from the Astra DB database
|
||||
- **Text Output** component that turns the Records into Text by concatenating them and also displays it in the Playground
|
||||
- **Astra DB Search** component that retrieves the most relevant Data from the Astra DB database
|
||||
- **Text Output** component that turns the Data into Text by concatenating them and also displays it in the Playground
|
||||
- One interesting point you'll see here is that this component is named `Extracted Chunks`, and that is how it will appear in the Playground
|
||||
- **Prompt** component that takes in the user input and the retrieved Records as text and builds a prompt for the OpenAI model
|
||||
- **Prompt** component that takes in the user input and the retrieved Data as text and builds a prompt for the OpenAI model
|
||||
- **OpenAI** component that generates a response to the prompt
|
||||
- **Chat Output** component that displays the response in the Playground
|
||||
|
||||
|
|
@ -176,7 +176,7 @@ Because this flow has a **Chat Input** and a **Text Output** component, the Pane
|
|||
style={{ width: "80%", margin: "20px auto" }}
|
||||
/>
|
||||
|
||||
Once we interact with it we get a response and the Extracted Chunks section is updated with the retrieved records.
|
||||
Once we interact with it we get a response and the Extracted Chunks section is updated with the retrieved data.
|
||||
|
||||
<ZoomableImage
|
||||
alt="Docusaurus themed image"
|
||||
|
|
@ -192,4 +192,16 @@ And that's it! You have successfully ran a RAG application using Astra DB and La
|
|||
# Conclusion
|
||||
|
||||
In this guide, we have learned how to run a RAG application using Astra DB and Langflow.
|
||||
We have seen how to create an Astra DB database, import the Astra DB RAG Flows project into Langflow, and run the ingestion and RAG flows.
|
||||
We have seen how to create an Astra DB database, import the Astra DB RAG Flows project into Langflow, and run the ingestion and RAG flows.import ThemedImage from "@theme/ThemedImage";
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
import ZoomableImage from "/src/theme/ZoomableImage.js";
|
||||
import Admonition from "@theme/Admonition";import ThemedImage from "@theme/ThemedImage";
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
import ZoomableImage from "/src/theme/ZoomableImage.js";
|
||||
import Admonition from "@theme/Admonition";import ThemedImage from "@theme/ThemedImage";
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
import ZoomableImage from "/src/theme/ZoomableImage.js";
|
||||
import Admonition from "@theme/Admonition";import ThemedImage from "@theme/ThemedImage";
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
import ZoomableImage from "/src/theme/ZoomableImage.js";
|
||||
import Admonition from "@theme/Admonition";
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import requests
|
|||
from typing import Dict
|
||||
|
||||
from langflow import CustomComponent
|
||||
from langflow.schema import Record
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class NotionDatabaseProperties(CustomComponent):
|
||||
|
|
@ -61,7 +61,7 @@ class NotionDatabaseProperties(CustomComponent):
|
|||
self,
|
||||
database_id: str,
|
||||
notion_secret: str,
|
||||
) -> Record:
|
||||
) -> Data:
|
||||
url = f"https://api.notion.com/v1/databases/{database_id}"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {notion_secret}",
|
||||
|
|
@ -74,7 +74,7 @@ class NotionDatabaseProperties(CustomComponent):
|
|||
data = response.json()
|
||||
properties = data.get("properties", {})
|
||||
|
||||
record = Record(text=str(response.json()), data=properties)
|
||||
record = Data(text=str(response.json()), data=properties)
|
||||
self.status = f"Retrieved {len(properties)} properties from the Notion database.\n {record.text}"
|
||||
return record
|
||||
```
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ import requests
|
|||
import json
|
||||
from typing import Dict, Any, List
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Record
|
||||
from langflow.schema import Data
|
||||
|
||||
class NotionListPages(CustomComponent):
|
||||
display_name = "List Pages [Notion]"
|
||||
|
|
@ -83,7 +83,7 @@ class NotionListPages(CustomComponent):
|
|||
notion_secret: str,
|
||||
database_id: str,
|
||||
query_payload: str = "{}",
|
||||
) -> List[Record]:
|
||||
) -> List[Data]:
|
||||
try:
|
||||
query_data = json.loads(query_payload)
|
||||
filter_obj = query_data.get("filter")
|
||||
|
|
@ -107,7 +107,7 @@ class NotionListPages(CustomComponent):
|
|||
response.raise_for_status()
|
||||
|
||||
results = response.json()
|
||||
records = []
|
||||
data = []
|
||||
combined_text = f"Pages found: {len(results['results'])}\n\n"
|
||||
for page in results['results']:
|
||||
page_data = {
|
||||
|
|
@ -127,14 +127,14 @@ class NotionListPages(CustomComponent):
|
|||
)
|
||||
|
||||
combined_text += text
|
||||
records.append(Record(text=text, data=page_data))
|
||||
data.append(Data(text=text, data=page_data))
|
||||
|
||||
self.status = combined_text.strip()
|
||||
return records
|
||||
return data
|
||||
|
||||
except Exception as e:
|
||||
self.status = f"An error occurred: {str(e)}"
|
||||
return [Record(text=self.status, data=[])]
|
||||
return [Data(text=self.status, data=[])]
|
||||
```
|
||||
|
||||
<Admonition type="info" title="Example Usage">
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import requests
|
|||
from typing import List
|
||||
|
||||
from langflow import CustomComponent
|
||||
from langflow.schema import Record
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class NotionUserList(CustomComponent):
|
||||
|
|
@ -52,7 +52,7 @@ class NotionUserList(CustomComponent):
|
|||
def build(
|
||||
self,
|
||||
notion_secret: str,
|
||||
) -> List[Record]:
|
||||
) -> List[Data]:
|
||||
url = "https://api.notion.com/v1/users"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {notion_secret}",
|
||||
|
|
@ -65,14 +65,14 @@ class NotionUserList(CustomComponent):
|
|||
data = response.json()
|
||||
results = data['results']
|
||||
|
||||
records = []
|
||||
data = []
|
||||
for user in results:
|
||||
id = user['id']
|
||||
type = user['type']
|
||||
name = user.get('name', '')
|
||||
avatar_url = user.get('avatar_url', '')
|
||||
|
||||
record_data = {
|
||||
data_dict = {
|
||||
"id": id,
|
||||
"type": type,
|
||||
"name": name,
|
||||
|
|
@ -80,15 +80,15 @@ class NotionUserList(CustomComponent):
|
|||
}
|
||||
|
||||
output = "User:\n"
|
||||
for key, value in record_data.items():
|
||||
for key, value in data_dict.items():
|
||||
output += f"{key.replace('_', ' ').title()}: {value}\n"
|
||||
output += "________________________\n"
|
||||
|
||||
record = Record(text=output, data=record_data)
|
||||
records.append(record)
|
||||
record = Data(text=output, data=data_dict)
|
||||
data.append(record)
|
||||
|
||||
self.status = "\n".join(record.text for record in records)
|
||||
return records
|
||||
self.status = "\n".join(record.text for record in data)
|
||||
return data
|
||||
```
|
||||
|
||||
## Example Usage
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import requests
|
|||
from typing import Dict, Any
|
||||
|
||||
from langflow import CustomComponent
|
||||
from langflow.schema import Record
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class NotionPageContent(CustomComponent):
|
||||
|
|
@ -64,7 +64,7 @@ class NotionPageContent(CustomComponent):
|
|||
self,
|
||||
page_id: str,
|
||||
notion_secret: str,
|
||||
) -> Record:
|
||||
) -> Data:
|
||||
blocks_url = f"https://api.notion.com/v1/blocks/{page_id}/children?page_size=100"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {notion_secret}",
|
||||
|
|
@ -80,7 +80,7 @@ class NotionPageContent(CustomComponent):
|
|||
content = self.parse_blocks(blocks_data["results"])
|
||||
|
||||
self.status = content
|
||||
return Record(data={"content": content}, text=content)
|
||||
return Data(data={"content": content}, text=content)
|
||||
|
||||
def parse_blocks(self, blocks: list) -> str:
|
||||
content = ""
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import requests
|
|||
from typing import Dict, Any
|
||||
|
||||
from langflow import CustomComponent
|
||||
from langflow.schema import Record
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class NotionPageUpdate(CustomComponent):
|
||||
|
|
@ -61,7 +61,7 @@ class NotionPageUpdate(CustomComponent):
|
|||
page_id: str,
|
||||
properties: str,
|
||||
notion_secret: str,
|
||||
) -> Record:
|
||||
) -> Data:
|
||||
url = f"https://api.notion.com/v1/pages/{page_id}"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {notion_secret}",
|
||||
|
|
@ -88,7 +88,7 @@ class NotionPageUpdate(CustomComponent):
|
|||
output += f"{prop_name}: {prop_value}\n"
|
||||
|
||||
self.status = output
|
||||
return Record(data=updated_page)
|
||||
return Data(data=updated_page)
|
||||
```
|
||||
|
||||
Let's break down the key parts of this component:
|
||||
|
|
@ -99,7 +99,7 @@ Let's break down the key parts of this component:
|
|||
|
||||
- The component interacts with the Notion API to update the page properties. It constructs the API URL, headers, and request data based on the provided parameters.
|
||||
|
||||
- The processed data is returned as a `Record` object, which can be connected to other components in the Langflow flow. The `Record` object contains the updated page data.
|
||||
- The processed data is returned as a `Data` object, which can be connected to other components in the Langflow flow. The `Data` object contains the updated page data.
|
||||
|
||||
- The component also stores the updated page properties in the `status` attribute for logging and debugging purposes.
|
||||
|
||||
|
|
@ -124,7 +124,7 @@ When using the `NotionPageUpdate` component, consider the following best practic
|
|||
|
||||
- Ensure that you have a valid Notion integration token with the necessary permissions to update page properties.
|
||||
- Handle edge cases and error scenarios gracefully, such as invalid JSON format for properties or API request failures.
|
||||
- We recommend using an LLM to generate the inputs for this component, to allow flexibilty
|
||||
- We recommend using an LLM to generate the inputs for this component, to allow flexibility
|
||||
|
||||
By leveraging the `NotionPageUpdate` component in Langflow, you can easily integrate updating Notion page properties into your language model workflows and build powerful applications that extend Langflow's capabilities.
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ To use the `NotionSearch` component in a Langflow flow, follow these steps:
|
|||
import requests
|
||||
from typing import Dict, Any, List
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Record
|
||||
from langflow.schema import Data
|
||||
|
||||
class NotionSearch(CustomComponent):
|
||||
display_name = "Search Notion"
|
||||
|
|
@ -88,7 +88,7 @@ class NotionSearch(CustomComponent):
|
|||
query: str = "",
|
||||
filter_value: str = "page",
|
||||
sort_direction: str = "descending",
|
||||
) -> List[Record]:
|
||||
) -> List[Data]:
|
||||
try:
|
||||
url = "https://api.notion.com/v1/search"
|
||||
headers = {
|
||||
|
|
@ -113,7 +113,7 @@ class NotionSearch(CustomComponent):
|
|||
response.raise_for_status()
|
||||
|
||||
results = response.json()
|
||||
records = []
|
||||
data = []
|
||||
combined_text = f"Results found: {len(results['results'])}\n\n"
|
||||
for result in results['results']:
|
||||
result_data = {
|
||||
|
|
@ -135,14 +135,14 @@ class NotionSearch(CustomComponent):
|
|||
|
||||
text += f"type: {result['object']}\nlast_edited_time: {result['last_edited_time']}\n\n"
|
||||
combined_text += text
|
||||
records.append(Record(text=text, data=result_data))
|
||||
data.append(Data(text=text, data=result_data))
|
||||
|
||||
self.status = combined_text
|
||||
return records
|
||||
return data
|
||||
|
||||
except Exception as e:
|
||||
self.status = f"An error occurred: {str(e)}"
|
||||
return [Record(text=self.status, data=[])]
|
||||
return [Data(text=self.status, data=[])]
|
||||
```
|
||||
|
||||
## Example Usage
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ We have a special channel in our Discord server dedicated to Langflow 1.0 migrat
|
|||
- Continued support for LangChain and new support for multiple frameworks
|
||||
- Redesigned sidebar and customizable interaction panel
|
||||
- New Native Categories and Components
|
||||
- Improved user experience with Text and Record modes
|
||||
- Improved user experience with Text and Data modes
|
||||
- CustomComponent for all components
|
||||
- Compatibility with previous versions using Runnable Executor
|
||||
- Multiple flows in the canvas
|
||||
|
|
@ -32,49 +32,49 @@ We have a special channel in our Discord server dedicated to Langflow 1.0 migrat
|
|||
|
||||
Langflow 1.0 introduces adds the concept of Inputs and Outputs to flows, allowing a clear definition of the data flow between components. Discover how to use Inputs and Outputs to pass data between components and create more dynamic flows.
|
||||
|
||||
[Learn more about Inputs and Outputs](../components/inputs-and-outputs.mdx)
|
||||
[Learn more about Inputs and Outputs](../components/inputs-and-outputs)
|
||||
|
||||
## To Compose or Not to Compose: The Choice is Yours
|
||||
|
||||
Even though composition is still possible in Langflow 1.0, the new standard is getting data moving through the flow. This allows for more flexibility and control over the data flow in your projects.
|
||||
|
||||
[See our example components](../examples/create-record.mdx) for examples of interweaving LangChain components with our Core components.
|
||||
[See our example components](../examples/create-record) for examples of interweaving LangChain components with our Core components.
|
||||
|
||||
## Continued Support for LangChain and Multiple Frameworks
|
||||
|
||||
Langflow 1.0 continues to support LangChain while also introducing support for multiple frameworks. This is another important boon that adding the paradigm of data flow brings to the table. Find out how to leverage the power of different frameworks in your projects.
|
||||
|
||||
[Learn more about compatibility and updating existing flows](./compatibility.mdx)
|
||||
[Learn more about compatibility and updating existing flows](./compatibility)
|
||||
|
||||
## Sidebar Redesign and Customizable Playground
|
||||
|
||||
We've expanded on the chat experience by creating a customizable interaction panel that allows you to design a panel that fits your needs and interact with it. The sidebar has also been redesigned to provide a more intuitive and user-friendly experience. Explore the new sidebar and interaction panel features to enhance your workflow.
|
||||
|
||||
[Learn more about the Playground](../administration/playground.mdx)
|
||||
[Learn more about the Playground](../administration/playground)
|
||||
|
||||
## New Native Categories and Components
|
||||
|
||||
Langflow 1.0 introduces many new native categories, including Inputs, Outputs, Helpers, Experimental, Models, and more. Discover the new components available, such as Chat Input, Prompt, Files, API Request, and others.
|
||||
|
||||
[Learn more about new components](../components/inputs-and-outputs.mdx)
|
||||
[Learn more about new components](../components/inputs-and-outputs)
|
||||
|
||||
## New Way of Using Langflow: Text and Record (and more to come)
|
||||
## New Way of Using Langflow: Text and Data (and more to come)
|
||||
|
||||
With the introduction of Text and Record types connections between Components are more intuitive and easier to understand. This is the first step in a series of improvements to the way you interact with Langflow. Learn how to use Text, and Record and how they help you build better flows.
|
||||
With the introduction of Text and Data types connections between Components are more intuitive and easier to understand. This is the first step in a series of improvements to the way you interact with Langflow. Learn how to use Text, and Data and how they help you build better flows.
|
||||
|
||||
[Learn more about Text and Record](../components/text-and-record.mdx)
|
||||
[Learn more about Text and Record](../components/text-and-record)
|
||||
|
||||
## CustomComponent for All Components
|
||||
|
||||
Almost all components in Langflow 1.0 are now CustomComponents, allowing you to check and modify the code of each component. Discover how to leverage this feature to customize your components to your specific needs.
|
||||
|
||||
[Learn more about CustomComponents](../components/custom.mdx)
|
||||
[Learn more about CustomComponents](../components/custom)
|
||||
|
||||
## Compatibility with Previous Versions
|
||||
|
||||
To use flows built in previous versions of Langflow, you can utilize the experimental component Runnable Executor along with an Input and Output. **We'd love your feedback on this**. Learn how to adapt your existing flows to work seamlessly in the new version of Langflow.
|
||||
|
||||
[Learn more about Compatibility with Previous Versions](./compatibility.mdx)
|
||||
[Learn more about Compatibility with Previous Versions](./compatibility)
|
||||
|
||||
## Multiple Flows in the Canvas
|
||||
|
||||
|
|
@ -86,37 +86,37 @@ Langflow 1.0 allows you to have more than one flow in the canvas and run them se
|
|||
|
||||
Each component now displays its status more clearly, allowing you to quickly identify any issues or errors. Explore how to use the new component status feature to troubleshoot and optimize your flows.
|
||||
|
||||
[Learn more about Component Status](../getting-started/canvas.mdx#component)
|
||||
[Learn more about Component Status](../getting-started/canvas#component)
|
||||
|
||||
## Connecting Output Components
|
||||
|
||||
You can now connect Output components to any other component (that has a Text output), providing a better understanding of the data flow. Explore the possibilities of connecting Output components and how it enhances your flow's functionality.
|
||||
|
||||
[Learn more about Inputs and Outputs](../components/inputs-and-outputs.mdx)
|
||||
[Learn more about Inputs and Outputs](../components/inputs-and-outputs)
|
||||
|
||||
## Renaming and Editing Component Descriptions
|
||||
|
||||
Langflow 1.0 allows you to rename and edit the description of each component, making it easier to understand and interact with the flow. Learn how to customize your component names and descriptions for improved clarity.
|
||||
|
||||
[Learn more about Component Descriptions](../getting-started/canvas.mdx#component-parameters)
|
||||
[Learn more about Component Descriptions](../getting-started/canvas#component-parameters)
|
||||
|
||||
## Passing Tweaks and Inputs in the API
|
||||
|
||||
Things got a whole lot easier. You can now pass tweaks and inputs in the API by referencing the Display Name of the component. Discover how to leverage this feature to dynamically control your flow's behavior.
|
||||
|
||||
[Learn more about Tweaks and API inputs](../getting-started/canvas.mdx#tweaks)
|
||||
[Learn more about Tweaks and API inputs](../getting-started/canvas#tweaks)
|
||||
|
||||
## Global Variables for Text Fields
|
||||
|
||||
Global Variables can be used in any Text Field across your projects. Learn how to define and utilize Global Variables to streamline your workflow.
|
||||
|
||||
[Learn more about Global Variables](../administration/global-env.mdx)
|
||||
[Learn more about Global Variables](../administration/global-env)
|
||||
|
||||
## Experimental Components
|
||||
|
||||
Explore the experimental components available in Langflow 1.0, such as SubFlow, which allows you to load a flow as a component dynamically, and Flow as Tool, which enables you to use a flow as a tool for an Agent.
|
||||
|
||||
[Learn more about Experimental Components](../components/experimental.mdx)
|
||||
[Learn more about Experimental Components](../components/experimental)
|
||||
|
||||
## Experimental State Management System
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ This article demonstrates how to use Langflow's prompt tools to issue basic prom
|
|||
|
||||
## Prerequisites
|
||||
|
||||
- [Langflow installed and running](../getting-started/install-langflow.mdx)
|
||||
- [Langflow installed and running](../getting-started/install-langflow)
|
||||
|
||||
- [OpenAI API key created](https://platform.openai.com)
|
||||
|
||||
|
|
@ -62,4 +62,8 @@ This should be interesting...
|
|||
The **Edit Prompt** window opens.
|
||||
2. Change `Answer the user as if you were a pirate` to a different character, perhaps `Answer the user as if you were Harold Abelson.`
|
||||
3. Run the basic prompting flow again.
|
||||
The response will be markedly different.
|
||||
The response will be markedly different.import ThemedImage from "@theme/ThemedImage";
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
import ZoomableImage from "/src/theme/ZoomableImage.js";
|
||||
import ReactPlayer from "react-player";
|
||||
import Admonition from "@theme/Admonition";
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Build a blog writer with OpenAI that uses URLs for reference content.
|
|||
|
||||
## Prerequisites
|
||||
|
||||
- [Langflow installed and running](../getting-started/install-langflow.mdx)
|
||||
- [Langflow installed and running](../getting-started/install-langflow)
|
||||
|
||||
- [OpenAI API key created](https://platform.openai.com)
|
||||
|
||||
|
|
@ -75,4 +75,8 @@ The `reference_1` and `reference_2` values are received from the **URL** fields
|
|||
3. The **OpenAI** component constructs a blog post with the **URL** items as context.
|
||||
The default **URL** values are for web pages at `promptingguide.ai`, so your blog post will be about prompting LLMs.
|
||||
|
||||
To write about something different, change the values in the **URL** components, and see what the LLM constructs.
|
||||
To write about something different, change the values in the **URL** components, and see what the LLM constructs.import ThemedImage from "@theme/ThemedImage";
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
import ZoomableImage from "/src/theme/ZoomableImage.js";
|
||||
import ReactPlayer from "react-player";
|
||||
import Admonition from "@theme/Admonition";
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Build a question-and-answer chatbot with a document loaded from local memory.
|
|||
|
||||
## Prerequisites
|
||||
|
||||
- [Langflow installed and running](../getting-started/install-langflow.mdx)
|
||||
- [Langflow installed and running](../getting-started/install-langflow)
|
||||
|
||||
- [OpenAI API key created](https://platform.openai.com)
|
||||
|
||||
|
|
@ -67,4 +67,8 @@ Including a file with the prompt gives the **OpenAI** component context it may n
|
|||
The issue occurred during the execution of migrations in the application. Specifically, an error was raised by the Alembic library, indicating that new upgrade operations were detected that had not been accounted for in the existing migration scripts. The operation in question involved modifying the nullable property of a column (apikey, created_at) in the database, with details about the existing type (DATETIME()), existing server default, and other properties.
|
||||
```
|
||||
|
||||
This result indicates that the bot received the loaded document and understood the context surrounding the vague question. It also correctly identified the issue in the error log, and followed up with appropriate troubleshooting suggestions. Nice!
|
||||
This result indicates that the bot received the loaded document and understood the context surrounding the vague question. It also correctly identified the issue in the error log, and followed up with appropriate troubleshooting suggestions. Nice!import ThemedImage from "@theme/ThemedImage";
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
import ZoomableImage from "/src/theme/ZoomableImage.js";
|
||||
import ReactPlayer from "react-player";
|
||||
import Admonition from "@theme/Admonition";
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ import Admonition from "@theme/Admonition";
|
|||
|
||||
# Memory Chatbot
|
||||
|
||||
This flow extends the [basic prompting flow](./basic-prompting.mdx) to include chat memory for unique SessionIDs.
|
||||
This flow extends the [basic prompting flow](./basic-prompting) to include chat memory for unique SessionIDs.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Langflow installed and running](../getting-started/install-langflow.mdx)
|
||||
- [Langflow installed and running](../getting-started/install-langflow)
|
||||
|
||||
- [OpenAI API key created](https://platform.openai.com)
|
||||
|
||||
|
|
@ -81,4 +81,8 @@ To store **Session ID** as a Langflow variable, in the **Session ID** field, cli
|
|||
1. In the **Variable Name** field, enter a name like `customer_chat_emea`.
|
||||
2. In the **Value** field, enter a value like `1B5EBD79-6E9C-4533-B2C8-7E4FF29E983B`.
|
||||
3. Click **Save Variable**.
|
||||
4. Apply this variable to **Chat Input**.
|
||||
4. Apply this variable to **Chat Input**.import ThemedImage from "@theme/ThemedImage";
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
import ZoomableImage from "/src/theme/ZoomableImage.js";
|
||||
import ReactPlayer from "react-player";
|
||||
import Admonition from "@theme/Admonition";
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ We've chosen [Astra DB](https://astra.datastax.com/signup?utm_source=langflow-pr
|
|||
to create your own Langflow workspace in minutes.
|
||||
</Admonition>
|
||||
|
||||
- [Langflow installed and running](../getting-started/install-langflow.mdx)
|
||||
- [Langflow installed and running](../getting-started/install-langflow)
|
||||
|
||||
- [OpenAI API key](https://platform.openai.com)
|
||||
|
||||
|
|
@ -56,9 +56,9 @@ The **query** flow (top of the screen) allows users to chat with the embedded ve
|
|||
|
||||
- **Chat Input** component defines where to put the user input coming from the Playground.
|
||||
- **OpenAI Embeddings** component generates embeddings from the user input.
|
||||
- **Astra DB Search** component retrieves the most relevant Records from the Astra DB database.
|
||||
- **Text Output** component turns the Records into Text by concatenating them and also displays it in the Playground.
|
||||
- **Prompt** component takes in the user input and the retrieved Records as text and builds a prompt for the OpenAI model.
|
||||
- **Astra DB Search** component retrieves the most relevant Data from the Astra DB database.
|
||||
- **Text Output** component turns the Data into Text by concatenating them and also displays it in the Playground.
|
||||
- **Prompt** component takes in the user input and the retrieved Data as text and builds a prompt for the OpenAI model.
|
||||
- **OpenAI** component generates a response to the prompt.
|
||||
- **Chat Output** component displays the response in the Playground.
|
||||
|
||||
|
|
@ -106,4 +106,8 @@ AI
|
|||
You should use a 3/8 inch wrench to remove the oil drain cap.
|
||||
```
|
||||
|
||||
This is the size the engine manual lists as well. This confirms our flow works, because the query returns the unique knowledge we embedded from the Astra vector store.
|
||||
This is the size the engine manual lists as well. This confirms our flow works, because the query returns the unique knowledge we embedded from the Astra vector store.import ThemedImage from "@theme/ThemedImage";
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
import ZoomableImage from "/src/theme/ZoomableImage.js";
|
||||
import ReactPlayer from "react-player";
|
||||
import Admonition from "@theme/Admonition";
|
||||
|
|
|
|||
|
|
@ -130,10 +130,10 @@ The RAG flow is a bit more complex. It consists of:
|
|||
|
||||
- **Chat Input** component that defines where to put the user input coming from the Playground
|
||||
- **OpenAI Embeddings** component that generates embeddings from the user input
|
||||
- **Astra DB Search** component that retrieves the most relevant Records from the Astra DB database
|
||||
- **Text Output** component that turns the Records into Text by concatenating them and also displays it in the Playground
|
||||
- **Astra DB Search** component that retrieves the most relevant Data from the Astra DB database
|
||||
- **Text Output** component that turns the Data into Text by concatenating them and also displays it in the Playground
|
||||
- One interesting point you'll see here is that this component is named `Extracted Chunks`, and that is how it will appear in the Playground
|
||||
- **Prompt** component that takes in the user input and the retrieved Records as text and builds a prompt for the OpenAI model
|
||||
- **Prompt** component that takes in the user input and the retrieved Data as text and builds a prompt for the OpenAI model
|
||||
- **OpenAI** component that generates a response to the prompt
|
||||
- **Chat Output** component that displays the response in the Playground
|
||||
|
||||
|
|
@ -170,7 +170,7 @@ Because this flow has a **Chat Input** and a **Text Output** component, the Pane
|
|||
style={{ width: "80%", margin: "20px auto" }}
|
||||
/>
|
||||
|
||||
Once we interact with it we get a response and the Extracted Chunks section is updated with the retrieved records.
|
||||
Once we interact with it we get a response and the Extracted Chunks section is updated with the retrieved Data.
|
||||
|
||||
<ZoomableImage
|
||||
alt="Docusaurus themed image"
|
||||
|
|
@ -186,7 +186,4 @@ And that's it! You have successfully ran a RAG application using Astra DB and La
|
|||
# Conclusion
|
||||
|
||||
In this guide, we have learned how to run a RAG application using Astra DB and Langflow.
|
||||
We have seen how to create an Astra DB database, import the Astra DB RAG Flows project into Langflow, and run the ingestion and RAG flows.import ThemedImage from "@theme/ThemedImage";
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
import ZoomableImage from "/src/theme/ZoomableImage.js";
|
||||
import Admonition from "@theme/Admonition";
|
||||
We have seen how to create an Astra DB database, import the Astra DB RAG Flows project into Langflow, and run the ingestion and RAG flows.
|
||||
|
|
|
|||
4791
docs/package-lock.json
generated
4791
docs/package-lock.json
generated
File diff suppressed because it is too large
Load diff
5793
docs/static/data/AstraDB-RAG-Flows.json
vendored
5793
docs/static/data/AstraDB-RAG-Flows.json
vendored
File diff suppressed because one or more lines are too long
291
poetry.lock
generated
291
poetry.lock
generated
|
|
@ -472,17 +472,17 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "boto3"
|
||||
version = "1.34.128"
|
||||
version = "1.34.129"
|
||||
description = "The AWS SDK for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "boto3-1.34.128-py3-none-any.whl", hash = "sha256:a048ff980a81cd652724a73bc496c519b336fabe19cc8bfc6c53b2ff6eb22c7b"},
|
||||
{file = "boto3-1.34.128.tar.gz", hash = "sha256:43a6e99f53a8d34b3b4dbe424dbcc6b894350dc41a85b0af7c7bc24a7ec2cead"},
|
||||
{file = "boto3-1.34.129-py3-none-any.whl", hash = "sha256:cc73de1c9d953b1f9da6ee2404af717e93d888f790f3e0291b22d1b8489eb401"},
|
||||
{file = "boto3-1.34.129.tar.gz", hash = "sha256:a7a696fd3e7f5f43a81450b441f3eb6c5a89d28efe867cd97d8fc73ea5d8c139"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
botocore = ">=1.34.128,<1.35.0"
|
||||
botocore = ">=1.34.129,<1.35.0"
|
||||
jmespath = ">=0.7.1,<2.0.0"
|
||||
s3transfer = ">=0.10.0,<0.11.0"
|
||||
|
||||
|
|
@ -491,13 +491,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
|
|||
|
||||
[[package]]
|
||||
name = "botocore"
|
||||
version = "1.34.128"
|
||||
version = "1.34.129"
|
||||
description = "Low-level, data-driven core of boto 3."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "botocore-1.34.128-py3-none-any.whl", hash = "sha256:db67fda136c372ab3fa432580c819c89ba18d28a6152a4d2a7ea40d44082892e"},
|
||||
{file = "botocore-1.34.128.tar.gz", hash = "sha256:8d8e03f7c8c080ecafda72036eb3b482d649f8417c90b5dca33b7c2c47adb0c9"},
|
||||
{file = "botocore-1.34.129-py3-none-any.whl", hash = "sha256:86d3dd30996aa459e9c3321edac12aebe47c73cb4acc7556941f9b4c39726088"},
|
||||
{file = "botocore-1.34.129.tar.gz", hash = "sha256:7c56e25af6112d69c5d14a15b42f76ba7687687abc463a96ac5edca19c0a9c2d"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -1005,13 +1005,13 @@ numpy = "*"
|
|||
|
||||
[[package]]
|
||||
name = "chromadb"
|
||||
version = "0.5.0"
|
||||
version = "0.5.3"
|
||||
description = "Chroma."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "chromadb-0.5.0-py3-none-any.whl", hash = "sha256:8193dc65c143b61d8faf87f02c44ecfa778d471febd70de517f51c5d88a06009"},
|
||||
{file = "chromadb-0.5.0.tar.gz", hash = "sha256:7954af614a9ff7b2902ddbd0a162f33f7ec0669e2429903905c4f7876d1f766f"},
|
||||
{file = "chromadb-0.5.3-py3-none-any.whl", hash = "sha256:b3874f08356e291c68c6d2e177db472cd51f22f3af7b9746215b748fd1e29982"},
|
||||
{file = "chromadb-0.5.3.tar.gz", hash = "sha256:05d887f56a46b2e0fc6ac5ab979503a27b9ee50d5ca9e455f83b2fb9840cd026"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -1020,10 +1020,11 @@ build = ">=1.0.3"
|
|||
chroma-hnswlib = "0.7.3"
|
||||
fastapi = ">=0.95.2"
|
||||
grpcio = ">=1.58.0"
|
||||
httpx = ">=0.27.0"
|
||||
importlib-resources = "*"
|
||||
kubernetes = ">=28.1.0"
|
||||
mmh3 = ">=4.0.1"
|
||||
numpy = ">=1.22.5"
|
||||
numpy = ">=1.22.5,<2.0.0"
|
||||
onnxruntime = ">=1.14.1"
|
||||
opentelemetry-api = ">=1.2.0"
|
||||
opentelemetry-exporter-otlp-proto-grpc = ">=1.2.0"
|
||||
|
|
@ -2439,8 +2440,8 @@ files = [
|
|||
[package.dependencies]
|
||||
cffi = {version = ">=1.12.2", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""}
|
||||
greenlet = [
|
||||
{version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""},
|
||||
{version = ">=3.0rc3", markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\""},
|
||||
{version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""},
|
||||
]
|
||||
"zope.event" = "*"
|
||||
"zope.interface" = "*"
|
||||
|
|
@ -2535,6 +2536,38 @@ benchmarks = ["httplib2", "httpx", "requests", "urllib3"]
|
|||
dev = ["dpkt", "pytest", "requests"]
|
||||
examples = ["oauth2"]
|
||||
|
||||
[[package]]
|
||||
name = "gitdb"
|
||||
version = "4.0.11"
|
||||
description = "Git Object Database"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"},
|
||||
{file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
smmap = ">=3.0.1,<6"
|
||||
|
||||
[[package]]
|
||||
name = "gitpython"
|
||||
version = "3.1.43"
|
||||
description = "GitPython is a Python library used to interact with Git repositories"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "GitPython-3.1.43-py3-none-any.whl", hash = "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff"},
|
||||
{file = "GitPython-3.1.43.tar.gz", hash = "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
gitdb = ">=4.0.1,<5"
|
||||
|
||||
[package.extras]
|
||||
doc = ["sphinx (==4.3.2)", "sphinx-autodoc-typehints", "sphinx-rtd-theme", "sphinxcontrib-applehelp (>=1.0.2,<=1.0.4)", "sphinxcontrib-devhelp (==1.0.2)", "sphinxcontrib-htmlhelp (>=2.0.0,<=2.0.1)", "sphinxcontrib-qthelp (==1.0.3)", "sphinxcontrib-serializinghtml (==1.1.5)"]
|
||||
test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions"]
|
||||
|
||||
[[package]]
|
||||
name = "google-ai-generativelanguage"
|
||||
version = "0.6.4"
|
||||
|
|
@ -2567,12 +2600,12 @@ files = [
|
|||
google-auth = ">=2.14.1,<3.0.dev0"
|
||||
googleapis-common-protos = ">=1.56.2,<2.0.dev0"
|
||||
grpcio = [
|
||||
{version = ">=1.33.2,<2.0dev", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""},
|
||||
{version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
|
||||
{version = ">=1.33.2,<2.0dev", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""},
|
||||
]
|
||||
grpcio-status = [
|
||||
{version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""},
|
||||
{version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
|
||||
{version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""},
|
||||
]
|
||||
proto-plus = ">=1.22.3,<2.0.0dev"
|
||||
protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0"
|
||||
|
|
@ -2641,13 +2674,13 @@ httplib2 = ">=0.19.0"
|
|||
|
||||
[[package]]
|
||||
name = "google-cloud-aiplatform"
|
||||
version = "1.55.0"
|
||||
version = "1.56.0"
|
||||
description = "Vertex AI API client library"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "google-cloud-aiplatform-1.55.0.tar.gz", hash = "sha256:aa87cb6c49ae5fde87fb831ce8ad4a853c4656fe04babe505e9144c7a9e09c1a"},
|
||||
{file = "google_cloud_aiplatform-1.55.0-py2.py3-none-any.whl", hash = "sha256:c6cc76ca5537f4636a0c3f8c0288d2e0d2d86ef708e562d2654313e11d6ee46a"},
|
||||
{file = "google-cloud-aiplatform-1.56.0.tar.gz", hash = "sha256:d4cfb085427dac01142915f523949ac2955d6c7f148d95017d3286a77caf5d5e"},
|
||||
{file = "google_cloud_aiplatform-1.56.0-py2.py3-none-any.whl", hash = "sha256:ee1ab3bd115c3caebf8ddfd3e47eeb8396a3ec2fc5f5baf1a5c295c8d64333ab"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -2669,8 +2702,8 @@ cloud-profiler = ["tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow
|
|||
datasets = ["pyarrow (>=10.0.1)", "pyarrow (>=14.0.0)", "pyarrow (>=3.0.0,<8.0dev)"]
|
||||
endpoint = ["requests (>=2.28.1)"]
|
||||
full = ["cloudpickle (<3.0)", "docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<=0.109.1)", "google-cloud-bigquery", "google-cloud-bigquery-storage", "google-cloud-logging (<4.0)", "google-vizier (>=0.1.6)", "httpx (>=0.23.0,<0.25.0)", "immutabledict", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "nest-asyncio (>=1.0.0,<1.6.0)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pandas (>=1.0.0,<2.2.0)", "pyarrow (>=10.0.1)", "pyarrow (>=14.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pydantic (<2)", "pyyaml (>=5.3.1,<7)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "ray[default] (>=2.5,<=2.9.3)", "requests (>=2.28.1)", "setuptools (<70.0.0)", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)"]
|
||||
langchain = ["langchain (>=0.1.16,<0.3)", "langchain-core (<0.2)", "langchain-google-vertexai (<2)", "openinference-instrumentation-langchain (>=0.1.19,<0.2)"]
|
||||
langchain-testing = ["absl-py", "cloudpickle (>=2.2.1,<4.0)", "langchain (>=0.1.16,<0.3)", "langchain-core (<0.2)", "langchain-google-vertexai (<2)", "openinference-instrumentation-langchain (>=0.1.19,<0.2)", "opentelemetry-exporter-gcp-trace (<2)", "opentelemetry-sdk (<2)", "pydantic (>=2.6.3,<3)", "pytest-xdist"]
|
||||
langchain = ["langchain (>=0.1.16,<0.3)", "langchain-core (<0.2)", "langchain-google-vertexai (<2)", "openinference-instrumentation-langchain (>=0.1.19,<0.2)", "tenacity (<=8.3)"]
|
||||
langchain-testing = ["absl-py", "cloudpickle (>=3.0,<4.0)", "langchain (>=0.1.16,<0.3)", "langchain-core (<0.2)", "langchain-google-vertexai (<2)", "openinference-instrumentation-langchain (>=0.1.19,<0.2)", "opentelemetry-exporter-gcp-trace (<2)", "opentelemetry-sdk (<2)", "pydantic (>=2.6.3,<3)", "pytest-xdist", "tenacity (<=8.3)"]
|
||||
lit = ["explainable-ai-sdk (>=1.0.0)", "lit-nlp (==0.4.0)", "pandas (>=1.0.0)", "tensorflow (>=2.3.0,<3.0.0dev)"]
|
||||
metadata = ["numpy (>=1.15.0)", "pandas (>=1.0.0)"]
|
||||
pipelines = ["pyyaml (>=5.3.1,<7)"]
|
||||
|
|
@ -2680,7 +2713,7 @@ private-endpoints = ["requests (>=2.28.1)", "urllib3 (>=1.21.1,<1.27)"]
|
|||
rapid-evaluation = ["nest-asyncio (>=1.0.0,<1.6.0)", "pandas (>=1.0.0,<2.2.0)"]
|
||||
ray = ["google-cloud-bigquery", "google-cloud-bigquery-storage", "immutabledict", "pandas (>=1.0.0,<2.2.0)", "pyarrow (>=6.0.1)", "pydantic (<2)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "ray[default] (>=2.5,<=2.9.3)", "setuptools (<70.0.0)"]
|
||||
ray-testing = ["google-cloud-bigquery", "google-cloud-bigquery-storage", "immutabledict", "pandas (>=1.0.0,<2.2.0)", "pyarrow (>=6.0.1)", "pydantic (<2)", "pytest-xdist", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "ray[default] (>=2.5,<=2.9.3)", "ray[train] (==2.9.3)", "scikit-learn", "setuptools (<70.0.0)", "tensorflow", "torch (>=2.0.0,<2.1.0)", "xgboost", "xgboost-ray"]
|
||||
reasoningengine = ["cloudpickle (>=2.2.1,<4.0)", "opentelemetry-exporter-gcp-trace (<2)", "opentelemetry-sdk (<2)", "pydantic (>=2.6.3,<3)"]
|
||||
reasoningengine = ["cloudpickle (>=3.0,<4.0)", "opentelemetry-exporter-gcp-trace (<2)", "opentelemetry-sdk (<2)", "pydantic (>=2.6.3,<3)"]
|
||||
tensorboard = ["tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "werkzeug (>=2.0.0,<2.1.0dev)"]
|
||||
testing = ["bigframes", "cloudpickle (<3.0)", "docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<=0.109.1)", "google-api-core (>=2.11,<3.0.0)", "google-cloud-bigquery", "google-cloud-bigquery-storage", "google-cloud-logging (<4.0)", "google-vizier (>=0.1.6)", "grpcio-testing", "httpx (>=0.23.0,<0.25.0)", "immutabledict", "ipython", "kfp (>=2.6.0,<3.0.0)", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "nest-asyncio (>=1.0.0,<1.6.0)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pandas (>=1.0.0,<2.2.0)", "pyarrow (>=10.0.1)", "pyarrow (>=14.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pydantic (<2)", "pyfakefs", "pytest-asyncio", "pytest-xdist", "pyyaml (>=5.3.1,<7)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "ray[default] (>=2.5,<=2.9.3)", "requests (>=2.28.1)", "requests-toolbelt (<1.0.0)", "scikit-learn", "setuptools (<70.0.0)", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (==2.13.0)", "tensorflow (==2.16.1)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "torch (>=2.0.0,<2.1.0)", "torch (>=2.2.0)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)", "xgboost"]
|
||||
vizier = ["google-vizier (>=0.1.6)"]
|
||||
|
|
@ -4140,6 +4173,22 @@ astrapy = ">=1.2,<2.0"
|
|||
langchain-core = ">=0.1.31,<0.3"
|
||||
numpy = ">=1,<2"
|
||||
|
||||
[[package]]
|
||||
name = "langchain-aws"
|
||||
version = "0.1.6"
|
||||
description = "An integration package connecting AWS and LangChain"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_aws-0.1.6-py3-none-any.whl", hash = "sha256:a747c282c2df29281918818238fea5f7e183cfa1b28add4a08d7f3f9a4438ca9"},
|
||||
{file = "langchain_aws-0.1.6.tar.gz", hash = "sha256:d5ceb3db72aef98b2fb2336add7932f1ed045df8824fef24cd3613d93d8b4efb"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
boto3 = ">=1.34.51,<1.35.0"
|
||||
langchain-core = ">=0.1.45,<0.3"
|
||||
numpy = ">=1,<2"
|
||||
|
||||
[[package]]
|
||||
name = "langchain-chroma"
|
||||
version = "0.1.1"
|
||||
|
|
@ -4200,22 +4249,25 @@ tenacity = ">=8.1.0,<9.0.0"
|
|||
|
||||
[[package]]
|
||||
name = "langchain-core"
|
||||
version = "0.2.8"
|
||||
version = "0.2.9"
|
||||
description = "Building applications with LLMs through composability"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_core-0.2.8-py3-none-any.whl", hash = "sha256:172c81c858dc1f3123cc72b7e44e10f44c92f8a761cae18c364081f6c208e9f6"},
|
||||
{file = "langchain_core-0.2.8.tar.gz", hash = "sha256:2db866a4514672c4875b69d5590aa2ed50aa0d144874268bef68d74b5e7f33f9"},
|
||||
{file = "langchain_core-0.2.9-py3-none-any.whl", hash = "sha256:426a5a4fea95a5db995ba5ab560b76edd4998fb6fe52ccc28ac987092a4cbfcd"},
|
||||
{file = "langchain_core-0.2.9.tar.gz", hash = "sha256:f1c59082642921727844e1cd0eb36d451edd1872c20e193aa3142aac03495986"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
jsonpatch = ">=1.33,<2.0"
|
||||
langsmith = ">=0.1.75,<0.2.0"
|
||||
packaging = ">=23.2,<25"
|
||||
pydantic = ">=1,<3"
|
||||
pydantic = [
|
||||
{version = ">=1,<3", markers = "python_full_version < \"3.12.4\""},
|
||||
{version = ">=2.7.4,<3.0.0", markers = "python_full_version >= \"3.12.4\""},
|
||||
]
|
||||
PyYAML = ">=5.3"
|
||||
tenacity = ">=8.1.0,<9.0.0"
|
||||
tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "langchain-experimental"
|
||||
|
|
@ -4301,6 +4353,22 @@ httpx-sse = ">=0.3.1,<1"
|
|||
langchain-core = ">=0.2.0,<0.3"
|
||||
tokenizers = ">=0.15.1,<1"
|
||||
|
||||
[[package]]
|
||||
name = "langchain-mongodb"
|
||||
version = "0.1.6"
|
||||
description = "An integration package connecting MongoDB and LangChain"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_mongodb-0.1.6-py3-none-any.whl", hash = "sha256:ba5e1e388131ae00e8fcd39c4b38ce42c29f00c4bc39c58023b354b597ffa8df"},
|
||||
{file = "langchain_mongodb-0.1.6.tar.gz", hash = "sha256:a7a6b66d5270d6f8732c4e848f9a3742bbd4485c8829a2d629332ee683b936d1"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
langchain-core = ">=0.1.46,<0.3"
|
||||
numpy = ">=1,<2"
|
||||
pymongo = ">=4.6.1,<5.0"
|
||||
|
||||
[[package]]
|
||||
name = "langchain-openai"
|
||||
version = "0.1.8"
|
||||
|
|
@ -4465,13 +4533,13 @@ openai = ["openai (>=0.27.8)"]
|
|||
|
||||
[[package]]
|
||||
name = "langsmith"
|
||||
version = "0.1.78"
|
||||
version = "0.1.80"
|
||||
description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform."
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langsmith-0.1.78-py3-none-any.whl", hash = "sha256:87bc5d9072bfcb6392d7552cbcd6089dcc1faed36d688b1587d80bd48a1acba2"},
|
||||
{file = "langsmith-0.1.78.tar.gz", hash = "sha256:d9112d2e9298ec6b02d3b1afec6ed557df9db3746c79d34ef3b448fc18e116cd"},
|
||||
{file = "langsmith-0.1.80-py3-none-any.whl", hash = "sha256:951fc29576b52afd8378d41f6db343090fea863e3620f0ca97e83b221f93c94d"},
|
||||
{file = "langsmith-0.1.80.tar.gz", hash = "sha256:a29b1dde27612308beee424f1388ad844c8e7e375bf2ac8bdf4da174013f279d"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -4481,18 +4549,19 @@ requests = ">=2,<3"
|
|||
|
||||
[[package]]
|
||||
name = "litellm"
|
||||
version = "1.40.15"
|
||||
version = "1.40.16"
|
||||
description = "Library to easily interface with LLM API providers"
|
||||
optional = false
|
||||
python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8"
|
||||
files = [
|
||||
{file = "litellm-1.40.15-py3-none-any.whl", hash = "sha256:4f51376535857c109723cede78f50e0794be3814b6da1f02ea8263fe5480dc42"},
|
||||
{file = "litellm-1.40.15.tar.gz", hash = "sha256:bd3ca27c69deb47c5d11c69a6c2f7a618512720bd2d94ff64d055c09d9fba366"},
|
||||
{file = "litellm-1.40.16-py3-none-any.whl", hash = "sha256:b78571a39a3b7819ca9cb96278f4b15b97557e2cd50e9aeee9210a7e744b8667"},
|
||||
{file = "litellm-1.40.16.tar.gz", hash = "sha256:b32cd15c7cf9084aa0f92eb7103323741d63cc8a14e28b739054b0cb6682c431"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
aiohttp = "*"
|
||||
click = "*"
|
||||
ijson = "*"
|
||||
importlib-metadata = ">=6.8.0"
|
||||
jinja2 = ">=3.1.2,<4.0.0"
|
||||
openai = ">=1.27.0"
|
||||
|
|
@ -4551,8 +4620,8 @@ psutil = ">=5.9.1"
|
|||
pywin32 = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
pyzmq = ">=25.0.0"
|
||||
requests = [
|
||||
{version = ">=2.26.0", markers = "python_version <= \"3.11\""},
|
||||
{version = ">=2.32.2", markers = "python_version > \"3.11\""},
|
||||
{version = ">=2.26.0", markers = "python_version <= \"3.11\""},
|
||||
]
|
||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||
Werkzeug = ">=2.0.0"
|
||||
|
|
@ -6020,9 +6089,9 @@ files = [
|
|||
|
||||
[package.dependencies]
|
||||
numpy = [
|
||||
{version = ">=1.26.0,<2", markers = "python_version >= \"3.12\""},
|
||||
{version = ">=1.22.4,<2", markers = "python_version < \"3.11\""},
|
||||
{version = ">=1.23.2,<2", markers = "python_version == \"3.11\""},
|
||||
{version = ">=1.26.0,<2", markers = "python_version >= \"3.12\""},
|
||||
]
|
||||
python-dateutil = ">=2.8.2"
|
||||
pytz = ">=2020.1"
|
||||
|
|
@ -6427,27 +6496,28 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "psutil"
|
||||
version = "5.9.8"
|
||||
version = "6.0.0"
|
||||
description = "Cross-platform lib for process and system monitoring in Python."
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
|
||||
files = [
|
||||
{file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"},
|
||||
{file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"},
|
||||
{file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"},
|
||||
{file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"},
|
||||
{file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"},
|
||||
{file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"},
|
||||
{file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"},
|
||||
{file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"},
|
||||
{file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"},
|
||||
{file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"},
|
||||
{file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"},
|
||||
{file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"},
|
||||
{file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"},
|
||||
{file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"},
|
||||
{file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"},
|
||||
{file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"},
|
||||
{file = "psutil-6.0.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6"},
|
||||
{file = "psutil-6.0.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0"},
|
||||
{file = "psutil-6.0.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c"},
|
||||
{file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3"},
|
||||
{file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c"},
|
||||
{file = "psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35"},
|
||||
{file = "psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1"},
|
||||
{file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"},
|
||||
{file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"},
|
||||
{file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"},
|
||||
{file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"},
|
||||
{file = "psutil-6.0.0-cp36-cp36m-win32.whl", hash = "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14"},
|
||||
{file = "psutil-6.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c"},
|
||||
{file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"},
|
||||
{file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"},
|
||||
{file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"},
|
||||
{file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
|
|
@ -7105,12 +7175,12 @@ image = ["Pillow (>=8.0.0)"]
|
|||
|
||||
[[package]]
|
||||
name = "pyperclip"
|
||||
version = "1.8.2"
|
||||
version = "1.9.0"
|
||||
description = "A cross-platform clipboard module for Python. (Only handles plain text for now.)"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "pyperclip-1.8.2.tar.gz", hash = "sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57"},
|
||||
{file = "pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -8482,6 +8552,17 @@ files = [
|
|||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smmap"
|
||||
version = "5.0.1"
|
||||
description = "A pure Python implementation of a sliding window memory map manager"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"},
|
||||
{file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sniffio"
|
||||
version = "1.3.1"
|
||||
|
|
@ -8506,64 +8587,64 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "sqlalchemy"
|
||||
version = "2.0.30"
|
||||
version = "2.0.31"
|
||||
description = "Database Abstraction Library"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3b48154678e76445c7ded1896715ce05319f74b1e73cf82d4f8b59b46e9c0ddc"},
|
||||
{file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2753743c2afd061bb95a61a51bbb6a1a11ac1c44292fad898f10c9839a7f75b2"},
|
||||
{file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7bfc726d167f425d4c16269a9a10fe8630ff6d14b683d588044dcef2d0f6be7"},
|
||||
{file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4f61ada6979223013d9ab83a3ed003ded6959eae37d0d685db2c147e9143797"},
|
||||
{file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a365eda439b7a00732638f11072907c1bc8e351c7665e7e5da91b169af794af"},
|
||||
{file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bba002a9447b291548e8d66fd8c96a6a7ed4f2def0bb155f4f0a1309fd2735d5"},
|
||||
{file = "SQLAlchemy-2.0.30-cp310-cp310-win32.whl", hash = "sha256:0138c5c16be3600923fa2169532205d18891b28afa817cb49b50e08f62198bb8"},
|
||||
{file = "SQLAlchemy-2.0.30-cp310-cp310-win_amd64.whl", hash = "sha256:99650e9f4cf3ad0d409fed3eec4f071fadd032e9a5edc7270cd646a26446feeb"},
|
||||
{file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:955991a09f0992c68a499791a753523f50f71a6885531568404fa0f231832aa0"},
|
||||
{file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f69e4c756ee2686767eb80f94c0125c8b0a0b87ede03eacc5c8ae3b54b99dc46"},
|
||||
{file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69c9db1ce00e59e8dd09d7bae852a9add716efdc070a3e2068377e6ff0d6fdaa"},
|
||||
{file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1429a4b0f709f19ff3b0cf13675b2b9bfa8a7e79990003207a011c0db880a13"},
|
||||
{file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:efedba7e13aa9a6c8407c48facfdfa108a5a4128e35f4c68f20c3407e4376aa9"},
|
||||
{file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:16863e2b132b761891d6c49f0a0f70030e0bcac4fd208117f6b7e053e68668d0"},
|
||||
{file = "SQLAlchemy-2.0.30-cp311-cp311-win32.whl", hash = "sha256:2ecabd9ccaa6e914e3dbb2aa46b76dede7eadc8cbf1b8083c94d936bcd5ffb49"},
|
||||
{file = "SQLAlchemy-2.0.30-cp311-cp311-win_amd64.whl", hash = "sha256:0b3f4c438e37d22b83e640f825ef0f37b95db9aa2d68203f2c9549375d0b2260"},
|
||||
{file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5a79d65395ac5e6b0c2890935bad892eabb911c4aa8e8015067ddb37eea3d56c"},
|
||||
{file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a5baf9267b752390252889f0c802ea13b52dfee5e369527da229189b8bd592e"},
|
||||
{file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cb5a646930c5123f8461f6468901573f334c2c63c795b9af350063a736d0134"},
|
||||
{file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:296230899df0b77dec4eb799bcea6fbe39a43707ce7bb166519c97b583cfcab3"},
|
||||
{file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c62d401223f468eb4da32627bffc0c78ed516b03bb8a34a58be54d618b74d472"},
|
||||
{file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3b69e934f0f2b677ec111b4d83f92dc1a3210a779f69bf905273192cf4ed433e"},
|
||||
{file = "SQLAlchemy-2.0.30-cp312-cp312-win32.whl", hash = "sha256:77d2edb1f54aff37e3318f611637171e8ec71472f1fdc7348b41dcb226f93d90"},
|
||||
{file = "SQLAlchemy-2.0.30-cp312-cp312-win_amd64.whl", hash = "sha256:b6c7ec2b1f4969fc19b65b7059ed00497e25f54069407a8701091beb69e591a5"},
|
||||
{file = "SQLAlchemy-2.0.30-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a8e3b0a7e09e94be7510d1661339d6b52daf202ed2f5b1f9f48ea34ee6f2d57"},
|
||||
{file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b60203c63e8f984df92035610c5fb76d941254cf5d19751faab7d33b21e5ddc0"},
|
||||
{file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1dc3eabd8c0232ee8387fbe03e0a62220a6f089e278b1f0aaf5e2d6210741ad"},
|
||||
{file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:40ad017c672c00b9b663fcfcd5f0864a0a97828e2ee7ab0c140dc84058d194cf"},
|
||||
{file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e42203d8d20dc704604862977b1470a122e4892791fe3ed165f041e4bf447a1b"},
|
||||
{file = "SQLAlchemy-2.0.30-cp37-cp37m-win32.whl", hash = "sha256:2a4f4da89c74435f2bc61878cd08f3646b699e7d2eba97144030d1be44e27584"},
|
||||
{file = "SQLAlchemy-2.0.30-cp37-cp37m-win_amd64.whl", hash = "sha256:b6bf767d14b77f6a18b6982cbbf29d71bede087edae495d11ab358280f304d8e"},
|
||||
{file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc0c53579650a891f9b83fa3cecd4e00218e071d0ba00c4890f5be0c34887ed3"},
|
||||
{file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:311710f9a2ee235f1403537b10c7687214bb1f2b9ebb52702c5aa4a77f0b3af7"},
|
||||
{file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:408f8b0e2c04677e9c93f40eef3ab22f550fecb3011b187f66a096395ff3d9fd"},
|
||||
{file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37a4b4fb0dd4d2669070fb05b8b8824afd0af57587393015baee1cf9890242d9"},
|
||||
{file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a943d297126c9230719c27fcbbeab57ecd5d15b0bd6bfd26e91bfcfe64220621"},
|
||||
{file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0a089e218654e740a41388893e090d2e2c22c29028c9d1353feb38638820bbeb"},
|
||||
{file = "SQLAlchemy-2.0.30-cp38-cp38-win32.whl", hash = "sha256:fa561138a64f949f3e889eb9ab8c58e1504ab351d6cf55259dc4c248eaa19da6"},
|
||||
{file = "SQLAlchemy-2.0.30-cp38-cp38-win_amd64.whl", hash = "sha256:7d74336c65705b986d12a7e337ba27ab2b9d819993851b140efdf029248e818e"},
|
||||
{file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae8c62fe2480dd61c532ccafdbce9b29dacc126fe8be0d9a927ca3e699b9491a"},
|
||||
{file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2383146973a15435e4717f94c7509982770e3e54974c71f76500a0136f22810b"},
|
||||
{file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8409de825f2c3b62ab15788635ccaec0c881c3f12a8af2b12ae4910a0a9aeef6"},
|
||||
{file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0094c5dc698a5f78d3d1539853e8ecec02516b62b8223c970c86d44e7a80f6c7"},
|
||||
{file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:edc16a50f5e1b7a06a2dcc1f2205b0b961074c123ed17ebda726f376a5ab0953"},
|
||||
{file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f7703c2010355dd28f53deb644a05fc30f796bd8598b43f0ba678878780b6e4c"},
|
||||
{file = "SQLAlchemy-2.0.30-cp39-cp39-win32.whl", hash = "sha256:1f9a727312ff6ad5248a4367358e2cf7e625e98b1028b1d7ab7b806b7d757513"},
|
||||
{file = "SQLAlchemy-2.0.30-cp39-cp39-win_amd64.whl", hash = "sha256:a0ef36b28534f2a5771191be6edb44cc2673c7b2edf6deac6562400288664221"},
|
||||
{file = "SQLAlchemy-2.0.30-py3-none-any.whl", hash = "sha256:7108d569d3990c71e26a42f60474b4c02c8586c4681af5fd67e51a044fdea86a"},
|
||||
{file = "SQLAlchemy-2.0.30.tar.gz", hash = "sha256:2b1708916730f4830bc69d6f49d37f7698b5bd7530aca7f04f785f8849e95255"},
|
||||
{file = "SQLAlchemy-2.0.31-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f2a213c1b699d3f5768a7272de720387ae0122f1becf0901ed6eaa1abd1baf6c"},
|
||||
{file = "SQLAlchemy-2.0.31-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9fea3d0884e82d1e33226935dac990b967bef21315cbcc894605db3441347443"},
|
||||
{file = "SQLAlchemy-2.0.31-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3ad7f221d8a69d32d197e5968d798217a4feebe30144986af71ada8c548e9fa"},
|
||||
{file = "SQLAlchemy-2.0.31-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2bee229715b6366f86a95d497c347c22ddffa2c7c96143b59a2aa5cc9eebbc"},
|
||||
{file = "SQLAlchemy-2.0.31-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cd5b94d4819c0c89280b7c6109c7b788a576084bf0a480ae17c227b0bc41e109"},
|
||||
{file = "SQLAlchemy-2.0.31-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:750900a471d39a7eeba57580b11983030517a1f512c2cb287d5ad0fcf3aebd58"},
|
||||
{file = "SQLAlchemy-2.0.31-cp310-cp310-win32.whl", hash = "sha256:7bd112be780928c7f493c1a192cd8c5fc2a2a7b52b790bc5a84203fb4381c6be"},
|
||||
{file = "SQLAlchemy-2.0.31-cp310-cp310-win_amd64.whl", hash = "sha256:5a48ac4d359f058474fadc2115f78a5cdac9988d4f99eae44917f36aa1476327"},
|
||||
{file = "SQLAlchemy-2.0.31-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f68470edd70c3ac3b6cd5c2a22a8daf18415203ca1b036aaeb9b0fb6f54e8298"},
|
||||
{file = "SQLAlchemy-2.0.31-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e2c38c2a4c5c634fe6c3c58a789712719fa1bf9b9d6ff5ebfce9a9e5b89c1ca"},
|
||||
{file = "SQLAlchemy-2.0.31-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd15026f77420eb2b324dcb93551ad9c5f22fab2c150c286ef1dc1160f110203"},
|
||||
{file = "SQLAlchemy-2.0.31-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2196208432deebdfe3b22185d46b08f00ac9d7b01284e168c212919891289396"},
|
||||
{file = "SQLAlchemy-2.0.31-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:352b2770097f41bff6029b280c0e03b217c2dcaddc40726f8f53ed58d8a85da4"},
|
||||
{file = "SQLAlchemy-2.0.31-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:56d51ae825d20d604583f82c9527d285e9e6d14f9a5516463d9705dab20c3740"},
|
||||
{file = "SQLAlchemy-2.0.31-cp311-cp311-win32.whl", hash = "sha256:6e2622844551945db81c26a02f27d94145b561f9d4b0c39ce7bfd2fda5776dac"},
|
||||
{file = "SQLAlchemy-2.0.31-cp311-cp311-win_amd64.whl", hash = "sha256:ccaf1b0c90435b6e430f5dd30a5aede4764942a695552eb3a4ab74ed63c5b8d3"},
|
||||
{file = "SQLAlchemy-2.0.31-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3b74570d99126992d4b0f91fb87c586a574a5872651185de8297c6f90055ae42"},
|
||||
{file = "SQLAlchemy-2.0.31-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f77c4f042ad493cb8595e2f503c7a4fe44cd7bd59c7582fd6d78d7e7b8ec52c"},
|
||||
{file = "SQLAlchemy-2.0.31-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd1591329333daf94467e699e11015d9c944f44c94d2091f4ac493ced0119449"},
|
||||
{file = "SQLAlchemy-2.0.31-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74afabeeff415e35525bf7a4ecdab015f00e06456166a2eba7590e49f8db940e"},
|
||||
{file = "SQLAlchemy-2.0.31-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b9c01990d9015df2c6f818aa8f4297d42ee71c9502026bb074e713d496e26b67"},
|
||||
{file = "SQLAlchemy-2.0.31-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:66f63278db425838b3c2b1c596654b31939427016ba030e951b292e32b99553e"},
|
||||
{file = "SQLAlchemy-2.0.31-cp312-cp312-win32.whl", hash = "sha256:0b0f658414ee4e4b8cbcd4a9bb0fd743c5eeb81fc858ca517217a8013d282c96"},
|
||||
{file = "SQLAlchemy-2.0.31-cp312-cp312-win_amd64.whl", hash = "sha256:fa4b1af3e619b5b0b435e333f3967612db06351217c58bfb50cee5f003db2a5a"},
|
||||
{file = "SQLAlchemy-2.0.31-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f43e93057cf52a227eda401251c72b6fbe4756f35fa6bfebb5d73b86881e59b0"},
|
||||
{file = "SQLAlchemy-2.0.31-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d337bf94052856d1b330d5fcad44582a30c532a2463776e1651bd3294ee7e58b"},
|
||||
{file = "SQLAlchemy-2.0.31-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c06fb43a51ccdff3b4006aafee9fcf15f63f23c580675f7734245ceb6b6a9e05"},
|
||||
{file = "SQLAlchemy-2.0.31-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:b6e22630e89f0e8c12332b2b4c282cb01cf4da0d26795b7eae16702a608e7ca1"},
|
||||
{file = "SQLAlchemy-2.0.31-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:79a40771363c5e9f3a77f0e28b3302801db08040928146e6808b5b7a40749c88"},
|
||||
{file = "SQLAlchemy-2.0.31-cp37-cp37m-win32.whl", hash = "sha256:501ff052229cb79dd4c49c402f6cb03b5a40ae4771efc8bb2bfac9f6c3d3508f"},
|
||||
{file = "SQLAlchemy-2.0.31-cp37-cp37m-win_amd64.whl", hash = "sha256:597fec37c382a5442ffd471f66ce12d07d91b281fd474289356b1a0041bdf31d"},
|
||||
{file = "SQLAlchemy-2.0.31-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dc6d69f8829712a4fd799d2ac8d79bdeff651c2301b081fd5d3fe697bd5b4ab9"},
|
||||
{file = "SQLAlchemy-2.0.31-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:23b9fbb2f5dd9e630db70fbe47d963c7779e9c81830869bd7d137c2dc1ad05fb"},
|
||||
{file = "SQLAlchemy-2.0.31-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a21c97efcbb9f255d5c12a96ae14da873233597dfd00a3a0c4ce5b3e5e79704"},
|
||||
{file = "SQLAlchemy-2.0.31-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26a6a9837589c42b16693cf7bf836f5d42218f44d198f9343dd71d3164ceeeac"},
|
||||
{file = "SQLAlchemy-2.0.31-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc251477eae03c20fae8db9c1c23ea2ebc47331bcd73927cdcaecd02af98d3c3"},
|
||||
{file = "SQLAlchemy-2.0.31-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2fd17e3bb8058359fa61248c52c7b09a97cf3c820e54207a50af529876451808"},
|
||||
{file = "SQLAlchemy-2.0.31-cp38-cp38-win32.whl", hash = "sha256:c76c81c52e1e08f12f4b6a07af2b96b9b15ea67ccdd40ae17019f1c373faa227"},
|
||||
{file = "SQLAlchemy-2.0.31-cp38-cp38-win_amd64.whl", hash = "sha256:4b600e9a212ed59355813becbcf282cfda5c93678e15c25a0ef896b354423238"},
|
||||
{file = "SQLAlchemy-2.0.31-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b6cf796d9fcc9b37011d3f9936189b3c8074a02a4ed0c0fbbc126772c31a6d4"},
|
||||
{file = "SQLAlchemy-2.0.31-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:78fe11dbe37d92667c2c6e74379f75746dc947ee505555a0197cfba9a6d4f1a4"},
|
||||
{file = "SQLAlchemy-2.0.31-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fc47dc6185a83c8100b37acda27658fe4dbd33b7d5e7324111f6521008ab4fe"},
|
||||
{file = "SQLAlchemy-2.0.31-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a41514c1a779e2aa9a19f67aaadeb5cbddf0b2b508843fcd7bafdf4c6864005"},
|
||||
{file = "SQLAlchemy-2.0.31-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:afb6dde6c11ea4525318e279cd93c8734b795ac8bb5dda0eedd9ebaca7fa23f1"},
|
||||
{file = "SQLAlchemy-2.0.31-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3f9faef422cfbb8fd53716cd14ba95e2ef655400235c3dfad1b5f467ba179c8c"},
|
||||
{file = "SQLAlchemy-2.0.31-cp39-cp39-win32.whl", hash = "sha256:fc6b14e8602f59c6ba893980bea96571dd0ed83d8ebb9c4479d9ed5425d562e9"},
|
||||
{file = "SQLAlchemy-2.0.31-cp39-cp39-win_amd64.whl", hash = "sha256:3cb8a66b167b033ec72c3812ffc8441d4e9f5f78f5e31e54dcd4c90a4ca5bebc"},
|
||||
{file = "SQLAlchemy-2.0.31-py3-none-any.whl", hash = "sha256:69f3e3c08867a8e4856e92d7afb618b95cdee18e0bc1647b77599722c9a28911"},
|
||||
{file = "SQLAlchemy-2.0.31.tar.gz", hash = "sha256:b607489dd4a54de56984a0c7656247504bd5523d9d0ba799aef59d4add009484"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""}
|
||||
greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"}
|
||||
typing-extensions = ">=4.6.0"
|
||||
|
||||
[package.extras]
|
||||
|
|
@ -10545,4 +10626,4 @@ local = ["ctransformers", "llama-cpp-python", "sentence-transformers"]
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.10,<3.13"
|
||||
content-hash = "6cfa9f164710bf283b50a8e12e9d0c91c58f5d0176bbb277b5f5d3630ec8b2cb"
|
||||
content-hash = "332baeb07342a5ad1367ce6b13a87bd96504f628d2e91e33dfda1a8317b2de13"
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ assemblyai = "^0.26.0"
|
|||
litellm = "^1.38.0"
|
||||
chromadb = "^0.5.0"
|
||||
langchain-anthropic = "^0.1.6"
|
||||
langchain-astradb = "^0.3.0"
|
||||
langchain-astradb = "^0.3.3"
|
||||
langchain-openai = "^0.1.1"
|
||||
zep-python = { version = "^2.0.0rc5", allow-prereleases = true }
|
||||
langchain-google-vertexai = "^1.0.3"
|
||||
|
|
@ -86,8 +86,11 @@ youtube-transcript-api = "^0.6.2"
|
|||
markdown = "^3.6"
|
||||
langchain-chroma = "^0.1.1"
|
||||
upstash-vector = "^0.4.0"
|
||||
gitpython = "^3.1.43"
|
||||
cassio = { extras = ["cassio"], version = "^0.1.7", optional = true }
|
||||
unstructured = {extras = ["docx", "md", "pptx"], version = "^0.14.4"}
|
||||
langchain-aws = "^0.1.6"
|
||||
langchain-mongodb = "^0.1.6"
|
||||
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
|
|
@ -130,17 +133,16 @@ local = ["llama-cpp-python", "sentence-transformers", "ctransformers"]
|
|||
optional = true
|
||||
|
||||
[tool.poetry.group.spelling.dependencies]
|
||||
codespell = "^2.2.6"
|
||||
codespell = "^2.3.0"
|
||||
|
||||
[tool.codespell]
|
||||
skip = '.git,*.pdf,*.svg,*.pdf,*.yaml,*.ipynb,poetry.lock,*.min.js,*.css,package-lock.json,*.trig'
|
||||
skip = '.git,*.pdf,*.svg,*.pdf,*.yaml,*.ipynb,poetry.lock,*.min.js,*.css,package-lock.json,*.trig.,**/node_modules/**,./stuff/*,*.csv'
|
||||
# Ignore latin etc
|
||||
ignore-regex = '.*(Stati Uniti|Tense=Pres).*'
|
||||
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
minversion = "6.0"
|
||||
addopts = "-ra -n auto"
|
||||
testpaths = ["tests", "integration"]
|
||||
console_output_style = "progress"
|
||||
filterwarnings = ["ignore::DeprecationWarning"]
|
||||
|
|
|
|||
|
|
@ -129,9 +129,6 @@ def update_template_field(frontend_template, key, value_dict):
|
|||
template_field["value"] = ""
|
||||
template_field["file_path"] = file_path_value
|
||||
|
||||
if "load_from_db" in value_dict and value_dict["load_from_db"]:
|
||||
template_field["load_from_db"] = value_dict["load_from_db"]
|
||||
|
||||
|
||||
def get_file_path_value(file_path):
|
||||
"""Get the file path value if the file exists, else return empty string."""
|
||||
|
|
@ -208,7 +205,7 @@ def format_elapsed_time(elapsed_time: float) -> str:
|
|||
return f"{minutes} {minutes_unit}, {seconds} {seconds_unit}"
|
||||
|
||||
|
||||
async def build_and_cache_graph_from_db(flow_id: str, session: Session, chat_service: "ChatService"):
|
||||
async def build_graph_from_db(flow_id: str, session: Session, chat_service: "ChatService"):
|
||||
"""Build and cache the graph."""
|
||||
flow: Optional[Flow] = session.get(Flow, flow_id)
|
||||
if not flow or not flow.data:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import time
|
||||
import traceback
|
||||
import uuid
|
||||
from functools import partial
|
||||
from typing import TYPE_CHECKING, Annotated, Optional
|
||||
|
|
@ -9,7 +10,7 @@ from loguru import logger
|
|||
|
||||
from langflow.api.utils import (
|
||||
build_and_cache_graph_from_data,
|
||||
build_and_cache_graph_from_db,
|
||||
build_graph_from_db,
|
||||
format_elapsed_time,
|
||||
format_exception_message,
|
||||
get_top_level_vertices,
|
||||
|
|
@ -23,6 +24,7 @@ from langflow.api.v1.schemas import (
|
|||
VertexBuildResponse,
|
||||
VerticesOrderResponse,
|
||||
)
|
||||
from langflow.exceptions.component import ComponentBuildException
|
||||
from langflow.schema.schema import Log
|
||||
from langflow.services.auth.utils import get_current_active_user
|
||||
from langflow.services.chat.service import ChatService
|
||||
|
|
@ -82,7 +84,7 @@ async def retrieve_vertices_order(
|
|||
flow_id_str = str(flow_id)
|
||||
# First, we need to check if the flow_id is in the cache
|
||||
if not data:
|
||||
graph = await build_and_cache_graph_from_db(flow_id=flow_id_str, session=session, chat_service=chat_service)
|
||||
graph = await build_graph_from_db(flow_id=flow_id_str, session=session, chat_service=chat_service)
|
||||
else:
|
||||
graph = await build_and_cache_graph_from_data(
|
||||
flow_id=flow_id_str, graph_data=data.model_dump(), chat_service=chat_service
|
||||
|
|
@ -109,6 +111,7 @@ async def retrieve_vertices_order(
|
|||
run_id = uuid.uuid4()
|
||||
graph.set_run_id(run_id)
|
||||
vertices_to_run = list(graph.vertices_to_run) + get_top_level_vertices(graph, graph.vertices_to_run)
|
||||
await chat_service.set_cache(str(flow_id), graph)
|
||||
return VerticesOrderResponse(ids=first_layer, run_id=run_id, vertices_to_run=vertices_to_run)
|
||||
|
||||
except Exception as exc:
|
||||
|
|
@ -156,7 +159,7 @@ async def build_vertex(
|
|||
if not cache:
|
||||
# If there's no cache
|
||||
logger.warning(f"No cache found for {flow_id_str}. Building graph starting at {vertex_id}")
|
||||
graph = await build_and_cache_graph_from_db(
|
||||
graph = await build_graph_from_db(
|
||||
flow_id=flow_id_str, session=next(get_session()), chat_service=chat_service
|
||||
)
|
||||
else:
|
||||
|
|
@ -183,22 +186,29 @@ async def build_vertex(
|
|||
lock, set_cache_coro, graph=graph, vertex=vertex, cache=False
|
||||
)
|
||||
top_level_vertices = graph.run_manager.get_top_level_vertices(graph, next_runnable_vertices)
|
||||
log_obj = Log(message=vertex.artifacts_raw, type=vertex.artifacts_type)
|
||||
|
||||
result_data_response = ResultDataResponse(**result_dict.model_dump())
|
||||
|
||||
result_data_response = ResultDataResponse.model_validate(result_dict, from_attributes=True)
|
||||
except Exception as exc:
|
||||
logger.exception(f"Error building Component: {exc}")
|
||||
params = format_exception_message(exc)
|
||||
if isinstance(exc, ComponentBuildException):
|
||||
params = exc.message
|
||||
tb = exc.formatted_traceback
|
||||
else:
|
||||
tb = traceback.format_exc()
|
||||
logger.exception(f"Error building Component: {exc}")
|
||||
params = format_exception_message(exc)
|
||||
message = {"errorMessage": params, "stackTrace": tb}
|
||||
valid = False
|
||||
log_obj = Log(message=params, type="error")
|
||||
result_data_response = ResultDataResponse(results={})
|
||||
output_label = vertex.outputs[0]["name"] if vertex.outputs else "output"
|
||||
logs = {output_label: Log(message=message, type="error")}
|
||||
result_data_response = ResultDataResponse(results={}, logs=logs)
|
||||
artifacts = {}
|
||||
# If there's an error building the vertex
|
||||
# we need to clear the cache
|
||||
await chat_service.clear_cache(flow_id_str)
|
||||
|
||||
result_data_response.message = artifacts
|
||||
result_data_response.logs.append(log_obj)
|
||||
|
||||
# Log the vertex build
|
||||
if not vertex.will_stream:
|
||||
|
|
@ -220,6 +230,7 @@ async def build_vertex(
|
|||
inactivated_vertices = list(graph.inactivated_vertices)
|
||||
graph.reset_inactivated_vertices()
|
||||
graph.reset_activated_vertices()
|
||||
|
||||
await chat_service.set_cache(flow_id_str, graph)
|
||||
|
||||
# graph.stop_vertex tells us if the user asked
|
||||
|
|
@ -240,7 +251,7 @@ async def build_vertex(
|
|||
)
|
||||
return build_response
|
||||
except Exception as exc:
|
||||
logger.error(f"Error building Component: {exc}")
|
||||
logger.error(f"Error building Component:\n\n{exc}")
|
||||
logger.exception(exc)
|
||||
message = parse_exception(exc)
|
||||
raise HTTPException(status_code=500, detail=message) from exc
|
||||
|
|
@ -284,18 +295,12 @@ async def build_vertex_stream(
|
|||
|
||||
async def stream_vertex():
|
||||
try:
|
||||
if not session_id:
|
||||
cache = await chat_service.get_cache(flow_id_str)
|
||||
if not cache:
|
||||
# If there's no cache
|
||||
raise ValueError(f"No cache found for {flow_id_str}.")
|
||||
else:
|
||||
graph = cache.get("result")
|
||||
cache = await chat_service.get_cache(flow_id_str)
|
||||
if not cache:
|
||||
# If there's no cache
|
||||
raise ValueError(f"No cache found for {flow_id_str}.")
|
||||
else:
|
||||
session_data = await session_service.load_session(session_id, flow_id=flow_id_str)
|
||||
graph, artifacts = session_data if session_data else (None, None)
|
||||
if not graph:
|
||||
raise ValueError(f"No graph found for {flow_id_str}.")
|
||||
graph = cache.get("result")
|
||||
|
||||
vertex: "InterfaceVertex" = graph.get_vertex(vertex_id)
|
||||
if not hasattr(vertex, "stream"):
|
||||
|
|
@ -342,6 +347,7 @@ async def build_vertex_stream(
|
|||
yield str(StreamData(event="error", data={"error": exc_message}))
|
||||
finally:
|
||||
logger.debug("Closing stream")
|
||||
await chat_service.set_cache(flow_id_str, graph)
|
||||
yield str(StreamData(event="close", data={"message": "Stream closed"}))
|
||||
|
||||
return StreamingResponse(stream_vertex(), media_type="text/event-stream")
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
from asyncio import Lock
|
||||
from http import HTTPStatus
|
||||
from typing import TYPE_CHECKING, Annotated, List, Optional, Union
|
||||
from uuid import UUID
|
||||
|
||||
import sqlalchemy as sa
|
||||
from fastapi import APIRouter, BackgroundTasks, Body, Depends, HTTPException, Request, UploadFile, status
|
||||
from loguru import logger
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from langflow.api.utils import update_frontend_node_with_template_values
|
||||
from langflow.api.v1.schemas import (
|
||||
ConfigResponse,
|
||||
|
|
@ -16,7 +20,7 @@ from langflow.api.v1.schemas import (
|
|||
UpdateCustomComponentRequest,
|
||||
UploadFileResponse,
|
||||
)
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.custom.custom_component.component import Component
|
||||
from langflow.custom.utils import build_custom_component_template
|
||||
from langflow.graph.graph.base import Graph
|
||||
from langflow.graph.schema import RunOutputs
|
||||
|
|
@ -26,30 +30,40 @@ from langflow.schema.graph import Tweaks
|
|||
from langflow.services.auth.utils import api_key_security, get_current_active_user
|
||||
from langflow.services.cache.utils import save_uploaded_file
|
||||
from langflow.services.database.models.flow import Flow
|
||||
from langflow.services.database.models.flow.utils import get_all_webhook_components_in_flow, get_flow_by_id
|
||||
from langflow.services.database.models.flow.utils import get_all_webhook_components_in_flow
|
||||
from langflow.services.database.models.user.model import User
|
||||
from langflow.services.deps import get_session, get_session_service, get_settings_service, get_task_service
|
||||
from langflow.services.deps import (
|
||||
get_cache_service,
|
||||
get_session,
|
||||
get_session_service,
|
||||
get_settings_service,
|
||||
get_task_service,
|
||||
)
|
||||
from langflow.services.session.service import SessionService
|
||||
from langflow.services.task.service import TaskService
|
||||
from loguru import logger
|
||||
from sqlmodel import Session, select
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.services.settings.manager import SettingsService
|
||||
from langflow.services.cache.base import CacheService
|
||||
from langflow.services.settings.service import SettingsService
|
||||
|
||||
router = APIRouter(tags=["Base"])
|
||||
|
||||
|
||||
@router.get("/all", dependencies=[Depends(get_current_active_user)])
|
||||
def get_all(
|
||||
async def get_all(
|
||||
settings_service=Depends(get_settings_service),
|
||||
cache_service: "CacheService" = Depends(dependency=get_cache_service),
|
||||
force_refresh: bool = False,
|
||||
):
|
||||
from langflow.interface.types import get_all_types_dict
|
||||
from langflow.interface.types import get_and_cache_all_types_dict
|
||||
|
||||
logger.debug("Building langchain types dict")
|
||||
try:
|
||||
all_types_dict = get_all_types_dict(settings_service.settings.components_path)
|
||||
return all_types_dict
|
||||
async with Lock() as lock:
|
||||
all_types_dict = await get_and_cache_all_types_dict(
|
||||
settings_service=settings_service, cache_service=cache_service, force_refresh=force_refresh, lock=lock
|
||||
)
|
||||
|
||||
return all_types_dict
|
||||
except Exception as exc:
|
||||
logger.exception(exc)
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
|
|
@ -106,12 +120,10 @@ async def simple_run_flow(
|
|||
|
||||
@router.post("/run/{flow_id_or_name}", response_model=RunResponse, response_model_exclude_none=True)
|
||||
async def simplified_run_flow(
|
||||
db: Annotated[Session, Depends(get_session)],
|
||||
flow: Annotated[Flow, Depends(get_flow_by_id_or_endpoint_name)],
|
||||
input_request: SimplifiedAPIRequest = SimplifiedAPIRequest(),
|
||||
stream: bool = False,
|
||||
api_key_user: User = Depends(api_key_security),
|
||||
session_service: SessionService = Depends(get_session_service),
|
||||
):
|
||||
"""
|
||||
Executes a specified flow by ID with input customization, performance enhancements through caching, and optional data streaming.
|
||||
|
|
@ -183,10 +195,10 @@ async def simplified_run_flow(
|
|||
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@router.post("/webhook/{flow_id}", response_model=dict, status_code=HTTPStatus.ACCEPTED)
|
||||
@router.post("/webhook/{flow_id_or_name}", response_model=dict, status_code=HTTPStatus.ACCEPTED)
|
||||
async def webhook_run_flow(
|
||||
db: Annotated[Session, Depends(get_session)],
|
||||
flow: Annotated[Flow, Depends(get_flow_by_id)],
|
||||
flow: Annotated[Flow, Depends(get_flow_by_id_or_endpoint_name)],
|
||||
request: Request,
|
||||
background_tasks: BackgroundTasks,
|
||||
session_service: SessionService = Depends(get_session_service),
|
||||
|
|
@ -232,7 +244,6 @@ async def webhook_run_flow(
|
|||
logger.debug("Starting background task")
|
||||
background_tasks.add_task( # type: ignore
|
||||
simple_run_flow,
|
||||
db=db,
|
||||
flow=flow,
|
||||
input_request=input_request,
|
||||
)
|
||||
|
|
@ -453,7 +464,7 @@ async def custom_component(
|
|||
raw_code: CustomComponentRequest,
|
||||
user: User = Depends(get_current_active_user),
|
||||
):
|
||||
component = CustomComponent(code=raw_code.code)
|
||||
component = Component(code=raw_code.code)
|
||||
|
||||
built_frontend_node, _ = build_custom_component_template(component, user_id=user.id)
|
||||
|
||||
|
|
@ -481,7 +492,7 @@ async def custom_component_update(
|
|||
|
||||
"""
|
||||
try:
|
||||
component = CustomComponent(code=code_request.code)
|
||||
component = Component(code=code_request.code)
|
||||
|
||||
component_node, cc_instance = build_custom_component_template(
|
||||
component,
|
||||
|
|
|
|||
|
|
@ -50,6 +50,26 @@ def create_flow(
|
|||
flow.name = f"{flow.name} ({max(numbers) + 1})"
|
||||
else:
|
||||
flow.name = f"{flow.name} (1)"
|
||||
# Now check if the endpoint is unique
|
||||
if (
|
||||
flow.endpoint_name
|
||||
and session.exec(
|
||||
select(Flow).where(Flow.endpoint_name == flow.endpoint_name).where(Flow.user_id == current_user.id)
|
||||
).first()
|
||||
):
|
||||
flows = session.exec(
|
||||
select(Flow)
|
||||
.where(Flow.endpoint_name.like(f"{flow.endpoint_name}-%")) # type: ignore
|
||||
.where(Flow.user_id == current_user.id)
|
||||
).all()
|
||||
if flows:
|
||||
# The endpoitn name is like "my-endpoint","my-endpoint-1", "my-endpoint-2"
|
||||
# so we need to get the highest number and add 1
|
||||
# we need to get the last part of the endpoint name
|
||||
numbers = [int(flow.endpoint_name.split("-")[-1]) for flow in flows] # type: ignore
|
||||
flow.endpoint_name = f"{flow.endpoint_name}-{max(numbers) + 1}"
|
||||
else:
|
||||
flow.endpoint_name = f"{flow.endpoint_name}-1"
|
||||
|
||||
db_flow = Flow.model_validate(flow, from_attributes=True)
|
||||
db_flow.updated_at = datetime.now(timezone.utc)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ from langflow.services.auth.utils import (
|
|||
)
|
||||
from langflow.services.database.models.folder.utils import create_default_folder_if_it_doesnt_exist
|
||||
from langflow.services.deps import get_session, get_settings_service, get_variable_service
|
||||
from langflow.services.settings.manager import SettingsService
|
||||
from langflow.services.settings.service import SettingsService
|
||||
from langflow.services.variable.service import VariableService
|
||||
|
||||
router = APIRouter(tags=["Login"])
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from typing import List, Optional
|
||||
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
|
||||
from langflow.services.deps import get_monitor_service
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ class VerticesOrderResponse(BaseModel):
|
|||
|
||||
class ResultDataResponse(BaseModel):
|
||||
results: Optional[Any] = Field(default_factory=dict)
|
||||
logs: List[Log | None] = Field(default_factory=list)
|
||||
logs: dict[str, Log] = Field(default_factory=dict)
|
||||
message: Optional[Any] = Field(default_factory=dict)
|
||||
artifacts: Optional[Any] = Field(default_factory=dict)
|
||||
timedelta: Optional[float] = None
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ from langchain.agents import AgentExecutor, BaseMultiActionAgent, BaseSingleActi
|
|||
from langchain_core.messages import BaseMessage
|
||||
from langchain_core.runnables import Runnable
|
||||
|
||||
from langflow.base.agents.utils import get_agents_list, records_to_messages
|
||||
from langflow.base.agents.utils import data_to_messages, get_agents_list
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Text, Tool
|
||||
from langflow.schema import Record
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class LCAgentComponent(CustomComponent):
|
||||
|
|
@ -49,7 +49,7 @@ class LCAgentComponent(CustomComponent):
|
|||
agent: Union[Runnable, BaseSingleActionAgent, BaseMultiActionAgent, AgentExecutor],
|
||||
inputs: str,
|
||||
tools: List[Tool],
|
||||
message_history: Optional[List[Record]] = None,
|
||||
message_history: Optional[List[Data]] = None,
|
||||
handle_parsing_errors: bool = True,
|
||||
output_key: str = "output",
|
||||
) -> Text:
|
||||
|
|
@ -64,7 +64,7 @@ class LCAgentComponent(CustomComponent):
|
|||
)
|
||||
input_dict: dict[str, str | list[BaseMessage]] = {"input": inputs}
|
||||
if message_history:
|
||||
input_dict["chat_history"] = records_to_messages(message_history)
|
||||
input_dict["chat_history"] = data_to_messages(message_history)
|
||||
result = await runnable.ainvoke(input_dict)
|
||||
self.status = result
|
||||
if output_key in result:
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ from langchain_core.prompts import BasePromptTemplate, ChatPromptTemplate
|
|||
from langchain_core.tools import BaseTool
|
||||
from pydantic import BaseModel
|
||||
|
||||
from langflow.schema import Record
|
||||
from langflow.schema import Data
|
||||
|
||||
from .default_prompts import XML_AGENT_PROMPT
|
||||
|
||||
|
|
@ -34,17 +34,17 @@ class AgentSpec(BaseModel):
|
|||
hub_repo: Optional[str] = None
|
||||
|
||||
|
||||
def records_to_messages(records: List[Record]) -> List[BaseMessage]:
|
||||
def data_to_messages(data: List[Data]) -> List[BaseMessage]:
|
||||
"""
|
||||
Convert a list of records to a list of messages.
|
||||
Convert a list of data to a list of messages.
|
||||
|
||||
Args:
|
||||
records (List[Record]): The records to convert.
|
||||
data (List[Data]): The data to convert.
|
||||
|
||||
Returns:
|
||||
List[Message]: The records as messages.
|
||||
List[Message]: The data as messages.
|
||||
"""
|
||||
return [record.to_lc_message() for record in records]
|
||||
return [value.to_lc_message() for value in data]
|
||||
|
||||
|
||||
def validate_and_create_xml_agent(
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import orjson
|
|||
|
||||
STREAM_INFO_TEXT = "Stream the response from the model. Streaming works only in Chat."
|
||||
|
||||
NODE_FORMAT_ATTRIBUTES = ["beta", "icon", "display_name", "description", "output_types"]
|
||||
NODE_FORMAT_ATTRIBUTES = ["beta", "icon", "display_name", "description", "output_types", "edited"]
|
||||
|
||||
|
||||
FIELD_FORMAT_ATTRIBUTES = [
|
||||
|
|
@ -28,6 +28,8 @@ FIELD_FORMAT_ATTRIBUTES = [
|
|||
"refresh_button",
|
||||
"refresh_button_text",
|
||||
"options",
|
||||
"advanced",
|
||||
"load_from_db",
|
||||
]
|
||||
|
||||
ORJSON_OPTIONS = orjson.OPT_INDENT_2 | orjson.OPT_SORT_KEYS | orjson.OPT_OMIT_MICROSECONDS
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ def normalize_newlines(multiline_text):
|
|||
def parse_curl_command(curl_command):
|
||||
tokens = shlex.split(normalize_newlines(curl_command))
|
||||
tokens = [token for token in tokens if token and token != " "]
|
||||
if "curl" not in tokens[0]:
|
||||
if tokens and "curl" not in tokens[0]:
|
||||
raise ValueError("Invalid curl command")
|
||||
args_template = {
|
||||
"command": None,
|
||||
|
|
@ -112,6 +112,10 @@ def parse_curl_command(curl_command):
|
|||
|
||||
def parse_context(curl_command):
|
||||
method = "get"
|
||||
if not curl_command:
|
||||
return ParsedContext(
|
||||
method=method, url="", data=None, headers={}, cookies={}, verify=True, auth=None, proxy=None
|
||||
)
|
||||
|
||||
parsed_args: ParsedArgs = parse_curl_command(curl_command)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import chardet
|
|||
import orjson
|
||||
import yaml
|
||||
|
||||
from langflow.schema import Record
|
||||
from langflow.schema import Data
|
||||
|
||||
# Types of files that can be read simply by file.read()
|
||||
# and have 100% to be completely readable
|
||||
|
|
@ -33,12 +33,7 @@ TEXT_FILE_TYPES = [
|
|||
"tsx",
|
||||
]
|
||||
|
||||
IMG_FILE_TYPES = [
|
||||
"jpg",
|
||||
"jpeg",
|
||||
"png",
|
||||
"bmp",
|
||||
]
|
||||
IMG_FILE_TYPES = ["jpg", "jpeg", "png", "bmp", "image"]
|
||||
|
||||
|
||||
def normalize_text(text):
|
||||
|
|
@ -82,7 +77,7 @@ def retrieve_file_paths(
|
|||
|
||||
# ! Removing unstructured dependency until
|
||||
# ! 3.12 is supported
|
||||
# def partition_file_to_record(file_path: str, silent_errors: bool) -> Optional[Record]:
|
||||
# def partition_file_to_data(file_path: str, silent_errors: bool) -> Optional[Data]:
|
||||
# # Use the partition function to load the file
|
||||
# from unstructured.partition.auto import partition # type: ignore
|
||||
|
||||
|
|
@ -93,11 +88,11 @@ def retrieve_file_paths(
|
|||
# raise ValueError(f"Error loading file {file_path}: {e}") from e
|
||||
# return None
|
||||
|
||||
# # Create a Record
|
||||
# # Create a Data
|
||||
# text = "\n\n".join([Text(el) for el in elements])
|
||||
# metadata = elements.metadata if hasattr(elements, "metadata") else {}
|
||||
# metadata["file_path"] = file_path
|
||||
# record = Record(text=text, data=metadata)
|
||||
# record = Data(text=text, data=metadata)
|
||||
# return record
|
||||
|
||||
|
||||
|
|
@ -129,7 +124,7 @@ def parse_pdf_to_text(file_path: str) -> str:
|
|||
return "\n\n".join([page.extract_text() for page in reader.pages])
|
||||
|
||||
|
||||
def parse_text_file_to_record(file_path: str, silent_errors: bool) -> Optional[Record]:
|
||||
def parse_text_file_to_data(file_path: str, silent_errors: bool) -> Optional[Data]:
|
||||
try:
|
||||
if file_path.endswith(".pdf"):
|
||||
text = parse_pdf_to_text(file_path)
|
||||
|
|
@ -145,6 +140,7 @@ def parse_text_file_to_record(file_path: str, silent_errors: bool) -> Optional[R
|
|||
text = {k: normalize_text(v) if isinstance(v, str) else v for k, v in text.items()}
|
||||
elif isinstance(text, list):
|
||||
text = [normalize_text(item) if isinstance(item, str) else item for item in text]
|
||||
text = orjson.dumps(text).decode("utf-8")
|
||||
|
||||
elif file_path.endswith(".yaml") or file_path.endswith(".yml"):
|
||||
text = yaml.safe_load(text)
|
||||
|
|
@ -156,7 +152,7 @@ def parse_text_file_to_record(file_path: str, silent_errors: bool) -> Optional[R
|
|||
raise ValueError(f"Error loading file {file_path}: {e}") from e
|
||||
return None
|
||||
|
||||
record = Record(data={"file_path": file_path, "text": text})
|
||||
record = Data(data={"file_path": file_path, "text": text})
|
||||
return record
|
||||
|
||||
|
||||
|
|
@ -167,21 +163,21 @@ def parse_text_file_to_record(file_path: str, silent_errors: bool) -> Optional[R
|
|||
# silent_errors: bool,
|
||||
# max_concurrency: int,
|
||||
# use_multithreading: bool,
|
||||
# ) -> List[Optional[Record]]:
|
||||
# ) -> List[Optional[Data]]:
|
||||
# if use_multithreading:
|
||||
# records = parallel_load_records(file_paths, silent_errors, max_concurrency)
|
||||
# data = parallel_load_data(file_paths, silent_errors, max_concurrency)
|
||||
# else:
|
||||
# records = [partition_file_to_record(file_path, silent_errors) for file_path in file_paths]
|
||||
# records = list(filter(None, records))
|
||||
# return records
|
||||
# data = [partition_file_to_data(file_path, silent_errors) for file_path in file_paths]
|
||||
# data = list(filter(None, data))
|
||||
# return data
|
||||
|
||||
|
||||
def parallel_load_records(
|
||||
def parallel_load_data(
|
||||
file_paths: List[str],
|
||||
silent_errors: bool,
|
||||
max_concurrency: int,
|
||||
load_function: Callable = parse_text_file_to_record,
|
||||
) -> List[Optional[Record]]:
|
||||
load_function: Callable = parse_text_file_to_data,
|
||||
) -> List[Optional[Data]]:
|
||||
with futures.ThreadPoolExecutor(max_workers=max_concurrency) as executor:
|
||||
loaded_files = executor.map(
|
||||
lambda file_path: load_function(file_path, silent_errors),
|
||||
|
|
|
|||
|
|
@ -3,62 +3,65 @@ from typing import List
|
|||
from loguru import logger
|
||||
|
||||
from langflow.graph.schema import ResultData, RunOutputs
|
||||
from langflow.schema import Record
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
def build_records_from_run_outputs(run_outputs: RunOutputs) -> List[Record]:
|
||||
def build_data_from_run_outputs(run_outputs: RunOutputs) -> List[Data]:
|
||||
"""
|
||||
Build a list of records from the given RunOutputs.
|
||||
Build a list of data from the given RunOutputs.
|
||||
|
||||
Args:
|
||||
run_outputs (RunOutputs): The RunOutputs object containing the output data.
|
||||
|
||||
Returns:
|
||||
List[Record]: A list of records built from the RunOutputs.
|
||||
List[Data]: A list of data built from the RunOutputs.
|
||||
|
||||
"""
|
||||
if not run_outputs:
|
||||
return []
|
||||
records = []
|
||||
data = []
|
||||
for result_data in run_outputs.outputs:
|
||||
if result_data:
|
||||
records.extend(build_records_from_result_data(result_data))
|
||||
return records
|
||||
data.extend(build_data_from_result_data(result_data))
|
||||
return data
|
||||
|
||||
|
||||
def build_records_from_result_data(result_data: ResultData, get_final_results_only: bool = True) -> List[Record]:
|
||||
def build_data_from_result_data(result_data: ResultData, get_final_results_only: bool = True) -> List[Data]:
|
||||
"""
|
||||
Build a list of records from the given ResultData.
|
||||
Build a list of data from the given ResultData.
|
||||
|
||||
Args:
|
||||
result_data (ResultData): The ResultData object containing the result data.
|
||||
get_final_results_only (bool, optional): Whether to include only final results. Defaults to True.
|
||||
|
||||
Returns:
|
||||
List[Record]: A list of records built from the ResultData.
|
||||
List[Data]: A list of data built from the ResultData.
|
||||
|
||||
"""
|
||||
messages = result_data.messages
|
||||
records = []
|
||||
|
||||
if not messages:
|
||||
return []
|
||||
data = []
|
||||
|
||||
# Handle results without chat messages (calling flow)
|
||||
if not messages:
|
||||
# Result with a single record
|
||||
if isinstance(result_data.artifacts, dict):
|
||||
records.append(Record(data=result_data.artifacts))
|
||||
data.append(Data(data=result_data.artifacts))
|
||||
# List of artifacts
|
||||
elif isinstance(result_data.artifacts, list):
|
||||
for artifact in result_data.artifacts:
|
||||
# If multiple records are found as artifacts, return as-is
|
||||
if isinstance(artifact, Record):
|
||||
records.append(artifact)
|
||||
if isinstance(artifact, Data):
|
||||
data.append(artifact)
|
||||
else:
|
||||
# Warn about unknown output type
|
||||
logger.warning(f"Unable to build record output from unknown ResultData.artifact: {str(artifact)}")
|
||||
# Chat or text output
|
||||
elif result_data.results:
|
||||
records.append(Record(data={"result": result_data.results}, text_key="result"))
|
||||
return records
|
||||
data.append(Data(data={"result": result_data.results}, text_key="result"))
|
||||
return data
|
||||
else:
|
||||
return []
|
||||
|
||||
|
|
@ -68,22 +71,22 @@ def build_records_from_result_data(result_data: ResultData, get_final_results_on
|
|||
result_data_dict = result_data.model_dump()
|
||||
results = result_data_dict.get("results", {})
|
||||
inner_result = results.get("result", {})
|
||||
record = Record(data={"result": inner_result, "message": message_dict}, text_key="result")
|
||||
records.append(record)
|
||||
return records
|
||||
record = Data(data={"result": inner_result, "message": message_dict}, text_key="result")
|
||||
data.append(record)
|
||||
return data
|
||||
|
||||
|
||||
def format_flow_output_records(records: List[Record]) -> str:
|
||||
def format_flow_output_data(data: List[Data]) -> str:
|
||||
"""
|
||||
Format the flow output records into a string.
|
||||
Format the flow output data into a string.
|
||||
|
||||
Args:
|
||||
records (List[Record]): The list of records to format.
|
||||
data (List[Data]): The list of data to format.
|
||||
|
||||
Returns:
|
||||
str: The formatted flow output records.
|
||||
str: The formatted flow output data.
|
||||
|
||||
"""
|
||||
result = "Flow run output:\n"
|
||||
results = "\n".join([record.result for record in records if record.data["message"]])
|
||||
results = "\n".join([value.result for value in data if value.data["message"]])
|
||||
return result + results
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
from typing import Optional, Union
|
||||
|
||||
from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.custom import Component
|
||||
from langflow.memory import store_message
|
||||
from langflow.schema import Record
|
||||
from langflow.schema import Data
|
||||
from langflow.schema.message import Message
|
||||
|
||||
|
||||
class ChatComponent(CustomComponent):
|
||||
class ChatComponent(Component):
|
||||
display_name = "Chat Component"
|
||||
description = "Use as base for chat components."
|
||||
|
||||
|
|
@ -34,10 +34,10 @@ class ChatComponent(CustomComponent):
|
|||
"info": "Return the message as a Message containing the sender, sender_name, and session_id.",
|
||||
"advanced": True,
|
||||
},
|
||||
"record_template": {
|
||||
"display_name": "Record Template",
|
||||
"data_template": {
|
||||
"display_name": "Data Template",
|
||||
"multiline": True,
|
||||
"info": "In case of Message being a Record, this template will be used to convert it to text.",
|
||||
"info": "In case of Message being a Data, this template will be used to convert it to text.",
|
||||
"advanced": True,
|
||||
},
|
||||
"files": {
|
||||
|
|
@ -61,20 +61,20 @@ class ChatComponent(CustomComponent):
|
|||
self.status = messages
|
||||
return messages
|
||||
|
||||
def build_with_record(
|
||||
def build_with_data(
|
||||
self,
|
||||
sender: Optional[str] = "User",
|
||||
sender_name: Optional[str] = "User",
|
||||
input_value: Optional[Union[str, Record, Message]] = None,
|
||||
input_value: Optional[Union[str, Data, Message]] = None,
|
||||
files: Optional[list[str]] = None,
|
||||
session_id: Optional[str] = None,
|
||||
return_message: Optional[bool] = False,
|
||||
) -> Message:
|
||||
message: Message | None = None
|
||||
|
||||
if isinstance(input_value, Record):
|
||||
if isinstance(input_value, Data):
|
||||
# Update the data of the record
|
||||
message = Message.from_record(input_value)
|
||||
message = Message.from_data(input_value)
|
||||
else:
|
||||
message = Message(
|
||||
text=input_value, sender=sender, sender_name=sender_name, files=files, session_id=session_id
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
from typing import Optional
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.custom import Component
|
||||
from langflow.field_typing import Text
|
||||
from langflow.helpers.record import records_to_text
|
||||
from langflow.schema import Record
|
||||
from langflow.helpers.data import data_to_text
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class TextComponent(CustomComponent):
|
||||
class TextComponent(Component):
|
||||
display_name = "Text Component"
|
||||
description = "Used to pass text to the next component."
|
||||
|
||||
|
|
@ -14,13 +14,13 @@ class TextComponent(CustomComponent):
|
|||
return {
|
||||
"input_value": {
|
||||
"display_name": "Value",
|
||||
"input_types": ["Text", "Record"],
|
||||
"info": "Text or Record to be passed.",
|
||||
"input_types": ["Text", "Data"],
|
||||
"info": "Text or Data to be passed.",
|
||||
},
|
||||
"record_template": {
|
||||
"display_name": "Record Template",
|
||||
"data_template": {
|
||||
"display_name": "Data Template",
|
||||
"multiline": True,
|
||||
"info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.",
|
||||
"info": "Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
|
@ -28,14 +28,16 @@ class TextComponent(CustomComponent):
|
|||
def build(
|
||||
self,
|
||||
input_value: Optional[Text] = "",
|
||||
record_template: Optional[str] = "{text}",
|
||||
data_template: Optional[str] = "{text}",
|
||||
) -> Text:
|
||||
if isinstance(input_value, Record):
|
||||
if record_template == "":
|
||||
# it should be dynamically set to the Record's .text_key value
|
||||
# meaning, if text_key = "bacon", then record_template = "{bacon}"
|
||||
record_template = "{" + input_value.text_key + "}"
|
||||
input_value = records_to_text(template=record_template, records=input_value)
|
||||
if isinstance(input_value, Data):
|
||||
if data_template == "":
|
||||
# it should be dynamically set to the Data's .text_key value
|
||||
# meaning, if text_key = "bacon", then data_template = "{bacon}"
|
||||
data_template = "{" + input_value.text_key + "}"
|
||||
input_value = data_to_text(template=data_template, data=input_value)
|
||||
elif not input_value:
|
||||
input_value = ""
|
||||
self.status = input_value
|
||||
if not input_value:
|
||||
input_value = ""
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from typing import Optional
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Record
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class BaseMemoryComponent(CustomComponent):
|
||||
|
|
@ -32,15 +32,15 @@ class BaseMemoryComponent(CustomComponent):
|
|||
"info": "Order of the messages.",
|
||||
"advanced": True,
|
||||
},
|
||||
"record_template": {
|
||||
"display_name": "Record Template",
|
||||
"data_template": {
|
||||
"display_name": "Data Template",
|
||||
"multiline": True,
|
||||
"info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.",
|
||||
"info": "Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def get_messages(self, **kwargs) -> list[Record]:
|
||||
def get_messages(self, **kwargs) -> list[Data]:
|
||||
raise NotImplementedError
|
||||
|
||||
def add_message(
|
||||
|
|
|
|||
|
|
@ -1,18 +1,47 @@
|
|||
import json
|
||||
import warnings
|
||||
from typing import Optional, Union
|
||||
|
||||
from langchain_core.language_models.chat_models import BaseChatModel
|
||||
from langchain_core.language_models.llms import LLM
|
||||
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
|
||||
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, SystemMessage
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing.prompt import Prompt
|
||||
from langflow.custom import Component
|
||||
from langflow.field_typing import LanguageModel
|
||||
from langflow.schema.message import Message
|
||||
from langflow.template.field.base import Output
|
||||
|
||||
|
||||
class LCModelComponent(CustomComponent):
|
||||
class LCModelComponent(Component):
|
||||
display_name: str = "Model Name"
|
||||
description: str = "Model Description"
|
||||
|
||||
outputs = [
|
||||
Output(display_name="Text", name="text_output", method="text_response"),
|
||||
Output(display_name="Language Model", name="model_output", method="build_model"),
|
||||
]
|
||||
|
||||
def _get_exception_message(self, e: Exception):
|
||||
return str(e)
|
||||
|
||||
def _validate_outputs(self):
|
||||
# At least these two outputs must be defined
|
||||
required_output_methods = ["text_response", "build_model"]
|
||||
output_names = [output.name for output in self.outputs]
|
||||
for method_name in required_output_methods:
|
||||
if method_name not in output_names:
|
||||
raise ValueError(f"Output with name '{method_name}' must be defined.")
|
||||
elif not hasattr(self, method_name):
|
||||
raise ValueError(f"Method '{method_name}' must be defined.")
|
||||
|
||||
def text_response(self) -> Message:
|
||||
input_value = self.input_value
|
||||
stream = self.stream
|
||||
system_message = self.system_message
|
||||
output = self.build_model()
|
||||
result = self.get_chat_result(output, stream, input_value, system_message)
|
||||
self.status = result
|
||||
return result
|
||||
|
||||
def get_result(self, runnable: LLM, stream: bool, input_value: str):
|
||||
"""
|
||||
Retrieves the result from the output of a Runnable object.
|
||||
|
|
@ -25,13 +54,18 @@ class LCModelComponent(CustomComponent):
|
|||
Returns:
|
||||
The result obtained from the output object.
|
||||
"""
|
||||
if stream:
|
||||
result = runnable.stream(input_value)
|
||||
else:
|
||||
message = runnable.invoke(input_value)
|
||||
result = message.content if hasattr(message, "content") else message
|
||||
self.status = result
|
||||
return result
|
||||
try:
|
||||
if stream:
|
||||
result = runnable.stream(input_value)
|
||||
else:
|
||||
message = runnable.invoke(input_value)
|
||||
result = message.content if hasattr(message, "content") else message
|
||||
self.status = result
|
||||
return result
|
||||
except Exception as e:
|
||||
if message := self._get_exception_message(e):
|
||||
raise ValueError(message) from e
|
||||
raise e
|
||||
|
||||
def build_status_message(self, message: AIMessage):
|
||||
"""
|
||||
|
|
@ -84,15 +118,15 @@ class LCModelComponent(CustomComponent):
|
|||
return status_message
|
||||
|
||||
def get_chat_result(
|
||||
self, runnable: BaseChatModel, stream: bool, input_value: str | Prompt, system_message: Optional[str] = None
|
||||
self, runnable: LanguageModel, stream: bool, input_value: str | Message, system_message: Optional[str] = None
|
||||
):
|
||||
messages: list[Union[HumanMessage, SystemMessage]] = []
|
||||
messages: list[Union[BaseMessage]] = []
|
||||
if not input_value and not system_message:
|
||||
raise ValueError("The message you want to send to the model is empty.")
|
||||
if system_message:
|
||||
messages.append(SystemMessage(content=system_message))
|
||||
if input_value:
|
||||
if isinstance(input_value, Prompt):
|
||||
if isinstance(input_value, Message):
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
if "prompt" in input_value:
|
||||
|
|
@ -102,15 +136,23 @@ class LCModelComponent(CustomComponent):
|
|||
messages.append(input_value.to_lc_message())
|
||||
else:
|
||||
messages.append(HumanMessage(content=input_value))
|
||||
inputs = messages or {} # type: ignore
|
||||
if stream:
|
||||
return runnable.stream(inputs) # type: ignore
|
||||
else:
|
||||
message = runnable.invoke(inputs) # type: ignore
|
||||
result = message.content
|
||||
if isinstance(message, AIMessage):
|
||||
status_message = self.build_status_message(message)
|
||||
self.status = status_message
|
||||
inputs: Union[list, dict] = messages or {}
|
||||
try:
|
||||
if stream:
|
||||
return runnable.stream(inputs)
|
||||
else:
|
||||
self.status = result
|
||||
return result
|
||||
message = runnable.invoke(inputs)
|
||||
result = message.content if hasattr(message, "content") else message
|
||||
if isinstance(message, AIMessage):
|
||||
status_message = self.build_status_message(message)
|
||||
self.status = status_message
|
||||
elif isinstance(result, dict):
|
||||
result = json.dumps(message, indent=4)
|
||||
self.status = result
|
||||
else:
|
||||
self.status = result
|
||||
return result
|
||||
except Exception as e:
|
||||
if message := self._get_exception_message(e):
|
||||
raise ValueError(message) from e
|
||||
raise e
|
||||
|
|
|
|||
|
|
@ -2,16 +2,15 @@ from copy import deepcopy
|
|||
|
||||
from langchain_core.documents import Document
|
||||
|
||||
from langflow.schema import Record
|
||||
from langflow.schema.message import Message
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
def record_to_string(record: Record) -> str:
|
||||
def data_to_string(record: Data) -> str:
|
||||
"""
|
||||
Convert a record to a string.
|
||||
|
||||
Args:
|
||||
record (Record): The record to convert.
|
||||
record (Data): The record to convert.
|
||||
|
||||
Returns:
|
||||
str: The record as a string.
|
||||
|
|
@ -29,22 +28,24 @@ def dict_values_to_string(d: dict) -> dict:
|
|||
Returns:
|
||||
dict: The dictionary with values converted to strings.
|
||||
"""
|
||||
from langflow.schema.message import Message
|
||||
|
||||
# Do something similar to the above
|
||||
d_copy = deepcopy(d)
|
||||
for key, value in d_copy.items():
|
||||
# it could be a list of records or documents or strings
|
||||
# it could be a list of data or documents or strings
|
||||
if isinstance(value, list):
|
||||
for i, item in enumerate(value):
|
||||
if isinstance(item, Message):
|
||||
d_copy[key][i] = item.text
|
||||
elif isinstance(item, Record):
|
||||
d_copy[key][i] = record_to_string(item)
|
||||
elif isinstance(item, Data):
|
||||
d_copy[key][i] = data_to_string(item)
|
||||
elif isinstance(item, Document):
|
||||
d_copy[key][i] = document_to_string(item)
|
||||
elif isinstance(value, Message):
|
||||
d_copy[key] = value.text
|
||||
elif isinstance(value, Record):
|
||||
d_copy[key] = record_to_string(value)
|
||||
elif isinstance(value, Data):
|
||||
d_copy[key] = data_to_string(value)
|
||||
elif isinstance(value, Document):
|
||||
d_copy[key] = document_to_string(value)
|
||||
return d_copy
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from langchain_core.runnables import RunnableConfig
|
|||
from langchain_core.tools import ToolException
|
||||
from pydantic.v1 import BaseModel
|
||||
|
||||
from langflow.base.flow_processing.utils import build_records_from_result_data, format_flow_output_records
|
||||
from langflow.base.flow_processing.utils import build_data_from_result_data, format_flow_output_data
|
||||
from langflow.graph.graph.base import Graph
|
||||
from langflow.graph.vertex.base import Vertex
|
||||
from langflow.helpers.flow import build_schema_from_inputs, get_arg_names, get_flow_inputs, run_flow
|
||||
|
|
@ -59,14 +59,12 @@ class FlowTool(BaseTool):
|
|||
return "No output"
|
||||
run_output = run_outputs[0]
|
||||
|
||||
records = []
|
||||
data = []
|
||||
if run_output is not None:
|
||||
for output in run_output.outputs:
|
||||
if output:
|
||||
records.extend(
|
||||
build_records_from_result_data(output, get_final_results_only=self.get_final_results_only)
|
||||
)
|
||||
return format_flow_output_records(records)
|
||||
data.extend(build_data_from_result_data(output, get_final_results_only=self.get_final_results_only))
|
||||
return format_flow_output_data(data)
|
||||
|
||||
def validate_inputs(self, args_names: List[dict[str, str]], args: Any, kwargs: Any):
|
||||
"""Validate the inputs."""
|
||||
|
|
@ -107,11 +105,9 @@ class FlowTool(BaseTool):
|
|||
return "No output"
|
||||
run_output = run_outputs[0]
|
||||
|
||||
records = []
|
||||
data = []
|
||||
if run_output is not None:
|
||||
for output in run_output.outputs:
|
||||
if output:
|
||||
records.extend(
|
||||
build_records_from_result_data(output, get_final_results_only=self.get_final_results_only)
|
||||
)
|
||||
return format_flow_output_records(records)
|
||||
data.extend(build_data_from_result_data(output, get_final_results_only=self.get_final_results_only))
|
||||
return format_flow_output_data(data)
|
||||
|
|
|
|||
104
src/backend/base/langflow/base/vectorstores/model.py
Normal file
104
src/backend/base/langflow/base/vectorstores/model.py
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
from typing import List
|
||||
|
||||
from langchain_core.documents import Document
|
||||
from loguru import logger
|
||||
|
||||
from langflow.custom import Component
|
||||
from langflow.field_typing import Retriever, Text, VectorStore
|
||||
from langflow.helpers.data import docs_to_data
|
||||
from langflow.io import Output
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class LCVectorStoreComponent(Component):
|
||||
outputs = [
|
||||
Output(
|
||||
display_name="Retriever",
|
||||
name="base_retriever",
|
||||
method="build_base_retriever",
|
||||
),
|
||||
Output(
|
||||
display_name="Search Results",
|
||||
name="search_results",
|
||||
method="search_documents",
|
||||
),
|
||||
]
|
||||
|
||||
def _validate_outputs(self):
|
||||
# At least these three outputs must be defined
|
||||
required_output_methods = ["build_base_retriever", "search_documents"]
|
||||
output_names = [output.name for output in self.outputs]
|
||||
for method_name in required_output_methods:
|
||||
if method_name not in output_names:
|
||||
raise ValueError(f"Output with name '{method_name}' must be defined.")
|
||||
elif not hasattr(self, method_name):
|
||||
raise ValueError(f"Method '{method_name}' must be defined.")
|
||||
|
||||
def search_with_vector_store(
|
||||
self,
|
||||
input_value: Text,
|
||||
search_type: str,
|
||||
vector_store: VectorStore,
|
||||
k=10,
|
||||
**kwargs,
|
||||
) -> List[Data]:
|
||||
"""
|
||||
Search for data in the vector store based on the input value and search type.
|
||||
|
||||
Args:
|
||||
input_value (Text): The input value to search for.
|
||||
search_type (str): The type of search to perform.
|
||||
vector_store (VectorStore): The vector store to search in.
|
||||
|
||||
Returns:
|
||||
List[Data]: A list of data matching the search criteria.
|
||||
|
||||
Raises:
|
||||
ValueError: If invalid inputs are provided.
|
||||
"""
|
||||
|
||||
docs: List[Document] = []
|
||||
if input_value and isinstance(input_value, str) and hasattr(vector_store, "search"):
|
||||
docs = vector_store.search(query=input_value, search_type=search_type.lower(), k=k, **kwargs)
|
||||
else:
|
||||
raise ValueError("Invalid inputs provided.")
|
||||
data = docs_to_data(docs)
|
||||
self.status = data
|
||||
return data
|
||||
|
||||
def build_vector_store(self) -> VectorStore:
|
||||
"""
|
||||
Builds the Vector Store object.c
|
||||
"""
|
||||
raise NotImplementedError("build_vector_store method must be implemented.")
|
||||
|
||||
def build_base_retriever(self) -> Retriever:
|
||||
"""
|
||||
Builds the BaseRetriever object.
|
||||
"""
|
||||
vector_store = self.build_vector_store()
|
||||
if hasattr(vector_store, "as_retriever"):
|
||||
return vector_store.as_retriever()
|
||||
else:
|
||||
raise ValueError(f"Vector Store {vector_store.__class__.__name__} does not have an as_retriever method.")
|
||||
|
||||
def search_documents(self) -> List[Data]:
|
||||
"""
|
||||
Search for documents in the Chroma vector store.
|
||||
"""
|
||||
search_query: str = self.search_query
|
||||
if not search_query:
|
||||
self.status = ""
|
||||
return []
|
||||
|
||||
vector_store = self.build_vector_store()
|
||||
|
||||
logger.debug(f"Search input: {search_query}")
|
||||
logger.debug(f"Search type: {self.search_type}")
|
||||
logger.debug(f"Number of results: {self.number_of_results}")
|
||||
|
||||
search_results = self.search_with_vector_store(
|
||||
search_query, self.search_type, vector_store, k=self.number_of_results
|
||||
)
|
||||
self.status = search_results
|
||||
return search_results
|
||||
|
|
@ -1,24 +1,24 @@
|
|||
from langflow.schema import Record
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
def chroma_collection_to_records(collection_dict: dict):
|
||||
def chroma_collection_to_data(collection_dict: dict):
|
||||
"""
|
||||
Converts a collection of chroma vectors into a list of records.
|
||||
Converts a collection of chroma vectors into a list of data.
|
||||
|
||||
Args:
|
||||
collection_dict (dict): A dictionary containing the collection of chroma vectors.
|
||||
|
||||
Returns:
|
||||
list: A list of records, where each record represents a document in the collection.
|
||||
list: A list of data, where each record represents a document in the collection.
|
||||
"""
|
||||
records = []
|
||||
data = []
|
||||
for i, doc in enumerate(collection_dict["documents"]):
|
||||
record_dict = {
|
||||
data_dict = {
|
||||
"id": collection_dict["ids"][i],
|
||||
"text": doc,
|
||||
}
|
||||
if "metadatas" in collection_dict:
|
||||
for key, value in collection_dict["metadatas"][i].items():
|
||||
record_dict[key] = value
|
||||
records.append(Record(**record_dict))
|
||||
return records
|
||||
data_dict[key] = value
|
||||
data.append(Data(**data_dict))
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from langchain_experimental.agents.agent_toolkits.csv.base import create_csv_agent
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import AgentExecutor, BaseLanguageModel
|
||||
from langflow.field_typing import AgentExecutor, LanguageModel
|
||||
|
||||
|
||||
class CSVAgentComponent(CustomComponent):
|
||||
|
|
@ -11,7 +11,7 @@ class CSVAgentComponent(CustomComponent):
|
|||
|
||||
def build_config(self):
|
||||
return {
|
||||
"llm": {"display_name": "LLM", "type": BaseLanguageModel},
|
||||
"llm": {"display_name": "LLM", "type": LanguageModel},
|
||||
"path": {"display_name": "Path", "field_type": "file", "suffixes": [".csv"], "file_types": [".csv"]},
|
||||
"handle_parsing_errors": {"display_name": "Handle Parse Errors", "advanced": True},
|
||||
"agent_type": {
|
||||
|
|
@ -22,7 +22,7 @@ class CSVAgentComponent(CustomComponent):
|
|||
}
|
||||
|
||||
def build(
|
||||
self, llm: BaseLanguageModel, path: str, handle_parsing_errors: bool = True, agent_type: str = "openai-tools"
|
||||
self, llm: LanguageModel, path: str, handle_parsing_errors: bool = True, agent_type: str = "openai-tools"
|
||||
) -> AgentExecutor:
|
||||
# Instantiate and return the CSV agent class with the provided llm and path
|
||||
return create_csv_agent(
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from langchain_community.agent_toolkits import create_json_agent
|
|||
from langchain_community.agent_toolkits.json.toolkit import JsonToolkit
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import BaseLanguageModel
|
||||
from langflow.field_typing import LanguageModel
|
||||
|
||||
|
||||
class JsonAgentComponent(CustomComponent):
|
||||
|
|
@ -18,7 +18,7 @@ class JsonAgentComponent(CustomComponent):
|
|||
|
||||
def build(
|
||||
self,
|
||||
llm: BaseLanguageModel,
|
||||
llm: LanguageModel,
|
||||
toolkit: JsonToolkit,
|
||||
) -> AgentExecutor:
|
||||
return create_json_agent(llm=llm, toolkit=toolkit)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from langchain_community.agent_toolkits.sql.base import create_sql_agent
|
|||
from langchain_community.utilities import SQLDatabase
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import BaseLanguageModel
|
||||
from langflow.field_typing import LanguageModel
|
||||
|
||||
|
||||
class SQLAgentComponent(CustomComponent):
|
||||
|
|
@ -22,7 +22,7 @@ class SQLAgentComponent(CustomComponent):
|
|||
|
||||
def build(
|
||||
self,
|
||||
llm: BaseLanguageModel,
|
||||
llm: LanguageModel,
|
||||
database_uri: str,
|
||||
verbose: bool = False,
|
||||
) -> Union[AgentExecutor, Callable]:
|
||||
|
|
|
|||
|
|
@ -1,64 +1,107 @@
|
|||
from typing import List, Optional
|
||||
from typing import List, cast
|
||||
|
||||
from langchain.agents import AgentExecutor, BaseSingleActionAgent
|
||||
from langchain.agents.tool_calling_agent.base import create_tool_calling_agent
|
||||
from langchain_core.messages import BaseMessage, HumanMessage
|
||||
from langchain_core.prompts import ChatPromptTemplate
|
||||
|
||||
from langflow.base.agents.agent import LCAgentComponent
|
||||
from langflow.field_typing import BaseLanguageModel, Text, Tool
|
||||
from langflow.schema import Record
|
||||
from langflow.custom import Component
|
||||
from langflow.io import BoolInput, HandleInput, Output, TextInput
|
||||
from langflow.schema import Data
|
||||
from langflow.schema.message import Message
|
||||
|
||||
|
||||
class ToolCallingAgentComponent(LCAgentComponent):
|
||||
class ToolCallingAgentComponent(Component):
|
||||
display_name: str = "Tool Calling Agent"
|
||||
description: str = "Agent that uses tools. Only models that are compatible with function calling are supported."
|
||||
icon = "Agent"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"llm": {"display_name": "LLM"},
|
||||
"tools": {"display_name": "Tools"},
|
||||
"user_prompt": {
|
||||
"display_name": "Prompt",
|
||||
"multiline": True,
|
||||
"info": "This prompt must contain 'input' key.",
|
||||
},
|
||||
"handle_parsing_errors": {
|
||||
"display_name": "Handle Parsing Errors",
|
||||
"info": "If True, the agent will handle parsing errors. If False, the agent will raise an error.",
|
||||
"advanced": True,
|
||||
},
|
||||
"memory": {
|
||||
"display_name": "Memory",
|
||||
"info": "Memory to use for the agent.",
|
||||
},
|
||||
"input_value": {
|
||||
"display_name": "Inputs",
|
||||
"info": "Input text to pass to the agent.",
|
||||
},
|
||||
}
|
||||
inputs = [
|
||||
HandleInput(
|
||||
name="llm",
|
||||
display_name="LLM",
|
||||
input_types=["LanguageModel"],
|
||||
),
|
||||
HandleInput(
|
||||
name="tools",
|
||||
display_name="Tools",
|
||||
input_types=["Tool"],
|
||||
is_list=True,
|
||||
),
|
||||
TextInput(
|
||||
name="user_prompt",
|
||||
display_name="Prompt",
|
||||
info="This prompt must contain 'input' key.",
|
||||
value="{input}",
|
||||
),
|
||||
BoolInput(
|
||||
name="handle_parsing_errors",
|
||||
display_name="Handle Parsing Errors",
|
||||
info="If True, the agent will handle parsing errors. If False, the agent will raise an error.",
|
||||
advanced=True,
|
||||
value=True,
|
||||
),
|
||||
HandleInput(
|
||||
name="memory",
|
||||
display_name="Memory",
|
||||
input_types=["Data"],
|
||||
info="Memory to use for the agent.",
|
||||
),
|
||||
TextInput(
|
||||
name="input_value",
|
||||
display_name="Inputs",
|
||||
info="Input text to pass to the agent.",
|
||||
),
|
||||
]
|
||||
|
||||
async def build(
|
||||
self,
|
||||
input_value: str,
|
||||
llm: BaseLanguageModel,
|
||||
tools: List[Tool],
|
||||
user_prompt: str = "{input}",
|
||||
message_history: Optional[List[Record]] = None,
|
||||
system_message: str = "You are a helpful assistant",
|
||||
handle_parsing_errors: bool = True,
|
||||
) -> Text:
|
||||
if "input" not in user_prompt:
|
||||
outputs = [
|
||||
Output(display_name="Text", name="text_output", method="run_agent"),
|
||||
]
|
||||
|
||||
async def run_agent(self) -> Message:
|
||||
if "input" not in self.user_prompt:
|
||||
raise ValueError("Prompt must contain 'input' key.")
|
||||
messages = [
|
||||
("system", system_message),
|
||||
("system", "You are a helpful assistant"),
|
||||
(
|
||||
"placeholder",
|
||||
"{chat_history}",
|
||||
),
|
||||
("human", user_prompt),
|
||||
("human", self.user_prompt),
|
||||
("placeholder", "{agent_scratchpad}"),
|
||||
]
|
||||
prompt = ChatPromptTemplate.from_messages(messages)
|
||||
agent = create_tool_calling_agent(llm, tools, prompt)
|
||||
result = await self.run_agent(agent, input_value, tools, message_history, handle_parsing_errors)
|
||||
agent = create_tool_calling_agent(self.llm, self.tools, prompt)
|
||||
|
||||
runnable = AgentExecutor.from_agent_and_tools(
|
||||
agent=cast(BaseSingleActionAgent, agent),
|
||||
tools=self.tools,
|
||||
verbose=True,
|
||||
handle_parsing_errors=self.handle_parsing_errors,
|
||||
)
|
||||
input_dict: dict[str, str | list[BaseMessage]] = {"input": self.input_value}
|
||||
if hasattr(self, "memory") and self.memory:
|
||||
input_dict["chat_history"] = self.convert_chat_history(self.memory)
|
||||
result = await runnable.ainvoke(input_dict)
|
||||
self.status = result
|
||||
return result
|
||||
|
||||
if "output" not in result:
|
||||
raise ValueError("Output key not found in result. Tried 'output'.")
|
||||
|
||||
result_string = result["output"]
|
||||
|
||||
return Message(text=result_string)
|
||||
|
||||
def convert_chat_history(self, chat_history: List[Message | Data]) -> List[BaseMessage]:
|
||||
messages = []
|
||||
for item in chat_history:
|
||||
if isinstance(item, (Message, Data)):
|
||||
messages.append(item.to_lc_message())
|
||||
elif isinstance(item, str):
|
||||
messages.append(HumanMessage(content=item))
|
||||
elif isinstance(item, dict) and "sender" in item and "text" in item:
|
||||
messages.append(HumanMessage(content=item["text"], sender=item["sender"]))
|
||||
else:
|
||||
raise ValueError(f"Invalid item ({type(item)}) in chat history: {item}")
|
||||
|
||||
return messages
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from langchain.agents import AgentExecutor, create_vectorstore_agent
|
|||
from langchain.agents.agent_toolkits.vectorstore.toolkit import VectorStoreToolkit
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import BaseLanguageModel
|
||||
from langflow.field_typing import LanguageModel
|
||||
|
||||
|
||||
class VectorStoreAgentComponent(CustomComponent):
|
||||
|
|
@ -19,7 +19,7 @@ class VectorStoreAgentComponent(CustomComponent):
|
|||
|
||||
def build(
|
||||
self,
|
||||
llm: BaseLanguageModel,
|
||||
llm: LanguageModel,
|
||||
vector_store_toolkit: VectorStoreToolkit,
|
||||
) -> Union[AgentExecutor, Callable]:
|
||||
return create_vectorstore_agent(llm=llm, toolkit=vector_store_toolkit)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from typing import Callable
|
|||
|
||||
from langchain.agents import create_vectorstore_router_agent
|
||||
from langchain.agents.agent_toolkits.vectorstore.toolkit import VectorStoreRouterToolkit
|
||||
from langchain_core.language_models.base import BaseLanguageModel
|
||||
from langflow.field_typing import LanguageModel
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
|
||||
|
|
@ -17,5 +17,5 @@ class VectorStoreRouterAgentComponent(CustomComponent):
|
|||
"vectorstoreroutertoolkit": {"display_name": "Vector Store Router Toolkit"},
|
||||
}
|
||||
|
||||
def build(self, llm: BaseLanguageModel, vectorstoreroutertoolkit: VectorStoreRouterToolkit) -> Callable:
|
||||
def build(self, llm: LanguageModel, vectorstoreroutertoolkit: VectorStoreRouterToolkit) -> Callable:
|
||||
return create_vectorstore_router_agent(llm=llm, toolkit=vectorstoreroutertoolkit)
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ from langchain.agents import create_xml_agent
|
|||
from langchain_core.prompts import ChatPromptTemplate
|
||||
|
||||
from langflow.base.agents.agent import LCAgentComponent
|
||||
from langflow.field_typing import BaseLanguageModel, Text, Tool
|
||||
from langflow.schema import Record
|
||||
from langflow.field_typing import LanguageModel, Text, Tool
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class XMLAgentComponent(LCAgentComponent):
|
||||
|
|
@ -72,11 +72,11 @@ class XMLAgentComponent(LCAgentComponent):
|
|||
async def build(
|
||||
self,
|
||||
input_value: str,
|
||||
llm: BaseLanguageModel,
|
||||
llm: LanguageModel,
|
||||
tools: List[Tool],
|
||||
user_prompt: str = "{input}",
|
||||
system_message: str = "You are a helpful assistant",
|
||||
message_history: Optional[List[Record]] = None,
|
||||
message_history: Optional[List[Data]] = None,
|
||||
tool_template: str = "{name}: {description}",
|
||||
handle_parsing_errors: bool = True,
|
||||
) -> Text:
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import Optional
|
|||
from langchain.chains import ConversationChain
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import BaseLanguageModel, BaseMemory, Text
|
||||
from langflow.field_typing import BaseMemory, LanguageModel, Text
|
||||
|
||||
|
||||
class ConversationChainComponent(CustomComponent):
|
||||
|
|
@ -27,7 +27,7 @@ class ConversationChainComponent(CustomComponent):
|
|||
def build(
|
||||
self,
|
||||
input_value: Text,
|
||||
llm: BaseLanguageModel,
|
||||
llm: LanguageModel,
|
||||
memory: Optional[BaseMemory] = None,
|
||||
) -> Text:
|
||||
if memory is None:
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from langchain.chains.llm import LLMChain
|
|||
from langchain_core.prompts import PromptTemplate
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import BaseLanguageModel, BaseMemory, Text
|
||||
from langflow.field_typing import BaseMemory, LanguageModel, Text
|
||||
|
||||
|
||||
class LLMChainComponent(CustomComponent):
|
||||
|
|
@ -21,7 +21,7 @@ class LLMChainComponent(CustomComponent):
|
|||
def build(
|
||||
self,
|
||||
template: Text,
|
||||
llm: BaseLanguageModel,
|
||||
llm: LanguageModel,
|
||||
memory: Optional[BaseMemory] = None,
|
||||
) -> Text:
|
||||
prompt = PromptTemplate.from_template(template)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from langchain.chains import LLMCheckerChain
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import BaseLanguageModel, Text
|
||||
from langflow.field_typing import LanguageModel, Text
|
||||
|
||||
|
||||
class LLMCheckerChainComponent(CustomComponent):
|
||||
|
|
@ -21,7 +21,7 @@ class LLMCheckerChainComponent(CustomComponent):
|
|||
def build(
|
||||
self,
|
||||
input_value: Text,
|
||||
llm: BaseLanguageModel,
|
||||
llm: LanguageModel,
|
||||
) -> Text:
|
||||
chain = LLMCheckerChain.from_llm(llm=llm)
|
||||
response = chain.invoke({chain.input_key: input_value})
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import Optional
|
|||
from langchain.chains import LLMChain, LLMMathChain
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import BaseLanguageModel, BaseMemory, Text
|
||||
from langflow.field_typing import BaseMemory, LanguageModel, Text
|
||||
|
||||
|
||||
class LLMMathChainComponent(CustomComponent):
|
||||
|
|
@ -27,7 +27,7 @@ class LLMMathChainComponent(CustomComponent):
|
|||
def build(
|
||||
self,
|
||||
input_value: Text,
|
||||
llm: BaseLanguageModel,
|
||||
llm: LanguageModel,
|
||||
llm_chain: LLMChain,
|
||||
input_key: str = "question",
|
||||
output_key: str = "answer",
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ from langchain.chains.retrieval_qa.base import RetrievalQA
|
|||
from langchain_core.documents import Document
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import BaseLanguageModel, BaseMemory, BaseRetriever, Text
|
||||
from langflow.schema import Record
|
||||
from langflow.field_typing import BaseMemory, BaseRetriever, LanguageModel, Text
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class RetrievalQAComponent(CustomComponent):
|
||||
|
|
@ -23,13 +23,13 @@ class RetrievalQAComponent(CustomComponent):
|
|||
"return_source_documents": {"display_name": "Return Source Documents"},
|
||||
"input_value": {
|
||||
"display_name": "Input",
|
||||
"input_types": ["Record", "Document"],
|
||||
"input_types": ["Data", "Document"],
|
||||
},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
llm: BaseLanguageModel,
|
||||
llm: LanguageModel,
|
||||
chain_type: str,
|
||||
retriever: BaseRetriever,
|
||||
input_value: str = "",
|
||||
|
|
@ -50,17 +50,17 @@ class RetrievalQAComponent(CustomComponent):
|
|||
)
|
||||
if isinstance(input_value, Document):
|
||||
input_value = input_value.page_content
|
||||
if isinstance(input_value, Record):
|
||||
if isinstance(input_value, Data):
|
||||
input_value = input_value.get_text()
|
||||
self.status = runnable
|
||||
result = runnable.invoke({input_key: input_value})
|
||||
result = result.content if hasattr(result, "content") else result
|
||||
# Result is a dict with keys "query", "result" and "source_documents"
|
||||
# for now we just return the result
|
||||
records = self.to_records(result.get("source_documents"))
|
||||
data = self.to_data(result.get("source_documents"))
|
||||
references_str = ""
|
||||
if return_source_documents:
|
||||
references_str = self.create_references_from_records(records)
|
||||
references_str = self.create_references_from_data(data)
|
||||
result_str = result.get("result", "")
|
||||
|
||||
final_result = "\n".join([Text(result_str), references_str])
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from langchain.chains import RetrievalQAWithSourcesChain
|
|||
from langchain_core.documents import Document
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import BaseLanguageModel, BaseMemory, BaseRetriever, Text
|
||||
from langflow.field_typing import BaseMemory, BaseRetriever, LanguageModel, Text
|
||||
|
||||
|
||||
class RetrievalQAWithSourcesChainComponent(CustomComponent):
|
||||
|
|
@ -32,7 +32,7 @@ class RetrievalQAWithSourcesChainComponent(CustomComponent):
|
|||
self,
|
||||
input_value: Text,
|
||||
retriever: BaseRetriever,
|
||||
llm: BaseLanguageModel,
|
||||
llm: LanguageModel,
|
||||
chain_type: str,
|
||||
memory: Optional[BaseMemory] = None,
|
||||
return_source_documents: Optional[bool] = True,
|
||||
|
|
@ -53,10 +53,10 @@ class RetrievalQAWithSourcesChainComponent(CustomComponent):
|
|||
result = result.content if hasattr(result, "content") else result
|
||||
# Result is a dict with keys "query", "result" and "source_documents"
|
||||
# for now we just return the result
|
||||
records = self.to_records(result.get("source_documents"))
|
||||
data = self.to_data(result.get("source_documents"))
|
||||
references_str = ""
|
||||
if return_source_documents:
|
||||
references_str = self.create_references_from_records(records)
|
||||
references_str = self.create_references_from_data(data)
|
||||
result_str = Text(result.get("answer", ""))
|
||||
final_result = "\n".join([result_str, references_str])
|
||||
self.status = final_result
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from langchain_core.prompts import PromptTemplate
|
|||
from langchain_core.runnables import Runnable
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import BaseLanguageModel, Text
|
||||
from langflow.field_typing import LanguageModel, Text
|
||||
|
||||
|
||||
class SQLGeneratorComponent(CustomComponent):
|
||||
|
|
@ -35,7 +35,7 @@ class SQLGeneratorComponent(CustomComponent):
|
|||
self,
|
||||
input_value: Text,
|
||||
db: SQLDatabase,
|
||||
llm: BaseLanguageModel,
|
||||
llm: LanguageModel,
|
||||
top_k: int = 5,
|
||||
prompt: Optional[Text] = None,
|
||||
) -> Text:
|
||||
|
|
|
|||
|
|
@ -1,54 +1,77 @@
|
|||
import asyncio
|
||||
import json
|
||||
from typing import Any, List, Optional
|
||||
from urllib.parse import parse_qsl, urlencode, urlparse, urlunparse
|
||||
|
||||
import httpx
|
||||
from loguru import logger
|
||||
|
||||
from langflow.base.curl.parse import parse_context
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import NestedDict
|
||||
from langflow.schema import Record
|
||||
from langflow.custom import Component
|
||||
from langflow.io import DataInput, DropdownInput, IntInput, NestedDictInput, Output, TextInput
|
||||
from langflow.schema import Data
|
||||
from langflow.schema.dotdict import dotdict
|
||||
|
||||
|
||||
class APIRequest(CustomComponent):
|
||||
display_name: str = "API Request"
|
||||
description: str = "Make HTTP requests given one or more URLs."
|
||||
output_types: list[str] = ["Record"]
|
||||
documentation: str = "https://docs.langflow.org/components/utilities#api-request"
|
||||
class APIRequestComponent(Component):
|
||||
display_name = "API Request"
|
||||
description = (
|
||||
"This component allows you to make HTTP requests to one or more URLs. "
|
||||
"You can provide headers and body as either dictionaries or Data objects. "
|
||||
"Additionally, you can append query parameters to the URLs.\n\n"
|
||||
"**Note:** Check advanced options for more settings."
|
||||
)
|
||||
icon = "Globe"
|
||||
|
||||
field_config = {
|
||||
"urls": {"display_name": "URLs", "info": "URLs to make requests to."},
|
||||
"curl": {
|
||||
"display_name": "Curl",
|
||||
"info": "Paste a curl command to populate the fields.",
|
||||
"refresh_button": True,
|
||||
"refresh_button_text": "",
|
||||
},
|
||||
"method": {
|
||||
"display_name": "Method",
|
||||
"info": "The HTTP method to use.",
|
||||
"options": ["GET", "POST", "PATCH", "PUT"],
|
||||
"value": "GET",
|
||||
},
|
||||
"headers": {
|
||||
"display_name": "Headers",
|
||||
"info": "The headers to send with the request.",
|
||||
"input_types": ["Record"],
|
||||
},
|
||||
"body": {
|
||||
"display_name": "Body",
|
||||
"info": "The body to send with the request (for POST, PATCH, PUT).",
|
||||
"input_types": ["Record"],
|
||||
},
|
||||
"timeout": {
|
||||
"display_name": "Timeout",
|
||||
"info": "The timeout to use for the request.",
|
||||
"value": 5,
|
||||
},
|
||||
}
|
||||
inputs = [
|
||||
TextInput(
|
||||
name="urls",
|
||||
display_name="URLs",
|
||||
is_list=True,
|
||||
info="Enter one or more URLs, separated by commas.",
|
||||
),
|
||||
TextInput(
|
||||
name="curl",
|
||||
display_name="Curl",
|
||||
info="Paste a curl command to populate the fields. This will fill in the dictionary fields for headers and body.",
|
||||
advanced=False,
|
||||
refresh_button=True,
|
||||
),
|
||||
DropdownInput(
|
||||
name="method",
|
||||
display_name="Method",
|
||||
options=["GET", "POST", "PATCH", "PUT"],
|
||||
value="GET",
|
||||
info="The HTTP method to use (GET, POST, PATCH, PUT).",
|
||||
),
|
||||
NestedDictInput(
|
||||
name="headers",
|
||||
display_name="Headers",
|
||||
info="The headers to send with the request as a dictionary. This is populated when using the CURL field.",
|
||||
input_types=["Data"],
|
||||
),
|
||||
NestedDictInput(
|
||||
name="body",
|
||||
display_name="Body",
|
||||
info="The body to send with the request as a dictionary (for POST, PATCH, PUT). This is populated when using the CURL field.",
|
||||
input_types=["Data"],
|
||||
),
|
||||
DataInput(
|
||||
name="query_params",
|
||||
display_name="Query Parameters",
|
||||
info="The query parameters to append to the URL.",
|
||||
),
|
||||
IntInput(
|
||||
name="timeout",
|
||||
display_name="Timeout",
|
||||
value=5,
|
||||
info="The timeout to use for the request.",
|
||||
),
|
||||
]
|
||||
|
||||
outputs = [
|
||||
Output(display_name="Data", name="data", method="make_requests"),
|
||||
]
|
||||
|
||||
def parse_curl(self, curl: str, build_config: dotdict) -> dotdict:
|
||||
try:
|
||||
|
|
@ -57,18 +80,21 @@ class APIRequest(CustomComponent):
|
|||
build_config["method"]["value"] = parsed.method.upper()
|
||||
build_config["headers"]["value"] = dict(parsed.headers)
|
||||
|
||||
try:
|
||||
json_data = json.loads(parsed.data)
|
||||
build_config["body"]["value"] = json_data
|
||||
except json.JSONDecodeError as e:
|
||||
print(e)
|
||||
if parsed.data:
|
||||
try:
|
||||
json_data = json.loads(parsed.data)
|
||||
build_config["body"]["value"] = json_data
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Error decoding JSON data: {e}")
|
||||
else:
|
||||
build_config["body"]["value"] = {}
|
||||
except Exception as exc:
|
||||
logger.error(f"Error parsing curl: {exc}")
|
||||
raise ValueError(f"Error parsing curl: {exc}")
|
||||
return build_config
|
||||
|
||||
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None):
|
||||
if field_name == "curl" and field_value is not None:
|
||||
if field_name == "curl" and field_value:
|
||||
build_config = self.parse_curl(field_value, build_config)
|
||||
return build_config
|
||||
|
||||
|
|
@ -80,20 +106,20 @@ class APIRequest(CustomComponent):
|
|||
headers: Optional[dict] = None,
|
||||
body: Optional[dict] = None,
|
||||
timeout: int = 5,
|
||||
) -> Record:
|
||||
) -> Data:
|
||||
method = method.upper()
|
||||
if method not in ["GET", "POST", "PATCH", "PUT", "DELETE"]:
|
||||
raise ValueError(f"Unsupported method: {method}")
|
||||
|
||||
data = body if body else None
|
||||
payload = json.dumps(data)
|
||||
payload = json.dumps(data) if data else None
|
||||
try:
|
||||
response = await client.request(method, url, headers=headers, content=payload, timeout=timeout)
|
||||
try:
|
||||
result = response.json()
|
||||
except Exception:
|
||||
result = response.text
|
||||
return Record(
|
||||
return Data(
|
||||
data={
|
||||
"source": url,
|
||||
"headers": headers,
|
||||
|
|
@ -102,7 +128,7 @@ class APIRequest(CustomComponent):
|
|||
},
|
||||
)
|
||||
except httpx.TimeoutException:
|
||||
return Record(
|
||||
return Data(
|
||||
data={
|
||||
"source": url,
|
||||
"headers": headers,
|
||||
|
|
@ -111,7 +137,7 @@ class APIRequest(CustomComponent):
|
|||
},
|
||||
)
|
||||
except Exception as exc:
|
||||
return Record(
|
||||
return Data(
|
||||
data={
|
||||
"source": url,
|
||||
"headers": headers,
|
||||
|
|
@ -120,36 +146,38 @@ class APIRequest(CustomComponent):
|
|||
},
|
||||
)
|
||||
|
||||
async def build(
|
||||
self,
|
||||
method: str,
|
||||
urls: List[str],
|
||||
curl: Optional[str] = None,
|
||||
headers: Optional[NestedDict] = {},
|
||||
body: Optional[NestedDict] = {},
|
||||
timeout: int = 5,
|
||||
) -> List[Record]:
|
||||
if headers is None:
|
||||
headers_dict = {}
|
||||
elif isinstance(headers, Record):
|
||||
headers_dict = headers.data
|
||||
else:
|
||||
headers_dict = headers
|
||||
def add_query_params(self, url: str, params: dict) -> str:
|
||||
url_parts = list(urlparse(url))
|
||||
query = dict(parse_qsl(url_parts[4]))
|
||||
query.update(params)
|
||||
url_parts[4] = urlencode(query)
|
||||
return urlunparse(url_parts)
|
||||
|
||||
bodies = []
|
||||
if body:
|
||||
if not isinstance(body, list):
|
||||
bodies = [body]
|
||||
else:
|
||||
bodies = body
|
||||
bodies = [b.data if isinstance(b, Record) else b for b in bodies] # type: ignore
|
||||
async def make_requests(self) -> List[Data]:
|
||||
method = self.method
|
||||
urls = [url.strip() for url in self.urls if url.strip()]
|
||||
curl = self.curl
|
||||
headers = self.headers or {}
|
||||
body = self.body or {}
|
||||
timeout = self.timeout
|
||||
query_params = self.query_params.data if self.query_params else {}
|
||||
|
||||
if curl:
|
||||
self._build_config = self.parse_curl(curl, dotdict())
|
||||
|
||||
if isinstance(headers, Data):
|
||||
headers = headers.data
|
||||
|
||||
if isinstance(body, Data):
|
||||
body = body.data
|
||||
|
||||
bodies = [body] * len(urls)
|
||||
|
||||
urls = [self.add_query_params(url, query_params) for url in urls]
|
||||
|
||||
if len(urls) != len(bodies):
|
||||
# add bodies with None
|
||||
bodies += [None] * (len(urls) - len(bodies)) # type: ignore
|
||||
async with httpx.AsyncClient() as client:
|
||||
results = await asyncio.gather(
|
||||
*[self.make_request(client, method, u, headers_dict, rec, timeout) for u, rec in zip(urls, bodies)]
|
||||
*[self.make_request(client, method, u, headers, rec, timeout) for u, rec in zip(urls, bodies)]
|
||||
)
|
||||
self.status = results
|
||||
return results
|
||||
|
|
|
|||
|
|
@ -1,63 +1,88 @@
|
|||
from typing import Any, Dict, List, Optional
|
||||
from typing import List, Optional
|
||||
|
||||
from langflow.base.data.utils import parallel_load_records, parse_text_file_to_record, retrieve_file_paths
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Record
|
||||
from langflow.base.data.utils import parallel_load_data, parse_text_file_to_data, retrieve_file_paths
|
||||
from langflow.custom import Component
|
||||
from langflow.io import BoolInput, IntInput, TextInput
|
||||
from langflow.schema import Data
|
||||
from langflow.template import Output
|
||||
|
||||
|
||||
class DirectoryComponent(CustomComponent):
|
||||
class DirectoryComponent(Component):
|
||||
display_name = "Directory"
|
||||
description = "Recursively load files from a directory."
|
||||
icon = "folder"
|
||||
|
||||
def build_config(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"path": {"display_name": "Path"},
|
||||
"types": {
|
||||
"display_name": "Types",
|
||||
"info": "File types to load. Leave empty to load all types.",
|
||||
},
|
||||
"depth": {"display_name": "Depth", "info": "Depth to search for files."},
|
||||
"max_concurrency": {"display_name": "Max Concurrency", "advanced": True},
|
||||
"load_hidden": {
|
||||
"display_name": "Load Hidden",
|
||||
"advanced": True,
|
||||
"info": "If true, hidden files will be loaded.",
|
||||
},
|
||||
"recursive": {
|
||||
"display_name": "Recursive",
|
||||
"advanced": True,
|
||||
"info": "If true, the search will be recursive.",
|
||||
},
|
||||
"silent_errors": {
|
||||
"display_name": "Silent Errors",
|
||||
"advanced": True,
|
||||
"info": "If true, errors will not raise an exception.",
|
||||
},
|
||||
"use_multithreading": {
|
||||
"display_name": "Use Multithreading",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
inputs = [
|
||||
TextInput(
|
||||
name="path",
|
||||
display_name="Path",
|
||||
info="Path to the directory to load files from.",
|
||||
),
|
||||
TextInput(
|
||||
name="types",
|
||||
display_name="Types",
|
||||
info="File types to load. Leave empty to load all types.",
|
||||
),
|
||||
IntInput(
|
||||
name="depth",
|
||||
display_name="Depth",
|
||||
info="Depth to search for files.",
|
||||
value=0,
|
||||
),
|
||||
IntInput(
|
||||
name="max_concurrency",
|
||||
display_name="Max Concurrency",
|
||||
advanced=True,
|
||||
info="Maximum concurrency for loading files.",
|
||||
value=2,
|
||||
),
|
||||
BoolInput(
|
||||
name="load_hidden",
|
||||
display_name="Load Hidden",
|
||||
advanced=True,
|
||||
info="If true, hidden files will be loaded.",
|
||||
),
|
||||
BoolInput(
|
||||
name="recursive",
|
||||
display_name="Recursive",
|
||||
advanced=True,
|
||||
info="If true, the search will be recursive.",
|
||||
),
|
||||
BoolInput(
|
||||
name="silent_errors",
|
||||
display_name="Silent Errors",
|
||||
advanced=True,
|
||||
info="If true, errors will not raise an exception.",
|
||||
),
|
||||
BoolInput(
|
||||
name="use_multithreading",
|
||||
display_name="Use Multithreading",
|
||||
advanced=True,
|
||||
info="If true, multithreading will be used.",
|
||||
),
|
||||
]
|
||||
|
||||
outputs = [
|
||||
Output(display_name="Data", name="data", method="load_directory"),
|
||||
]
|
||||
|
||||
def load_directory(self) -> List[Optional[Data]]:
|
||||
path = self.path
|
||||
depth = self.depth
|
||||
max_concurrency = self.max_concurrency
|
||||
load_hidden = self.load_hidden
|
||||
recursive = self.recursive
|
||||
silent_errors = self.silent_errors
|
||||
use_multithreading = self.use_multithreading
|
||||
|
||||
def build(
|
||||
self,
|
||||
path: str,
|
||||
depth: int = 0,
|
||||
max_concurrency: int = 2,
|
||||
load_hidden: bool = False,
|
||||
recursive: bool = True,
|
||||
silent_errors: bool = False,
|
||||
use_multithreading: bool = True,
|
||||
) -> List[Optional[Record]]:
|
||||
resolved_path = self.resolve_path(path)
|
||||
file_paths = retrieve_file_paths(resolved_path, load_hidden, recursive, depth)
|
||||
loaded_records = []
|
||||
loaded_data = []
|
||||
|
||||
if use_multithreading:
|
||||
loaded_records = parallel_load_records(file_paths, silent_errors, max_concurrency)
|
||||
loaded_data = parallel_load_data(file_paths, silent_errors, max_concurrency)
|
||||
else:
|
||||
loaded_records = [parse_text_file_to_record(file_path, silent_errors) for file_path in file_paths]
|
||||
loaded_records = list(filter(None, loaded_records))
|
||||
self.status = loaded_records
|
||||
return loaded_records
|
||||
loaded_data = [parse_text_file_to_data(file_path, silent_errors) for file_path in file_paths]
|
||||
loaded_data = list(filter(None, loaded_data))
|
||||
self.status = loaded_data
|
||||
return loaded_data
|
||||
|
|
|
|||
|
|
@ -1,48 +1,48 @@
|
|||
from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
from langflow.base.data.utils import TEXT_FILE_TYPES, parse_text_file_to_record
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Record
|
||||
from langflow.base.data.utils import TEXT_FILE_TYPES, parse_text_file_to_data
|
||||
from langflow.custom import Component
|
||||
from langflow.io import BoolInput, FileInput, Output
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class FileComponent(CustomComponent):
|
||||
class FileComponent(Component):
|
||||
display_name = "File"
|
||||
description = "A generic file loader."
|
||||
icon = "file-text"
|
||||
|
||||
def build_config(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"path": {
|
||||
"display_name": "Path",
|
||||
"field_type": "file",
|
||||
"file_types": TEXT_FILE_TYPES,
|
||||
"info": f"Supported file types: {', '.join(TEXT_FILE_TYPES)}",
|
||||
},
|
||||
"silent_errors": {
|
||||
"display_name": "Silent Errors",
|
||||
"advanced": True,
|
||||
"info": "If true, errors will not raise an exception.",
|
||||
},
|
||||
}
|
||||
inputs = [
|
||||
FileInput(
|
||||
name="path",
|
||||
display_name="Path",
|
||||
file_types=TEXT_FILE_TYPES,
|
||||
info=f"Supported file types: {', '.join(TEXT_FILE_TYPES)}",
|
||||
),
|
||||
BoolInput(
|
||||
name="silent_errors",
|
||||
display_name="Silent Errors",
|
||||
advanced=True,
|
||||
info="If true, errors will not raise an exception.",
|
||||
),
|
||||
]
|
||||
|
||||
outputs = [
|
||||
Output(display_name="Data", name="data", method="load_file"),
|
||||
]
|
||||
|
||||
def load_file(self) -> Data:
|
||||
if not self.path:
|
||||
raise ValueError("Please, upload a file to use this component.")
|
||||
resolved_path = self.resolve_path(self.path)
|
||||
silent_errors = self.silent_errors
|
||||
|
||||
extension = Path(resolved_path).suffix[1:].lower()
|
||||
|
||||
def load_file(self, path: str, silent_errors: bool = False) -> Record:
|
||||
resolved_path = self.resolve_path(path)
|
||||
path_obj = Path(resolved_path)
|
||||
extension = path_obj.suffix[1:].lower()
|
||||
if extension == "doc":
|
||||
raise ValueError("doc files are not supported. Please save as .docx")
|
||||
if extension not in TEXT_FILE_TYPES:
|
||||
raise ValueError(f"Unsupported file type: {extension}")
|
||||
record = parse_text_file_to_record(resolved_path, silent_errors)
|
||||
self.status = record if record else "No data"
|
||||
return record or Record()
|
||||
|
||||
def build(
|
||||
self,
|
||||
path: str,
|
||||
silent_errors: bool = False,
|
||||
) -> Record:
|
||||
record = self.load_file(path, silent_errors)
|
||||
self.status = record
|
||||
return record
|
||||
data = parse_text_file_to_data(resolved_path, silent_errors)
|
||||
self.status = data if data else "No data"
|
||||
return data or Data()
|
||||
|
|
|
|||
|
|
@ -1,27 +1,65 @@
|
|||
from typing import Any, Dict
|
||||
import re
|
||||
|
||||
from langchain_community.document_loaders.web_base import WebBaseLoader
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Record
|
||||
from langflow.custom import Component
|
||||
from langflow.io import Output, TextInput
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class URLComponent(CustomComponent):
|
||||
class URLComponent(Component):
|
||||
display_name = "URL"
|
||||
description = "Fetch content from one or more URLs."
|
||||
icon = "layout-template"
|
||||
|
||||
def build_config(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"urls": {"display_name": "URL"},
|
||||
}
|
||||
inputs = [
|
||||
TextInput(
|
||||
name="urls",
|
||||
display_name="URLs",
|
||||
info="Enter one or more URLs, separated by commas.",
|
||||
is_list=True,
|
||||
),
|
||||
]
|
||||
|
||||
def build(
|
||||
self,
|
||||
urls: list[str],
|
||||
) -> list[Record]:
|
||||
loader = WebBaseLoader(web_paths=[url for url in urls if url])
|
||||
outputs = [
|
||||
Output(display_name="Data", name="data", method="fetch_content"),
|
||||
]
|
||||
|
||||
def ensure_url(self, string: str) -> str:
|
||||
"""
|
||||
Ensures the given string is a URL by adding 'http://' if it doesn't start with 'http://' or 'https://'.
|
||||
Raises an error if the string is not a valid URL.
|
||||
|
||||
Parameters:
|
||||
string (str): The string to be checked and possibly modified.
|
||||
|
||||
Returns:
|
||||
str: The modified string that is ensured to be a URL.
|
||||
|
||||
Raises:
|
||||
ValueError: If the string is not a valid URL.
|
||||
"""
|
||||
if not string.startswith(("http://", "https://")):
|
||||
string = "http://" + string
|
||||
|
||||
# Basic URL validation regex
|
||||
url_regex = re.compile(
|
||||
r"^(http://|https://)?" # http:// or https://
|
||||
r"(([a-zA-Z0-9\.-]+)" # domain
|
||||
r"(\.[a-zA-Z]{2,}))" # top-level domain
|
||||
r"(:[0-9]{1,5})?" # optional port
|
||||
r"(\/.*)?$" # optional path
|
||||
)
|
||||
|
||||
if not re.match(url_regex, string):
|
||||
raise ValueError(f"Invalid URL: {string}")
|
||||
|
||||
return string
|
||||
|
||||
def fetch_content(self) -> list[Data]:
|
||||
urls = [self.ensure_url(url.strip()) for url in self.urls if url.strip()]
|
||||
loader = WebBaseLoader(web_paths=urls, encoding="utf-8")
|
||||
docs = loader.load()
|
||||
records = self.to_records(docs)
|
||||
self.status = records
|
||||
return records
|
||||
data = [Data(text=doc.page_content, **doc.metadata) for doc in docs]
|
||||
self.status = data
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -1,39 +1,37 @@
|
|||
import json
|
||||
import uuid
|
||||
from typing import Any, Optional
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Record
|
||||
from langflow.schema.dotdict import dotdict
|
||||
from langflow.custom import Component
|
||||
from langflow.io import MultilineInput, Output
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class WebhookComponent(CustomComponent):
|
||||
class WebhookComponent(Component):
|
||||
display_name = "Webhook Input"
|
||||
description = "Defines a webhook input for the flow."
|
||||
|
||||
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None):
|
||||
if field_name == "webhook_id":
|
||||
build_config["webhook_id"]["value"] = uuid.uuid4().hex
|
||||
return build_config
|
||||
inputs = [
|
||||
MultilineInput(
|
||||
name="data",
|
||||
display_name="Data",
|
||||
info="Use this field to quickly test the webhook component by providing a JSON payload.",
|
||||
)
|
||||
]
|
||||
outputs = [
|
||||
Output(display_name="Data", name="output_data", method="build_data"),
|
||||
]
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"data": {
|
||||
"display_name": "Data",
|
||||
"info": "Use this field to quickly test the webhook component by providing a JSON payload.",
|
||||
"multiline": True,
|
||||
}
|
||||
}
|
||||
|
||||
def build(self, data: Optional[str] = "") -> Record:
|
||||
message = ""
|
||||
def build_data(self) -> Data:
|
||||
message: str | Data = ""
|
||||
if not self.data:
|
||||
self.status = "No data provided."
|
||||
return Data(data={})
|
||||
try:
|
||||
body = json.loads(data or "{}")
|
||||
body = json.loads(self.data or "{}")
|
||||
except json.JSONDecodeError:
|
||||
body = {"payload": data}
|
||||
message = f"Invalid JSON payload. Please check the format.\n\n{data}"
|
||||
record = Record(data=body)
|
||||
body = {"payload": self.data}
|
||||
message = f"Invalid JSON payload. Please check the format.\n\n{self.data}"
|
||||
data = Data(data=body)
|
||||
if not message:
|
||||
message = json.dumps(body, indent=2)
|
||||
message = data
|
||||
self.status = message
|
||||
return record
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
from .APIRequest import APIRequest
|
||||
from .APIRequest import APIRequestComponent
|
||||
from .Directory import DirectoryComponent
|
||||
from .File import FileComponent
|
||||
from .URL import URLComponent
|
||||
from .Webhook import WebhookComponent
|
||||
|
||||
from .URL import URLComponent
|
||||
|
||||
__all__ = ["APIRequest", "DirectoryComponent", "FileComponent", "URLComponent", "WebhookComponent"]
|
||||
__all__ = ["APIRequestComponent", "DirectoryComponent", "FileComponent", "URLComponent", "WebhookComponent"]
|
||||
|
|
|
|||
|
|
@ -1,42 +1,48 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain_community.embeddings import BedrockEmbeddings
|
||||
from langchain_core.embeddings import Embeddings
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.io import DropdownInput, Output, TextInput
|
||||
|
||||
|
||||
class AmazonBedrockEmeddingsComponent(CustomComponent):
|
||||
class AmazonBedrockEmbeddingsComponent(LCModelComponent):
|
||||
display_name: str = "Amazon Bedrock Embeddings"
|
||||
description: str = "Generate embeddings using Amazon Bedrock models."
|
||||
documentation = "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/bedrock"
|
||||
icon = "Amazon"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"model_id": {
|
||||
"display_name": "Model Id",
|
||||
"options": ["amazon.titan-embed-text-v1"],
|
||||
},
|
||||
"credentials_profile_name": {"display_name": "Credentials Profile Name"},
|
||||
"endpoint_url": {"display_name": "Bedrock Endpoint URL"},
|
||||
"region_name": {"display_name": "AWS Region"},
|
||||
"code": {"show": False},
|
||||
}
|
||||
inputs = [
|
||||
DropdownInput(
|
||||
name="model_id",
|
||||
display_name="Model Id",
|
||||
options=["amazon.titan-embed-text-v1"],
|
||||
value="amazon.titan-embed-text-v1",
|
||||
),
|
||||
TextInput(
|
||||
name="credentials_profile_name",
|
||||
display_name="Credentials Profile Name",
|
||||
),
|
||||
TextInput(
|
||||
name="endpoint_url",
|
||||
display_name="Bedrock Endpoint URL",
|
||||
),
|
||||
TextInput(
|
||||
name="region_name",
|
||||
display_name="AWS Region",
|
||||
),
|
||||
]
|
||||
|
||||
def build(
|
||||
self,
|
||||
model_id: str = "amazon.titan-embed-text-v1",
|
||||
credentials_profile_name: Optional[str] = None,
|
||||
endpoint_url: Optional[str] = None,
|
||||
region_name: Optional[str] = None,
|
||||
) -> Embeddings:
|
||||
outputs = [
|
||||
Output(display_name="Embeddings", name="embeddings", method="build_embeddings"),
|
||||
]
|
||||
|
||||
def build_embeddings(self) -> Embeddings:
|
||||
try:
|
||||
output = BedrockEmbeddings(
|
||||
credentials_profile_name=credentials_profile_name,
|
||||
model_id=model_id,
|
||||
endpoint_url=endpoint_url,
|
||||
region_name=region_name,
|
||||
credentials_profile_name=self.credentials_profile_name,
|
||||
model_id=self.model_id,
|
||||
endpoint_url=self.endpoint_url,
|
||||
region_name=self.region_name,
|
||||
) # type: ignore
|
||||
except Exception as e:
|
||||
raise ValueError("Could not connect to AmazonBedrock API.") from e
|
||||
raise ValueError("Could not connect to Amazon Bedrock API.") from e
|
||||
return output
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
from typing import Optional
|
||||
from langchain_core.embeddings import Embeddings
|
||||
from langchain_openai import AzureOpenAIEmbeddings
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.io import DropdownInput, IntInput, Output, SecretStrInput, TextInput
|
||||
|
||||
|
||||
class AzureOpenAIEmbeddingsComponent(CustomComponent):
|
||||
class AzureOpenAIEmbeddingsComponent(LCModelComponent):
|
||||
display_name: str = "Azure OpenAI Embeddings"
|
||||
description: str = "Generate embeddings using Azure OpenAI models."
|
||||
documentation: str = "https://python.langchain.com/docs/integrations/text_embedding/azureopenai"
|
||||
beta = False
|
||||
icon = "Azure"
|
||||
|
||||
API_VERSION_OPTIONS = [
|
||||
|
|
@ -22,57 +21,56 @@ class AzureOpenAIEmbeddingsComponent(CustomComponent):
|
|||
"2023-08-01-preview",
|
||||
]
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"azure_endpoint": {
|
||||
"display_name": "Azure Endpoint",
|
||||
"required": True,
|
||||
"info": "Your Azure endpoint, including the resource.. Example: `https://example-resource.azure.openai.com/`",
|
||||
},
|
||||
"azure_deployment": {
|
||||
"display_name": "Deployment Name",
|
||||
"required": True,
|
||||
},
|
||||
"api_version": {
|
||||
"display_name": "API Version",
|
||||
"options": self.API_VERSION_OPTIONS,
|
||||
"value": self.API_VERSION_OPTIONS[-1],
|
||||
"advanced": True,
|
||||
},
|
||||
"api_key": {
|
||||
"display_name": "API Key",
|
||||
"required": True,
|
||||
"password": True,
|
||||
},
|
||||
"code": {"show": False},
|
||||
"dimensions": {
|
||||
"display_name": "Dimensions",
|
||||
"info": "The number of dimensions the resulting output embeddings should have. Only supported by certain models.",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
inputs = [
|
||||
TextInput(
|
||||
name="azure_endpoint",
|
||||
display_name="Azure Endpoint",
|
||||
required=True,
|
||||
info="Your Azure endpoint, including the resource. Example: `https://example-resource.azure.openai.com/`",
|
||||
),
|
||||
TextInput(
|
||||
name="azure_deployment",
|
||||
display_name="Deployment Name",
|
||||
required=True,
|
||||
),
|
||||
DropdownInput(
|
||||
name="api_version",
|
||||
display_name="API Version",
|
||||
options=API_VERSION_OPTIONS,
|
||||
value=API_VERSION_OPTIONS[-1],
|
||||
advanced=True,
|
||||
),
|
||||
SecretStrInput(
|
||||
name="api_key",
|
||||
display_name="API Key",
|
||||
required=True,
|
||||
),
|
||||
IntInput(
|
||||
name="dimensions",
|
||||
display_name="Dimensions",
|
||||
info="The number of dimensions the resulting output embeddings should have. Only supported by certain models.",
|
||||
advanced=True,
|
||||
),
|
||||
]
|
||||
|
||||
outputs = [
|
||||
Output(display_name="Embeddings", name="embeddings", method="build_embeddings"),
|
||||
]
|
||||
|
||||
def build_embeddings(self) -> Embeddings:
|
||||
if not self.api_key:
|
||||
raise ValueError("API Key is required")
|
||||
|
||||
azure_api_key = SecretStr(self.api_key)
|
||||
|
||||
def build(
|
||||
self,
|
||||
azure_endpoint: str,
|
||||
azure_deployment: str,
|
||||
api_version: str,
|
||||
api_key: str,
|
||||
dimensions: Optional[int] = None,
|
||||
) -> Embeddings:
|
||||
if api_key:
|
||||
azure_api_key = SecretStr(api_key)
|
||||
else:
|
||||
azure_api_key = None
|
||||
try:
|
||||
embeddings = AzureOpenAIEmbeddings(
|
||||
azure_endpoint=azure_endpoint,
|
||||
azure_deployment=azure_deployment,
|
||||
api_version=api_version,
|
||||
azure_endpoint=self.azure_endpoint,
|
||||
azure_deployment=self.azure_deployment,
|
||||
api_version=self.api_version,
|
||||
api_key=azure_api_key,
|
||||
dimensions=dimensions,
|
||||
dimensions=self.dimensions,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
raise ValueError("Could not connect to AzureOpenAIEmbeddings API.") from e
|
||||
|
||||
|
|
|
|||
|
|
@ -1,38 +1,44 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain_community.embeddings.cohere import CohereEmbeddings
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.io import DropdownInput, FloatInput, IntInput, Output, SecretStrInput, TextInput
|
||||
|
||||
|
||||
class CohereEmbeddingsComponent(CustomComponent):
|
||||
class CohereEmbeddingsComponent(LCModelComponent):
|
||||
display_name = "Cohere Embeddings"
|
||||
description = "Generate embeddings using Cohere models."
|
||||
icon = "Cohere"
|
||||
inputs = [
|
||||
SecretStrInput(name="cohere_api_key", display_name="Cohere API Key"),
|
||||
DropdownInput(
|
||||
name="model",
|
||||
display_name="Model",
|
||||
advanced=True,
|
||||
options=[
|
||||
"embed-english-v2.0",
|
||||
"embed-multilingual-v2.0",
|
||||
"embed-english-light-v2.0",
|
||||
"embed-multilingual-light-v2.0",
|
||||
],
|
||||
value="embed-english-v2.0",
|
||||
),
|
||||
TextInput(name="truncate", display_name="Truncate", advanced=True),
|
||||
IntInput(name="max_retries", display_name="Max Retries", value=3, advanced=True),
|
||||
TextInput(name="user_agent", display_name="User Agent", advanced=True, value="langchain"),
|
||||
FloatInput(name="request_timeout", display_name="Request Timeout", advanced=True),
|
||||
]
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"cohere_api_key": {"display_name": "Cohere API Key", "password": True},
|
||||
"model": {"display_name": "Model", "default": "embed-english-v2.0", "advanced": True},
|
||||
"truncate": {"display_name": "Truncate", "advanced": True},
|
||||
"max_retries": {"display_name": "Max Retries", "advanced": True},
|
||||
"user_agent": {"display_name": "User Agent", "advanced": True},
|
||||
"request_timeout": {"display_name": "Request Timeout", "advanced": True},
|
||||
}
|
||||
outputs = [
|
||||
Output(display_name="Embeddings", name="embeddings", method="build_embeddings"),
|
||||
]
|
||||
|
||||
def build(
|
||||
self,
|
||||
request_timeout: Optional[float] = None,
|
||||
cohere_api_key: str = "",
|
||||
max_retries: int = 3,
|
||||
model: str = "embed-english-v2.0",
|
||||
truncate: Optional[str] = None,
|
||||
user_agent: str = "langchain",
|
||||
) -> CohereEmbeddings:
|
||||
def build_embeddings(self) -> Embeddings:
|
||||
return CohereEmbeddings( # type: ignore
|
||||
max_retries=max_retries,
|
||||
user_agent=user_agent,
|
||||
request_timeout=request_timeout,
|
||||
cohere_api_key=cohere_api_key,
|
||||
model=model,
|
||||
truncate=truncate,
|
||||
cohere_api_key=self.cohere_api_key,
|
||||
model=self.model,
|
||||
truncate=self.truncate,
|
||||
max_retries=self.max_retries,
|
||||
user_agent=self.user_agent,
|
||||
request_timeout=self.request_timeout or None,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
from typing import Dict, Optional
|
||||
|
||||
from langchain_community.embeddings.huggingface import HuggingFaceEmbeddings
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.io import BoolInput, DictInput, TextInput, Output
|
||||
|
||||
|
||||
class HuggingFaceEmbeddingsComponent(CustomComponent):
|
||||
class HuggingFaceEmbeddingsComponent(LCModelComponent):
|
||||
display_name = "Hugging Face Embeddings"
|
||||
description = "Generate embeddings using HuggingFace models."
|
||||
documentation = (
|
||||
|
|
@ -13,27 +13,23 @@ class HuggingFaceEmbeddingsComponent(CustomComponent):
|
|||
)
|
||||
icon = "HuggingFace"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"cache_folder": {"display_name": "Cache Folder", "advanced": True},
|
||||
"encode_kwargs": {"display_name": "Encode Kwargs", "advanced": True, "field_type": "dict"},
|
||||
"model_kwargs": {"display_name": "Model Kwargs", "field_type": "dict", "advanced": True},
|
||||
"model_name": {"display_name": "Model Name"},
|
||||
"multi_process": {"display_name": "Multi Process", "advanced": True},
|
||||
}
|
||||
inputs = [
|
||||
TextInput(name="cache_folder", display_name="Cache Folder", advanced=True),
|
||||
DictInput(name="encode_kwargs", display_name="Encode Kwargs", advanced=True),
|
||||
DictInput(name="model_kwargs", display_name="Model Kwargs", advanced=True),
|
||||
TextInput(name="model_name", display_name="Model Name", value="sentence-transformers/all-mpnet-base-v2"),
|
||||
BoolInput(name="multi_process", display_name="Multi Process", advanced=True),
|
||||
]
|
||||
|
||||
def build(
|
||||
self,
|
||||
cache_folder: Optional[str] = None,
|
||||
encode_kwargs: Optional[Dict] = {},
|
||||
model_kwargs: Optional[Dict] = {},
|
||||
model_name: str = "sentence-transformers/all-mpnet-base-v2",
|
||||
multi_process: bool = False,
|
||||
) -> HuggingFaceEmbeddings:
|
||||
outputs = [
|
||||
Output(display_name="Embeddings", name="embeddings", method="build_embeddings"),
|
||||
]
|
||||
|
||||
def build_embeddings(self) -> Embeddings:
|
||||
return HuggingFaceEmbeddings(
|
||||
cache_folder=cache_folder,
|
||||
encode_kwargs=encode_kwargs,
|
||||
model_kwargs=model_kwargs,
|
||||
model_name=model_name,
|
||||
multi_process=multi_process,
|
||||
cache_folder=self.cache_folder,
|
||||
encode_kwargs=self.encode_kwargs,
|
||||
model_kwargs=self.model_kwargs,
|
||||
model_name=self.model_name,
|
||||
multi_process=self.multi_process,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,44 +1,31 @@
|
|||
from typing import Dict, Optional
|
||||
|
||||
from langchain_community.embeddings.huggingface import HuggingFaceInferenceAPIEmbeddings
|
||||
from pydantic.v1.types import SecretStr
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.io import Output, SecretStrInput, TextInput
|
||||
|
||||
|
||||
class HuggingFaceInferenceAPIEmbeddingsComponent(CustomComponent):
|
||||
class HuggingFaceInferenceAPIEmbeddingsComponent(LCModelComponent):
|
||||
display_name = "Hugging Face API Embeddings"
|
||||
description = "Generate embeddings using Hugging Face Inference API models."
|
||||
documentation = "https://github.com/huggingface/text-embeddings-inference"
|
||||
icon = "HuggingFace"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"api_key": {"display_name": "API Key", "password": True, "advanced": True},
|
||||
"api_url": {"display_name": "API URL", "advanced": True},
|
||||
"model_name": {"display_name": "Model Name"},
|
||||
"cache_folder": {"display_name": "Cache Folder", "advanced": True},
|
||||
"encode_kwargs": {"display_name": "Encode Kwargs", "advanced": True, "field_type": "dict"},
|
||||
"model_kwargs": {"display_name": "Model Kwargs", "field_type": "dict", "advanced": True},
|
||||
"multi_process": {"display_name": "Multi Process", "advanced": True},
|
||||
}
|
||||
inputs = [
|
||||
SecretStrInput(name="api_key", display_name="API Key", advanced=True),
|
||||
TextInput(name="api_url", display_name="API URL", advanced=True, value="http://localhost:8080"),
|
||||
TextInput(name="model_name", display_name="Model Name", value="BAAI/bge-large-en-v1.5"),
|
||||
]
|
||||
|
||||
def build(
|
||||
self,
|
||||
api_key: Optional[str] = "",
|
||||
api_url: str = "http://localhost:8080",
|
||||
model_name: str = "BAAI/bge-large-en-v1.5",
|
||||
cache_folder: Optional[str] = None,
|
||||
encode_kwargs: Optional[Dict] = {},
|
||||
model_kwargs: Optional[Dict] = {},
|
||||
multi_process: bool = False,
|
||||
) -> HuggingFaceInferenceAPIEmbeddings:
|
||||
if api_key:
|
||||
secret_api_key = SecretStr(api_key)
|
||||
else:
|
||||
outputs = [
|
||||
Output(display_name="Embeddings", name="embeddings", method="build_embeddings"),
|
||||
]
|
||||
|
||||
def build_embeddings(self) -> Embeddings:
|
||||
if not self.api_key:
|
||||
raise ValueError("API Key is required")
|
||||
return HuggingFaceInferenceAPIEmbeddings(
|
||||
api_key=secret_api_key,
|
||||
api_url=api_url,
|
||||
model_name=model_name,
|
||||
)
|
||||
|
||||
api_key = SecretStr(self.api_key)
|
||||
|
||||
return HuggingFaceInferenceAPIEmbeddings(api_key=api_key, api_url=self.api_url, model_name=self.model_name)
|
||||
|
|
|
|||
|
|
@ -1,64 +1,56 @@
|
|||
from langchain_mistralai.embeddings import MistralAIEmbeddings
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.io import DropdownInput, IntInput, Output, SecretStrInput, TextInput
|
||||
|
||||
|
||||
class MistralAIEmbeddingsComponent(CustomComponent):
|
||||
class MistralAIEmbeddingsComponent(LCModelComponent):
|
||||
display_name = "MistralAI Embeddings"
|
||||
description = "Generate embeddings using MistralAI models."
|
||||
icon = "MistralAI"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"model": {
|
||||
"display_name": "Model",
|
||||
"advanced": False,
|
||||
"options": ["mistral-embed"],
|
||||
"value": "mistral-embed",
|
||||
},
|
||||
"mistral_api_key": {
|
||||
"display_name": "Mistral API Key",
|
||||
"password": True,
|
||||
"advanced": False,
|
||||
},
|
||||
"max_concurrent_requests": {
|
||||
"display_name": "Max Concurrent Requests",
|
||||
"advanced": True,
|
||||
"value": 64,
|
||||
},
|
||||
"max_retries": {
|
||||
"display_name": "Max Retries",
|
||||
"advanced": True,
|
||||
"value": 5,
|
||||
},
|
||||
"timeout": {
|
||||
"display_name": "Request Timeout",
|
||||
"advanced": True,
|
||||
"value": 120,
|
||||
},
|
||||
"endpoint": {"display_name": "API Endpoint", "advanced": True, "value": "https://api.mistral.ai/v1/"},
|
||||
}
|
||||
inputs = [
|
||||
DropdownInput(
|
||||
name="model",
|
||||
display_name="Model",
|
||||
advanced=False,
|
||||
options=["mistral-embed"],
|
||||
value="mistral-embed",
|
||||
),
|
||||
SecretStrInput(name="mistral_api_key", display_name="Mistral API Key"),
|
||||
IntInput(
|
||||
name="max_concurrent_requests",
|
||||
display_name="Max Concurrent Requests",
|
||||
advanced=True,
|
||||
value=64,
|
||||
),
|
||||
IntInput(name="max_retries", display_name="Max Retries", advanced=True, value=5),
|
||||
IntInput(name="timeout", display_name="Request Timeout", advanced=True, value=120),
|
||||
TextInput(
|
||||
name="endpoint",
|
||||
display_name="API Endpoint",
|
||||
advanced=True,
|
||||
value="https://api.mistral.ai/v1/",
|
||||
),
|
||||
]
|
||||
|
||||
def build(
|
||||
self,
|
||||
mistral_api_key: str,
|
||||
model: str = "mistral-embed",
|
||||
max_concurrent_requests: int = 64,
|
||||
max_retries: int = 5,
|
||||
timeout: int = 120,
|
||||
endpoint: str = "https://api.mistral.ai/v1/",
|
||||
) -> Embeddings:
|
||||
if mistral_api_key:
|
||||
api_key = SecretStr(mistral_api_key)
|
||||
else:
|
||||
api_key = None
|
||||
outputs = [
|
||||
Output(display_name="Embeddings", name="embeddings", method="build_embeddings"),
|
||||
]
|
||||
|
||||
def build_embeddings(self) -> Embeddings:
|
||||
if not self.mistral_api_key:
|
||||
raise ValueError("Mistral API Key is required")
|
||||
|
||||
api_key = SecretStr(self.mistral_api_key)
|
||||
|
||||
return MistralAIEmbeddings(
|
||||
api_key=api_key,
|
||||
model=model,
|
||||
endpoint=endpoint,
|
||||
max_concurrent_requests=max_concurrent_requests,
|
||||
max_retries=max_retries,
|
||||
timeout=timeout,
|
||||
model=self.model,
|
||||
endpoint=self.endpoint,
|
||||
max_concurrent_requests=self.max_concurrent_requests,
|
||||
max_retries=self.max_retries,
|
||||
timeout=self.timeout,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,34 +1,44 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain_community.embeddings import OllamaEmbeddings
|
||||
from langchain_core.embeddings import Embeddings
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.io import FloatInput, Output, TextInput
|
||||
|
||||
|
||||
class OllamaEmbeddingsComponent(CustomComponent):
|
||||
class OllamaEmbeddingsComponent(LCModelComponent):
|
||||
display_name: str = "Ollama Embeddings"
|
||||
description: str = "Generate embeddings using Ollama models."
|
||||
documentation = "https://python.langchain.com/docs/integrations/text_embedding/ollama"
|
||||
icon = "Ollama"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"model": {
|
||||
"display_name": "Ollama Model",
|
||||
},
|
||||
"base_url": {"display_name": "Ollama Base URL"},
|
||||
"temperature": {"display_name": "Model Temperature"},
|
||||
"code": {"show": False},
|
||||
}
|
||||
inputs = [
|
||||
TextInput(
|
||||
name="model",
|
||||
display_name="Ollama Model",
|
||||
value="llama2",
|
||||
),
|
||||
TextInput(
|
||||
name="base_url",
|
||||
display_name="Ollama Base URL",
|
||||
value="http://localhost:11434",
|
||||
),
|
||||
FloatInput(
|
||||
name="temperature",
|
||||
display_name="Model Temperature",
|
||||
advanced=True,
|
||||
),
|
||||
]
|
||||
|
||||
def build(
|
||||
self,
|
||||
model: str = "llama2",
|
||||
base_url: str = "http://localhost:11434",
|
||||
temperature: Optional[float] = None,
|
||||
) -> Embeddings:
|
||||
outputs = [
|
||||
Output(display_name="Embeddings", name="embeddings", method="build_embeddings"),
|
||||
]
|
||||
|
||||
def build_embeddings(self) -> Embeddings:
|
||||
try:
|
||||
output = OllamaEmbeddings(model=model, base_url=base_url, temperature=temperature) # type: ignore
|
||||
output = OllamaEmbeddings(
|
||||
model=self.model,
|
||||
base_url=self.base_url,
|
||||
temperature=self.temperature,
|
||||
) # type: ignore
|
||||
except Exception as e:
|
||||
raise ValueError("Could not connect to Ollama API.") from e
|
||||
return output
|
||||
|
|
|
|||
|
|
@ -1,150 +1,96 @@
|
|||
from typing import Dict, List, Optional
|
||||
|
||||
from langchain_openai.embeddings.base import OpenAIEmbeddings
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Embeddings, NestedDict
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.io import BoolInput, DictInput, DropdownInput, FloatInput, IntInput, Output, SecretStrInput, TextInput
|
||||
|
||||
|
||||
class OpenAIEmbeddingsComponent(CustomComponent):
|
||||
class OpenAIEmbeddingsComponent(LCModelComponent):
|
||||
display_name = "OpenAI Embeddings"
|
||||
description = "Generate embeddings using OpenAI models."
|
||||
icon = "OpenAI"
|
||||
inputs = [
|
||||
DictInput(
|
||||
name="default_headers",
|
||||
display_name="Default Headers",
|
||||
advanced=True,
|
||||
info="Default headers to use for the API request.",
|
||||
),
|
||||
DictInput(
|
||||
name="default_query",
|
||||
display_name="Default Query",
|
||||
advanced=True,
|
||||
info="Default query parameters to use for the API request.",
|
||||
),
|
||||
IntInput(name="chunk_size", display_name="Chunk Size", advanced=True, value=1000),
|
||||
TextInput(name="client", display_name="Client", advanced=True),
|
||||
TextInput(name="deployment", display_name="Deployment", advanced=True),
|
||||
IntInput(name="embedding_ctx_length", display_name="Embedding Context Length", advanced=True, value=1536),
|
||||
IntInput(name="max_retries", display_name="Max Retries", value=3, advanced=True),
|
||||
DropdownInput(
|
||||
name="model",
|
||||
display_name="Model",
|
||||
advanced=False,
|
||||
options=[
|
||||
"text-embedding-3-small",
|
||||
"text-embedding-3-large",
|
||||
"text-embedding-ada-002",
|
||||
],
|
||||
value="text-embedding-3-small",
|
||||
),
|
||||
DictInput(name="model_kwargs", display_name="Model Kwargs", advanced=True),
|
||||
SecretStrInput(name="openai_api_base", display_name="OpenAI API Base", advanced=True),
|
||||
SecretStrInput(name="openai_api_key", display_name="OpenAI API Key"),
|
||||
SecretStrInput(name="openai_api_type", display_name="OpenAI API Type", advanced=True),
|
||||
TextInput(name="openai_api_version", display_name="OpenAI API Version", advanced=True),
|
||||
TextInput(
|
||||
name="openai_organization",
|
||||
display_name="OpenAI Organization",
|
||||
advanced=True,
|
||||
),
|
||||
TextInput(name="openai_proxy", display_name="OpenAI Proxy", advanced=True),
|
||||
FloatInput(name="request_timeout", display_name="Request Timeout", advanced=True),
|
||||
BoolInput(name="show_progress_bar", display_name="Show Progress Bar", advanced=True),
|
||||
BoolInput(name="skip_empty", display_name="Skip Empty", advanced=True),
|
||||
TextInput(
|
||||
name="tiktoken_model_name",
|
||||
display_name="TikToken Model Name",
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="tiktoken_enable",
|
||||
display_name="TikToken Enable",
|
||||
advanced=True,
|
||||
value=True,
|
||||
info="If False, you must have transformers installed.",
|
||||
),
|
||||
]
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"allowed_special": {
|
||||
"display_name": "Allowed Special",
|
||||
"advanced": True,
|
||||
"field_type": "str",
|
||||
"is_list": True,
|
||||
},
|
||||
"default_headers": {
|
||||
"display_name": "Default Headers",
|
||||
"advanced": True,
|
||||
"field_type": "dict",
|
||||
},
|
||||
"default_query": {
|
||||
"display_name": "Default Query",
|
||||
"advanced": True,
|
||||
"field_type": "NestedDict",
|
||||
},
|
||||
"disallowed_special": {
|
||||
"display_name": "Disallowed Special",
|
||||
"advanced": True,
|
||||
"field_type": "str",
|
||||
"is_list": True,
|
||||
},
|
||||
"chunk_size": {"display_name": "Chunk Size", "advanced": True},
|
||||
"client": {"display_name": "Client", "advanced": True},
|
||||
"deployment": {"display_name": "Deployment", "advanced": True},
|
||||
"embedding_ctx_length": {
|
||||
"display_name": "Embedding Context Length",
|
||||
"advanced": True,
|
||||
},
|
||||
"max_retries": {"display_name": "Max Retries", "advanced": True},
|
||||
"model": {
|
||||
"display_name": "Model",
|
||||
"advanced": False,
|
||||
"options": [
|
||||
"text-embedding-3-small",
|
||||
"text-embedding-3-large",
|
||||
"text-embedding-ada-002",
|
||||
],
|
||||
},
|
||||
"model_kwargs": {"display_name": "Model Kwargs", "advanced": True},
|
||||
"openai_api_base": {
|
||||
"display_name": "OpenAI API Base",
|
||||
"password": True,
|
||||
"advanced": True,
|
||||
},
|
||||
"openai_api_key": {"display_name": "OpenAI API Key", "password": True},
|
||||
"openai_api_type": {
|
||||
"display_name": "OpenAI API Type",
|
||||
"advanced": True,
|
||||
"password": True,
|
||||
},
|
||||
"openai_api_version": {
|
||||
"display_name": "OpenAI API Version",
|
||||
"advanced": True,
|
||||
},
|
||||
"openai_organization": {
|
||||
"display_name": "OpenAI Organization",
|
||||
"advanced": True,
|
||||
},
|
||||
"openai_proxy": {"display_name": "OpenAI Proxy", "advanced": True},
|
||||
"request_timeout": {"display_name": "Request Timeout", "advanced": True},
|
||||
"show_progress_bar": {
|
||||
"display_name": "Show Progress Bar",
|
||||
"advanced": True,
|
||||
},
|
||||
"skip_empty": {"display_name": "Skip Empty", "advanced": True},
|
||||
"tiktoken_model_name": {
|
||||
"display_name": "TikToken Model Name",
|
||||
"advanced": True,
|
||||
},
|
||||
"tiktoken_enable": {"display_name": "TikToken Enable", "advanced": True},
|
||||
"dimensions": {
|
||||
"display_name": "Dimensions",
|
||||
"info": "The number of dimensions the resulting output embeddings should have. Only supported by certain models.",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
openai_api_key: str,
|
||||
default_headers: Optional[Dict[str, str]] = None,
|
||||
default_query: Optional[NestedDict] = {},
|
||||
allowed_special: List[str] = [],
|
||||
disallowed_special: List[str] = ["all"],
|
||||
chunk_size: int = 1000,
|
||||
deployment: str = "text-embedding-ada-002",
|
||||
embedding_ctx_length: int = 8191,
|
||||
max_retries: int = 6,
|
||||
model: str = "text-embedding-ada-002",
|
||||
model_kwargs: NestedDict = {},
|
||||
openai_api_base: Optional[str] = None,
|
||||
openai_api_type: Optional[str] = None,
|
||||
openai_api_version: Optional[str] = None,
|
||||
openai_organization: Optional[str] = None,
|
||||
openai_proxy: Optional[str] = None,
|
||||
request_timeout: Optional[float] = None,
|
||||
show_progress_bar: bool = False,
|
||||
skip_empty: bool = False,
|
||||
tiktoken_enable: bool = True,
|
||||
tiktoken_model_name: Optional[str] = None,
|
||||
dimensions: Optional[int] = None,
|
||||
) -> Embeddings:
|
||||
# This is to avoid errors with Vector Stores (e.g Chroma)
|
||||
if disallowed_special == ["all"]:
|
||||
disallowed_special = "all" # type: ignore
|
||||
if openai_api_key:
|
||||
api_key = SecretStr(openai_api_key)
|
||||
else:
|
||||
api_key = None
|
||||
outputs = [
|
||||
Output(display_name="Embeddings", name="embeddings", method="build_embeddings"),
|
||||
]
|
||||
|
||||
def build_embeddings(self) -> Embeddings:
|
||||
return OpenAIEmbeddings(
|
||||
tiktoken_enabled=tiktoken_enable,
|
||||
default_headers=default_headers,
|
||||
default_query=default_query,
|
||||
allowed_special=set(allowed_special),
|
||||
tiktoken_enabled=self.tiktoken_enable,
|
||||
default_headers=self.default_headers,
|
||||
default_query=self.default_query,
|
||||
allowed_special="all",
|
||||
disallowed_special="all",
|
||||
chunk_size=chunk_size,
|
||||
deployment=deployment,
|
||||
embedding_ctx_length=embedding_ctx_length,
|
||||
max_retries=max_retries,
|
||||
model=model,
|
||||
model_kwargs=model_kwargs,
|
||||
base_url=openai_api_base,
|
||||
api_key=api_key,
|
||||
openai_api_type=openai_api_type,
|
||||
api_version=openai_api_version,
|
||||
organization=openai_organization,
|
||||
openai_proxy=openai_proxy,
|
||||
timeout=request_timeout,
|
||||
show_progress_bar=show_progress_bar,
|
||||
skip_empty=skip_empty,
|
||||
tiktoken_model_name=tiktoken_model_name,
|
||||
dimensions=dimensions,
|
||||
chunk_size=self.chunk_size,
|
||||
deployment=self.deployment,
|
||||
embedding_ctx_length=self.embedding_ctx_length,
|
||||
max_retries=self.max_retries,
|
||||
model=self.model,
|
||||
model_kwargs=self.model_kwargs,
|
||||
base_url=self.openai_api_base,
|
||||
api_key=self.openai_api_key,
|
||||
openai_api_type=self.openai_api_type,
|
||||
api_version=self.openai_api_version,
|
||||
organization=self.openai_organization,
|
||||
openai_proxy=self.openai_proxy,
|
||||
timeout=self.request_timeout or None,
|
||||
show_progress_bar=self.show_progress_bar,
|
||||
skip_empty=self.skip_empty,
|
||||
tiktoken_model_name=self.tiktoken_model_name,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,90 +1,119 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from langchain_google_vertexai import VertexAIEmbeddings
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.io import BoolInput, DictInput, FileInput, FloatInput, IntInput, Output, TextInput
|
||||
|
||||
|
||||
class VertexAIEmbeddingsComponent(CustomComponent):
|
||||
class VertexAIEmbeddingsComponent(LCModelComponent):
|
||||
display_name = "VertexAI Embeddings"
|
||||
description = "Generate embeddings using Google Cloud VertexAI models."
|
||||
icon = "VertexAI"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"credentials": {
|
||||
"display_name": "Credentials",
|
||||
"value": "",
|
||||
"file_types": [".json"],
|
||||
"field_type": "file",
|
||||
},
|
||||
"instance": {
|
||||
"display_name": "instance",
|
||||
"advanced": True,
|
||||
"field_type": "dict",
|
||||
},
|
||||
"location": {
|
||||
"display_name": "Location",
|
||||
"value": "us-central1",
|
||||
"advanced": True,
|
||||
},
|
||||
"max_output_tokens": {"display_name": "Max Output Tokens", "value": 128},
|
||||
"max_retries": {
|
||||
"display_name": "Max Retries",
|
||||
"value": 6,
|
||||
"advanced": True,
|
||||
},
|
||||
"model_name": {
|
||||
"display_name": "Model Name",
|
||||
"value": "textembedding-gecko",
|
||||
},
|
||||
"n": {"display_name": "N", "value": 1, "advanced": True},
|
||||
"project": {"display_name": "Project", "advanced": True},
|
||||
"request_parallelism": {
|
||||
"display_name": "Request Parallelism",
|
||||
"value": 5,
|
||||
"advanced": True,
|
||||
},
|
||||
"stop": {"display_name": "Stop", "advanced": True},
|
||||
"streaming": {
|
||||
"display_name": "Streaming",
|
||||
"value": False,
|
||||
"advanced": True,
|
||||
},
|
||||
"temperature": {"display_name": "Temperature", "value": 0.0},
|
||||
"top_k": {"display_name": "Top K", "value": 40, "advanced": True},
|
||||
"top_p": {"display_name": "Top P", "value": 0.95, "advanced": True},
|
||||
}
|
||||
inputs = [
|
||||
FileInput(
|
||||
name="credentials",
|
||||
display_name="Credentials",
|
||||
value="",
|
||||
file_types=["json"], # Removed the dot
|
||||
),
|
||||
DictInput(
|
||||
name="instance",
|
||||
display_name="Instance",
|
||||
advanced=True,
|
||||
),
|
||||
TextInput(
|
||||
name="location",
|
||||
display_name="Location",
|
||||
value="us-central1",
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="max_output_tokens",
|
||||
display_name="Max Output Tokens",
|
||||
value=128,
|
||||
),
|
||||
IntInput(
|
||||
name="max_retries",
|
||||
display_name="Max Retries",
|
||||
value=6,
|
||||
advanced=True,
|
||||
),
|
||||
TextInput(
|
||||
name="model_name",
|
||||
display_name="Model Name",
|
||||
value="textembedding-gecko",
|
||||
),
|
||||
IntInput(
|
||||
name="n",
|
||||
display_name="N",
|
||||
value=1,
|
||||
advanced=True,
|
||||
),
|
||||
TextInput(
|
||||
name="project",
|
||||
display_name="Project",
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="request_parallelism",
|
||||
display_name="Request Parallelism",
|
||||
value=5,
|
||||
advanced=True,
|
||||
),
|
||||
TextInput(
|
||||
name="stop",
|
||||
display_name="Stop",
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="streaming",
|
||||
display_name="Streaming",
|
||||
value=False,
|
||||
advanced=True,
|
||||
),
|
||||
FloatInput(
|
||||
name="temperature",
|
||||
display_name="Temperature",
|
||||
value=0.0,
|
||||
),
|
||||
IntInput(
|
||||
name="top_k",
|
||||
display_name="Top K",
|
||||
value=40,
|
||||
advanced=True,
|
||||
),
|
||||
FloatInput(
|
||||
name="top_p",
|
||||
display_name="Top P",
|
||||
value=0.95,
|
||||
advanced=True,
|
||||
),
|
||||
]
|
||||
|
||||
outputs = [
|
||||
Output(display_name="Embeddings", name="embeddings", method="build_embeddings"),
|
||||
]
|
||||
|
||||
def build_embeddings(self) -> Embeddings:
|
||||
try:
|
||||
from langchain_google_vertexai import VertexAIEmbeddings
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Please install the langchain-google-vertexai package to use the VertexAIEmbeddings component."
|
||||
)
|
||||
|
||||
def build(
|
||||
self,
|
||||
instance: Optional[str] = None,
|
||||
credentials: Optional[str] = None,
|
||||
location: str = "us-central1",
|
||||
max_output_tokens: int = 128,
|
||||
max_retries: int = 6,
|
||||
model_name: str = "textembedding-gecko",
|
||||
n: int = 1,
|
||||
project: Optional[str] = None,
|
||||
request_parallelism: int = 5,
|
||||
stop: Optional[List[str]] = None,
|
||||
streaming: bool = False,
|
||||
temperature: float = 0.0,
|
||||
top_k: int = 40,
|
||||
top_p: float = 0.95,
|
||||
) -> VertexAIEmbeddings:
|
||||
return VertexAIEmbeddings(
|
||||
instance=instance,
|
||||
credentials=credentials,
|
||||
location=location,
|
||||
max_output_tokens=max_output_tokens,
|
||||
max_retries=max_retries,
|
||||
model_name=model_name,
|
||||
n=n,
|
||||
project=project,
|
||||
request_parallelism=request_parallelism,
|
||||
stop=stop,
|
||||
streaming=streaming,
|
||||
temperature=temperature,
|
||||
top_k=top_k,
|
||||
top_p=top_p,
|
||||
instance=self.instance,
|
||||
credentials=self.credentials,
|
||||
location=self.location,
|
||||
max_output_tokens=self.max_output_tokens,
|
||||
max_retries=self.max_retries,
|
||||
model_name=self.model_name,
|
||||
n=self.n,
|
||||
project=self.project,
|
||||
request_parallelism=self.request_parallelism,
|
||||
stop=self.stop,
|
||||
streaming=self.streaming,
|
||||
temperature=self.temperature,
|
||||
top_k=self.top_k,
|
||||
top_p=self.top_p,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from .AmazonBedrockEmbeddings import AmazonBedrockEmeddingsComponent
|
||||
from .AmazonBedrockEmbeddings import AmazonBedrockEmbeddingsComponent
|
||||
from .AzureOpenAIEmbeddings import AzureOpenAIEmbeddingsComponent
|
||||
from .CohereEmbeddings import CohereEmbeddingsComponent
|
||||
from .HuggingFaceEmbeddings import HuggingFaceEmbeddingsComponent
|
||||
|
|
@ -8,7 +8,7 @@ from .OpenAIEmbeddings import OpenAIEmbeddingsComponent
|
|||
from .VertexAIEmbeddings import VertexAIEmbeddingsComponent
|
||||
|
||||
__all__ = [
|
||||
"AmazonBedrockEmeddingsComponent",
|
||||
"AmazonBedrockEmbeddingsComponent",
|
||||
"AzureOpenAIEmbeddingsComponent",
|
||||
"CohereEmbeddingsComponent",
|
||||
"HuggingFaceEmbeddingsComponent",
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ from langchain_core.prompts.chat import HumanMessagePromptTemplate, SystemMessag
|
|||
|
||||
from langflow.base.agents.agent import LCAgentComponent
|
||||
from langflow.base.agents.utils import AGENTS, AgentSpec, get_agents_list
|
||||
from langflow.field_typing import BaseLanguageModel, Text, Tool
|
||||
from langflow.schema import Record
|
||||
from langflow.field_typing import LanguageModel, Text, Tool
|
||||
from langflow.schema import Data
|
||||
from langflow.schema.dotdict import dotdict
|
||||
|
||||
|
||||
|
|
@ -145,11 +145,11 @@ class AgentComponent(LCAgentComponent):
|
|||
self,
|
||||
agent_name: str,
|
||||
input_value: str,
|
||||
llm: BaseLanguageModel,
|
||||
llm: LanguageModel,
|
||||
tools: List[Tool],
|
||||
system_message: str = "You are a helpful assistant. Help the user answer any questions.",
|
||||
user_prompt: str = "{input}",
|
||||
message_history: Optional[List[Record]] = None,
|
||||
message_history: Optional[List[Data]] = None,
|
||||
tool_template: str = "{name}: {description}",
|
||||
handle_parsing_errors: bool = True,
|
||||
) -> Text:
|
||||
|
|
|
|||
|
|
@ -21,6 +21,6 @@ class ClearMessageHistoryComponent(CustomComponent):
|
|||
session_id: str,
|
||||
) -> None:
|
||||
delete_messages(session_id=session_id)
|
||||
records = get_messages(session_id=session_id)
|
||||
self.records = records
|
||||
return records
|
||||
data = get_messages(session_id=session_id)
|
||||
self.data = data
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
from langflow.custom import Component
|
||||
from langflow.io import BoolInput, DropdownInput, MessageInput, Output, TextInput
|
||||
from langflow.schema.message import Message
|
||||
|
||||
|
||||
class ConditionalRouterComponent(Component):
|
||||
display_name = "Conditional Router"
|
||||
description = "Routes an input message to a corresponding output based on text comparison."
|
||||
icon = "equal"
|
||||
|
||||
inputs = [
|
||||
TextInput(
|
||||
name="input_text",
|
||||
display_name="Input Text",
|
||||
info="The primary text input for the operation.",
|
||||
),
|
||||
TextInput(
|
||||
name="match_text",
|
||||
display_name="Match Text",
|
||||
info="The text input to compare against.",
|
||||
),
|
||||
DropdownInput(
|
||||
name="operator",
|
||||
display_name="Operator",
|
||||
options=["equals", "not equals", "contains", "starts with", "ends with"],
|
||||
info="The operator to apply for comparing the texts.",
|
||||
value="equals",
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="case_sensitive",
|
||||
display_name="Case Sensitive",
|
||||
info="If true, the comparison will be case sensitive.",
|
||||
value=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageInput(
|
||||
name="message",
|
||||
display_name="Message",
|
||||
info="The message to pass through either route.",
|
||||
),
|
||||
]
|
||||
|
||||
outputs = [
|
||||
Output(display_name="True Route", name="true_result", method="true_response"),
|
||||
Output(display_name="False Route", name="false_result", method="false_response"),
|
||||
]
|
||||
|
||||
def evaluate_condition(self, input_text: str, match_text: str, operator: str, case_sensitive: bool) -> bool:
|
||||
if not case_sensitive:
|
||||
input_text = input_text.lower()
|
||||
match_text = match_text.lower()
|
||||
|
||||
if operator == "equals":
|
||||
return input_text == match_text
|
||||
elif operator == "not equals":
|
||||
return input_text != match_text
|
||||
elif operator == "contains":
|
||||
return match_text in input_text
|
||||
elif operator == "starts with":
|
||||
return input_text.startswith(match_text)
|
||||
elif operator == "ends with":
|
||||
return input_text.endswith(match_text)
|
||||
return False
|
||||
|
||||
def true_response(self) -> Message:
|
||||
result = self.evaluate_condition(self.input_text, self.match_text, self.operator, self.case_sensitive)
|
||||
if result:
|
||||
self.stop("false_result")
|
||||
response = self.message if self.message else self.input_text
|
||||
self.status = response
|
||||
return response
|
||||
else:
|
||||
self.stop("true_result")
|
||||
return Message()
|
||||
|
||||
def false_response(self) -> Message:
|
||||
result = self.evaluate_condition(self.input_text, self.match_text, self.operator, self.case_sensitive)
|
||||
if not result:
|
||||
self.stop("true_result")
|
||||
response = self.message if self.message else self.input_text
|
||||
self.status = response
|
||||
return response
|
||||
else:
|
||||
self.stop("false_result")
|
||||
return Message()
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Record
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class EmbedComponent(CustomComponent):
|
||||
|
|
@ -9,7 +9,7 @@ class EmbedComponent(CustomComponent):
|
|||
def build_config(self):
|
||||
return {"texts": {"display_name": "Texts"}, "embbedings": {"display_name": "Embeddings"}}
|
||||
|
||||
def build(self, texts: list[str], embbedings: Embeddings) -> Record:
|
||||
vectors = Record(vector=embbedings.embed_documents(texts))
|
||||
def build(self, texts: list[str], embbedings: Embeddings) -> Data:
|
||||
vectors = Data(vector=embbedings.embed_documents(texts))
|
||||
self.status = vectors
|
||||
return vectors
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class ExtractKeyFromRecordComponent(CustomComponent):
|
||||
display_name = "Extract Key From Record"
|
||||
description = "Extracts a key from a record."
|
||||
beta: bool = True
|
||||
|
||||
field_config = {
|
||||
"record": {"display_name": "Record"},
|
||||
"keys": {
|
||||
"display_name": "Keys",
|
||||
"info": "The keys to extract from the record.",
|
||||
"input_types": [],
|
||||
},
|
||||
"silent_error": {
|
||||
"display_name": "Silent Errors",
|
||||
"info": "If True, errors will not be raised.",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def build(self, record: Record, keys: list[str], silent_error: bool = True) -> Record:
|
||||
"""
|
||||
Extracts the keys from a record.
|
||||
|
||||
Args:
|
||||
record (Record): The record from which to extract the keys.
|
||||
keys (list[str]): The keys to extract from the record.
|
||||
silent_error (bool): If True, errors will not be raised.
|
||||
|
||||
Returns:
|
||||
dict: The extracted keys.
|
||||
"""
|
||||
extracted_keys = {}
|
||||
for key in keys:
|
||||
try:
|
||||
extracted_keys[key] = getattr(record, key)
|
||||
except AttributeError:
|
||||
if not silent_error:
|
||||
raise KeyError(f"The key '{key}' does not exist in the record.")
|
||||
return_record = Record(data=extracted_keys)
|
||||
self.status = return_record
|
||||
return return_record
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class ExtractKeyFromDataComponent(CustomComponent):
|
||||
display_name = "Extract Key From Data"
|
||||
description = "Extracts a key from a data."
|
||||
beta: bool = True
|
||||
|
||||
field_config = {
|
||||
"data": {"display_name": "Data"},
|
||||
"keys": {
|
||||
"display_name": "Keys",
|
||||
"info": "The keys to extract from the data.",
|
||||
"input_types": [],
|
||||
},
|
||||
"silent_error": {
|
||||
"display_name": "Silent Errors",
|
||||
"info": "If True, errors will not be raised.",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def build(self, data: Data, keys: list[str], silent_error: bool = True) -> Data:
|
||||
"""
|
||||
Extracts the keys from a data.
|
||||
|
||||
Args:
|
||||
data (Data): The data from which to extract the keys.
|
||||
keys (list[str]): The keys to extract from the data.
|
||||
silent_error (bool): If True, errors will not be raised.
|
||||
|
||||
Returns:
|
||||
dict: The extracted keys.
|
||||
"""
|
||||
extracted_keys = {}
|
||||
for key in keys:
|
||||
try:
|
||||
extracted_keys[key] = getattr(data, key)
|
||||
except AttributeError:
|
||||
if not silent_error:
|
||||
raise KeyError(f"The key '{key}' does not exist in the data.")
|
||||
return_data = Data(data=extracted_keys)
|
||||
self.status = return_data
|
||||
return return_data
|
||||
|
|
@ -7,7 +7,7 @@ from langflow.custom import CustomComponent
|
|||
from langflow.field_typing import Tool
|
||||
from langflow.graph.graph.base import Graph
|
||||
from langflow.helpers.flow import get_flow_inputs
|
||||
from langflow.schema import Record
|
||||
from langflow.schema import Data
|
||||
from langflow.schema.dotdict import dotdict
|
||||
|
||||
|
||||
|
|
@ -17,10 +17,10 @@ class FlowToolComponent(CustomComponent):
|
|||
field_order = ["flow_name", "name", "description", "return_direct"]
|
||||
|
||||
def get_flow_names(self) -> List[str]:
|
||||
flow_records = self.list_flows()
|
||||
return [flow_record.data["name"] for flow_record in flow_records]
|
||||
flow_datas = self.list_flows()
|
||||
return [flow_data.data["name"] for flow_data in flow_datas]
|
||||
|
||||
def get_flow(self, flow_name: str) -> Optional[Record]:
|
||||
def get_flow(self, flow_name: str) -> Optional[Data]:
|
||||
"""
|
||||
Retrieves a flow by its name.
|
||||
|
||||
|
|
@ -30,10 +30,10 @@ class FlowToolComponent(CustomComponent):
|
|||
Returns:
|
||||
Optional[Text]: The flow record if found, None otherwise.
|
||||
"""
|
||||
flow_records = self.list_flows()
|
||||
for flow_record in flow_records:
|
||||
if flow_record.data["name"] == flow_name:
|
||||
return flow_record
|
||||
flow_datas = self.list_flows()
|
||||
for flow_data in flow_datas:
|
||||
if flow_data.data["name"] == flow_name:
|
||||
return flow_data
|
||||
return None
|
||||
|
||||
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None):
|
||||
|
|
@ -69,10 +69,10 @@ class FlowToolComponent(CustomComponent):
|
|||
|
||||
async def build(self, flow_name: str, name: str, description: str, return_direct: bool = False) -> Tool:
|
||||
FlowTool.update_forward_refs()
|
||||
flow_record = self.get_flow(flow_name)
|
||||
if not flow_record:
|
||||
flow_data = self.get_flow(flow_name)
|
||||
if not flow_data:
|
||||
raise ValueError("Flow not found.")
|
||||
graph = Graph.from_payload(flow_record.data["data"])
|
||||
graph = Graph.from_payload(flow_data.data["data"])
|
||||
inputs = get_flow_inputs(graph)
|
||||
tool = FlowTool(
|
||||
name=name,
|
||||
|
|
@ -80,7 +80,7 @@ class FlowToolComponent(CustomComponent):
|
|||
graph=graph,
|
||||
return_direct=return_direct,
|
||||
inputs=inputs,
|
||||
flow_id=str(flow_record.id),
|
||||
flow_id=str(flow_data.id),
|
||||
user_id=str(self._user_id),
|
||||
)
|
||||
description_repr = repr(tool.description).strip("'")
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from typing import List
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Record
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class ListFlowsComponent(CustomComponent):
|
||||
|
|
@ -15,7 +15,7 @@ class ListFlowsComponent(CustomComponent):
|
|||
|
||||
def build(
|
||||
self,
|
||||
) -> List[Record]:
|
||||
) -> List[Data]:
|
||||
flows = self.list_flows()
|
||||
self.status = flows
|
||||
return flows
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Record
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class ListenComponent(CustomComponent):
|
||||
|
|
@ -15,7 +15,7 @@ class ListenComponent(CustomComponent):
|
|||
},
|
||||
}
|
||||
|
||||
def build(self, name: str) -> Record:
|
||||
def build(self, name: str) -> Data:
|
||||
state = self.get_state(name)
|
||||
self.status = state
|
||||
return state
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class MergeDataComponent(CustomComponent):
|
||||
display_name = "Merge Data"
|
||||
description = "Merges data."
|
||||
beta: bool = True
|
||||
|
||||
field_config = {
|
||||
"data": {"display_name": "Data"},
|
||||
}
|
||||
|
||||
def build(self, data: list[Data]) -> Data:
|
||||
if not data:
|
||||
return Data()
|
||||
if len(data) == 1:
|
||||
return data[0]
|
||||
merged_data = Data()
|
||||
for value in data:
|
||||
if merged_data is None:
|
||||
merged_data = value
|
||||
else:
|
||||
merged_data += value
|
||||
self.status = merged_data
|
||||
return merged_data
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
data = [
|
||||
Data(data={"key1": "value1"}),
|
||||
Data(data={"key2": "value2"}),
|
||||
]
|
||||
component = MergeDataComponent()
|
||||
result = component.build(data)
|
||||
print(result)
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue