Skip to content

Commit 4f728e6

Browse files
Merge branch 'main' into main
2 parents 98e7f59 + 8641417 commit 4f728e6

753 files changed

Lines changed: 70992 additions & 23675 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
description: Rules and checklist for creating a basic Langflow Component
3+
globs:
4+
alwaysApply: false
5+
---
6+
# Rule: How to Create a Basic Langflow Component
7+
8+
## Purpose
9+
Guide for Creating a Langflow Component
10+
11+
---
12+
13+
### 1. Gather Requirements
14+
15+
Ask the user for:
16+
- **Component Name:** What should the component be called?
17+
- **Description:** What does the component do?
18+
- **Inputs:** What are the required inputs? (e.g., text, dropdown, boolean, etc.)
19+
- **Outputs:** What should the component output? (e.g., a message, a value, etc.)
20+
- **Category:** Which component category should this component be stored under in `langflow/src/backend/base/langflow/components`
21+
22+
### 2. Define the Component
23+
24+
- Inherit from `Component`.
25+
- Set `display_name`, `description`, `icon`.
26+
- Define the `inputs` and `outputs` as lists of input/output field objects (e.g., `DropdownInput`, `MessageTextInput`, `Output`).
27+
- Implement the main logic as a method (e.g., `get_current_date`, `true_response`, etc.).
28+
29+
### 3. Example: Conditional If-Else Component
30+
31+
```python
32+
class ConditionalRouterComponent(Component):
33+
display_name = "If-Else"
34+
description = "Routes an input message to a corresponding output based on text comparison."
35+
icon = "split"
36+
name = "ConditionalRouter"
37+
inputs = [
38+
# Define your inputs here
39+
]
40+
outputs = [
41+
# Define your outputs here
42+
]
43+
# Implement your logic methods here
44+
```
45+
46+
### 4. Example: Current Date Component
47+
48+
```python
49+
class CurrentDateComponent(Component):
50+
display_name = "Current Date"
51+
description = "Returns the current date and time in the selected timezone."
52+
icon = "clock"
53+
name = "CurrentDate"
54+
inputs = [
55+
# Define your inputs here
56+
]
57+
outputs = [
58+
# Define your outputs here
59+
]
60+
# Implement your logic methods here
61+
```
62+
63+
### 5. Best Practices
64+
65+
- Use clear and descriptive names for inputs and outputs.
66+
- Provide helpful `info` for each input to guide users.
67+
- Handle errors gracefully and provide meaningful error messages.
68+
- Use appropriate icons to visually represent the component's function.
69+
- Use a Lucide icon or if you want a custom icon follow the icon rules (`./cursor/rules/icons.mdc`)
70+
71+
---
72+
73+
## Checklist for Creating a Component
74+
- [ ] Ask the user for component name, description, inputs, and outputs.
75+
- [ ] Define the component class with the required fields.
76+
- [ ] Implement the main logic.
77+
- [ ] Add helpful info and error handling.
78+
- [ ] Test the component.

