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:
parent
c7575b18df
commit
cffed2c9be
18 changed files with 415 additions and 237 deletions
|
|
@ -1,50 +1,55 @@
|
|||
from typing import Optional
|
||||
|
||||
from astra_assistants import patch # type: ignore
|
||||
from openai import OpenAI
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.custom import Component
|
||||
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"
|
||||
description = "Creates an Assistant and returns it's id"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"name": {
|
||||
"display_name": "Assistant Name",
|
||||
"advanced": False,
|
||||
"info": "Name for the assistant being created",
|
||||
},
|
||||
"instructions": {
|
||||
"display_name": "Instructions",
|
||||
"info": "Instructions for the assistant, think of these as the system prompt.",
|
||||
"advanced": False,
|
||||
},
|
||||
"model": {
|
||||
"display_name": "Model name",
|
||||
"advanced": False,
|
||||
"info": (
|
||||
"Model for the assistant.\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."
|
||||
),
|
||||
},
|
||||
"env_set": {
|
||||
"display_name": "Environment Set",
|
||||
"advanced": False,
|
||||
"info": "Dummy input to allow chaining with Dotenv Component.",
|
||||
},
|
||||
}
|
||||
inputs = [
|
||||
StrInput(
|
||||
name="assistant_name",
|
||||
display_name="Assistant Name",
|
||||
info="Name for the assistant being created",
|
||||
),
|
||||
StrInput(
|
||||
name="instructions",
|
||||
display_name="Instructions",
|
||||
info="Instructions for the assistant, think of these as the system prompt.",
|
||||
),
|
||||
StrInput(
|
||||
name="model",
|
||||
display_name="Model name",
|
||||
info=(
|
||||
"Model for the assistant.\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."
|
||||
),
|
||||
# refresh_model=True
|
||||
),
|
||||
MultilineInput(
|
||||
name="env_set",
|
||||
display_name="Environment Set",
|
||||
info="Dummy input to allow chaining with Dotenv Component.",
|
||||
),
|
||||
]
|
||||
|
||||
def build(self, name: str, instructions: str, model: str, env_set: Optional[str] = None) -> str:
|
||||
if env_set is None:
|
||||
raise Exception("Environment variables not set")
|
||||
outputs = [
|
||||
Output(display_name="Assistant ID", name="assistant_id", method="process_inputs"),
|
||||
]
|
||||
|
||||
def process_inputs(self) -> Message:
|
||||
print(f"env_set is {self.env_set}")
|
||||
client = patch(OpenAI())
|
||||
assistant = client.beta.assistants.create(
|
||||
name=name,
|
||||
instructions=instructions,
|
||||
model=model,
|
||||
name=self.assistant_name,
|
||||
instructions=self.instructions,
|
||||
model=self.model,
|
||||
)
|
||||
return assistant.id
|
||||
message = Message(text=assistant.id)
|
||||
return message
|
||||
|
|
|
|||
|
|
@ -1,28 +1,32 @@
|
|||
from typing import Optional
|
||||
|
||||
from astra_assistants import patch # type: ignore
|
||||
from langflow.custom import Component
|
||||
from openai import OpenAI
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.inputs import MultilineInput
|
||||
from langflow.schema.message import Message
|
||||
from langflow.template import Output
|
||||
|
||||
|
||||
class AssistantsCreateThread(CustomComponent):
|
||||
class AssistantsCreateThread(Component):
|
||||
display_name = "Create Assistant Thread"
|
||||
description = "Creates a thread and returns the thread id"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"env_set": {
|
||||
"display_name": "Environment Set",
|
||||
"advanced": False,
|
||||
"info": "Dummy input to allow chaining with Dotenv Component.",
|
||||
},
|
||||
}
|
||||
inputs = [
|
||||
MultilineInput(
|
||||
name="env_set",
|
||||
display_name="Environment Set",
|
||||
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())
|
||||
|
||||
thread = client.beta.threads.create()
|
||||
thread_id = thread.id
|
||||
|
||||
return thread_id
|
||||
message = Message(text=thread_id)
|
||||
return message
|
||||
|
|
|
|||
|
|
@ -1,30 +1,32 @@
|
|||
import io
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.custom import Component
|
||||
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"
|
||||
description = "Load .env file into env vars"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"dotenv_file_content": {
|
||||
"display_name": "Dotenv file content",
|
||||
"advanced": False,
|
||||
"info": (
|
||||
"Paste the content of your .env file directly\n\n"
|
||||
"Since contents are sensitive, using a Global variable set as 'password' is recommended"
|
||||
),
|
||||
},
|
||||
}
|
||||
inputs = [
|
||||
MultilineSecretInput(
|
||||
name="dotenv_file_content",
|
||||
display_name="Dotenv file content",
|
||||
info="Paste the content of your .env file directly, since contents are sensitive, using a Global variable set as 'password' is recommended",
|
||||
)
|
||||
]
|
||||
|
||||
def build(self, dotenv_file_content: str) -> str:
|
||||
try:
|
||||
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"
|
||||
except Exception as e:
|
||||
raise e
|
||||
outputs = [
|
||||
Output(display_name="env_set", name="env_set", method="process_inputs"),
|
||||
]
|
||||
|
||||
def process_inputs(self) -> Message:
|
||||
fake_file = io.StringIO(self.dotenv_file_content)
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,30 +1,37 @@
|
|||
from typing import Optional
|
||||
from astra_assistants import patch # type: ignore
|
||||
from langflow.custom import Component
|
||||
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"
|
||||
description = "Assistant by id"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"assistant_id": {
|
||||
"display_name": "Assistant ID",
|
||||
"advanced": False,
|
||||
},
|
||||
"env_set": {
|
||||
"display_name": "Environment Set",
|
||||
"advanced": False,
|
||||
"info": "Dummy input to allow chaining with Dotenv Component.",
|
||||
},
|
||||
}
|
||||
inputs = [
|
||||
StrInput(
|
||||
name="assistant_id",
|
||||
display_name="Assistant ID",
|
||||
info="ID of the assistant",
|
||||
),
|
||||
MultilineInput(
|
||||
name="env_set",
|
||||
display_name="Environment Set",
|
||||
info="Dummy input to allow chaining with Dotenv Component.",
|
||||
),
|
||||
]
|
||||
|
||||
def build(self, assistant_id: str, env_set: Optional[str] = None) -> str:
|
||||
client = patch(OpenAI())
|
||||
assistant = client.beta.assistants.retrieve(
|
||||
assistant_id=assistant_id,
|
||||
outputs = [
|
||||
Output(display_name="Assistant Name", name="assistant_name", method="process_inputs"),
|
||||
]
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,14 +1,30 @@
|
|||
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"
|
||||
description = "Get env var"
|
||||
icon = "custom_components"
|
||||
|
||||
def build_config(self):
|
||||
return {"env_var_name": {"display_name": "Env var name"}}
|
||||
inputs = [
|
||||
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:
|
||||
return os.environ[env_var_name]
|
||||
outputs = [
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,20 +1,24 @@
|
|||
from typing import List
|
||||
|
||||
from astra_assistants import patch # type: ignore
|
||||
from langflow.template.field.base import Output
|
||||
from langflow.custom import Component
|
||||
from openai import OpenAI
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema.message import Message
|
||||
|
||||
|
||||
class AssistantsListAssistants(CustomComponent):
|
||||
class AssistantsListAssistants(Component):
|
||||
client = patch(OpenAI())
|
||||
display_name = "List Assistants"
|
||||
description = "Returns a list of assistant id's"
|
||||
|
||||
def build_config(self):
|
||||
return {}
|
||||
outputs = [
|
||||
Output(display_name="Assistants", name="assistants", method="process_inputs"),
|
||||
]
|
||||
|
||||
def build(self) -> List[str]:
|
||||
client = patch(OpenAI())
|
||||
assistants = client.beta.assistants.list()
|
||||
def process_inputs(self) -> Message:
|
||||
assistants = self.client.beta.assistants.list()
|
||||
id_list = [assistant.id for assistant in assistants]
|
||||
return id_list
|
||||
message = Message(
|
||||
# get text from list
|
||||
text="\n".join(id_list)
|
||||
)
|
||||
return message
|
||||
|
|
|
|||
|
|
@ -1,67 +1,94 @@
|
|||
from typing import Optional
|
||||
from astra_assistants import patch # type: ignore
|
||||
from typing import Any, Optional
|
||||
|
||||
from langflow.custom import Component
|
||||
from openai import OpenAI
|
||||
from openai.lib.streaming import AssistantEventHandler
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.inputs import MultilineInput
|
||||
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"
|
||||
description = "Executes an Assistant Run against a thread"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"assistant_id": {
|
||||
"display_name": "Assistant ID",
|
||||
"advanced": False,
|
||||
"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."
|
||||
),
|
||||
},
|
||||
"user_message": {
|
||||
"display_name": "User Message",
|
||||
"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 update_build_config(
|
||||
self,
|
||||
build_config: dotdict,
|
||||
field_value: Any,
|
||||
field_name: Optional[str] = None,
|
||||
):
|
||||
if field_name == "thread_id":
|
||||
if field_value is None:
|
||||
thread = self.client.beta.threads.create()
|
||||
self.thread_id = thread.id
|
||||
field_value
|
||||
build_config["thread_id"] = field_value
|
||||
|
||||
def build(
|
||||
self, assistant_id: str, user_message: str, thread_id: Optional[str] = None, env_set: Optional[str] = None
|
||||
) -> str:
|
||||
text = ""
|
||||
client = patch(OpenAI())
|
||||
inputs = [
|
||||
MultilineInput(
|
||||
name="assistant_id",
|
||||
display_name="Assistant ID",
|
||||
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:
|
||||
thread = client.beta.threads.create()
|
||||
thread_id = thread.id
|
||||
outputs = [Output(display_name="Assistant Response", name="assistant_response", method="process_inputs")]
|
||||
|
||||
# add the user message
|
||||
client.beta.threads.messages.create(thread_id=thread_id, role="user", content=user_message)
|
||||
def process_inputs(self) -> Message:
|
||||
try:
|
||||
text = ""
|
||||
|
||||
class EventHandler(AssistantEventHandler):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
if self.thread_id is None:
|
||||
thread = self.client.beta.threads.create()
|
||||
self.thread_id = thread.id
|
||||
|
||||
event_handler = EventHandler()
|
||||
with client.beta.threads.runs.create_and_stream(
|
||||
thread_id=thread_id,
|
||||
assistant_id=assistant_id,
|
||||
event_handler=event_handler,
|
||||
) as stream:
|
||||
# return stream.text_deltas
|
||||
for part in stream.text_deltas:
|
||||
text += part
|
||||
print(part)
|
||||
return text
|
||||
# add the user message
|
||||
self.client.beta.threads.messages.create(thread_id=self.thread_id, role="user", content=self.user_message)
|
||||
|
||||
class EventHandler(AssistantEventHandler):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def on_exception(self, exception: Exception) -> None:
|
||||
print(f"Exception: {exception}")
|
||||
raise exception
|
||||
|
||||
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}")
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ from .inputs import (
|
|||
MessageInput,
|
||||
MessageTextInput,
|
||||
MultilineInput,
|
||||
MultilineSecretInput,
|
||||
NestedDictInput,
|
||||
PromptInput,
|
||||
SecretStrInput,
|
||||
|
|
@ -30,6 +31,7 @@ __all__ = [
|
|||
"IntInput",
|
||||
"MessageInput",
|
||||
"MultilineInput",
|
||||
"MultilineSecretInput",
|
||||
"NestedDictInput",
|
||||
"PromptInput",
|
||||
"SecretStrInput",
|
||||
|
|
|
|||
|
|
@ -199,6 +199,20 @@ class MultilineInput(MessageTextInput, MultilineMixin, InputTraceMixin):
|
|||
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):
|
||||
"""
|
||||
Represents a field with password field type.
|
||||
|
|
@ -354,6 +368,7 @@ InputTypes = Union[
|
|||
HandleInput,
|
||||
IntInput,
|
||||
MultilineInput,
|
||||
MultilineSecretInput,
|
||||
NestedDictInput,
|
||||
PromptInput,
|
||||
SecretStrInput,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ from langflow.inputs import (
|
|||
MessageInput,
|
||||
MessageTextInput,
|
||||
MultilineInput,
|
||||
MultilineSecretInput,
|
||||
NestedDictInput,
|
||||
PromptInput,
|
||||
SecretStrInput,
|
||||
|
|
@ -31,6 +32,7 @@ __all__ = [
|
|||
"IntInput",
|
||||
"MessageInput",
|
||||
"MultilineInput",
|
||||
"MultilineSecretInput",
|
||||
"NestedDictInput",
|
||||
"PromptInput",
|
||||
"SecretStrInput",
|
||||
|
|
|
|||
1
src/frontend/package-lock.json
generated
1
src/frontend/package-lock.json
generated
|
|
@ -1078,6 +1078,7 @@
|
|||
},
|
||||
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
|
||||
"version": "1.3.0",
|
||||
"extraneous": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
|
|
|||
|
|
@ -39,6 +39,15 @@ export function StrRenderComponent({
|
|||
/>
|
||||
) : templateData.multiline ? (
|
||||
<TextAreaComponent
|
||||
password={templateData.password}
|
||||
updateVisibility={() => {
|
||||
if (templateData.password !== undefined) {
|
||||
handleOnNewValue(
|
||||
{ password: !templateData.password },
|
||||
{ skipSnapshot: true },
|
||||
);
|
||||
}
|
||||
}}
|
||||
id={`textarea_${id}`}
|
||||
disabled={disabled}
|
||||
editNode={editNode}
|
||||
|
|
|
|||
|
|
@ -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 { TypeModal } from "../../constants/enums";
|
||||
import GenericModal from "../../modals/genericModal";
|
||||
|
|
@ -14,6 +15,8 @@ export default function TextAreaComponent({
|
|||
disabled,
|
||||
editNode = false,
|
||||
id = "",
|
||||
password,
|
||||
updateVisibility,
|
||||
}: TextAreaComponentType): JSX.Element {
|
||||
// Clear text area
|
||||
useEffect(() => {
|
||||
|
|
@ -23,22 +26,29 @@ export default function TextAreaComponent({
|
|||
}, [disabled]);
|
||||
|
||||
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}>
|
||||
<Case condition={!editNode}>
|
||||
<Input
|
||||
id={id}
|
||||
data-testid={id}
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
className={editNode ? "input-edit-node w-full" : "w-full"}
|
||||
placeholder={"Type something..."}
|
||||
onChange={(event) => {
|
||||
onChange(event.target.value);
|
||||
}}
|
||||
/>
|
||||
</Case>
|
||||
<Input
|
||||
id={id}
|
||||
data-testid={id}
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
className={classNames(
|
||||
password !== undefined && password && value !== ""
|
||||
? "text-clip password"
|
||||
: "",
|
||||
editNode ? "input-edit-node" : "",
|
||||
password && editNode ? "pr-8" : "",
|
||||
password && !editNode ? "pr-10" : "",
|
||||
"w-full",
|
||||
)}
|
||||
placeholder={"Type something..."}
|
||||
onChange={(event) => {
|
||||
onChange(event.target.value);
|
||||
}}
|
||||
/>
|
||||
<GenericModal
|
||||
changeVisibility={updateVisibility}
|
||||
type={TypeModal.TEXT}
|
||||
buttonText="Finish Editing"
|
||||
modalTitle={EDIT_TEXT_MODAL_TITLE}
|
||||
|
|
@ -47,40 +57,77 @@ export default function TextAreaComponent({
|
|||
onChange(value);
|
||||
}}
|
||||
disabled={disabled}
|
||||
password={password}
|
||||
>
|
||||
{!editNode ? (
|
||||
<div className="flex items-center">
|
||||
<Button unstyled>
|
||||
<IconComponent
|
||||
strokeWidth={1.5}
|
||||
id={id}
|
||||
name="ExternalLink"
|
||||
className={
|
||||
"icons-parameters-comp shrink-0" +
|
||||
(disabled ? " text-ring" : " hover:text-accent-foreground")
|
||||
}
|
||||
/>
|
||||
</Button>
|
||||
</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>
|
||||
<div
|
||||
className={
|
||||
"flex items-center" + (password ? "relative left-6" : "")
|
||||
}
|
||||
>
|
||||
<Button unstyled>
|
||||
<IconComponent
|
||||
strokeWidth={1.5}
|
||||
id={id}
|
||||
name="ExternalLink"
|
||||
className={
|
||||
"icons-parameters-comp shrink-0" +
|
||||
(disabled ? " text-ring" : " hover:text-accent-foreground")
|
||||
}
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,22 +2,31 @@ import * as React from "react";
|
|||
import { cn } from "../../utils/utils";
|
||||
|
||||
export interface TextareaProps
|
||||
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
||||
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
||||
password?: boolean;
|
||||
editNode?: boolean;
|
||||
}
|
||||
|
||||
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
||||
({ className, ...props }, ref) => {
|
||||
({ className, password, editNode, ...props }, ref) => {
|
||||
return (
|
||||
<textarea
|
||||
className={cn(
|
||||
"nopan nodelete nodrag noflow textarea-primary",
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
<div className="w-full">
|
||||
<textarea
|
||||
className={cn(
|
||||
"nopan nodelete nodrag noflow textarea-primary",
|
||||
className,
|
||||
password ? "password" : "",
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
value={props.value as string}
|
||||
onChange={props.onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Textarea.displayName = "Textarea";
|
||||
|
||||
export { Textarea };
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ export default function GenericModal({
|
|||
disabled,
|
||||
id = "",
|
||||
readonly = false,
|
||||
password,
|
||||
changeVisibility,
|
||||
}: genericModalPropsType): JSX.Element {
|
||||
const [myButtonText] = useState(buttonText);
|
||||
const [myModalTitle] = useState(modalTitle);
|
||||
|
|
@ -201,14 +203,34 @@ export default function GenericModal({
|
|||
}
|
||||
})()}
|
||||
>
|
||||
<span className="pr-2" data-testid="modal-title">
|
||||
{myModalTitle}
|
||||
</span>
|
||||
<IconComponent
|
||||
name={myModalTitle === "Edit Prompt" ? "TerminalSquare" : "FileText"}
|
||||
className="h-6 w-6 pl-1 text-primary"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<div className="flex w-full items-start gap-3">
|
||||
<div className="flex">
|
||||
<span className="pr-2" data-testid="modal-title">
|
||||
{myModalTitle}
|
||||
</span>
|
||||
<IconComponent
|
||||
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.Content overflowHidden>
|
||||
<div className={classNames("flex h-full w-full rounded-lg border")}>
|
||||
|
|
@ -243,6 +265,7 @@ export default function GenericModal({
|
|||
/>
|
||||
) : type !== TypeModal.PROMPT ? (
|
||||
<Textarea
|
||||
password={password}
|
||||
ref={textRef}
|
||||
className="form-input h-full w-full resize-none overflow-auto rounded-lg focus-visible:ring-1"
|
||||
value={inputValue}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ export type InputFieldType = {
|
|||
list: boolean;
|
||||
show: boolean;
|
||||
readonly: boolean;
|
||||
password?: boolean;
|
||||
multiline?: boolean;
|
||||
value?: any;
|
||||
dynamic?: boolean;
|
||||
|
|
|
|||
|
|
@ -136,6 +136,8 @@ export type TextAreaComponentType = {
|
|||
editNode?: boolean;
|
||||
id?: string;
|
||||
readonly?: boolean;
|
||||
password?: boolean;
|
||||
updateVisibility?: () => void;
|
||||
};
|
||||
|
||||
export type TableComponentType = {
|
||||
|
|
@ -653,6 +655,8 @@ export type genericModalPropsType = {
|
|||
children: ReactNode;
|
||||
id?: string;
|
||||
readonly?: boolean;
|
||||
password?: boolean;
|
||||
changeVisibility?: () => void;
|
||||
};
|
||||
|
||||
export type newFlowModalPropsType = {
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ test("PromptTemplateComponent", async ({ page }) => {
|
|||
await page.getByTestId("edit-button-modal").click();
|
||||
|
||||
value =
|
||||
(await page.locator('//*[@id="textarea_str_edit_prompt"]').textContent()) ??
|
||||
(await page.locator('//*[@id="textarea_str_edit_prompt"]').inputValue()) ??
|
||||
"";
|
||||
|
||||
if (value != "prompt_value_!@#!@#") {
|
||||
|
|
@ -112,7 +112,7 @@ test("PromptTemplateComponent", async ({ page }) => {
|
|||
value =
|
||||
(await page
|
||||
.locator('//*[@id="textarea_str_edit_prompt1"]')
|
||||
.textContent()) ?? "";
|
||||
.inputValue()) ?? "";
|
||||
|
||||
if (value != "prompt_name_test_123123!@#!@#") {
|
||||
expect(false).toBeTruthy();
|
||||
|
|
@ -126,14 +126,14 @@ test("PromptTemplateComponent", async ({ page }) => {
|
|||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
await page.locator('//*[@id="textarea_str_edit_prompt1"]').click();
|
||||
await page.getByTestId('textarea_str_edit_prompt1-ExternalLink').click();
|
||||
await page
|
||||
.getByTestId("text-area-modal")
|
||||
.fill("prompt_edit_test_12312312321!@#$");
|
||||
|
||||
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
|
||||
.getByTestId("text-area-modal")
|
||||
.fill("prompt_edit_test_44444444444!@#$");
|
||||
|
|
@ -186,7 +186,7 @@ test("PromptTemplateComponent", async ({ page }) => {
|
|||
expect(await page.locator('//*[@id="showprompt1"]').isChecked()).toBeTruthy();
|
||||
|
||||
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!@#$") {
|
||||
|
|
@ -196,7 +196,7 @@ test("PromptTemplateComponent", async ({ page }) => {
|
|||
value =
|
||||
(await page
|
||||
.locator('//*[@id="textarea_str_edit_prompt1"]')
|
||||
.textContent()) ?? "";
|
||||
.inputValue()) ?? "";
|
||||
|
||||
if (value != "prompt_edit_test_12312312321!@#$") {
|
||||
expect(false).toBeTruthy();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue