From 0ab5262b716ba2132e6c1ddf12d2d30f6ed1b141 Mon Sep 17 00:00:00 2001 From: Davor Runje Date: Thu, 2 May 2024 09:05:04 +0200 Subject: [PATCH 01/34] fixed wrong type of Bing API key in WebSurfer (#115) --- fastagency/models/agents/web_surfer.py | 2 +- tests/models/agents/test_web_surfer.py | 33 +++++++++++++++++++------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/fastagency/models/agents/web_surfer.py b/fastagency/models/agents/web_surfer.py index 29c24d92..67a70167 100644 --- a/fastagency/models/agents/web_surfer.py +++ b/fastagency/models/agents/web_surfer.py @@ -31,5 +31,5 @@ class WebSurferAgent(AgentBaseModel): int, Field(description="The viewport size of the browser") ] = 1080 bing_api_key: Annotated[ - Optional[BingAPIKey], Field(description="The Bing API key for the browser") + Optional[BingAPIKeyRef], Field(description="The Bing API key for the browser") ] = None diff --git a/tests/models/agents/test_web_surfer.py b/tests/models/agents/test_web_surfer.py index 0d710253..678042a0 100644 --- a/tests/models/agents/test_web_surfer.py +++ b/tests/models/agents/test_web_surfer.py @@ -57,16 +57,33 @@ def test_web_surfer_model_schema(self) -> None: "title": "AzureOAIRef", "type": "object", }, - "BingAPIKey": { + "BingAPIKeyRef": { "properties": { - "api_key": { - "description": "The API Key from OpenAI", - "title": "Api Key", + "type": { + "const": "secret", + "default": "secret", + "description": "The name of the type of the data", + "enum": ["secret"], + "title": "Type", + "type": "string", + }, + "name": { + "const": "BingAPIKey", + "default": "BingAPIKey", + "description": "The name of the data", + "enum": ["BingAPIKey"], + "title": "Name", "type": "string", - } + }, + "uuid": { + "description": "The unique identifier", + "format": "uuid", + "title": "UUID", + "type": "string", + }, }, - "required": ["api_key"], - "title": "BingAPIKey", + "required": ["uuid"], + "title": "BingAPIKeyRef", "type": "object", }, "OpenAIRef": { @@ -123,7 +140,7 @@ def test_web_surfer_model_schema(self) -> None: "type": "integer", }, "bing_api_key": { - "anyOf": [{"$ref": "#/$defs/BingAPIKey"}, {"type": "null"}], + "anyOf": [{"$ref": "#/$defs/BingAPIKeyRef"}, {"type": "null"}], "default": None, "description": "The Bing API key for the browser", }, From 321bcd17d2787186e3c938c5fa60ab37cb6982f5 Mon Sep 17 00:00:00 2001 From: Harish Mohan Raj Date: Fri, 3 May 2024 11:24:27 +0530 Subject: [PATCH 02/34] UI for defining agents and teams workflows (#117) * Copy frontend code from faststream-web-ui-repo * Update Footer copy * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * Cleanup * WIP * WIP * WIP * Complete llm flow * Cleanup docs * Validate dependencies before creating a property * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * Complete agent flow --- app/.gitignore | 1 + app/main.wasp | 77 +- app/package-lock.json | 50 +- app/package.json | 4 +- app/src/client/App.tsx | 26 +- app/src/client/admin/common/Loader/index.tsx | 4 +- app/src/client/app/AccountPage.tsx | 9 +- app/src/client/app/BuildPage.tsx | 190 +++ app/src/client/app/ChatPage.tsx | 16 - app/src/client/app/PlayGroundPage.tsx | 16 + .../app/layout/CustomAuthRequiredLayout.tsx | 25 + app/src/client/app/utils/formHelpers.ts | 19 + .../auth/createAuthRequiredChatPage.jsx | 43 - .../client/components/CustomBreadcrumb.tsx | 24 + app/src/client/components/CustomSidebar.tsx | 314 ++++ .../client/components/DynamicFormBuilder.tsx | 216 +++ app/src/client/components/ModelForm.tsx | 64 + .../client/components/ModelFormContainer.tsx | 41 + app/src/client/components/ModelItem.tsx | 98 ++ app/src/client/components/ModelsList.tsx | 32 + app/src/client/components/NotificationBox.tsx | 4 +- .../components/ServerNotRechableComponent.tsx | 5 +- .../components/TosAndMarketingEmailsModal.tsx | 4 +- .../client/components/UserActionButton.tsx | 2 +- .../client/components/buildPage/Agents.tsx | 21 + app/src/client/components/buildPage/LLMs.tsx | 21 + .../client/components/buildPage/Secrets.tsx | 21 + app/src/client/components/buildPage/Teams.tsx | 22 + .../client/components/buildPage/ToolBoxes.tsx | 22 + .../buildPage/UserPropertyHandler.tsx | 121 ++ .../client/components/form/SelectInput.tsx | 18 + app/src/client/components/form/TextArea.tsx | 18 + app/src/client/components/form/TextInput.tsx | 19 + app/src/client/hooks/useBuildPage.ts | 31 + app/src/client/hooks/useForm.ts | 52 + .../client/interfaces/BuildPageInterfaces.ts | 64 + .../client/landing-page/contentSections.ts | 3 +- app/src/client/services/commonService.ts | 10 + app/src/client/services/modelService.ts | 12 + .../tests/AnimatedCharacterLoader.test.tsx | 14 + app/src/client/tests/Button.test.tsx | 21 + .../client/tests/DynamicFormBuilder.test.tsx | 179 +++ app/src/client/tests/FreeTrialButton.test.tsx | 44 + .../client/tests/LoadingComponent.test.tsx | 12 + .../MarketingEmailPreferenceSwitcher.test.tsx | 65 + app/src/client/tests/NotificationBox.test.tsx | 33 + .../tests/ServerNotRechableComponent.test.tsx | 14 + .../tests/TosAndMarketingEmails.test.tsx | 68 + .../tests/TosAndMarketingEmailsModal.test.tsx | 74 + app/src/client/tests/buildPageUtils.test.ts | 1389 +++++++++++++++++ app/src/client/tests/formHelper.test.ts | 56 + app/src/client/tests/schemaParser.test.ts | 150 ++ app/src/client/utils/buildPageUtils.ts | 196 +++ app/src/client/utils/constants.ts | 21 + app/src/client/utils/schemaParser.ts | 25 + app/src/server/actions.ts | 179 ++- app/src/server/common/constants.ts | 1 + app/src/server/queries.ts | 90 +- app/tailwind.config.cjs | 1 + docs/docs/SUMMARY.md | 1 - .../docs/en/api/fastagency/app/ModelUpdate.md | 11 - fastagency/app.py | 82 +- fastagency/models/llms/azure.py | 2 +- scripts/run_server.sh | 2 +- 64 files changed, 4299 insertions(+), 170 deletions(-) create mode 100644 app/src/client/app/BuildPage.tsx delete mode 100644 app/src/client/app/ChatPage.tsx create mode 100644 app/src/client/app/PlayGroundPage.tsx create mode 100644 app/src/client/app/layout/CustomAuthRequiredLayout.tsx create mode 100644 app/src/client/app/utils/formHelpers.ts delete mode 100644 app/src/client/auth/createAuthRequiredChatPage.jsx create mode 100644 app/src/client/components/CustomBreadcrumb.tsx create mode 100644 app/src/client/components/CustomSidebar.tsx create mode 100644 app/src/client/components/DynamicFormBuilder.tsx create mode 100644 app/src/client/components/ModelForm.tsx create mode 100644 app/src/client/components/ModelFormContainer.tsx create mode 100644 app/src/client/components/ModelItem.tsx create mode 100644 app/src/client/components/ModelsList.tsx create mode 100644 app/src/client/components/buildPage/Agents.tsx create mode 100644 app/src/client/components/buildPage/LLMs.tsx create mode 100644 app/src/client/components/buildPage/Secrets.tsx create mode 100644 app/src/client/components/buildPage/Teams.tsx create mode 100644 app/src/client/components/buildPage/ToolBoxes.tsx create mode 100644 app/src/client/components/buildPage/UserPropertyHandler.tsx create mode 100644 app/src/client/components/form/SelectInput.tsx create mode 100644 app/src/client/components/form/TextArea.tsx create mode 100644 app/src/client/components/form/TextInput.tsx create mode 100644 app/src/client/hooks/useBuildPage.ts create mode 100644 app/src/client/hooks/useForm.ts create mode 100644 app/src/client/interfaces/BuildPageInterfaces.ts create mode 100644 app/src/client/services/commonService.ts create mode 100644 app/src/client/services/modelService.ts create mode 100644 app/src/client/tests/AnimatedCharacterLoader.test.tsx create mode 100644 app/src/client/tests/Button.test.tsx create mode 100644 app/src/client/tests/DynamicFormBuilder.test.tsx create mode 100644 app/src/client/tests/FreeTrialButton.test.tsx create mode 100644 app/src/client/tests/LoadingComponent.test.tsx create mode 100644 app/src/client/tests/MarketingEmailPreferenceSwitcher.test.tsx create mode 100644 app/src/client/tests/NotificationBox.test.tsx create mode 100644 app/src/client/tests/ServerNotRechableComponent.test.tsx create mode 100644 app/src/client/tests/TosAndMarketingEmails.test.tsx create mode 100644 app/src/client/tests/TosAndMarketingEmailsModal.test.tsx create mode 100644 app/src/client/tests/buildPageUtils.test.ts create mode 100644 app/src/client/tests/formHelper.test.ts create mode 100644 app/src/client/tests/schemaParser.test.ts create mode 100644 app/src/client/utils/buildPageUtils.ts create mode 100644 app/src/client/utils/constants.ts create mode 100644 app/src/client/utils/schemaParser.ts create mode 100644 app/src/server/common/constants.ts delete mode 100644 docs/docs/en/api/fastagency/app/ModelUpdate.md diff --git a/app/.gitignore b/app/.gitignore index 9014e1ba..a1da9f48 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -1,4 +1,5 @@ .wasp/ +coverage/ node_modules/ **/.DS_Store diff --git a/app/main.wasp b/app/main.wasp index c9a85e31..0a568e00 100644 --- a/app/main.wasp +++ b/app/main.wasp @@ -1,4 +1,4 @@ -app OpenSaaS { +app FastAgency { wasp: { version: "^0.13.0" }, @@ -33,7 +33,7 @@ app OpenSaaS { }, }, onAuthFailedRedirectTo: "/login", - onAuthSucceededRedirectTo: "/chat", + onAuthSucceededRedirectTo: "/build", }, db: { system: PostgreSQL, @@ -121,9 +121,39 @@ page LandingPage { component: import LandingPage from "@src/client/landing-page/LandingPage" } -route ChatPageRoute { path: "/chat/:id?", to: ChatPage } -page ChatPage { - component: import createAuthRequiredChatPage from "@src/client/app/ChatPage" +route PlayGroundPageRoute { path: "/playground/:id?", to: PlayGroundPage } +page PlayGroundPage { + component: import PlayGroundPageWithCustomAuth from "@src/client/app/PlayGroundPage" +} + +route BuildRoute { path: "/build", to: BuildPage } +page BuildPage { + component: import BuildPageWithCustomAuth from "@src/client/app/BuildPage" +} + +route SecretsRoute { path: "/build/secrets", to: SecretsPage } +page SecretsPage { + component: import BuildPageWithCustomAuth from "@src/client/app/BuildPage" +} + +route ToolBoxRoute { path: "/build/toolboxes", to: ToolBoxPage } +page ToolBoxPage { + component: import BuildPageWithCustomAuth from "@src/client/app/BuildPage" +} + +route LLMsRoute { path: "/build/llms", to: LLMsPage } +page LLMsPage { + component: import BuildPageWithCustomAuth from "@src/client/app/BuildPage" +} + +route TeamsRoute { path: "/build/teams", to: TeamsPage } +page TeamsPage { + component: import BuildPageWithCustomAuth from "@src/client/app/BuildPage" +} + +route AgentsRoute { path: "/build/agents", to: AgentsPage } +page AgentsPage { + component: import BuildPageWithCustomAuth from "@src/client/app/BuildPage" } route LoginRoute { path: "/login", to: LoginPage } @@ -143,8 +173,7 @@ page PricingPage { route AccountRoute { path: "/account", to: AccountPage } page AccountPage { - authRequired: true, - component: import Account from "@src/client/app/AccountPage" + component: import AccountPageWithCustomAuth from "@src/client/app/AccountPage" } route CheckoutRoute { path: "/checkout", to: CheckoutPage } @@ -247,6 +276,30 @@ action updateUserById { entities: [User] } +action getAvailableModels { + fn: import { getAvailableModels } from "@src/server/actions.js", + entities: [] +} + +action updateUserModels { + fn: import { updateUserModels } from "@src/server/actions.js", + entities: [] +} + +action addUserModels { + fn: import { addUserModels } from "@src/server/actions.js", + entities: [] +} + +action deleteUserModels { + fn: import { deleteUserModels } from "@src/server/actions.js", + entities: [] +} + +action validateForm { + fn: import { validateForm } from "@src/server/actions.js", + entities: [] +} // 📚 Queries @@ -260,6 +313,16 @@ query getPaginatedUsers { entities: [User] } +query getModels { + fn: import { getModels } from "@src/server/queries.js", + entities: [User] +} + +query propertyDependencies { + fn: import { propertyDependencies } from "@src/server/queries.js", + entities: [User] +} + /* * 📡 These are custom Wasp API Endpoints. Use them for callbacks, webhooks, etc. * https://wasp-lang.dev/docs/advanced/apis diff --git a/app/package-lock.json b/app/package-lock.json index 1b35a4c1..d586d4cd 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -16,6 +16,7 @@ "apexcharts": "^3.41.0", "clsx": "^2.1.0", "headlessui": "^0.0.0", + "lodash": "^4.17.21", "node-fetch": "3.3.0", "openai": "^4.24.1", "prettier": "3.1.1", @@ -26,6 +27,7 @@ "react-icons": "4.11.0", "stripe": "11.15.0", "tailwind-merge": "^2.2.1", + "uuid": "^9.0.1", "wasp": "file:.wasp/out/sdk/wasp", "zod": "3.22.4" }, @@ -563,18 +565,6 @@ "auri": "1.0.2" } }, - ".wasp/out/sdk/wasp/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@actions/core": { "version": "1.10.1", "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.1.tgz", @@ -6756,18 +6746,6 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, - "node_modules/google-gax/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/google-gax/node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -9953,18 +9931,6 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, - "node_modules/teeny-request/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/teeny-request/node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -10267,6 +10233,18 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/value-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", diff --git a/app/package.json b/app/package.json index 21554f77..37cee7b4 100644 --- a/app/package.json +++ b/app/package.json @@ -22,7 +22,9 @@ "stripe": "11.15.0", "tailwind-merge": "^2.2.1", "wasp": "file:.wasp/out/sdk/wasp", - "zod": "3.22.4" + "zod": "3.22.4", + "uuid": "^9.0.1", + "lodash":"^4.17.21" }, "devDependencies": { "@types/express": "^4.17.13", diff --git a/app/src/client/App.tsx b/app/src/client/App.tsx index 4c8785cd..1678ea48 100644 --- a/app/src/client/App.tsx +++ b/app/src/client/App.tsx @@ -49,13 +49,16 @@ export default function App({ children }: { children: ReactNode }) { return location.pathname.startsWith('/account'); }, [location]); - const isChatPage = useMemo(() => { - return location.pathname.startsWith('/chat'); + const isPlayGroundPage = useMemo(() => { + return location.pathname.startsWith('/playground'); + }, [location]); + + const isBuildPage = useMemo(() => { + return location.pathname.startsWith('/build'); }, [location]); useEffect(() => { if (user) { - console.log('user', user); if (!user.isSignUpComplete) { if (user.hasAcceptedTos) { updateCurrentUser({ @@ -101,7 +104,7 @@ export default function App({ children }: { children: ReactNode }) { <>
{isError && (addServerErrorClass(), ())} - {isAdminDashboard || isChatPage ? ( + {isAdminDashboard || isPlayGroundPage || isBuildPage ? ( <> {showTosAndMarketingEmailsModal ? ( <> @@ -109,15 +112,24 @@ export default function App({ children }: { children: ReactNode }) { ) : ( <> - {isAdminDashboard ? ( + {shouldDisplayAppNavBar && } + {children} + {/* {isAdminDashboard ? ( children ) : (
{shouldDisplayAppNavBar && } {children} - +
+
+
+

+ © 2024 airt. All rights reserved. +

+
+
- )} + )} */} )} diff --git a/app/src/client/admin/common/Loader/index.tsx b/app/src/client/admin/common/Loader/index.tsx index 0915c828..4221fda7 100644 --- a/app/src/client/admin/common/Loader/index.tsx +++ b/app/src/client/admin/common/Loader/index.tsx @@ -1,7 +1,7 @@ const Loader = () => { return ( -
-
+
+
); }; diff --git a/app/src/client/app/AccountPage.tsx b/app/src/client/app/AccountPage.tsx index 7f238880..40018361 100644 --- a/app/src/client/app/AccountPage.tsx +++ b/app/src/client/app/AccountPage.tsx @@ -2,12 +2,12 @@ import { Link } from 'wasp/client/router'; import { type User } from 'wasp/entities'; import { logout } from 'wasp/client/auth'; import { STRIPE_CUSTOMER_PORTAL_LINK } from '../../shared/constants'; -import { TierIds } from '../../shared/constants'; +import CustomAuthRequiredLayout from '../app/layout/CustomAuthRequiredLayout'; import Button from '../components/Button'; import FreeTrialButton from '../components/FreeTrialButton'; import { MarketingEmailPreferenceSwitcher } from '../components/MarketingEmailPreferenceSwitcher'; -export default function AccountPage({ user }: { user: User }) { +const AccountPage = ({ user }: { user: User }) => { return (
@@ -79,7 +79,7 @@ export default function AccountPage({ user }: { user: User }) {
); -} +}; function BuyMoreButton() { return ( @@ -107,3 +107,6 @@ function CustomerPortalButton() {
); } + +const AccountPageWithCustomAuth = CustomAuthRequiredLayout(AccountPage); +export default AccountPageWithCustomAuth; diff --git a/app/src/client/app/BuildPage.tsx b/app/src/client/app/BuildPage.tsx new file mode 100644 index 00000000..4426edd3 --- /dev/null +++ b/app/src/client/app/BuildPage.tsx @@ -0,0 +1,190 @@ +import React, { useEffect, useState } from 'react'; +import { useLocation } from 'react-router-dom'; + +import { type User } from 'wasp/entities'; + +import CustomAuthRequiredLayout from './layout/CustomAuthRequiredLayout'; +import CustomSidebar from '../components/CustomSidebar'; +import { cn } from '../../shared/utils'; + +import Secrets from '../components/buildPage/Secrets'; +import LLMs from '../components/buildPage/LLMs'; +import Agents from '../components/buildPage/Agents'; +import Teams from '../components/buildPage/Teams'; +import ToolBoxes from '../components/buildPage/ToolBoxes'; +import LoadingComponent from '../components/LoadingComponent'; +import { useBuildPage } from '../hooks/useBuildPage'; +import { filerOutComponentData } from '../utils/buildPageUtils'; + +interface BuildPageProps { + user: User; +} + +interface componentsMapType { + [key: string]: React.FC; +} + +const componentsMap: componentsMapType = { + secret: Secrets, + llm: LLMs, + agent: Agents, + team: Teams, + toolbox: ToolBoxes, +}; + +interface HeaderProps { + sidebarOpen: boolean; + setSidebarOpen: (open: boolean) => void; +} + +const Header: React.FC = ({ sidebarOpen, setSidebarOpen }) => { + return ( +
+
+
+ {/* */} + + + + {/* */} +
+
+
+ ); +}; + +const BuildPage = ({ user }: BuildPageProps) => { + const { data, loading, error } = useBuildPage(); + const [sidebarOpen, setSidebarOpen] = useState(false); + const [sideNavSelectedItem, setSideNavSelectedItem] = useState('secret'); + + const wrapperClass = document.body.classList.contains('server-error') + ? 'h-[calc(100vh-165px)]' + : 'h-[calc(100vh-80px)]'; + + useEffect(() => { + const selectedTab = sessionStorage.getItem('selectedBuildPageTab'); + if (selectedTab) { + setSideNavSelectedItem(selectedTab); + } + }, []); + + if (loading) { + return ; + } + + const handleSideNavItemClick = (selectedComponentName: string) => { + setSideNavSelectedItem(selectedComponentName); + sessionStorage.setItem('selectedBuildPageTab', selectedComponentName); + }; + + const ComponentToRender = data && componentsMap[sideNavSelectedItem]; + + return ( +
+ {/* */} +
+ {/* */} + + {/* */} + + {/* */} +
+ {/* */} +
+ {/* */} + + {/* */} +
+
+ {error ? ( +

+ Oops! Something went wrong. Our server is currently unavailable. Please try again later. +

+ ) : ComponentToRender ? ( + + ) : ( +

+ Build Page - No valid component found for this section. +

+ )} +
+
+ {/* */} + <> +
+ + {/* */} +
+ {/* */} +
+ ); +}; + +const BuildPageWithCustomAuth = CustomAuthRequiredLayout(BuildPage); +export default BuildPageWithCustomAuth; diff --git a/app/src/client/app/ChatPage.tsx b/app/src/client/app/ChatPage.tsx deleted file mode 100644 index 09fccea5..00000000 --- a/app/src/client/app/ChatPage.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { type User } from 'wasp/entities'; - -import createAuthRequiredChatPage from '../auth/createAuthRequiredChatPage'; - -const ChatPage = ({ user }: { user: User }) => { - return ( -

- Coming soon... -

- ); -}; - -export default createAuthRequiredChatPage(ChatPage); diff --git a/app/src/client/app/PlayGroundPage.tsx b/app/src/client/app/PlayGroundPage.tsx new file mode 100644 index 00000000..c7727bb8 --- /dev/null +++ b/app/src/client/app/PlayGroundPage.tsx @@ -0,0 +1,16 @@ +import { type User } from 'wasp/entities'; +import CustomAuthRequiredLayout from './layout/CustomAuthRequiredLayout'; + +const PlayGroundPage = ({ user }: { user: User }) => { + return ( +

+ Playground... +

+ ); +}; + +const PlayGroundPageWithCustomAuth = CustomAuthRequiredLayout(PlayGroundPage); +export default PlayGroundPageWithCustomAuth; diff --git a/app/src/client/app/layout/CustomAuthRequiredLayout.tsx b/app/src/client/app/layout/CustomAuthRequiredLayout.tsx new file mode 100644 index 00000000..9028a487 --- /dev/null +++ b/app/src/client/app/layout/CustomAuthRequiredLayout.tsx @@ -0,0 +1,25 @@ +import { useAuth } from 'wasp/client/auth'; +import { Redirect } from 'react-router-dom'; + +import LoadingComponent from '../../components/LoadingComponent'; + +const CustomAuthRequiredLayout = (Page: any) => { + return (props: any) => { + const { data: user, isError, isSuccess, isLoading } = useAuth(); + if (isSuccess) { + if (user) { + return ; + } else { + return ; + } + } else if (isLoading) { + return ; + } else if (isError) { + return ; + } else { + return ; + } + }; +}; + +export default CustomAuthRequiredLayout; diff --git a/app/src/client/app/utils/formHelpers.ts b/app/src/client/app/utils/formHelpers.ts new file mode 100644 index 00000000..65752a96 --- /dev/null +++ b/app/src/client/app/utils/formHelpers.ts @@ -0,0 +1,19 @@ +type ValidationError = { + type: string; + loc: string[]; + msg: string; + input: any; + url: string; + ctx?: any; +}; + +type ErrorOutput = { [key: string]: string }; + +export function parseValidationErrors(errors: ValidationError[]): ErrorOutput { + const result: ErrorOutput = {}; + errors.forEach((error) => { + const key = error.loc[error.loc.length - 1]; // Using the last item in 'loc' array as the key + result[key] = error.msg; + }); + return result; +} diff --git a/app/src/client/auth/createAuthRequiredChatPage.jsx b/app/src/client/auth/createAuthRequiredChatPage.jsx deleted file mode 100644 index f0524824..00000000 --- a/app/src/client/auth/createAuthRequiredChatPage.jsx +++ /dev/null @@ -1,43 +0,0 @@ -import { useAuth } from 'wasp/client/auth'; - -import React from 'react'; -import { Redirect } from 'react-router-dom'; - -import LoadingComponent from '../components/LoadingComponent'; - -const createAuthRequiredChatPage = (Page) => { - return (props) => { - const { data: user, isError, isSuccess, isLoading } = useAuth(); - if (isSuccess) { - if (user) { - const redirectUrl = localStorage.getItem('fastagency:redirectUrl'); - if (redirectUrl) { - localStorage.removeItem('fastagency:redirectUrl'); - window.location.href = redirectUrl; - // return ; - } else { - return ; - } - } else { - localStorage.setItem('fastagency:redirectUrl', window.location.pathname + window.location.search); - return ; - } - } else if (isLoading) { - return ; - } else if (isError) { - return ( - <> - - - ); - } else { - return ( - <> - - - ); - } - }; -}; - -export default createAuthRequiredChatPage; diff --git a/app/src/client/components/CustomBreadcrumb.tsx b/app/src/client/components/CustomBreadcrumb.tsx new file mode 100644 index 00000000..b644cb60 --- /dev/null +++ b/app/src/client/components/CustomBreadcrumb.tsx @@ -0,0 +1,24 @@ +import { Link } from 'react-router-dom'; +interface BreadcrumbProps { + pageName: string; +} +const CustomBreadcrumb = ({ pageName }: BreadcrumbProps) => { + return ( +
+

{pageName}

+ + {/* */} +
+ ); +}; + +export default CustomBreadcrumb; diff --git a/app/src/client/components/CustomSidebar.tsx b/app/src/client/components/CustomSidebar.tsx new file mode 100644 index 00000000..45215446 --- /dev/null +++ b/app/src/client/components/CustomSidebar.tsx @@ -0,0 +1,314 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { NavLink, useLocation } from 'react-router-dom'; +import Logo from '../static/logo.svg'; +import { cn } from '../../shared/utils'; + +interface NavLinkItem { + label: string; + svgIcon: React.ReactNode; + componentName: string; +} + +export const navLinkItems: NavLinkItem[] = [ + { + label: 'Secrets', + svgIcon: ( + + key + + + ), + componentName: 'secret', + }, + { + label: 'LLMs', + svgIcon: ( + + + + ), + componentName: 'llm', + }, + { + label: 'Agents', + svgIcon: ( + + + + ), + componentName: 'agent', + }, + { + label: 'Teams', + svgIcon: ( + + + + + + + + + ), + componentName: 'team', + }, + { + label: 'ToolBoxes', + svgIcon: ( + + + + + + + + + + + + ), + componentName: 'toolbox', + }, +]; + +interface SidebarProps { + sidebarOpen: boolean; + setSidebarOpen: (arg: boolean) => void; + onSideNavItemClick: (selectedItem: string) => void; + sideNavSelectedItem: string; +} + +const CustomSidebar = ({ sidebarOpen, setSidebarOpen, onSideNavItemClick, sideNavSelectedItem }: SidebarProps) => { + const location = useLocation(); + const { pathname } = location; + + const trigger = useRef(null); + const sidebar = useRef(null); + + const storedSidebarExpanded = localStorage.getItem('sidebar-expanded'); + const [sidebarExpanded, setSidebarExpanded] = useState( + storedSidebarExpanded === null ? false : storedSidebarExpanded === 'true' + ); + + // close on click outside + useEffect(() => { + const clickHandler = ({ target }: MouseEvent) => { + if (!sidebar.current || !trigger.current) return; + if (!sidebarOpen || sidebar.current.contains(target) || trigger.current.contains(target)) return; + setSidebarOpen(false); + }; + document.addEventListener('click', clickHandler); + return () => document.removeEventListener('click', clickHandler); + }); + + // close if the esc key is pressed + useEffect(() => { + const keyHandler = ({ keyCode }: KeyboardEvent) => { + if (!sidebarOpen || keyCode !== 27) return; + setSidebarOpen(false); + }; + document.addEventListener('keydown', keyHandler); + return () => document.removeEventListener('keydown', keyHandler); + }); + + useEffect(() => { + localStorage.setItem('sidebar-expanded', sidebarExpanded.toString()); + if (sidebarExpanded) { + document.querySelector('body')?.classList.add('sidebar-expanded'); + } else { + document.querySelector('body')?.classList.remove('sidebar-expanded'); + } + }, [sidebarExpanded]); + + const handleClick = (selectedItemLable: string) => { + onSideNavItemClick(selectedItemLable); + }; + + return ( + + ); +}; + +export default CustomSidebar; diff --git a/app/src/client/components/DynamicFormBuilder.tsx b/app/src/client/components/DynamicFormBuilder.tsx new file mode 100644 index 00000000..440a2017 --- /dev/null +++ b/app/src/client/components/DynamicFormBuilder.tsx @@ -0,0 +1,216 @@ +import React, { useState, useEffect } from 'react'; +import _ from 'lodash'; + +import { useForm } from '../hooks/useForm'; +import { JsonSchema } from '../interfaces/BuildPageInterfaces'; +import { TextInput } from './form/TextInput'; +import { SelectInput } from './form/SelectInput'; +import { TextArea } from './form/TextArea'; +import { validateForm } from '../services/commonService'; +import { parseValidationErrors } from '../app/utils/formHelpers'; +import Loader from '../admin/common/Loader'; +import NotificationBox from './NotificationBox'; + +import { SelectedModelSchema } from '../interfaces/BuildPageInterfaces'; +import { + // getPropertyReferenceValues, + getFormSubmitValues, + getRefValues, + getMatchedUserProperties, + constructHTMLSchema, + getAllRefs, + checkForDependency, +} from '../utils/buildPageUtils'; +import { set } from 'zod'; + +interface DynamicFormBuilderProps { + allUserProperties: any; + property_type: string; + jsonSchema: JsonSchema; + validationURL: string; + updateExistingModel: SelectedModelSchema | null; + onSuccessCallback: (data: any) => void; + onCancelCallback: (data: any) => void; + onDeleteCallback: (data: any) => void; +} + +const DynamicFormBuilder: React.FC = ({ + allUserProperties, + property_type, + jsonSchema, + validationURL, + updateExistingModel, + onSuccessCallback, + onCancelCallback, + onDeleteCallback, +}) => { + const { formData, handleChange, formErrors, setFormErrors } = useForm({ + jsonSchema, + defaultValues: updateExistingModel, + }); + const [isLoading, setIsLoading] = useState(false); + const [showNotification, setShowNotification] = useState(false); + const [refValues, setRefValues] = useState>({}); + const [missingDependency, setMissingDependency] = useState([]); + + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + setIsLoading(true); + const formDataToSubmit = getFormSubmitValues(refValues, formData); + try { + const response = await validateForm(formDataToSubmit, validationURL); + onSuccessCallback(response); + } catch (error: any) { + try { + const errorMsgObj = JSON.parse(error.message); + const errors = parseValidationErrors(errorMsgObj); + setFormErrors(errors); + } catch (e) { + setShowNotification(true); + } + } finally { + setIsLoading(false); + } + }; + + const notificationMsg = 'Oops. Something went wrong. Please try again later.'; + const missingDependencyNotificationMsg = `Please create atleast one item of type "${missingDependency.join( + ', ' + )}" to proceed.`; + + const notificationOnClick = () => { + setShowNotification(false); + }; + useEffect(() => { + async function fetchPropertyReferenceValues() { + if (jsonSchema) { + setIsLoading(true); + for (const [key, property] of Object.entries(jsonSchema.properties)) { + const propertyHasRef = _.has(property, '$ref') && property['$ref']; + const propertyHasAnyOf = _.has(property, 'anyOf') && _.has(jsonSchema, '$defs'); + if (propertyHasRef || propertyHasAnyOf) { + const allRefList = propertyHasRef ? [property['$ref']] : getAllRefs(property); + const userPropertyData = getMatchedUserProperties(allUserProperties, allRefList); + const missingDependencyList = checkForDependency(userPropertyData, allRefList); + const htmlSchema = constructHTMLSchema(userPropertyData, key, property); + if (missingDependencyList.length > 0) { + setMissingDependency((prev) => prev.concat(missingDependencyList)); + } + setRefValues((prev) => ({ + ...prev, + [key]: { htmlSchema: htmlSchema, userPropertyData: userPropertyData }, + })); + } + } + setIsLoading(false); + } + } + + fetchPropertyReferenceValues(); + }, [jsonSchema]); + + useEffect(() => { + if (missingDependency) { + if (missingDependency.length > 0) { + setShowNotification(true); + } + } + }, [missingDependency?.length]); + + return ( + <> + {/*
*/} + + {Object.entries(jsonSchema.properties).map(([key, property]) => { + if (key === 'uuid') { + return null; + } + const inputValue = formData[key] || ''; + + let formElementsObject = property; + if (_.has(property, '$ref') || _.has(property, 'anyOf')) { + if (refValues[key]) { + formElementsObject = refValues[key].htmlSchema; + } + } + + // return formElementsObject?.enum?.length === 1 ? null : ( + return ( +
+ + {formElementsObject.enum ? ( + handleChange(key, value)} + /> + ) : key === 'system_message' ? ( +