mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-24 04:13:36 +08:00
Merge branch 'main' into docs-publish-v0
This commit is contained in:
8
.github/workflows/deploy_gh-pages.yml
vendored
8
.github/workflows/deploy_gh-pages.yml
vendored
@ -18,13 +18,13 @@ jobs:
|
|||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
cache: npm
|
cache: yarn
|
||||||
cache-dependency-path: ./docs/package-lock.json
|
cache-dependency-path: ./docs/yarn.lock
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: cd docs && npm install --legacy-peer-deps
|
run: cd docs && yarn install
|
||||||
- name: Build website
|
- name: Build website
|
||||||
run: cd docs && npm run build
|
run: cd docs && yarn build
|
||||||
|
|
||||||
# Popular action to deploy to GitHub Pages:
|
# Popular action to deploy to GitHub Pages:
|
||||||
# Docs: https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-docusaurus
|
# Docs: https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-docusaurus
|
||||||
|
|||||||
@ -91,6 +91,21 @@ To access the **Logs** pane, click your **Flow Name**, and then select **Logs**.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
Langflow stores logs at the location specified in the `LANGFLOW_CONFIG_DIR` environment variable.
|
||||||
|
|
||||||
|
This directory's default location depends on your operating system.
|
||||||
|
|
||||||
|
* **Linux/WSL**: `~/.cache/langflow/`
|
||||||
|
* **macOS**: `/Users/<username>/Library/Caches/langflow/`
|
||||||
|
* **Windows**: `%LOCALAPPDATA%\langflow\langflow\Cache`
|
||||||
|
|
||||||
|
To modify the location of your log file:
|
||||||
|
|
||||||
|
1. Add `LANGFLOW_LOG_FILE=path/to/logfile.log` in your `.env.` file.
|
||||||
|
2. To start Langflow with the values from your `.env` file, start Langflow with `uv run langflow run --env-file .env`.
|
||||||
|
|
||||||
|
An example `.env` file is available in the [project repository](https://github.com/langflow-ai/langflow/blob/main/.env.example).
|
||||||
|
|
||||||
## Projects and folders
|
## Projects and folders
|
||||||
|
|
||||||
The **My Projects** page displays all the flows and components you've created in the Langflow workspace.
|
The **My Projects** page displays all the flows and components you've created in the Langflow workspace.
|
||||||
|
|||||||
@ -20,9 +20,6 @@ const config = {
|
|||||||
projectName: "langflow",
|
projectName: "langflow",
|
||||||
trailingSlash: false,
|
trailingSlash: false,
|
||||||
staticDirectories: ["static"],
|
staticDirectories: ["static"],
|
||||||
customFields: {
|
|
||||||
mendableAnonKey: "b7f52734-297c-41dc-8737-edbd13196394", // Mendable Anon Client-side key, safe to expose to the public
|
|
||||||
},
|
|
||||||
i18n: {
|
i18n: {
|
||||||
defaultLocale: "en",
|
defaultLocale: "en",
|
||||||
locales: ["en"],
|
locales: ["en"],
|
||||||
@ -261,6 +258,15 @@ const config = {
|
|||||||
hideable: false,
|
hideable: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
algolia: {
|
||||||
|
appId: 'UZK6BDPCVY',
|
||||||
|
// public key, safe to commit
|
||||||
|
apiKey: 'adbd7686dceb1cd510d5ce20d04bf74c',
|
||||||
|
indexName: 'langflow',
|
||||||
|
contextualSearch: true,
|
||||||
|
searchParameters: {},
|
||||||
|
searchPagePath: 'search',
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -12,10 +12,10 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@code-hike/mdx": "^0.9.0",
|
"@code-hike/mdx": "^0.9.0",
|
||||||
"@docusaurus/core": "^3.7.0",
|
"@docusaurus/core": "3.7.0",
|
||||||
"@docusaurus/plugin-client-redirects": "^3.7.0",
|
"@docusaurus/plugin-client-redirects": "^3.7.0",
|
||||||
"@docusaurus/plugin-google-tag-manager": "^3.7.0",
|
"@docusaurus/plugin-google-tag-manager": "^3.7.0",
|
||||||
"@docusaurus/preset-classic": "^3.7.0",
|
"@docusaurus/preset-classic": "3.7.0",
|
||||||
"@easyops-cn/docusaurus-search-local": "^0.45.0",
|
"@easyops-cn/docusaurus-search-local": "^0.45.0",
|
||||||
"@mdx-js/react": "^3.0.1",
|
"@mdx-js/react": "^3.0.1",
|
||||||
"@mendable/search": "^0.0.206",
|
"@mendable/search": "^0.0.206",
|
||||||
|
|||||||
@ -1,52 +1,77 @@
|
|||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import Footer from "@theme-original/Footer";
|
import Footer from "@theme-original/Footer";
|
||||||
import { MendableFloatingButton } from "@mendable/search";
|
import { useDocSearchKeyboardEvents } from '@docsearch/react';
|
||||||
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
|
|
||||||
|
|
||||||
export default function FooterWrapper(props) {
|
export default function FooterWrapper(props) {
|
||||||
const iconSpan1 = React.createElement(
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
"img",
|
const searchButtonRef = React.useRef(null);
|
||||||
{
|
|
||||||
src: "/img/langflow-icon-black-transparent.svg",
|
|
||||||
srcDark: "",
|
|
||||||
style: { width: "40px" },
|
|
||||||
},
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
const icon = React.createElement(
|
useDocSearchKeyboardEvents({
|
||||||
"div",
|
isOpen: false,
|
||||||
{
|
onOpen: () => {
|
||||||
style: {
|
searchButtonRef.current?.click();
|
||||||
color: "#000000",
|
|
||||||
fontSize: "22px",
|
|
||||||
width: "48px",
|
|
||||||
height: "48px",
|
|
||||||
margin: "0px",
|
|
||||||
padding: "0px",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
textAlign: "center",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
[iconSpan1]
|
|
||||||
);
|
|
||||||
const {
|
|
||||||
siteConfig: { customFields },
|
|
||||||
} = useDocusaurusContext();
|
|
||||||
|
|
||||||
const mendableFloatingButton = React.createElement(MendableFloatingButton, {
|
|
||||||
floatingButtonStyle: { color: "#000000", backgroundColor: "#f6f6f6" },
|
|
||||||
anon_key: customFields.mendableAnonKey,
|
|
||||||
showSimpleSearch: true,
|
|
||||||
icon: icon,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const searchButton = (
|
||||||
|
<div
|
||||||
|
ref={searchButtonRef}
|
||||||
|
onClick={() => {
|
||||||
|
// This will trigger Docusaurus's default search modal
|
||||||
|
document.querySelector('.DocSearch-Button')?.click();
|
||||||
|
}}
|
||||||
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
|
style={{
|
||||||
|
position: 'fixed',
|
||||||
|
right: '20px',
|
||||||
|
bottom: '20px',
|
||||||
|
zIndex: 100,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '10px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isHovered && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundColor: "#f6f6f6",
|
||||||
|
padding: '8px 16px',
|
||||||
|
borderRadius: '20px',
|
||||||
|
color: '#000',
|
||||||
|
fontSize: '14px',
|
||||||
|
boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Hi, how can I help you?
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundColor: "#f6f6f6",
|
||||||
|
borderRadius: "50%",
|
||||||
|
width: "48px",
|
||||||
|
height: "48px",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="/img/langflow-icon-black-transparent.svg"
|
||||||
|
style={{ width: "40px" }}
|
||||||
|
alt="Search"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Footer />
|
<Footer {...props} />
|
||||||
{mendableFloatingButton}
|
{searchButton}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,23 +0,0 @@
|
|||||||
// By default, the classic theme does not provide any SearchBar implementation
|
|
||||||
// If you swizzled this, it is your responsibility to provide an implementation
|
|
||||||
// Tip: swizzle the SearchBar from the Algolia theme for inspiration:
|
|
||||||
// npm run swizzle @docusaurus/theme-search-algolia SearchBar
|
|
||||||
import React from 'react'
|
|
||||||
import { MendableSearchBar } from '@mendable/search'
|
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
|
|
||||||
|
|
||||||
export default function SearchBarWrapper() {
|
|
||||||
const {
|
|
||||||
siteConfig: { customFields },
|
|
||||||
} = useDocusaurusContext()
|
|
||||||
return (
|
|
||||||
<div className="mendable-search">
|
|
||||||
<MendableSearchBar
|
|
||||||
anon_key={customFields.mendableAnonKey}
|
|
||||||
placeholder="Search..."
|
|
||||||
dialogPlaceholder="How to deploy my application?"
|
|
||||||
showSimpleSearch
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
2567
docs/yarn.lock
2567
docs/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -19,7 +19,7 @@ export class EcrRepository extends Construct {
|
|||||||
super(scope, id)
|
super(scope, id)
|
||||||
|
|
||||||
const imagePlatform = props.arch == ecs.CpuArchitecture.ARM64 ? Platform.LINUX_ARM64 : Platform.LINUX_AMD64
|
const imagePlatform = props.arch == ecs.CpuArchitecture.ARM64 ? Platform.LINUX_ARM64 : Platform.LINUX_AMD64
|
||||||
const backendPath = path.join(__dirname, "../../../../../", "langflow")
|
const backendPath = path.join(__dirname, "../../../../", "docker")
|
||||||
const excludeDir = ['node_modules','.git', 'cdk.out']
|
const excludeDir = ['node_modules','.git', 'cdk.out']
|
||||||
const LifecycleRule = {
|
const LifecycleRule = {
|
||||||
tagStatus: ecr.TagStatus.ANY,
|
tagStatus: ecr.TagStatus.ANY,
|
||||||
|
|||||||
154
scripts/aws/package-lock.json
generated
154
scripts/aws/package-lock.json
generated
@ -22,7 +22,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.12",
|
||||||
"@types/node": "^20.12.12",
|
"@types/node": "^20.12.12",
|
||||||
"aws-cdk": "^2.141.0",
|
"aws-cdk": "^2.1000.2",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"ts-jest": "^29.1.2",
|
"ts-jest": "^29.1.2",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
@ -43,19 +43,49 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-cdk/asset-awscli-v1": {
|
"node_modules/@aws-cdk/asset-awscli-v1": {
|
||||||
"version": "2.2.202",
|
"version": "2.2.224",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.202.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.224.tgz",
|
||||||
"integrity": "sha512-JqlF0D4+EVugnG5dAsNZMqhu3HW7ehOXm5SDMxMbXNDMdsF0pxtQKNHRl52z1U9igsHmaFpUgSGjbhAJ+0JONg=="
|
"integrity": "sha512-4CQP+y0rLq4IWzOlTqBhe8IxBU3Tul9KcmHxiAqztQRWLIl5HAVGCOWdLzHMLgbpFWNNMlIJxB8GwBEV0pWtfQ==",
|
||||||
},
|
"license": "Apache-2.0"
|
||||||
"node_modules/@aws-cdk/asset-kubectl-v20": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg=="
|
|
||||||
},
|
},
|
||||||
"node_modules/@aws-cdk/asset-node-proxy-agent-v6": {
|
"node_modules/@aws-cdk/asset-node-proxy-agent-v6": {
|
||||||
"version": "2.0.3",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.1.0.tgz",
|
||||||
"integrity": "sha512-twhuEG+JPOYCYPx/xy5uH2+VUsIEhPTzDY0F1KuB+ocjWWB/KEDiOVL19nHvbPCB6fhWnkykXEMJ4HHcKvjtvg=="
|
"integrity": "sha512-7bY3J8GCVxLupn/kNmpPc5VJz8grx+4RKfnnJiO1LG+uxkZfANZG3RMHhE+qQxxwkyQ9/MfPtTpf748UhR425A==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
|
"node_modules/@aws-cdk/cloud-assembly-schema": {
|
||||||
|
"version": "39.2.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-39.2.20.tgz",
|
||||||
|
"integrity": "sha512-RI7S8jphGA8mak154ElnEJQPNTTV4PZmA7jgqnBBHQGyOPJIXxtACubNQ5m4YgjpkK3UJHsWT+/cOAfM/Au/Wg==",
|
||||||
|
"bundleDependencies": [
|
||||||
|
"jsonschema",
|
||||||
|
"semver"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"jsonschema": "~1.4.1",
|
||||||
|
"semver": "^7.7.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@aws-cdk/cloud-assembly-schema/node_modules/jsonschema": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"inBundle": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@aws-cdk/cloud-assembly-schema/node_modules/semver": {
|
||||||
|
"version": "7.7.1",
|
||||||
|
"inBundle": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-cdk/integ-tests-alpha": {
|
"node_modules/@aws-cdk/integ-tests-alpha": {
|
||||||
"version": "2.138.0-alpha.0",
|
"version": "2.138.0-alpha.0",
|
||||||
@ -1451,24 +1481,25 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/aws-cdk": {
|
"node_modules/aws-cdk": {
|
||||||
"version": "2.141.0",
|
"version": "2.1000.2",
|
||||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.141.0.tgz",
|
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1000.2.tgz",
|
||||||
"integrity": "sha512-RM9uDiETBEKCHemItaRGVjOLwoZ5iqXnejpyXY7+YF75c2c0Ui7HSZI8QD0stDg3S/2UbLcKv2RA9dBsjrWUGA==",
|
"integrity": "sha512-QsXqJhGWjHNqP7etgE3sHOTiDBXItmSKdFKgsm1qPMBabCMyFfmWZnEeUxfZ4sMaIoxvLpr3sqoWSNeLuUk4sg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"cdk": "bin/cdk"
|
"cdk": "bin/cdk"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 14.15.0"
|
"node": ">= 16.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"fsevents": "2.3.2"
|
"fsevents": "2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/aws-cdk-lib": {
|
"node_modules/aws-cdk-lib": {
|
||||||
"version": "2.141.0",
|
"version": "2.179.0",
|
||||||
"resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.141.0.tgz",
|
"resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.179.0.tgz",
|
||||||
"integrity": "sha512-xda56Lfwpdqg9MssnFdXrAKTmeeNjfrfFCaWwqGqToG6cfGY2W+6wyyoObX60/MeZGhhs3Lhdb/K94ulLJ4X/A==",
|
"integrity": "sha512-fwkoEzvh3TXYj+GPkyq/GSQt63JJV1dBgDKwr3xRdb8lQaPntSclmisa+ARO8tjPfkru1NqxAFQTtiqtAE83cA==",
|
||||||
"bundleDependencies": [
|
"bundleDependencies": [
|
||||||
"@balena/dockerignore",
|
"@balena/dockerignore",
|
||||||
"case",
|
"case",
|
||||||
@ -1482,19 +1513,20 @@
|
|||||||
"yaml",
|
"yaml",
|
||||||
"mime-types"
|
"mime-types"
|
||||||
],
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-cdk/asset-awscli-v1": "^2.2.202",
|
"@aws-cdk/asset-awscli-v1": "^2.2.208",
|
||||||
"@aws-cdk/asset-kubectl-v20": "^2.1.2",
|
"@aws-cdk/asset-node-proxy-agent-v6": "^2.1.0",
|
||||||
"@aws-cdk/asset-node-proxy-agent-v6": "^2.0.3",
|
"@aws-cdk/cloud-assembly-schema": "^39.2.0",
|
||||||
"@balena/dockerignore": "^1.0.2",
|
"@balena/dockerignore": "^1.0.2",
|
||||||
"case": "1.6.3",
|
"case": "1.6.3",
|
||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.2.0",
|
||||||
"ignore": "^5.3.1",
|
"ignore": "^5.3.2",
|
||||||
"jsonschema": "^1.4.1",
|
"jsonschema": "^1.4.1",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"minimatch": "^3.1.2",
|
"minimatch": "^3.1.2",
|
||||||
"punycode": "^2.3.1",
|
"punycode": "^2.3.1",
|
||||||
"semver": "^7.6.0",
|
"semver": "^7.6.3",
|
||||||
"table": "^6.8.2",
|
"table": "^6.8.2",
|
||||||
"yaml": "1.10.2"
|
"yaml": "1.10.2"
|
||||||
},
|
},
|
||||||
@ -1511,14 +1543,14 @@
|
|||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/aws-cdk-lib/node_modules/ajv": {
|
"node_modules/aws-cdk-lib/node_modules/ajv": {
|
||||||
"version": "8.13.0",
|
"version": "8.17.1",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"fast-uri": "^3.0.1",
|
||||||
"json-schema-traverse": "^1.0.0",
|
"json-schema-traverse": "^1.0.0",
|
||||||
"require-from-string": "^2.0.2",
|
"require-from-string": "^2.0.2"
|
||||||
"uri-js": "^4.4.1"
|
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@ -1608,8 +1640,23 @@
|
|||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/aws-cdk-lib/node_modules/fast-uri": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/fastify"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/fastify"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inBundle": true,
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/aws-cdk-lib/node_modules/fs-extra": {
|
"node_modules/aws-cdk-lib/node_modules/fs-extra": {
|
||||||
"version": "11.2.0",
|
"version": "11.3.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1627,7 +1674,7 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/aws-cdk-lib/node_modules/ignore": {
|
"node_modules/aws-cdk-lib/node_modules/ignore": {
|
||||||
"version": "5.3.1",
|
"version": "5.3.2",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -1659,7 +1706,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/aws-cdk-lib/node_modules/jsonschema": {
|
"node_modules/aws-cdk-lib/node_modules/jsonschema": {
|
||||||
"version": "1.4.1",
|
"version": "1.5.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -1671,17 +1718,6 @@
|
|||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/aws-cdk-lib/node_modules/lru-cache": {
|
|
||||||
"version": "6.0.0",
|
|
||||||
"inBundle": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"yallist": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/aws-cdk-lib/node_modules/mime-db": {
|
"node_modules/aws-cdk-lib/node_modules/mime-db": {
|
||||||
"version": "1.52.0",
|
"version": "1.52.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@ -1729,12 +1765,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/aws-cdk-lib/node_modules/semver": {
|
"node_modules/aws-cdk-lib/node_modules/semver": {
|
||||||
"version": "7.6.0",
|
"version": "7.6.3",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
|
||||||
"lru-cache": "^6.0.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
},
|
},
|
||||||
@ -1783,7 +1816,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/aws-cdk-lib/node_modules/table": {
|
"node_modules/aws-cdk-lib/node_modules/table": {
|
||||||
"version": "6.8.2",
|
"version": "6.9.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1805,19 +1838,6 @@
|
|||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/aws-cdk-lib/node_modules/uri-js": {
|
|
||||||
"version": "4.4.1",
|
|
||||||
"inBundle": true,
|
|
||||||
"license": "BSD-2-Clause",
|
|
||||||
"dependencies": {
|
|
||||||
"punycode": "^2.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/aws-cdk-lib/node_modules/yallist": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"inBundle": true,
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/aws-cdk-lib/node_modules/yaml": {
|
"node_modules/aws-cdk-lib/node_modules/yaml": {
|
||||||
"version": "1.10.2",
|
"version": "1.10.2",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@ -2535,10 +2555,11 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
|
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"path-key": "^3.1.0",
|
"path-key": "^3.1.0",
|
||||||
"shebang-command": "^2.0.0",
|
"shebang-command": "^2.0.0",
|
||||||
@ -3837,12 +3858,13 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/micromatch": {
|
"node_modules/micromatch": {
|
||||||
"version": "4.0.5",
|
"version": "4.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||||
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
|
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"braces": "^3.0.2",
|
"braces": "^3.0.3",
|
||||||
"picomatch": "^2.3.1"
|
"picomatch": "^2.3.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.12",
|
||||||
"@types/node": "^20.12.12",
|
"@types/node": "^20.12.12",
|
||||||
"aws-cdk": "^2.141.0",
|
"aws-cdk": "^2.1000.2",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"ts-jest": "^29.1.2",
|
"ts-jest": "^29.1.2",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
|
|||||||
@ -19,6 +19,7 @@ from .openrouter import OpenRouterComponent
|
|||||||
from .perplexity import PerplexityComponent
|
from .perplexity import PerplexityComponent
|
||||||
from .sambanova import SambaNovaComponent
|
from .sambanova import SambaNovaComponent
|
||||||
from .vertexai import ChatVertexAIComponent
|
from .vertexai import ChatVertexAIComponent
|
||||||
|
from .xai import XAIModelComponent
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"AIMLModelComponent",
|
"AIMLModelComponent",
|
||||||
@ -42,4 +43,5 @@ __all__ = [
|
|||||||
"PerplexityComponent",
|
"PerplexityComponent",
|
||||||
"QianfanChatEndpointComponent",
|
"QianfanChatEndpointComponent",
|
||||||
"SambaNovaComponent",
|
"SambaNovaComponent",
|
||||||
|
"XAIModelComponent",
|
||||||
]
|
]
|
||||||
|
|||||||
155
src/backend/base/langflow/components/models/xai.py
Normal file
155
src/backend/base/langflow/components/models/xai.py
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
import requests
|
||||||
|
from langchain_openai import ChatOpenAI
|
||||||
|
from pydantic.v1 import SecretStr
|
||||||
|
from typing_extensions import override
|
||||||
|
|
||||||
|
from langflow.base.models.model import LCModelComponent
|
||||||
|
from langflow.field_typing import LanguageModel
|
||||||
|
from langflow.field_typing.range_spec import RangeSpec
|
||||||
|
from langflow.inputs import BoolInput, DictInput, DropdownInput, IntInput, MessageTextInput, SecretStrInput, SliderInput
|
||||||
|
|
||||||
|
XAI_DEFAULT_MODELS = ["grok-2-latest"]
|
||||||
|
|
||||||
|
|
||||||
|
class XAIModelComponent(LCModelComponent):
|
||||||
|
display_name = "xAI"
|
||||||
|
description = "Generates text using xAI models like Grok."
|
||||||
|
icon = "xAI"
|
||||||
|
name = "xAIModel"
|
||||||
|
|
||||||
|
inputs = [
|
||||||
|
*LCModelComponent._base_inputs,
|
||||||
|
IntInput(
|
||||||
|
name="max_tokens",
|
||||||
|
display_name="Max Tokens",
|
||||||
|
advanced=True,
|
||||||
|
info="The maximum number of tokens to generate. Set to 0 for unlimited tokens.",
|
||||||
|
range_spec=RangeSpec(min=0, max=128000),
|
||||||
|
),
|
||||||
|
DictInput(
|
||||||
|
name="model_kwargs",
|
||||||
|
display_name="Model Kwargs",
|
||||||
|
advanced=True,
|
||||||
|
info="Additional keyword arguments to pass to the model.",
|
||||||
|
),
|
||||||
|
BoolInput(
|
||||||
|
name="json_mode",
|
||||||
|
display_name="JSON Mode",
|
||||||
|
advanced=True,
|
||||||
|
info="If True, it will output JSON regardless of passing a schema.",
|
||||||
|
),
|
||||||
|
DropdownInput(
|
||||||
|
name="model_name",
|
||||||
|
display_name="Model Name",
|
||||||
|
advanced=False,
|
||||||
|
options=XAI_DEFAULT_MODELS,
|
||||||
|
value=XAI_DEFAULT_MODELS[0],
|
||||||
|
refresh_button=True,
|
||||||
|
combobox=True,
|
||||||
|
info="The xAI model to use",
|
||||||
|
),
|
||||||
|
MessageTextInput(
|
||||||
|
name="base_url",
|
||||||
|
display_name="xAI API Base",
|
||||||
|
advanced=True,
|
||||||
|
info="The base URL of the xAI API. Defaults to https://api.x.ai/v1",
|
||||||
|
value="https://api.x.ai/v1",
|
||||||
|
),
|
||||||
|
SecretStrInput(
|
||||||
|
name="api_key",
|
||||||
|
display_name="xAI API Key",
|
||||||
|
info="The xAI API Key to use for the model.",
|
||||||
|
advanced=False,
|
||||||
|
value="XAI_API_KEY",
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
SliderInput(
|
||||||
|
name="temperature", display_name="Temperature", value=0.1, range_spec=RangeSpec(min=0, max=2, step=0.01)
|
||||||
|
),
|
||||||
|
IntInput(
|
||||||
|
name="seed",
|
||||||
|
display_name="Seed",
|
||||||
|
info="The seed controls the reproducibility of the job.",
|
||||||
|
advanced=True,
|
||||||
|
value=1,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_models(self) -> list[str]:
|
||||||
|
"""Fetch available models from xAI API."""
|
||||||
|
if not self.api_key:
|
||||||
|
return XAI_DEFAULT_MODELS
|
||||||
|
|
||||||
|
base_url = self.base_url or "https://api.x.ai/v1"
|
||||||
|
url = f"{base_url}/language-models"
|
||||||
|
headers = {"Authorization": f"Bearer {self.api_key}", "Accept": "application/json"}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(url, headers=headers, timeout=10)
|
||||||
|
response.raise_for_status()
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
# Extract model IDs and any aliases
|
||||||
|
models = set()
|
||||||
|
for model in data.get("models", []):
|
||||||
|
models.add(model["id"])
|
||||||
|
models.update(model.get("aliases", []))
|
||||||
|
|
||||||
|
return sorted(models) if models else XAI_DEFAULT_MODELS
|
||||||
|
except requests.RequestException as e:
|
||||||
|
self.status = f"Error fetching models: {e}"
|
||||||
|
return XAI_DEFAULT_MODELS
|
||||||
|
|
||||||
|
@override
|
||||||
|
def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None):
|
||||||
|
"""Update build configuration with fresh model list when key fields change."""
|
||||||
|
if field_name in {"api_key", "base_url", "model_name"}:
|
||||||
|
models = self.get_models()
|
||||||
|
build_config["model_name"]["options"] = models
|
||||||
|
return build_config
|
||||||
|
|
||||||
|
def build_model(self) -> LanguageModel:
|
||||||
|
api_key = self.api_key
|
||||||
|
temperature = self.temperature
|
||||||
|
model_name: str = self.model_name
|
||||||
|
max_tokens = self.max_tokens
|
||||||
|
model_kwargs = self.model_kwargs or {}
|
||||||
|
base_url = self.base_url or "https://api.x.ai/v1"
|
||||||
|
json_mode = self.json_mode
|
||||||
|
seed = self.seed
|
||||||
|
|
||||||
|
api_key = SecretStr(api_key).get_secret_value() if api_key else None
|
||||||
|
|
||||||
|
output = ChatOpenAI(
|
||||||
|
max_tokens=max_tokens or None,
|
||||||
|
model_kwargs=model_kwargs,
|
||||||
|
model=model_name,
|
||||||
|
base_url=base_url,
|
||||||
|
api_key=api_key,
|
||||||
|
temperature=temperature if temperature is not None else 0.1,
|
||||||
|
seed=seed,
|
||||||
|
)
|
||||||
|
|
||||||
|
if json_mode:
|
||||||
|
output = output.bind(response_format={"type": "json_object"})
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
def _get_exception_message(self, e: Exception):
|
||||||
|
"""Get a message from an xAI exception.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
e (Exception): The exception to get the message from.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The message from the exception.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from openai import BadRequestError
|
||||||
|
except ImportError:
|
||||||
|
return None
|
||||||
|
if isinstance(e, BadRequestError):
|
||||||
|
message = e.body.get("message")
|
||||||
|
if message:
|
||||||
|
return message
|
||||||
|
return None
|
||||||
@ -82,6 +82,7 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
|
|||||||
"new_collection_name",
|
"new_collection_name",
|
||||||
"embedding_generation_provider",
|
"embedding_generation_provider",
|
||||||
"embedding_generation_model",
|
"embedding_generation_model",
|
||||||
|
"dimension",
|
||||||
],
|
],
|
||||||
"template": {
|
"template": {
|
||||||
"new_collection_name": StrInput(
|
"new_collection_name": StrInput(
|
||||||
@ -330,7 +331,7 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
|
|||||||
embedding_generation_model: str | None = None,
|
embedding_generation_model: str | None = None,
|
||||||
):
|
):
|
||||||
# Create the data API client
|
# Create the data API client
|
||||||
client = DataAPIClient(token=token)
|
client = DataAPIClient(token=token, environment=environment)
|
||||||
|
|
||||||
# Get the database object
|
# Get the database object
|
||||||
database = client.get_async_database(api_endpoint=api_endpoint, token=token)
|
database = client.get_async_database(api_endpoint=api_endpoint, token=token)
|
||||||
@ -554,6 +555,9 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
|
|||||||
api_endpoint=build_config["api_endpoint"]["value"],
|
api_endpoint=build_config["api_endpoint"]["value"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Append a special case for Bring your own
|
||||||
|
vectorize_providers["Bring your own"] = [None, ["Bring your own"]]
|
||||||
|
|
||||||
# If the collection is set, allow user to see embedding options
|
# If the collection is set, allow user to see embedding options
|
||||||
build_config["collection_name"]["dialog_inputs"]["fields"]["data"]["node"]["template"][
|
build_config["collection_name"]["dialog_inputs"]["fields"]["data"]["node"]["template"][
|
||||||
"embedding_generation_provider"
|
"embedding_generation_provider"
|
||||||
@ -714,7 +718,11 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
|
|||||||
generation_provider = field_value["embedding_generation_provider"]
|
generation_provider = field_value["embedding_generation_provider"]
|
||||||
provider = generation_provider if generation_provider != "Bring your own" else None
|
provider = generation_provider if generation_provider != "Bring your own" else None
|
||||||
generation_model = field_value["embedding_generation_model"]
|
generation_model = field_value["embedding_generation_model"]
|
||||||
model = generation_model if generation_model else None
|
model = generation_model if generation_model and generation_model != "Bring your own" else None
|
||||||
|
|
||||||
|
# Set the embedding choice
|
||||||
|
build_config["embedding_choice"]["value"] = "Astra Vectorize" if provider else "Embedding Model"
|
||||||
|
build_config["embedding_model"]["advanced"] = bool(provider)
|
||||||
|
|
||||||
# Add the new collection to the list of options
|
# Add the new collection to the list of options
|
||||||
icon = "NVIDIA" if provider == "Nvidia" else "vectorstores"
|
icon = "NVIDIA" if provider == "Nvidia" else "vectorstores"
|
||||||
@ -788,6 +796,10 @@ class AstraDBVectorStoreComponent(LCVectorStoreComponent):
|
|||||||
# Ensure that autodetect collection is set to False, since its a new collection
|
# Ensure that autodetect collection is set to False, since its a new collection
|
||||||
build_config["autodetect_collection"]["value"] = False
|
build_config["autodetect_collection"]["value"] = False
|
||||||
|
|
||||||
|
# If nothing is selected, can't detect provider - return
|
||||||
|
if not field_value:
|
||||||
|
return build_config
|
||||||
|
|
||||||
# Find the position of the selected collection to align with metadata
|
# Find the position of the selected collection to align with metadata
|
||||||
index_of_name = build_config["collection_name"]["options"].index(field_value)
|
index_of_name = build_config["collection_name"]["options"].index(field_value)
|
||||||
value_of_provider = build_config["collection_name"]["options_metadata"][index_of_name]["provider"]
|
value_of_provider = build_config["collection_name"]["options_metadata"][index_of_name]["provider"]
|
||||||
|
|||||||
@ -960,7 +960,9 @@
|
|||||||
"key": "ParseData",
|
"key": "ParseData",
|
||||||
"legacy": false,
|
"legacy": false,
|
||||||
"lf_version": "1.1.5",
|
"lf_version": "1.1.5",
|
||||||
"metadata": {},
|
"metadata": {
|
||||||
|
"legacy_name": "Parse Data"
|
||||||
|
},
|
||||||
"minimized": false,
|
"minimized": false,
|
||||||
"output_types": [],
|
"output_types": [],
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@ -1011,7 +1013,7 @@
|
|||||||
"show": true,
|
"show": true,
|
||||||
"title_case": false,
|
"title_case": false,
|
||||||
"type": "code",
|
"type": "code",
|
||||||
"value": "from langflow.custom import Component\nfrom langflow.helpers.data import data_to_text, data_to_text_list\nfrom langflow.io import DataInput, MultilineInput, Output, StrInput\nfrom langflow.schema import Data\nfrom langflow.schema.message import Message\n\n\nclass ParseDataComponent(Component):\n display_name = \"Data to Message\"\n description = \"Convert Data objects into Messages using any {field_name} from input data.\"\n icon = \"message-square\"\n name = \"ParseData\"\n\n inputs = [\n DataInput(name=\"data\", display_name=\"Data\", info=\"The data to convert to text.\", is_list=True, required=True),\n MultilineInput(\n name=\"template\",\n display_name=\"Template\",\n info=\"The template to use for formatting the data. \"\n \"It can contain the keys {text}, {data} or any other key in the Data.\",\n value=\"{text}\",\n required=True,\n ),\n StrInput(name=\"sep\", display_name=\"Separator\", advanced=True, value=\"\\n\"),\n ]\n\n outputs = [\n Output(\n display_name=\"Message\",\n name=\"text\",\n info=\"Data as a single Message, with each input Data separated by Separator\",\n method=\"parse_data\",\n ),\n Output(\n display_name=\"Data List\",\n name=\"data_list\",\n info=\"Data as a list of new Data, each having `text` formatted by Template\",\n method=\"parse_data_as_list\",\n ),\n ]\n\n def _clean_args(self) -> tuple[list[Data], str, str]:\n data = self.data if isinstance(self.data, list) else [self.data]\n template = self.template\n sep = self.sep\n return data, template, sep\n\n def parse_data(self) -> Message:\n data, template, sep = self._clean_args()\n result_string = data_to_text(template, data, sep)\n self.status = result_string\n return Message(text=result_string)\n\n def parse_data_as_list(self) -> list[Data]:\n data, template, _ = self._clean_args()\n text_list, data_list = data_to_text_list(template, data)\n for item, text in zip(data_list, text_list, strict=True):\n item.set_text(text)\n self.status = data_list\n return data_list\n"
|
"value": "from langflow.custom import Component\nfrom langflow.helpers.data import data_to_text, data_to_text_list\nfrom langflow.io import DataInput, MultilineInput, Output, StrInput\nfrom langflow.schema import Data\nfrom langflow.schema.message import Message\n\n\nclass ParseDataComponent(Component):\n display_name = \"Data to Message\"\n description = \"Convert Data objects into Messages using any {field_name} from input data.\"\n icon = \"message-square\"\n name = \"ParseData\"\n metadata = {\n \"legacy_name\": \"Parse Data\",\n }\n\n inputs = [\n DataInput(\n name=\"data\",\n display_name=\"Data\",\n info=\"The data to convert to text.\",\n is_list=True,\n required=True,\n ),\n MultilineInput(\n name=\"template\",\n display_name=\"Template\",\n info=\"The template to use for formatting the data. \"\n \"It can contain the keys {text}, {data} or any other key in the Data.\",\n value=\"{text}\",\n required=True,\n ),\n StrInput(name=\"sep\", display_name=\"Separator\", advanced=True, value=\"\\n\"),\n ]\n\n outputs = [\n Output(\n display_name=\"Message\",\n name=\"text\",\n info=\"Data as a single Message, with each input Data separated by Separator\",\n method=\"parse_data\",\n ),\n Output(\n display_name=\"Data List\",\n name=\"data_list\",\n info=\"Data as a list of new Data, each having `text` formatted by Template\",\n method=\"parse_data_as_list\",\n ),\n ]\n\n def _clean_args(self) -> tuple[list[Data], str, str]:\n data = self.data if isinstance(self.data, list) else [self.data]\n template = self.template\n sep = self.sep\n return data, template, sep\n\n def parse_data(self) -> Message:\n data, template, sep = self._clean_args()\n result_string = data_to_text(template, data, sep)\n self.status = result_string\n return Message(text=result_string)\n\n def parse_data_as_list(self) -> list[Data]:\n data, template, _ = self._clean_args()\n text_list, data_list = data_to_text_list(template, data)\n for item, text in zip(data_list, text_list, strict=True):\n item.set_text(text)\n self.status = data_list\n return data_list\n"
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"_input_type": "DataInput",
|
"_input_type": "DataInput",
|
||||||
|
|||||||
@ -452,7 +452,9 @@
|
|||||||
"icon": "message-square",
|
"icon": "message-square",
|
||||||
"legacy": false,
|
"legacy": false,
|
||||||
"lf_version": "1.1.5",
|
"lf_version": "1.1.5",
|
||||||
"metadata": {},
|
"metadata": {
|
||||||
|
"legacy_name": "Parse Data"
|
||||||
|
},
|
||||||
"minimized": false,
|
"minimized": false,
|
||||||
"output_types": [],
|
"output_types": [],
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@ -502,7 +504,7 @@
|
|||||||
"show": true,
|
"show": true,
|
||||||
"title_case": false,
|
"title_case": false,
|
||||||
"type": "code",
|
"type": "code",
|
||||||
"value": "from langflow.custom import Component\nfrom langflow.helpers.data import data_to_text, data_to_text_list\nfrom langflow.io import DataInput, MultilineInput, Output, StrInput\nfrom langflow.schema import Data\nfrom langflow.schema.message import Message\n\n\nclass ParseDataComponent(Component):\n display_name = \"Data to Message\"\n description = \"Convert Data objects into Messages using any {field_name} from input data.\"\n icon = \"message-square\"\n name = \"ParseData\"\n\n inputs = [\n DataInput(name=\"data\", display_name=\"Data\", info=\"The data to convert to text.\", is_list=True, required=True),\n MultilineInput(\n name=\"template\",\n display_name=\"Template\",\n info=\"The template to use for formatting the data. \"\n \"It can contain the keys {text}, {data} or any other key in the Data.\",\n value=\"{text}\",\n required=True,\n ),\n StrInput(name=\"sep\", display_name=\"Separator\", advanced=True, value=\"\\n\"),\n ]\n\n outputs = [\n Output(\n display_name=\"Message\",\n name=\"text\",\n info=\"Data as a single Message, with each input Data separated by Separator\",\n method=\"parse_data\",\n ),\n Output(\n display_name=\"Data List\",\n name=\"data_list\",\n info=\"Data as a list of new Data, each having `text` formatted by Template\",\n method=\"parse_data_as_list\",\n ),\n ]\n\n def _clean_args(self) -> tuple[list[Data], str, str]:\n data = self.data if isinstance(self.data, list) else [self.data]\n template = self.template\n sep = self.sep\n return data, template, sep\n\n def parse_data(self) -> Message:\n data, template, sep = self._clean_args()\n result_string = data_to_text(template, data, sep)\n self.status = result_string\n return Message(text=result_string)\n\n def parse_data_as_list(self) -> list[Data]:\n data, template, _ = self._clean_args()\n text_list, data_list = data_to_text_list(template, data)\n for item, text in zip(data_list, text_list, strict=True):\n item.set_text(text)\n self.status = data_list\n return data_list\n"
|
"value": "from langflow.custom import Component\nfrom langflow.helpers.data import data_to_text, data_to_text_list\nfrom langflow.io import DataInput, MultilineInput, Output, StrInput\nfrom langflow.schema import Data\nfrom langflow.schema.message import Message\n\n\nclass ParseDataComponent(Component):\n display_name = \"Data to Message\"\n description = \"Convert Data objects into Messages using any {field_name} from input data.\"\n icon = \"message-square\"\n name = \"ParseData\"\n metadata = {\n \"legacy_name\": \"Parse Data\",\n }\n\n inputs = [\n DataInput(\n name=\"data\",\n display_name=\"Data\",\n info=\"The data to convert to text.\",\n is_list=True,\n required=True,\n ),\n MultilineInput(\n name=\"template\",\n display_name=\"Template\",\n info=\"The template to use for formatting the data. \"\n \"It can contain the keys {text}, {data} or any other key in the Data.\",\n value=\"{text}\",\n required=True,\n ),\n StrInput(name=\"sep\", display_name=\"Separator\", advanced=True, value=\"\\n\"),\n ]\n\n outputs = [\n Output(\n display_name=\"Message\",\n name=\"text\",\n info=\"Data as a single Message, with each input Data separated by Separator\",\n method=\"parse_data\",\n ),\n Output(\n display_name=\"Data List\",\n name=\"data_list\",\n info=\"Data as a list of new Data, each having `text` formatted by Template\",\n method=\"parse_data_as_list\",\n ),\n ]\n\n def _clean_args(self) -> tuple[list[Data], str, str]:\n data = self.data if isinstance(self.data, list) else [self.data]\n template = self.template\n sep = self.sep\n return data, template, sep\n\n def parse_data(self) -> Message:\n data, template, sep = self._clean_args()\n result_string = data_to_text(template, data, sep)\n self.status = result_string\n return Message(text=result_string)\n\n def parse_data_as_list(self) -> list[Data]:\n data, template, _ = self._clean_args()\n text_list, data_list = data_to_text_list(template, data)\n for item, text in zip(data_list, text_list, strict=True):\n item.set_text(text)\n self.status = data_list\n return data_list\n"
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"_input_type": "DataInput",
|
"_input_type": "DataInput",
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -124,6 +124,15 @@ class DatabaseService(Service):
|
|||||||
# if the user specifies an empty dict, we allow it.
|
# if the user specifies an empty dict, we allow it.
|
||||||
kwargs = self._build_connection_kwargs()
|
kwargs = self._build_connection_kwargs()
|
||||||
|
|
||||||
|
poolclass_key = kwargs.get("poolclass")
|
||||||
|
if poolclass_key is not None:
|
||||||
|
pool_class = getattr(sa, poolclass_key, None)
|
||||||
|
if pool_class and isinstance(pool_class(), sa.pool.Pool):
|
||||||
|
logger.debug(f"Using poolclass: {poolclass_key}.")
|
||||||
|
kwargs["poolclass"] = pool_class
|
||||||
|
else:
|
||||||
|
logger.error(f"Invalid poolclass '{poolclass_key}' specified. Using default pool class.")
|
||||||
|
|
||||||
return create_async_engine(
|
return create_async_engine(
|
||||||
self.database_url,
|
self.database_url,
|
||||||
connect_args=self._get_connect_args(),
|
connect_args=self._get_connect_args(),
|
||||||
@ -136,16 +145,18 @@ class DatabaseService(Service):
|
|||||||
return self._create_engine()
|
return self._create_engine()
|
||||||
|
|
||||||
def _get_connect_args(self):
|
def _get_connect_args(self):
|
||||||
if self.settings_service.settings.database_url and self.settings_service.settings.database_url.startswith(
|
settings = self.settings_service.settings
|
||||||
"sqlite"
|
|
||||||
):
|
if settings.db_driver_connection_settings is not None:
|
||||||
connect_args = {
|
return settings.db_driver_connection_settings
|
||||||
|
|
||||||
|
if settings.database_url and settings.database_url.startswith("sqlite"):
|
||||||
|
return {
|
||||||
"check_same_thread": False,
|
"check_same_thread": False,
|
||||||
"timeout": self.settings_service.settings.db_connect_timeout,
|
"timeout": settings.db_connect_timeout,
|
||||||
}
|
}
|
||||||
else:
|
|
||||||
connect_args = {}
|
return {}
|
||||||
return connect_args
|
|
||||||
|
|
||||||
def on_connection(self, dbapi_connection, _connection_record) -> None:
|
def on_connection(self, dbapi_connection, _connection_record) -> None:
|
||||||
if isinstance(dbapi_connection, sqlite3.Connection | dialect_sqlite.aiosqlite.AsyncAdapt_aiosqlite_connection):
|
if isinstance(dbapi_connection, sqlite3.Connection | dialect_sqlite.aiosqlite.AsyncAdapt_aiosqlite_connection):
|
||||||
|
|||||||
@ -90,6 +90,9 @@ class Settings(BaseSettings):
|
|||||||
sqlite_pragmas: dict | None = {"synchronous": "NORMAL", "journal_mode": "WAL"}
|
sqlite_pragmas: dict | None = {"synchronous": "NORMAL", "journal_mode": "WAL"}
|
||||||
"""SQLite pragmas to use when connecting to the database."""
|
"""SQLite pragmas to use when connecting to the database."""
|
||||||
|
|
||||||
|
db_driver_connection_settings: dict | None = None
|
||||||
|
"""Database driver connection settings."""
|
||||||
|
|
||||||
db_connection_settings: dict | None = {
|
db_connection_settings: dict | None = {
|
||||||
"pool_size": 20, # Match the pool_size above
|
"pool_size": 20, # Match the pool_size above
|
||||||
"max_overflow": 30, # Match the max_overflow above
|
"max_overflow": 30, # Match the max_overflow above
|
||||||
|
|||||||
198
src/backend/tests/unit/components/models/test_xai.py
Normal file
198
src/backend/tests/unit/components/models/test_xai.py
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from langflow.components.models import XAIModelComponent
|
||||||
|
from langflow.custom import Component
|
||||||
|
from langflow.custom.utils import build_custom_component_template
|
||||||
|
from langflow.inputs import (
|
||||||
|
BoolInput,
|
||||||
|
DictInput,
|
||||||
|
DropdownInput,
|
||||||
|
IntInput,
|
||||||
|
MessageTextInput,
|
||||||
|
SecretStrInput,
|
||||||
|
SliderInput,
|
||||||
|
)
|
||||||
|
|
||||||
|
from tests.base import ComponentTestBaseWithoutClient
|
||||||
|
|
||||||
|
|
||||||
|
class TestXAIComponent(ComponentTestBaseWithoutClient):
|
||||||
|
@pytest.fixture
|
||||||
|
def component_class(self):
|
||||||
|
return XAIModelComponent
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def default_kwargs(self):
|
||||||
|
return {
|
||||||
|
"temperature": 0.1,
|
||||||
|
"max_tokens": 50,
|
||||||
|
"api_key": "dummy-key",
|
||||||
|
"model_name": "grok-2-latest",
|
||||||
|
"model_kwargs": {},
|
||||||
|
"base_url": "https://api.x.ai/v1",
|
||||||
|
"seed": 42,
|
||||||
|
}
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def file_names_mapping(self):
|
||||||
|
return []
|
||||||
|
|
||||||
|
def test_initialization(self, component_class):
|
||||||
|
component = component_class()
|
||||||
|
assert component.display_name == "xAI"
|
||||||
|
assert component.description == "Generates text using xAI models like Grok."
|
||||||
|
assert component.icon == "xAI"
|
||||||
|
assert component.name == "xAIModel"
|
||||||
|
|
||||||
|
def test_template(self, default_kwargs):
|
||||||
|
component = XAIModelComponent(**default_kwargs)
|
||||||
|
comp = Component(_code=component._code)
|
||||||
|
frontend_node, _ = build_custom_component_template(comp)
|
||||||
|
assert isinstance(frontend_node, dict)
|
||||||
|
assert "template" in frontend_node
|
||||||
|
input_names = [inp["name"] for inp in frontend_node["template"].values() if isinstance(inp, dict)]
|
||||||
|
expected_inputs = [
|
||||||
|
"max_tokens",
|
||||||
|
"model_kwargs",
|
||||||
|
"json_mode",
|
||||||
|
"model_name",
|
||||||
|
"base_url",
|
||||||
|
"api_key",
|
||||||
|
"temperature",
|
||||||
|
"seed",
|
||||||
|
]
|
||||||
|
for input_name in expected_inputs:
|
||||||
|
assert input_name in input_names
|
||||||
|
|
||||||
|
def test_inputs(self):
|
||||||
|
component = XAIModelComponent()
|
||||||
|
inputs = component.inputs
|
||||||
|
expected_inputs = {
|
||||||
|
"max_tokens": IntInput,
|
||||||
|
"model_kwargs": DictInput,
|
||||||
|
"json_mode": BoolInput,
|
||||||
|
"model_name": DropdownInput,
|
||||||
|
"base_url": MessageTextInput,
|
||||||
|
"api_key": SecretStrInput,
|
||||||
|
"temperature": SliderInput,
|
||||||
|
"seed": IntInput,
|
||||||
|
}
|
||||||
|
for name, input_type in expected_inputs.items():
|
||||||
|
matching_inputs = [inp for inp in inputs if isinstance(inp, input_type) and inp.name == name]
|
||||||
|
assert matching_inputs, f"Missing or incorrect input: {name}"
|
||||||
|
if name == "model_name":
|
||||||
|
input_field = matching_inputs[0]
|
||||||
|
assert input_field.value == "grok-2-latest"
|
||||||
|
assert input_field.refresh_button is True
|
||||||
|
elif name == "temperature":
|
||||||
|
input_field = matching_inputs[0]
|
||||||
|
assert input_field.value == 0.1
|
||||||
|
assert input_field.range_spec.min == 0
|
||||||
|
assert input_field.range_spec.max == 2
|
||||||
|
|
||||||
|
def test_build_model(self, component_class, default_kwargs, mocker):
|
||||||
|
component = component_class(**default_kwargs)
|
||||||
|
component.temperature = 0.7
|
||||||
|
component.max_tokens = 100
|
||||||
|
component.api_key = "test-key"
|
||||||
|
component.model_name = "grok-2-latest"
|
||||||
|
component.model_kwargs = {}
|
||||||
|
component.base_url = "https://api.x.ai/v1"
|
||||||
|
component.seed = 1
|
||||||
|
|
||||||
|
mock_chat_openai = mocker.patch("langflow.components.models.xai.ChatOpenAI", return_value=MagicMock())
|
||||||
|
model = component.build_model()
|
||||||
|
mock_chat_openai.assert_called_once_with(
|
||||||
|
max_tokens=100,
|
||||||
|
model_kwargs={},
|
||||||
|
model="grok-2-latest",
|
||||||
|
base_url="https://api.x.ai/v1",
|
||||||
|
api_key="test-key",
|
||||||
|
temperature=0.7,
|
||||||
|
seed=1,
|
||||||
|
)
|
||||||
|
assert model == mock_chat_openai.return_value
|
||||||
|
|
||||||
|
def test_get_models(self):
|
||||||
|
component = XAIModelComponent()
|
||||||
|
with patch("requests.get") as mock_get:
|
||||||
|
mock_response = MagicMock()
|
||||||
|
mock_response.json.return_value = {
|
||||||
|
"models": [
|
||||||
|
{"id": "grok-2-latest", "aliases": ["grok-2"]},
|
||||||
|
{"id": "grok-1", "aliases": []},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
mock_get.return_value = mock_response
|
||||||
|
|
||||||
|
component.api_key = "test-key"
|
||||||
|
models = component.get_models()
|
||||||
|
assert sorted(models) == ["grok-1", "grok-2", "grok-2-latest"]
|
||||||
|
mock_get.assert_called_once_with(
|
||||||
|
"https://api.x.ai/v1/language-models",
|
||||||
|
headers={
|
||||||
|
"Authorization": "Bearer test-key",
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
timeout=10,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_get_models_no_api_key(self):
|
||||||
|
component = XAIModelComponent(api_key=None)
|
||||||
|
models = component.get_models()
|
||||||
|
assert models == ["grok-2-latest"]
|
||||||
|
|
||||||
|
def test_build_model_error(self, component_class, mocker):
|
||||||
|
from openai import BadRequestError
|
||||||
|
|
||||||
|
component = component_class()
|
||||||
|
component.api_key = "invalid-key"
|
||||||
|
component.model_name = "grok-2-latest"
|
||||||
|
component.temperature = 0.7
|
||||||
|
component.max_tokens = 100
|
||||||
|
component.model_kwargs = {}
|
||||||
|
component.base_url = "https://api.x.ai/v1"
|
||||||
|
component.seed = 1
|
||||||
|
|
||||||
|
mocker.patch(
|
||||||
|
"langflow.components.models.xai.ChatOpenAI",
|
||||||
|
side_effect=BadRequestError(
|
||||||
|
message="Invalid API key",
|
||||||
|
response=MagicMock(),
|
||||||
|
body={"message": "Invalid API key provided"},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
with pytest.raises(BadRequestError) as exc_info:
|
||||||
|
component.build_model()
|
||||||
|
assert exc_info.value.body["message"] == "Invalid API key provided"
|
||||||
|
|
||||||
|
def test_json_mode(self, component_class, mocker):
|
||||||
|
component = component_class()
|
||||||
|
component.api_key = "test-key"
|
||||||
|
component.json_mode = True
|
||||||
|
component.temperature = 0.7
|
||||||
|
component.max_tokens = 100
|
||||||
|
component.model_name = "grok-2-latest"
|
||||||
|
component.model_kwargs = {}
|
||||||
|
component.base_url = "https://api.x.ai/v1"
|
||||||
|
component.seed = 1
|
||||||
|
|
||||||
|
mock_instance = MagicMock()
|
||||||
|
mock_bound_instance = MagicMock()
|
||||||
|
mock_instance.bind.return_value = mock_bound_instance
|
||||||
|
mocker.patch("langflow.components.models.xai.ChatOpenAI", return_value=mock_instance)
|
||||||
|
|
||||||
|
model = component.build_model()
|
||||||
|
mock_instance.bind.assert_called_once_with(response_format={"type": "json_object"})
|
||||||
|
assert model == mock_bound_instance
|
||||||
|
|
||||||
|
def test_update_build_config(self):
|
||||||
|
component = XAIModelComponent()
|
||||||
|
build_config = {"model_name": {"options": []}}
|
||||||
|
|
||||||
|
updated_config = component.update_build_config(build_config, "test-key", "api_key")
|
||||||
|
assert "model_name" in updated_config
|
||||||
|
|
||||||
|
updated_config = component.update_build_config(build_config, "grok-2-latest", "model_name")
|
||||||
|
assert "model_name" in updated_config
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
import { getNodeOutputColors } from "../../../helpers/get-node-output-colors";
|
import { getNodeOutputColors } from "../../../helpers/get-node-output-colors";
|
||||||
import { getNodeOutputColorsName } from "../../../helpers/get-node-output-colors-name";
|
import { getNodeOutputColorsName } from "../../../helpers/get-node-output-colors-name";
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
import ForwardedIconComponent from "../../components/common/genericIconComponent";
|
import ForwardedIconComponent from "../../components/common/genericIconComponent";
|
||||||
import Checkmark from "../../components/ui/checkmark";
|
|
||||||
import Loading from "../../components/ui/loading";
|
|
||||||
import Xmark from "../../components/ui/xmark";
|
import Xmark from "../../components/ui/xmark";
|
||||||
import { BuildStatus } from "../../constants/enums";
|
import { BuildStatus } from "../../constants/enums";
|
||||||
import { VertexBuildTypeAPI } from "../../types/api";
|
|
||||||
|
|
||||||
const useIconStatus = (buildStatus: BuildStatus | undefined) => {
|
const useIconStatus = (buildStatus: BuildStatus | undefined) => {
|
||||||
const conditionError = buildStatus === BuildStatus.ERROR;
|
const conditionError = buildStatus === BuildStatus.ERROR;
|
||||||
|
|||||||
@ -18,7 +18,6 @@ import {
|
|||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "../../ui/card";
|
} from "../../ui/card";
|
||||||
import Loading from "../../ui/loading";
|
|
||||||
import IconComponent from "../genericIconComponent";
|
import IconComponent from "../genericIconComponent";
|
||||||
import ShadTooltip from "../shadTooltipComponent";
|
import ShadTooltip from "../shadTooltipComponent";
|
||||||
import useDataEffect from "./hooks/use-data-effect";
|
import useDataEffect from "./hooks/use-data-effect";
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import BaseModal from "../../../modals/baseModal";
|
import BaseModal from "../../../modals/baseModal";
|
||||||
import { fetchErrorComponentType } from "../../../types/components";
|
import { fetchErrorComponentType } from "../../../types/components";
|
||||||
import Loading from "../../ui/loading";
|
import Loading from "../../ui/loading";
|
||||||
import IconComponent from "../genericIconComponent";
|
|
||||||
|
|
||||||
export default function TimeoutErrorComponent({
|
export default function TimeoutErrorComponent({
|
||||||
message,
|
message,
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import ForwardedIconComponent from "@/components/common/genericIconComponent";
|
|
||||||
import { useLogout } from "@/controllers/API/queries/auth";
|
import { useLogout } from "@/controllers/API/queries/auth";
|
||||||
import { CustomFeedbackDialog } from "@/customization/components/custom-feedback-dialog";
|
import { CustomFeedbackDialog } from "@/customization/components/custom-feedback-dialog";
|
||||||
import { CustomHeaderMenuItemsTitle } from "@/customization/components/custom-header-menu-items-title";
|
import { CustomHeaderMenuItemsTitle } from "@/customization/components/custom-header-menu-items-title";
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { FlowType } from "@/types/flow";
|
import { FlowType } from "@/types/flow";
|
||||||
import { storeComponent } from "../../../../../types/store";
|
|
||||||
import { cn } from "../../../../../utils/utils";
|
import { cn } from "../../../../../utils/utils";
|
||||||
import ForwardedIconComponent from "../../../../common/genericIconComponent";
|
import ForwardedIconComponent from "../../../../common/genericIconComponent";
|
||||||
import { Card, CardHeader, CardTitle } from "../../../../ui/card";
|
import { Card, CardHeader, CardTitle } from "../../../../ui/card";
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { FlowType } from "@/types/flow";
|
|||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||||
import { storeComponent } from "../../../../types/store";
|
|
||||||
import DragCardComponent from "../components/dragCardComponent";
|
import DragCardComponent from "../components/dragCardComponent";
|
||||||
|
|
||||||
const useDragStart = (data: FlowType) => {
|
const useDragStart = (data: FlowType) => {
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import { getInputsAndOutputs } from "../../../utils/storeUtils";
|
|||||||
import { cn } from "../../../utils/utils";
|
import { cn } from "../../../utils/utils";
|
||||||
import IconComponent from "../../common/genericIconComponent";
|
import IconComponent from "../../common/genericIconComponent";
|
||||||
import ShadTooltip from "../../common/shadTooltipComponent";
|
import ShadTooltip from "../../common/shadTooltipComponent";
|
||||||
import { Button } from "../../ui/button";
|
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardDescription,
|
CardDescription,
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||||
import { tomorrow } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
import { tomorrow } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
||||||
import { useDarkStore } from "../../../stores/darkStore";
|
|
||||||
import IconComponent from "../../common/genericIconComponent";
|
import IconComponent from "../../common/genericIconComponent";
|
||||||
import { Button } from "../../ui/button";
|
import { Button } from "../../ui/button";
|
||||||
|
|
||||||
|
|||||||
@ -65,10 +65,12 @@ export default function TableNodeComponent({
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
const [selectedNodes, setSelectedNodes] = useState<Array<any>>([]);
|
const [selectedNodes, setSelectedNodes] = useState<Array<any>>([]);
|
||||||
|
const [tempValue, setTempValue] = useState<any[]>(cloneDeep(value));
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const agGrid = useRef<AgGridReact>(null);
|
const agGrid = useRef<AgGridReact>(null);
|
||||||
const componentColumns = columns
|
const componentColumns = columns
|
||||||
? columns
|
? columns
|
||||||
: generateBackendColumnsFromValue(value ?? [], table_options);
|
: generateBackendColumnsFromValue(tempValue ?? [], table_options);
|
||||||
let AgColumns = FormatColumns(componentColumns);
|
let AgColumns = FormatColumns(componentColumns);
|
||||||
// add info to each column
|
// add info to each column
|
||||||
AgColumns = AgColumns.map((col) => {
|
AgColumns = AgColumns.map((col) => {
|
||||||
@ -93,7 +95,7 @@ export default function TableNodeComponent({
|
|||||||
if (agGrid.current && !agGrid.current.api.isDestroyed()) {
|
if (agGrid.current && !agGrid.current.api.isDestroyed()) {
|
||||||
const rows: any = [];
|
const rows: any = [];
|
||||||
agGrid.current.api.forEachNode((node) => rows.push(node.data));
|
agGrid.current.api.forEachNode((node) => rows.push(node.data));
|
||||||
handleOnNewValue({ value: rows });
|
setTempValue(rows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function deleteRow() {
|
function deleteRow() {
|
||||||
@ -109,8 +111,7 @@ export default function TableNodeComponent({
|
|||||||
if (agGrid.current && selectedNodes.length > 0) {
|
if (agGrid.current && selectedNodes.length > 0) {
|
||||||
const toDuplicate = selectedNodes.map((node) => cloneDeep(node.data));
|
const toDuplicate = selectedNodes.map((node) => cloneDeep(node.data));
|
||||||
setSelectedNodes([]);
|
setSelectedNodes([]);
|
||||||
const rows: any = [];
|
setTempValue([...tempValue, ...toDuplicate]);
|
||||||
handleOnNewValue({ value: [...value, ...toDuplicate] });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function addRow() {
|
function addRow() {
|
||||||
@ -118,12 +119,23 @@ export default function TableNodeComponent({
|
|||||||
componentColumns.forEach((column) => {
|
componentColumns.forEach((column) => {
|
||||||
newRow[column.name] = column.default ?? null; // Use the default value if available
|
newRow[column.name] = column.default ?? null; // Use the default value if available
|
||||||
});
|
});
|
||||||
handleOnNewValue({ value: [...value, newRow] });
|
setTempValue([...tempValue, newRow]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateComponent() {
|
function updateComponent() {
|
||||||
setAllRows();
|
setAllRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleSave() {
|
||||||
|
handleOnNewValue({ value: tempValue });
|
||||||
|
setIsModalOpen(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancel() {
|
||||||
|
setTempValue(cloneDeep(value));
|
||||||
|
setIsModalOpen(false);
|
||||||
|
}
|
||||||
|
|
||||||
const editable = componentColumns
|
const editable = componentColumns
|
||||||
.map((column) => {
|
.map((column) => {
|
||||||
const isCustomEdit =
|
const isCustomEdit =
|
||||||
@ -149,6 +161,8 @@ export default function TableNodeComponent({
|
|||||||
>
|
>
|
||||||
<div className="flex w-full items-center gap-3" data-testid={"div-" + id}>
|
<div className="flex w-full items-center gap-3" data-testid={"div-" + id}>
|
||||||
<TableModal
|
<TableModal
|
||||||
|
open={isModalOpen}
|
||||||
|
setOpen={setIsModalOpen}
|
||||||
stopEditingWhenCellsLoseFocus={true}
|
stopEditingWhenCellsLoseFocus={true}
|
||||||
tableIcon={table_icon}
|
tableIcon={table_icon}
|
||||||
tableOptions={table_options}
|
tableOptions={table_options}
|
||||||
@ -169,8 +183,10 @@ export default function TableNodeComponent({
|
|||||||
displayEmptyAlert={false}
|
displayEmptyAlert={false}
|
||||||
className="h-full w-full"
|
className="h-full w-full"
|
||||||
columnDefs={AgColumns}
|
columnDefs={AgColumns}
|
||||||
rowData={value}
|
rowData={tempValue}
|
||||||
context={{ field_parsers: table_options?.field_parsers }}
|
context={{ field_parsers: table_options?.field_parsers }}
|
||||||
|
onSave={handleSave}
|
||||||
|
onCancel={handleCancel}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "../../../../ui/input";
|
import { Input } from "../../../../ui/input";
|
||||||
import { ButtonInputList } from "./components/button-input-list";
|
import { ButtonInputList } from "./components/button-input-list";
|
||||||
import { DropdownMenuInputList } from "./components/dropdown-menu";
|
|
||||||
|
|
||||||
import { GRADIENT_CLASS } from "@/constants/constants";
|
import { GRADIENT_CLASS } from "@/constants/constants";
|
||||||
import { cn } from "../../../../../utils/utils";
|
import { cn } from "../../../../../utils/utils";
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import {
|
|||||||
} from "@/utils/reactflowUtils";
|
} from "@/utils/reactflowUtils";
|
||||||
import { cn } from "@/utils/utils";
|
import { cn } from "@/utils/utils";
|
||||||
import { cloneDeep } from "lodash";
|
import { cloneDeep } from "lodash";
|
||||||
import React, { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import IconComponent from "../../../../common/genericIconComponent";
|
import IconComponent from "../../../../common/genericIconComponent";
|
||||||
|
|
||||||
const KeypairListComponent = ({
|
const KeypairListComponent = ({
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { PopoverAnchor } from "@radix-ui/react-popover";
|
|
||||||
import Fuse from "fuse.js";
|
import Fuse from "fuse.js";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { cn } from "../../../../../utils/utils";
|
import { cn } from "../../../../../utils/utils";
|
||||||
|
|||||||
@ -13,7 +13,6 @@ import {
|
|||||||
memo,
|
memo,
|
||||||
useCallback,
|
useCallback,
|
||||||
useContext,
|
useContext,
|
||||||
useEffect,
|
|
||||||
useId,
|
useId,
|
||||||
useMemo,
|
useMemo,
|
||||||
} from "react";
|
} from "react";
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import {
|
|||||||
TargetAndTransition,
|
TargetAndTransition,
|
||||||
Variants,
|
Variants,
|
||||||
} from "framer-motion";
|
} from "framer-motion";
|
||||||
import React, { ReactElement } from "react";
|
import React from "react";
|
||||||
|
|
||||||
type PresetType = "blur" | "shake" | "scale" | "fade" | "slide";
|
type PresetType = "blur" | "shake" | "scale" | "fade" | "slide";
|
||||||
|
|
||||||
|
|||||||
@ -3,13 +3,11 @@ import { AxiosRequestConfig, AxiosResponse } from "axios";
|
|||||||
import { BASE_URL_API } from "../../constants/constants";
|
import { BASE_URL_API } from "../../constants/constants";
|
||||||
import { api } from "../../controllers/API/api";
|
import { api } from "../../controllers/API/api";
|
||||||
import {
|
import {
|
||||||
Component,
|
|
||||||
VertexBuildTypeAPI,
|
VertexBuildTypeAPI,
|
||||||
VerticesOrderTypeAPI,
|
VerticesOrderTypeAPI,
|
||||||
} from "../../types/api/index";
|
} from "../../types/api/index";
|
||||||
import { FlowStyleType, FlowType } from "../../types/flow";
|
import { FlowStyleType, FlowType } from "../../types/flow";
|
||||||
import { StoreComponentResponse } from "../../types/store";
|
import { StoreComponentResponse } from "../../types/store";
|
||||||
import { FlowPoolType } from "../../types/zustand/flow";
|
|
||||||
|
|
||||||
const GITHUB_API_URL = "https://api.github.com";
|
const GITHUB_API_URL = "https://api.github.com";
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,4 @@
|
|||||||
import {
|
import { resetPasswordType, useMutationFunctionType } from "@/types/api";
|
||||||
changeUser,
|
|
||||||
resetPasswordType,
|
|
||||||
useMutationFunctionType,
|
|
||||||
} from "@/types/api";
|
|
||||||
import { UseMutationResult } from "@tanstack/react-query";
|
import { UseMutationResult } from "@tanstack/react-query";
|
||||||
import { api } from "../../api";
|
import { api } from "../../api";
|
||||||
import { getURL } from "../../helpers/constants";
|
import { getURL } from "../../helpers/constants";
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { LoginType, changeUser, useMutationFunctionType } from "@/types/api";
|
import { LoginType, useMutationFunctionType } from "@/types/api";
|
||||||
import { UseMutationResult } from "@tanstack/react-query";
|
import { UseMutationResult } from "@tanstack/react-query";
|
||||||
import { api } from "../../api";
|
import { api } from "../../api";
|
||||||
import { getURL } from "../../helpers/constants";
|
import { getURL } from "../../helpers/constants";
|
||||||
|
|||||||
@ -1,9 +1,4 @@
|
|||||||
import { keepPreviousData } from "@tanstack/react-query";
|
import { useMutationFunctionType } from "../../../../types/api";
|
||||||
import {
|
|
||||||
useMutationFunctionType,
|
|
||||||
useQueryFunctionType,
|
|
||||||
} from "../../../../types/api";
|
|
||||||
import { api } from "../../api";
|
|
||||||
import { getURL } from "../../helpers/constants";
|
import { getURL } from "../../helpers/constants";
|
||||||
import { UseRequestProcessor } from "../../services/request-processor";
|
import { UseRequestProcessor } from "../../services/request-processor";
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { useGlobalVariablesStore } from "@/stores/globalVariablesStore/globalVar
|
|||||||
import getUnavailableFields from "@/stores/globalVariablesStore/utils/get-unavailable-fields";
|
import getUnavailableFields from "@/stores/globalVariablesStore/utils/get-unavailable-fields";
|
||||||
import { useMutationFunctionType } from "@/types/api";
|
import { useMutationFunctionType } from "@/types/api";
|
||||||
import { GlobalVariable } from "@/types/global_variables";
|
import { GlobalVariable } from "@/types/global_variables";
|
||||||
import { UseMutationOptions, UseMutationResult } from "@tanstack/react-query";
|
import { UseMutationResult } from "@tanstack/react-query";
|
||||||
import { api } from "../../api";
|
import { api } from "../../api";
|
||||||
import { getURL } from "../../helpers/constants";
|
import { getURL } from "../../helpers/constants";
|
||||||
import { UseRequestProcessor } from "../../services/request-processor";
|
import { UseRequestProcessor } from "../../services/request-processor";
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { useMutationFunctionType } from "@/types/api";
|
import { useMutationFunctionType } from "@/types/api";
|
||||||
import { GlobalVariable } from "@/types/global_variables";
|
|
||||||
import { UseMutationResult } from "@tanstack/react-query";
|
import { UseMutationResult } from "@tanstack/react-query";
|
||||||
import { api } from "../../api";
|
import { api } from "../../api";
|
||||||
import { getURL } from "../../helpers/constants";
|
import { getURL } from "../../helpers/constants";
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { useGetRefreshFlows } from "@/controllers/API/queries/flows/use-get-refresh-flows";
|
|
||||||
import { createFileUpload } from "@/helpers/create-file-upload";
|
import { createFileUpload } from "@/helpers/create-file-upload";
|
||||||
import { getObjectsFromFilelist } from "@/helpers/get-objects-from-filelist";
|
import { getObjectsFromFilelist } from "@/helpers/get-objects-from-filelist";
|
||||||
import useFlowStore from "@/stores/flowStore";
|
import useFlowStore from "@/stores/flowStore";
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
const ArXivIcon = (props) => {
|
const ArXivIcon = (props) => {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
const UpstashIcon = (props) => (
|
const UpstashIcon = (props) => (
|
||||||
<svg
|
<svg
|
||||||
width="256px"
|
width="256px"
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
const YouTubeIcon = (props) => (
|
const YouTubeIcon = (props) => (
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
|||||||
10
src/frontend/src/icons/xAI/index.tsx
Normal file
10
src/frontend/src/icons/xAI/index.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { useDarkStore } from "@/stores/darkStore";
|
||||||
|
import React, { forwardRef } from "react";
|
||||||
|
import XAISVG from "./xAIIcon.jsx";
|
||||||
|
|
||||||
|
export const XAIIcon = forwardRef<SVGSVGElement, React.PropsWithChildren<{}>>(
|
||||||
|
(props, ref) => {
|
||||||
|
const isdark = useDarkStore((state) => state.dark).toString();
|
||||||
|
return <XAISVG ref={ref} isdark={isdark} {...props} />;
|
||||||
|
},
|
||||||
|
);
|
||||||
19
src/frontend/src/icons/xAI/xAIIcon.jsx
Normal file
19
src/frontend/src/icons/xAI/xAIIcon.jsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { stringToBool } from "@/utils/utils";
|
||||||
|
|
||||||
|
const XAISVG = (props) => (
|
||||||
|
<svg
|
||||||
|
{...props}
|
||||||
|
fill={stringToBool(props.isdark) ? "#ffffff" : "#0A0A0A"}
|
||||||
|
fillRule="evenodd"
|
||||||
|
height="1em"
|
||||||
|
style={{ flex: "none", lineHeight: 1 }}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<title>Grok</title>
|
||||||
|
<path d="M6.469 8.776L16.512 23h-4.464L2.005 8.776H6.47zm-.004 7.9l2.233 3.164L6.467 23H2l4.465-6.324zM22 2.582V23h-3.659V7.764L22 2.582zM22 1l-9.952 14.095-2.233-3.163L17.533 1H22z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default XAISVG;
|
||||||
1
src/frontend/src/icons/xAI/xai.svg
Normal file
1
src/frontend/src/icons/xAI/xai.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Grok</title><path d="M6.469 8.776L16.512 23h-4.464L2.005 8.776H6.47zm-.004 7.9l2.233 3.164L6.467 23H2l4.465-6.324zM22 2.582V23h-3.659V7.764L22 2.582zM22 1l-9.952 14.095-2.233-3.163L17.533 1H22z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 372 B |
@ -5,7 +5,7 @@ import useFileSizeValidator from "@/shared/hooks/use-file-size-validator";
|
|||||||
import useAlertStore from "@/stores/alertStore";
|
import useAlertStore from "@/stores/alertStore";
|
||||||
import useFlowStore from "@/stores/flowStore";
|
import useFlowStore from "@/stores/flowStore";
|
||||||
import { useUtilityStore } from "@/stores/utilityStore";
|
import { useUtilityStore } from "@/stores/utilityStore";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
import {
|
import {
|
||||||
ALLOWED_IMAGE_INPUT_EXTENSIONS,
|
ALLOWED_IMAGE_INPUT_EXTENSIONS,
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { useState } from "react";
|
|
||||||
import IconComponent, {
|
import IconComponent, {
|
||||||
ForwardedIconComponent,
|
ForwardedIconComponent,
|
||||||
} from "../../../../../../components/common/genericIconComponent";
|
} from "../../../../../../components/common/genericIconComponent";
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import JsonView from "react18-json-view";
|
|||||||
import "react18-json-view/src/dark.css";
|
import "react18-json-view/src/dark.css";
|
||||||
import "react18-json-view/src/style.css";
|
import "react18-json-view/src/style.css";
|
||||||
import IconComponent from "../../components/common/genericIconComponent";
|
import IconComponent from "../../components/common/genericIconComponent";
|
||||||
import { CODE_DICT_DIALOG_SUBTITLE } from "../../constants/constants";
|
|
||||||
import { useDarkStore } from "../../stores/darkStore";
|
import { useDarkStore } from "../../stores/darkStore";
|
||||||
import BaseModal from "../baseModal";
|
import BaseModal from "../baseModal";
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { track } from "../../../../customization/utils/analytics";
|
import { track } from "../../../../customization/utils/analytics";
|
||||||
import useAddFlow from "../../../../hooks/flows/use-add-flow";
|
import useAddFlow from "../../../../hooks/flows/use-add-flow";
|
||||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
|
||||||
import { FlowType } from "../../../../types/flow";
|
import { FlowType } from "../../../../types/flow";
|
||||||
import { updateIds } from "../../../../utils/reactflowUtils";
|
import { updateIds } from "../../../../utils/reactflowUtils";
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,6 @@ import {
|
|||||||
} from "../../../../components/ui/card";
|
} from "../../../../components/ui/card";
|
||||||
import { useFolderStore } from "../../../../stores/foldersStore";
|
import { useFolderStore } from "../../../../stores/foldersStore";
|
||||||
import { UndrawCardComponentProps } from "../../../../types/components";
|
import { UndrawCardComponentProps } from "../../../../types/components";
|
||||||
import { updateIds } from "../../../../utils/reactflowUtils";
|
|
||||||
import { useFlowCardClick } from "../hooks/use-redirect-flow-card-click";
|
import { useFlowCardClick } from "../hooks/use-redirect-flow-card-click";
|
||||||
|
|
||||||
export default function UndrawCardComponent({
|
export default function UndrawCardComponent({
|
||||||
|
|||||||
@ -15,6 +15,10 @@ interface TableModalProps extends TableComponentProps {
|
|||||||
tableOptions?: TableOptionsTypeAPI;
|
tableOptions?: TableOptionsTypeAPI;
|
||||||
hideColumns?: boolean | string[];
|
hideColumns?: boolean | string[];
|
||||||
tableIcon?: string;
|
tableIcon?: string;
|
||||||
|
open?: boolean;
|
||||||
|
setOpen?: (open: boolean) => void;
|
||||||
|
onSave?: () => void;
|
||||||
|
onCancel?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TableModal = forwardRef<AgGridReact, TableModalProps>(
|
const TableModal = forwardRef<AgGridReact, TableModalProps>(
|
||||||
@ -25,22 +29,43 @@ const TableModal = forwardRef<AgGridReact, TableModalProps>(
|
|||||||
children,
|
children,
|
||||||
disabled,
|
disabled,
|
||||||
tableIcon,
|
tableIcon,
|
||||||
|
open,
|
||||||
|
setOpen,
|
||||||
|
onSave,
|
||||||
|
onCancel,
|
||||||
...props
|
...props
|
||||||
}: TableModalProps,
|
}: TableModalProps,
|
||||||
ref: ForwardedRef<AgGridReact>,
|
ref: ForwardedRef<AgGridReact>,
|
||||||
) => {
|
) => {
|
||||||
|
const handleSetOpen = (newOpen: boolean) => {
|
||||||
|
if (!newOpen && onCancel) {
|
||||||
|
onCancel();
|
||||||
|
}
|
||||||
|
if (setOpen) {
|
||||||
|
setOpen(newOpen);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOnEscapeKeyDown = (e: KeyboardEvent) => {
|
||||||
|
const editingCells = (
|
||||||
|
ref as React.RefObject<AgGridReact>
|
||||||
|
)?.current?.api?.getEditingCells();
|
||||||
|
|
||||||
|
if (editingCells && editingCells.length > 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseModal
|
<BaseModal
|
||||||
onEscapeKeyDown={(e) => {
|
onEscapeKeyDown={(e) => {
|
||||||
if (
|
handleOnEscapeKeyDown(e);
|
||||||
(
|
|
||||||
ref as React.RefObject<AgGridReact>
|
|
||||||
)?.current?.api.getEditingCells().length
|
|
||||||
) {
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
disable={disabled}
|
disable={disabled}
|
||||||
|
open={open}
|
||||||
|
setOpen={(newOpen) => {
|
||||||
|
handleSetOpen(newOpen);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<BaseModal.Trigger asChild>{children}</BaseModal.Trigger>
|
<BaseModal.Trigger asChild>{children}</BaseModal.Trigger>
|
||||||
<BaseModal.Header
|
<BaseModal.Header
|
||||||
@ -59,6 +84,9 @@ const TableModal = forwardRef<AgGridReact, TableModalProps>(
|
|||||||
{...props}
|
{...props}
|
||||||
></TableComponent>
|
></TableComponent>
|
||||||
</BaseModal.Content>
|
</BaseModal.Content>
|
||||||
|
<BaseModal.Footer
|
||||||
|
submit={onSave ? { label: "Save", onClick: onSave } : undefined}
|
||||||
|
></BaseModal.Footer>
|
||||||
</BaseModal>
|
</BaseModal>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,7 +1,4 @@
|
|||||||
import {
|
import { ENABLE_INTEGRATIONS } from "@/customization/feature-flags";
|
||||||
ENABLE_INTEGRATIONS,
|
|
||||||
ENABLE_MVPS,
|
|
||||||
} from "@/customization/feature-flags";
|
|
||||||
import { useStoreStore } from "@/stores/storeStore";
|
import { useStoreStore } from "@/stores/storeStore";
|
||||||
import { cloneDeep } from "lodash";
|
import { cloneDeep } from "lodash";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import React from "react";
|
|
||||||
const NoResultsMessage = ({
|
const NoResultsMessage = ({
|
||||||
onClearSearch,
|
onClearSearch,
|
||||||
message = "No components found.",
|
message = "No components found.",
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
const FeatureToggles = ({
|
const FeatureToggles = ({
|
||||||
showBeta,
|
showBeta,
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import ForwardedIconComponent from "@/components/common/genericIconComponent";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { SidebarMenuButton } from "@/components/ui/sidebar";
|
import { SidebarMenuButton } from "@/components/ui/sidebar";
|
||||||
import { CustomLink } from "@/customization/components/custom-link";
|
import { CustomLink } from "@/customization/components/custom-link";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
const SidebarMenuButtons = ({
|
const SidebarMenuButtons = ({
|
||||||
hasStore = false,
|
hasStore = false,
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { APIDataType } from "@/types/api";
|
import { APIDataType } from "@/types/api";
|
||||||
import { FuseResult } from "fuse.js";
|
|
||||||
|
|
||||||
export const filteredDataFn = (
|
export const filteredDataFn = (
|
||||||
data: APIDataType,
|
data: APIDataType,
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { APIDataType } from "@/types/api";
|
import { APIDataType } from "@/types/api";
|
||||||
import { normalizeString } from "./normalize-string";
|
|
||||||
import { searchInMetadata } from "./search-on-metadata";
|
import { searchInMetadata } from "./search-on-metadata";
|
||||||
|
|
||||||
export const traditionalSearchMetadata = (
|
export const traditionalSearchMetadata = (
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import EditNodeModal from "@/modals/editNodeModal";
|
|||||||
import ShareModal from "@/modals/shareModal";
|
import ShareModal from "@/modals/shareModal";
|
||||||
import { APIClassType } from "@/types/api";
|
import { APIClassType } from "@/types/api";
|
||||||
import { FlowType } from "@/types/flow";
|
import { FlowType } from "@/types/flow";
|
||||||
import React, { memo } from "react";
|
import { memo } from "react";
|
||||||
|
|
||||||
interface ToolbarModalsProps {
|
interface ToolbarModalsProps {
|
||||||
// Modal visibility states
|
// Modal visibility states
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import ForwardedIconComponent from "../../../../../components/common/genericIconComponent";
|
import ForwardedIconComponent from "../../../../../components/common/genericIconComponent";
|
||||||
import RenderIcons from "../../../../../components/common/renderIconComponent";
|
import RenderIcons from "../../../../../components/common/renderIconComponent";
|
||||||
import { IS_MAC } from "../../../../../constants/constants";
|
|
||||||
import { toolbarSelectItemProps } from "../../../../../types/components";
|
import { toolbarSelectItemProps } from "../../../../../types/components";
|
||||||
|
|
||||||
export default function ToolbarSelectItem({
|
export default function ToolbarSelectItem({
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { useGetFolderQuery } from "@/controllers/API/queries/folders/use-get-fol
|
|||||||
import useDeleteFlow from "@/hooks/flows/use-delete-flow";
|
import useDeleteFlow from "@/hooks/flows/use-delete-flow";
|
||||||
import { useFolderStore } from "@/stores/foldersStore";
|
import { useFolderStore } from "@/stores/foldersStore";
|
||||||
import { useIsFetching, useIsMutating } from "@tanstack/react-query";
|
import { useIsFetching, useIsMutating } from "@tanstack/react-query";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { useLocation, useParams } from "react-router-dom";
|
import { useLocation, useParams } from "react-router-dom";
|
||||||
import ComponentsComponent from "../componentsComponent";
|
import ComponentsComponent from "../componentsComponent";
|
||||||
import HeaderTabsSearchComponent from "./components/headerTabsSearchComponent";
|
import HeaderTabsSearchComponent from "./components/headerTabsSearchComponent";
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import {
|
|||||||
useGetApiKeysQuery,
|
useGetApiKeysQuery,
|
||||||
} from "@/controllers/API/queries/api-keys";
|
} from "@/controllers/API/queries/api-keys";
|
||||||
import { SelectionChangedEvent } from "ag-grid-community";
|
import { SelectionChangedEvent } from "ag-grid-community";
|
||||||
import { useContext, useEffect, useRef, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
import TableComponent from "../../../../components/core/parameterRenderComponent/components/tableComponent";
|
import TableComponent from "../../../../components/core/parameterRenderComponent/components/tableComponent";
|
||||||
import { AuthContext } from "../../../../contexts/authContext";
|
import { AuthContext } from "../../../../contexts/authContext";
|
||||||
import useAlertStore from "../../../../stores/alertStore";
|
import useAlertStore from "../../../../stores/alertStore";
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
import { useGetRefreshFlows } from "@/controllers/API/queries/flows/use-get-refresh-flows";
|
|
||||||
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
|
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
|
||||||
import { useTypesStore } from "@/stores/typesStore";
|
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import { FieldParserType, FieldValidatorType } from "../api";
|
|
||||||
|
|
||||||
export type getCodesObjProps = {
|
export type getCodesObjProps = {
|
||||||
runCurlCode: string;
|
runCurlCode: string;
|
||||||
webhookCurlCode: string;
|
webhookCurlCode: string;
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { FlowType } from "@/types/flow";
|
|
||||||
import { FolderType } from "../../../pages/MainPage/entities";
|
import { FolderType } from "../../../pages/MainPage/entities";
|
||||||
|
|
||||||
export type FoldersStoreType = {
|
export type FoldersStoreType = {
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { ColDef, ColGroupDef } from "ag-grid-community";
|
|
||||||
import { Message } from "../../messages";
|
import { Message } from "../../messages";
|
||||||
|
|
||||||
export type MessagesStoreType = {
|
export type MessagesStoreType = {
|
||||||
|
|||||||
@ -316,6 +316,7 @@ import SvgWolfram from "../icons/Wolfram/Wolfram";
|
|||||||
import { HackerNewsIcon } from "../icons/hackerNews";
|
import { HackerNewsIcon } from "../icons/hackerNews";
|
||||||
import { MistralIcon } from "../icons/mistral";
|
import { MistralIcon } from "../icons/mistral";
|
||||||
import { SupabaseIcon } from "../icons/supabase";
|
import { SupabaseIcon } from "../icons/supabase";
|
||||||
|
import { XAIIcon } from "../icons/xAI";
|
||||||
import { iconsType } from "../types/components";
|
import { iconsType } from "../types/components";
|
||||||
export const BG_NOISE =
|
export const BG_NOISE =
|
||||||
"url(data:image/png;base64,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)";
|
"url(data:image/png;base64,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)";
|
||||||
@ -689,6 +690,7 @@ export const nodeIconsLucide: iconsType = {
|
|||||||
OpenAI: OpenAiIcon,
|
OpenAI: OpenAiIcon,
|
||||||
OpenRouter: OpenRouterIcon,
|
OpenRouter: OpenRouterIcon,
|
||||||
DeepSeek: DeepSeekIcon,
|
DeepSeek: DeepSeekIcon,
|
||||||
|
xAI: XAIIcon,
|
||||||
OpenAIEmbeddings: OpenAiIcon,
|
OpenAIEmbeddings: OpenAiIcon,
|
||||||
Pinecone: PineconeIcon,
|
Pinecone: PineconeIcon,
|
||||||
Qdrant: QDrantIcon,
|
Qdrant: QDrantIcon,
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { expect, Page, test } from "@playwright/test";
|
|||||||
import * as dotenv from "dotenv";
|
import * as dotenv from "dotenv";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
||||||
import { evaluateReactStateChanges } from "../../utils/evaluate-input-react-state-changes";
|
|
||||||
import { initialGPTsetup } from "../../utils/initialGPTsetup";
|
import { initialGPTsetup } from "../../utils/initialGPTsetup";
|
||||||
|
|
||||||
test(
|
test(
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
import { adjustScreenView } from "../../utils/adjust-screen-view";
|
|
||||||
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
||||||
|
|
||||||
test(
|
test(
|
||||||
|
|||||||
@ -25,6 +25,10 @@ test(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await page.waitForSelector('[data-testid="zoom_out"]', {
|
||||||
|
timeout: 3000,
|
||||||
|
});
|
||||||
|
|
||||||
await page.getByTestId("sidebar-custom-component-button").click();
|
await page.getByTestId("sidebar-custom-component-button").click();
|
||||||
|
|
||||||
await page.getByTestId("zoom_out").click();
|
await page.getByTestId("zoom_out").click();
|
||||||
@ -164,7 +168,7 @@ class CustomComponent(Component):
|
|||||||
numberOfCopiedRows = await page.getByText(thirdRandomText).count();
|
numberOfCopiedRows = await page.getByText(thirdRandomText).count();
|
||||||
expect(numberOfCopiedRows).toBe(0);
|
expect(numberOfCopiedRows).toBe(0);
|
||||||
|
|
||||||
await page.getByText("Close").last().click();
|
await page.getByText("Save").last().click();
|
||||||
|
|
||||||
await page.waitForSelector("text=Open table", {
|
await page.waitForSelector("text=Open table", {
|
||||||
timeout: 3000,
|
timeout: 3000,
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
import { readFileSync } from "fs";
|
|
||||||
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
||||||
import { simulateDragAndDrop } from "../../utils/simulate-drag-and-drop";
|
|
||||||
test(
|
test(
|
||||||
"user should be able to edit flow name by clicking on the header or on the main page",
|
"user should be able to edit flow name by clicking on the header or on the main page",
|
||||||
{ tag: ["@release"] },
|
{ tag: ["@release"] },
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { expect, Page, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
import { adjustScreenView } from "../../utils/adjust-screen-view";
|
import { adjustScreenView } from "../../utils/adjust-screen-view";
|
||||||
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { expect, test } from "@playwright/test";
|
import { test } from "@playwright/test";
|
||||||
import * as dotenv from "dotenv";
|
import * as dotenv from "dotenv";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { expect, Page, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
import * as dotenv from "dotenv";
|
import * as dotenv from "dotenv";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
||||||
|
|||||||
Reference in New Issue
Block a user