.cursor/rules/icons.mdc

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
---
2+
description: Rules and checklist for adding and using langflow component icons.
3+
globs:
4+
alwaysApply: false
5+
---
6+
# Component Icon Rules
7+
8+
## Purpose
9+
To ensure consistent, clear, and functional icon usage for components, covering both backend (Python) and frontend (React/TypeScript) steps.
10+
11+
---
12+
13+
## 1. Backend (Python) — Setting the Icon Name
14+
15+
- **Where:** In your component class (e.g., in `src/backend/base/langflow/components/vectorstores/astradb.py`)
16+
- **How:**
17+
Set the `icon` attribute to a string matching the icon you want to use.
18+
```python
19+
icon = "AstraDB"
20+
```
21+
- **Tip:**
22+
The string must match the frontend icon mapping exactly (case-sensitive).
23+
24+
---
25+
26+
## 2. Frontend (React/TypeScript) — Adding the Icon
27+
28+
### a. Create the Icon Component
29+
30+
- **Where:**
31+
In a new directory for your icon, e.g., `src/frontend/src/icons/AstraDB/`.
32+
- **How:**
33+
- Add your SVG as a React component, e.g., `AstraSVG` in `AstraDB.jsx`.
34+
```jsx
35+
const AstraSVG = (props) => (
36+
<svg {...props}>
37+
<path
38+
// ...
39+
/>
40+
</svg>
41+
);
42+
```
43+
- Create an `index.tsx` that exports your icon using `forwardRef`:
44+
```tsx
45+
import { useDarkStore } from "@/stores/darkStore";
46+
import React, { forwardRef } from "react";
47+
import AstraSVG from "./AstraDB";
48+
49+
export const AstraDBIcon = forwardRef<
50+
SVGSVGElement,
51+
React.PropsWithChildren<{}>
52+
>((props, ref) => {
53+
const isdark = useDarkStore((state) => state.dark).toString();
54+
return <AstraSVG ref={ref} isdark={isdark} {...props} />;
55+
});
56+
```
57+
58+
#### Supporting Light and Dark Mode Icons
59+
60+
- **How:**
61+
- In your SVG component (e.g., `AstraDB.jsx`), use the `isdark` prop to switch colors:
62+
```jsx
63+
const AstraSVG = (props) => (
64+
<svg {...props}>
65+
<path
66+
fill={stringToBool(props.isdark) ? "#ffffff" : "#0A0A0A"}
67+
// ...
68+
/>
69+
</svg>
70+
);
71+
```
72+
- The `isdark` prop is passed from the icon wrapper (see above) and should be used to toggle between light and dark color schemes.
73+
- You can use a utility like `stringToBool` to ensure the prop is interpreted correctly.
74+
75+
### b. Add to Lazy Icon Imports
76+
77+
- **Where:**
78+
In `src/frontend/src/icons/lazyIconImports.ts`
79+
- **How:**
80+
Add an entry to the `lazyIconsMapping` object:
81+
```ts
82+
AstraDB: () =>
83+
import("@/icons/AstraDB").then((mod) => ({ default: mod.AstraDBIcon })),
84+
```
85+
- **Tip:**
86+
The key (`AstraDB`) must match the string used in the backend.
87+
88+
---
89+
90+
## 3. Best Practices
91+
92+
- **Naming:**
93+
Use clear, recognizable names (e.g., `"AstraDB"`, `"Postgres"`, `"OpenAI"`).
94+
- **Consistency:**
95+
Always use the same icon name for the same service across backend and frontend.
96+
- **Missing Icon:**
97+
If no icon exists, use a [lucide icon](https://lucide.dev/icons)
98+
- **Light/Dark Mode:**
99+
Always support both light and dark mode for custom icons by using the `isdark` prop in your SVG.
100+
101+
---
102+
103+
## 4. Checklist for Adding a New Icon
104+
105+
- [ ] Decide on a clear, descriptive icon name (e.g., `AstraDB`).
106+
- [ ] In your Python component, set `icon = "YourIconName"`.
107+
- [ ] Create a new icon directory in `src/frontend/src/icons/YourIconName/`.
108+
- [ ] Add your SVG as a React component (e.g., `YourIconNameIcon.jsx`).
109+
- [ ] Create an `index.tsx` that exports your icon using `forwardRef` and passes the `isdark` prop.
110+
- [ ] Add your icon to `lazyIconsMapping` in `src/frontend/src/icons/lazyIconImports.ts` with the exact same name.
111+
- [ ] Verify the icon appears correctly in the UI in both light and dark mode.
112+
- [ ] If no suitable icon exists, use a generic icon and request a new one if needed.
113+
114+
---
115+
116+
**Example for AstraDB:**
117+
- Backend:
118+
```python
119+
icon = "AstraDB"
120+
```
121+
- Frontend:
122+
- `src/icons/AstraDB/AstraDB.jsx` (SVG as React component, uses `isdark` prop)
123+
- `src/icons/AstraDB/index.tsx` (exports `AstraDBIcon` and passes `isdark`)
124+
- Add to `lazyIconImports.ts`:
125+
```ts
126+
AstraDB: () =>
127+
import("@/icons/AstraDB").then((mod) => ({ default: mod.AstraDBIcon })),
128+
```
129+
130+
---

.github/workflows/ci.yml

Lines changed: 71 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -46,54 +46,85 @@ concurrency:
4646

4747
jobs:
4848
check-nightly-status:
49-
name: Check Nightly Status
49+
name: Check PyPI Version Update
5050
runs-on: ubuntu-latest
5151
outputs:
52-
should-proceed: ${{ steps.check-workflow.outputs.success }}
52+
should-proceed: ${{ steps.check-pypi.outputs.success }}
5353
steps:
54-
- name: Check nightly workflow status
55-
id: check-workflow
56-
uses: actions/github-script@v7
57-
with:
58-
script: |
59-
const workflow_name = 'nightly_build.yml';
60-
const today = new Date();
61-
today.setHours(0, 0, 0, 0); // Set to beginning of day
62-
63-
const { data: runs } = await github.rest.actions.listWorkflowRuns({
64-
owner: context.repo.owner,
65-
repo: context.repo.repo,
66-
workflow_id: workflow_name,
67-
created: `>=${today.toISOString()}`,
68-
per_page: 100, // Get more runs to check
69-
status: 'completed'
70-
});
71-
72-
if (runs.workflow_runs.length === 0) {
73-
console.log('No completed workflow runs found today');
74-
return core.setOutput('success', 'true');
75-
}
76-
77-
// Check if any runs today were successful
78-
const successfulTodayRuns = runs.workflow_runs.filter(run => run.conclusion === 'success');
79-
const hasSuccessfulRunToday = successfulTodayRuns.length > 0;
80-
81-
console.log(`Found ${runs.workflow_runs.length} completed runs today, ${successfulTodayRuns.length} successful`);
82-
core.setOutput('success', hasSuccessfulRunToday.toString());
54+
- name: Check PyPI package update
55+
id: check-pypi
56+
run: |
57+
# Get today's date in ISO format for comparison
58+
TODAY=$(date -u +"%Y-%m-%d")
59+
echo "Today's date: $TODAY"
60+
61+
# Query PyPI API for the langflow package
62+
HTTP_STATUS=$(curl -s -o response.json -w "%{http_code}" https://pypi.org/pypi/langflow-nightly/json)
63+
64+
# Check HTTP status code first
65+
if [ "$HTTP_STATUS" -ne 200 ]; then
66+
echo "Error: PyPI API returned HTTP status $HTTP_STATUS"
67+
echo "success=false" >> $GITHUB_OUTPUT
68+
exit 0
69+
fi
70+
71+
# Check if response is valid JSON before proceeding
72+
if ! jq -e . response.json >/dev/null 2>&1; then
73+
echo "Error: Invalid JSON response from PyPI API"
74+
echo "Response preview:"
75+
head -n 10 response.json
76+
echo "success=false" >> $GITHUB_OUTPUT
77+
exit 0
78+
fi
79+
80+
# Extract the latest version
81+
LATEST_VERSION=$(jq -r '.info.version // empty' response.json)
82+
83+
if [ -z "$LATEST_VERSION" ]; then
84+
echo "Could not extract latest version"
85+
echo "success=false" >> $GITHUB_OUTPUT
86+
exit 0
87+
fi
88+
89+
# Extract the release date of the latest version
90+
RELEASE_DATE=$(jq -r --arg ver "$LATEST_VERSION" '.releases[$ver][0].upload_time_iso_8601 // empty' response.json | cut -d'T' -f1)
91+
92+
if [ -z "$RELEASE_DATE" ]; then
93+
echo "Could not extract release date"
94+
echo "success=false" >> $GITHUB_OUTPUT
95+
exit 0
96+
fi
97+
98+
echo "Latest version: $LATEST_VERSION"
99+
echo "Release date: $RELEASE_DATE"
100+
101+
# Check if the release date is today
102+
if [[ "$RELEASE_DATE" == "$TODAY" ]]; then
103+
echo "Package was updated today"
104+
echo "success=true" >> $GITHUB_OUTPUT
105+
else
106+
echo "Package was not updated today"
107+
echo "success=false" >> $GITHUB_OUTPUT
108+
fi
109+
110+
# Clean up
111+
rm -f response.json
83112
84113
set-ci-condition:
85114
needs: check-nightly-status
86115
name: Should Run CI
87116
runs-on: ubuntu-latest
88117
outputs:
89118
should-run-ci: ${{ (needs.check-nightly-status.outputs.should-proceed == 'true' || github.event_name == 'workflow_dispatch') && ((contains( github.event.pull_request.labels.*.name, 'lgtm') && github.event.pull_request.draft == false) || (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call' || github.event_name == 'merge_group')) }}
119+
should-run-tests: ${{ !contains(github.event.pull_request.labels.*.name, 'fast-track') || github.event_name == 'workflow_call' || github.event_name == 'workflow_dispatch' || github.event_name == 'merge_group' }}
90120
steps:
91121
# Do anything just to make the job run
92122
- run: echo "Debug CI Condition"
93123
- run: echo "Labels -> ${{ join(github.event.pull_request.labels.*.name, ',') }}"
94124
- run: echo "IsDraft -> ${{ github.event.pull_request.draft }}"
95125
- run: echo "Event name -> ${{ github.event_name }}"
96126
- run: echo "Nightly build status -> ${{ needs.check-nightly-status.outputs.should-proceed }}"
127+
- run: echo "Should run tests -> ${{ !contains(github.event.pull_request.labels.*.name, 'fast-track') || github.event_name == 'workflow_call' || github.event_name == 'workflow_dispatch' || github.event_name == 'merge_group' }}"
97128

98129
path-filter:
99130
needs: set-ci-condition
@@ -125,16 +156,17 @@ jobs:
125156
filters: ./.github/changes-filter.yaml
126157

127158
test-backend:
128-
needs: path-filter
159+
needs: [path-filter, set-ci-condition]
129160
name: Run Backend Tests
130-
if: ${{ needs.path-filter.outputs.python == 'true'}}
161+
if: ${{ needs.path-filter.outputs.python == 'true' && needs.set-ci-condition.outputs.should-run-tests == 'true' }}
131162
uses: ./.github/workflows/python_test.yml
132163
with:
133164
python-versions: ${{ inputs.python-versions || '["3.10"]' }}
165+
134166
test-frontend:
135-
needs: path-filter
167+
needs: [path-filter, set-ci-condition]
136168
name: Run Frontend Tests
137-
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' }}
169+
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') && needs.set-ci-condition.outputs.should-run-tests == 'true' }}
138170
uses: ./.github/workflows/typescript_test.yml
139171
with:
140172
tests_folder: ${{ inputs.frontend-tests-folder }}
@@ -167,18 +199,21 @@ jobs:
167199
lint-backend,
168200
test-docs-build,
169201
set-ci-condition,
202+
path-filter
170203
]
171204

172205
if: always()
173206
runs-on: ubuntu-latest
174207
env:
175208
JOBS_JSON: ${{ toJSON(needs) }}
176209
RESULTS_JSON: ${{ toJSON(needs.*.result) }}
177-
EXIT_CODE: ${{!contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') && needs.set-ci-condition.outputs.should-run-ci == 'true' && '0' || '1'}}
210+
EXIT_CODE: ${{ ((!contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') && needs.set-ci-condition.outputs.should-run-ci == 'true') || (needs.set-ci-condition.outputs.should-run-tests == 'false' && needs.set-ci-condition.outputs.should-run-ci == 'true')) && '0' || '1' }}
178211
steps:
179212
- name: "CI Success"
180213
run: |
181214
echo $JOBS_JSON
182215
echo $RESULTS_JSON
216+
echo "Should run tests: ${{ needs.set-ci-condition.outputs.should-run-tests }}"
217+
echo "Should run CI: ${{ needs.set-ci-condition.outputs.should-run-ci }}"
183218
echo "Exiting with $EXIT_CODE"
184219
exit $EXIT_CODE

.github/workflows/codspeed.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ jobs:
4242
--ignore=src/backend/tests/integration \
4343
--codspeed \
4444
-m "not api_key_required" \
45-
-n auto
45+
-n auto \
46+
--timeout 600
4647
- name: Minimize uv cache
4748
run: uv cache prune --ci

0 commit comments

Comments
 (0)