Refactor "created_at" column type for consistency and fix cancel middleware (#2316) (#2320)

* chore: update linting workflows to include dev branch in merge_group

* Update README.md

Add 1.0 banner

* Update README.md

* chore: update package versions in pyproject.toml files

* Refactor "created_at" column type for consistency and fix cancel middleware (#2316)

* chore: update linting workflows to include dev branch in merge_group

* Update README.md

Add 1.0 banner

* Update README.md

* chore: update package versions in pyproject.toml files

* refactor: update "created_at" column type to use the "sa" module for consistency

* Update README.md

Add 1.0 banner

* chore: Remove unused import in ToolCallingAgent.py

* fix: adapt RequestCancelledMiddleware to handle cancelled requests

* chore: Remove unused import in test_helper_components.py

* refactor: Declare queue variable with explicit type in RequestCancelledMiddleware

---------

Co-authored-by: Rodrigo Nader <rodrigosilvanader@gmail.com>

* chore: Update AstraDB.py imports and method signature for search_documents

* chore: Update package versions in pyproject.toml files

* chore: Update run-name in release.yml for Langflow Release

* fix: add call to _add_documents_to_vector_store in AstraDB component

---------

Co-authored-by: Rodrigo Nader <rodrigosilvanader@gmail.com>
This commit is contained in:
Gabriel Luiz Freitas Almeida 2024-06-24 13:15:54 -07:00 • committed by GitHub
commit d85657f214
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 126 additions and 79 deletions

View file

@ -1,5 +1,6 @@
name: Langflow Release name: Langflow Release
run-name: Langflow (${{inputs.release_type}}) Release by @${{ github.actor }} run-name: Langflow Release by @${{ github.actor }}
on: on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
@ -8,25 +9,23 @@ on:
required: true required: true
type: boolean type: boolean
default: false default: false
release_type:
description: "Type of release (base or main)"
required: true
type: choice
options:
- base
- main
pre_release: pre_release:
description: "Pre-release" description: "Pre-release"
required: false required: false
type: boolean type: boolean
default: true default: true
branch:
description: "Branch to release from"
required: true
type: string
default: "main"
env: env:
POETRY_VERSION: "1.8.2" POETRY_VERSION: "1.8.2"
jobs: jobs:
release: release-base:
name: Release Langflow name: Release Langflow Base
if: inputs.release_package == true if: inputs.release_package == true
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
@ -47,13 +46,8 @@ jobs:
- name: Check Version - name: Check Version
id: check-version id: check-version
run: | run: |
if [ "${{ inputs.release_type }}" == "base" ]; then version=$(cd src/backend/base && poetry version --short)
version=$(cd src/backend/base && poetry version --short) last_released_version=$(curl -s "https://pypi.org/pypi/langflow-base/json" | jq -r '.releases | keys | .[]' | sort -V | tail -n 1)
last_released_version=$(curl -s "https://pypi.org/pypi/langflow-base/json" | jq -r '.releases | keys | .[]' | sort -V | tail -n 1)
else
version=$(poetry version --short)
last_released_version=$(curl -s "https://pypi.org/pypi/langflow/json" | jq -r '.releases | keys | .[]' | sort -V | tail -n 1)
fi
if [ "$version" = "$last_released_version" ]; then if [ "$version" = "$last_released_version" ]; then
echo "Version $version is already released. Skipping release." echo "Version $version is already released. Skipping release."
exit 1 exit 1
@ -61,19 +55,10 @@ jobs:
echo version=$version >> $GITHUB_OUTPUT echo version=$version >> $GITHUB_OUTPUT
fi fi
- name: Build project for distribution - name: Build project for distribution
run: | run: make build base=true
if [ "${{ inputs.release_type }}" == "base" ]; then
make build base=true
else
make build main=true
fi
- name: Test CLI - name: Test CLI
run: | run: |
if [ "${{ inputs.release_type }}" == "base" ]; then python -m pip install src/backend/base/dist/*.whl
python -m pip install src/backend/base/dist/*.whl
else
python -m pip install dist/*.whl
fi
python -m langflow run --host 127.0.0.1 --port 7860 & python -m langflow run --host 127.0.0.1 --port 7860 &
SERVER_PID=$! SERVER_PID=$!
# Wait for the server to start # Wait for the server to start
@ -91,37 +76,100 @@ jobs:
- name: Publish to PyPI - name: Publish to PyPI
env: env:
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }} POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }}
run: | run: make publish base=true
if [ "${{ inputs.release_type }}" == "base" ]; then
make publish base=true
else
make publish main=true
fi
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: dist${{ inputs.release_type }} name: dist-base
path: ${{ inputs.release_type == 'base' && 'src/backend/base/dist' || 'dist' }} path: src/backend/base/dist
release-main:
name: Release Langflow Main
if: inputs.release_package == true
needs: release-base
runs-on: ubuntu-latest
outputs:
version: ${{ steps.check-version.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Install poetry
run: pipx install poetry==${{ env.POETRY_VERSION }}
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"
cache: "poetry"
- name: Set up Nodejs 20
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Check Version
id: check-version
run: |
version=$(poetry version --short)
last_released_version=$(curl -s "https://pypi.org/pypi/langflow/json" | jq -r '.releases | keys | .[]' | sort -V | tail -n 1)
if [ "$version" = "$last_released_version" ]; then
echo "Version $version is already released. Skipping release."
exit 1
else
echo version=$version >> $GITHUB_OUTPUT
fi
- name: Wait for PyPI Propagation
run: sleep 300 # wait for 5 minutes to ensure PyPI propagation
- name: Build project for distribution
run: make build main=true
- name: Test CLI
run: |
python -m pip install dist/*.whl
python -m langflow run --host 127.0.0.1 --port 7860 &
SERVER_PID=$!
# Wait for the server to start
timeout 120 bash -c 'until curl -f http://127.0.0.1:7860/health; do sleep 2; done' || (echo "Server did not start in time" && kill $SERVER_PID && exit 1)
# Terminate the server
kill $SERVER_PID || (echo "Failed to terminate the server" && exit 1)
sleep 10 # give the server some time to terminate
# Check if the server is still running
if kill -0 $SERVER_PID 2>/dev/null; then
echo "Failed to terminate the server"
exit 1
else
echo "Server terminated successfully"
fi
- name: Publish to PyPI
env:
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }}
run: make publish main=true
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: dist-main
path: dist
call_docker_build: call_docker_build:
name: Call Docker Build Workflow name: Call Docker Build Workflow
needs: release needs: [release-base, release-main]
uses: langflow-ai/langflow/.github/workflows/docker-build.yml@main uses: langflow-ai/langflow/.github/workflows/docker-build.yml@main
strategy:
matrix:
release_type:
- base
- main
with: with:
version: ${{ needs.release.outputs.version }} # version should be needs.release-base.outputs.version if release_type is base
release_type: ${{ inputs.release_type }} # version should be needs.release-main.outputs.version if release_type is main
version: ${{ matrix.release_type == 'base' && needs.release-base.outputs.version || matrix.release_type == 'main' && needs.release-main.outputs.version }}
release_type: ${{ matrix.release_type }}
pre_release: ${{ inputs.pre_release }} pre_release: ${{ inputs.pre_release }}
secrets: inherit secrets: inherit
create_release: create_release:
name: Create Release name: Create Release
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [release] needs: release-main
if: ${{ inputs.release_type == 'main' }}
steps: steps:
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v4
with: with:
name: dist${{ inputs.release_type }} name: dist-main
path: dist path: dist
- name: Create Release - name: Create Release
uses: ncipollo/release-action@v1 uses: ncipollo/release-action@v1
@ -130,6 +178,6 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
draft: false draft: false
generateReleaseNotes: true generateReleaseNotes: true
prerelease: false prerelease: ${{ inputs.pre_release }}
tag: v${{ needs.release.outputs.version }} tag: v${{ needs.release-main.outputs.version }}
commit: dev commit: ${{ inputs.branch }}

View file

@ -42,6 +42,7 @@
# 📝 Content # 📝 Content
- [](#)
- [📝 Content](#-content) - [📝 Content](#-content)
- [📦 Get Started](#-get-started) - [📦 Get Started](#-get-started)
- [🎨 Create Flows](#-create-flows) - [🎨 Create Flows](#-create-flows)
@ -49,6 +50,7 @@
- [Deploy Langflow on Google Cloud Platform](#deploy-langflow-on-google-cloud-platform) - [Deploy Langflow on Google Cloud Platform](#deploy-langflow-on-google-cloud-platform)
- [Deploy on Railway](#deploy-on-railway) - [Deploy on Railway](#deploy-on-railway)
- [Deploy on Render](#deploy-on-render) - [Deploy on Render](#deploy-on-render)
- [Deploy on Kubernetes](#deploy-on-kubernetes)
- [🖥️ Command Line Interface (CLI)](#️-command-line-interface-cli) - [🖥️ Command Line Interface (CLI)](#️-command-line-interface-cli)
- [Usage](#usage) - [Usage](#usage)
- [Environment Variables](#environment-variables) - [Environment Variables](#environment-variables)

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "langflow" name = "langflow"
version = "1.0.1" version = "1.0.2"
description = "A Python package with a built-in web application" description = "A Python package with a built-in web application"
authors = ["Langflow <contact@langflow.org>"] authors = ["Langflow <contact@langflow.org>"]
maintainers = [ maintainers = [

View file

@ -34,7 +34,7 @@ def upgrade() -> None:
sa.Column("provider", sqlmodel.sql.sqltypes.AutoString(), nullable=True), sa.Column("provider", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
sa.Column("user_id", sqlmodel.sql.sqltypes.GUID(), nullable=False), sa.Column("user_id", sqlmodel.sql.sqltypes.GUID(), nullable=False),
sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False), sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False),
sa.Column("created_at", sqlmodel.sql.sqltypes.DateTime(), nullable=False), sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("updated_at", sa.DateTime(), nullable=True), sa.Column("updated_at", sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint("id"), sa.PrimaryKeyConstraint("id"),
) )

View file

@ -2,7 +2,6 @@ from typing import Dict, List, cast
from langchain.agents import AgentExecutor, BaseSingleActionAgent from langchain.agents import AgentExecutor, BaseSingleActionAgent
from langchain.agents.tool_calling_agent.base import create_tool_calling_agent from langchain.agents.tool_calling_agent.base import create_tool_calling_agent
from langchain_core.messages import BaseMessage
from langchain_core.prompts import ChatPromptTemplate from langchain_core.prompts import ChatPromptTemplate
from langflow.custom import Component from langflow.custom import Component

View file

@ -3,13 +3,13 @@ from loguru import logger
from langflow.base.vectorstores.model import LCVectorStoreComponent from langflow.base.vectorstores.model import LCVectorStoreComponent
from langflow.io import ( from langflow.io import (
BoolInput, BoolInput,
DataInput,
DropdownInput, DropdownInput,
HandleInput, HandleInput,
IntInput, IntInput,
MultilineInput, MultilineInput,
SecretStrInput, SecretStrInput,
StrInput, StrInput,
DataInput,
) )
from langflow.schema import Data from langflow.schema import Data
@ -196,6 +196,10 @@ class AstraVectorStoreComponent(LCVectorStoreComponent):
except Exception as e: except Exception as e:
raise ValueError(f"Error initializing AstraDBVectorStore: {str(e)}") from e raise ValueError(f"Error initializing AstraDBVectorStore: {str(e)}") from e
if hasattr(self, "ingest_data") and self.ingest_data:
logger.debug("Ingesting data into the Vector Store.")
self._add_documents_to_vector_store(vector_store)
self.status = self._astradb_collection_to_data(vector_store.collection) self.status = self._astradb_collection_to_data(vector_store.collection)
return vector_store return vector_store
@ -216,7 +220,7 @@ class AstraVectorStoreComponent(LCVectorStoreComponent):
else: else:
logger.debug("No documents to add to the Vector Store.") logger.debug("No documents to add to the Vector Store.")
def search_documents(self): def search_documents(self) -> list[Data]:
vector_store = self.build_vector_store() vector_store = self.build_vector_store()
logger.debug(f"Search input: {self.search_input}") logger.debug(f"Search input: {self.search_input}")

View file

@ -6,7 +6,7 @@ from typing import Optional
from urllib.parse import urlencode from urllib.parse import urlencode
import nest_asyncio # type: ignore import nest_asyncio # type: ignore
from fastapi import FastAPI, Request from fastapi import FastAPI, Request, Response
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
@ -32,22 +32,17 @@ from langflow.utils.logger import configure
warnings.filterwarnings("ignore", category=PydanticDeprecatedSince20) warnings.filterwarnings("ignore", category=PydanticDeprecatedSince20)
class RequestCancelledMiddleware: class RequestCancelledMiddleware(BaseHTTPMiddleware):
def __init__(self, app): def __init__(self, app):
self.app = app super().__init__(app)
async def __call__(self, scope, receive, send): async def dispatch(self, request: Request, call_next):
if scope["type"] != "http": queue: asyncio.Queue = asyncio.Queue()
await self.app(scope, receive, send)
return
# Let's make a shared queue for the request messages async def message_poller(sentinel, handler_task, request):
queue = asyncio.Queue()
async def message_poller(sentinel, handler_task):
nonlocal queue nonlocal queue
while True: while True:
message = await receive() message = await request.receive
if message["type"] == "http.disconnect": if message["type"] == "http.disconnect":
handler_task.cancel() handler_task.cancel()
return sentinel # Break the loop return sentinel # Break the loop
@ -56,13 +51,14 @@ class RequestCancelledMiddleware:
await queue.put(message) await queue.put(message)
sentinel = object() sentinel = object()
handler_task = asyncio.create_task(self.app(scope, queue.get, send)) handler_task = asyncio.create_task(call_next(request))
asyncio.create_task(message_poller(sentinel, handler_task)) asyncio.create_task(message_poller(sentinel, handler_task, request))
try: try:
return await handler_task response = await handler_task
return response
except asyncio.CancelledError: except asyncio.CancelledError:
pass return Response("Request was cancelled", status_code=499)
class JavaScriptMIMETypeMiddleware(BaseHTTPMiddleware): class JavaScriptMIMETypeMiddleware(BaseHTTPMiddleware):

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "langflow-base" name = "langflow-base"
version = "0.0.77" version = "0.0.78"
description = "A Python package with a built-in web application" description = "A Python package with a built-in web application"
authors = ["Langflow <contact@langflow.org>"] authors = ["Langflow <contact@langflow.org>"]
maintainers = [ maintainers = [

View file

@ -1,5 +1,3 @@
from langchain_core.documents import Document
from langflow.components import helpers from langflow.components import helpers
from langflow.custom.utils import build_custom_component_template from langflow.custom.utils import build_custom_component_template
from langflow.schema import Data from langflow.schema import Data
@ -17,18 +15,18 @@ from langflow.schema import Data
# assert result.new_key == "new_value" # assert result.new_key == "new_value"
def test_document_to_data_component(): # def test_document_to_data_component():
# Arrange # # Arrange
document_to_data_component = helpers.DocumentsToDataComponent() # document_to_data_component = helpers.DocumentsToDataComponent()
# Act # # Act
# Replace with your actual test data # # Replace with your actual test data
document = Document(page_content="key: value", metadata={"url": "https://example.com"}) # document = Document(page_content="key: value", metadata={"url": "https://example.com"})
result = document_to_data_component.build(document) # result = document_to_data_component.build(document)
# Assert # # Assert
# Replace with your actual expected result # # Replace with your actual expected result
assert result == [Data(data={"text": "key: value", "url": "https://example.com"})] # assert result == [Data(data={"text": "key: value", "url": "https://example.com"})]
def test_uuid_generator_component(): def test_uuid_generator_component():