merge fix

This commit is contained in:
cristhianzl 2024-03-29 21:56:59 -03:00
commit c3ff98c499
40 changed files with 480 additions and 317 deletions

View file

@ -301,3 +301,10 @@ def get_top_level_vertices(graph, vertices_ids):
else:
top_level_vertices.append(vertex_id)
return top_level_vertices
def parse_exception(exc):
"""Parse the exception message."""
if hasattr(exc, "body"):
return exc.body["message"]
return str(exc)

View file

@ -12,6 +12,7 @@ from langflow.api.utils import (
format_elapsed_time,
format_exception_message,
get_top_level_vertices,
parse_exception,
)
from langflow.api.v1.schemas import (
InputValueRequest,
@ -314,7 +315,10 @@ async def build_vertex_stream(
except Exception as exc:
logger.exception(f"Error building vertex: {exc}")
yield str(StreamData(event="error", data={"error": str(exc)}))
exc_message = parse_exception(exc)
if exc_message == "The message must be an iterator or an async iterator.":
exc_message = "This stream has already been closed."
yield str(StreamData(event="error", data={"error": exc_message}))
finally:
logger.debug("Closing stream")
yield str(StreamData(event="close", data={"message": "Stream closed"}))

View file

@ -7,7 +7,7 @@ from langflow.schema import Record
class FileComponent(CustomComponent):
display_name = "Files"
display_name = "File"
description = "A generic file loader."
def build_config(self) -> Dict[str, Any]:

View file

@ -8,7 +8,7 @@ from langflow.schema import Record
class URLComponent(CustomComponent):
display_name = "URL"
description = "Fetch text content given one or more URLs."
description = "Fetch content from one or more URLs."
def build_config(self) -> Dict[str, Any]:
return {

View file

@ -4,7 +4,7 @@ from langflow.schema import Record
class CreateRecordComponent(CustomComponent):
display_name = "Create Record"
description = "A component to create a record."
description = "Create a Record structure using text-based key/value pairs."
beta: bool = True
def build_config(self):

View file

@ -1,4 +1,5 @@
from langflow.field_typing import Data
# from langflow.field_typing import Data
from langflow.schema import Record
from langflow.interface.custom.custom_component import CustomComponent
@ -11,5 +12,5 @@ class Component(CustomComponent):
def build_config(self):
return {"param": {"display_name": "Parameter"}}
def build(self, param: Data) -> Data:
return param
def build(self, param: str) -> Record:
return Record(data=param)

View file

@ -7,7 +7,7 @@ from langflow.schema import Record
class MessageHistoryComponent(CustomComponent):
display_name = "Message History"
description = "Used to retrieve stored chat messages."
description = "Retrieves stored chat messages given a specific Session ID."
beta: bool = True
def build_config(self):

View file

@ -6,7 +6,8 @@ from langflow.schema import Record
class RecordsToTextComponent(CustomComponent):
display_name = "Records To Text"
description = "Convert Records into plain text using a pre-defined template."
description = "Convert Records into plain text following a specified template."
def build_config(self):
return {

View file

@ -4,7 +4,7 @@ from langflow.schema import Record
class UpdateRecordComponent(CustomComponent):
display_name = "Update Record"
description = "Updates a Record with new data. Akin to a Python dictionary update."
description = "Update Record with text-based key/value pairs, similar to updating a Python dictionary."
def build_config(self):
return {

View file

@ -7,7 +7,7 @@ from langflow.schema import Record
class ChatInput(ChatComponent):
display_name = "Chat Input"
description = "Capture user inputs from the chat interface."
description = "Get chat inputs from the Interaction Panel."
icon = "ChatInput"
def build(

View file

@ -6,7 +6,7 @@ from langflow.interface.custom.custom_component import CustomComponent
class PromptComponent(CustomComponent):
display_name: str = "Prompt"
description: str = "A component for creating prompt templates using dynamic variables."
description: str = "Create prompt templates with dynamic variables. Prompts can help guide the behavior of a Language Model."
icon = "terminal-square"
def build_config(self):

View file

@ -6,7 +6,7 @@ from langflow.field_typing import Text
class TextInput(TextComponent):
display_name = "Text Input"
description = "Capture Text or Record and send text inputs."
description = "Get text inputs from the Interaction Panel."
def build_config(self):
return {

View file

@ -8,7 +8,7 @@ from langflow.field_typing import Text
class AmazonBedrockComponent(LCModelComponent):
display_name: str = "Amazon Bedrock"
description: str = "Generate text using LLM model from Amazon Bedrock."
description: str = "Generate text using Amazon Bedrock LLMs."
icon = "Amazon"
field_order = [
"model_id",

View file

@ -9,7 +9,7 @@ from langflow.field_typing import Text
class AnthropicLLM(LCModelComponent):
display_name: str = "Anthropic"
description: str = "Generate text using Anthropic Chat&Completion large language models."
description: str = "Generate text using Anthropic Chat&Completion LLMs."
icon = "Anthropic"
def build_config(self):

View file

@ -9,7 +9,7 @@ from langflow.field_typing import Text
class AzureChatOpenAIComponent(LCModelComponent):
display_name: str = "Azure OpenAI"
description: str = "Generate text using LLM model from Azure OpenAI."
description: str = "Generate text using Azure OpenAI LLMs."
documentation: str = "https://python.langchain.com/docs/integrations/llms/azure_openai"
beta = False
icon = "Azure"

View file

@ -9,10 +9,8 @@ from langflow.field_typing import Text
class QianfanChatEndpointComponent(LCModelComponent):
display_name: str = "Qianfan"
description: str = (
"Generate text using Baidu Qianfan chat models. Get more detail from "
"https://python.langchain.com/docs/integrations/chat/baidu_qianfan_endpoint."
)
description: str = "Generate text using Baidu Qianfan LLMs."
documentation: str = "https://python.langchain.com/docs/integrations/chat/baidu_qianfan_endpoint."
icon = "BaiduQianfan"
def build_config(self):

View file

@ -10,7 +10,7 @@ from langflow.field_typing import Text
class CohereComponent(LCModelComponent):
display_name = "Cohere"
description = "Generate text using Cohere large language models."
description = "Generate text using Cohere LLMs."
documentation = "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/cohere"
icon = "Cohere"

View file

@ -9,7 +9,7 @@ from langflow.field_typing import RangeSpec, Text
class GoogleGenerativeAIComponent(LCModelComponent):
display_name: str = "Google Generative AI"
description: str = "Generate text using Google Generative AI to generate text."
description: str = "Generate text using Google Generative AI."
icon = "GoogleGenerativeAI"
icon = "Google"

View file

@ -9,7 +9,7 @@ from langflow.field_typing import Text
class HuggingFaceEndpointsComponent(LCModelComponent):
display_name: str = "Hugging Face API"
description: str = "Generate text using LLM model from Hugging Face Inference API."
description: str = "Generate text using Hugging Face Inference APIs."
icon = "HuggingFace"
def build_config(self):

View file

@ -14,7 +14,7 @@ from langflow.field_typing import Text
class ChatOllamaComponent(LCModelComponent):
display_name = "Ollama"
description = "Generate text using Local LLM for chat with Ollama."
description = "Generate text using Ollama Local LLMs."
icon = "Ollama"
def build_config(self) -> dict:

View file

@ -8,7 +8,7 @@ from langflow.field_typing import NestedDict, Text
class OpenAIModelComponent(LCModelComponent):
display_name = "OpenAI"
description = "Generates text using OpenAI's models."
description = "Generates text using OpenAI LLMs."
icon = "OpenAI"
def build_config(self):

View file

@ -8,7 +8,7 @@ from langflow.field_typing import Text
class ChatVertexAIComponent(LCModelComponent):
display_name = "Vertex AI"
description = "Generate text using Vertex AI Chat large language models API."
description = "Generate text using Vertex AI LLMs."
icon = "VertexAI"
def build_config(self):

View file

@ -7,7 +7,7 @@ from langflow.schema import Record
class ChatOutput(ChatComponent):
display_name = "Chat Output"
description = "Used to send a chat message."
description = "Display a chat message in the Interaction Panel."
icon = "ChatOutput"
def build(

View file

@ -6,7 +6,7 @@ from langflow.field_typing import Text
class TextOutput(TextComponent):
display_name = "Text Output"
description = "Used to send a text output."
description = "Display a text output in the Interaction Panel."
def build_config(self):
return {

View file

@ -10,5 +10,5 @@ class DefaultPromptField(TemplateField):
advanced: bool = False
multiline: bool = True
input_types: list[str] = ["Document", "BaseOutputParser", "Text", "Record"]
input_types: list[str] = ["Document", "BaseOutputParser", "Record", "Text"]
value: str = "" # Set the value to empty string

View file

@ -421,8 +421,8 @@ export default function ParameterComponent({
{title}
</span>
)}
<span className={(info === "" ? "" : "ml-1 ") + " text-status-red"}>
{required ? " *" : ""}
<span className={(info === "" ? "" : "ml-1 ") + " text-status-red pl-1"}>
{required ? "*" : ""}
</span>
<div className="">
{info !== "" && (
@ -487,7 +487,7 @@ export default function ParameterComponent({
{left === true &&
type === "str" &&
!data.node?.template[name].options ? (
<div className="mt-2 w-full">
<div className="w-full">
{data.node?.template[name].list ? (
<div
className={
@ -628,7 +628,7 @@ export default function ParameterComponent({
isLoading={isLoading}
options={data.node.template[name].options}
onSelect={handleOnNewValue}
value={data.node.template[name].value ?? "Choose an option"}
value={data.node.template[name].value}
id={"dropdown-" + name}
/>
</div>

View file

@ -77,7 +77,7 @@ export default function AddNewVariableButton({ children }): JSX.Element {
}}
selectedOption={type}
password={false}
options={["Variable", "Credential"]}
options={["Generic", "Credential"]}
placeholder="Choose a type for the variable..."
></InputComponent>
<Label>Value</Label>
@ -92,7 +92,7 @@ export default function AddNewVariableButton({ children }): JSX.Element {
</div>
</BaseModal.Content>
<BaseModal.Footer>
<Button onClick={handleSaveVariable}>Save variable</Button>
<Button onClick={handleSaveVariable}>Save Variable</Button>
</BaseModal.Footer>
</BaseModal>
);

View file

@ -52,11 +52,11 @@ export default function Dropdown({
editNode ? "input-edit-node" : "py-2"
)}
>
<span data-testid={`value-${id ?? ""}`}>
{value
? options.find((option) => option === value)
: "Choose an option..."}
</span>
{value &&
value !== "" &&
options.find((option) => option === value)
? options.find((option) => option === value)
: "Choose an option..."}
<ForwardedIconComponent
name="ChevronsUpDown"
className="ml-2 h-4 w-4 shrink-0 opacity-50"

View file

@ -9,6 +9,7 @@ import Robot from "../../../assets/robot.png";
import SanitizedHTMLWrapper from "../../../components/SanitizedHTMLWrapper";
import CodeTabsComponent from "../../../components/codeTabsComponent";
import IconComponent from "../../../components/genericIconComponent";
import useAlertStore from "../../../stores/alertStore";
import useFlowStore from "../../../stores/flowStore";
import { chatMessagePropsType } from "../../../types/components";
import { classNames } from "../../../utils/utils";
@ -33,6 +34,7 @@ export default function ChatMessage({
const [isStreaming, setIsStreaming] = useState(false);
const eventSource = useRef<EventSource | undefined>(undefined);
const updateFlowPool = useFlowStore((state) => state.updateFlowPool);
const setErrorData = useAlertStore((state) => state.setErrorData);
const chatMessageRef = useRef(chatMessage);
// Sync ref with state
@ -57,7 +59,17 @@ export default function ChatMessage({
setIsStreaming(false);
eventSource.current?.close();
setStreamUrl(undefined);
reject(new Error("Streaming failed"));
// property data is not available in the event object
// so check if the event object has a data property
if (event.data) {
let parsedData = JSON.parse(event.data);
if (parsedData.error) {
reject(new Error(parsedData.error));
} else
reject(new Error("An error occurred while streaming the output"));
} else {
reject(new Error("An error occurred while streaming the output"));
}
};
eventSource.current.addEventListener("close", (event) => {
setStreamUrl(undefined); // Update state to reflect the stream is closed
@ -79,7 +91,10 @@ export default function ChatMessage({
}
})
.catch((error) => {
console.error(error);
setErrorData({
title: "Streaming Error",
list: [error.message],
});
setLockChat(false);
});
}

View file

@ -41,14 +41,27 @@ export interface ButtonProps
asChild?: boolean;
}
function toTitleCase(text: string) {
return text
.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join(' ');
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
({ className, variant, size, asChild = false,children, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
let newChildren = children;
if (typeof(children)==="string"){
newChildren = toTitleCase(children)
}
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
children={newChildren}
{...props}
/>
);
}

View file

@ -107,7 +107,7 @@ export const CODE_DIALOG_SUBTITLE =
* @constant
*/
export const CHAT_FORM_DIALOG_SUBTITLE =
"Adjust or overwrite input values, inspect outputs or interact through the chat console.";
"Set up the input variables defined in prompt templates. Interact with agents and chains.";
/**
* The base text for subtitle of Edit Node Dialog
@ -732,12 +732,14 @@ export const PRIORITY_SIDEBAR_ORDER = [
"saved_components",
"inputs",
"outputs",
"prompts",
"data",
"prompt",
"models",
"helpers",
"vectorstores",
"vectorsearch",
"helpers",
"embeddings",
];
export const NATIVE_CATEGORIES = [
"inputs",

View file

@ -4,6 +4,7 @@ import { DisclosureComponentType } from "../../../../types/components";
export default function DisclosureComponent({
button: { title, Icon, buttons = [] },
isChild = true,
children,
openDisc,
}: DisclosureComponentType): JSX.Element {
@ -13,10 +14,14 @@ export default function DisclosureComponent({
<>
<div>
<Disclosure.Button
className="components-disclosure-arrangement"
className={
isChild
? "components-disclosure-arrangement-child"
: "components-disclosure-arrangement"
}
data-testid={`disclosure-${title.toLocaleLowerCase()}`}
>
<div className="flex gap-4">
<div className={"flex gap-4" + (isChild ? " pl-2" : "")}>
{/* BUG ON THIS ICON */}
<Icon strokeWidth={1.5} size={22} className="text-primary" />
<span className="components-disclosure-title">{title}</span>

View file

@ -0,0 +1,41 @@
import { Disclosure } from "@headlessui/react";
import IconComponent from "../../../../components/genericIconComponent";
import { DisclosureComponentType } from "../../../../types/components";
export default function ParentDisclosureComponent({
button: { title, Icon, buttons = [] },
children,
openDisc,
}: DisclosureComponentType): JSX.Element {
return (
<Disclosure as="div" defaultOpen={openDisc} key={title}>
{({ open }) => (
<>
<div>
<Disclosure.Button className="parent-disclosure-arrangement">
<div className="flex gap-4">
<span className="parent-disclosure-title">{title}</span>
</div>
<div className="components-disclosure-div">
{buttons.map((btn, index) => (
<button key={index} onClick={btn.onClick}>
{btn.Icon}
</button>
))}
<div>
<IconComponent
name="ChevronRight"
className={`${
open || openDisc ? "rotate-90 transform" : ""
} h-4 w-4 text-foreground`}
/>
</div>
</div>
</Disclosure.Button>
</div>
<Disclosure.Panel as="div">{children}</Disclosure.Panel>
</>
)}
</Disclosure>
);
}

View file

@ -5,6 +5,7 @@ import ShadTooltip from "../../../../components/ShadTooltipComponent";
import IconComponent from "../../../../components/genericIconComponent";
import { Input } from "../../../../components/ui/input";
import { Separator } from "../../../../components/ui/separator";
import { PRIORITY_SIDEBAR_ORDER } from "../../../../constants/constants";
import ExportModal from "../../../../modals/exportModal";
import ShareModal from "../../../../modals/shareModal";
import useAlertStore from "../../../../stores/alertStore";
@ -24,6 +25,7 @@ import {
sensitiveSort,
} from "../../../../utils/utils";
import DisclosureComponent from "../DisclosureComponent";
import ParentDisclosureComponent from "../ParentDisclosureComponent";
import SidebarDraggableComponent from "./sideBarDraggableComponent";
import { sortKeys } from "./utils";
@ -271,23 +273,17 @@ export default function ExtraSidebar(): JSX.Element {
<div className="side-bar-components-div-arrangement">
{Object.keys(dataFilter)
.sort(sortKeys)
.filter((x) => PRIORITY_SIDEBAR_ORDER.includes(x))
.map((SBSectionName: keyof APIObjectType, index) =>
Object.keys(dataFilter[SBSectionName]).length > 0 ? (
<>
{index === 0 && (
<div className="pt-0.5">
<div className="p-2 px-4 text-sm font-semibold" key={index}>
Native Components
</div>
</div>
)}
<DisclosureComponent
openDisc={
getFilterEdge.length !== 0 || search.length !== 0
? true
: false
}
isChild={false}
key={index + search + JSON.stringify(getFilterEdge)}
button={{
title: nodeNames[SBSectionName] ?? nodeNames.unknown,
@ -342,37 +338,124 @@ export default function ExtraSidebar(): JSX.Element {
))}
</div>
</DisclosureComponent>
{index === Object.keys(dataFilter).length - 1 && (
<>
<a
target={"_blank"}
href="https://langflow.store"
className="components-disclosure-arrangement"
>
<div className="flex gap-4">
{/* BUG ON THIS ICON */}
<SparklesIcon
strokeWidth={1.5}
className="w-[22px] text-primary"
/>
<span className="components-disclosure-title">
Discover More
</span>
</div>
<div className="components-disclosure-div">
<div>
<LinkIcon className="h-4 w-4 text-foreground" />
</div>
</div>
</a>
</>
)}
</>
) : (
<div key={index}></div>
)
)}
)}{" "}
<ParentDisclosureComponent
openDisc={
getFilterEdge.length !== 0 || search.length !== 0 ? true : false
}
key={"Extended"}
button={{
title: "Extended",
Icon: nodeIconsLucide.unknown,
}}
>
{Object.keys(dataFilter)
.sort(sortKeys)
.filter((x) => !PRIORITY_SIDEBAR_ORDER.includes(x))
.map((SBSectionName: keyof APIObjectType, index) =>
Object.keys(dataFilter[SBSectionName]).length > 0 ? (
<>
<DisclosureComponent
openDisc={
getFilterEdge.length !== 0 || search.length !== 0
? true
: false
}
key={index + search + JSON.stringify(getFilterEdge)}
button={{
title: nodeNames[SBSectionName] ?? nodeNames.unknown,
Icon:
nodeIconsLucide[SBSectionName] ??
nodeIconsLucide.unknown,
}}
>
<div className="side-bar-components-gap">
{Object.keys(dataFilter[SBSectionName])
.sort((a, b) =>
sensitiveSort(
dataFilter[SBSectionName][a].display_name,
dataFilter[SBSectionName][b].display_name
)
)
.map((SBItemName: string, index) => (
<ShadTooltip
content={
dataFilter[SBSectionName][SBItemName].display_name
}
side="right"
key={index}
>
<SidebarDraggableComponent
sectionName={SBSectionName as string}
apiClass={dataFilter[SBSectionName][SBItemName]}
key={index}
onDragStart={(event) =>
onDragStart(event, {
//split type to remove type in nodes saved with same name removing it's
type: removeCountFromString(SBItemName),
node: dataFilter[SBSectionName][SBItemName],
})
}
color={nodeColors[SBSectionName]}
itemName={SBItemName}
//convert error to boolean
error={
!!dataFilter[SBSectionName][SBItemName].error
}
display_name={
dataFilter[SBSectionName][SBItemName]
.display_name
}
official={
dataFilter[SBSectionName][SBItemName]
.official === false
? false
: true
}
/>
</ShadTooltip>
))}
</div>
</DisclosureComponent>
{index ===
Object.keys(dataFilter).length -
PRIORITY_SIDEBAR_ORDER.length +
1 && (
<>
<a
target={"_blank"}
href="https://langflow.store"
className="components-disclosure-arrangement"
>
<div className="flex gap-4">
{/* BUG ON THIS ICON */}
<SparklesIcon
strokeWidth={1.5}
className="w-[22px] text-primary"
/>
<span className="components-disclosure-title">
Discover More
</span>
</div>
<div className="components-disclosure-div">
<div>
<LinkIcon className="h-4 w-4 text-foreground" />
</div>
</div>
</a>
</>
)}
</>
) : (
<div key={index}></div>
)
)}
</ParentDisclosureComponent>
</div>
</div>
);

View file

@ -117,7 +117,7 @@
@apply relative inline-flex w-full items-center justify-center rounded-md bg-background px-2 py-2 text-foreground transition-all duration-500 ease-in-out;
}
.header-menubar-item {
@apply relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 cursor-pointer;
@apply relative flex cursor-default cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50;
}
.extra-side-bar-buttons:hover {
@apply hover:bg-muted;
@ -183,9 +183,19 @@
.disabled-message-button-icon {
@apply fill-chat-trigger-disabled stroke-chat-trigger-disabled stroke-1;
}
.parent-disclosure-arrangement {
@apply flex w-full select-none items-center justify-between border-y border-y-input bg-background px-3 py-2;
}
.components-disclosure-arrangement {
@apply -mt-px flex w-full select-none items-center justify-between border-y border-y-input bg-muted px-3 py-2;
}
.components-disclosure-arrangement-child {
/* different color than the non child */
@apply -mt-px flex w-full select-none items-center justify-between border-y border-y-input bg-muted px-3 py-2;
}
.parent-disclosure-title {
@apply p-2 px-2 text-sm font-semibold;
}
.components-disclosure-title {
@apply flex items-center text-sm text-primary;
}

View file

@ -149,6 +149,7 @@ export type FileComponentType = {
export type DisclosureComponentType = {
children: ReactNode;
openDisc: boolean;
isChild?: boolean;
button: {
title: string;
Icon: React.ElementType;

View file

@ -249,7 +249,6 @@ export const nodeColors: { [char: string]: string } = {
retrievers: "#e6b25a",
unknown: "#9CA3AF",
custom_components: "#ab11ab",
Record:"#31a3cc"
};
export const nodeNames: { [char: string]: string } = {