mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-24 00:39:35 +08:00
Merge branch 'main' into docs-publish-v0
This commit is contained in:
5
.github/changes-filter.yaml
vendored
5
.github/changes-filter.yaml
vendored
@ -28,6 +28,8 @@ starter-projects:
|
||||
- "src/frontend/src/pages/MainPage/**"
|
||||
- "src/frontend/src/utils/reactflowUtils.ts"
|
||||
- "src/frontend/tests/extended/features/**"
|
||||
- "src/backend/base/langflow/custom/**"
|
||||
- "src/backend/base/langflow/graph/**"
|
||||
|
||||
components:
|
||||
- "src/frontend/src/components/**"
|
||||
@ -38,7 +40,6 @@ components:
|
||||
- "src/frontend/src/CustomNodes/**"
|
||||
- "src/frontend/src/style/**"
|
||||
- "src/frontend/src/utils/styleUtils.ts"
|
||||
- "src/backend/base/langflow/components/**"
|
||||
- "src/frontend/tests/core/features/**"
|
||||
- "src/frontend/tests/core/integrations/**"
|
||||
- "src/frontend/tests/core/regression/**"
|
||||
@ -49,6 +50,8 @@ components:
|
||||
- "src/backend/base/langflow/schema/**"
|
||||
- "src/backend/base/langflow/graph/**"
|
||||
- "src/backend/base/langflow/utils/**"
|
||||
- "src/backend/base/langflow/custom/**"
|
||||
- "src/backend/base/langflow/components/**"
|
||||
|
||||
workspace:
|
||||
- "src/backend/base/langflow/inputs/**"
|
||||
|
||||
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
@ -101,6 +101,12 @@ jobs:
|
||||
frontend-tests: ${{ steps.filter.outputs.frontend-tests }}
|
||||
components-changes: ${{ steps.filter.outputs.components-changes }}
|
||||
starter-projects-changes: ${{ steps.filter.outputs.starter-projects-changes }}
|
||||
starter-projects: ${{ steps.filter.outputs.starter-projects }}
|
||||
components: ${{ steps.filter.outputs.components }}
|
||||
workspace: ${{ steps.filter.outputs.workspace }}
|
||||
api: ${{ steps.filter.outputs.api }}
|
||||
database: ${{ steps.filter.outputs.database }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
@ -119,11 +125,10 @@ jobs:
|
||||
uses: ./.github/workflows/python_test.yml
|
||||
with:
|
||||
python-versions: ${{ inputs.python-versions || '["3.10"]' }}
|
||||
|
||||
test-frontend:
|
||||
needs: path-filter
|
||||
name: Run Frontend Tests
|
||||
if: ${{ needs.path-filter.outputs.frontend == 'true' || needs.path-filter.outputs.frontend-tests == 'true' || needs.path-filter.outputs.components-changes == 'true' || needs.path-filter.outputs.starter-projects-changes == 'true' }}
|
||||
if: ${{ needs.path-filter.outputs.frontend == 'true' || needs.path-filter.outputs.frontend-tests == 'true' || needs.path-filter.outputs.components-changes == 'true' || needs.path-filter.outputs.starter-projects-changes == 'true' || needs.path-filter.outputs.starter-projects == 'true' || needs.path-filter.outputs.components == 'true' || needs.path-filter.outputs.workspace == 'true' || needs.path-filter.outputs.api == 'true' || needs.path-filter.outputs.database == 'true' }}
|
||||
uses: ./.github/workflows/typescript_test.yml
|
||||
with:
|
||||
tests_folder: ${{ inputs.frontend-tests-folder }}
|
||||
|
||||
@ -168,8 +168,8 @@ In the *Documentation Terminal* (from the project root directory), run the follo
|
||||
|
||||
```bash
|
||||
cd docs
|
||||
npm install
|
||||
npm run start
|
||||
yarn install
|
||||
yarn start
|
||||
```
|
||||
|
||||
If the frontend service is running on port `3000` you might be prompted `Would you like to run the app on another port instead?`, in which case answer "yes". You will get output similar to:
|
||||
|
||||
@ -205,32 +205,32 @@ body {
|
||||
height: auto;
|
||||
display: block;
|
||||
margin: 1rem auto;
|
||||
max-height: 500px;
|
||||
max-height: 600px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* Default size for most images */
|
||||
.markdown img:not(.resized-image) {
|
||||
max-width: 500px;
|
||||
max-width: 600px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.resized-image {
|
||||
width: 250px;
|
||||
max-height: 400px;
|
||||
width: 300px;
|
||||
max-height: 500px;
|
||||
}
|
||||
|
||||
/* Responsive images on mobile devices */
|
||||
@media (max-width: 768px) {
|
||||
.markdown img:not(.resized-image) {
|
||||
max-width: 100%;
|
||||
max-height: 400px;
|
||||
max-height: 500px;
|
||||
}
|
||||
|
||||
.resized-image {
|
||||
width: 100%;
|
||||
max-width: 250px;
|
||||
max-height: 300px;
|
||||
max-width: 300px;
|
||||
max-height: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -210,7 +210,7 @@ curl -X POST \
|
||||
To test the webhook component:
|
||||
|
||||
1. Add a **Webhook** component to the flow.
|
||||
2. Connect the **Webhook** component's **Data** output to the **Data** input of a [Data to Message](/components-processing#parse-data-data-to-message) component.
|
||||
2. Connect the **Webhook** component's **Data** output to the **Data** input of a [Data to Message](/components-processing#data-to-message) component.
|
||||
3. Connect the **Data to Message** component's **Message** output to the **Text** input of a [Chat Output](/components-io#chat-output) component.
|
||||
4. To send a POST request, copy the code from the **Webhook cURL** tab in the **API** pane and paste it into a terminal.
|
||||
5. Send the POST request.
|
||||
|
||||
@ -17,7 +17,7 @@ The component offers control over chunk size, overlap, and separator, which affe
|
||||
|
||||
## Alter metadata
|
||||
|
||||
This component modifies metadata of input objects. It can add new metadata, update existing metadata, and remove specified metadata fields. The component works with both [Message](/concepts-objects#message-object) and [Data](/concepts-objects) objects, and can also create a new Data object from user-provided text.
|
||||
This component modifies metadata of input objects. It can add new metadata, update existing metadata, and remove specified metadata fields. The component works with both [Message](/concepts-objects#message-object) and [Data](/concepts-objects#data-object) objects, and can also create a new Data object from user-provided text.
|
||||
|
||||
### Inputs
|
||||
|
||||
@ -164,7 +164,7 @@ This component transforms structured data into human-readable text formats, allo
|
||||
This component is in **Beta** as of Langflow version 1.1.3, and is not yet fully supported.
|
||||
:::
|
||||
|
||||
This component filters a [Data](/concepts-objects#data-objects) object based on a list of keys.
|
||||
This component filters a [Data](/concepts-objects#data-object) object based on a list of keys.
|
||||
|
||||
### Inputs
|
||||
|
||||
|
||||
@ -123,7 +123,7 @@ The [File](/components-data#file) component loads files from your local machine.
|
||||
3. Click **Processing**, select the **Split Text** component, and then drag it to the canvas.
|
||||
The [Split Text](/components-processing#split-text) component splits the loaded text into smaller chunks.
|
||||
4. Click **Processing**, select the **Parse Data** component, and then drag it to the canvas.
|
||||
The [Parse Data](/components-processing#parse-data) component converts the data from the **Astra DB** component into plain text.
|
||||
The [Data to Message](/components-processing#data-to-message) component converts the data from the **Astra DB** component into plain text.
|
||||
5. Click **Embeddings**, select the **OpenAI Embeddings** component, and then drag it to the canvas.
|
||||
The [OpenAI Embeddings](/components-embedding-models#openai-embeddings) component generates embeddings for the user's input, which are compared to the vector data in the database.
|
||||
6. Connect the new components into the existing flow, so your flow looks like this:
|
||||
|
||||
@ -40,7 +40,7 @@ const config = {
|
||||
docs: {
|
||||
routeBasePath: "/", // Serve the docs at the site's root
|
||||
sidebarPath: require.resolve("./sidebars.js"), // Use sidebars.js file
|
||||
sidebarCollapsed: true,
|
||||
sidebarCollapsed: false,
|
||||
beforeDefaultRemarkPlugins: [
|
||||
[
|
||||
remarkCodeHike,
|
||||
@ -258,7 +258,7 @@ const config = {
|
||||
},
|
||||
docs: {
|
||||
sidebar: {
|
||||
hideable: true,
|
||||
hideable: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
@ -140,16 +140,16 @@ module.exports = {
|
||||
type: "category",
|
||||
label: "API reference",
|
||||
items: [
|
||||
{
|
||||
type: "doc",
|
||||
id: "API-Reference/api-reference-api-examples",
|
||||
label: "API examples",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
label: "API documentation",
|
||||
href: "/api",
|
||||
},
|
||||
{
|
||||
type: "doc",
|
||||
id: "API-Reference/api-reference-api-examples",
|
||||
label: "API examples",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@ -981,17 +981,25 @@ class Component(CustomComponent):
|
||||
return {"repr": custom_repr, "raw": raw, "type": artifact_type}
|
||||
|
||||
def _process_raw_result(self, result):
|
||||
"""Process the raw result of the component."""
|
||||
if len(self.outputs) == 1:
|
||||
return self.status or self.extract_data(result)
|
||||
return self.extract_data(result)
|
||||
|
||||
def extract_data(self, result):
|
||||
"""Extract the data from the result. this is where the self.status is set."""
|
||||
if isinstance(result, Message):
|
||||
self.status = result.get_text()
|
||||
return (
|
||||
self.status if self.status is not None else "No text available"
|
||||
) # Provide a default message if .text_key is missing
|
||||
if hasattr(result, "data"):
|
||||
return result.data
|
||||
if hasattr(result, "model_dump"):
|
||||
return result.model_dump()
|
||||
if isinstance(result, Data | dict | str):
|
||||
return result.data if isinstance(result, Data) else result
|
||||
|
||||
if self.status:
|
||||
return self.status
|
||||
return result
|
||||
|
||||
@ -116,10 +116,10 @@ class Graph:
|
||||
|
||||
self.top_level_vertices: list[str] = []
|
||||
self.vertex_map: dict[str, Vertex] = {}
|
||||
self.predecessor_map: dict[str, set[str]] = defaultdict(set)
|
||||
self.successor_map: dict[str, set[str]] = defaultdict(set)
|
||||
self.predecessor_map: dict[str, list[str]] = defaultdict(list)
|
||||
self.successor_map: dict[str, list[str]] = defaultdict(list)
|
||||
self.in_degree_map: dict[str, int] = defaultdict(int)
|
||||
self.parent_child_map: dict[str, set[str]] = defaultdict(set)
|
||||
self.parent_child_map: dict[str, list[str]] = defaultdict(list)
|
||||
self._run_queue: deque[str] = deque()
|
||||
self._first_layer: list[str] = []
|
||||
self._lock = asyncio.Lock()
|
||||
@ -469,10 +469,10 @@ class Graph:
|
||||
self.add_edge(edge)
|
||||
source_id = edge["data"]["sourceHandle"]["id"]
|
||||
target_id = edge["data"]["targetHandle"]["id"]
|
||||
self.predecessor_map[target_id].add(source_id)
|
||||
self.successor_map[source_id].add(target_id)
|
||||
self.predecessor_map[target_id].append(source_id)
|
||||
self.successor_map[source_id].append(target_id)
|
||||
self.in_degree_map[target_id] += 1
|
||||
self.parent_child_map[source_id].add(target_id)
|
||||
self.parent_child_map[source_id].append(target_id)
|
||||
|
||||
def add_node(self, node: NodeData) -> None:
|
||||
self._vertices.append(node)
|
||||
@ -1554,7 +1554,7 @@ class Graph:
|
||||
logger.debug("Graph processing complete")
|
||||
return self
|
||||
|
||||
def find_next_runnable_vertices(self, vertex_successors_ids: set[str]) -> list[str]:
|
||||
def find_next_runnable_vertices(self, vertex_successors_ids: list[str]) -> list[str]:
|
||||
next_runnable_vertices = set()
|
||||
for v_id in sorted(vertex_successors_ids):
|
||||
if not self.is_vertex_runnable(v_id):
|
||||
@ -2023,7 +2023,10 @@ class Graph:
|
||||
|
||||
def build_in_degree(self, edges: list[CycleEdge]) -> dict[str, int]:
|
||||
in_degree: dict[str, int] = defaultdict(int)
|
||||
|
||||
for edge in edges:
|
||||
# We don't need to count if a Component connects more than one
|
||||
# time to the same vertex.
|
||||
in_degree[edge.target_id] += 1
|
||||
for vertex in self.vertices:
|
||||
if vertex.id not in in_degree:
|
||||
@ -2031,13 +2034,13 @@ class Graph:
|
||||
return in_degree
|
||||
|
||||
@staticmethod
|
||||
def build_adjacency_maps(edges: list[CycleEdge]) -> tuple[dict[str, set[str]], dict[str, set[str]]]:
|
||||
def build_adjacency_maps(edges: list[CycleEdge]) -> tuple[dict[str, list[str]], dict[str, list[str]]]:
|
||||
"""Returns the adjacency maps for the graph."""
|
||||
predecessor_map: dict[str, set[str]] = defaultdict(set)
|
||||
successor_map: dict[str, set[str]] = defaultdict(set)
|
||||
predecessor_map: dict[str, list[str]] = defaultdict(list)
|
||||
successor_map: dict[str, list[str]] = defaultdict(list)
|
||||
for edge in edges:
|
||||
predecessor_map[edge.target_id].add(edge.source_id)
|
||||
successor_map[edge.source_id].add(edge.target_id)
|
||||
predecessor_map[edge.target_id].append(edge.source_id)
|
||||
successor_map[edge.source_id].append(edge.target_id)
|
||||
return predecessor_map, successor_map
|
||||
|
||||
def __to_dict(self) -> dict[str, dict[str, list[str]]]:
|
||||
|
||||
@ -2,9 +2,9 @@ from collections import defaultdict
|
||||
|
||||
|
||||
class RunnableVerticesManager:
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self.run_map: dict[str, list[str]] = defaultdict(list) # Tracks successors of each vertex
|
||||
self.run_predecessors: dict[str, set[str]] = defaultdict(set) # Tracks predecessors for each vertex
|
||||
self.run_predecessors: dict[str, list[str]] = defaultdict(list) # Tracks predecessors for each vertex
|
||||
self.vertices_to_run: set[str] = set() # Set of vertices that are ready to run
|
||||
self.vertices_being_run: set[str] = set() # Set of vertices that are currently running
|
||||
self.cycle_vertices: set[str] = set() # Set of vertices that are in a cycle
|
||||
@ -74,13 +74,14 @@ class RunnableVerticesManager:
|
||||
bool: True if all predecessor conditions are met, False otherwise
|
||||
"""
|
||||
# Get pending predecessors, return True if none exist
|
||||
if not (pending := self.run_predecessors.get(vertex_id, set())):
|
||||
pending = self.run_predecessors.get(vertex_id, [])
|
||||
if not pending:
|
||||
return True
|
||||
|
||||
# For cycle vertices, check if any pending predecessors are also in cycle
|
||||
# Using set intersection is faster than iteration
|
||||
if vertex_id in self.cycle_vertices:
|
||||
return is_loop or not bool(pending.intersection(self.cycle_vertices))
|
||||
return is_loop or not bool(set(pending) & self.cycle_vertices)
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@ -461,8 +461,8 @@ def find_cycle_vertices(edges):
|
||||
def layered_topological_sort(
|
||||
vertices_ids: set[str],
|
||||
in_degree_map: dict[str, int],
|
||||
successor_map: dict[str, set[str]],
|
||||
predecessor_map: dict[str, set[str]],
|
||||
successor_map: dict[str, list[str]],
|
||||
predecessor_map: dict[str, list[str]],
|
||||
start_id: str | None = None,
|
||||
cycle_vertices: set[str] | None = None,
|
||||
is_input_vertex: Callable[[str], bool] | None = None, # noqa: ARG001
|
||||
@ -780,8 +780,8 @@ def get_sorted_vertices(
|
||||
start_component_id: str | None = None,
|
||||
graph_dict: dict[str, Any] | None = None,
|
||||
in_degree_map: dict[str, int] | None = None,
|
||||
successor_map: dict[str, set[str]] | None = None,
|
||||
predecessor_map: dict[str, set[str]] | None = None,
|
||||
successor_map: dict[str, list[str]] | None = None,
|
||||
predecessor_map: dict[str, list[str]] | None = None,
|
||||
is_input_vertex: Callable[[str], bool] | None = None,
|
||||
get_vertex_predecessors: Callable[[str], list[str]] | None = None,
|
||||
get_vertex_successors: Callable[[str], list[str]] | None = None,
|
||||
@ -826,18 +826,18 @@ def get_sorted_vertices(
|
||||
successor_map = {}
|
||||
for vertex_id in vertices_ids:
|
||||
if get_vertex_successors is not None:
|
||||
successor_map[vertex_id] = set(get_vertex_successors(vertex_id))
|
||||
successor_map[vertex_id] = get_vertex_successors(vertex_id)
|
||||
else:
|
||||
successor_map[vertex_id] = set()
|
||||
successor_map[vertex_id] = []
|
||||
|
||||
# Build predecessor_map if not provided
|
||||
if predecessor_map is None:
|
||||
predecessor_map = {}
|
||||
for vertex_id in vertices_ids:
|
||||
if get_vertex_predecessors is not None:
|
||||
predecessor_map[vertex_id] = set(get_vertex_predecessors(vertex_id))
|
||||
predecessor_map[vertex_id] = get_vertex_predecessors(vertex_id)
|
||||
else:
|
||||
predecessor_map[vertex_id] = set()
|
||||
predecessor_map[vertex_id] = []
|
||||
|
||||
# If we have a stop component, we need to filter out all vertices
|
||||
# that are not predecessors of the stop component
|
||||
|
||||
@ -79,7 +79,7 @@ class Vertex:
|
||||
self.built_object: Any = UnbuiltObject()
|
||||
self.built_result: Any = None
|
||||
self.built = False
|
||||
self._successors_ids: set[str] | None = None
|
||||
self._successors_ids: list[str] | None = None
|
||||
self.artifacts: dict[str, Any] = {}
|
||||
self.artifacts_raw: dict[str, Any] = {}
|
||||
self.artifacts_type: dict[str, str] = {}
|
||||
@ -207,8 +207,8 @@ class Vertex:
|
||||
return self.graph.get_successors(self)
|
||||
|
||||
@property
|
||||
def successors_ids(self) -> set[str]:
|
||||
return self.graph.successor_map.get(self.id, set())
|
||||
def successors_ids(self) -> list[str]:
|
||||
return self.graph.successor_map.get(self.id, [])
|
||||
|
||||
def __getstate__(self):
|
||||
state = self.__dict__.copy()
|
||||
|
||||
@ -466,7 +466,7 @@ class StateVertex(ComponentVertex):
|
||||
self.is_state = False
|
||||
|
||||
@property
|
||||
def successors_ids(self) -> set[str]:
|
||||
def successors_ids(self) -> list[str]:
|
||||
if self._successors_ids is None:
|
||||
self.is_state = False
|
||||
return super().successors_ids
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -5,3 +5,20 @@ export const track = async (
|
||||
): Promise<void> => {
|
||||
return;
|
||||
};
|
||||
|
||||
export const trackFlowBuild = async (
|
||||
flowName: string,
|
||||
isError?: boolean,
|
||||
properties?: Record<string, any>,
|
||||
): Promise<void> => {
|
||||
return;
|
||||
};
|
||||
|
||||
export const trackDataLoaded = async (
|
||||
flowId?: string,
|
||||
flowName?: string,
|
||||
component?: string,
|
||||
componentId?: string,
|
||||
): Promise<void> => {
|
||||
return;
|
||||
};
|
||||
|
||||
@ -2,7 +2,12 @@ import {
|
||||
BROKEN_EDGES_WARNING,
|
||||
componentsToIgnoreUpdate,
|
||||
} from "@/constants/constants";
|
||||
import { track } from "@/customization/utils/analytics";
|
||||
import { ENABLE_DATASTAX_LANGFLOW } from "@/customization/feature-flags";
|
||||
import {
|
||||
track,
|
||||
trackDataLoaded,
|
||||
trackFlowBuild,
|
||||
} from "@/customization/utils/analytics";
|
||||
import { brokenEdgeMessage } from "@/utils/utils";
|
||||
import {
|
||||
EdgeChange,
|
||||
@ -19,7 +24,7 @@ import {
|
||||
MISSED_ERROR_ALERT,
|
||||
} from "../constants/alerts_constants";
|
||||
import { BuildStatus } from "../constants/enums";
|
||||
import { VertexBuildTypeAPI } from "../types/api";
|
||||
import { LogsLogType, VertexBuildTypeAPI } from "../types/api";
|
||||
import { ChatInputType, ChatOutputType } from "../types/chat";
|
||||
import {
|
||||
AllNodeType,
|
||||
@ -699,6 +704,28 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||
...get().verticesBuild!.verticesIds,
|
||||
...next_vertices_ids,
|
||||
];
|
||||
if (
|
||||
ENABLE_DATASTAX_LANGFLOW &&
|
||||
vertexBuildData?.id?.includes("AstraDB")
|
||||
) {
|
||||
const search_results: LogsLogType[] = Object.values(
|
||||
vertexBuildData?.data?.logs?.search_results,
|
||||
);
|
||||
search_results.forEach((log) => {
|
||||
if (
|
||||
log.message.includes("Adding") &&
|
||||
log.message.includes("documents") &&
|
||||
log.message.includes("Vector Store")
|
||||
) {
|
||||
trackDataLoaded(
|
||||
get().currentFlow?.id,
|
||||
get().currentFlow?.name,
|
||||
"AstraDB Vector Store",
|
||||
vertexBuildData?.id,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
get().updateVerticesBuild({
|
||||
verticesIds: newIds,
|
||||
verticesLayers: newLayers,
|
||||
@ -747,6 +774,9 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||
false,
|
||||
);
|
||||
get().setIsBuilding(false);
|
||||
trackFlowBuild(get().currentFlow?.name ?? "Unknown", false, {
|
||||
flowId: get().currentFlow?.id,
|
||||
});
|
||||
},
|
||||
onBuildUpdate: handleBuildUpdate,
|
||||
onBuildError: (title: string, list: string[], elementList) => {
|
||||
@ -767,6 +797,10 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||
setErrorData({ list, title });
|
||||
get().setIsBuilding(false);
|
||||
get().buildController.abort();
|
||||
trackFlowBuild(get().currentFlow?.name ?? "Unknown", true, {
|
||||
flowId: get().currentFlow?.id,
|
||||
error: list,
|
||||
});
|
||||
},
|
||||
onBuildStart: (elementList) => {
|
||||
const idList = elementList
|
||||
|
||||
@ -266,6 +266,7 @@ export function groupByFamily(
|
||||
return (
|
||||
template?.type &&
|
||||
template?.show &&
|
||||
!template?.advanced &&
|
||||
((!excludeTypes.has(template.type) &&
|
||||
baseClassesSet.has(template.type)) ||
|
||||
(template?.input_types &&
|
||||
|
||||
@ -17,16 +17,22 @@ test.describe("group node test", () => {
|
||||
await page.getByTestId("fit_view").first().click();
|
||||
|
||||
await page.getByTestId("title-OpenAI").click();
|
||||
await page.getByTestId("title-OpenAI").click({ modifiers: ["Control"] });
|
||||
await page.getByTestId("title-Prompt").click({ modifiers: ["Control"] });
|
||||
await page.getByTestId("title-OpenAI").click({ modifiers: ["Control"] });
|
||||
await page
|
||||
.getByTestId("title-OpenAI")
|
||||
.click({ modifiers: ["ControlOrMeta"] });
|
||||
await page
|
||||
.getByTestId("title-Prompt")
|
||||
.click({ modifiers: ["ControlOrMeta"] });
|
||||
await page
|
||||
.getByTestId("title-OpenAI")
|
||||
.click({ modifiers: ["ControlOrMeta"] });
|
||||
|
||||
await page.getByRole("button", { name: "Group" }).click();
|
||||
await page.getByTestId("title-Group").click();
|
||||
await page.getByTestId("edit-name-description-button").click();
|
||||
await page.getByTestId("input-title-Group").first().fill("test");
|
||||
await page.getByTestId("save-name-description-button").first().click();
|
||||
await page.keyboard.press("Control+g");
|
||||
await page.keyboard.press("ControlOrMeta+g");
|
||||
await page.getByTestId("title-OpenAI").isVisible();
|
||||
await page.getByTestId("title-Prompt").isVisible();
|
||||
},
|
||||
|
||||
@ -6,7 +6,7 @@ import { initialGPTsetup } from "../../utils/initialGPTsetup";
|
||||
|
||||
test(
|
||||
"Basic Prompting (Hello, World)",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page }) => {
|
||||
test.skip(
|
||||
!process?.env?.OPENAI_API_KEY,
|
||||
|
||||
@ -6,7 +6,7 @@ import { initialGPTsetup } from "../../utils/initialGPTsetup";
|
||||
|
||||
test(
|
||||
"Blog Writer",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page }) => {
|
||||
test.skip(
|
||||
!process?.env?.OPENAI_API_KEY,
|
||||
|
||||
@ -7,7 +7,7 @@ import { waitForOpenModalWithChatInput } from "../../utils/wait-for-open-modal";
|
||||
|
||||
test(
|
||||
"Custom Component Generator",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page }) => {
|
||||
test.skip(
|
||||
!process?.env?.ANTHROPIC_API_KEY,
|
||||
|
||||
@ -6,7 +6,7 @@ import { initialGPTsetup } from "../../utils/initialGPTsetup";
|
||||
|
||||
test(
|
||||
"Document Q&A",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page }) => {
|
||||
test.skip(
|
||||
!process?.env?.OPENAI_API_KEY,
|
||||
|
||||
@ -6,7 +6,7 @@ import { initialGPTsetup } from "../../utils/initialGPTsetup";
|
||||
|
||||
test.skip(
|
||||
"Dynamic Agent",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page }) => {
|
||||
test.skip(
|
||||
!process?.env?.OPENAI_API_KEY,
|
||||
|
||||
@ -6,7 +6,7 @@ import { initialGPTsetup } from "../../utils/initialGPTsetup";
|
||||
|
||||
test.skip(
|
||||
"Hierarchical Tasks Agent",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page }) => {
|
||||
test.skip(
|
||||
!process?.env?.OPENAI_API_KEY,
|
||||
|
||||
@ -15,7 +15,7 @@ import { waitForOpenModalWithoutChatInput } from "../../utils/wait-for-open-moda
|
||||
|
||||
test(
|
||||
"Image Sentiment Analysis",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page }) => {
|
||||
test.skip(
|
||||
!process?.env?.OPENAI_API_KEY,
|
||||
|
||||
@ -8,7 +8,7 @@ import { waitForOpenModalWithChatInput } from "../../utils/wait-for-open-modal";
|
||||
|
||||
test(
|
||||
"Instagram Copywriter",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page }) => {
|
||||
test.skip(
|
||||
!process?.env?.OPENAI_API_KEY,
|
||||
|
||||
@ -8,7 +8,7 @@ import { waitForOpenModalWithChatInput } from "../../utils/wait-for-open-modal";
|
||||
|
||||
test(
|
||||
"Market Research",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page }) => {
|
||||
test.skip(
|
||||
!process?.env?.OPENAI_API_KEY,
|
||||
|
||||
@ -6,7 +6,7 @@ import { initialGPTsetup } from "../../utils/initialGPTsetup";
|
||||
|
||||
test(
|
||||
"Memory Chatbot",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page }) => {
|
||||
test.skip(
|
||||
!process?.env?.OPENAI_API_KEY,
|
||||
|
||||
@ -13,7 +13,7 @@ import { waitForOpenModalWithChatInput } from "../../utils/wait-for-open-modal";
|
||||
|
||||
test(
|
||||
"Prompt Chaining",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page }) => {
|
||||
test.skip(
|
||||
!process?.env?.OPENAI_API_KEY,
|
||||
|
||||
@ -13,7 +13,7 @@ import { waitForOpenModalWithoutChatInput } from "../../utils/wait-for-open-moda
|
||||
|
||||
test(
|
||||
"SEO Keyword Generator",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page }) => {
|
||||
test.skip(
|
||||
!process?.env?.OPENAI_API_KEY,
|
||||
|
||||
@ -13,7 +13,7 @@ import { waitForOpenModalWithoutChatInput } from "../../utils/wait-for-open-moda
|
||||
|
||||
test(
|
||||
"SaaS Pricing",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page }) => {
|
||||
test.skip(
|
||||
!process?.env?.OPENAI_API_KEY,
|
||||
|
||||
@ -6,7 +6,7 @@ import { initialGPTsetup } from "../../utils/initialGPTsetup";
|
||||
|
||||
test.skip(
|
||||
"Sequential Task Agent",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page }) => {
|
||||
test.skip(
|
||||
!process?.env?.OPENAI_API_KEY,
|
||||
|
||||
@ -6,7 +6,7 @@ import { initialGPTsetup } from "../../utils/initialGPTsetup";
|
||||
|
||||
test(
|
||||
"Simple Agent",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page }) => {
|
||||
test.skip(
|
||||
!process?.env?.OPENAI_API_KEY,
|
||||
|
||||
@ -11,7 +11,7 @@ import { updateOldComponents } from "../../utils/update-old-components";
|
||||
|
||||
test(
|
||||
"Travel Planning Agent",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page }) => {
|
||||
test.skip(
|
||||
!process?.env?.OPENAI_API_KEY,
|
||||
|
||||
@ -13,7 +13,7 @@ import { waitForOpenModalWithoutChatInput } from "../../utils/wait-for-open-moda
|
||||
|
||||
test(
|
||||
"Twitter Thread Generator",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page }) => {
|
||||
test.skip(
|
||||
!process?.env?.OPENAI_API_KEY,
|
||||
|
||||
@ -5,7 +5,7 @@ import { extractAndCleanCode } from "../../utils/extract-and-clean-code";
|
||||
|
||||
test(
|
||||
"Vector Store RAG",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page }) => {
|
||||
test.skip(
|
||||
!process?.env?.OPENAI_API_KEY,
|
||||
|
||||
@ -3,7 +3,7 @@ import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
||||
|
||||
test(
|
||||
"vector store from starter projects should have its connections and nodes on the flow",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page, request }) => {
|
||||
const response = await request.get("/api/v1/starter-projects");
|
||||
expect(response.status()).toBe(200);
|
||||
|
||||
@ -5,7 +5,7 @@ import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
||||
|
||||
test(
|
||||
"user must be able to edit an empty prompt",
|
||||
{ tag: ["@release", "@starter-project"] },
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
async ({ page }) => {
|
||||
test.skip(
|
||||
!process?.env?.OPENAI_API_KEY,
|
||||
|
||||
@ -3,7 +3,7 @@ import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
||||
|
||||
test(
|
||||
"user must be able to interact with starter projects",
|
||||
{ tag: ["@release", "@starter-projects"] },
|
||||
{ tag: ["@release", "@starter-projectss"] },
|
||||
async ({ page, context }) => {
|
||||
await awaitBootstrapTest(page);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user