test: fix output group preview test to be interactive (#2880)
* 🐛 (generalBugs-shard-5.spec.ts): fix test to wait for elements to be interactable before performing actions to prevent flakiness
* change temperature on canvas beside on component
This commit is contained in:
parent
77cc789e62
commit
7007c05a08
2 changed files with 163 additions and 134 deletions
|
|
@ -8,146 +8,145 @@ test("user must be able to freeze a path", async ({ page }) => {
|
||||||
"OPENAI_API_KEY required to run this test",
|
"OPENAI_API_KEY required to run this test",
|
||||||
);
|
);
|
||||||
|
|
||||||
const codeOpenAI = `
|
// const codeOpenAI = `
|
||||||
import operator
|
// import operator
|
||||||
from functools import reduce
|
// from functools import reduce
|
||||||
|
|
||||||
from langchain_openai import ChatOpenAI
|
// from langchain_openai import ChatOpenAI
|
||||||
from pydantic.v1 import SecretStr
|
// from pydantic.v1 import SecretStr
|
||||||
|
|
||||||
from langflow.base.constants import STREAM_INFO_TEXT
|
// from langflow.base.constants import STREAM_INFO_TEXT
|
||||||
from langflow.base.models.model import LCModelComponent
|
// from langflow.base.models.model import LCModelComponent
|
||||||
from langflow.base.models.openai_constants import MODEL_NAMES
|
// from langflow.base.models.openai_constants import MODEL_NAMES
|
||||||
from langflow.field_typing import LanguageModel
|
// from langflow.field_typing import LanguageModel
|
||||||
from langflow.inputs import (
|
// from langflow.inputs import (
|
||||||
BoolInput,
|
// BoolInput,
|
||||||
DictInput,
|
// DictInput,
|
||||||
DropdownInput,
|
// DropdownInput,
|
||||||
FloatInput,
|
// FloatInput,
|
||||||
IntInput,
|
// IntInput,
|
||||||
MessageInput,
|
// MessageInput,
|
||||||
SecretStrInput,
|
// SecretStrInput,
|
||||||
StrInput,
|
// StrInput,
|
||||||
)
|
// )
|
||||||
|
|
||||||
|
// class OpenAIModelComponent(LCModelComponent):
|
||||||
|
// display_name = "OpenAI"
|
||||||
|
// description = "Generates text using OpenAI LLMs."
|
||||||
|
// icon = "OpenAI"
|
||||||
|
// name = "OpenAIModel"
|
||||||
|
|
||||||
class OpenAIModelComponent(LCModelComponent):
|
// inputs = [
|
||||||
display_name = "OpenAI"
|
// MessageInput(name="input_value", display_name="Input"),
|
||||||
description = "Generates text using OpenAI LLMs."
|
// IntInput(
|
||||||
icon = "OpenAI"
|
// name="max_tokens",
|
||||||
name = "OpenAIModel"
|
// display_name="Max Tokens",
|
||||||
|
// advanced=True,
|
||||||
|
// info="The maximum number of tokens to generate. Set to 0 for unlimited tokens.",
|
||||||
|
// ),
|
||||||
|
// DictInput(name="model_kwargs", display_name="Model Kwargs", advanced=True),
|
||||||
|
// BoolInput(
|
||||||
|
// name="json_mode",
|
||||||
|
// display_name="JSON Mode",
|
||||||
|
// advanced=True,
|
||||||
|
// info="If True, it will output JSON regardless of passing a schema.",
|
||||||
|
// ),
|
||||||
|
// DictInput(
|
||||||
|
// name="output_schema",
|
||||||
|
// is_list=True,
|
||||||
|
// display_name="Schema",
|
||||||
|
// advanced=True,
|
||||||
|
// info="The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled.",
|
||||||
|
// ),
|
||||||
|
// DropdownInput(
|
||||||
|
// name="model_name", display_name="Model Name", advanced=False, options=MODEL_NAMES, value=MODEL_NAMES[0]
|
||||||
|
// ),
|
||||||
|
// StrInput(
|
||||||
|
// name="openai_api_base",
|
||||||
|
// display_name="OpenAI API Base",
|
||||||
|
// advanced=True,
|
||||||
|
// info="The base URL of the OpenAI API. Defaults to https://api.openai.com/v1. You can change this to use other APIs like JinaChat, LocalAI and Prem.",
|
||||||
|
// ),
|
||||||
|
// SecretStrInput(
|
||||||
|
// name="api_key",
|
||||||
|
// display_name="OpenAI API Key",
|
||||||
|
// info="The OpenAI API Key to use for the OpenAI model.",
|
||||||
|
// advanced=False,
|
||||||
|
// value="OPENAI_API_KEY",
|
||||||
|
// ),
|
||||||
|
// FloatInput(name="temperature", display_name="Temperature", value=0.1),
|
||||||
|
// BoolInput(name="stream", display_name="Stream", info=STREAM_INFO_TEXT, advanced=True),
|
||||||
|
// StrInput(
|
||||||
|
// name="system_message",
|
||||||
|
// display_name="System Message",
|
||||||
|
// info="System message to pass to the model.",
|
||||||
|
// advanced=True,
|
||||||
|
// ),
|
||||||
|
// IntInput(
|
||||||
|
// name="seed",
|
||||||
|
// display_name="Seed",
|
||||||
|
// info="The seed controls the reproducibility of the job.",
|
||||||
|
// advanced=True,
|
||||||
|
// value=1,
|
||||||
|
// ),
|
||||||
|
// ]
|
||||||
|
|
||||||
inputs = [
|
// def build_model(self) -> LanguageModel: # type: ignore[type-var]
|
||||||
MessageInput(name="input_value", display_name="Input"),
|
// # self.output_schema is a list of dictionaries
|
||||||
IntInput(
|
// # let's convert it to a dictionary
|
||||||
name="max_tokens",
|
// output_schema_dict: dict[str, str] = reduce(operator.ior, self.output_schema or {}, {})
|
||||||
display_name="Max Tokens",
|
// openai_api_key = self.api_key
|
||||||
advanced=True,
|
// temperature = self.temperature
|
||||||
info="The maximum number of tokens to generate. Set to 0 for unlimited tokens.",
|
// model_name: str = self.model_name
|
||||||
),
|
// max_tokens = self.max_tokens
|
||||||
DictInput(name="model_kwargs", display_name="Model Kwargs", advanced=True),
|
// model_kwargs = self.model_kwargs or {}
|
||||||
BoolInput(
|
// openai_api_base = self.openai_api_base or "https://api.openai.com/v1"
|
||||||
name="json_mode",
|
// json_mode = bool(output_schema_dict) or self.json_mode
|
||||||
display_name="JSON Mode",
|
// seed = self.seed
|
||||||
advanced=True,
|
|
||||||
info="If True, it will output JSON regardless of passing a schema.",
|
|
||||||
),
|
|
||||||
DictInput(
|
|
||||||
name="output_schema",
|
|
||||||
is_list=True,
|
|
||||||
display_name="Schema",
|
|
||||||
advanced=True,
|
|
||||||
info="The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled.",
|
|
||||||
),
|
|
||||||
DropdownInput(
|
|
||||||
name="model_name", display_name="Model Name", advanced=False, options=MODEL_NAMES, value=MODEL_NAMES[0]
|
|
||||||
),
|
|
||||||
StrInput(
|
|
||||||
name="openai_api_base",
|
|
||||||
display_name="OpenAI API Base",
|
|
||||||
advanced=True,
|
|
||||||
info="The base URL of the OpenAI API. Defaults to https://api.openai.com/v1. You can change this to use other APIs like JinaChat, LocalAI and Prem.",
|
|
||||||
),
|
|
||||||
SecretStrInput(
|
|
||||||
name="api_key",
|
|
||||||
display_name="OpenAI API Key",
|
|
||||||
info="The OpenAI API Key to use for the OpenAI model.",
|
|
||||||
advanced=False,
|
|
||||||
value="OPENAI_API_KEY",
|
|
||||||
),
|
|
||||||
FloatInput(name="temperature", display_name="Temperature", value=0.1),
|
|
||||||
BoolInput(name="stream", display_name="Stream", info=STREAM_INFO_TEXT, advanced=True),
|
|
||||||
StrInput(
|
|
||||||
name="system_message",
|
|
||||||
display_name="System Message",
|
|
||||||
info="System message to pass to the model.",
|
|
||||||
advanced=True,
|
|
||||||
),
|
|
||||||
IntInput(
|
|
||||||
name="seed",
|
|
||||||
display_name="Seed",
|
|
||||||
info="The seed controls the reproducibility of the job.",
|
|
||||||
advanced=True,
|
|
||||||
value=1,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
def build_model(self) -> LanguageModel: # type: ignore[type-var]
|
// if openai_api_key:
|
||||||
# self.output_schema is a list of dictionaries
|
// api_key = SecretStr(openai_api_key)
|
||||||
# let's convert it to a dictionary
|
// else:
|
||||||
output_schema_dict: dict[str, str] = reduce(operator.ior, self.output_schema or {}, {})
|
// api_key = None
|
||||||
openai_api_key = self.api_key
|
// output = ChatOpenAI(
|
||||||
temperature = self.temperature
|
// max_tokens=max_tokens or None,
|
||||||
model_name: str = self.model_name
|
// model_kwargs=model_kwargs,
|
||||||
max_tokens = self.max_tokens
|
// model=model_name,
|
||||||
model_kwargs = self.model_kwargs or {}
|
// base_url=openai_api_base,
|
||||||
openai_api_base = self.openai_api_base or "https://api.openai.com/v1"
|
// api_key=api_key,
|
||||||
json_mode = bool(output_schema_dict) or self.json_mode
|
// temperature=0.8,
|
||||||
seed = self.seed
|
// seed=seed,
|
||||||
|
// )
|
||||||
|
// if json_mode:
|
||||||
|
// if output_schema_dict:
|
||||||
|
// output = output.with_structured_output(schema=output_schema_dict, method="json_mode") # type: ignore
|
||||||
|
// else:
|
||||||
|
// output = output.bind(response_format={"type": "json_object"}) # type: ignore
|
||||||
|
|
||||||
if openai_api_key:
|
// return output # type: ignore
|
||||||
api_key = SecretStr(openai_api_key)
|
|
||||||
else:
|
|
||||||
api_key = None
|
|
||||||
output = ChatOpenAI(
|
|
||||||
max_tokens=max_tokens or None,
|
|
||||||
model_kwargs=model_kwargs,
|
|
||||||
model=model_name,
|
|
||||||
base_url=openai_api_base,
|
|
||||||
api_key=api_key,
|
|
||||||
temperature=0.8,
|
|
||||||
seed=seed,
|
|
||||||
)
|
|
||||||
if json_mode:
|
|
||||||
if output_schema_dict:
|
|
||||||
output = output.with_structured_output(schema=output_schema_dict, method="json_mode") # type: ignore
|
|
||||||
else:
|
|
||||||
output = output.bind(response_format={"type": "json_object"}) # type: ignore
|
|
||||||
|
|
||||||
return output # type: ignore
|
// def _get_exception_message(self, e: Exception):
|
||||||
|
// """
|
||||||
|
// Get a message from an OpenAI exception.
|
||||||
|
|
||||||
def _get_exception_message(self, e: Exception):
|
// Args:
|
||||||
"""
|
// exception (Exception): The exception to get the message from.
|
||||||
Get a message from an OpenAI exception.
|
|
||||||
|
|
||||||
Args:
|
// Returns:
|
||||||
exception (Exception): The exception to get the message from.
|
// str: The message from the exception.
|
||||||
|
// """
|
||||||
|
|
||||||
Returns:
|
// try:
|
||||||
str: The message from the exception.
|
// from openai import BadRequestError
|
||||||
"""
|
// except ImportError:
|
||||||
|
// return
|
||||||
|
// if isinstance(e, BadRequestError):
|
||||||
|
// message = e.body.get("message") # type: ignore
|
||||||
|
// if message:
|
||||||
|
// return message
|
||||||
|
// return
|
||||||
|
|
||||||
try:
|
// `;
|
||||||
from openai import BadRequestError
|
|
||||||
except ImportError:
|
|
||||||
return
|
|
||||||
if isinstance(e, BadRequestError):
|
|
||||||
message = e.body.get("message") # type: ignore
|
|
||||||
if message:
|
|
||||||
return message
|
|
||||||
return
|
|
||||||
|
|
||||||
`;
|
|
||||||
|
|
||||||
if (!process.env.CI) {
|
if (!process.env.CI) {
|
||||||
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
|
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
|
||||||
|
|
@ -205,13 +204,17 @@ class OpenAIModelComponent(LCModelComponent):
|
||||||
await page.getByTestId("dropdown-model_name").click();
|
await page.getByTestId("dropdown-model_name").click();
|
||||||
await page.getByTestId("gpt-4o-1-option").click();
|
await page.getByTestId("gpt-4o-1-option").click();
|
||||||
|
|
||||||
await page.getByText("OpenAI").first().click();
|
// await page.getByText("OpenAI").first().click();
|
||||||
|
|
||||||
await page.getByTestId("code-button-modal").first().click();
|
// await page.getByTestId("code-button-modal").first().click();
|
||||||
|
|
||||||
await page.locator("textarea").press("Control+a");
|
// await page.locator("textarea").press("Control+a");
|
||||||
await page.locator("textarea").fill(codeOpenAI);
|
// await page.locator("textarea").fill(codeOpenAI);
|
||||||
await page.locator('//*[@id="checkAndSaveBtn"]').click();
|
// await page.locator('//*[@id="checkAndSaveBtn"]').click();
|
||||||
|
|
||||||
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
|
await page.getByTestId("float-input").fill("1.0");
|
||||||
|
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
|
|
@ -234,6 +237,10 @@ class OpenAIModelComponent(LCModelComponent):
|
||||||
|
|
||||||
await page.waitForTimeout(3000);
|
await page.waitForTimeout(3000);
|
||||||
|
|
||||||
|
await page.getByTestId("float-input").fill("1.2");
|
||||||
|
|
||||||
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
await page.getByTestId("button_run_chat output").click();
|
await page.getByTestId("button_run_chat output").click();
|
||||||
await page.waitForSelector("text=built successfully", { timeout: 30000 });
|
await page.waitForSelector("text=built successfully", { timeout: 30000 });
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -196,19 +196,26 @@ test("should be able to see output preview from grouped components", async ({
|
||||||
.getByTestId("popover-anchor-input-input_value")
|
.getByTestId("popover-anchor-input-input_value")
|
||||||
.nth(0)
|
.nth(0)
|
||||||
.fill(randomName);
|
.fill(randomName);
|
||||||
|
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
await page
|
await page
|
||||||
.getByTestId("popover-anchor-input-input_value")
|
.getByTestId("popover-anchor-input-input_value")
|
||||||
.nth(1)
|
.nth(1)
|
||||||
.fill(secondRandomName);
|
.fill(secondRandomName);
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.getByPlaceholder("Type something...", { exact: true })
|
.getByPlaceholder("Type something...", { exact: true })
|
||||||
.nth(6)
|
.nth(6)
|
||||||
.fill(thirdRandomName);
|
.fill(thirdRandomName);
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.getByPlaceholder("Type something...", { exact: true })
|
.getByPlaceholder("Type something...", { exact: true })
|
||||||
.nth(3)
|
.nth(3)
|
||||||
.fill("-");
|
.fill("-");
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.getByPlaceholder("Type something...", { exact: true })
|
.getByPlaceholder("Type something...", { exact: true })
|
||||||
.nth(4)
|
.nth(4)
|
||||||
|
|
@ -229,9 +236,24 @@ test("should be able to see output preview from grouped components", async ({
|
||||||
await page.getByTestId("output-inspection-combined text").first(),
|
await page.getByTestId("output-inspection-combined text").first(),
|
||||||
).not.toBeDisabled();
|
).not.toBeDisabled();
|
||||||
await page.getByTestId("output-inspection-combined text").first().click();
|
await page.getByTestId("output-inspection-combined text").first().click();
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
await page.getByText("Component Output").isVisible();
|
await page.getByText("Component Output").isVisible();
|
||||||
|
|
||||||
const text = await page.getByPlaceholder("Empty").textContent();
|
const text = await page.getByPlaceholder("Empty").textContent();
|
||||||
expect(text).toBe(`${randomName}-${secondRandomName}-${thirdRandomName}`);
|
|
||||||
|
const permutations = [
|
||||||
|
`${randomName}-${secondRandomName}-${thirdRandomName}`,
|
||||||
|
`${randomName}-${thirdRandomName}-${secondRandomName}`,
|
||||||
|
`${thirdRandomName}-${randomName}-${secondRandomName}`,
|
||||||
|
`${thirdRandomName}-${secondRandomName}-${randomName}`,
|
||||||
|
`${secondRandomName}-${randomName}-${thirdRandomName}`,
|
||||||
|
`${secondRandomName}-${thirdRandomName}-${randomName}`,
|
||||||
|
];
|
||||||
|
|
||||||
|
const isPermutationIncluded = permutations.some((permutation) =>
|
||||||
|
text!.includes(permutation),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(isPermutationIncluded).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue