fix: astra-assistants types (#2881)

* merge

* [autofix.ci] apply automated fixes

* support for MultilineSecretInput

* add support for MultilineSecretInput

* [autofix.ci] apply automated fixes

* ruff

* align eye-icon

* tweaks and modal

* [autofix.ci] apply automated fixes

* textare edit modal hidden text plus eye icon

* fix pydantic serialization warning

* [autofix.ci] apply automated fixes

* chore: Add password visibility toggle to text area components

* [autofix.ci] apply automated fixes

* fix list assistants

* fix: remove extraneous property from is-unicode-supported dependency

* fix: update TextAreaComponent styling for edit node table

Adjust the styling of the TextAreaComponent in the edit node table to fix the positioning of the side-bar button. The right margin of the button was changed from 5.2rem to 4.2rem to align it correctly with the text area. This change improves the visual consistency of the edit node table.

* fix modal not sync with outside state

* add comment for future devs

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: anovazzi1 <otavio2204@gmail.com>
This commit is contained in:
Sebastián Estévez 2024-07-29 13:56:05 -04:00 • committed by GitHub
commit cffed2c9be
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 415 additions and 237 deletions

View file

@ -1,50 +1,55 @@
from typing import Optional
from astra_assistants import patch # type: ignore from astra_assistants import patch # type: ignore
from openai import OpenAI from openai import OpenAI
from langflow.custom import Component
from langflow.custom import CustomComponent from langflow.inputs import StrInput, MultilineInput
from langflow.template import Output
from langflow.schema.message import Message
class AssistantsCreateAssistant(CustomComponent): class AssistantsCreateAssistant(Component):
icon = "bot"
display_name = "Create Assistant" display_name = "Create Assistant"
description = "Creates an Assistant and returns it's id" description = "Creates an Assistant and returns it's id"
def build_config(self): inputs = [
return { StrInput(
"name": { name="assistant_name",
"display_name": "Assistant Name", display_name="Assistant Name",
"advanced": False, info="Name for the assistant being created",
"info": "Name for the assistant being created", ),
}, StrInput(
"instructions": { name="instructions",
"display_name": "Instructions", display_name="Instructions",
"info": "Instructions for the assistant, think of these as the system prompt.", info="Instructions for the assistant, think of these as the system prompt.",
"advanced": False, ),
}, StrInput(
"model": { name="model",
"display_name": "Model name", display_name="Model name",
"advanced": False, info=(
"info": ( "Model for the assistant.\n\n"
"Model for the assistant.\n\n" "Environment variables for provider credentials can be set with the Dotenv Component.\n\n"
"Environment variables for provider credentials can be set with the Dotenv Component.\n\n" "Models are supported via LiteLLM, see (https://docs.litellm.ai/docs/providers) for supported model names and env vars."
"Models are supported via LiteLLM, see (https://docs.litellm.ai/docs/providers) for supported model names and env vars." ),
), # refresh_model=True
}, ),
"env_set": { MultilineInput(
"display_name": "Environment Set", name="env_set",
"advanced": False, display_name="Environment Set",
"info": "Dummy input to allow chaining with Dotenv Component.", info="Dummy input to allow chaining with Dotenv Component.",
}, ),
} ]
def build(self, name: str, instructions: str, model: str, env_set: Optional[str] = None) -> str: outputs = [
if env_set is None: Output(display_name="Assistant ID", name="assistant_id", method="process_inputs"),
raise Exception("Environment variables not set") ]
def process_inputs(self) -> Message:
print(f"env_set is {self.env_set}")
client = patch(OpenAI()) client = patch(OpenAI())
assistant = client.beta.assistants.create( assistant = client.beta.assistants.create(
name=name, name=self.assistant_name,
instructions=instructions, instructions=self.instructions,
model=model, model=self.model,
) )
return assistant.id message = Message(text=assistant.id)
return message

View file

@ -1,28 +1,32 @@
from typing import Optional
from astra_assistants import patch # type: ignore from astra_assistants import patch # type: ignore
from langflow.custom import Component
from openai import OpenAI from openai import OpenAI
from langflow.inputs import MultilineInput
from langflow.custom import CustomComponent from langflow.schema.message import Message
from langflow.template import Output
class AssistantsCreateThread(CustomComponent): class AssistantsCreateThread(Component):
display_name = "Create Assistant Thread" display_name = "Create Assistant Thread"
description = "Creates a thread and returns the thread id" description = "Creates a thread and returns the thread id"
def build_config(self): inputs = [
return { MultilineInput(
"env_set": { name="env_set",
"display_name": "Environment Set", display_name="Environment Set",
"advanced": False, info="Dummy input to allow chaining with Dotenv Component.",
"info": "Dummy input to allow chaining with Dotenv Component.", ),
}, ]
}
def build(self, env_set: Optional[str] = None) -> str: outputs = [
Output(display_name="Thread ID", name="thread_id", method="process_inputs"),
]
def process_inputs(self) -> Message:
client = patch(OpenAI()) client = patch(OpenAI())
thread = client.beta.threads.create() thread = client.beta.threads.create()
thread_id = thread.id thread_id = thread.id
return thread_id message = Message(text=thread_id)
return message

View file

@ -1,30 +1,32 @@
import io import io
from dotenv import load_dotenv from dotenv import load_dotenv
from langflow.custom import Component
from langflow.custom import CustomComponent from langflow.inputs import MultilineSecretInput
from langflow.schema.message import Message
from langflow.template import Output
class Dotenv(CustomComponent): class Dotenv(Component):
display_name = "Dotenv" display_name = "Dotenv"
description = "Load .env file into env vars" description = "Load .env file into env vars"
def build_config(self): inputs = [
return { MultilineSecretInput(
"dotenv_file_content": { name="dotenv_file_content",
"display_name": "Dotenv file content", display_name="Dotenv file content",
"advanced": False, info="Paste the content of your .env file directly, since contents are sensitive, using a Global variable set as 'password' is recommended",
"info": ( )
"Paste the content of your .env file directly\n\n" ]
"Since contents are sensitive, using a Global variable set as 'password' is recommended"
),
},
}
def build(self, dotenv_file_content: str) -> str: outputs = [
try: Output(display_name="env_set", name="env_set", method="process_inputs"),
fake_file = io.StringIO(dotenv_file_content) ]
result = load_dotenv(stream=fake_file, override=True)
return "Loaded .env" if result else "No variables found in .env" def process_inputs(self) -> Message:
except Exception as e: fake_file = io.StringIO(self.dotenv_file_content)
raise e result = load_dotenv(stream=fake_file, override=True)
message = Message(text="No variables found in .env")
if result:
message = Message(text="Loaded .env")
return message

View file

@ -1,30 +1,37 @@
from typing import Optional
from astra_assistants import patch # type: ignore from astra_assistants import patch # type: ignore
from langflow.custom import Component
from openai import OpenAI from openai import OpenAI
from langflow.custom import CustomComponent from langflow.inputs import StrInput, MultilineInput
from langflow.schema.message import Message
from langflow.template import Output
class AssistantsGetAssistantName(CustomComponent): class AssistantsGetAssistantName(Component):
client = patch(OpenAI())
display_name = "Get Assistant name" display_name = "Get Assistant name"
description = "Assistant by id" description = "Assistant by id"
def build_config(self): inputs = [
return { StrInput(
"assistant_id": { name="assistant_id",
"display_name": "Assistant ID", display_name="Assistant ID",
"advanced": False, info="ID of the assistant",
}, ),
"env_set": { MultilineInput(
"display_name": "Environment Set", name="env_set",
"advanced": False, display_name="Environment Set",
"info": "Dummy input to allow chaining with Dotenv Component.", info="Dummy input to allow chaining with Dotenv Component.",
}, ),
} ]
def build(self, assistant_id: str, env_set: Optional[str] = None) -> str: outputs = [
client = patch(OpenAI()) Output(display_name="Assistant Name", name="assistant_name", method="process_inputs"),
assistant = client.beta.assistants.retrieve( ]
assistant_id=assistant_id,
def process_inputs(self) -> Message:
assistant = self.client.beta.assistants.retrieve(
assistant_id=self.assistant_id,
) )
return assistant.name message = Message(text=assistant.name)
return message

View file

@ -1,14 +1,30 @@
import os import os
from langflow.custom import CustomComponent from langflow.custom import Component
from langflow.inputs import StrInput
from langflow.schema.message import Message
from langflow.template import Output
class GetEnvVar(CustomComponent): class GetEnvVar(Component):
display_name = "Get env var" display_name = "Get env var"
description = "Get env var" description = "Get env var"
icon = "custom_components" icon = "custom_components"
def build_config(self): inputs = [
return {"env_var_name": {"display_name": "Env var name"}} StrInput(
name="env_var_name",
display_name="Env var name",
info="Name of the environment variable to get",
)
]
def build(self, env_var_name: str) -> str: outputs = [
return os.environ[env_var_name] Output(display_name="Env var value", name="env_var_value", method="process_inputs"),
]
def process_inputs(self) -> Message:
if self.env_var_name not in os.environ:
raise Exception(f"Environment variable {self.env_var_name} not set")
else:
message = Message(text=os.environ[self.env_var_name])
return message

View file

@ -1,20 +1,24 @@
from typing import List
from astra_assistants import patch # type: ignore from astra_assistants import patch # type: ignore
from langflow.template.field.base import Output
from langflow.custom import Component
from openai import OpenAI from openai import OpenAI
from langflow.schema.message import Message
from langflow.custom import CustomComponent
class AssistantsListAssistants(CustomComponent): class AssistantsListAssistants(Component):
client = patch(OpenAI())
display_name = "List Assistants" display_name = "List Assistants"
description = "Returns a list of assistant id's" description = "Returns a list of assistant id's"
def build_config(self): outputs = [
return {} Output(display_name="Assistants", name="assistants", method="process_inputs"),
]
def build(self) -> List[str]: def process_inputs(self) -> Message:
client = patch(OpenAI()) assistants = self.client.beta.assistants.list()
assistants = client.beta.assistants.list()
id_list = [assistant.id for assistant in assistants] id_list = [assistant.id for assistant in assistants]
return id_list message = Message(
# get text from list
text="\n".join(id_list)
)
return message

View file

@ -1,67 +1,94 @@
from typing import Optional
from astra_assistants import patch # type: ignore from astra_assistants import patch # type: ignore
from typing import Any, Optional
from langflow.custom import Component
from openai import OpenAI from openai import OpenAI
from openai.lib.streaming import AssistantEventHandler from openai.lib.streaming import AssistantEventHandler
from langflow.inputs import MultilineInput
from langflow.custom import CustomComponent from langflow.schema import dotdict
from langflow.schema.message import Message
from langflow.template import Output
class AssistantsRun(CustomComponent): class AssistantsRun(Component):
client = patch(OpenAI())
display_name = "Run Assistant" display_name = "Run Assistant"
description = "Executes an Assistant Run against a thread" description = "Executes an Assistant Run against a thread"
def build_config(self): def update_build_config(
return { self,
"assistant_id": { build_config: dotdict,
"display_name": "Assistant ID", field_value: Any,
"advanced": False, field_name: Optional[str] = None,
"info": ( ):
"The ID of the assistant to run. \n\n" if field_name == "thread_id":
"Can be retrieved using the List Assistants component or created with the Create Assistant component." if field_value is None:
), thread = self.client.beta.threads.create()
}, self.thread_id = thread.id
"user_message": { field_value
"display_name": "User Message", build_config["thread_id"] = field_value
"info": "User message to pass to the run.",
"advanced": False,
},
"thread_id": {
"display_name": "Thread ID",
"advanced": False,
"info": "Thread ID to use with the run. If not provided, a new thread will be created.",
},
"env_set": {
"display_name": "Environment Set",
"advanced": False,
"info": "Dummy input to allow chaining with Dotenv Component.",
},
}
def build( inputs = [
self, assistant_id: str, user_message: str, thread_id: Optional[str] = None, env_set: Optional[str] = None MultilineInput(
) -> str: name="assistant_id",
text = "" display_name="Assistant ID",
client = patch(OpenAI()) info=(
"The ID of the assistant to run. \n\n"
"Can be retrieved using the List Assistants component or created with the Create Assistant component."
),
),
MultilineInput(
name="user_message",
display_name="User Message",
info="User message to pass to the run.",
),
MultilineInput(
name="thread_id",
display_name="Thread ID",
required=False,
info="Thread ID to use with the run. If not provided, a new thread will be created.",
),
MultilineInput(
name="env_set",
display_name="Environment Set",
info="Dummy input to allow chaining with Dotenv Component.",
),
]
if thread_id is None: outputs = [Output(display_name="Assistant Response", name="assistant_response", method="process_inputs")]
thread = client.beta.threads.create()
thread_id = thread.id
# add the user message def process_inputs(self) -> Message:
client.beta.threads.messages.create(thread_id=thread_id, role="user", content=user_message) try:
text = ""
class EventHandler(AssistantEventHandler): if self.thread_id is None:
def __init__(self): thread = self.client.beta.threads.create()
super().__init__() self.thread_id = thread.id
event_handler = EventHandler() # add the user message
with client.beta.threads.runs.create_and_stream( self.client.beta.threads.messages.create(thread_id=self.thread_id, role="user", content=self.user_message)
thread_id=thread_id,
assistant_id=assistant_id, class EventHandler(AssistantEventHandler):
event_handler=event_handler, def __init__(self):
) as stream: super().__init__()
# return stream.text_deltas
for part in stream.text_deltas: def on_exception(self, exception: Exception) -> None:
text += part print(f"Exception: {exception}")
print(part) raise exception
return text
event_handler = EventHandler()
with self.client.beta.threads.runs.create_and_stream(
thread_id=self.thread_id,
assistant_id=self.assistant_id,
event_handler=event_handler,
) as stream:
# return stream.text_deltas
for part in stream.text_deltas:
text += part
print(part)
message = Message(text=text)
return message
except Exception as e:
print(e)
raise Exception(f"Error running assistant: {e}")

View file

@ -11,6 +11,7 @@ from .inputs import (
MessageInput, MessageInput,
MessageTextInput, MessageTextInput,
MultilineInput, MultilineInput,
MultilineSecretInput,
NestedDictInput, NestedDictInput,
PromptInput, PromptInput,
SecretStrInput, SecretStrInput,
@ -30,6 +31,7 @@ __all__ = [
"IntInput", "IntInput",
"MessageInput", "MessageInput",
"MultilineInput", "MultilineInput",
"MultilineSecretInput",
"NestedDictInput", "NestedDictInput",
"PromptInput", "PromptInput",
"SecretStrInput", "SecretStrInput",

View file

@ -199,6 +199,20 @@ class MultilineInput(MessageTextInput, MultilineMixin, InputTraceMixin):
multiline: CoalesceBool = True multiline: CoalesceBool = True
class MultilineSecretInput(MessageTextInput, MultilineMixin, InputTraceMixin):
"""
Represents a multiline input field.
Attributes:
field_type (Optional[SerializableFieldTypes]): The type of the field. Defaults to FieldTypes.TEXT.
multiline (CoalesceBool): Indicates whether the input field should support multiple lines. Defaults to True.
"""
field_type: Optional[SerializableFieldTypes] = FieldTypes.PASSWORD
multiline: CoalesceBool = True
password: CoalesceBool = Field(default=True)
class SecretStrInput(BaseInputMixin, DatabaseLoadMixin): class SecretStrInput(BaseInputMixin, DatabaseLoadMixin):
""" """
Represents a field with password field type. Represents a field with password field type.
@ -354,6 +368,7 @@ InputTypes = Union[
HandleInput, HandleInput,
IntInput, IntInput,
MultilineInput, MultilineInput,
MultilineSecretInput,
NestedDictInput, NestedDictInput,
PromptInput, PromptInput,
SecretStrInput, SecretStrInput,

View file

@ -11,6 +11,7 @@ from langflow.inputs import (
MessageInput, MessageInput,
MessageTextInput, MessageTextInput,
MultilineInput, MultilineInput,
MultilineSecretInput,
NestedDictInput, NestedDictInput,
PromptInput, PromptInput,
SecretStrInput, SecretStrInput,
@ -31,6 +32,7 @@ __all__ = [
"IntInput", "IntInput",
"MessageInput", "MessageInput",
"MultilineInput", "MultilineInput",
"MultilineSecretInput",
"NestedDictInput", "NestedDictInput",
"PromptInput", "PromptInput",
"SecretStrInput", "SecretStrInput",

View file

@ -1078,6 +1078,7 @@
}, },
"node_modules/@clack/prompts/node_modules/is-unicode-supported": { "node_modules/@clack/prompts/node_modules/is-unicode-supported": {
"version": "1.3.0", "version": "1.3.0",
"extraneous": true,
"inBundle": true, "inBundle": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {

View file

@ -39,6 +39,15 @@ export function StrRenderComponent({
/> />
) : templateData.multiline ? ( ) : templateData.multiline ? (
<TextAreaComponent <TextAreaComponent
password={templateData.password}
updateVisibility={() => {
if (templateData.password !== undefined) {
handleOnNewValue(
{ password: !templateData.password },
{ skipSnapshot: true },
);
}
}}
id={`textarea_${id}`} id={`textarea_${id}`}
disabled={disabled} disabled={disabled}
editNode={editNode} editNode={editNode}

View file

@ -1,4 +1,5 @@
import { useEffect } from "react"; import { classNames } from "@/utils/utils";
import { useEffect, useState } from "react";
import { EDIT_TEXT_MODAL_TITLE } from "../../constants/constants"; import { EDIT_TEXT_MODAL_TITLE } from "../../constants/constants";
import { TypeModal } from "../../constants/enums"; import { TypeModal } from "../../constants/enums";
import GenericModal from "../../modals/genericModal"; import GenericModal from "../../modals/genericModal";
@ -14,6 +15,8 @@ export default function TextAreaComponent({
disabled, disabled,
editNode = false, editNode = false,
id = "", id = "",
password,
updateVisibility,
}: TextAreaComponentType): JSX.Element { }: TextAreaComponentType): JSX.Element {
// Clear text area // Clear text area
useEffect(() => { useEffect(() => {
@ -23,22 +26,29 @@ export default function TextAreaComponent({
}, [disabled]); }, [disabled]);
return ( return (
<div className={"flex w-full items-center " + (disabled ? "" : "")}> <div className={"flex w-full items-center" + (disabled ? "" : "")}>
<div className="flex w-full items-center gap-3" data-testid={"div-" + id}> <div className="flex w-full items-center gap-3" data-testid={"div-" + id}>
<Case condition={!editNode}> <Input
<Input id={id}
id={id} data-testid={id}
data-testid={id} value={value}
value={value} disabled={disabled}
disabled={disabled} className={classNames(
className={editNode ? "input-edit-node w-full" : "w-full"} password !== undefined && password && value !== ""
placeholder={"Type something..."} ? "text-clip password"
onChange={(event) => { : "",
onChange(event.target.value); editNode ? "input-edit-node" : "",
}} password && editNode ? "pr-8" : "",
/> password && !editNode ? "pr-10" : "",
</Case> "w-full",
)}
placeholder={"Type something..."}
onChange={(event) => {
onChange(event.target.value);
}}
/>
<GenericModal <GenericModal
changeVisibility={updateVisibility}
type={TypeModal.TEXT} type={TypeModal.TEXT}
buttonText="Finish Editing" buttonText="Finish Editing"
modalTitle={EDIT_TEXT_MODAL_TITLE} modalTitle={EDIT_TEXT_MODAL_TITLE}
@ -47,40 +57,77 @@ export default function TextAreaComponent({
onChange(value); onChange(value);
}} }}
disabled={disabled} disabled={disabled}
password={password}
> >
{!editNode ? ( <div
<div className="flex items-center"> className={
<Button unstyled> "flex items-center" + (password ? "relative left-6" : "")
<IconComponent }
strokeWidth={1.5} >
id={id} <Button unstyled>
name="ExternalLink" <IconComponent
className={ strokeWidth={1.5}
"icons-parameters-comp shrink-0" + id={id}
(disabled ? " text-ring" : " hover:text-accent-foreground") name="ExternalLink"
} className={
/> "icons-parameters-comp shrink-0" +
</Button> (disabled ? " text-ring" : " hover:text-accent-foreground")
</div> }
) : ( />
<Button unstyled className="w-full">
<div className="flex w-full items-center gap-3">
<span
id={id}
data-testid={id}
className={
editNode
? "input-edit-node input-dialog"
: (disabled ? "input-disable text-ring " : "") +
" primary-input text-muted-foreground"
}
>
{value !== "" ? value : "Type something..."}
</span>
</div>
</Button> </Button>
)} </div>
</GenericModal> </GenericModal>
{password !== undefined && (
<button
type="button"
tabIndex={-1}
className={classNames(
"mb-px",
editNode
? "side-bar-button-size absolute bottom-[1.3rem] right-[4.2rem]"
: "side-bar-button-size absolute bottom-4 right-[4.2rem]",
)}
onClick={(event) => {
event.preventDefault();
if (updateVisibility) updateVisibility();
}}
>
{password ? (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z"
/>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M3.98 8.223A10.477 10.477 0 001.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.45 10.45 0 0112 4.5c4.756 0 8.773 3.162 10.065 7.498a10.523 10.523 0 01-4.293 5.774M6.228 6.228L3 3m3.228 3.228l3.65 3.65m7.894 7.894L21 21m-3.228-3.228l-3.65-3.65m0 0a3 3 0 10-4.243-4.243m4.242 4.242L9.88 9.88"
/>
</svg>
)}
</button>
)}
</div> </div>
</div> </div>
); );

View file

@ -2,22 +2,31 @@ import * as React from "react";
import { cn } from "../../utils/utils"; import { cn } from "../../utils/utils";
export interface TextareaProps export interface TextareaProps
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {} extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
password?: boolean;
editNode?: boolean;
}
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>( const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
({ className, ...props }, ref) => { ({ className, password, editNode, ...props }, ref) => {
return ( return (
<textarea <div className="w-full">
className={cn( <textarea
"nopan nodelete nodrag noflow textarea-primary", className={cn(
className, "nopan nodelete nodrag noflow textarea-primary",
)} className,
ref={ref} password ? "password" : "",
{...props} )}
/> ref={ref}
{...props}
value={props.value as string}
onChange={props.onChange}
/>
</div>
); );
}, },
); );
Textarea.displayName = "Textarea"; Textarea.displayName = "Textarea";
export { Textarea }; export { Textarea };

View file

@ -41,6 +41,8 @@ export default function GenericModal({
disabled, disabled,
id = "", id = "",
readonly = false, readonly = false,
password,
changeVisibility,
}: genericModalPropsType): JSX.Element { }: genericModalPropsType): JSX.Element {
const [myButtonText] = useState(buttonText); const [myButtonText] = useState(buttonText);
const [myModalTitle] = useState(modalTitle); const [myModalTitle] = useState(modalTitle);
@ -201,14 +203,34 @@ export default function GenericModal({
} }
})()} })()}
> >
<span className="pr-2" data-testid="modal-title"> <div className="flex w-full items-start gap-3">
{myModalTitle} <div className="flex">
</span> <span className="pr-2" data-testid="modal-title">
<IconComponent {myModalTitle}
name={myModalTitle === "Edit Prompt" ? "TerminalSquare" : "FileText"} </span>
className="h-6 w-6 pl-1 text-primary" <IconComponent
aria-hidden="true" name={
/> myModalTitle === "Edit Prompt" ? "TerminalSquare" : "FileText"
}
className="h-6 w-6 pl-1 text-primary"
aria-hidden="true"
/>
</div>
{password !== undefined && (
<div>
<button
onClick={() => {
if (changeVisibility) changeVisibility();
}}
>
<IconComponent
name={password ? "Eye" : "EyeOff"}
className="h-6 w-6 cursor-pointer text-primary"
/>
</button>
</div>
)}
</div>
</BaseModal.Header> </BaseModal.Header>
<BaseModal.Content overflowHidden> <BaseModal.Content overflowHidden>
<div className={classNames("flex h-full w-full rounded-lg border")}> <div className={classNames("flex h-full w-full rounded-lg border")}>
@ -243,6 +265,7 @@ export default function GenericModal({
/> />
) : type !== TypeModal.PROMPT ? ( ) : type !== TypeModal.PROMPT ? (
<Textarea <Textarea
password={password}
ref={textRef} ref={textRef}
className="form-input h-full w-full resize-none overflow-auto rounded-lg focus-visible:ring-1" className="form-input h-full w-full resize-none overflow-auto rounded-lg focus-visible:ring-1"
value={inputValue} value={inputValue}

View file

@ -64,6 +64,7 @@ export type InputFieldType = {
list: boolean; list: boolean;
show: boolean; show: boolean;
readonly: boolean; readonly: boolean;
password?: boolean;
multiline?: boolean; multiline?: boolean;
value?: any; value?: any;
dynamic?: boolean; dynamic?: boolean;

View file

@ -136,6 +136,8 @@ export type TextAreaComponentType = {
editNode?: boolean; editNode?: boolean;
id?: string; id?: string;
readonly?: boolean; readonly?: boolean;
password?: boolean;
updateVisibility?: () => void;
}; };
export type TableComponentType = { export type TableComponentType = {
@ -653,6 +655,8 @@ export type genericModalPropsType = {
children: ReactNode; children: ReactNode;
id?: string; id?: string;
readonly?: boolean; readonly?: boolean;
password?: boolean;
changeVisibility?: () => void;
}; };
export type newFlowModalPropsType = { export type newFlowModalPropsType = {

View file

@ -102,7 +102,7 @@ test("PromptTemplateComponent", async ({ page }) => {
await page.getByTestId("edit-button-modal").click(); await page.getByTestId("edit-button-modal").click();
value = value =
(await page.locator('//*[@id="textarea_str_edit_prompt"]').textContent()) ?? (await page.locator('//*[@id="textarea_str_edit_prompt"]').inputValue()) ??
""; "";
if (value != "prompt_value_!@#!@#") { if (value != "prompt_value_!@#!@#") {
@ -112,7 +112,7 @@ test("PromptTemplateComponent", async ({ page }) => {
value = value =
(await page (await page
.locator('//*[@id="textarea_str_edit_prompt1"]') .locator('//*[@id="textarea_str_edit_prompt1"]')
.textContent()) ?? ""; .inputValue()) ?? "";
if (value != "prompt_name_test_123123!@#!@#") { if (value != "prompt_name_test_123123!@#!@#") {
expect(false).toBeTruthy(); expect(false).toBeTruthy();
@ -126,14 +126,14 @@ test("PromptTemplateComponent", async ({ page }) => {
expect(false).toBeTruthy(); expect(false).toBeTruthy();
} }
await page.locator('//*[@id="textarea_str_edit_prompt1"]').click(); await page.getByTestId('textarea_str_edit_prompt1-ExternalLink').click();
await page await page
.getByTestId("text-area-modal") .getByTestId("text-area-modal")
.fill("prompt_edit_test_12312312321!@#$"); .fill("prompt_edit_test_12312312321!@#$");
await page.getByText("Finish Editing", { exact: true }).click(); await page.getByText("Finish Editing", { exact: true }).click();
await page.locator('//*[@id="textarea_str_edit_prompt"]').click(); await page.getByTestId('textarea_str_edit_prompt-ExternalLink').click();
await page await page
.getByTestId("text-area-modal") .getByTestId("text-area-modal")
.fill("prompt_edit_test_44444444444!@#$"); .fill("prompt_edit_test_44444444444!@#$");
@ -186,7 +186,7 @@ test("PromptTemplateComponent", async ({ page }) => {
expect(await page.locator('//*[@id="showprompt1"]').isChecked()).toBeTruthy(); expect(await page.locator('//*[@id="showprompt1"]').isChecked()).toBeTruthy();
value = value =
(await page.locator('//*[@id="textarea_str_edit_prompt"]').textContent()) ?? (await page.locator('//*[@id="textarea_str_edit_prompt"]').inputValue()) ??
""; "";
if (value != "prompt_edit_test_44444444444!@#$") { if (value != "prompt_edit_test_44444444444!@#$") {
@ -196,7 +196,7 @@ test("PromptTemplateComponent", async ({ page }) => {
value = value =
(await page (await page
.locator('//*[@id="textarea_str_edit_prompt1"]') .locator('//*[@id="textarea_str_edit_prompt1"]')
.textContent()) ?? ""; .inputValue()) ?? "";
if (value != "prompt_edit_test_12312312321!@#$") { if (value != "prompt_edit_test_12312312321!@#$") {
expect(false).toBeTruthy(); expect(false).toBeTruthy();