Merge branch 'two_edges_merge' into two_edges

This commit is contained in:
Lucas Oliveira 2024-06-11 18:19:59 -03:00
commit fde9a77035
385 changed files with 7922 additions and 2254 deletions

4
.gitignore vendored
View file

@ -267,4 +267,6 @@ src/frontend/playwright-report/index.html
*.bak
prof/*
src/frontend/temp
src/frontend/temp
*.db-shm
*.db-wal

View file

@ -181,9 +181,8 @@ Use the widget API to customize your Chat Widget:
format &#123;<span>"key":"value"</span>&#125;.
</Admonition>
| Prop | Type | Required | Description |
|-----------------------|---------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| --------------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| bot_message_style | JSON | No | Applies custom formatting to bot messages. |
| chat_input_field | String | Yes | Defines the type of the input field for chat messages. |
| chat_inputs | JSON | Yes | Determines the chat input elements and their respective values. |
@ -207,4 +206,3 @@ Use the widget API to customize your Chat Widget:
| user_message_style | JSON | No | Determines the formatting for user messages in the chat window. |
| width | Number | No | Sets the width of the chat window in pixels. |
| window_title | String | No | Sets the title displayed in the chat window's header or title bar. |

View file

@ -86,7 +86,7 @@ With _`LANGFLOW_AUTO_LOGIN`_ set to _`False`_, Langflow requires users to sign u
light: useBaseUrl("img/sign-up.png"),
dark: useBaseUrl("img/sign-up.png"),
}}
style={{ width: "40%", margin: "20px auto" }}
style={{ width: "40%", margin: "20px auto" }}
/>
## Profile settings

View file

@ -40,14 +40,13 @@ The Playground's appearance changes depending on what components are in your can
Adding or removing any of the below components modifies your Playground so you can monitor the inputs and outputs.
* Chat Input
* Text Input
* Chat Output
* Text Output
* Records Output
* Inspect Memory
- Chat Input
- Text Input
- Chat Output
- Text Output
- Records 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).

View file

@ -48,8 +48,8 @@ One significant capability of the Chat Input component is its ability to transfo
<ZoomableImage
alt="Docusaurus themed image"
sources={{
light: ("img/playground-chat.png"),
dark: ("img/playground-chat.png"),
light: "img/playground-chat.png",
dark: "img/playground-chat.png",
}}
style={{ width: "100%", maxWidth: "800px", margin: "0 auto" }}
/>

View file

@ -38,7 +38,6 @@ For example, the [Basic prompting](../starter-projects/basic-prompting.mdx) flow
style={{ width: "100%", maxWidth: "800px", margin: "0 auto" }}
/>
In this flow, the **OpenAI LLM component** receives input (left side) and produces output (right side) - in this case, receiving input from the **Chat Input** and **Prompt** components and producing output to the **Chat Output** component.
## Component

View file

@ -86,7 +86,7 @@ You'll be presented with the following screen:
light: "img/duplicate-space.png",
dark: "img/duplicate-space.png",
}}
style={{ width: "80%", maxWidth: "800px", margin: "0 auto" }}
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!

View file

@ -32,8 +32,9 @@ There are two possible reasons for this error:
Clear the cache by deleting the contents of the cache folder.
This folder can be found at:
- **Linux or WSL2 on Windows**: `home/<username>/.cache/langflow/`
- **MacOS**: `/Users/<username>/Library/Caches/langflow/`
- **Linux or WSL2 on Windows**: `home/<username>/.cache/langflow/`
- **MacOS**: `/Users/<username>/Library/Caches/langflow/`
This error can occur during Langflow upgrades when the new version can't override `langflow-pre.db` in `.cache/langflow/`. Clearing the cache removes this file but will also erase your settings.

View file

@ -33,7 +33,7 @@ Build a question-and-answer chatbot with a document loaded from local memory.
light: "img/document-qa.png",
dark: "img/document-qa.png",
}}
style={{ width: "100%", maxWidth: "800px", margin: "0 auto" }}
style={{ width: "100%", maxWidth: "800px", margin: "0 auto" }}
/>
This flow creates a basic chatbot with the **Chat Input**, **Prompt**, **OpenAI**, and **Chat Output** components.

View file

@ -13,7 +13,8 @@ In this guide, we will use Astra DB as a vector store to store and retrieve the
<Admonition type="tip">
This guide assumes that you have Langflow up and running. If you are new to
Langflow, you can check out the [Getting Started](../getting-started/install-langflow.mdx) guide.
Langflow, you can check out the [Getting
Started](../getting-started/install-langflow.mdx) guide.
</Admonition>
TLDR;

49
poetry.lock generated
View file

@ -471,17 +471,17 @@ files = [
[[package]]
name = "boto3"
version = "1.34.123"
version = "1.34.124"
description = "The AWS SDK for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "boto3-1.34.123-py3-none-any.whl", hash = "sha256:56bec52d485d5670ce96d53ae7b2cd4ae4e8a705fb2298a21093cdd77d642331"},
{file = "boto3-1.34.123.tar.gz", hash = "sha256:42b140fc850cf261ee4b1e8ef527fa071b1f1592a6d6a68d34b29f37cc46b4dd"},
{file = "boto3-1.34.124-py3-none-any.whl", hash = "sha256:549d9735bbb7cf8e1a2f4b3b676be32946717c59f33c47234586835dc904d04e"},
{file = "boto3-1.34.124.tar.gz", hash = "sha256:a91ee58fa54b170f17b2e144f038e155f92cf515f1c073ac2595e9ee45f125a8"},
]
[package.dependencies]
botocore = ">=1.34.123,<1.35.0"
botocore = ">=1.34.124,<1.35.0"
jmespath = ">=0.7.1,<2.0.0"
s3transfer = ">=0.10.0,<0.11.0"
@ -490,13 +490,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
[[package]]
name = "botocore"
version = "1.34.123"
version = "1.34.124"
description = "Low-level, data-driven core of boto 3."
optional = false
python-versions = ">=3.8"
files = [
{file = "botocore-1.34.123-py3-none-any.whl", hash = "sha256:8c34ada2a708c82e7174bff700611643db7ce2cb18f1130c35045c24310d299d"},
{file = "botocore-1.34.123.tar.gz", hash = "sha256:a8577f6574600c4d159b5cd103ee05744a443d77f7778304e17307940b369c4f"},
{file = "botocore-1.34.124-py3-none-any.whl", hash = "sha256:fede092c7f8f414f78f3e7991e47cb0cf2279d90c027606ad7cba79fa370b827"},
{file = "botocore-1.34.124.tar.gz", hash = "sha256:3f0bf79c17d656acdfdb53581224f6a38867ff2829f7428c586198f67a90ea26"},
]
[package.dependencies]
@ -2616,13 +2616,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"]
[[package]]
name = "google-api-python-client"
version = "2.132.0"
version = "2.133.0"
description = "Google API Client Library for Python"
optional = false
python-versions = ">=3.7"
files = [
{file = "google-api-python-client-2.132.0.tar.gz", hash = "sha256:d6340dc83b72d72333cee5d50f7dcfecbff66a8783164090e945f985ec4c374d"},
{file = "google_api_python_client-2.132.0-py2.py3-none-any.whl", hash = "sha256:cde87700bd4d37f39f5e940292c1c6cd0910990b5b01f50b1332a8cea38e8595"},
{file = "google-api-python-client-2.133.0.tar.gz", hash = "sha256:293092905b66a046d3187a99ac454e12b00cc2c70444f26eb2f1f9c1a82720b4"},
{file = "google_api_python_client-2.133.0-py2.py3-none-any.whl", hash = "sha256:396fe676ea0dfed066654dcf9f8dea77a1342f9d9bb23bb88e45b7b81e773926"},
]
[package.dependencies]
@ -3057,13 +3057,13 @@ test = ["objgraph", "psutil"]
[[package]]
name = "groq"
version = "0.8.0"
version = "0.9.0"
description = "The official Python library for the groq API"
optional = false
python-versions = ">=3.7"
files = [
{file = "groq-0.8.0-py3-none-any.whl", hash = "sha256:f5e4e892d45001241a930db451e633ca1f0007e3f749deaa5d7360062fcd61e3"},
{file = "groq-0.8.0.tar.gz", hash = "sha256:37ceb2f706bd516d0bfcac8e89048a24b375172987a0d6bd9efb521c54f6deff"},
{file = "groq-0.9.0-py3-none-any.whl", hash = "sha256:d0e46f4ad645504672bb09c8100af3ced3a7db0d5119dc13e4aca535fc455874"},
{file = "groq-0.9.0.tar.gz", hash = "sha256:130ed5e35d3acfaab46b9e7a078eeaebf91052f4a9d71f86f87fb319b5fec332"},
]
[package.dependencies]
@ -4409,7 +4409,7 @@ six = "*"
[[package]]
name = "langflow-base"
version = "0.0.61"
version = "0.0.63"
description = "A Python package with a built-in web application"
optional = false
python-versions = ">=3.10,<3.13"
@ -4466,13 +4466,13 @@ url = "src/backend/base"
[[package]]
name = "langfuse"
version = "2.35.2"
version = "2.36.1"
description = "A client library for accessing langfuse"
optional = false
python-versions = "<4.0,>=3.8.1"
files = [
{file = "langfuse-2.35.2-py3-none-any.whl", hash = "sha256:d01a23842cab484594f03878aacb9732ef8fd361158eb819c7bf43f758a0954b"},
{file = "langfuse-2.35.2.tar.gz", hash = "sha256:32b2e6c5bc71b4efdc430c6b964ab1c1e1ba1e105a4a73912c38b3959dc4502d"},
{file = "langfuse-2.36.1-py3-none-any.whl", hash = "sha256:2cba244024bdf63aa3295bffada6b257497f9d18a3ca09026595470f46e09e5b"},
{file = "langfuse-2.36.1.tar.gz", hash = "sha256:5117038d35d4629cfa37e8d89986132c926c52c05631cdfef4f9d54bc05591d8"},
]
[package.dependencies]
@ -6966,13 +6966,13 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
[[package]]
name = "pydantic-settings"
version = "2.3.1"
version = "2.3.2"
description = "Settings management using Pydantic"
optional = false
python-versions = ">=3.8"
files = [
{file = "pydantic_settings-2.3.1-py3-none-any.whl", hash = "sha256:acb2c213140dfff9669f4fe9f8180d43914f51626db28ab2db7308a576cce51a"},
{file = "pydantic_settings-2.3.1.tar.gz", hash = "sha256:e34bbd649803a6bb3e2f0f58fb0edff1f0c7f556849fda106cc21bcce12c30ab"},
{file = "pydantic_settings-2.3.2-py3-none-any.whl", hash = "sha256:ae06e44349e4c7bff8d57aff415dfd397ae75c217a098d54e9e6990ad7594ac7"},
{file = "pydantic_settings-2.3.2.tar.gz", hash = "sha256:05d33003c74c2cd585de97b59eb17b6ed67181bc8a3ce594d74b5d24e4df7323"},
]
[package.dependencies]
@ -8662,13 +8662,13 @@ typing = ["mypy (>=1.4)", "rich", "twisted"]
[[package]]
name = "supabase"
version = "2.5.0"
version = "2.5.1"
description = "Supabase client for Python."
optional = false
python-versions = "<4.0,>=3.8"
files = [
{file = "supabase-2.5.0-py3-none-any.whl", hash = "sha256:13e5ed9e9377a1a69e70ad18ed7b82997cf13ffcd28173952f7503e4d5067771"},
{file = "supabase-2.5.0.tar.gz", hash = "sha256:133dc832dfdd617f2f90ac5b52664df96ac8a9302ac6656ee769dc3f545812f0"},
{file = "supabase-2.5.1-py3-none-any.whl", hash = "sha256:74a1f24f04fede1967ef084b50dea688228f7b10eb2f9d73350fe2251a865188"},
{file = "supabase-2.5.1.tar.gz", hash = "sha256:c50e0eba5b03de3abd5ac0f887957ca43558ba44c4d17bb44e73ec454b41734c"},
]
[package.dependencies]
@ -10505,6 +10505,7 @@ test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
[extras]
cassio = ["cassio"]
couchbase = ["couchbase"]
deploy = ["celery", "flower", "redis"]
local = ["ctransformers", "llama-cpp-python", "sentence-transformers"]
@ -10512,4 +10513,4 @@ local = ["ctransformers", "llama-cpp-python", "sentence-transformers"]
[metadata]
lock-version = "2.0"
python-versions = ">=3.10,<3.13"
content-hash = "07b4a3ead472c37bcbb6179ac2802907c39688c50a6e36ddb41690b1c8e09b2d"
content-hash = "6f5103d7e9f32094f0ebe8f192b1b967043382a1b2b97db42c3680cd0bc11efd"

View file

@ -1,6 +1,6 @@
[tool.poetry]
name = "langflow"
version = "1.0.0a50"
version = "1.0.0a52"
description = "A Python package with a built-in web application"
authors = ["Langflow <contact@langflow.org>"]
maintainers = [
@ -87,7 +87,7 @@ markdown = "^3.6"
langchain-chroma = "^0.1.1"
upstash-vector = "^0.4.0"
gitpython = "^3.1.43"
cassio = "^0.1.7"
cassio = { extras = ["cassio"], version = "^0.1.7", optional = true }
unstructured = {extras = ["docx", "md", "pptx"], version = "^0.14.4"}
@ -123,6 +123,7 @@ dictdiffer = "^0.9.0"
[tool.poetry.extras]
deploy = ["celery", "redis", "flower"]
couchbase = ["couchbase"]
cassio = ["cassio"]
local = ["llama-cpp-python", "sentence-transformers", "ctransformers"]

View file

@ -31,25 +31,60 @@ def create_flow(
flow: FlowCreate,
current_user: User = Depends(get_current_active_user),
):
"""Create a new flow."""
if flow.user_id is None:
flow.user_id = current_user.id
try:
"""Create a new flow."""
if flow.user_id is None:
flow.user_id = current_user.id
db_flow = Flow.model_validate(flow, from_attributes=True)
db_flow.updated_at = datetime.now(timezone.utc)
# First check if the flow.name is unique
# there might be flows with name like: "MyFlow", "MyFlow (1)", "MyFlow (2)"
# so we need to check if the name is unique with `like` operator
# if we find a flow with the same name, we add a number to the end of the name
# based on the highest number found
if session.exec(select(Flow).where(Flow.name == flow.name).where(Flow.user_id == current_user.id)).first():
flows = session.exec(
select(Flow).where(Flow.name.like(f"{flow.name} (%")).where(Flow.user_id == current_user.id)
).all()
if flows:
numbers = [int(flow.name.split("(")[1].split(")")[0]) for flow in flows]
flow.name = f"{flow.name} ({max(numbers) + 1})"
else:
flow.name = f"{flow.name} (1)"
if db_flow.folder_id is None:
# Make sure flows always have a folder
default_folder = session.exec(
select(Folder).where(Folder.name == DEFAULT_FOLDER_NAME, Folder.user_id == current_user.id)
).first()
if default_folder:
db_flow.folder_id = default_folder.id
db_flow = Flow.model_validate(flow, from_attributes=True)
db_flow.updated_at = datetime.now(timezone.utc)
session.add(db_flow)
session.commit()
session.refresh(db_flow)
return db_flow
if db_flow.folder_id is None:
# Make sure flows always have a folder
default_folder = session.exec(
select(Folder).where(Folder.name == DEFAULT_FOLDER_NAME, Folder.user_id == current_user.id)
).first()
if default_folder:
db_flow.folder_id = default_folder.id
session.add(db_flow)
session.commit()
session.refresh(db_flow)
return db_flow
except Exception as e:
# If it is a validation error, return the error message
if hasattr(e, "errors"):
raise HTTPException(status_code=400, detail=str(e)) from e
elif "UNIQUE constraint failed" in str(e):
# Get the name of the column that failed
columns = str(e).split("UNIQUE constraint failed: ")[1].split(".")[1].split("\n")[0]
# UNIQUE constraint failed: flow.user_id, flow.name
# or UNIQUE constraint failed: flow.name
# if the column has id in it, we want the other column
column = columns.split(",")[1] if "id" in columns.split(",")[0] else columns.split(",")[0]
raise HTTPException(
status_code=400, detail=f"{column.capitalize().replace('_', ' ')} must be unique"
) from e
elif isinstance(e, HTTPException):
raise e
else:
raise HTTPException(status_code=500, detail=str(e)) from e
@router.get("/", response_model=list[FlowRead], status_code=200)

View file

@ -1,7 +1,5 @@
from typing import List
from langflow.helpers.flow import generate_unique_flow_name
from langflow.helpers.folders import generate_unique_folder_name
import orjson
from fastapi import APIRouter, Depends, File, HTTPException, Response, UploadFile, status
from sqlalchemy import or_, update
@ -9,6 +7,8 @@ from sqlmodel import Session, select
from langflow.api.v1.flows import create_flows
from langflow.api.v1.schemas import FlowListCreate, FlowListReadWithFolderName
from langflow.helpers.flow import generate_unique_flow_name
from langflow.helpers.folders import generate_unique_folder_name
from langflow.services.auth.utils import get_current_active_user
from langflow.services.database.models.flow.model import Flow, FlowCreate, FlowRead
from langflow.services.database.models.folder.constants import DEFAULT_FOLDER_NAME
@ -35,17 +35,27 @@ def create_folder(
try:
new_folder = Folder.model_validate(folder, from_attributes=True)
new_folder.user_id = current_user.id
folder_results = session.exec(
select(Folder).where(
Folder.name.like(f"{new_folder.name}%"), # type: ignore
Folder.user_id == current_user.id,
# First check if the folder.name is unique
# there might be flows with name like: "MyFlow", "MyFlow (1)", "MyFlow (2)"
# so we need to check if the name is unique with `like` operator
# if we find a flow with the same name, we add a number to the end of the name
# based on the highest number found
if session.exec(
statement=select(Folder).where(Folder.name == new_folder.name).where(Folder.user_id == current_user.id)
).first():
folder_results = session.exec(
select(Folder).where(
Folder.name.like(f"{new_folder.name}%"), # type: ignore
Folder.user_id == current_user.id,
)
)
)
existing_folder_names = [folder.name for folder in folder_results]
if existing_folder_names:
new_folder.name = f"{new_folder.name} ({len(existing_folder_names) + 1})"
if folder_results:
folder_names = [folder.name for folder in folder_results]
folder_numbers = [int(name.split("(")[-1].split(")")[0]) for name in folder_names if "(" in name]
if folder_numbers:
new_folder.name = f"{new_folder.name} ({max(folder_numbers) + 1})"
else:
new_folder.name = f"{new_folder.name} (1)"
session.add(new_folder)
session.commit()

View file

@ -23,15 +23,15 @@ class ChatComponent(Component):
"display_name": "Sender Type",
"advanced": True,
},
"sender_name": {"display_name": "Sender Name"},
"sender_name": {"display_name": "Sender Name", "advanced": True},
"session_id": {
"display_name": "Session ID",
"info": "If provided, the message will be stored in the memory.",
"advanced": True,
},
"return_record": {
"display_name": "Return Record",
"info": "Return the message as a record containing the sender, sender_name, and session_id.",
"return_message": {
"display_name": "Return Message",
"info": "Return the message as a Message containing the sender, sender_name, and session_id.",
"advanced": True,
},
"record_template": {
@ -68,6 +68,7 @@ class ChatComponent(Component):
input_value: Optional[Union[str, Record, Message]] = None,
files: Optional[list[str]] = None,
session_id: Optional[str] = None,
return_message: Optional[bool] = False,
) -> Message:
message: Message | None = None
@ -78,7 +79,12 @@ class ChatComponent(Component):
message = Message(
text=input_value, sender=sender, sender_name=sender_name, files=files, session_id=session_id
)
self.status = message
if session_id and isinstance(message, Message):
if not return_message:
message_text = message.text
else:
message_text = message
self.status = message_text
if session_id and isinstance(message, Message) and isinstance(message.text, str):
self.store_message(message)
return message
return message_text

View file

@ -20,7 +20,7 @@ class URLComponent(CustomComponent):
self,
urls: list[str],
) -> list[Record]:
loader = WebBaseLoader(web_paths=urls)
loader = WebBaseLoader(web_paths=[url for url in urls if url])
docs = loader.load()
records = self.to_records(docs)
self.status = records

View file

@ -0,0 +1,15 @@
from langflow.custom import CustomComponent
from langflow.schema import Record
from langflow.field_typing import Embeddings
class EmbedComponent(CustomComponent):
display_name = "Embed Texts"
def build_config(self):
return {"texts": {"display_name": "Texts"}, "embbedings": {"display_name": "Embeddings"}}
def build(self, texts: list[str], embbedings: Embeddings) -> Embeddings:
vectors = Record(vector=embbedings.embed_documents(texts))
self.status = vectors
return vectors

View file

@ -0,0 +1,38 @@
from typing import Optional
from langflow.custom import CustomComponent
from langflow.schema.message import Message
class MessageComponent(CustomComponent):
display_name = "Message"
description = "Creates a Message object given a Session ID."
def build_config(self):
return {
"sender": {
"options": ["Machine", "User"],
"display_name": "Sender Type",
},
"sender_name": {"display_name": "Sender Name"},
"text": {"display_name": "Text"},
"session_id": {
"display_name": "Session ID",
"info": "Session ID of the chat history.",
"input_types": ["Text"],
},
}
def build(
self,
sender: str = "User",
sender_name: Optional[str] = None,
session_id: Optional[str] = None,
text: str = "",
) -> Message:
message = Message(
text=text, sender=sender, sender_name=sender_name, flow_id=self.graph.flow_id, session_id=session_id
)
self.status = message
return message

View file

@ -1,5 +1,3 @@
from typing import List, Optional
from langflow.custom import CustomComponent
from langflow.memory import get_messages, store_message
from langflow.schema.message import Message
@ -7,36 +5,18 @@ from langflow.schema.message import Message
class StoreMessageComponent(CustomComponent):
display_name = "Store Message"
description = "Stores a chat message given a Session ID."
beta: bool = True
description = "Stores a chat message."
def build_config(self):
return {
"sender": {
"options": ["Machine", "User"],
"display_name": "Sender Type",
},
"sender_name": {"display_name": "Sender Name"},
"message": {"display_name": "Message"},
"session_id": {
"display_name": "Session ID",
"info": "Session ID of the chat history.",
"input_types": ["Text"],
},
}
def build(
self,
sender: str = "User",
sender_name: Optional[str] = None,
session_id: Optional[str] = None,
message: str = "",
) -> List[Message]:
store_message(
message=Message(
text=message, sender=sender, sender_name=sender_name, flow_id=self.graph.flow_id, session_id=session_id
)
)
message: Message,
) -> Message:
store_message(message, flow_id=self.graph.flow_id)
self.status = get_messages()
self.status = get_messages(session_id=session_id)
return get_messages(session_id=session_id)
return message

View file

@ -6,25 +6,27 @@ from langflow.schema import Record
class MessageHistoryComponent(CustomComponent):
display_name = "Message History"
description = "Retrieves stored chat messages given a specific Session ID."
beta: bool = True
display_name = "Memory"
description = "Retrieves stored chat messages."
def build_config(self):
return {
"sender": {
"options": ["Machine", "User", "Machine and User"],
"display_name": "Sender Type",
"advanced": True,
},
"sender_name": {"display_name": "Sender Name", "advanced": True},
"n_messages": {
"display_name": "Number of Messages",
"info": "Number of messages to retrieve.",
"advanced": True,
},
"session_id": {
"display_name": "Session ID",
"info": "Session ID of the chat history.",
"input_types": ["Text"],
"advanced": True,
},
"order": {
"options": ["Ascending", "Descending"],
@ -39,7 +41,7 @@ class MessageHistoryComponent(CustomComponent):
sender: Optional[str] = "Machine and User",
sender_name: Optional[str] = None,
session_id: Optional[str] = None,
n_messages: int = 5,
n_messages: int = 100,
order: Optional[str] = "Descending",
) -> List[Record]:
order = "DESC" if order == "Descending" else "ASC"

Some files were not shown because too many files have changed in this diff Show more