├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── eslint.yml │ └── test-webapp.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── agent-backend ├── .env.example ├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── __init__.py ├── kubernetes │ └── prod │ │ ├── cloudbuild.yaml │ │ ├── deployment.yaml │ │ ├── ingress.yaml │ │ └── ssl.yaml ├── poetry.lock ├── pyproject.toml └── src │ ├── __init__.py │ ├── chat │ ├── __init__.py │ ├── agents │ │ ├── __init__.py │ │ ├── anthropic.py │ │ ├── base.py │ │ ├── base_anthropic_vertex.py │ │ ├── factory.py │ │ ├── ollama.py │ │ ├── open_ai.py │ │ └── vertex.py │ ├── chat_assistant.py │ ├── docs │ │ ├── chat_workflow.md │ │ └── graph_visualization.png │ ├── human_input_tool.py │ └── mongo_db_saver.py │ ├── crew │ ├── __init__.py │ ├── build_crew.py │ ├── build_task_helpers.py │ ├── exceptions.py │ └── get_crew_components.py │ ├── init │ ├── __init__.py │ ├── env_variables.py │ ├── mongo_session.py │ └── redis_session.py │ ├── lang_models │ ├── __init__.py │ ├── cohere.py │ └── factory.py │ ├── main.py │ ├── messaging │ ├── __init__.py │ ├── client.py │ └── send_message_to_socket.py │ ├── models │ ├── __init__.py │ ├── mongo.py │ ├── sockets.py │ └── vectordatabase.py │ ├── mongo │ ├── __init__.py │ ├── client.py │ └── queries.py │ ├── qdrant │ ├── __init__.py │ └── qdrant_connection.py │ ├── redisClient │ ├── __init__.py │ ├── redis_connection.py │ └── utilities.py │ ├── storage │ ├── __init__.py │ ├── google.py │ ├── local.py │ └── provider.py │ ├── test │ ├── init │ │ ├── test_OAI_CONFIG_LIST.py │ │ ├── test_env_variables.py │ │ └── test_mongo_session.py │ ├── messaging │ │ └── test_send_message_to_socket.py │ └── qdrantClient │ │ └── test_qdrant_connection.py │ ├── tools │ ├── __init__.py │ ├── build_request_from_api_spec.py │ ├── builtin_tools.py │ ├── builtins │ │ ├── __init__.py │ │ ├── apify_google_search.py │ │ ├── arxiv.py │ │ ├── base.py │ │ ├── duckduckgo.py │ │ ├── firecrawl_loader.py │ │ ├── serper.py │ │ ├── stackexchange.py │ │ ├── wikidata.py │ │ ├── wikipedia.py │ │ └── youtube.py │ ├── code_execution_docker_notebook_tool.py │ ├── code_execution_tool.py │ ├── global_tools.py │ ├── google_cloud_function.py │ ├── rag_tool.py │ └── retrievers │ │ ├── __init__.py │ │ ├── base.py │ │ ├── callback_handler.py │ │ ├── custom │ │ ├── __init__.py │ │ └── time_weighted_retriever.py │ │ ├── default.py │ │ ├── factory.py │ │ ├── filters.py │ │ ├── multi_query.py │ │ ├── self_query.py │ │ ├── similarity_search.py │ │ └── time_weighted.py │ ├── utils │ ├── __init__.py │ ├── class_checker.py │ ├── dict_utils.py │ ├── json_schema_to_pydantic.py │ ├── log_exception_context_manager.py │ └── model_helper.py │ └── vectorstores │ ├── __init__.py │ └── factory.py ├── docker-compose.minimal.yml ├── docker-compose.yml ├── install.sh ├── package-lock.json ├── setup.ts ├── values.yaml ├── vector-db-proxy ├── .cargo │ └── config.toml ├── .dockerignore ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── Makefile ├── README.md └── src │ ├── adaptors │ ├── gcp │ │ ├── gcs.rs │ │ ├── mod.rs │ │ ├── models.rs │ │ └── pubsub.rs │ ├── mod.rs │ ├── mongo │ │ ├── client.rs │ │ ├── error.rs │ │ ├── mod.rs │ │ ├── models.rs │ │ └── queries.rs │ ├── pinecone │ │ ├── apis.rs │ │ ├── client.rs │ │ ├── helpers.rs │ │ └── mod.rs │ ├── qdrant │ │ ├── apis.rs │ │ ├── client.rs │ │ ├── helpers.rs │ │ └── mod.rs │ └── rabbitmq │ │ ├── client.rs │ │ ├── mod.rs │ │ └── models.rs │ ├── data │ ├── helpers.rs │ ├── mod.rs │ ├── models.rs │ ├── processing_incoming_messages.rs │ └── unstructuredio │ │ ├── apis.rs │ │ ├── mod.rs │ │ └── models.rs │ ├── dummy.rs │ ├── embeddings │ ├── helpers.rs │ ├── mod.rs │ ├── models.rs │ └── utils.rs │ ├── init │ ├── env_variables.rs │ ├── mod.rs │ └── models.rs │ ├── main.rs │ ├── messages │ ├── mod.rs │ ├── models.rs │ ├── task_handoff.rs │ └── tasks.rs │ ├── routes │ ├── apis.rs │ ├── helpers.rs │ ├── mod.rs │ └── models.rs │ ├── utils │ ├── conversions.rs │ ├── file_operations.rs │ ├── macros.rs │ ├── maths.rs │ ├── mod.rs │ ├── models.rs │ └── webhook.rs │ └── vector_databases │ ├── error.rs │ ├── helpers.rs │ ├── mod.rs │ ├── models.rs │ ├── utils.rs │ └── vector_database.rs └── webapp ├── .dockerignore ├── .env.example ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .yarn └── install-state.gz ├── Dockerfile ├── Dockerfile_syncserver ├── LICENSE ├── Makefile ├── README.md ├── components.json ├── ecosystem.config.js ├── favicon.ico ├── jest.config.js ├── keyfile.json ├── next-env.d.ts ├── next.config.js ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── postcss.config.cjs ├── public ├── agentcloud-full-black-bg-trans.png ├── agentcloud-full-black-bg-white.png ├── agentcloud-full-white-bg-black.png ├── agentcloud-full-white-bg-trans.png ├── agentcloud-mark-black-bg-trans.png ├── agentcloud-mark-black-bg-white.png ├── agentcloud-mark-white-bg-black-Enterprise.png ├── agentcloud-mark-white-bg-black-Free.png ├── agentcloud-mark-white-bg-black-Pro.png ├── agentcloud-mark-white-bg-black-Teams.png ├── agentcloud-mark-white-bg-black.png ├── agentcloud-mark-white-bg-trans.png ├── apps │ └── identicon.png ├── favicon.ico ├── images │ ├── agent-cloud-introduction-RAG-google-gigquery-youtube.png │ ├── agentcloud-full-black-bg-trans.png │ ├── agentcloud-full-black-bg-white.png │ ├── agentcloud-full-white-bg-black.png │ ├── agentcloud-full-white-bg-trans.png │ ├── agentcloud-mark-black-bg-trans.png │ ├── agentcloud-mark-black-bg-white.png │ ├── agentcloud-mark-white-bg-black-Enterprise.png │ ├── agentcloud-mark-white-bg-black-Free.png │ ├── agentcloud-mark-white-bg-black-Pro.png │ ├── agentcloud-mark-white-bg-black-Teams.png │ ├── agentcloud-mark-white-bg-black.png │ ├── agentcloud-mark-white-bg-trans.png │ ├── email │ │ └── logo-light.png │ ├── favicon.ico │ ├── get-started │ │ ├── DemoProcessApp.png │ │ ├── chat-app-square.png │ │ ├── create-chat-app-dark.jpg │ │ ├── create-chat-app-dark.svg │ │ ├── create-chat-app.png │ │ ├── create-chat-app.svg │ │ ├── create-process-app-dark.jpg │ │ ├── create-process-app-dark.svg │ │ ├── create-process-app.png │ │ ├── create-process-app.svg │ │ └── process-app-square.png │ ├── onboarding │ │ ├── anthropic.svg │ │ ├── azure-openai.svg │ │ ├── brain-outline.svg │ │ ├── cohere.svg │ │ ├── fastembed.svg │ │ ├── github.svg │ │ ├── google-ai-studio.svg │ │ ├── google-vertex.svg │ │ ├── groq.svg │ │ ├── ollama.svg │ │ └── openai.svg │ └── vector-db │ │ ├── pinecone.png │ │ └── qdrant.png ├── process.svg ├── robots.txt └── sidebar │ ├── agents.png │ ├── api.png │ ├── apps.png │ ├── connections.png │ ├── logout.png │ ├── models.png │ ├── organizations.png │ ├── session-history.png │ ├── team.png │ ├── tools.png │ └── variables.png ├── src ├── animations │ ├── SignInAnimationTransparent.json │ └── dataSyncLoaderAnimation.json ├── api.ts ├── components │ ├── AddEmailModal.tsx │ ├── AgentAvatar.tsx │ ├── AgentForm.tsx │ ├── AgentList.tsx │ ├── AgentList2.tsx │ ├── ApiKeyList.tsx │ ├── AppCard.tsx │ ├── AvatarUploader.tsx │ ├── BackButton.tsx │ ├── BillingBanner.tsx │ ├── ButtonSpinner.tsx │ ├── ChatAppForm.tsx │ ├── ChatAppForm2.tsx │ ├── ClassNames.ts │ ├── ConfirmModal.tsx │ ├── ConversationStarters.tsx │ ├── CopyToClipboardInput.tsx │ ├── CreateAPIKeyModal.tsx │ ├── CreateAgentModal.tsx │ ├── CreateDatasourceForm.tsx │ ├── CreateDatasourceModal.tsx │ ├── CreateModelModal.tsx │ ├── CreateTaskModal.tsx │ ├── CreateTeamModal.tsx │ ├── CrewAppForm.tsx │ ├── CrewAppForm2.tsx │ ├── DatasourceChunkingForm.tsx │ ├── DatasourceFileTable.tsx │ ├── DatasourceScheduleForm.tsx │ ├── DatasourceStatusIndicator.tsx │ ├── DatasourceStream.tsx │ ├── DatasourceTable.tsx │ ├── DatasourceTabs.tsx │ ├── DeleteModal.tsx │ ├── DevBadge.tsx │ ├── DraggableSortableItem.tsx │ ├── DropZone.tsx │ ├── Editor.tsx │ ├── ErrorAlert.tsx │ ├── Flow.tsx │ ├── FormConfig.tsx │ ├── FormatDatasourceOptionLabel.tsx │ ├── FormatModelOptionLabel.tsx │ ├── FunctionCard.tsx │ ├── InfoAlert.tsx │ ├── InviteForm.tsx │ ├── InviteFormModal.tsx │ ├── Invoice.tsx │ ├── Layout.tsx │ ├── Layout2.tsx │ ├── LoadingBar.tsx │ ├── LoadingPlaceholder.tsx │ ├── MemberList.tsx │ ├── ModelForm.tsx │ ├── ModelTable.tsx │ ├── NewButtonSection.tsx │ ├── Notification.tsx │ ├── NotificationBell.tsx │ ├── OrgSelector.tsx │ ├── OrgSelector2.tsx │ ├── OrgSettingsForm.tsx │ ├── PageTitleWithNewButton.tsx │ ├── ParameterForm.tsx │ ├── PermissionsEditor.tsx │ ├── PreviewSessionList.tsx │ ├── ProgressBar.tsx │ ├── RetrievalStrategyComponent.tsx │ ├── SearchFilter.tsx │ ├── SessionCards.tsx │ ├── SessionChatbox.tsx │ ├── SharingModeInfoAlert.tsx │ ├── SharingModeSelect.tsx │ ├── SortableItem.tsx │ ├── Spinner.tsx │ ├── StripeCheckoutModal.tsx │ ├── StripePricingTable.tsx │ ├── SubscriptionCard.tsx │ ├── SubscriptionModal.tsx │ ├── SuccessAlert.tsx │ ├── TaskCards.tsx │ ├── TaskFlow.tsx │ ├── TaskForm.tsx │ ├── TeamForm.tsx │ ├── TeamSettingsForm.tsx │ ├── ToolList.tsx │ ├── ToolList2.tsx │ ├── ToolSelectIcons.tsx │ ├── ToolStateBadge.tsx │ ├── TrialNag.tsx │ ├── agents │ │ ├── AgentsSelect.tsx │ │ └── NewAgentSheet.tsx │ ├── apikeys │ │ └── ApiKeyForm.tsx │ ├── apps │ │ ├── AgentCreatedDisplay.tsx │ │ ├── AgentSelectDisplay.tsx │ │ ├── CreateAgentSheet.tsx │ │ ├── CreateChatAppMain.tsx │ │ ├── InsightChat.tsx │ │ ├── ToolsDialogContent.tsx │ │ ├── crew-apps │ │ │ ├── AgentSelection.tsx │ │ │ └── Config.tsx │ │ ├── tool.ts │ │ └── toolsdialogscreens │ │ │ ├── Custom.tsx │ │ │ ├── Initial.tsx │ │ │ ├── Install.tsx │ │ │ ├── New.tsx │ │ │ └── Tool.tsx │ ├── assets │ │ └── BrainIcon.tsx │ ├── chat │ │ ├── ChatRestartMessage.tsx │ │ └── message.tsx │ ├── connections │ │ ├── DataSourceConfigSteps.tsx │ │ ├── DataSourceCredentialsForm.tsx │ │ ├── DataSourceDetails.tsx │ │ ├── DataSourceGrid.tsx │ │ ├── DataSourceOnboardingSteps.tsx │ │ ├── DataSourceSearch.tsx │ │ ├── DatasourceStream.tsx │ │ ├── DatasourceStreamConfiguration.tsx │ │ ├── DatasourceSyncing.tsx │ │ ├── EmbeddingModelSelect.tsx │ │ ├── LLMConfigurationForm.tsx │ │ ├── LeftFrame.tsx │ │ ├── OnboardingSelect.tsx │ │ └── VectorDBSelection.tsx │ ├── connectorform │ │ ├── AdditionalFields.tsx │ │ ├── ArrayField.tsx │ │ ├── DynamicConnectorForm.tsx │ │ ├── FormField.tsx │ │ ├── FormSection.tsx │ │ ├── InputField.tsx │ │ ├── MultiSelectField.tsx │ │ └── ObjectArrayField.tsx │ ├── datasources │ │ └── DatasourcesSelect.tsx │ ├── form │ │ ├── InputField.tsx │ │ └── SelectField.tsx │ ├── modal │ │ └── CreateToolModal.tsx │ ├── models │ │ ├── ModelSelect.tsx │ │ └── ModelTypeRequirements.tsx │ ├── onboarding │ │ ├── DataSourceConfigSteps.tsx │ │ ├── DataSourceCredentialsForm.tsx │ │ ├── DataSourceDetails.tsx │ │ ├── DataSourceGrid.tsx │ │ ├── DataSourceOnboardingSteps.tsx │ │ ├── DataSourceSearch.tsx │ │ ├── DatasourceStreamConfiguration.tsx │ │ ├── DatasourceSyncing.tsx │ │ ├── EmbeddingModelSelect.tsx │ │ ├── LLMConfigurationForm.tsx │ │ ├── LeftFrame.tsx │ │ ├── OldDatasourceStream.tsx │ │ ├── OnboardingSelect.tsx │ │ └── VectorDBSelection.tsx │ ├── session │ │ ├── SessionVariableForm.tsx │ │ └── StructuredInputForm.tsx │ ├── sessions │ │ └── SessionTable.tsx │ ├── shared │ │ └── ToolTip.tsx │ ├── sharingmodes │ │ └── WhiteListSharing.tsx │ ├── tasks │ │ └── NewTaskSheet.tsx │ ├── tools │ │ ├── ToolForm.tsx │ │ ├── ToolsSelect.tsx │ │ ├── form │ │ │ ├── FunctionRevisionForm.tsx │ │ │ ├── FunctionToolForm.tsx │ │ │ └── ToolDetailsForm.tsx │ │ ├── tools-create.tsx │ │ ├── tools-edit.tsx │ │ ├── tools-library.tsx │ │ ├── tools-mytools.tsx │ │ ├── tools-stepper-create │ │ │ ├── stepper-create-dependencies.tsx │ │ │ ├── stepper-create-parameters.tsx │ │ │ └── stepper-create-source.tsx │ │ ├── tools-stepper-edit │ │ │ ├── stepper-edit-config.tsx │ │ │ ├── stepper-edit-dependencies.tsx │ │ │ ├── stepper-edit-source.tsx │ │ │ └── stepper-edit-version-history.tsx │ │ └── toolsdialogscreens │ │ │ ├── Custom.tsx │ │ │ ├── Initial.tsx │ │ │ ├── Install.tsx │ │ │ ├── New.tsx │ │ │ └── Tool.tsx │ ├── variables │ │ ├── CreateVariableModal.tsx │ │ ├── VariableDropdown.tsx │ │ ├── VariableForm.tsx │ │ └── VariableTable.tsx │ └── vectordbs │ │ ├── CreateVectorDbModal.tsx │ │ ├── VectorDbForm.tsx │ │ └── VectorDbTable.tsx ├── context │ ├── account.tsx │ ├── chat.tsx │ ├── connectorform.tsx │ ├── developer.tsx │ ├── notifications.tsx │ ├── onboardingform.tsx │ ├── socket.tsx │ ├── stepwrapper.tsx │ └── themecontext.tsx ├── controllers │ ├── account.ts │ ├── agent.ts │ ├── airbyte.ts │ ├── apikey.ts │ ├── app.ts │ ├── asset.ts │ ├── datasource.ts │ ├── model.ts │ ├── notification.ts │ ├── oauth.ts │ ├── org.ts │ ├── session.ts │ ├── sharelink.ts │ ├── stripe.ts │ ├── task.ts │ ├── team.ts │ ├── tool.ts │ ├── variables.ts │ └── vectordb.ts ├── data │ ├── apps.js │ └── connections.js ├── db │ ├── account.ts │ ├── agent.ts │ ├── apikey.ts │ ├── app.ts │ ├── asset.ts │ ├── auditlog.ts │ ├── chat.ts │ ├── checkoutsession.ts │ ├── crew.ts │ ├── datasource.ts │ ├── index.ts │ ├── migrate.ts │ ├── model.ts │ ├── notification.ts │ ├── org.ts │ ├── paymentlink.ts │ ├── portallink.ts │ ├── session.ts │ ├── sharelink.ts │ ├── task.ts │ ├── team.ts │ ├── tool.ts │ ├── toolrevision.ts │ ├── variable.ts │ ├── vectordb.ts │ └── verification.ts ├── emails │ ├── Invite.tsx │ ├── PasswordReset.tsx │ └── Verification.tsx ├── hooks │ ├── session │ │ └── useActiveTask.ts │ ├── use-toast.ts │ ├── useAutoCompleteDropdown.ts │ └── useResponsive.ts ├── lib │ ├── account │ │ └── create.ts │ ├── airbyte │ │ ├── api.ts │ │ ├── cronconverter.ts │ │ ├── definition-internal.yaml │ │ ├── format-duration.ts │ │ ├── getconnectors.ts │ │ ├── internal.ts │ │ └── setup.ts │ ├── auditlog.ts │ ├── commit.ts │ ├── email │ │ └── ses.ts │ ├── function │ │ ├── base.ts │ │ ├── google.ts │ │ ├── index.ts │ │ ├── local.ts │ │ └── provider.ts │ ├── middleware │ │ ├── auth │ │ │ ├── checkresourceslug.ts │ │ │ ├── checksession.ts │ │ │ ├── checksessionwelcome.ts │ │ │ ├── checksubscription.ts │ │ │ ├── csrf.ts │ │ │ ├── fetchsession.ts │ │ │ ├── setpermissions.ts │ │ │ ├── usejwt.ts │ │ │ └── usesession.ts │ │ ├── checkonboarded.ts │ │ ├── homeredirect.ts │ │ ├── passportmanager.ts │ │ ├── permissions │ │ │ └── hasperms.ts │ │ └── render │ │ │ └── staticpage.ts │ ├── misc │ │ ├── defaultchunkingoptions.ts │ │ ├── dynamicresponse.ts │ │ ├── getdotprop.test.ts │ │ ├── getdotprop.ts │ │ ├── getfileformat.ts │ │ ├── getgooglecredentials.ts │ │ ├── handleshiftnewlines.ts │ │ ├── passwordpattern.ts │ │ ├── time.test.ts │ │ ├── time.ts │ │ ├── toobjectid.test.ts │ │ ├── toobjectid.ts │ │ ├── tosnakecase.test.ts │ │ ├── tosnakecase.ts │ │ └── withlogging.ts │ ├── permissions │ │ ├── Permission.ts │ │ ├── bits.ts │ │ ├── metadata.ts │ │ ├── permissions.ts │ │ └── roles.ts │ ├── posthog │ │ └── index.ts │ ├── queue │ │ ├── bull.ts │ │ ├── google.ts │ │ ├── index.ts │ │ ├── provider.ts │ │ └── rabbitmq.ts │ ├── redis │ │ └── redis.ts │ ├── secret │ │ ├── google.ts │ │ ├── index.ts │ │ ├── local.ts │ │ ├── provider.ts │ │ └── secretkeys.ts │ ├── storage │ │ ├── google.ts │ │ ├── index.ts │ │ ├── local.ts │ │ └── provider.ts │ ├── stripe │ │ └── index.ts │ ├── struct │ │ ├── agent.ts │ │ ├── apikey.ts │ │ ├── app.ts │ │ ├── asset.ts │ │ ├── auditlog.ts │ │ ├── billing.ts │ │ ├── connector.ts │ │ ├── crew.ts │ │ ├── datasource.ts │ │ ├── db.ts │ │ ├── dispatchtypes.ts │ │ ├── editorschemas.ts │ │ ├── form.ts │ │ ├── function.ts │ │ ├── globaltools.ts │ │ ├── model.ts │ │ ├── notification.ts │ │ ├── oauth.ts │ │ ├── qdrantfilter.ts │ │ ├── role.ts │ │ ├── schedule.ts │ │ ├── session.ts │ │ ├── sharelink.ts │ │ ├── sharing.ts │ │ ├── syncserver.ts │ │ ├── task.ts │ │ ├── teammodels.ts │ │ ├── tool.ts │ │ ├── toolrevision.ts │ │ ├── variable.ts │ │ ├── vectordb.ts │ │ └── vectorproxy.ts │ ├── styles │ │ └── SelectClassNames.ts │ ├── utils │ │ ├── capitalize.test.ts │ │ ├── capitalize.ts │ │ ├── cn.ts │ │ ├── formatsize.test.ts │ │ ├── formatsize.ts │ │ ├── resync.ts │ │ ├── submittingreducer.ts │ │ ├── tosentencecase.test.ts │ │ ├── tosentencecase.ts │ │ ├── validationutils.test.ts │ │ └── validationutils.ts │ └── vectorproxy │ │ ├── client.ts │ │ └── limit.ts ├── migrations │ ├── 0.0.3.ts │ ├── 0.2.0.ts │ ├── 1.10.0.ts │ ├── 1.11.0.ts │ ├── 1.12.0.ts │ ├── 1.13.0.ts │ ├── 1.14.0.ts │ ├── 1.2.0.ts │ ├── 1.3.0.ts │ ├── 1.4.0.ts │ ├── 1.5.0.ts │ ├── 1.6.0.ts │ ├── 1.7.0.ts │ ├── 1.8.0.ts │ ├── 1.9.0.ts │ └── index.ts ├── modules │ └── components │ │ ├── connections │ │ ├── ConnectionsCards.tsx │ │ ├── ConnectionsIdCards.tsx │ │ ├── ConnectionsIdTabs.tsx │ │ ├── ConnectionsTable.tsx │ │ ├── NextLevel.tsx │ │ ├── Schedule.tsx │ │ └── Streams.tsx │ │ ├── multi-select-dialog.tsx │ │ ├── multi-select.tsx │ │ ├── stepper.tsx │ │ └── ui │ │ ├── badge.tsx │ │ ├── button.tsx │ │ ├── card.tsx │ │ ├── checkbox.tsx │ │ ├── collapsible.tsx │ │ ├── command.tsx │ │ ├── dialog.tsx │ │ ├── dropdown-menu.tsx │ │ ├── input.tsx │ │ ├── label.tsx │ │ ├── menubar.tsx │ │ ├── popover-dialog.tsx │ │ ├── popover.tsx │ │ ├── progress.tsx │ │ ├── select.tsx │ │ ├── separator.tsx │ │ ├── sheet.tsx │ │ ├── sidebar.tsx │ │ ├── skeleton.tsx │ │ ├── table.tsx │ │ ├── tabs.tsx │ │ ├── textarea.tsx │ │ ├── toast.tsx │ │ ├── toaster.tsx │ │ └── tooltip.tsx ├── next-env.d.ts ├── pages │ ├── 404.tsx │ ├── [resourceSlug] │ │ ├── agent │ │ │ ├── [agentId] │ │ │ │ └── edit.tsx │ │ │ └── add.tsx │ │ ├── agents.tsx │ │ ├── agents2.tsx │ │ ├── app │ │ │ ├── [appId] │ │ │ │ ├── edit.tsx │ │ │ │ └── index.tsx │ │ │ ├── add.tsx │ │ │ ├── add2.tsx │ │ │ └── add3.tsx │ │ ├── apps.tsx │ │ ├── connections │ │ │ ├── [datasourceId].tsx │ │ │ ├── add.tsx │ │ │ └── index.tsx │ │ ├── datasource │ │ │ ├── [datasourceId] │ │ │ │ └── index.tsx │ │ │ └── add.tsx │ │ ├── datasources.tsx │ │ ├── flow.tsx │ │ ├── model │ │ │ ├── [modelId] │ │ │ │ └── edit.tsx │ │ │ └── add.tsx │ │ ├── models.tsx │ │ ├── onboarding │ │ │ ├── configuremodels.tsx │ │ │ └── index.tsx │ │ ├── org.tsx │ │ ├── org │ │ │ └── [memberId] │ │ │ │ └── edit.tsx │ │ ├── session │ │ │ └── [sessionId] │ │ │ │ └── index.tsx │ │ ├── sessions.tsx │ │ ├── task │ │ │ ├── [taskId] │ │ │ │ └── edit.tsx │ │ │ └── add.tsx │ │ ├── tasks.tsx │ │ ├── team.tsx │ │ ├── team │ │ │ └── [memberId] │ │ │ │ └── edit.tsx │ │ ├── tool │ │ │ ├── [toolId] │ │ │ │ └── edit.tsx │ │ │ └── add.tsx │ │ ├── tools.tsx │ │ ├── variable │ │ │ ├── [variableId] │ │ │ │ └── edit.tsx │ │ │ └── add.tsx │ │ ├── variables.tsx │ │ ├── vectordb │ │ │ ├── [vectorDbId] │ │ │ │ └── edit.tsx │ │ │ └── add.tsx │ │ └── vectordbs.tsx │ ├── _app.tsx │ ├── _document.tsx │ ├── _error.tsx │ ├── account.tsx │ ├── apikey │ │ └── add.tsx │ ├── apikeys.tsx │ ├── billing.tsx │ ├── changepassword.tsx │ ├── favicon.ico │ ├── globals.css │ ├── index.tsx │ ├── login.tsx │ ├── redirect.tsx │ ├── register.tsx │ ├── requestchangepassword.tsx │ ├── verify.tsx │ └── welcome.tsx ├── router.ts ├── server.ts ├── socketio.ts ├── store │ ├── agent.ts │ ├── connection.ts │ ├── connections.ts │ ├── data-source.ts │ ├── datasource.ts │ └── tool.ts ├── sync-server │ ├── ecosystem.config.js │ ├── main.ts │ └── worker.ts ├── test │ ├── account.ts │ ├── helpers.ts │ ├── integration.test.ts │ ├── logout.ts │ ├── models.ts │ ├── pages.ts │ └── teams.ts └── types │ ├── sessions.ts │ └── tools.ts ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.server.json └── webapp.code-workspace /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/test-webapp.yml: -------------------------------------------------------------------------------- 1 | name: Test webapp 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - develop 8 | pull_request: 9 | branches: 10 | - master 11 | - develop 12 | 13 | jobs: 14 | test: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v4 20 | 21 | - name: Set up Node.js 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: '20' 25 | 26 | - name: Install dependencies 27 | working-directory: ./webapp 28 | run: npm install 29 | 30 | - name: Run tests and generate coverage report 31 | working-directory: ./webapp 32 | run: npm run test:coverage 33 | 34 | # - name: Upload test results 35 | # uses: actions/upload-artifact@v3 36 | # with: 37 | # name: test-results 38 | # path: webapp/coverage 39 | 40 | - name: Upload coverage results 41 | uses: actions/upload-artifact@v4 42 | with: 43 | name: coverage-report 44 | path: webapp/coverage/cobertura-coverage.xml 45 | 46 | - name: Publish Cobertura Coverage 47 | uses: 5monkeys/cobertura-action@master 48 | with: 49 | path: webapp/coverage/cobertura-coverage.xml 50 | minimum_coverage: 80 51 | skip_covered: true 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | */**/.DS_Store 4 | docker-compose.yml 5 | airbyte/ 6 | 7 | .env 8 | 9 | # Personal vscode settings 10 | agent-backend/src/.vscode/ 11 | agent-backend/src/OAI_CONFIG_LIST 12 | 13 | # Local Cache 14 | agent-backend/src/local_cache/ 15 | .vscode/ 16 | webapp/keyfile.json 17 | .aider* 18 | *.code-workspace 19 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # default_language_version: 2 | # python: python3 3 | 4 | # ci: 5 | # autofix_prs: true 6 | # autoupdate_commit_msg: '[pre-commit.ci] pre-commit suggestions' 7 | # autoupdate_schedule: 'quarterly' 8 | 9 | # repos: 10 | # - repo: https://github.com/pre-commit/pre-commit-hooks 11 | # rev: v4.4.0 12 | # hooks: 13 | # - id: check-added-large-files 14 | # exclude: '^(?!agent-backend/src/)' 15 | # - id: check-ast 16 | # exclude: '^(?!agent-backend/src/)' 17 | # - id: check-yaml 18 | # exclude: '^(?!agent-backend/src/)' 19 | # - id: check-toml 20 | # exclude: '^(?!agent-backend/src/)' 21 | # - id: check-json 22 | # exclude: '^(?!agent-backend/src/)' 23 | # - id: check-byte-order-marker 24 | # exclude: '^(?!agent-backend/src/)' 25 | # - id: check-merge-conflict 26 | # exclude: '^(?!agent-backend/src/)' 27 | # - id: detect-private-key 28 | # exclude: '^(?!agent-backend/src/)' 29 | # - id: trailing-whitespace 30 | # exclude: '^(?!agent-backend/src/)' 31 | # - id: end-of-file-fixer 32 | # exclude: '^(?!agent-backend/src/)' 33 | # - repo: https://github.com/psf/black 34 | # rev: 23.3.0 35 | # hooks: 36 | # - id: black 37 | # exclude: '^(?!agent-backend/src/)' 38 | -------------------------------------------------------------------------------- /agent-backend/.env.example: -------------------------------------------------------------------------------- 1 | LOCAL=True 2 | MAX_THREADS=8 3 | BASE_PATH=. 4 | REDIS_HOST=localhost 5 | DB_URL=mongodb://localhost:27017/test 6 | SOCKET_URL=http://localhost:3000/ 7 | AGENT_BACKEND_SOCKET_TOKEN= 8 | SERPER_API_KEY= 9 | 10 | GCS_BUCKET_NAME_PRIVATE=agentcloud-bucket-dev 11 | GCS_BUCKET_NAME=agentcloud-public-dev 12 | 13 | UPLOADS_BASE_PATH=/tmp 14 | GOOGLE_APPLICATION_CREDENTIALS=keyfile.json 15 | 16 | PROJECT_ID=agentcloud-dev 17 | STORAGE_PROVIDER=local -------------------------------------------------------------------------------- /agent-backend/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | PYTHONPATH=src poetry run uvicorn src.main:app --host 0.0.0.0 --port 8080 -------------------------------------------------------------------------------- /agent-backend/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /agent-backend/kubernetes/prod/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | # build the container image 3 | - name: "gcr.io/kaniko-project/executor:latest" 4 | id: "Build Docker image" 5 | env: 6 | - 'BUILD=$BUILD_ID' 7 | - 'PROJECT=$PROJECT_ID' 8 | - 'REV=$REVISION_ID' 9 | args: 10 | - --destination=gcr.io/$PROJECT_ID/agent-cloud:$BUILD_ID 11 | - --cache=true 12 | - --cache-ttl=96h 13 | # deploy container image to GKE 14 | - name: "gcr.io/cloud-builders/gke-deploy" 15 | id: "Deploy Container to GKE" 16 | args: 17 | - run 18 | - --filename=kubernetes/prod/deployment.yaml 19 | - --image=gcr.io/$PROJECT_ID/agent-cloud:$BUILD_ID 20 | - --location=$_ZONE 21 | - --cluster=$_CLUSTER 22 | timeout: '16000s' 23 | options: 24 | machineType: 'E2_HIGHCPU_8' 25 | -------------------------------------------------------------------------------- /agent-backend/kubernetes/prod/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: agent-cloud-ingress 5 | namespace: agent-cloud 6 | annotations: 7 | kubernetes.io/ingress.global-static-ip-name: agent-cloud-ip 8 | networking.gke.io/managed-certificates: agent-cloud-ssl 9 | nginx.ingress.kubernetes.io/websocket-services: gateway 10 | nginx.ingress.kubernetes.io/proxy-send-timeout: '3600' 11 | nginx.ingress.kubernetes.io/proxy-read-timeout: '3600' 12 | nginx.ingress.kubernetes.io/proxy-connect-timeout: '3600' 13 | spec: 14 | rules: 15 | - host: YOUR_DOMAIN 16 | http: 17 | paths: 18 | - path: /* 19 | pathType: ImplementationSpecific 20 | backend: 21 | service: 22 | name: agent-cloud-app 23 | port: 24 | number: 80 -------------------------------------------------------------------------------- /agent-backend/kubernetes/prod/ssl.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.gke.io/v1 2 | kind: ManagedCertificate 3 | metadata: 4 | name: agent-cloud-ssl 5 | namespace: agent-cloud 6 | spec: 7 | domains: 8 | - YOUR_DOMAIN -------------------------------------------------------------------------------- /agent-backend/src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/agent-backend/src/__init__.py -------------------------------------------------------------------------------- /agent-backend/src/chat/__init__.py: -------------------------------------------------------------------------------- 1 | from .chat_assistant import ChatAssistant 2 | -------------------------------------------------------------------------------- /agent-backend/src/chat/agents/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/agent-backend/src/chat/agents/__init__.py -------------------------------------------------------------------------------- /agent-backend/src/chat/agents/anthropic.py: -------------------------------------------------------------------------------- 1 | from chat.agents.base_anthropic_vertex import BaseAnthropicVertexChatAgent 2 | 3 | 4 | class AnthropicChatAgent(BaseAnthropicVertexChatAgent): 5 | """ 6 | Anthropic-specific customizations 7 | """ 8 | @staticmethod 9 | def _parse_model_chunk(chunk_content: list) -> str: 10 | if len(chunk_content) > 0: 11 | if "text" in chunk_content[0]: 12 | return chunk_content[0]["text"] 13 | elif "partial_json" in chunk_content[0]: 14 | return chunk_content[0]["partial_json"] 15 | return "" 16 | -------------------------------------------------------------------------------- /agent-backend/src/chat/agents/base_anthropic_vertex.py: -------------------------------------------------------------------------------- 1 | from langchain_core.messages import SystemMessage, HumanMessage, AIMessage 2 | 3 | from chat.agents.open_ai import OpenAIChatAgent 4 | 5 | 6 | class BaseAnthropicVertexChatAgent(OpenAIChatAgent): 7 | """ 8 | Customizations that work for both Anthropic and Vertex/Gemini models 9 | """ 10 | async def call_model(self, state, config): 11 | messages = state["messages"] 12 | updates = False 13 | if isinstance(messages[0], SystemMessage) and not isinstance(messages[1], HumanMessage): 14 | # to work around the "first message is not user message" error 15 | messages.insert(1, HumanMessage(content="<< dummy message >>")) 16 | updates = True 17 | if len(messages) >= 3 and isinstance(messages[-2], AIMessage) and isinstance(messages[-3], AIMessage): 18 | # to work around the "roles must alternate between "user" and "assistant"..." error 19 | messages.insert(-2, HumanMessage(content="<< dummy message >>")) 20 | updates = True 21 | 22 | if updates: 23 | self.graph.update_state(config, {"messages": messages}) 24 | return await super().call_model(state, config) 25 | -------------------------------------------------------------------------------- /agent-backend/src/chat/agents/factory.py: -------------------------------------------------------------------------------- 1 | from langchain_anthropic import ChatAnthropic 2 | from langchain_google_genai import ChatGoogleGenerativeAI 3 | from langchain_google_vertexai import ChatVertexAI 4 | from langchain_ollama import ChatOllama 5 | from typing import TYPE_CHECKING 6 | 7 | from .anthropic import AnthropicChatAgent 8 | from .ollama import OllamaChatAgent 9 | from .open_ai import OpenAIChatAgent 10 | from .vertex import VertexChatAgent 11 | 12 | if TYPE_CHECKING: 13 | from ..chat_assistant import ChatAssistant 14 | 15 | 16 | def chat_agent_factory(chat_assistant_obj: 'ChatAssistant'): 17 | chat_model = chat_assistant_obj.chat_model 18 | 19 | if isinstance(chat_model, ChatAnthropic): 20 | return AnthropicChatAgent(chat_assistant_obj) 21 | elif isinstance(chat_model, ChatVertexAI) or isinstance(chat_model, ChatGoogleGenerativeAI): 22 | return VertexChatAgent(chat_assistant_obj) 23 | elif isinstance(chat_model, ChatOllama): 24 | return OllamaChatAgent(chat_assistant_obj) 25 | 26 | # default; works for OpenAI, Azure OpenAI and Groq 27 | return OpenAIChatAgent(chat_assistant_obj) 28 | -------------------------------------------------------------------------------- /agent-backend/src/chat/agents/ollama.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from langchain_core.messages import AIMessage, HumanMessage 4 | 5 | from chat.agents.open_ai import OpenAIChatAgent 6 | 7 | 8 | class OllamaChatAgent(OpenAIChatAgent): 9 | """ 10 | Customization for Ollama models 11 | """ 12 | 13 | async def invoke_human_input(self, state, config): 14 | messages = state["messages"] + [ 15 | HumanMessage(content="Ask user what assistance they need or if they have any further query")] 16 | 17 | response = await self.chat_model.ainvoke(messages, config={**config, 'tags': ['no_stream']}) 18 | 19 | if isinstance(response, AIMessage) and len(response.tool_calls) == 0: 20 | response.tool_calls.append({ 21 | "name": "human_input", 22 | "args": { 23 | "text": response.content, 24 | }, 25 | "id": str(uuid.uuid4()) 26 | }) 27 | 28 | return {"messages": response} 29 | -------------------------------------------------------------------------------- /agent-backend/src/chat/agents/vertex.py: -------------------------------------------------------------------------------- 1 | from chat.agents.base_anthropic_vertex import BaseAnthropicVertexChatAgent 2 | 3 | 4 | class VertexChatAgent(BaseAnthropicVertexChatAgent): 5 | pass 6 | -------------------------------------------------------------------------------- /agent-backend/src/chat/docs/chat_workflow.md: -------------------------------------------------------------------------------- 1 | ## Description of chat workflow graph 2 | 3 | ![graph_visualization](graph_visualization.png) 4 | 5 | - Always **START** with the `human_input_invoker` node which is mostly the same as 6 | the `chat_model` node, the only difference being it appends a `HumanMessage` to 7 | the conversation invoking the `human_input` tool with a prompt. 8 | - Output of the `human_input` tool i.e. the user's message is passed to the `chat_model` node 9 | - `chat_model` node responds to the user message by invoking the LLM resulting 10 | in tool calling or chat response. This node has following edges: 11 | - A conditional edge from `chat_model` to invoke tools 12 | - An edge from the `chat_model` to `human_input` to allow for a 13 | [human-in-the-loop](https://langchain-ai.github.io/langgraph/concepts/agentic_concepts/#human-in-the-loop) 14 | flow. 15 | - Finally, once the LLM signals an **END**, end the session 16 | - An edge from `tools` node to `chat_model` allows for tools to invoke the LLM. 17 | -------------------------------------------------------------------------------- /agent-backend/src/chat/docs/graph_visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/agent-backend/src/chat/docs/graph_visualization.png -------------------------------------------------------------------------------- /agent-backend/src/chat/human_input_tool.py: -------------------------------------------------------------------------------- 1 | from langchain_core.tools import BaseTool 2 | from socketio import SimpleClient 3 | 4 | 5 | class HumanInputTool(BaseTool): 6 | name = "human_input" 7 | description = "Accepts messages from the user" 8 | socket_client: SimpleClient = None 9 | 10 | def __init__(self, socket_client: SimpleClient, **kwargs): 11 | super().__init__(**kwargs) 12 | self.socket_client = socket_client 13 | 14 | def _run(self): 15 | feedback = self.socket_client.receive() 16 | return feedback[1] 17 | 18 | -------------------------------------------------------------------------------- /agent-backend/src/crew/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/agent-backend/src/crew/__init__.py -------------------------------------------------------------------------------- /agent-backend/src/crew/exceptions.py: -------------------------------------------------------------------------------- 1 | class CrewAIBuilderException(Exception): 2 | """ Raise for crew builder errors """ 3 | 4 | -------------------------------------------------------------------------------- /agent-backend/src/init/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/agent-backend/src/init/__init__.py -------------------------------------------------------------------------------- /agent-backend/src/init/mongo_session.py: -------------------------------------------------------------------------------- 1 | from mongo.queries import MongoClientConnection 2 | 3 | 4 | def start_mongo_session() -> MongoClientConnection: 5 | return MongoClientConnection() 6 | -------------------------------------------------------------------------------- /agent-backend/src/init/redis_session.py: -------------------------------------------------------------------------------- 1 | from redisClient.redis_connection import RedisConnection 2 | import redis 3 | 4 | 5 | def start_redis_connection() -> redis: 6 | return RedisConnection().connect() 7 | -------------------------------------------------------------------------------- /agent-backend/src/lang_models/__init__.py: -------------------------------------------------------------------------------- 1 | from .factory import model_factory 2 | -------------------------------------------------------------------------------- /agent-backend/src/main.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import signal 3 | import threading 4 | import time 5 | 6 | from asyncio import CancelledError 7 | 8 | from langchain.globals import set_debug, set_verbose 9 | 10 | from messaging.client import consume_tasks 11 | from init.env_variables import MAX_THREADS, LOG_LEVEL 12 | from fastapi import FastAPI 13 | from contextlib import asynccontextmanager 14 | 15 | app_logger = logging.getLogger("app") 16 | 17 | app_logger.setLevel(LOG_LEVEL.upper()) 18 | 19 | if app_logger.getEffectiveLevel() == logging.DEBUG: 20 | set_debug(True) 21 | set_verbose(True) 22 | 23 | 24 | def sigint_handler(*args): 25 | raise SystemExit("Got SIGINT. Exiting...") 26 | 27 | 28 | @asynccontextmanager 29 | async def lifespan(app: FastAPI): 30 | signal.signal(signal.SIGINT, sigint_handler) 31 | 32 | print("running lifespan function") 33 | 34 | try: 35 | if threading.active_count() < MAX_THREADS: 36 | await consume_tasks() 37 | else: 38 | print("All threads are busy...will try again") 39 | time.sleep(10) 40 | await consume_tasks() 41 | yield 42 | except CancelledError: 43 | pass 44 | 45 | 46 | app = FastAPI(lifespan=lifespan) 47 | 48 | if __name__ == "__main__": 49 | print("hello world") 50 | -------------------------------------------------------------------------------- /agent-backend/src/messaging/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/agent-backend/src/messaging/__init__.py -------------------------------------------------------------------------------- /agent-backend/src/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/agent-backend/src/models/__init__.py -------------------------------------------------------------------------------- /agent-backend/src/models/vectordatabase.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | class VectorDatabase(str, Enum): 4 | Qdrant = "qdrant" 5 | Pinecone = "pinecone" -------------------------------------------------------------------------------- /agent-backend/src/mongo/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/agent-backend/src/mongo/__init__.py -------------------------------------------------------------------------------- /agent-backend/src/mongo/client.py: -------------------------------------------------------------------------------- 1 | from pymongo import MongoClient 2 | import logging 3 | from typing import Optional 4 | from init.env_variables import DB_URL 5 | 6 | 7 | class MongoConnection: 8 | def __init__(self): 9 | self.mongo_uri = DB_URL 10 | 11 | def connect(self) -> Optional[MongoClient]: 12 | try: 13 | mongo_client = MongoClient(self.mongo_uri, maxPoolSize=10) 14 | return mongo_client 15 | except Exception as e: 16 | logging.exception(e) 17 | return None 18 | -------------------------------------------------------------------------------- /agent-backend/src/qdrant/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/agent-backend/src/qdrant/__init__.py -------------------------------------------------------------------------------- /agent-backend/src/qdrant/qdrant_connection.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, constr, conint 2 | from qdrant_client import QdrantClient 3 | 4 | 5 | def get_connection(host: str, port: int) -> QdrantClient: 6 | # Check inputs 7 | class QdrantConnection(BaseModel): 8 | host: constr(min_length=2) 9 | port: conint(gt=0) # Port number should be greater than 0 10 | 11 | # Validate and create connection 12 | connection = QdrantConnection(host=host, port=port) 13 | 14 | # Connect to Qdrant 15 | client = QdrantClient(host=connection.host, port=connection.port) 16 | 17 | try: 18 | # Request a list of collections from Qdrant 19 | collections = client.get_collections() 20 | print("Successfully connected to Qdrant. Collections:", collections) 21 | except Exception as e: 22 | # Handle exceptions (e.g., connection errors) 23 | print(f"Failed to connect to Qdrant: {e}") 24 | 25 | return client 26 | -------------------------------------------------------------------------------- /agent-backend/src/redisClient/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/agent-backend/src/redisClient/__init__.py -------------------------------------------------------------------------------- /agent-backend/src/redisClient/redis_connection.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import redis 3 | from init.env_variables import REDIS_HOST, REDIS_PORT 4 | 5 | 6 | class RedisConnection: 7 | def __init__(self): 8 | self.redis_host = REDIS_HOST 9 | self.redis_port = REDIS_PORT 10 | 11 | def connect(self) -> redis: 12 | try: 13 | connection_pool = redis.ConnectionPool( 14 | host=self.redis_host, port=self.redis_port, db=0 15 | ) 16 | connection = redis.Redis(connection_pool=connection_pool) 17 | return connection 18 | except Exception as e: 19 | logging.exception(e) 20 | return None 21 | -------------------------------------------------------------------------------- /agent-backend/src/storage/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | STORAGE_PROVIDER = os.getenv("STORAGE_PROVIDER", "local") 4 | print(f"STORAGE_PROVIDER: {STORAGE_PROVIDER}") 5 | 6 | if STORAGE_PROVIDER == "google": 7 | from .google import google_storage_provider 8 | 9 | storage_provider = google_storage_provider 10 | else: 11 | from .local import local_storage_provider 12 | 13 | storage_provider = local_storage_provider 14 | -------------------------------------------------------------------------------- /agent-backend/src/storage/provider.py: -------------------------------------------------------------------------------- 1 | class StorageProvider: 2 | def init(self): 3 | raise NotImplementedError('init method not implemented') 4 | 5 | def upload_file_buffer(self, buffer, filename, file_folder, is_public=False): 6 | raise NotImplementedError('upload_file_buffer method not implemented') 7 | 8 | def upload_local_file(self, filename, file_folder, is_public=False): 9 | raise NotImplementedError('upload_local_file method not implemented') 10 | 11 | def delete_file(self, filename, file_folder, is_public=False): 12 | raise NotImplementedError('delete_file method not implemented') 13 | 14 | def get_signed_url(self, filename, file_folder, is_public=False): 15 | raise NotImplementedError('download_file method not implemented') 16 | -------------------------------------------------------------------------------- /agent-backend/src/test/init/test_OAI_CONFIG_LIST.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/agent-backend/src/test/init/test_OAI_CONFIG_LIST.py -------------------------------------------------------------------------------- /agent-backend/src/test/init/test_env_variables.py: -------------------------------------------------------------------------------- 1 | from init.env_variables import SOCKET_URL, BASE_PATH 2 | 3 | 4 | class TestEnvariables: 5 | # Assert that the constants are not null 6 | def test_non_null_constants(self): 7 | assert len(SOCKET_URL) > 0 8 | assert len(BASE_PATH) > 0 9 | -------------------------------------------------------------------------------- /agent-backend/src/test/init/test_mongo_session.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from init.mongo_session import start_mongo_session 4 | 5 | 6 | @pytest.mark.require_docker_compose_up 7 | class TestMongoSession: 8 | def test_mongo_session(self): 9 | mongo_client = start_mongo_session() 10 | connection = mongo_client.connect() 11 | result = connection.server_info()["ok"] == 1.0 12 | connection.close() 13 | 14 | assert result 15 | -------------------------------------------------------------------------------- /agent-backend/src/test/messaging/test_send_message_to_socket.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from unittest.mock import MagicMock, patch 3 | from models.sockets import SocketMessage, SocketEvents 4 | from socketio import SimpleClient 5 | from messaging import send_message_to_socket as sms 6 | from pydantic import ValidationError 7 | 8 | 9 | class TestSend: 10 | @pytest.fixture 11 | def mock_client(self): 12 | return MagicMock(spec=SimpleClient) 13 | 14 | @pytest.fixture 15 | def mock_event(self): 16 | return MagicMock(spec=SocketEvents) 17 | 18 | @pytest.fixture 19 | def mock_message(self): 20 | return MagicMock(spec=SocketMessage) 21 | 22 | def test_send_with_invalid_socket_or_logging( 23 | self, mock_client, mock_event, mock_message 24 | ): 25 | with pytest.raises((ValidationError, AssertionError, TypeError)) as excinfo: 26 | sms.send( 27 | mock_client, mock_event, mock_message, socket_or_logging="invalid_value" 28 | ) 29 | 30 | def test_send_socket_behavior_none_inputs(self): 31 | with pytest.raises(AssertionError) as excinfo: 32 | sms.send(None, None, None, socket_logging="socket") 33 | assert "client cannot be None" in str(excinfo.value) 34 | -------------------------------------------------------------------------------- /agent-backend/src/test/qdrantClient/test_qdrant_connection.py: -------------------------------------------------------------------------------- 1 | # test_qdrant_connection.py 2 | import pytest 3 | import qdrant.qdrant_connection as qdc 4 | 5 | 6 | @pytest.mark.require_docker_compose_up 7 | class TestQdrantConnection: 8 | def test_get_connection_success(self): 9 | # Test the successful creation of a Qdrant connection 10 | # Replace 'localhost' and 6333 with actual values if different 11 | client = qdc.get_connection(host="localhost", port=6333) 12 | assert client is not None 13 | 14 | def test_get_connection_invalid_host(self): 15 | # Test connection failure due to invalid host 16 | with pytest.raises(ValueError): 17 | qdc.get_connection(host="", port=6333) 18 | 19 | def test_get_connection_invalid_port(self): 20 | # Test connection failure due to invalid port 21 | with pytest.raises(ValueError): 22 | qdc.get_connection(host="localhost", port=0) 23 | -------------------------------------------------------------------------------- /agent-backend/src/tools/__init__.py: -------------------------------------------------------------------------------- 1 | from . import rag_tool, code_execution_tool, code_execution_docker_notebook_tool, google_cloud_function 2 | 3 | RagTool = rag_tool.RagTool 4 | CodeExecutionTool = code_execution_tool.CodeExecutionTool 5 | CodeExecutionUsingDockerNotebookTool = code_execution_docker_notebook_tool.CodeExecutionUsingDockerNotebookTool 6 | GoogleCloudFunctionTool = google_cloud_function.GoogleCloudFunctionTool 7 | # RagToolFactory = rag_tool.RagToolFactory 8 | -------------------------------------------------------------------------------- /agent-backend/src/tools/build_request_from_api_spec.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | # TODO: Load spec from mongo 5 | def load_spec(spec_file): 6 | pass 7 | 8 | 9 | def build_request(spec, endpoint_name, method, **params): 10 | base_url = spec["servers"][0]["url"] # Assuming the first server listed 11 | endpoint = spec["paths"][endpoint_name][method.lower()] 12 | 13 | # Constructing the URL 14 | url = f"{base_url}{endpoint_name}" 15 | 16 | # Adding parameters based on parameter types (path, query, etc.) 17 | if "parameters" in endpoint: 18 | for param in endpoint["parameters"]: 19 | if param["in"] == "query": 20 | url += f"?{param['name']}={params[param['name']]}" 21 | 22 | return url, method.upper() 23 | 24 | 25 | def make_request(url, method): 26 | if method == "GET": 27 | response = requests.get(url) 28 | elif method == "POST": 29 | response = requests.post(url) # Add data for POST requests 30 | # Add other methods as needed 31 | return response 32 | 33 | 34 | def build_run_request(path, endpoint_name, method, **params): 35 | # Usage 36 | spec = load_spec(path) 37 | url, method = build_request(spec, endpoint_name, method, **params) 38 | response = make_request(url, method) 39 | if response.ok(): 40 | return response.content 41 | -------------------------------------------------------------------------------- /agent-backend/src/tools/builtin_tools.py: -------------------------------------------------------------------------------- 1 | from typing import Type 2 | 3 | from langchain_core.tools import ToolException 4 | 5 | from .builtins import * 6 | from .builtins.base import BaseBuiltinTool 7 | 8 | 9 | class BuiltinTools: 10 | tools = { 11 | 'get_papers_from_arxiv': ArxivTool, 12 | 'search_wikipedia': WikipediaTool, 13 | 'search_wikidata': WikidataTool, 14 | 'search_duckduckgo': DuckDuckGoSearchTool, 15 | 'search_stackexchange': StackExchangeTool, 16 | 'search_youtube': YoutubeSearchTool, 17 | 'search_google': SerperGoogleSearchTool, 18 | 'apify_search_google': ApifyGoogleSearchTool, 19 | 'firecrawl_loader' : FireCrawlLoader, 20 | } 21 | 22 | @classmethod 23 | def get_tool_class(cls, tool_name: str) -> Type[BaseBuiltinTool]: 24 | klass = cls.tools.get(tool_name) 25 | if klass is None: 26 | raise ToolException(f"Tool with name '{tool_name}' not found.") 27 | return klass 28 | 29 | -------------------------------------------------------------------------------- /agent-backend/src/tools/builtins/__init__.py: -------------------------------------------------------------------------------- 1 | from .arxiv import ArxivTool 2 | from .wikipedia import WikipediaTool 3 | from .wikidata import WikidataTool 4 | from .duckduckgo import DuckDuckGoSearchTool 5 | from .stackexchange import StackExchangeTool 6 | from .youtube import YoutubeSearchTool 7 | from .serper import SerperGoogleSearchTool 8 | from .apify_google_search import ApifyGoogleSearchTool 9 | from .firecrawl_loader import FireCrawlLoader -------------------------------------------------------------------------------- /agent-backend/src/tools/builtins/apify_google_search.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from langchain_community.utilities import ApifyWrapper 4 | from langchain_core.documents import Document 5 | from langchain_core.tools import ToolException 6 | 7 | from tools.builtins.base import BaseBuiltinTool 8 | 9 | 10 | class ApifyGoogleSearchTool(BaseBuiltinTool): 11 | apify: ApifyWrapper 12 | 13 | def __init__(self, **kwargs): 14 | kwargs["apify"] = ApifyWrapper(**kwargs["parameters"]) 15 | super().__init__(**kwargs) 16 | 17 | def run_tool(self, query: str) -> str: 18 | loader = self.apify.call_actor( 19 | actor_id="apify/google-search-scraper", 20 | run_input={"queries": query}, 21 | dataset_mapping_function=lambda item: Document( 22 | page_content=str(item["organicResults"]) + "\n" + str(item["paidResults"]), 23 | metadata={"source": item["url"]} 24 | ), 25 | ) 26 | results = loader.load() 27 | self.logger.debug(f"{self.__class__.__name__} search results: {results}") 28 | return "\n".join(r.page_content for r in results) 29 | -------------------------------------------------------------------------------- /agent-backend/src/tools/builtins/arxiv.py: -------------------------------------------------------------------------------- 1 | import arxiv 2 | from .base import BaseBuiltinTool 3 | 4 | 5 | class ArxivTool(BaseBuiltinTool): 6 | """ 7 | get_papers_from_arxiv 8 | This tool takes a string query as input and fetches related research papers from the arXiv repository. 9 | The run_tool function connects to the arXiv API, submits the query, and retrieves a list of papers matching the query criteria. 10 | The returned data includes essential details like the paper's title, authors, abstract, and arXiv ID. 11 | Args: 12 | query (str): The query to send to arxiv to search for papers with. 13 | """ 14 | 15 | def run_tool(self, query: str) -> str: 16 | try: 17 | self.logger.info(f"{self.__class__.__name__} searching for '{query}'") 18 | search = arxiv.Search( 19 | query=query, max_results=10, sort_by=arxiv.SortCriterion.SubmittedDate 20 | ) 21 | results = [] 22 | for result in arxiv.Client().results(search): 23 | results.append(result.title) 24 | return results 25 | except Exception as e: 26 | print(f"An error occurred: {str(e)}") 27 | return f"An error occurred: {str(e)}" 28 | -------------------------------------------------------------------------------- /agent-backend/src/tools/builtins/duckduckgo.py: -------------------------------------------------------------------------------- 1 | from langchain_community.tools import DuckDuckGoSearchResults 2 | 3 | from tools.builtins.base import BaseBuiltinTool 4 | 5 | 6 | class DuckDuckGoSearchTool(BaseBuiltinTool): 7 | ddg: DuckDuckGoSearchResults 8 | 9 | def __init__(self, **kwargs): 10 | kwargs["ddg"] = DuckDuckGoSearchResults() 11 | super().__init__(**kwargs) 12 | 13 | def run_tool(self, query: str) -> str: 14 | results = self.ddg.run(query) 15 | self.logger.debug(f"{self.__class__.__name__} search results: {results}") 16 | return results 17 | -------------------------------------------------------------------------------- /agent-backend/src/tools/builtins/firecrawl_loader.py: -------------------------------------------------------------------------------- 1 | from langchain_community.document_loaders import AsyncChromiumLoader 2 | from langchain_community.document_loaders import FireCrawlLoader 3 | from pydantic import PrivateAttr 4 | from tools.builtins.base import BaseBuiltinTool 5 | import requests 6 | 7 | class FireCrawlLoader(BaseBuiltinTool): 8 | 9 | def __init__(self, *args, **kwargs): 10 | super().__init__(**kwargs) 11 | parameters: dict = kwargs.get("parameters") 12 | if parameters is not None: 13 | self.__dict__['_api_key'] = parameters.get("api_key", "") 14 | else: 15 | print("Parameters was None!") 16 | 17 | def run_tool(self, query: str) -> str: 18 | if not getattr(self, '_api_key', None): 19 | raise ValueError("API key is not set!") #type saftey 20 | 21 | # Use the API key for running the tool logic 22 | url = "https://api.firecrawl.dev/v1/scrape" 23 | payload = { 24 | "url": query 25 | } 26 | headers = { 27 | "Authorization": f"Bearer {self._api_key}", 28 | "Content-Type": "application/json" 29 | } 30 | response = requests.request('POST', url, json=payload, headers=headers) 31 | responseJson = response.json() 32 | return responseJson -------------------------------------------------------------------------------- /agent-backend/src/tools/builtins/serper.py: -------------------------------------------------------------------------------- 1 | from langchain_community.utilities import GoogleSerperAPIWrapper 2 | 3 | from tools.builtins.base import BaseBuiltinTool 4 | 5 | 6 | class SerperGoogleSearchTool(BaseBuiltinTool): 7 | serper: GoogleSerperAPIWrapper 8 | 9 | def __init__(self, **kwargs): 10 | kwargs["serper"] = GoogleSerperAPIWrapper(**kwargs["parameters"]) 11 | super().__init__(**kwargs) 12 | 13 | def run_tool(self, query: str) -> str: 14 | results = self.serper.run(query) 15 | self.logger.debug(f"{self.__class__.__name__} search results: {results}") 16 | return results 17 | -------------------------------------------------------------------------------- /agent-backend/src/tools/builtins/stackexchange.py: -------------------------------------------------------------------------------- 1 | from langchain_community.utilities import StackExchangeAPIWrapper 2 | from tools.builtins.base import BaseBuiltinTool 3 | 4 | 5 | class StackExchangeTool(BaseBuiltinTool): 6 | stackexchange: StackExchangeAPIWrapper 7 | 8 | def __init__(self, **kwargs): 9 | kwargs["stackexchange"] = StackExchangeAPIWrapper() 10 | super().__init__(**kwargs) 11 | 12 | def run_tool(self, query: str) -> str: 13 | results = self.stackexchange.run(query) 14 | self.logger.debug(f"{self.__class__.__name__} search results: {results}") 15 | return results 16 | -------------------------------------------------------------------------------- /agent-backend/src/tools/builtins/wikidata.py: -------------------------------------------------------------------------------- 1 | from typing import Type 2 | 3 | from langchain_community.tools.wikidata.tool import WikidataAPIWrapper, WikidataQueryRun 4 | from langchain_core.tools import ToolException, BaseTool 5 | 6 | from tools.builtins.base import BaseBuiltinTool 7 | 8 | 9 | class WikidataTool(BaseBuiltinTool): 10 | wikidata: WikidataQueryRun 11 | 12 | def __init__(self, **kwargs): 13 | kwargs["wikidata"] = WikidataQueryRun(api_wrapper=WikidataAPIWrapper()) 14 | super().__init__(**kwargs) 15 | 16 | def run_tool(self, query: str) -> str: 17 | results = self.wikidata.run(query) 18 | self.logger.debug(f"{self.__class__.__name__} search results: {results}") 19 | return results 20 | 21 | -------------------------------------------------------------------------------- /agent-backend/src/tools/builtins/wikipedia.py: -------------------------------------------------------------------------------- 1 | from langchain_community.tools.wikipedia.tool import WikipediaQueryRun, WikipediaAPIWrapper 2 | from langchain_core.tools import ToolException 3 | 4 | from tools.builtins.base import BaseBuiltinTool 5 | 6 | 7 | class WikipediaTool(BaseBuiltinTool): 8 | wikipedia: WikipediaQueryRun 9 | 10 | def __init__(self, **kwargs): 11 | kwargs["wikipedia"] = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper()) 12 | super().__init__(**kwargs) 13 | 14 | def run_tool(self, query: str) -> str: 15 | results = self.wikipedia.run(query) 16 | self.logger.debug(f"{self.__class__.__name__} search results: {results}") 17 | return results 18 | -------------------------------------------------------------------------------- /agent-backend/src/tools/builtins/youtube.py: -------------------------------------------------------------------------------- 1 | from langchain_community.tools import YouTubeSearchTool as LC_YouTubeSearchTool 2 | from tools.builtins.base import BaseBuiltinTool 3 | 4 | 5 | class YoutubeSearchTool(BaseBuiltinTool): 6 | yt: LC_YouTubeSearchTool 7 | 8 | def __init__(self, **kwargs): 9 | kwargs["yt"] = LC_YouTubeSearchTool() 10 | super().__init__(**kwargs) 11 | 12 | def run_tool(self, query: str) -> str: 13 | results = self.yt.run(query) 14 | self.logger.debug(f"{self.__class__.__name__} search results: {results}") 15 | return results 16 | -------------------------------------------------------------------------------- /agent-backend/src/tools/retrievers/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import BaseToolRetriever 2 | from .factory import retriever_factory 3 | -------------------------------------------------------------------------------- /agent-backend/src/tools/retrievers/callback_handler.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import Dict, Any, Sequence 3 | 4 | from langchain_core.callbacks import BaseCallbackHandler 5 | from langchain_core.documents import Document 6 | 7 | logger = logging.getLogger(__name__) 8 | logger.setLevel(logging.DEBUG) 9 | 10 | 11 | class RetrieverCallbackHandler(BaseCallbackHandler): 12 | def on_retriever_start( 13 | self, 14 | serialized: Dict[str, Any], 15 | query: str, 16 | **kwargs: Any, 17 | ) -> None: 18 | logger.debug(f"on_retriever_start: \n {serialized} \n query => {query} \n args => {kwargs} \n -----") 19 | 20 | def on_retriever_end( 21 | self, 22 | documents: Sequence[Document], 23 | **kwargs: Any, 24 | ) -> None: 25 | logger.debug(f"on_retriever_end: \n documents => {documents} \n args => {kwargs} \n -----") 26 | 27 | def on_retriever_error( 28 | self, 29 | error: BaseException, 30 | **kwargs: Any, 31 | ) -> Any: 32 | logger.debug(f"on_retriever_error: \n error => {error} \n args => {kwargs} \n -----") 33 | 34 | def on_text( 35 | self, 36 | text: str, 37 | **kwargs: Any, 38 | ) -> None: 39 | logger.debug(f"on_text: \n text => {text} \n args => {kwargs} \n -----") 40 | -------------------------------------------------------------------------------- /agent-backend/src/tools/retrievers/custom/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/agent-backend/src/tools/retrievers/custom/__init__.py -------------------------------------------------------------------------------- /agent-backend/src/tools/retrievers/default.py: -------------------------------------------------------------------------------- 1 | from langchain_core.embeddings import Embeddings 2 | from langchain_core.vectorstores import VectorStore 3 | 4 | from models.mongo import Tool 5 | from .base import BaseToolRetriever 6 | from .callback_handler import RetrieverCallbackHandler 7 | from .similarity_search import SimilaritySearchRetriever 8 | 9 | 10 | class DefaultRetriever(BaseToolRetriever): 11 | def __init__(self, tool: Tool, embedding: Embeddings, vector_store: VectorStore): 12 | self.tool = tool 13 | self.retriever = SimilaritySearchRetriever( 14 | embedding=embedding, 15 | vector_store=vector_store, 16 | k=tool.retriever_config.k, 17 | rag_filters=tool.ragFilters 18 | ) 19 | super().__init__() 20 | -------------------------------------------------------------------------------- /agent-backend/src/tools/retrievers/factory.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from langchain_core.embeddings import Embeddings 3 | from langchain_community.vectorstores import VectorStore 4 | from langchain_core.language_models import BaseLanguageModel 5 | 6 | from models.mongo import Retriever, Tool 7 | from .default import DefaultRetriever 8 | from .self_query import SelfQueryRetriever 9 | from .time_weighted import TimeWeightedRetriever 10 | from .multi_query import MultiQueryRetriever 11 | 12 | 13 | def retriever_factory(tool: Tool, vector_store: VectorStore, embedding: Embeddings, llm: BaseLanguageModel): 14 | metadata_field_info = tool.retriever_config.metadata_field_info 15 | for field in metadata_field_info: 16 | if isinstance(field.type, list) and 'null' in field.type: 17 | field.type = [t for t in field.type if t != 'null'][0] 18 | match tool.retriever_type: 19 | case Retriever.RAW: 20 | return DefaultRetriever(tool, embedding, vector_store) 21 | case Retriever.SELF_QUERY: 22 | return SelfQueryRetriever(tool, embedding, llm, vector_store) 23 | case Retriever.TIME_WEIGHTED: 24 | return TimeWeightedRetriever(tool, vector_store) 25 | case Retriever.MULTI_QUERY: 26 | return MultiQueryRetriever(tool, llm, vector_store) 27 | -------------------------------------------------------------------------------- /agent-backend/src/tools/retrievers/similarity_search.py: -------------------------------------------------------------------------------- 1 | from langchain_community.vectorstores import Qdrant 2 | from langchain_core.callbacks import CallbackManagerForRetrieverRun 3 | from langchain_core.embeddings import Embeddings 4 | from langchain_core.retrievers import BaseRetriever 5 | from langchain_core.vectorstores import VectorStore 6 | from tools.retrievers.filters import create_qdrant_filters, create_pinecone_filters 7 | from typing import Dict 8 | from .base import BaseToolRetriever 9 | 10 | class SimilaritySearchRetriever(BaseRetriever): 11 | embedding: Embeddings 12 | vector_store: VectorStore 13 | k: int 14 | rag_filters: Dict 15 | 16 | def _get_relevant_documents(self, query: str, *, run_manager: CallbackManagerForRetrieverRun): 17 | embedded_question = self.embedding.embed_query(query) 18 | if isinstance(self.vector_store, Qdrant): 19 | return self.vector_store.similarity_search_with_score_by_vector(embedded_question, k=self.k, filter=create_qdrant_filters(self.rag_filters)) 20 | 21 | return self.vector_store.similarity_search_by_vector_with_score(embedded_question, k=self.k, filter=create_pinecone_filters(self.rag_filters)) 22 | -------------------------------------------------------------------------------- /agent-backend/src/tools/retrievers/time_weighted.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | from langchain_core.vectorstores import VectorStore 4 | 5 | from models.mongo import Tool 6 | from .base import BaseToolRetriever 7 | from .custom.time_weighted_retriever import CustomTimeWeightedVectorStoreRetriever 8 | 9 | 10 | class TimeWeightedRetriever(BaseToolRetriever): 11 | 12 | def __init__(self, tool: Tool, vector_store: VectorStore): 13 | self.tool = tool 14 | self.retriever = CustomTimeWeightedVectorStoreRetriever( 15 | vectorstore=vector_store, 16 | time_weight_field_name=tool.retriever_config.timeWeightField, 17 | decay_rate=tool.retriever_config.decay_rate, 18 | k=tool.retriever_config.k 19 | ) 20 | super().__init__() 21 | 22 | def format_results(self, results): 23 | self.logger.debug(f"{self.__class__.__name__} results: {results}") 24 | return "\n".join( 25 | map(lambda x: x if type(x) is str else str({ 26 | 'data': x[0].page_content, 27 | 'metadata': x[0].metadata, 28 | 'time_weight_score': x[1] 29 | }), results)) 30 | -------------------------------------------------------------------------------- /agent-backend/src/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/agent-backend/src/utils/__init__.py -------------------------------------------------------------------------------- /agent-backend/src/utils/class_checker.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Type 2 | 3 | 4 | def check_instance_of_class(instance: Any, class_type: Type[Any]) -> Any: 5 | if not isinstance(instance, class_type): 6 | raise AssertionError( 7 | f"Expected instance of {class_type.__name__}, got {type(instance).__name__}" 8 | ) 9 | return instance 10 | -------------------------------------------------------------------------------- /agent-backend/src/utils/dict_utils.py: -------------------------------------------------------------------------------- 1 | def exclude_items(dictionary: dict, keys_to_exclude: list) -> dict: 2 | return {k: v for k, v in dictionary.items() if k not in keys_to_exclude} 3 | -------------------------------------------------------------------------------- /agent-backend/src/utils/log_exception_context_manager.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from contextlib import contextmanager 3 | 4 | 5 | @contextmanager 6 | def raise_exception(): 7 | try: 8 | yield 9 | except Exception as err: 10 | logging.exception(err) 11 | raise 12 | 13 | 14 | @contextmanager 15 | def log_exception(): 16 | try: 17 | yield 18 | except Exception as err: 19 | logging.exception(err) 20 | -------------------------------------------------------------------------------- /agent-backend/src/vectorstores/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/agent-backend/src/vectorstores/__init__.py -------------------------------------------------------------------------------- /docker-compose.minimal.yml: -------------------------------------------------------------------------------- 1 | name: agentcloud 2 | 3 | services: 4 | 5 | unstructured-api: 6 | ports: 7 | - 9500:9500 8 | container_name: unstructured-api 9 | environment: 10 | - PORT=9500 11 | image: localhost:5000/unstructured-api 12 | 13 | docker_rabbitmq: 14 | image: rabbitmq:3.13.1-management 15 | environment: 16 | RABBITMQ_DEFAULT_USER: guest 17 | RABBITMQ_DEFAULT_PASS: guest 18 | ports: 19 | - "127.0.0.1:5672:5672" 20 | - "127.0.0.1:15672:15672" 21 | 22 | docker_mongo: 23 | restart: always 24 | image: mongo:latest 25 | ports: 26 | - "127.0.0.1:27017:27017" 27 | volumes: 28 | - mongodb_data_container:/data/db 29 | 30 | docker_redis: 31 | restart: always 32 | image: redis:latest 33 | ports: 34 | - "127.0.0.1:6379:6379" 35 | volumes: 36 | - redis_data:/data 37 | 38 | qdrant: 39 | ports: 40 | - '127.0.0.1:6333:6333' 41 | - '127.0.0.1:6334:6334' 42 | image: qdrant/qdrant 43 | environment: 44 | - QDRANT__LOG_LEVEL=DEBUG 45 | volumes: 46 | - qdrant_data:/qdrant_data 47 | 48 | volumes: 49 | mongodb_data_container: 50 | redis_data: 51 | datasource_files: 52 | qdrant_data: 53 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "agentcloud", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": {} 6 | } 7 | -------------------------------------------------------------------------------- /values.yaml: -------------------------------------------------------------------------------- 1 | global: 2 | auth: 3 | enabled: false -------------------------------------------------------------------------------- /vector-db-proxy/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.aarch64-apple-darwin] 2 | rustflags = ["-Clink-arg=-fapple-link-rtlib"] -------------------------------------------------------------------------------- /vector-db-proxy/.dockerignore: -------------------------------------------------------------------------------- 1 | target 2 | local_cache 3 | .env 4 | -------------------------------------------------------------------------------- /vector-db-proxy/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .env 3 | .DS_Store 4 | # Generated by Cargo 5 | # will have compiled files and executables 6 | debug/ 7 | target/ 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | # MSVC Windows builds of rustc generate these, which store debugging information 13 | *.pdb 14 | *.pdf 15 | *.json 16 | *.txt 17 | *.pptx 18 | *.docx 19 | *.xlsx 20 | local_cache 21 | -------------------------------------------------------------------------------- /vector-db-proxy/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | cargo run 3 | -------------------------------------------------------------------------------- /vector-db-proxy/README.md: -------------------------------------------------------------------------------- 1 | # vector-db 2 | Implementing a vector DB to power RAG applications 3 | -------------------------------------------------------------------------------- /vector-db-proxy/src/adaptors/gcp/gcs.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{anyhow, Result}; 2 | use google_cloud_storage::client::{Client, ClientConfig}; 3 | use google_cloud_storage::http::objects::download::Range; 4 | use google_cloud_storage::http::objects::get::GetObjectRequest; 5 | 6 | pub async fn get_object_from_gcs(bucket: &str, object: &str) -> Result> { 7 | let bucket_name = bucket.to_string(); 8 | let filename = object.to_string(); 9 | 10 | let client = match ClientConfig::default().with_auth().await { 11 | Ok(config) => { 12 | let client = Client::new(config); 13 | Ok(client) 14 | } 15 | Err(e) => Err(anyhow!( 16 | "An error occurred while authenticating to GCS. Error: {:?}", 17 | e 18 | )), 19 | }; 20 | 21 | return match client 22 | .unwrap() 23 | .download_object( 24 | &GetObjectRequest { 25 | bucket: bucket_name, 26 | object: filename, 27 | ..Default::default() 28 | }, 29 | &Range::default(), 30 | ) 31 | .await 32 | { 33 | Ok(data) => Ok(data), 34 | Err(e) => Err(anyhow!( 35 | "An error occurred while fetching data from GCS. Error: {}", 36 | e 37 | )), 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /vector-db-proxy/src/adaptors/gcp/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod gcs; 2 | pub mod pubsub; 3 | pub mod models; -------------------------------------------------------------------------------- /vector-db-proxy/src/adaptors/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod gcp; 2 | pub mod mongo; 3 | pub mod pinecone; 4 | pub mod qdrant; 5 | pub mod rabbitmq; 6 | -------------------------------------------------------------------------------- /vector-db-proxy/src/adaptors/mongo/client.rs: -------------------------------------------------------------------------------- 1 | use crate::adaptors::mongo::error::CustomMongoError; 2 | use crate::init::env_variables::GLOBAL_DATA; 3 | use anyhow::{anyhow, Result}; 4 | use mongodb::{options::ClientOptions, Client, Database}; 5 | 6 | pub async fn start_mongo_connection() -> Result { 7 | let global_data = GLOBAL_DATA.read().await; 8 | let client_options = ClientOptions::parse(global_data.mongo_uri.as_str()).await?; 9 | // Get a handle to the deployment. 10 | let client = match Client::with_options(client_options) { 11 | Ok(c) => c, 12 | Err(e) => { 13 | println!("Failed to create client: {}", e); 14 | log::error!("Failed to create client: {}", e); 15 | return Err(CustomMongoError::InternalError(anyhow!(e))); 16 | } 17 | }; 18 | // Get a handle to a database. 19 | let db = client.database(global_data.mongo_db_name.as_str()); 20 | Ok(db) 21 | } 22 | -------------------------------------------------------------------------------- /vector-db-proxy/src/adaptors/mongo/error.rs: -------------------------------------------------------------------------------- 1 | use actix_web::http::StatusCode; 2 | use actix_web::{HttpResponse, ResponseError}; 3 | use anyhow::Error; 4 | use mongodb::error::Error as MongoError; 5 | use thiserror::Error as ThisError; 6 | 7 | #[derive(Debug, ThisError)] 8 | pub enum CustomMongoError { 9 | #[error("an unspecified internal error occurred: {0}")] 10 | InternalError(#[from] Error), 11 | #[error("a standard error occurred: {0}")] 12 | StdError(#[from] Box), 13 | #[error("Mongo DB error occurred: {0}")] 14 | MongoError(#[from] MongoError), 15 | } 16 | 17 | impl ResponseError for CustomMongoError { 18 | fn status_code(&self) -> StatusCode { 19 | match self { 20 | CustomMongoError::InternalError(_) => StatusCode::INTERNAL_SERVER_ERROR, 21 | CustomMongoError::StdError(_) => StatusCode::INTERNAL_SERVER_ERROR, 22 | CustomMongoError::MongoError(_) => StatusCode::INTERNAL_SERVER_ERROR, 23 | } 24 | } 25 | 26 | fn error_response(&self) -> HttpResponse { 27 | HttpResponse::build(self.status_code()).body(self.to_string()) 28 | } 29 | } 30 | 31 | // Short hand alias, which allows you to use just Result 32 | pub type Result = std::result::Result; 33 | -------------------------------------------------------------------------------- /vector-db-proxy/src/adaptors/mongo/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod client; 2 | pub mod error; 3 | pub mod models; 4 | pub mod queries; 5 | -------------------------------------------------------------------------------- /vector-db-proxy/src/adaptors/pinecone/client.rs: -------------------------------------------------------------------------------- 1 | use pinecone_sdk::pinecone::{PineconeClient, PineconeClientConfig}; 2 | 3 | pub async fn build_pinecone_client( 4 | url: Option, 5 | api_key: Option, 6 | ) -> anyhow::Result { 7 | let mut client_config = PineconeClientConfig { 8 | api_key, 9 | ..Default::default() 10 | }; 11 | if url.is_none() { 12 | client_config.control_plane_host = url 13 | } 14 | anyhow::Ok(client_config.client()?) 15 | } 16 | -------------------------------------------------------------------------------- /vector-db-proxy/src/adaptors/pinecone/mod.rs: -------------------------------------------------------------------------------- 1 | mod apis; 2 | pub(crate) mod client; 3 | mod helpers; 4 | -------------------------------------------------------------------------------- /vector-db-proxy/src/adaptors/qdrant/client.rs: -------------------------------------------------------------------------------- 1 | use qdrant_client::client::QdrantClient; 2 | 3 | pub async fn build_qdrant_client( 4 | url: Option, 5 | api_key: Option, 6 | ) -> anyhow::Result { 7 | if let Some(url) = url { 8 | let client = QdrantClient::from_url(url.as_str()).with_api_key(api_key); 9 | Ok(client.build()?) 10 | } else { 11 | Err(anyhow::anyhow!( 12 | "No URL was provided for Qdrant host. Can not build client" 13 | )) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /vector-db-proxy/src/adaptors/qdrant/mod.rs: -------------------------------------------------------------------------------- 1 | mod apis; 2 | pub(crate) mod client; 3 | pub mod helpers; 4 | -------------------------------------------------------------------------------- /vector-db-proxy/src/adaptors/rabbitmq/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod client; 2 | pub mod models; -------------------------------------------------------------------------------- /vector-db-proxy/src/data/helpers.rs: -------------------------------------------------------------------------------- 1 | use std::hash::{DefaultHasher, Hash, Hasher}; 2 | use uuid::Uuid; 3 | 4 | pub fn hash_string_to_uuid(hashing_salt: &str, string: &str) -> String { 5 | // Create a hasher for the first 64 bits 6 | let mut hasher1 = DefaultHasher::new(); 7 | string.hash(&mut hasher1); 8 | let hash1 = hasher1.finish(); 9 | 10 | // Create a hasher for the second 64 bits 11 | let mut hasher2 = DefaultHasher::new(); 12 | 13 | format!("{}{}", string, hashing_salt).hash(&mut hasher2); // Adding a "salt" to differentiate the hashes 14 | let hash2 = hasher2.finish(); 15 | 16 | // Combine the two 64-bit hashes into a 128-bit array 17 | let mut uuid_bytes = [0u8; 16]; 18 | uuid_bytes[..8].copy_from_slice(&hash1.to_be_bytes()); 19 | uuid_bytes[8..].copy_from_slice(&hash2.to_be_bytes()); 20 | 21 | // Convert the 128-bit array into a UUID 22 | Uuid::from_bytes(uuid_bytes).to_string() 23 | } 24 | -------------------------------------------------------------------------------- /vector-db-proxy/src/data/mod.rs: -------------------------------------------------------------------------------- 1 | mod helpers; 2 | pub mod models; 3 | pub mod processing_incoming_messages; 4 | pub mod unstructuredio; 5 | -------------------------------------------------------------------------------- /vector-db-proxy/src/data/models.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Copy, Clone, Debug, Serialize, Deserialize)] 4 | #[serde(rename_all = "lowercase")] 5 | pub enum FileType { 6 | PDF, 7 | TXT, 8 | CSV, 9 | DOCX, 10 | MARKDOWN, 11 | UNKNOWN, 12 | } 13 | 14 | impl FileType { 15 | pub fn to_str<'a>(value: Self) -> &'a str { 16 | match value { 17 | Self::MARKDOWN => "markdown", 18 | Self::CSV => "csv", 19 | Self::DOCX => "docx", 20 | Self::PDF => "pdf", 21 | Self::TXT => "txt", 22 | _ => "unknown", 23 | } 24 | } 25 | } 26 | 27 | impl From for FileType { 28 | fn from(value: String) -> Self { 29 | match value.as_str() { 30 | "pdf" => Self::PDF, 31 | "txt" => Self::TXT, 32 | "csv" => Self::CSV, 33 | "markdown" => Self::MARKDOWN, 34 | "docx" | "pptx" | "xlsx" | "odt" | "ods" | "odp" => Self::DOCX, 35 | _ => Self::UNKNOWN, 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /vector-db-proxy/src/data/unstructuredio/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod apis; 2 | pub mod models; 3 | -------------------------------------------------------------------------------- /vector-db-proxy/src/dummy.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | -------------------------------------------------------------------------------- /vector-db-proxy/src/embeddings/helpers.rs: -------------------------------------------------------------------------------- 1 | use regex::Regex; 2 | use serde_json::{Map, Value}; 3 | use std::collections::HashMap; 4 | 5 | fn _custom_format_metadata(mut metadata_map: HashMap) -> HashMap { 6 | let mut new_map = HashMap::new(); 7 | let metadata_map_as_serde_map: Map = metadata_map.clone().into_iter().collect(); 8 | new_map.insert( 9 | "metadata".to_string(), 10 | Value::Object(metadata_map_as_serde_map), 11 | ); 12 | if let Some(content) = metadata_map.remove("content") { 13 | let clean_text = clean_text(content.to_string()); 14 | new_map.insert("content".to_string(), Value::String(clean_text)); 15 | } else { 16 | log::warn!("Content field was not found in metadata") 17 | } 18 | new_map 19 | } 20 | 21 | pub fn clean_text(text: String) -> String { 22 | let re_back_slash = Regex::new(r"\\").unwrap(); 23 | let re = Regex::new(r#"(?:[\\"\n\r]|\\[nr])+"#).unwrap(); 24 | let phase_1 = re.replace_all(&text, "").into_owned(); 25 | re_back_slash.replace_all(&phase_1, "").into_owned() 26 | } 27 | -------------------------------------------------------------------------------- /vector-db-proxy/src/embeddings/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod helpers; 2 | pub mod models; 3 | pub mod utils; 4 | -------------------------------------------------------------------------------- /vector-db-proxy/src/init/env_variables.rs: -------------------------------------------------------------------------------- 1 | use dotenv; 2 | use std::env; 3 | use once_cell::sync::Lazy; 4 | use tokio::sync::RwLock; 5 | use crate::init::models::GlobalData; 6 | 7 | pub async fn set_all_env_vars() { 8 | for (k, v) in dotenv::vars() { 9 | env::set_var(k, v) 10 | } 11 | } 12 | 13 | 14 | pub static GLOBAL_DATA: Lazy> = Lazy::new(|| { 15 | let data: GlobalData = GlobalData::new(); 16 | RwLock::new(data) 17 | }); -------------------------------------------------------------------------------- /vector-db-proxy/src/init/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod env_variables; 2 | pub mod models; -------------------------------------------------------------------------------- /vector-db-proxy/src/messages/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod models; 2 | pub mod tasks; 3 | pub mod task_handoff; 4 | -------------------------------------------------------------------------------- /vector-db-proxy/src/messages/task_handoff.rs: -------------------------------------------------------------------------------- 1 | use crate::adaptors::mongo::models::DataSources; 2 | use crossbeam::channel::Sender; 3 | 4 | /// Adds the incoming task to the execution Queue to be processes when threads are available 5 | pub async fn send_task( 6 | sender: Sender<(DataSources, Option, String)>, 7 | params: (DataSources, Option, String), 8 | ) { 9 | let (dataset_id, stream_config_key, message) = params; 10 | // Instantiate a new instance of the MyQueue 11 | let _ = sender 12 | .send((dataset_id, stream_config_key, message)) 13 | .map_err(|err| log::error!("An error occurred while sending task to channel: {}", err)); 14 | } 15 | -------------------------------------------------------------------------------- /vector-db-proxy/src/routes/helpers.rs: -------------------------------------------------------------------------------- 1 | use crate::vector_databases::error::VectorDatabaseError; 2 | use serde_json::Value; 3 | 4 | pub fn format_error_message(msg: VectorDatabaseError) -> Option { 5 | let error_message_str = format!("{}", msg); 6 | let error_message_json: Option = error_message_str 7 | .split("content: ") 8 | .nth(1) 9 | .and_then(|json_part| serde_json::from_str(json_part).ok()); 10 | error_message_json 11 | } 12 | -------------------------------------------------------------------------------- /vector-db-proxy/src/routes/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod apis; 2 | mod helpers; 3 | pub mod models; 4 | -------------------------------------------------------------------------------- /vector-db-proxy/src/routes/models.rs: -------------------------------------------------------------------------------- 1 | use crate::vector_databases::models::StorageSize; 2 | use serde::{Deserialize, Serialize}; 3 | use serde_json::Value; 4 | 5 | #[derive(Serialize, Deserialize)] 6 | pub enum Status { 7 | Success, 8 | Failure, 9 | DoesNotExist, 10 | NotFound, 11 | } 12 | 13 | #[derive(Serialize, Deserialize)] 14 | pub struct ResponseBody { 15 | pub status: Status, 16 | pub data: Option, 17 | pub error_message: Option, 18 | } 19 | 20 | #[derive(Serialize, Clone, Debug)] 21 | pub struct CollectionStorageSizeResponse { 22 | pub list_of_datasources: Vec, 23 | pub total_size: f64, 24 | pub total_points: u64, 25 | } 26 | -------------------------------------------------------------------------------- /vector-db-proxy/src/utils/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! convert_bson_to_string_or_return_empty { 3 | ($e:expr) => {{ 4 | $e.map(|value| 5 | value.as_str()) 6 | .unwrap_or(Some("")) 7 | .map(|value| 8 | value.to_string()) 9 | .unwrap_or_else(|| { "".to_string() }) 10 | }}; 11 | } 12 | 13 | #[macro_export] 14 | macro_rules! hash_map_values_as_serde_values { 15 | ($row:expr) => {{ 16 | use std::collections::HashMap; 17 | use serde_json::Value; 18 | 19 | let payload: HashMap = $row 20 | .iter() 21 | .filter_map(|(k, v)| { 22 | if let Ok(value) = serde_json::from_str::(v.to_string().as_str()) { 23 | Some((k.clone(), value)) 24 | } else { 25 | None 26 | } 27 | }) 28 | .collect(); 29 | payload 30 | }}; 31 | } -------------------------------------------------------------------------------- /vector-db-proxy/src/utils/maths.rs: -------------------------------------------------------------------------------- 1 | pub fn mean_of_vec(vector: &Vec) -> Option{ 2 | if !vector.is_empty(){ 3 | let vector_sum: f32 = vector.iter().sum(); 4 | Some(vector_sum / vector.len() as f32) 5 | }else { 6 | return None; 7 | } 8 | } 9 | 10 | pub fn negative_vector(vector: &mut Vec) -> Option>{ 11 | if !vector.is_empty(){ 12 | for num in vector.iter_mut() { 13 | *num *= -1.0; 14 | }; 15 | return Some(vector.to_owned()); 16 | 17 | } 18 | return None; 19 | } -------------------------------------------------------------------------------- /vector-db-proxy/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod conversions; 2 | pub mod maths; 3 | pub mod macros; 4 | pub mod webhook; 5 | pub mod file_operations; 6 | pub mod models; 7 | -------------------------------------------------------------------------------- /vector-db-proxy/src/utils/models.rs: -------------------------------------------------------------------------------- 1 | pub enum FileSources { 2 | GCS, 3 | LOCAL, 4 | UNKNOWN, 5 | } 6 | 7 | impl From for FileSources { 8 | fn from(value: String) -> Self { 9 | match value.as_str() { 10 | "google" => FileSources::GCS, 11 | "local" => FileSources::LOCAL, 12 | _ => FileSources::UNKNOWN 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /vector-db-proxy/src/utils/webhook.rs: -------------------------------------------------------------------------------- 1 | use reqwest::{Client}; 2 | use anyhow::{anyhow}; 3 | use serde_json::json; 4 | use crate::init::env_variables::GLOBAL_DATA; 5 | 6 | pub async fn send_webapp_embed_ready(datasource_id: &str) -> Result<(), anyhow::Error> { 7 | let global_data = GLOBAL_DATA.read().await; 8 | let url = format!("http://{}:{}{}", global_data.webapp_host, global_data.webapp_port, "/webhook/embed-successful"); 9 | 10 | // Prepare the POST request body 11 | let body = json!({ 12 | "datasourceId": datasource_id 13 | }); 14 | 15 | // Create a client instance 16 | let client = Client::new(); 17 | 18 | // Make the POST request 19 | let res = client.post(&url) 20 | .json(&body) 21 | .send() 22 | .await?; 23 | 24 | // Check if the request was successful 25 | if res.status().is_success() { 26 | Ok(()) 27 | } else { 28 | Err(anyhow!("Failed to notify webapp. Status: {}", res.status())) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /vector-db-proxy/src/vector_databases/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod error; 2 | pub mod helpers; 3 | pub mod models; 4 | pub mod utils; 5 | pub mod vector_database; 6 | -------------------------------------------------------------------------------- /vector-db-proxy/src/vector_databases/utils.rs: -------------------------------------------------------------------------------- 1 | pub fn calculate_vector_storage_size(number_of_vectors: usize, vector_length: usize) -> f64 { 2 | (number_of_vectors * vector_length * 4) as f64 * 1.15 3 | } 4 | -------------------------------------------------------------------------------- /webapp/.dockerignore: -------------------------------------------------------------------------------- 1 | dist 2 | Dockerfile 3 | .dockerignore 4 | node_modules 5 | npm-debug.log 6 | README.md 7 | .next 8 | .git 9 | .env 10 | -------------------------------------------------------------------------------- /webapp/.eslintignore: -------------------------------------------------------------------------------- 1 | src/test/* -------------------------------------------------------------------------------- /webapp/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "ignorePatterns": [ 4 | "*.config.js", 5 | "*.config.ts", 6 | "node_modules/", 7 | "src/test/*", 8 | "src/**/*.test.ts" 9 | ], 10 | "extends": [ 11 | // "next/core-web-vitals", 12 | // "plugin:prettier/recommended" 13 | ], 14 | "parserOptions": { 15 | "ecmaVersion": "latest", 16 | "sourceType": "module", 17 | "project": "./tsconfig.json" 18 | }, 19 | "plugins": [ 20 | "simple-import-sort" 21 | ], 22 | "rules": { 23 | // "simple-import-sort/imports": "error", 24 | "react-hooks/exhaustive-deps": "off", 25 | "@next/next/no-img-element": "off", 26 | "import/no-anonymous-default-export": "off", 27 | "jsx-a11y/alt-text": "off" 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /webapp/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next/ 3 | out/ 4 | dist/ 5 | .env 6 | .env.local 7 | keyfile.json 8 | doc/ 9 | !src/lib/airbyte 10 | static/* 11 | tsconfig.tsbuildinfo 12 | coverage/ 13 | -------------------------------------------------------------------------------- /webapp/.prettierignore: -------------------------------------------------------------------------------- 1 | src/router.ts 2 | src/api.ts 3 | src/test/ 4 | src/**/*.test.ts 5 | -------------------------------------------------------------------------------- /webapp/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": true, 4 | "singleQuote": true, 5 | "semi": true, 6 | "bracketSpacing": true, 7 | "bracketSameLine": false, 8 | "jsxSingleQuote": true, 9 | "trailingComma": "none", 10 | "arrowParens": "avoid", 11 | "endOfLine": "lf", 12 | "printWidth": 100, 13 | "quoteProps": "as-needed", 14 | "jsxBracketSameLine": true 15 | } 16 | -------------------------------------------------------------------------------- /webapp/.yarn/install-state.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/.yarn/install-state.gz -------------------------------------------------------------------------------- /webapp/Dockerfile_syncserver: -------------------------------------------------------------------------------- 1 | # First Stage: Build 2 | FROM node:20 as builder 3 | 4 | WORKDIR /app 5 | 6 | # Copy package.json and package-lock.json for npm install 7 | COPY package.json package-lock.json ./ 8 | 9 | # Install dependencies 10 | RUN npm ci --only=production 11 | 12 | # Copy the rest of your application code 13 | COPY . . 14 | 15 | # ARGs for environment variables that need to be passed at build time 16 | ARG GOOGLE_KEYPATH 17 | ARG NEXT_PUBLIC_SECRET_PROVIDER 18 | ARG NEXT_PUBLIC_STORAGE_PROVIDER 19 | ARG GOOGLE_APPLICATION_CREDENTIALS 20 | 21 | # Set environment variables based on ARGs 22 | ENV NEXT_PUBLIC_SECRET_PROVIDER=$NEXT_PUBLIC_SECRET_PROVIDER 23 | ENV NEXT_PUBLIC_STORAGE_PROVIDER=$NEXT_PUBLIC_STORAGE_PROVIDER 24 | 25 | # Second Stage: Runtime 26 | FROM node:20 27 | 28 | WORKDIR /app 29 | 30 | # Set NODE_ENV environment variable 31 | ENV NODE_ENV production 32 | 33 | COPY --from=builder /app ./ 34 | 35 | # Display the environment variables to verify 36 | RUN echo "NEXT_PUBLIC_POSTHOG_KEY: ${NEXT_PUBLIC_POSTHOG_KEY}" 37 | 38 | # Expose the port the app runs on 39 | EXPOSE 3000 40 | 41 | # Command to run your app 42 | CMD ["npm", "run", "start:syncserver"] 43 | -------------------------------------------------------------------------------- /webapp/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | pm2-runtime start ecosystem.config.js 3 | -------------------------------------------------------------------------------- /webapp/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "src/pages/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "/modules/components", 15 | "utils": "/modules/lib/utils", 16 | "ui": "/modules/components/ui", 17 | "lib": "modules/lib", 18 | "hooks": "modules/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } -------------------------------------------------------------------------------- /webapp/ecosystem.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | apps: [ 3 | { 4 | script: 'npm run dev', 5 | watch: true, 6 | // watch_delay: 1000, 7 | ignore_watch: [ 8 | 'node_modules', 9 | 'src/components', 10 | 'src/pages', 11 | 'src/sync-server', 12 | 'src/context', 13 | '\\.next', 14 | '\\.dist', 15 | 'tsconfig.tsbuildinfo', 16 | '.DS_Store', 17 | 'src/test' 18 | ], 19 | env: { 20 | DEBUG: 'webapp*,-webapp:middleware:auth:*,airbyte:*', 21 | DEBUG_COLORS: true 22 | } 23 | } 24 | ] 25 | }; 26 | -------------------------------------------------------------------------------- /webapp/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/favicon.ico -------------------------------------------------------------------------------- /webapp/jest.config.js: -------------------------------------------------------------------------------- 1 | const { pathsToModuleNameMapper } = require('ts-jest'); 2 | // In the following statement, replace `./tsconfig` with the path to your `tsconfig` file 3 | // which contains the path mapping (ie the `compilerOptions.paths` option): 4 | const { compilerOptions } = require('./tsconfig.json'); 5 | 6 | module.exports = { 7 | // [...] 8 | preset: 'ts-jest', 9 | moduleDirectories: ["node_modules", "src"], 10 | moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths /*, { prefix: '/' } */ ) 11 | }; 12 | -------------------------------------------------------------------------------- /webapp/keyfile.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/keyfile.json -------------------------------------------------------------------------------- /webapp/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /webapp/next.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | images: { 3 | dangerouslyAllowSVG: true, //remove once we stop using the tailwind images 4 | remotePatterns: [ 5 | { 6 | protocol: 'https', 7 | hostname: 'tailwindui.com', 8 | port: '', 9 | pathname: '/**', 10 | }, 11 | { 12 | protocol: 'https', 13 | hostname: 'images.unsplash.com', 14 | port: '', 15 | pathname: '/**', 16 | }, 17 | ], 18 | }, 19 | webpack(config) { 20 | config.resolve.fallback = { 21 | // if you miss it, all the other options in fallback, specified 22 | // by next.js will be dropped. 23 | ...config.resolve.fallback, 24 | fs: false, 25 | path: false, 26 | net: false, 27 | child_process: false, 28 | tls: false, 29 | }; 30 | return config; 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /webapp/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /webapp/public/agentcloud-full-black-bg-trans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/agentcloud-full-black-bg-trans.png -------------------------------------------------------------------------------- /webapp/public/agentcloud-full-black-bg-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/agentcloud-full-black-bg-white.png -------------------------------------------------------------------------------- /webapp/public/agentcloud-full-white-bg-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/agentcloud-full-white-bg-black.png -------------------------------------------------------------------------------- /webapp/public/agentcloud-full-white-bg-trans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/agentcloud-full-white-bg-trans.png -------------------------------------------------------------------------------- /webapp/public/agentcloud-mark-black-bg-trans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/agentcloud-mark-black-bg-trans.png -------------------------------------------------------------------------------- /webapp/public/agentcloud-mark-black-bg-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/agentcloud-mark-black-bg-white.png -------------------------------------------------------------------------------- /webapp/public/agentcloud-mark-white-bg-black-Enterprise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/agentcloud-mark-white-bg-black-Enterprise.png -------------------------------------------------------------------------------- /webapp/public/agentcloud-mark-white-bg-black-Free.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/agentcloud-mark-white-bg-black-Free.png -------------------------------------------------------------------------------- /webapp/public/agentcloud-mark-white-bg-black-Pro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/agentcloud-mark-white-bg-black-Pro.png -------------------------------------------------------------------------------- /webapp/public/agentcloud-mark-white-bg-black-Teams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/agentcloud-mark-white-bg-black-Teams.png -------------------------------------------------------------------------------- /webapp/public/agentcloud-mark-white-bg-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/agentcloud-mark-white-bg-black.png -------------------------------------------------------------------------------- /webapp/public/agentcloud-mark-white-bg-trans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/agentcloud-mark-white-bg-trans.png -------------------------------------------------------------------------------- /webapp/public/apps/identicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/apps/identicon.png -------------------------------------------------------------------------------- /webapp/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/favicon.ico -------------------------------------------------------------------------------- /webapp/public/images/agent-cloud-introduction-RAG-google-gigquery-youtube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/agent-cloud-introduction-RAG-google-gigquery-youtube.png -------------------------------------------------------------------------------- /webapp/public/images/agentcloud-full-black-bg-trans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/agentcloud-full-black-bg-trans.png -------------------------------------------------------------------------------- /webapp/public/images/agentcloud-full-black-bg-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/agentcloud-full-black-bg-white.png -------------------------------------------------------------------------------- /webapp/public/images/agentcloud-full-white-bg-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/agentcloud-full-white-bg-black.png -------------------------------------------------------------------------------- /webapp/public/images/agentcloud-full-white-bg-trans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/agentcloud-full-white-bg-trans.png -------------------------------------------------------------------------------- /webapp/public/images/agentcloud-mark-black-bg-trans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/agentcloud-mark-black-bg-trans.png -------------------------------------------------------------------------------- /webapp/public/images/agentcloud-mark-black-bg-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/agentcloud-mark-black-bg-white.png -------------------------------------------------------------------------------- /webapp/public/images/agentcloud-mark-white-bg-black-Enterprise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/agentcloud-mark-white-bg-black-Enterprise.png -------------------------------------------------------------------------------- /webapp/public/images/agentcloud-mark-white-bg-black-Free.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/agentcloud-mark-white-bg-black-Free.png -------------------------------------------------------------------------------- /webapp/public/images/agentcloud-mark-white-bg-black-Pro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/agentcloud-mark-white-bg-black-Pro.png -------------------------------------------------------------------------------- /webapp/public/images/agentcloud-mark-white-bg-black-Teams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/agentcloud-mark-white-bg-black-Teams.png -------------------------------------------------------------------------------- /webapp/public/images/agentcloud-mark-white-bg-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/agentcloud-mark-white-bg-black.png -------------------------------------------------------------------------------- /webapp/public/images/agentcloud-mark-white-bg-trans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/agentcloud-mark-white-bg-trans.png -------------------------------------------------------------------------------- /webapp/public/images/email/logo-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/email/logo-light.png -------------------------------------------------------------------------------- /webapp/public/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/favicon.ico -------------------------------------------------------------------------------- /webapp/public/images/get-started/DemoProcessApp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/get-started/DemoProcessApp.png -------------------------------------------------------------------------------- /webapp/public/images/get-started/chat-app-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/get-started/chat-app-square.png -------------------------------------------------------------------------------- /webapp/public/images/get-started/create-chat-app-dark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/get-started/create-chat-app-dark.jpg -------------------------------------------------------------------------------- /webapp/public/images/get-started/create-chat-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/get-started/create-chat-app.png -------------------------------------------------------------------------------- /webapp/public/images/get-started/create-process-app-dark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/get-started/create-process-app-dark.jpg -------------------------------------------------------------------------------- /webapp/public/images/get-started/create-process-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/get-started/create-process-app.png -------------------------------------------------------------------------------- /webapp/public/images/get-started/process-app-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/get-started/process-app-square.png -------------------------------------------------------------------------------- /webapp/public/images/onboarding/anthropic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /webapp/public/images/onboarding/azure-openai.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /webapp/public/images/onboarding/cohere.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /webapp/public/images/vector-db/pinecone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/vector-db/pinecone.png -------------------------------------------------------------------------------- /webapp/public/images/vector-db/qdrant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/images/vector-db/qdrant.png -------------------------------------------------------------------------------- /webapp/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /webapp/public/sidebar/agents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/sidebar/agents.png -------------------------------------------------------------------------------- /webapp/public/sidebar/api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/sidebar/api.png -------------------------------------------------------------------------------- /webapp/public/sidebar/apps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/sidebar/apps.png -------------------------------------------------------------------------------- /webapp/public/sidebar/connections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/sidebar/connections.png -------------------------------------------------------------------------------- /webapp/public/sidebar/logout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/sidebar/logout.png -------------------------------------------------------------------------------- /webapp/public/sidebar/models.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/sidebar/models.png -------------------------------------------------------------------------------- /webapp/public/sidebar/organizations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/sidebar/organizations.png -------------------------------------------------------------------------------- /webapp/public/sidebar/session-history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/sidebar/session-history.png -------------------------------------------------------------------------------- /webapp/public/sidebar/team.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/sidebar/team.png -------------------------------------------------------------------------------- /webapp/public/sidebar/tools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/sidebar/tools.png -------------------------------------------------------------------------------- /webapp/public/sidebar/variables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/public/sidebar/variables.png -------------------------------------------------------------------------------- /webapp/src/components/AgentAvatar.tsx: -------------------------------------------------------------------------------- 1 | import Blockies from 'react-blockies'; 2 | import StorageProviderFactory from 'storage/index'; 3 | 4 | export default function AgentAvatar({ agent, fill = false, size = 10 }) { 5 | const storageProvider = StorageProviderFactory.getStorageProvider(); 6 | return ( 7 | 8 | {agent?.icon?.filename ? ( 9 | 13 | ) : ( 14 | agent?.name && 15 | )} 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /webapp/src/components/BackButton.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | 3 | const labelStyles = { width: 16, height: 16 }; 4 | 5 | export default function BackButton({ to }) { 6 | return ( 7 | 8 | 9 | Back 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /webapp/src/components/ClassNames.ts: -------------------------------------------------------------------------------- 1 | export default function classNames(...classes) { 2 | return classes.filter(Boolean).join(' '); 3 | } 4 | -------------------------------------------------------------------------------- /webapp/src/components/ConversationStarters.tsx: -------------------------------------------------------------------------------- 1 | import * as API from '@api'; 2 | import { useRouter } from 'next/router'; 3 | import React from 'react'; 4 | import { toast } from 'react-toastify'; 5 | 6 | const ConversationStarters = ({ session, app, sendMessage, conversationStarters }) => { 7 | const router = useRouter(); 8 | return ( 9 |
10 | {conversationStarters.map((starter, index) => ( 11 |
{ 15 | if (session != null) { 16 | sendMessage(starter); 17 | } 18 | }} 19 | > 20 | {starter} 21 |
22 | ))} 23 |
24 | ); 25 | }; 26 | 27 | export default ConversationStarters; 28 | -------------------------------------------------------------------------------- /webapp/src/components/CreateAPIKeyModal.tsx: -------------------------------------------------------------------------------- 1 | import { Separator } from 'modules/components/ui/separator'; 2 | import { Dialog, DialogContent, DialogHeader, DialogTitle } from 'modules/components/ui/dialog'; 3 | import ApiKeyForm from './apikeys/ApiKeyForm'; 4 | 5 | export default function CreateModelModal({ 6 | open, 7 | setOpen, 8 | callback 9 | }: { 10 | open: boolean; 11 | setOpen: (open: boolean) => void; 12 | callback: () => void; 13 | }) { 14 | return ( 15 | 16 | 17 | 18 | New Model 19 | 20 | 21 | 22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /webapp/src/components/DevBadge.tsx: -------------------------------------------------------------------------------- 1 | import { ClipboardDocumentIcon } from '@heroicons/react/24/outline'; 2 | import { useDeveloperContext } from 'context/developer'; 3 | import { toast } from 'react-toastify'; 4 | 5 | interface DevBadgeProps { 6 | label?: string; 7 | value: string; 8 | } 9 | 10 | export default function DevBadge({ label = 'ID', value }: DevBadgeProps) { 11 | const { developerMode } = useDeveloperContext(); 12 | if (!developerMode) { 13 | return null; 14 | } 15 | return ( 16 | { 18 | e.preventDefault(); 19 | e.stopPropagation(); 20 | await navigator.clipboard.writeText(value); 21 | toast.success('Copied to clipboard'); 22 | return false; 23 | }} 24 | className='hover:bg-blue-200 transition-all cursor-pointer whitespace-nowrap h-6 px-2 py-[0.5px] border text-sm rounded-lg bg-blue-100 text-blue-800 border-blue-300' 25 | > 26 | {label}: {value} 27 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /webapp/src/components/ErrorAlert.tsx: -------------------------------------------------------------------------------- 1 | import { XCircleIcon } from '@heroicons/react/20/solid'; 2 | 3 | interface ErrorAlertProps { 4 | error: string; 5 | } 6 | 7 | export default function ErrorAlert({ error }: ErrorAlertProps) { 8 | return ( 9 | error && ( 10 |
11 |
12 |
13 |
15 |
16 |

{error}

17 |
18 |
19 |
20 | ) 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /webapp/src/components/FormatDatasourceOptionLabel.tsx: -------------------------------------------------------------------------------- 1 | export default function formatDatasourceOptionLabel(data: any) { 2 | return ( 3 |
  • 8 | 9 | {data.sourceType && ( 10 | 15 | )} 16 | {data.label} 17 | 18 |
  • 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /webapp/src/components/FormatModelOptionLabel.tsx: -------------------------------------------------------------------------------- 1 | export default function formatModelOptionLabel(data) { 2 | return ( 3 |
  • 8 | {data.label} {data?.model ? `(${data?.model})` : null} 9 |
  • 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /webapp/src/components/FunctionCard.tsx: -------------------------------------------------------------------------------- 1 | import dynamic from 'next/dynamic'; 2 | // @ts-ignore 3 | const Markdown = dynamic(() => import('react-markdown'), { 4 | loading: () =>

    Loading...

    , 5 | ssr: false 6 | }); 7 | 8 | export default function FunctionCard({ 9 | name, 10 | description, 11 | parameters, 12 | onClickFunction, 13 | highlighted 14 | }) { 15 | // Filter out parameters starting with __ 16 | const paramNames = Object.keys(parameters).filter(p => !p.startsWith('__')); 17 | 18 | // TODO: not this 19 | const updatedDescription = description.substring(0, description.indexOf('The __path') - 3); 20 | 21 | return ( 22 |
    26 |

    {name}

    27 |
    28 | {updatedDescription} 29 |
    30 | {/*
      31 | {paramNames.map(param => ( 32 |
    • {param}
    • 33 | ))} 34 |
    */} 35 |
    36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /webapp/src/components/InfoAlert.tsx: -------------------------------------------------------------------------------- 1 | import { InformationCircleIcon } from '@heroicons/react/20/solid'; 2 | 3 | export default function InfoAlert({ 4 | message, 5 | textColor = 'blue', 6 | className = null, 7 | children = null 8 | }: { 9 | message: any; 10 | textColor?: string; 11 | className?: string; 12 | children?: any; 13 | }) { 14 | return ( 15 | message && ( 16 |
    17 |
    18 |
    19 |
    21 |
    22 |

    {message}

    23 |
    24 |
    25 | {children} 26 |
    27 | ) 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /webapp/src/components/LoadingBar.tsx: -------------------------------------------------------------------------------- 1 | import ButtonSpinner from 'components/ButtonSpinner'; 2 | import React from 'react'; 3 | 4 | interface LoadingBarProps { 5 | total?: number; 6 | success?: number; 7 | failure?: number; 8 | text?: string; 9 | } 10 | 11 | const LoadingBar: React.FC = function ({ 12 | total = null, 13 | success = 0, 14 | failure = 0, 15 | text = 'Embedding' 16 | }) { 17 | const successPercentage = (total != null ? (success / total) * 100 : 0) || 0; 18 | const failurePercentage = (total != null ? (failure / total) * 100 : 0) || 0; 19 | return ( 20 |
    21 |
    22 | {text} ({successPercentage.toFixed(1)}%) 23 | 24 |
    25 |
    26 | 27 | 31 |
    32 |
    33 | ); 34 | }; 35 | 36 | export default LoadingBar; 37 | -------------------------------------------------------------------------------- /webapp/src/components/LoadingPlaceholder.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ContentLoader from 'react-content-loader'; 3 | 4 | const LoadingPlaceholder = props => ( 5 | 14 | 15 | 16 | ); 17 | 18 | export default LoadingPlaceholder; 19 | -------------------------------------------------------------------------------- /webapp/src/components/NewButtonSection.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | import React from 'react'; 3 | 4 | export default function NewButtonSection({ 5 | emptyMessage, 6 | icon, 7 | message, 8 | buttonIcon, 9 | buttonMessage, 10 | setOpen, 11 | disabled, 12 | link 13 | }: { 14 | emptyMessage: string; 15 | icon?: any; 16 | message: string; 17 | buttonIcon: any; 18 | buttonMessage: string; 19 | disabled?: boolean; 20 | setOpen?: (open: boolean) => void; 21 | link?: string; 22 | }) { 23 | return ( 24 |
    25 | {icon} 26 |

    {emptyMessage}

    27 |

    {message}

    28 |
    29 | 37 |
    38 |
    39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /webapp/src/components/SearchFilter.tsx: -------------------------------------------------------------------------------- 1 | export default function SearchFilter({ filter, setFilter }) { 2 | return ( 3 |
    4 |
    5 | 6 | 7 | 8 |
    9 | setFilter(e.target.value || '')} 11 | type='text' 12 | className='form-control' 13 | placeholder='Search' 14 | /> 15 |
    16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /webapp/src/components/SharingModeInfoAlert.tsx: -------------------------------------------------------------------------------- 1 | import CopyToClipboardInput from 'components/CopyToClipboardInput'; 2 | import InfoAlert from 'components/InfoAlert'; 3 | import { useRouter } from 'next/router'; 4 | import React from 'react'; 5 | 6 | export default function SharingModeInfoAlert({ 7 | shareLinkShareId, 8 | message = 'Public apps can be accessed by anyone, potentially incurring token costs.', 9 | classNames = 'rounded bg-yellow-200 p-4 -mt-3 sm:col-span-12' 10 | }) { 11 | const origin = typeof location !== 'undefined' ? location.origin : ''; 12 | const router = useRouter(); 13 | const { resourceSlug } = router.query; 14 | return ( 15 | 16 | 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /webapp/src/components/Spinner.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface SpinnerProps { 4 | loadingText?: string; 5 | color?: string; 6 | } 7 | 8 | const Spinner: React.FC = function ({ loadingText, color = 'white' }) { 9 | const spinnerClasses = `w-16 h-16 rounded-full animate-spin border-4 border-solid border-${color}-500 border-t-transparent`; 10 | 11 | return ( 12 |
    13 |
    14 |
    15 | {loadingText} 16 |
    17 | ); 18 | }; 19 | 20 | export default Spinner; 21 | -------------------------------------------------------------------------------- /webapp/src/components/StripePricingTable.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | 3 | const StripePricingTable = ({}) => { 4 | useEffect(() => { 5 | const script = document.createElement('script'); 6 | script.src = 'https://js.stripe.com/v3/pricing-table.js'; 7 | script.async = true; 8 | document.body.appendChild(script); 9 | 10 | return () => { 11 | document.body.removeChild(script); 12 | }; 13 | }, []); 14 | 15 | return React.createElement('stripe-pricing-table', { 16 | 'pricing-table-id': process.env.NEXT_PUBLIC_STRIPE_PRICING_TABLE_ID, 17 | 'publishable-key': process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY 18 | }); 19 | }; 20 | 21 | export default StripePricingTable; 22 | -------------------------------------------------------------------------------- /webapp/src/components/SuccessAlert.tsx: -------------------------------------------------------------------------------- 1 | import { CheckCircleIcon } from '@heroicons/react/20/solid'; 2 | 3 | export default function SuccessAlert({ message }) { 4 | return ( 5 | message && ( 6 |
    7 |
    8 |
    9 |
    11 |
    12 |

    {message}

    13 |
    14 |
    15 |
    16 | ) 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /webapp/src/components/ToolSelectIcons.tsx: -------------------------------------------------------------------------------- 1 | import { CircleStackIcon, CodeBracketIcon } from '@heroicons/react/20/solid'; 2 | import { ToolType } from 'struct/tool'; 3 | 4 | const ToolSelectIcons = { 5 | [ToolType.FUNCTION_TOOL]: , 6 | [ToolType.RAG_TOOL]: 7 | }; 8 | 9 | export default ToolSelectIcons; 10 | -------------------------------------------------------------------------------- /webapp/src/components/ToolStateBadge.tsx: -------------------------------------------------------------------------------- 1 | import ButtonSpinner from 'components/ButtonSpinner'; 2 | import React from 'react'; 3 | import { ToolState } from 'struct/tool'; 4 | 5 | const toolStateColors = { 6 | [ToolState.PENDING]: 'bg-blue-200 text-blue-800 border-blue-800', 7 | [ToolState.READY]: 'bg-green-200 text-green-800 border-green-800', 8 | [ToolState.ERROR]: 'bg-red-200 text-red-800 border-red-800' 9 | }; 10 | 11 | export default function ToolStateBadge({ state }) { 12 | return ( 13 | 16 | {state === ToolState.PENDING && ( 17 | 18 | )} 19 | {state} 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /webapp/src/components/apps/CreateChatAppMain.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function CreateChatAppMain() { 4 | return
    CreateChatAppMain
    ; 5 | } 6 | -------------------------------------------------------------------------------- /webapp/src/components/apps/tool.ts: -------------------------------------------------------------------------------- 1 | import { toolsData } from 'data/apps'; 2 | import { create } from 'zustand'; 3 | 4 | type Tag = { 5 | name: string; 6 | textColor: string; 7 | backgroundColor: string; 8 | }; 9 | 10 | type Tool = { 11 | value: string; 12 | title: string; 13 | label: string; 14 | description: string; 15 | isInstalled: boolean; 16 | tags: Tag[]; 17 | }; 18 | 19 | type ToolStore = { 20 | tools: Tool[]; 21 | setTools: (tools: Tool[]) => void; 22 | addTool: () => void; 23 | }; 24 | 25 | export const useToolStore = create(set => ({ 26 | tools: [...toolsData] as Tool[], 27 | setTools: tools => set({ tools }), 28 | addTool: () => {} 29 | })); 30 | -------------------------------------------------------------------------------- /webapp/src/components/apps/toolsdialogscreens/Custom.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const Custom = () => { 4 | return
    Custom
    ; 5 | }; 6 | -------------------------------------------------------------------------------- /webapp/src/components/chat/ChatRestartMessage.tsx: -------------------------------------------------------------------------------- 1 | import * as API from '@api'; 2 | import { useChatContext } from 'context/chat'; 3 | import { useRouter } from 'next/router'; 4 | import { usePostHog } from 'posthog-js/react'; 5 | import { toast } from 'react-toastify'; 6 | 7 | export default function ChatRestartMessage() { 8 | const [chatContext]: any = useChatContext(); 9 | const router = useRouter(); 10 | const posthog = usePostHog(); 11 | const resourceSlug = router?.query?.resourceSlug || chatContext?.account?.currentTeam; 12 | 13 | const restartSession = () => { 14 | posthog.capture('restartSession', { 15 | appId: chatContext?.app._id, 16 | appType: chatContext?.app.type, 17 | appName: chatContext?.app.name 18 | }); 19 | API.addSession( 20 | { 21 | _csrf: chatContext?.csrf, 22 | resourceSlug, 23 | id: chatContext?.app?._id 24 | }, 25 | null, 26 | toast.error, 27 | router 28 | ); 29 | }; 30 | 31 | return ( 32 |
    33 | 34 | ⛔ Conversation max limit reached, click{' '} 35 | {' '} 38 | to restart the chat{' '} 39 | 40 |
    41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /webapp/src/components/connections/DataSourceSearch.tsx: -------------------------------------------------------------------------------- 1 | import { MagnifyingGlassIcon } from '@heroicons/react/24/outline'; 2 | import React from 'react'; 3 | import { useDatasourceStore } from 'store/datasource'; 4 | import { useShallow } from 'zustand/react/shallow'; 5 | 6 | const DataSourceSearch = () => { 7 | const { searchInput, setSearchInput } = useDatasourceStore( 8 | useShallow(state => ({ 9 | searchInput: state.searchInput, 10 | setSearchInput: state.setSearchInput 11 | })) 12 | ); 13 | 14 | return ( 15 |
    16 | 17 | setSearchInput(e.target.value)} 23 | /> 24 |
    25 | ); 26 | }; 27 | 28 | export default DataSourceSearch; 29 | -------------------------------------------------------------------------------- /webapp/src/components/onboarding/DataSourceSearch.tsx: -------------------------------------------------------------------------------- 1 | import { MagnifyingGlassIcon } from '@heroicons/react/24/outline'; 2 | import React from 'react'; 3 | 4 | const DataSourceSearch = ({ 5 | searchInput, 6 | setSearchInput 7 | }: { 8 | searchInput: string; 9 | setSearchInput: Function; 10 | }) => { 11 | return ( 12 |
    13 | 14 | setSearchInput(e.target.value)} 20 | /> 21 |
    22 | ); 23 | }; 24 | 25 | export default DataSourceSearch; 26 | -------------------------------------------------------------------------------- /webapp/src/components/shared/ToolTip.tsx: -------------------------------------------------------------------------------- 1 | import Tippy, { TippyProps } from '@tippyjs/react'; 2 | import parse, { DOMNode } from 'html-react-parser'; 3 | import React, { DOMElement } from 'react'; 4 | 5 | const modifyAnchorTags = (node: DOMNode) => { 6 | if (node.type === 'tag' && node.name === 'a') { 7 | node.attribs = { 8 | ...node.attribs, 9 | target: '_blank', 10 | rel: 'noopener noreferrer', 11 | className: 'cursor-pointer hover:underline hover:text-blue-500 text-blue-300' 12 | }; 13 | } 14 | return node; 15 | }; 16 | 17 | interface ToolTipProps extends TippyProps { 18 | content: string; 19 | } 20 | 21 | const ToolTip = ({ content, children, ...props }: ToolTipProps) => { 22 | const modifiedContent = parse(content, { 23 | replace: modifyAnchorTags 24 | }); 25 | 26 | return ( 27 | 28 | {children} 29 | 30 | ); 31 | }; 32 | 33 | export default ToolTip; 34 | -------------------------------------------------------------------------------- /webapp/src/components/tools/tools-stepper-create/stepper-create-dependencies.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function StepperCreateDependencies() { 4 | return
    StepperCreateDependencies
    ; 5 | } 6 | -------------------------------------------------------------------------------- /webapp/src/components/tools/tools-stepper-create/stepper-create-parameters.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function StepperCreateParameters() { 4 | return
    StepperCreateParameters
    ; 5 | } 6 | -------------------------------------------------------------------------------- /webapp/src/components/tools/tools-stepper-create/stepper-create-source.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Select, 3 | SelectContent, 4 | SelectItem, 5 | SelectTrigger, 6 | SelectValue 7 | } from 'modules/components/ui/select'; 8 | import React from 'react'; 9 | 10 | export default function StepperCreateSource() { 11 | return ( 12 |
    13 |
    14 |

    Main Python Code

    15 |

    16 | Write the core logic of your custom tool here. This script will handle the main operations 17 | and data processing. 18 |

    19 |
    20 |
    21 |

    Runtime

    22 | 30 |
    31 |
    32 |
    33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /webapp/src/components/tools/tools-stepper-edit/stepper-edit-dependencies.tsx: -------------------------------------------------------------------------------- 1 | export function StepperEditDependencies() { 2 | return ( 3 |
    4 |
    5 |

    Dependencies

    6 |
    7 |
    8 |

    Requirements.txt

    9 |
    10 |
    11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /webapp/src/components/tools/tools-stepper-edit/stepper-edit-source.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Select, 3 | SelectContent, 4 | SelectItem, 5 | SelectTrigger, 6 | SelectValue 7 | } from 'modules/components/ui/select'; 8 | import React from 'react'; 9 | 10 | export function StepperEditSource() { 11 | return ( 12 |
    13 |
    14 |

    Main Python Code

    15 |

    16 | Write the core logic of your custom tool here. This script will handle the main operations 17 | and data processing. 18 |

    19 |
    20 |
    21 |

    Runtime

    22 | 30 |
    31 |
    32 |
    33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /webapp/src/components/tools/tools-stepper-edit/stepper-edit-version-history.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const StepperEditVersionHistory = () => { 4 | return
    StepperEditVersionHistory
    ; 5 | }; 6 | -------------------------------------------------------------------------------- /webapp/src/components/tools/toolsdialogscreens/Custom.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { useRouter } from 'next/router'; 3 | import { useAccountContext } from 'context/account'; 4 | import { toast } from 'react-toastify'; 5 | import * as API from '@api'; 6 | import ToolForm from 'components/tools/ToolForm'; 7 | import { ToolType } from 'struct/tool'; 8 | 9 | export function Custom({ fetchTools, setDisplayScreen, setActiveTab }) { 10 | const [accountContext]: any = useAccountContext(); 11 | const { csrf } = accountContext; 12 | const router = useRouter(); 13 | const { resourceSlug } = router.query; 14 | const [submitting, setSubmitting] = useState(false); 15 | 16 | return ( 17 |
    18 |

    Create Custom Tool

    19 | 27 |
    28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /webapp/src/context/connectorform.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { FormProvider, useForm } from 'react-hook-form'; 3 | 4 | const FormContext = ({ children, schema }: { children: React.ReactNode; schema?: any }) => { 5 | const methods = useForm(); 6 | 7 | useEffect(() => { 8 | if (schema && schema.properties) { 9 | methods.reset(); 10 | } 11 | }, [schema]); 12 | 13 | return {children}; 14 | }; 15 | 16 | export default FormContext; 17 | 18 | import { useFormContext } from 'react-hook-form'; 19 | 20 | export const useFormContextHook = () => { 21 | const context = useFormContext(); 22 | if (!context) { 23 | throw new Error('useFormContextHook must be used within a FormProvider'); 24 | } 25 | return context; 26 | }; 27 | -------------------------------------------------------------------------------- /webapp/src/context/developer.tsx: -------------------------------------------------------------------------------- 1 | import React, { Context, createContext, useContext, useEffect, useState } from 'react'; 2 | 3 | export type DeveloperContextProps = { 4 | developerMode: boolean; 5 | toggleDeveloperMode: Function; 6 | }; 7 | 8 | const DeveloperContext: Context = createContext(null); 9 | 10 | export function DeveloperWrapper({ children }) { 11 | const [developerMode, setDeveloperMode] = useState(false); 12 | 13 | useEffect(() => { 14 | const isDeveloper = localStorage.getItem('developer') === '1'; 15 | setDeveloperMode(isDeveloper); 16 | }, []); 17 | 18 | const toggleDeveloperMode = () => { 19 | const isDeveloper = localStorage.getItem('developer') === '1'; 20 | localStorage.setItem('developer', isDeveloper === true ? '0' : '1'); 21 | setDeveloperMode(!isDeveloper); 22 | }; 23 | 24 | return ( 25 | 26 | {children} 27 | 28 | ); 29 | } 30 | 31 | export const useDeveloperContext = () => useContext(DeveloperContext); 32 | -------------------------------------------------------------------------------- /webapp/src/context/onboardingform.tsx: -------------------------------------------------------------------------------- 1 | import { FieldValue, FieldValues, FormProvider, useForm } from 'react-hook-form'; 2 | 3 | const OnboardingFormContext = ({ children }: { children: React.ReactNode }) => { 4 | const methods = useForm(); 5 | 6 | return {children}; 7 | }; 8 | 9 | export default OnboardingFormContext; 10 | 11 | import { useFormContext } from 'react-hook-form'; 12 | 13 | export const useOnboardingFormContext = () => { 14 | // Updated to extend Record 15 | const context = useFormContext(); 16 | if (!context) { 17 | // ... existing code ... 18 | } 19 | return context; 20 | }; 21 | -------------------------------------------------------------------------------- /webapp/src/context/stepwrapper.tsx: -------------------------------------------------------------------------------- 1 | import { useRouter } from 'next/router'; 2 | import React, { 3 | createContext, 4 | Dispatch, 5 | SetStateAction, 6 | useContext, 7 | useEffect, 8 | useState 9 | } from 'react'; 10 | 11 | interface StepContext { 12 | step: number; 13 | setStep?: React.Dispatch>; 14 | } 15 | 16 | const StepContext = createContext({ 17 | step: 0, 18 | setStep: () => {} 19 | }); 20 | 21 | export function StepWrapper({ children }) { 22 | const router = useRouter(); 23 | const { resourceSlug } = router.query; 24 | const [step, setStep] = useState(0); 25 | const isAppAdd = router?.asPath?.includes('/app/add'); 26 | 27 | useEffect(() => { 28 | if (isAppAdd) { 29 | const hashStep = parseInt(window.location.hash.replace('#step', ''), 10); 30 | if (hashStep) { 31 | setStep(hashStep - 1); 32 | } 33 | } 34 | }, []); 35 | 36 | useEffect(() => { 37 | if (isAppAdd) { 38 | window.location.hash = `#step${step + 1}`; 39 | } 40 | }, [step, isAppAdd]); 41 | 42 | useEffect(() => { 43 | if (!isAppAdd) { 44 | setStep(0); 45 | } 46 | }, [router?.asPath]); 47 | 48 | return {children}; 49 | } 50 | 51 | export function useStepContext() { 52 | return useContext(StepContext); 53 | } 54 | -------------------------------------------------------------------------------- /webapp/src/controllers/notification.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { dynamicResponse } from '@dr'; 4 | import { getNotificationsByTeam, markNotificationsSeen } from 'db/notification'; 5 | import toObjectId from 'misc/toobjectid'; 6 | 7 | export async function notificationsData(req, res, _next) { 8 | const notifications = await getNotificationsByTeam(req.params.resourceSlug); 9 | // const notifications = await getAllNotificationsByTeam(req.params.resourceSlug); 10 | return { 11 | csrf: req.csrfToken(), 12 | notifications 13 | }; 14 | } 15 | 16 | /** 17 | * GET /notifications.json 18 | * team tools json data 19 | */ 20 | export async function notificationsJson(req, res, next) { 21 | const data = await notificationsData(req, res, next); 22 | return res.json({ ...data, account: res.locals.account }); 23 | } 24 | 25 | export async function markNotificationsSeenApi(req, res, next) { 26 | const { notificationIds } = req.body; 27 | 28 | const notificationMongoIds = 29 | notificationIds && 30 | notificationIds.filter(ni => typeof ni === 'string').map(ni => toObjectId(ni)); 31 | if (!notificationIds) { 32 | return dynamicResponse(req, res, 200, {}); //error or? 33 | } 34 | 35 | await markNotificationsSeen(req.params.resourceSlug, notificationMongoIds); 36 | 37 | return dynamicResponse(req, res, 200, {}); 38 | } 39 | -------------------------------------------------------------------------------- /webapp/src/db/auditlog.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as db from 'db/index'; 4 | import toObjectId from 'misc/toobjectid'; 5 | import { InsertOneResult } from 'mongodb'; 6 | import { Log } from 'struct/auditlog'; // Adjust the import path as necessary 7 | 8 | // Function to get the logs collection 9 | export function LogsCollection() { 10 | return db.db().collection('logs'); 11 | } 12 | 13 | // Function to add a new log entry 14 | export async function addLog(logEntry: Log): Promise> { 15 | return LogsCollection().insertOne(logEntry); 16 | } 17 | 18 | // Function to retrieve a log by its ID 19 | export async function getLogById(logId: db.IdOrStr): Promise { 20 | return LogsCollection().findOne({ 21 | _id: toObjectId(logId) 22 | }); 23 | } 24 | 25 | // Function to delete a log by its ID 26 | export async function deleteLogById(logId: db.IdOrStr): Promise { 27 | const result = await LogsCollection().deleteOne({ 28 | _id: toObjectId(logId) 29 | }); 30 | return result.deletedCount > 0; 31 | } 32 | -------------------------------------------------------------------------------- /webapp/src/db/index.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import debug from 'debug'; 4 | const log = debug('webapp:db'); 5 | import { MongoClient, ObjectId } from 'mongodb'; 6 | import mongoose from 'mongoose'; 7 | 8 | export type IdOrStr = string | ObjectId; 9 | 10 | let _client: MongoClient | null = null; 11 | 12 | export async function connect() { 13 | if (!_client) { 14 | _client = new MongoClient(process.env.DB_URL, { 15 | maxPoolSize: 10 16 | }); 17 | log('connecting to mongodb'); 18 | await _client.connect(); 19 | } else { 20 | log('mongodb connection already established'); 21 | } 22 | } 23 | 24 | export function client(): MongoClient { 25 | return _client; 26 | } 27 | 28 | export function db() { 29 | return client() && client().db(); 30 | } 31 | 32 | export async function connectMongooseDB() { 33 | try { 34 | await mongoose.connect(process.env.DB_URL); 35 | log('Mongoose connected successfully'); 36 | } catch (error) { 37 | log('Mongoose connection error:', error); 38 | process.exit(1); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /webapp/src/db/portallink.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as db from 'db/index'; 4 | import { ObjectId } from 'mongodb'; 5 | import { InsertResult } from 'struct/db'; 6 | 7 | import toObjectId from '../lib/misc/toobjectid'; 8 | 9 | export type PortalLink = { 10 | _id?: ObjectId; 11 | accountId?: ObjectId; 12 | portalLinkId: string; 13 | url: string; 14 | createdDate: Date; 15 | payload: any; 16 | }; 17 | 18 | export function PortalLinkCollection(): any { 19 | return db.db().collection('portallinks'); 20 | } 21 | 22 | export function getPortalLinkById( 23 | accountId: db.IdOrStr, 24 | portalLinkId: string 25 | ): Promise { 26 | return PortalLinkCollection().findOne({ 27 | portalLinkId: portalLinkId, 28 | accountId: toObjectId(accountId) 29 | }); 30 | } 31 | 32 | export function unsafeGetPortalLinkById(portalLinkId: string): Promise { 33 | return PortalLinkCollection().findOne({ 34 | portalLinkId: portalLinkId 35 | }); 36 | } 37 | 38 | export async function addPortalLink(portalLink: PortalLink): Promise { 39 | return PortalLinkCollection().insertOne(portalLink); 40 | } 41 | 42 | export function deletePortalLinkById(accountId: db.IdOrStr, portalLinkId: string): Promise { 43 | return PortalLinkCollection().deleteOne({ 44 | portalLinkId: portalLinkId, 45 | accountId: toObjectId(accountId) 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /webapp/src/db/vectordb.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as db from 'db/index'; 4 | import toObjectId from 'misc/toobjectid'; 5 | import { UpdateResult } from 'mongodb'; 6 | import { VectorDb, VectorDbModel } from 'struct/vectordb'; 7 | 8 | export type GetVectorDb = ReturnType; 9 | 10 | export function getVectorDbById(vectorDbId: db.IdOrStr) { 11 | return VectorDbModel.findOne({ 12 | _id: toObjectId(vectorDbId) 13 | }); 14 | } 15 | 16 | export type GetVectorDbsByTeam = ReturnType; 17 | 18 | export async function getVectorDbsByTeam(teamId: db.IdOrStr) { 19 | return VectorDbModel.find({ 20 | teamId: toObjectId(teamId) 21 | }); 22 | } 23 | 24 | export async function addVectorDb(vectorDb: VectorDb) { 25 | return VectorDbModel.create(vectorDb); 26 | } 27 | 28 | export async function updateVectorDb( 29 | vectorDbId: db.IdOrStr, 30 | vectorDb: Partial 31 | ): Promise { 32 | return VectorDbModel.updateOne( 33 | { 34 | _id: toObjectId(vectorDbId) 35 | }, 36 | { 37 | ...vectorDb 38 | } 39 | ); 40 | } 41 | 42 | export function deleteVectorDbById(vectorDbId: db.IdOrStr) { 43 | return VectorDbModel.deleteOne({ 44 | _id: toObjectId(vectorDbId) 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /webapp/src/db/verification.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { randomBytes } from 'crypto'; 4 | import * as db from 'db/index'; 5 | import { ObjectId } from 'mongodb'; 6 | 7 | import toObjectId from '../lib/misc/toobjectid'; 8 | 9 | export enum VerificationTypes { 10 | VERIFY_EMAIL = 'email', 11 | CHANGE_PASSWORD = 'change_password', 12 | TEAM_INVITE = 'team_invite' 13 | } 14 | 15 | export type VerificationType = VerificationTypes; 16 | 17 | export type Verification = { 18 | _id?: ObjectId; 19 | token: string; 20 | accountId: ObjectId; 21 | type: VerificationType; 22 | }; 23 | 24 | export function VerificationCollection(): any { 25 | return db.db().collection('verifications'); 26 | } 27 | 28 | export async function addVerification( 29 | accountId: db.IdOrStr, 30 | type: VerificationType 31 | ): Promise { 32 | const randomBytesHex: string = await randomBytes(64).toString('hex'); 33 | await VerificationCollection().insertOne({ 34 | token: randomBytesHex, 35 | accountId: toObjectId(accountId), 36 | type 37 | }); 38 | return randomBytesHex; 39 | } 40 | 41 | export function getAndDeleteVerification(token: string, type: VerificationType): Promise { 42 | // Note: findOneAndDelete to be atomic, prevent double use 43 | return VerificationCollection().findOneAndDelete({ 44 | token, 45 | type 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /webapp/src/hooks/useResponsive.ts: -------------------------------------------------------------------------------- 1 | import { useMediaQuery } from 'react-responsive'; 2 | 3 | const useResponsive = () => { 4 | const isDesktop = useMediaQuery({ minWidth: 1024 }); 5 | const isTablet = useMediaQuery({ minWidth: 768 }); 6 | const isMobile = useMediaQuery({ maxWidth: 768 }); 7 | 8 | return { 9 | isDesktop, 10 | isTablet, 11 | isMobile 12 | }; 13 | }; 14 | 15 | export default useResponsive; 16 | -------------------------------------------------------------------------------- /webapp/src/lib/airbyte/format-duration.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Converts an ISO 8601 duration string to a human-readable format. 3 | * @param {string} duration - The ISO 8601 duration string (e.g., "PT57S"). 4 | * @returns {string} - A human-readable duration string (e.g., "57 seconds"). 5 | */ 6 | export function formatDuration(duration) { 7 | const regex = /PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?/; 8 | const matches = duration.match(regex); 9 | 10 | if (!matches) { 11 | return 'Invalid duration'; 12 | } 13 | 14 | const hours = matches[1] ? `${matches[1]} hour${matches[1] > 1 ? 's' : ''}` : ''; 15 | const minutes = matches[2] ? `${matches[2]} minute${matches[2] > 1 ? 's' : ''}` : ''; 16 | const seconds = matches[3] ? `${matches[3]} second${matches[3] > 1 ? 's' : ''}` : ''; 17 | 18 | // Combine the parts and filter out any empty strings 19 | const parts = [hours, minutes, seconds].filter(Boolean); 20 | return parts.join(', ') || '0 seconds'; 21 | } 22 | -------------------------------------------------------------------------------- /webapp/src/lib/airbyte/getconnectors.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import getAirbyteInternalApi from './internal'; 4 | 5 | //TODO: can we download this json or will it change? Will it break things? 6 | export default async function getConnectors() { 7 | return fetch( 8 | 'https://connectors.airbyte.com/files/generated_reports/connector_registry_report.json' 9 | ).then(res => res.json()); 10 | } 11 | 12 | export async function getConnectorSpecification(sourceDefinitionId: string) { 13 | const internalApi = await getAirbyteInternalApi(); 14 | const getSourceDefinitionSpecificationBody = { 15 | workspaceId: process.env.AIRBYTE_ADMIN_WORKSPACE_ID, 16 | sourceDefinitionId: sourceDefinitionId 17 | }; 18 | const sourceDefinitionRes = await internalApi 19 | .getSourceDefinitionSpecification(null, getSourceDefinitionSpecificationBody) 20 | .then(res => res.data); 21 | if (sourceDefinitionRes.connectionSpecification) { 22 | sourceDefinitionRes.connectionSpecification.$schema = 'http://json-schema.org/draft-07/schema#'; 23 | } 24 | return { 25 | schema: sourceDefinitionRes 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /webapp/src/lib/airbyte/internal.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import fs from 'fs'; 4 | import yaml from 'js-yaml'; 5 | import { Document, OpenAPIClientAxios } from 'openapi-client-axios'; 6 | 7 | import { getAirbyteAuthToken } from './api'; 8 | 9 | // Read the YAML file synchronously and load it into configYaml 10 | const configYaml = fs.readFileSync(__dirname + '/definition-internal.yaml', 'utf8'); 11 | // Load the YAML content into a definition object 12 | const definition = yaml.load(configYaml) as Document; 13 | 14 | let client; 15 | async function getAirbyteInternalApi() { 16 | if (client) { 17 | return client; 18 | } 19 | const api = new OpenAPIClientAxios({ 20 | definition, 21 | // axiosConfigDefaults: { 22 | // headers: { 23 | // authorization: `Bearer ${await getAirbyteAuthToken()}` 24 | // } 25 | // } 26 | }); 27 | client = await api.init(); 28 | if (process.env.AIRBYTE_API_URL !== 'https://cloud.airbyte.com') { 29 | client.defaults.baseURL = `${process.env.AIRBYTE_API_URL}/api`; 30 | } 31 | return client; 32 | } 33 | 34 | export default getAirbyteInternalApi; 35 | -------------------------------------------------------------------------------- /webapp/src/lib/auditlog.ts: -------------------------------------------------------------------------------- 1 | import { ObjectId } from 'mongodb'; 2 | 3 | export type Log = { 4 | orgId?: ObjectId | string; 5 | teamId?: ObjectId | string; 6 | userId: ObjectId | string; 7 | objectIds?: ObjectId[] | string[]; 8 | startTime: Date; 9 | functionName: string; 10 | arguments: any; 11 | }; 12 | -------------------------------------------------------------------------------- /webapp/src/lib/commit.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { execSync } from 'child_process'; 4 | 5 | export function getShortCommitHash() { 6 | /* "but my bootcamp said you cant use synchronous methods!!" 7 | https://i.kym-cdn.com/photos/images/original/001/937/655/73c.jpg */ 8 | return execSync('git rev-parse --short HEAD').toString().trim(); 9 | } 10 | -------------------------------------------------------------------------------- /webapp/src/lib/function/base.ts: -------------------------------------------------------------------------------- 1 | export const indent = (x, count = 1) => 2 | x 3 | .split(/\r?\n/) 4 | .map(l => `${'\t'.repeat(count)}${l}`) 5 | .join('\n'); 6 | 7 | // https://cloud.google.com/functions/docs/create-deploy-http-python#create_your_function 8 | export const WrapToolCode = x => `import functions_framework 9 | 10 | @functions_framework.http 11 | def hello_http(request): 12 | args = request.get_json(silent=True) 13 | ${indent(x, 1)} 14 | `; 15 | 16 | // https://cloud.google.com/functions/docs/create-deploy-http-python#specify_dependencies 17 | export const StandardRequirements = ['functions-framework==3.*']; 18 | -------------------------------------------------------------------------------- /webapp/src/lib/function/index.ts: -------------------------------------------------------------------------------- 1 | import GoogleFunctionProvider from 'function/google'; 2 | import LocalFunctionProvider from 'function/local'; 3 | 4 | export default class FunctionProviderFactory { 5 | static getFunctionProvider() { 6 | switch ((process.env.FUNCTION_PROVIDER || '').toLowerCase()) { 7 | case 'google': 8 | return GoogleFunctionProvider; 9 | case 'local': 10 | return LocalFunctionProvider; 11 | default: 12 | console.error('Invalid FUNCTION_PROVIDER env value:', process.env.FUNCTION_PROVIDER); 13 | process.exit(1); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /webapp/src/lib/function/local.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | import FunctionProvider from './provider'; 3 | 4 | class LocalFunctionProvider extends FunctionProvider { 5 | async init() { 6 | return; 7 | } 8 | 9 | async getFunctionLogs(functionId: string, limit = 100): Promise { 10 | return ''; 11 | } 12 | 13 | async deployFunction({ 14 | id, 15 | code, 16 | requirements, 17 | runtime = 'python310', 18 | environmentVariables = {} 19 | }): Promise { 20 | return ''; 21 | } 22 | 23 | async deleteFunction(functionId: string) { 24 | return; 25 | } 26 | 27 | async getFunctionState(functionId: string): Promise { 28 | return ''; 29 | } 30 | 31 | async waitForFunctionToBeActive(functionId: string, maxWaitTime = 120000): Promise { 32 | return false; 33 | } 34 | 35 | async callFunction(functionName: string, body: object) { 36 | return {}; 37 | } 38 | } 39 | 40 | export default new LocalFunctionProvider(); 41 | -------------------------------------------------------------------------------- /webapp/src/lib/function/provider.ts: -------------------------------------------------------------------------------- 1 | import { IdOrStr } from 'db/index'; 2 | import { DeployFunctionArgs } from 'struct/function'; 3 | 4 | export default class FunctionProvider { 5 | async init() { 6 | throw new Error('init method not implemented'); 7 | } 8 | 9 | async deployFunction(args: DeployFunctionArgs): Promise { 10 | throw new Error('deployFunction method not implemented'); 11 | } 12 | 13 | async deleteFunction(functionId: string, data: any): Promise { 14 | throw new Error('deleteFunction method not implemented'); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /webapp/src/lib/middleware/auth/checksession.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { dynamicResponse } from '@dr'; 4 | 5 | export default function checkSession(req, res, next) { 6 | if (!res.locals.account?._id && !res.locals.isAgentBackend) { 7 | if (res.locals.isSocket) { 8 | return res?.locals?.socket?.disconnect(); 9 | } else { 10 | return dynamicResponse(req, res, 302, { 11 | redirect: `/login?goto=${encodeURIComponent(req.originalUrl)}` 12 | }); 13 | } 14 | } 15 | next(); 16 | } 17 | -------------------------------------------------------------------------------- /webapp/src/lib/middleware/auth/checksessionwelcome.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { dynamicResponse } from '@dr'; 4 | 5 | export default function checkSessionWelcome(req, res, next) { 6 | if (res.locals.account) { 7 | // need to check for account verification for verify page as well 8 | return dynamicResponse(req, res, 302, { redirect: '/welcome' }); 9 | } 10 | next(); 11 | } 12 | -------------------------------------------------------------------------------- /webapp/src/lib/middleware/auth/csrf.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import csrf from 'csurf'; 4 | 5 | const csrfCheck = csrf(); 6 | 7 | export default function csrfMiddleware(req, res, next) { 8 | if ( 9 | req.method === 'GET' || //GETs should always run the middleware because it attaches req.csrfToken() for later. 10 | res.locals.checkCsrf === true 11 | ) { 12 | return csrfCheck(req, res, next); 13 | } 14 | 15 | next(); 16 | } 17 | -------------------------------------------------------------------------------- /webapp/src/lib/middleware/auth/usesession.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import dotenv from 'dotenv'; 4 | dotenv.config({ path: '.env' }); 5 | 6 | import RedisStore from 'connect-redis'; 7 | import session from 'express-session'; 8 | import { client as redisClient } from 'redis/redis'; 9 | const dev = process.env.NODE_ENV !== 'production'; 10 | 11 | const sessionStore = session({ 12 | secret: process.env.COOKIE_SECRET, 13 | store: new RedisStore({ 14 | prefix: 'sessions:', 15 | client: redisClient 16 | }), 17 | resave: false, 18 | saveUninitialized: false, 19 | rolling: true, 20 | cookie: { 21 | httpOnly: true, 22 | secure: false, //TODO: check https 23 | sameSite: 'lax', 24 | maxAge: 1000 * 60 * 60 * 24 * 30 //1 month 25 | } 26 | }); 27 | 28 | export default function useSession(req, res, next) { 29 | if (!res.locals) { 30 | res.locals = {}; 31 | } 32 | sessionStore(req, res, next); 33 | } 34 | -------------------------------------------------------------------------------- /webapp/src/lib/middleware/checkonboarded.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { dynamicResponse } from 'misc/dynamicresponse'; 4 | 5 | const ONBOARDING_ALLOWED_PATHS = new Set([ 6 | '/onboarding', 7 | '/onboarding/configuremodels', 8 | '/account.json', //Account context refreshed from here 9 | '/team/models.json', 10 | '/airbyte/connectors.json', 11 | '/airbyte/specification' 12 | ]); 13 | 14 | export default async function checkOnboardedStatus(req, res, next) { 15 | if (req.method === 'GET' && res.locals.account) { 16 | const desiredPath = req.path; // We don't care about the resourceSlug 17 | 18 | const originPath = req.headers.referer || ''; 19 | const lastSegment = originPath.split('/').pop(); 20 | 21 | // Proceed if the last segment of the originPath includes "onboarding" 22 | if (lastSegment && lastSegment.includes('onboarding')) { 23 | return next(); 24 | } 25 | 26 | if (!ONBOARDING_ALLOWED_PATHS.has(desiredPath) && desiredPath !== 'onboarding') { 27 | if (res.locals.account.onboarded === false) { 28 | return dynamicResponse(req, res, 302, { 29 | redirect: `/${res.locals.account.currentTeam.toString()}/onboarding` 30 | }); 31 | } 32 | } 33 | } 34 | 35 | next(); 36 | } 37 | -------------------------------------------------------------------------------- /webapp/src/lib/middleware/homeredirect.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export default function homeRedirect(_req, res, _next) { 4 | const homeRedirect = res.locals.account ? `/${res.locals.account.currentTeam}/apps` : '/register'; 5 | res.redirect(homeRedirect); 6 | } 7 | -------------------------------------------------------------------------------- /webapp/src/lib/middleware/render/staticpage.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const cache: Record = {}; 4 | 5 | export default function renderStaticPage(app, pagePath: string) { 6 | return ( 7 | cache[pagePath] || 8 | (cache[pagePath] = (req, res, _next) => { 9 | app.render(req, res, pagePath); 10 | }) 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /webapp/src/lib/misc/defaultchunkingoptions.ts: -------------------------------------------------------------------------------- 1 | export const defaultChunkingOptions = Object.freeze( 2 | Object.preventExtensions( 3 | Object.seal({ 4 | partitioning: 'auto', 5 | strategy: 'basic', 6 | max_characters: 500, 7 | new_after_n_chars: null, // Defaults to max_characters unless changed 8 | overlap: 0, 9 | similarity_threshold: 0.5, 10 | overlap_all: false, 11 | file_type: 'txt' 12 | }) 13 | ) 14 | ); 15 | -------------------------------------------------------------------------------- /webapp/src/lib/misc/dynamicresponse.ts: -------------------------------------------------------------------------------- 1 | import url from 'node:url'; 2 | 3 | import debug from 'debug'; 4 | const log = debug('webapp:dynamicresponse'); 5 | 6 | export function dynamicResponse(req, res, code, data) { 7 | if (typeof res?.status !== 'function') { 8 | return log('res.status is not a function, returning from mock call'); 9 | } 10 | const isRedirect = code === 302; 11 | if ( 12 | req.headers && 13 | (req.headers['content-type'] === 'application/json' || 14 | req.headers['content-type']?.startsWith('multipart/form-data')) 15 | ) { 16 | return res.status(isRedirect ? 200 : code).json(data); 17 | } 18 | if (isRedirect) { 19 | return res.redirect(data.redirect); 20 | } 21 | //TODO: pass through app (bind to middleware) and app.render an "error" page for nojs users? 22 | return res.status(code).send(data); 23 | } 24 | -------------------------------------------------------------------------------- /webapp/src/lib/misc/getdotprop.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from '@jest/globals'; 2 | 3 | import getDotProp from './getdotprop'; 4 | 5 | const testObject = { 6 | a: { 7 | b: { 8 | c: 'value', 9 | d: null, 10 | }, 11 | e: 'another value' 12 | }, 13 | f: null, 14 | }; 15 | 16 | const testCases = [ 17 | { obj: testObject, prop: 'a.b.c', expected: 'value' }, 18 | { obj: testObject, prop: 'a.b.d', expected: null }, 19 | { obj: testObject, prop: 'a.e', expected: 'another value' }, 20 | { obj: testObject, prop: 'f', expected: null }, 21 | { obj: testObject, prop: 'a.b.x', expected: null }, 22 | { obj: testObject, prop: 'a.x.c', expected: null }, 23 | { obj: testObject, prop: '', expected: null }, 24 | { obj: {}, prop: 'a.b.c', expected: null }, 25 | ]; 26 | 27 | describe('Test getDotProp() util', () => { 28 | for (let { obj, prop, expected } of testCases) { 29 | test(`Test getDotProp(${JSON.stringify(obj)}, "${prop}") -> ${expected}`, () => { 30 | expect(getDotProp(obj, prop)).toBe(expected); 31 | }); 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /webapp/src/lib/misc/getdotprop.ts: -------------------------------------------------------------------------------- 1 | export default function getDotProp(obj, prop) { 2 | return prop.split('.').reduce((a, b) => { 3 | if (a && a[b]) { 4 | return a[b]; 5 | } 6 | return null; 7 | }, obj); 8 | } 9 | -------------------------------------------------------------------------------- /webapp/src/lib/misc/getgooglecredentials.ts: -------------------------------------------------------------------------------- 1 | const { GoogleAuth } = require('google-auth-library'); 2 | 3 | export default async function getGoogleCredentials() { 4 | const auth = new GoogleAuth(); 5 | const credentials = await auth.getCredentials(); 6 | return JSON.stringify(credentials); 7 | } 8 | -------------------------------------------------------------------------------- /webapp/src/lib/misc/handleshiftnewlines.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export default async function handleShiftNewlines( 4 | e, 5 | value, 6 | onSubmit, 7 | setInputValue, 8 | scrollToBottom?, 9 | chatBusyState?, 10 | showConversationStarters? 11 | ) { 12 | scrollToBottom && scrollToBottom(); 13 | if (e.key === 'Enter' && !e.shiftKey) { 14 | e.preventDefault(); 15 | if (chatBusyState && !showConversationStarters) { 16 | return; 17 | } 18 | if (value.trim().length > 0) { 19 | const success = await onSubmit(e); 20 | success && setInputValue(''); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /webapp/src/lib/misc/passwordpattern.ts: -------------------------------------------------------------------------------- 1 | const passwordPattern = /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[^A-Za-z\d]).{8,}$/; 2 | 3 | export default passwordPattern; 4 | -------------------------------------------------------------------------------- /webapp/src/lib/misc/time.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const YEAR = 31536000000, 4 | MONTH = 2629800000, 5 | WEEK = 604800000, 6 | DAY = 86400000, 7 | HOUR = 3600000, 8 | MINUTE = 60000, 9 | SECOND = 1000; 10 | 11 | export function relativeString(now, relativeTo) { 12 | let difference = now.getTime() - relativeTo.getTime(); 13 | let amount = 0; 14 | let unit = ''; 15 | let isFuture = false; 16 | if (difference < 0) { 17 | difference = Math.abs(difference); 18 | isFuture = true; 19 | } 20 | if (difference < MINUTE) { 21 | return 'Just now'; 22 | } else if (difference < MINUTE * 59.5) { 23 | amount = Math.round(difference / MINUTE); 24 | unit = 'minute'; 25 | } else if (difference < HOUR * 23.5) { 26 | amount = Math.round(difference / HOUR); 27 | unit = 'hour'; 28 | } else if (difference < DAY * 6.5) { 29 | amount = Math.round(difference / DAY); 30 | unit = 'day'; 31 | } else if (difference < WEEK * 3.5) { 32 | amount = Math.round(difference / WEEK); 33 | unit = 'week'; 34 | } else if (difference < MONTH * 11.5) { 35 | amount = Math.round(difference / MONTH); 36 | unit = 'month'; 37 | } else { 38 | amount = Math.round(difference / YEAR); 39 | unit = 'year'; 40 | } 41 | return `${amount} ${unit}${amount > 1 ? 's' : ''} ${isFuture ? 'from now' : 'ago'}`; 42 | } 43 | -------------------------------------------------------------------------------- /webapp/src/lib/misc/toobjectid.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from '@jest/globals'; 2 | import { ObjectId } from 'mongodb'; 3 | 4 | import toObjectId from './toobjectid'; 5 | 6 | const testCases = [ 7 | { in: '507f1f77bcf86cd799439011', isString: true }, 8 | { in: new ObjectId('507f1f77bcf86cd799439011'), isString: false }, 9 | { in: 'invalid-id', isString: true, expectError: true }, 10 | ]; 11 | 12 | describe('Test toObjectId() util', () => { 13 | for (let t of testCases) { 14 | if (t.expectError) { 15 | test(`Test toObjectId(${t.in}) throws an error`, () => { 16 | expect(() => toObjectId(t.in)).toThrow(); 17 | }); 18 | } else { 19 | test(`Test toObjectId(${t.in}) returns ObjectId`, () => { 20 | const result = toObjectId(t.in); 21 | expect(result).toBeInstanceOf(ObjectId); 22 | if (t.isString) { 23 | expect(result.toHexString()).toBe(t.in); 24 | } else { 25 | expect(result).toBe(t.in); 26 | } 27 | }); 28 | } 29 | } 30 | }); 31 | -------------------------------------------------------------------------------- /webapp/src/lib/misc/toobjectid.ts: -------------------------------------------------------------------------------- 1 | 'usestrict'; 2 | 3 | import { ObjectId } from 'mongodb'; 4 | 5 | export default function toObjectId(str: string | ObjectId) { 6 | return typeof str === 'string' ? new ObjectId(str) : str; 7 | } 8 | -------------------------------------------------------------------------------- /webapp/src/lib/misc/tosnakecase.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from '@jest/globals'; 2 | 3 | import toSnakeCase from './tosnakecase'; 4 | 5 | const testCases = [{ in: 'THIS IS A TEST', out: 'this_is_a_test' }]; 6 | 7 | describe('Test toSnakeCase() util', () => { 8 | for (let t of testCases) { 9 | test(`Test toSnakeCase(${t.in}) -> ${t.out}`, () => { 10 | expect(toSnakeCase('THIS IS A TEST')).toBe('this_is_a_test'); 11 | }); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /webapp/src/lib/misc/tosnakecase.ts: -------------------------------------------------------------------------------- 1 | export default function toSnakeCase(str: string) { 2 | // Lowercase the string and replace non-alphanumeric characters with underscores 3 | return str 4 | .toLowerCase() 5 | .replace(/[^a-z0-9]/g, '_') 6 | .replace(/_+/g, '_') // Replace multiple underscores with a single one 7 | .replace(/^_|_$/g, ''); // Remove leading and trailing underscores 8 | } 9 | -------------------------------------------------------------------------------- /webapp/src/lib/misc/withlogging.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/src/lib/misc/withlogging.ts -------------------------------------------------------------------------------- /webapp/src/lib/posthog/index.ts: -------------------------------------------------------------------------------- 1 | import { PostHog } from 'posthog-node'; 2 | 3 | const client = 4 | process.env.NEXT_PUBLIC_POSTHOG_KEY && 5 | new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY, { 6 | host: process.env.NEXT_PUBLIC_POSTHOG_HOST || 'https://us.i.posthog.com' 7 | }); 8 | 9 | export default client; 10 | -------------------------------------------------------------------------------- /webapp/src/lib/queue/bull.ts: -------------------------------------------------------------------------------- 1 | import { Queue, Worker } from 'bullmq'; 2 | import { client as redisClient } from 'redis/redis'; 3 | 4 | export const sessionTaskQueue = new Queue('task_queue', { 5 | connection: redisClient 6 | }); 7 | 8 | export const vectorLimitTaskQueue = new Queue('vector_limit_check', { 9 | connection: redisClient 10 | }); 11 | 12 | export { Worker }; 13 | -------------------------------------------------------------------------------- /webapp/src/lib/queue/google.ts: -------------------------------------------------------------------------------- 1 | import { PubSub } from '@google-cloud/pubsub'; 2 | import debug from 'debug'; 3 | import MessageQueueProvider from 'queue/provider'; 4 | const log = debug('webapp:queue:google'); 5 | 6 | class GooglePubSubProvider extends MessageQueueProvider { 7 | #pubsubClient: PubSub; 8 | 9 | constructor() { 10 | super(); 11 | } 12 | 13 | async init() { 14 | const options: any = { projectId: process.env.PROJECT_ID }; 15 | // if (process.env.GOOGLE_APPLICATION_CREDENTIALS) { 16 | // options['keyFilename'] = process.env.GOOGLE_APPLICATION_CREDENTIALS; 17 | // } 18 | this.#pubsubClient = new PubSub(options); 19 | log('Google Pub/Sub client initialized.'); 20 | } 21 | 22 | async sendMessage(message: string, metadata: any) { 23 | log('message %O', message); 24 | log('metadata %O', metadata); 25 | const dataBuffer = Buffer.from(message); 26 | try { 27 | const messageId = await this.#pubsubClient 28 | .topic(process.env.QUEUE_NAME) 29 | .publish(dataBuffer, metadata); 30 | log(`Message ${messageId} sent successfully.`); 31 | } catch (error) { 32 | log(`Error in sending message: ${error.message}`); 33 | throw error; 34 | } 35 | } 36 | } 37 | 38 | export default new GooglePubSubProvider(); 39 | -------------------------------------------------------------------------------- /webapp/src/lib/queue/index.ts: -------------------------------------------------------------------------------- 1 | import GooglePubSubProvider from 'queue/google'; 2 | import RabbitMQProvider from 'queue/rabbitmq'; 3 | 4 | export default class MessageQueueProviderFactory { 5 | static getMessageQueueProvider() { 6 | switch ((process.env.MESSAGE_QUEUE_PROVIDER || '').toLowerCase()) { 7 | case 'rabbitmq': 8 | return RabbitMQProvider; 9 | case 'google': 10 | return GooglePubSubProvider; 11 | default: 12 | console.error( 13 | 'Invalid MESSAGE_QUEUE_PROVIDER env value:', 14 | process.env.MESSAGE_QUEUE_PROVIDER 15 | ); 16 | process.exit(1); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /webapp/src/lib/queue/provider.ts: -------------------------------------------------------------------------------- 1 | export default class MessageQueueProvider { 2 | async init() { 3 | throw new Error('init method not implemented'); 4 | } 5 | 6 | async sendMessage(message: string, metadata: any): Promise { 7 | throw new Error('sendMessage method not implemented'); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /webapp/src/lib/secret/index.ts: -------------------------------------------------------------------------------- 1 | import GoogleSecretProvider from 'secret/google'; 2 | import LocalSecretProvider from 'secret/local'; 3 | 4 | export default class SecretProviderFactory { 5 | static getSecretProvider(provider: string = 'local') { 6 | switch ((process.env.NEXT_PUBLIC_SECRET_PROVIDER || provider).toLowerCase()) { 7 | case 'google': 8 | return GoogleSecretProvider; 9 | case 'local': 10 | return LocalSecretProvider; 11 | default: 12 | console.error( 13 | 'Invalid process.env.NEXT_PUBLIC_SECRET_PROVIDER env value:', 14 | process.env.NEXT_PUBLIC_SECRET_PROVIDER 15 | ); 16 | process.exit(1); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /webapp/src/lib/secret/local.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { SecretManagerServiceClient } from '@google-cloud/secret-manager'; 4 | import dotenv from 'dotenv'; 5 | import SecretProvider from 'secret/provider'; 6 | dotenv.config({ path: '.env' }); 7 | 8 | class LocalSecretProvider extends SecretProvider { 9 | #secretClient: any; 10 | #cache = {}; 11 | 12 | async getSecret(key, bypassCache = false): Promise { 13 | return process.env[key]; 14 | } 15 | } 16 | 17 | export default new LocalSecretProvider(); 18 | -------------------------------------------------------------------------------- /webapp/src/lib/secret/provider.ts: -------------------------------------------------------------------------------- 1 | export default class SecretProvider { 2 | async init() {} 3 | 4 | async getSecret(key: string, bypassCache: boolean = false) { 5 | throw new Error('getSecret method not implemented'); 6 | } 7 | 8 | // I don't think we need these 9 | // async setSecret() { ... } 10 | // async deleteSecret() { ... } 11 | } 12 | -------------------------------------------------------------------------------- /webapp/src/lib/secret/secretkeys.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // In case the name of the actual secret changes, we maintain them in one place 4 | const SecretKeys = { 5 | AMAZON_ACCESS_ID: 'AMAZON_ACCESS_ID', 6 | AMAZON_SECRET_ACCESS_KEY: 'AMAZON_SECRET_ACCESS_KEY', 7 | OAUTH_GITHUB_CLIENT_ID: 'OAUTH_GITHUB_CLIENT_ID', 8 | OAUTH_GITHUB_CLIENT_SECRET: 'OAUTH_GITHUB_CLIENT_SECRET', 9 | OAUTH_GOOGLE_CLIENT_ID: 'OAUTH_GOOGLE_CLIENT_ID', 10 | OAUTH_GOOGLE_CLIENT_SECRET: 'OAUTH_GOOGLE_CLIENT_SECRET', 11 | OAUTH_HUBSPOT_CLIENT_SECRET: 'OAUTH_HUBSPOT_CLIENT_SECRET', 12 | OAUTH_HUBSPOT_CLIENT_ID: 'OAUTH_HUBSPOT_CLIENT_ID', 13 | STRIPE_ACCOUNT_SECRET: 'STRIPE_ACCOUNT_SECRET', 14 | STRIPE_WEBHOOK_SECRET: 'STRIPE_WEBHOOK_SECRET' 15 | }; 16 | 17 | export default SecretKeys; 18 | -------------------------------------------------------------------------------- /webapp/src/lib/storage/index.ts: -------------------------------------------------------------------------------- 1 | import GoogleStorageProvider from 'storage/google'; 2 | import LocalStorageProvider from 'storage/local'; 3 | 4 | export default class StorageProviderFactory { 5 | static getStorageProvider(providerName?: string) { 6 | const provider = providerName || process.env.NEXT_PUBLIC_STORAGE_PROVIDER; 7 | switch ((provider || 'local').toLowerCase()) { 8 | case 'google': 9 | return GoogleStorageProvider; 10 | case 'local': 11 | return LocalStorageProvider; 12 | default: 13 | console.error('Invalid storage provider:', provider); 14 | process.exit(1); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /webapp/src/lib/storage/provider.ts: -------------------------------------------------------------------------------- 1 | export default class StorageProvider { 2 | async init() { 3 | throw new Error('init method not implemented'); 4 | } 5 | 6 | async uploadLocalFile( 7 | filename: string, 8 | uploadedFile: any, 9 | contentType: string, 10 | isPublic = false 11 | ): Promise { 12 | throw new Error('uploadLocalFile method not implemented'); 13 | } 14 | 15 | async uploadBuffer( 16 | filename: string, 17 | content: Buffer, 18 | contentType: string, 19 | isPublic = false 20 | ): Promise { 21 | throw new Error('uploadBuffer method not implemented'); 22 | } 23 | 24 | async cloneFile(sourceFilename: string, destinationFilename: string): Promise { 25 | throw new Error('cloneFile method not implemented'); 26 | } 27 | 28 | async deleteFile(filename: string, isPublic = false): Promise { 29 | throw new Error('deleteFile method not implemented'); 30 | } 31 | 32 | getBasePath() { 33 | throw new Error('getBasePath method not implemented.'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /webapp/src/lib/stripe/index.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | import SecretProviderFactory from 'secret/index'; 3 | import SecretKeys from 'secret/secretkeys'; 4 | import Stripe from 'stripe'; 5 | const log = debug('webapp:stripe'); 6 | 7 | class StripeClient { 8 | #stripeClient; 9 | 10 | async init() { 11 | log('Initializing stripe client'); 12 | try { 13 | // Get stripe secret 14 | const secretProvider = SecretProviderFactory.getSecretProvider(); 15 | const STRIPE_ACCOUNT_SECRET = await secretProvider.getSecret( 16 | SecretKeys.STRIPE_ACCOUNT_SECRET 17 | ); 18 | 19 | // Initialize the Stripe client 20 | this.#stripeClient = new Stripe(STRIPE_ACCOUNT_SECRET); 21 | } catch (e) { 22 | log(e); 23 | } 24 | log('Stripe client initialized'); 25 | } 26 | 27 | get() { 28 | return this.#stripeClient; 29 | } 30 | } 31 | 32 | export default new StripeClient(); 33 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/apikey.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import { Binary, ObjectId } from 'mongodb'; 3 | 4 | export type APIKey = { 5 | _id?: ObjectId; 6 | version?: number; 7 | name: string; 8 | description?: string; 9 | expirationDate: Date | null; 10 | ownerId: ObjectId; 11 | token?: string; 12 | permissions?: Binary; //TODO: set up permissions with API keys 13 | }; 14 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/app.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { ObjectId } from 'mongodb'; 4 | import { IconAttachment } from 'struct/asset'; 5 | import { SharingConfig } from 'struct/sharing'; 6 | 7 | export enum AppType { 8 | CHAT = 'chat', 9 | CREW = 'crew' 10 | } 11 | 12 | export type ChatAppConfig = { 13 | agentId: ObjectId; 14 | conversationStarters: string[]; 15 | maxMessages?: number; 16 | }; 17 | 18 | export type VariableConfig = { 19 | id: ObjectId; 20 | name: string; 21 | defaultValue: string; 22 | }; 23 | 24 | export type App = { 25 | _id?: ObjectId; 26 | orgId: ObjectId; 27 | teamId: ObjectId; 28 | name: string; 29 | description: string; 30 | type: AppType; 31 | author: string; 32 | tags: string[]; 33 | icon: IconAttachment; 34 | hidden?: boolean; 35 | sharingConfig: SharingConfig; 36 | chatAppConfig?: ChatAppConfig; 37 | //TODO: create a "CrewAppConfig" for these: 38 | memory?: boolean; 39 | cache?: boolean; 40 | crewId?: ObjectId; 41 | shareLinkShareId?: string; 42 | variables?: VariableConfig[]; 43 | createdBy: ObjectId; 44 | kickOffVariablesIds?: ObjectId[]; 45 | }; 46 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/asset.ts: -------------------------------------------------------------------------------- 1 | import { ObjectId } from 'mongodb'; 2 | import { CollectionName } from 'struct/db'; 3 | 4 | export type Asset = { 5 | _id?: ObjectId; 6 | teamId: ObjectId; 7 | orgId: ObjectId; 8 | filename: string; 9 | originalFilename: string; 10 | mimeType: string; 11 | uploadedAt: Date; 12 | linkedToId?: ObjectId; 13 | linkedCollection?: CollectionName; 14 | }; 15 | 16 | export type IconAttachment = { 17 | id: ObjectId; 18 | linkedId?: ObjectId; 19 | filename: string; 20 | }; 21 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/auditlog.ts: -------------------------------------------------------------------------------- 1 | import { ObjectId } from 'mongodb'; 2 | 3 | export type Log = { 4 | orgId?: ObjectId | string; 5 | teamId?: ObjectId | string; 6 | userId: ObjectId | string; 7 | startTime: Date; 8 | functionName: string; 9 | arguments?: any; 10 | }; 11 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/connector.ts: -------------------------------------------------------------------------------- 1 | export interface Connector { 2 | sourceDefinitionId: string; 3 | name: string; 4 | dockerRepository: string; 5 | dockerImageTag: string; 6 | documentationUrl: string; 7 | icon: string; 8 | protocolVersion: string; 9 | custom: boolean; 10 | supportLevel: string; 11 | releaseStage: string; 12 | releaseDate: string; 13 | sourceType: string; 14 | maxSecondsBetweenMessages: number; 15 | lastPublished: string; 16 | cdkVersion: string; 17 | metrics: { 18 | all: { 19 | connector_name: string; 20 | connector_type: string; 21 | airbyte_platform: string; 22 | connector_version: string; 23 | docker_repository: string; 24 | connector_definition_id: string; 25 | }; 26 | oss: { 27 | connector_name: string; 28 | connector_type: string; 29 | airbyte_platform: string; 30 | connector_version: string; 31 | docker_repository: string; 32 | connector_definition_id: string; 33 | }; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/crew.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { ObjectId } from 'mongodb'; 4 | 5 | export type Group = { 6 | _id?: ObjectId; 7 | orgId?: ObjectId; 8 | teamId?: ObjectId; 9 | name: string; 10 | adminAgent: ObjectId; 11 | agents: ObjectId[]; 12 | groupChat?: boolean; 13 | }; 14 | 15 | export enum ProcessImpl { 16 | SEQUENTIAL = 'sequential', 17 | HIERARCHICAL = 'hierarchical' 18 | } 19 | 20 | export type Crew = { 21 | _id?: ObjectId; 22 | orgId?: ObjectId; 23 | teamId?: ObjectId; 24 | name: string; 25 | tasks: ObjectId[]; 26 | agents: ObjectId[]; 27 | process: ProcessImpl; 28 | managerModelId?: ObjectId; 29 | hidden?: boolean; 30 | verbose?: number; 31 | fullOutput?: boolean; 32 | }; 33 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/db.ts: -------------------------------------------------------------------------------- 1 | import { ObjectId } from 'mongodb'; 2 | 3 | export type InsertResult = { 4 | acknowledged?: boolean; 5 | insertedId?: ObjectId; 6 | }; 7 | 8 | export enum CollectionName { 9 | Accounts = 'accounts', 10 | Agents = 'agents', 11 | Apps = 'apps', 12 | Chat = 'chat', 13 | CheckoutSessions = 'checkoutsessions', 14 | Crews = 'crews', 15 | Datasources = 'datasources', 16 | Version = 'version', 17 | Models = 'models', 18 | Notifications = 'notifications', 19 | Orgs = 'orgs', 20 | PaymentLinks = 'paymentlinks', 21 | PortalLinks = 'portallinks', 22 | Sessions = 'sessions', 23 | Tasks = 'tasks', 24 | Teams = 'teams', 25 | Tools = 'tools', 26 | Toolrevisions = 'toolrevisions', 27 | Verifications = 'verifications', 28 | ShareLinks = 'sharelinks', 29 | APIKeys = 'apikeys', 30 | Checkpoints = 'checkpoints' 31 | } 32 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/dispatchtypes.ts: -------------------------------------------------------------------------------- 1 | import { Dispatch, SetStateAction } from 'react'; 2 | 3 | import { Task } from './task'; 4 | import { TeamModelResponse } from './teammodels'; 5 | import { Variable } from './variable'; 6 | import { VectorDbDocument } from './vectordb'; 7 | 8 | export type GetTeamModelsDispatch = Dispatch>; 9 | export type GetTaskByNameDispatch = Dispatch>; 10 | export type GetVariableDispatch = Dispatch>; 11 | export type GetVectorDbDispatch = Dispatch>; 12 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/form.ts: -------------------------------------------------------------------------------- 1 | export interface Connector { 2 | name_oss: string; 3 | spec_oss: { 4 | documentationUrl: string; 5 | connectionSpecification: Schema; 6 | }; 7 | } 8 | 9 | export interface Schema { 10 | $schema: string; 11 | title: string; 12 | type: string; 13 | description?: string; 14 | required: string[]; 15 | additionalProperties: boolean; 16 | properties: { 17 | [key: string]: Property; 18 | }; 19 | } 20 | 21 | export interface FormFieldProps { 22 | name: string; 23 | testId?: string; 24 | type: string; 25 | autofocus?: boolean; 26 | disabled?: boolean; 27 | property: Property; 28 | isRequired?: boolean; 29 | level?: number; 30 | } 31 | 32 | export interface Property { 33 | const?: string; 34 | type: 'string' | 'object' | 'integer' | 'boolean' | 'array'; 35 | enum?: string[]; 36 | description?: string; 37 | order?: number; 38 | items: { 39 | enum?: string[]; 40 | type: 'array' | 'object' | 'string'; 41 | properties: { 42 | [key: string]: Property; 43 | }; 44 | required?: string[]; 45 | }; 46 | minItems?: number; 47 | title?: string; 48 | uniqueItems: boolean; 49 | format?: string; 50 | oneOf?: Property[]; 51 | properties?: { 52 | [key: string]: Property; 53 | }; 54 | required?: string[]; 55 | } 56 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/function.ts: -------------------------------------------------------------------------------- 1 | export type DeployFunctionArgs = { 2 | code: string; 3 | requirements: string; 4 | id: string; 5 | runtime?: string; 6 | environmentVariables?: Record; 7 | }; 8 | 9 | //Runtime optiosn for google cloud functions, used in tool form 10 | export const runtimeOptions = [ 11 | { label: 'Python 3.12', value: 'python312' }, 12 | { label: 'Python 3.11', value: 'python311' }, 13 | { label: 'Python 3.10', value: 'python310' }, 14 | { label: 'Python 3.9', value: 'python39' }, 15 | { label: 'Python 3.8', value: 'python38' } 16 | ]; 17 | 18 | export const runtimeValues = runtimeOptions.map(x => x.value); 19 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/oauth.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export enum OAUTH_PROVIDER { 4 | GOOGLE = 'google', 5 | GITHUB = 'github' 6 | } 7 | 8 | export type OAuthStrategy = { 9 | strategy: any; 10 | callback: Function; 11 | secretKeys: { 12 | clientId: string; 13 | secret: string; 14 | }; 15 | path: string; 16 | extra?: any; // Stuff like scope (this object is a different shape depending on provider hence any) 17 | }; 18 | 19 | export enum AIRBYTE_OAUTH_PROVIDERS { //OAuth to initiate airbyte datasource connection is handled seperately from OAuth to register/log in to agent cloud 20 | HUBSPOT = 'hubspot' 21 | } 22 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/qdrantfilter.ts: -------------------------------------------------------------------------------- 1 | export type RagFilter = { 2 | must?: Condition[]; 3 | should?: Condition[]; 4 | must_not?: Condition[]; 5 | }; 6 | 7 | type Condition = { 8 | key: string; 9 | match: Match; 10 | }; 11 | 12 | type Match = { 13 | value?: any; 14 | any?: any[]; 15 | except?: any[]; 16 | }; 17 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/role.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | []; 4 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/schedule.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rnadigital/agentcloud/d13d6c6e6ee40e2683a70b4b9882f245fff5a19a/webapp/src/lib/struct/schedule.ts -------------------------------------------------------------------------------- /webapp/src/lib/struct/session.ts: -------------------------------------------------------------------------------- 1 | export enum SessionStatus { 2 | STARTED = 'started', 3 | RUNNING = 'running', 4 | WAITING = 'waiting', 5 | WARNING = 'warning', 6 | ERRORED = 'error', 7 | TERMINATED = 'terminated' 8 | } 9 | 10 | export enum FeedbackOption { 11 | EXIT = 'exit', 12 | CONTINUE = 'continue', 13 | CANCEL = 'cancel' 14 | } 15 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/sharelink.ts: -------------------------------------------------------------------------------- 1 | import { ObjectId } from 'mongodb'; 2 | 3 | export enum ShareLinkTypes { 4 | APP = 'app' 5 | } 6 | 7 | export type ShareLinkType = ShareLinkTypes; 8 | 9 | export type ShareLink = { 10 | _id?: ObjectId; 11 | orgId: ObjectId; 12 | teamId: ObjectId; 13 | shareId: string; // actual id that goes in the link 14 | type: ShareLinkType; 15 | createdDate: Date; 16 | payload: { 17 | id: ObjectId; // i.e the app _id since we only suppor apps for now 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/sharing.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { Binary } from 'mongodb'; 4 | 5 | export enum SharingMode { 6 | TEAM = 'team', 7 | PUBLIC = 'public', 8 | PRIVATE = 'owner', 9 | WHITELIST = 'whitelist' 10 | } 11 | 12 | /* Note: While the permissions object is intended to hold permissions presence 13 | * in the permissions object as a key implies view/read access for now until we 14 | * implement checks and update middleware chains for more complex permissions. 15 | * Keys are intended to be user, team, or org IDs with a mapping to permissions. 16 | */ 17 | export type SharingConfig = { 18 | permissions: Record | {}; //Note: string keys, not ObjectId 19 | mode: SharingMode; 20 | }; 21 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/task.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { ObjectId } from 'mongodb'; 4 | import { IconAttachment } from 'struct/asset'; 5 | 6 | export interface FormFieldConfig { 7 | position: string; 8 | type: 'string' | 'number' | 'radio' | 'checkbox' | 'select' | 'multiselect' | 'date'; 9 | name: string; 10 | label: string; 11 | description?: string; 12 | required?: boolean; 13 | options?: string[]; 14 | tooltip?: string; 15 | variable?: string; 16 | } 17 | 18 | export interface Task { 19 | _id?: ObjectId | string; 20 | orgId?: ObjectId | string; 21 | teamId?: ObjectId | string; 22 | name: string; 23 | description: string; 24 | agentId?: ObjectId | string; 25 | expectedOutput?: string; 26 | toolIds?: (ObjectId | string)[]; 27 | asyncExecution?: boolean; 28 | context?: (ObjectId | string)[]; 29 | outputJson?: any; 30 | outputPydantic?: any; 31 | outputFile?: string; 32 | icon?: 33 | | IconAttachment 34 | | { 35 | id: string; 36 | filename: string; 37 | } 38 | | null; 39 | requiresHumanInput?: boolean; 40 | hidden?: boolean; 41 | storeTaskOutput?: boolean; 42 | taskOutputFileName?: string; 43 | formFields?: FormFieldConfig[]; 44 | isStructuredOutput?: boolean; 45 | displayOnlyFinalOutput?: boolean; 46 | variableIds?: (ObjectId | string)[]; 47 | taskOutputVariableName?: string; 48 | dateCreated?: Date; 49 | } 50 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/toolrevision.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { ObjectId } from 'mongodb'; 4 | 5 | export type ToolRevision = { 6 | _id?: ObjectId; 7 | orgId?: ObjectId; 8 | teamId?: ObjectId; 9 | toolId: ObjectId; 10 | content: any; 11 | date: Date; 12 | }; 13 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/variable.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { ObjectId } from 'mongodb'; 4 | 5 | export interface Variable { 6 | _id?: ObjectId | string; 7 | orgId?: ObjectId | string; 8 | teamId?: ObjectId | string; 9 | name: string; 10 | defaultValue: string; 11 | createdBy?: ObjectId | string; 12 | createDate?: Date; 13 | usedInTasks?: (ObjectId | string)[]; 14 | usedInAgents?: (ObjectId | string)[]; 15 | description?: string; 16 | } 17 | -------------------------------------------------------------------------------- /webapp/src/lib/struct/vectordb.ts: -------------------------------------------------------------------------------- 1 | import mongoose, { Document, InferSchemaType, Model, Schema, Types } from 'mongoose'; 2 | 3 | const { ObjectId } = Types; 4 | 5 | export interface VectorDb { 6 | orgId: Types.ObjectId; 7 | teamId: Types.ObjectId; 8 | type: string; 9 | apiKey: string; 10 | url?: string; 11 | name: string; 12 | } 13 | 14 | const vectorDbschema = new Schema( 15 | { 16 | orgId: ObjectId, 17 | teamId: ObjectId, 18 | type: { type: String, required: true }, 19 | apiKey: { type: String, required: true }, 20 | url: String, 21 | name: String 22 | }, 23 | { timestamps: true } 24 | ); 25 | 26 | export type VectorDbType = InferSchemaType; 27 | 28 | const modelName = 'VectorDb'; 29 | const existingModel = mongoose.models[modelName] as mongoose.Model | undefined; 30 | export const VectorDbModel: mongoose.Model = 31 | existingModel || mongoose.model(modelName, vectorDbschema); 32 | 33 | export type VectorDbDocument = InferSchemaType & { 34 | _id: string; 35 | createdAt: string; 36 | }; 37 | -------------------------------------------------------------------------------- /webapp/src/lib/styles/SelectClassNames.ts: -------------------------------------------------------------------------------- 1 | import cn from 'utils/cn'; 2 | 3 | const SelectClassNames = { 4 | menuButton: ({ isDisabled }) => 5 | cn( 6 | 'flex text-sm text-gray-500 dark:text-gray-50 border border-gray-300 rounded shadow-sm transition-all duration-300 focus:outline-none bg-white dark:bg-slate-800 dark:border-slate-600 hover:border-gray-400 focus:border-indigo-500 focus:ring focus:ring-indigo-500/20', 7 | { 'bg-gray-300': isDisabled } 8 | ), 9 | menu: 'absolute z-10 w-full bg-white shadow-lg border roundedu py-1 mt-1.5 text-sm text-gray-700 dark:bg-slate-700 dark:border-slate-600 disabled:bg-red-500', 10 | list: 'dark:bg-slate-700 rounded', 11 | listGroupLabel: 'dark:bg-slate-700', 12 | listItem: (value?: { isSelected?: boolean }) => 13 | `block transition duration-200 px-2 py-2 cursor-pointer select-none truncate rounded dark:text-white ${value.isSelected ? 'text-white bg-indigo-500' : 'dark:hover:bg-slate-600'}` 14 | }; 15 | export default SelectClassNames; 16 | export const SelectClassNamesInverted = { 17 | ...SelectClassNames, 18 | menu: SelectClassNames.menu + ' invert-menu' 19 | }; 20 | -------------------------------------------------------------------------------- /webapp/src/lib/utils/capitalize.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from '@jest/globals'; 2 | 3 | import { capitalize } from './capitalize'; 4 | 5 | const testCases = [ 6 | { in: 'hello', out: 'Hello' }, 7 | { in: 'world', out: 'World' }, 8 | { in: '', out: '' }, 9 | { in: undefined, out: '' }, 10 | { in: 'capitalize', out: 'Capitalize' } 11 | ]; 12 | 13 | describe('Test capitalize() util', () => { 14 | for (let t of testCases) { 15 | test(`Test capitalize(${t.in}) -> ${t.out}`, () => { 16 | expect(capitalize(t.in)).toBe(t.out); 17 | }); 18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /webapp/src/lib/utils/capitalize.ts: -------------------------------------------------------------------------------- 1 | export function capitalize(string?: string) { 2 | if (!string) { 3 | return ''; 4 | } 5 | return string.charAt(0).toUpperCase() + string.slice(1); 6 | } 7 | -------------------------------------------------------------------------------- /webapp/src/lib/utils/cn.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from 'clsx'; 2 | import { twMerge } from 'tailwind-merge'; 3 | 4 | // We use clsx to conditionally join class names together and twMerge to merge Tailwind CSS classes with conflict resolution. 5 | const cn = (...inputs: ClassValue[]) => { 6 | return twMerge(clsx(inputs)); 7 | }; 8 | 9 | export default cn; 10 | -------------------------------------------------------------------------------- /webapp/src/lib/utils/formatsize.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from '@jest/globals'; 2 | 3 | import formatSize from './formatsize'; 4 | 5 | const testCases = [ 6 | { in: 0, out: '0B' }, 7 | { in: 500, out: '500B' }, 8 | { in: 1024, out: '1KB' }, 9 | { in: 1048576, out: '1MB' }, 10 | { in: 1073741824, out: '1GB' }, 11 | { in: 1099511627776, out: '1TB' }, 12 | { in: 123456789, out: '117.7MB' }, 13 | { in: 9876543210, out: '9.2GB' } 14 | ]; 15 | 16 | describe('Test formatSize() util', () => { 17 | for (let t of testCases) { 18 | test(`Test formatSize(${t.in}) -> ${t.out}`, () => { 19 | expect(formatSize(t.in)).toBe(t.out); 20 | }); 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /webapp/src/lib/utils/formatsize.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Courtesy of https://gitgud.io/fatchan/jschan/-/blob/master/lib/converter/formatsize.js 4 | 5 | const sizes: string[] = ['B', 'KB', 'MB', 'GB', 'TB']; 6 | const k: number = 1024; 7 | 8 | export default function formatSize(bytes: number): string { 9 | if (bytes === 0) { 10 | return '0B'; 11 | } 12 | const i: number = Math.min(sizes.length - 1, Math.floor(Math.log(bytes) / Math.log(k))); 13 | return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))}${sizes[i]}`; 14 | } 15 | -------------------------------------------------------------------------------- /webapp/src/lib/utils/submittingreducer.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export default function submittingReducer(state, action) { 4 | return { 5 | ...state, 6 | ...action 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /webapp/src/lib/utils/tosentencecase.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from '@jest/globals'; 2 | 3 | import { toSentenceCase } from './tosentencecase'; 4 | 5 | const testCases = [ 6 | { in: 'hello_world', out: 'Hello World' }, 7 | { in: 'this_is_a_test', out: 'This Is A Test' }, 8 | { in: 'convert_to_sentence_case', out: 'Convert To Sentence Case' }, 9 | { in: 'singleword', out: 'Singleword' }, 10 | { in: '', out: '' } 11 | ]; 12 | 13 | describe('Test toSentenceCase() util', () => { 14 | for (let t of testCases) { 15 | test(`Test toSentenceCase(${t.in}) -> ${t.out}`, () => { 16 | expect(toSentenceCase(t.in)).toBe(t.out); 17 | }); 18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /webapp/src/lib/utils/tosentencecase.ts: -------------------------------------------------------------------------------- 1 | export const toSentenceCase = (str: string) => { 2 | return str 3 | .split('_') 4 | .map(word => word.charAt(0).toUpperCase() + word.slice(1)) 5 | .join(' '); 6 | }; 7 | -------------------------------------------------------------------------------- /webapp/src/migrations/0.0.3.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | const log = debug('webapp:migration:0.0.3'); 3 | 4 | export default async function (db) { 5 | log('Terminating all existing sessions, and removing their agents array'); 6 | await db.collection('sessions').updateMany( 7 | {}, 8 | { 9 | $set: { 10 | status: 'terminated' 11 | }, 12 | $unset: { 13 | agents: '' 14 | } 15 | } 16 | ); 17 | log('For each group, take the first agent and make it adminAgent, and remove it from the array.'); 18 | const groups = await db.collection('groups').find().toArray(); 19 | for (let g of groups) { 20 | const newAdminAgent = g.agents[0]; 21 | const newAgents = g.agents.slice(1); 22 | await db.collection('groups').updateOne( 23 | { 24 | _id: g._id 25 | }, 26 | { 27 | $set: { 28 | agents: newAgents, 29 | adminAgent: newAdminAgent 30 | } 31 | } 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /webapp/src/migrations/0.2.0.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | const log = debug('webapp:migration:0.2.0'); 3 | 4 | export default async function (db) { 5 | log('Updating all existing "groups" in db to groupChat: true'); 6 | await db.collection('groups').updateMany( 7 | {}, 8 | { 9 | $set: { 10 | groupChat: true 11 | } 12 | } 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /webapp/src/migrations/1.10.0.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | const log = debug('webapp:migration:1.10.0'); 3 | 4 | export default async function (db) { 5 | log('set empty ragFilters on all existing tools'); 6 | await db.collection('tools').updateMany( 7 | { 8 | type: 'rag', 9 | ragFilter: { 10 | $exists: false 11 | } 12 | }, 13 | { 14 | $set: { 15 | ragFilters: {} 16 | } 17 | } 18 | ); 19 | log('adding variableIds to all tasks and agents'); 20 | await db.collection('tasks').updateMany( 21 | { variableIds: { $exists: false } }, 22 | { 23 | $set: { 24 | variableIds: [] 25 | } 26 | } 27 | ); 28 | await db.collection('agents').updateMany( 29 | { variableIds: { $exists: false } }, 30 | { 31 | $set: { 32 | variableIds: [] 33 | } 34 | } 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /webapp/src/migrations/1.11.0.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | import { CollectionName } from 'lib/struct/db'; 3 | const log = debug('webapp:migration:1.11.0'); 4 | 5 | export default async function (db) { 6 | log('add indexes to checkpoints collection'); 7 | await db.collection(CollectionName.Checkpoints).createIndex({ checkpoint_id: 1 }); 8 | await db.collection(CollectionName.Checkpoints).createIndex({ thread_id: 1 }); 9 | } 10 | -------------------------------------------------------------------------------- /webapp/src/migrations/1.2.0.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | const log = debug('webapp:migration:1.2.0'); 3 | 4 | export default async function (db) { 5 | log('Updating apps collection with new properties'); 6 | await db.collection('apps').updateMany( 7 | {}, 8 | { 9 | $set: { 10 | memory: false, 11 | cache: false 12 | } 13 | } 14 | ); 15 | log('Updating datasource collection with new properties'); 16 | await db.collection('apps').updateMany( 17 | {}, 18 | { 19 | $set: { 20 | recordCount: {} 21 | }, 22 | $unset: { 23 | syncedCount: '', 24 | embeddedCount: '' 25 | } 26 | } 27 | ); 28 | 29 | log('Making all non builtin tasks require human input'); 30 | //NOTE: we don't use libs here e.g. SubscriptionPlan.RAW because that struct could change/not be importable anymore. all has to be encapsulated 31 | await db.collection('apps').updateMany( 32 | { 33 | 'data.builtin': false 34 | }, 35 | { 36 | $set: { 37 | requiresHumanInput: true, 38 | retriever_type: 'raw', 39 | retriever_config: {} //not required for raw 40 | } 41 | } 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /webapp/src/migrations/1.3.0.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | const log = debug('webapp:migration:1.3.0'); 3 | import { CollectionName } from 'struct/db'; 4 | 5 | export default async function (db) { 6 | log('Adding database indexes'); 7 | const collections = [ 8 | CollectionName.Accounts, 9 | CollectionName.Agents, 10 | CollectionName.Apps, 11 | CollectionName.Chat, 12 | CollectionName.Crews, 13 | CollectionName.Datasources, 14 | CollectionName.Notifications, 15 | CollectionName.Orgs, 16 | CollectionName.Tasks, 17 | CollectionName.Teams, 18 | CollectionName.Tools, 19 | CollectionName.Toolrevisions, 20 | CollectionName.Sessions 21 | ]; 22 | 23 | for (const collection of collections) { 24 | try { 25 | await db.collection(collection).createIndex({ teamId: 1 }); 26 | log(`Created index on teamId for collection: ${collection}`); 27 | } catch (error) { 28 | log(`Error creating index on teamId for collection: ${collection} - ${error.message}`); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /webapp/src/migrations/1.4.0.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | const log = debug('webapp:migration:1.4.0'); 3 | import { CollectionName } from 'struct/db'; 4 | 5 | export default async function (db) { 6 | log('creating index on sharelinks'); 7 | await db.collection(CollectionName.ShareLinks).createIndex({ shareId: 1 }); 8 | 9 | log('creating TTL index on sharelinks for payload.id null'); 10 | await db 11 | .collection(CollectionName.ShareLinks) 12 | .createIndex( 13 | { createdDate: 1 }, 14 | { expireAfterSeconds: 24 * 60 * 60, partialFilterExpression: { 'payload.id': null } } 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /webapp/src/migrations/1.5.0.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | const log = debug('webapp:migration:1.5.0'); 3 | import { CollectionName } from 'struct/db'; 4 | 5 | export default async function (db) { 6 | log('add fullOutput: false to all existing crews'); 7 | await db.collection(CollectionName.Crews).updateMany({}, { $set: { fullOutput: false } }); 8 | } 9 | -------------------------------------------------------------------------------- /webapp/src/migrations/1.6.0.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | const log = debug('webapp:migration:1.6.0'); 3 | 4 | export default async function (db) { 5 | log('Updating all sourceType: file datasources to new unstructured.io configs'); 6 | //NOTE: we don't use libs here e.g. SubscriptionPlan.RAW because that struct could change/not be importable anymore. all has to be encapsulated 7 | await db.collection('datasources').updateMany( 8 | { 9 | sourceType: 'file' 10 | }, 11 | { 12 | $unset: { 13 | chunkingStrategy: '', 14 | chunkingCharacter: '' 15 | }, 16 | $set: { 17 | chunkingConfig: { 18 | partitioning: 'auto', 19 | strategy: 'basic', 20 | max_characters: 500, 21 | new_after_n_chars: 500, 22 | overlap: 0, 23 | similarity_threshold: 0.5, 24 | overlap_all: false 25 | } 26 | } 27 | } 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /webapp/src/migrations/1.7.0.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | const log = debug('webapp:migration:1.6.0'); 3 | 4 | export default async function (db) { 5 | log('Removing displayOnlyFinalOutput from all tasks'); 6 | 7 | await db.collection('tasks').updateMany( 8 | {}, 9 | { 10 | $unset: { 11 | displayOnlyFinalOutput: '' 12 | } 13 | } 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /webapp/src/migrations/1.8.0.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | import { CollectionName } from 'struct/db'; 3 | const log = debug('webapp:migration:1.7.0'); 4 | 5 | export default async function (db) { 6 | log( 7 | 'adding a new index on datasources for "connectionId" so the enormous amount of queries from sync-server doesnt choke mongo' 8 | ); 9 | await db.collection(CollectionName.Datasources).createIndex({ connectionId: 1 }); 10 | } 11 | -------------------------------------------------------------------------------- /webapp/src/migrations/1.9.0.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | const log = debug('webapp:migration:1.9.0'); 3 | 4 | export default async function (db) { 5 | log('renaming chatAppConfig.recursionLimit to maxMessages'); 6 | await db.collection('apps').updateMany( 7 | {}, 8 | { 9 | $rename: { 10 | 'chatAppConfig.recursionLimit': 'chatAppConfig.maxMessages' 11 | } 12 | } 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /webapp/src/migrations/index.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | 3 | import semver from 'semver'; 4 | 5 | let migrationVersions = []; 6 | 7 | // rather this than write a static index file 8 | fs.readdirSync(__dirname).forEach(file => { 9 | const version = file.substring(0, file.length - 3); 10 | if (!semver.valid(version)) { 11 | return; 12 | } 13 | migrationVersions.push(version); 14 | }); 15 | 16 | const sortedVersions = migrationVersions.sort(semver.compare); 17 | const migrationVersion = sortedVersions[sortedVersions.length - 1]; 18 | 19 | export { migrationVersions, migrationVersion }; 20 | -------------------------------------------------------------------------------- /webapp/src/modules/components/stepper.tsx: -------------------------------------------------------------------------------- 1 | import cn from 'utils/cn'; 2 | 3 | type Step = { 4 | stepName: string; 5 | title?: string; 6 | description?: string; 7 | }; 8 | 9 | type StepperProps = { 10 | steps: Step[]; 11 | currentStep: number; 12 | }; 13 | 14 | export function Stepper({ steps, currentStep }: StepperProps) { 15 | return ( 16 |
    17 | {steps.map((step, index) => ( 18 |
    25 |
    31 | {index + 1} 32 |
    33 | {step.stepName} 34 |
    35 | ))} 36 |
    37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /webapp/src/modules/components/ui/badge.tsx: -------------------------------------------------------------------------------- 1 | import { cva, type VariantProps } from 'class-variance-authority'; 2 | import * as React from 'react'; 3 | import cn from 'utils/cn'; 4 | 5 | const badgeVariants = cva( 6 | 'inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', 7 | { 8 | variants: { 9 | variant: { 10 | default: 'border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80', 11 | secondary: 12 | 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80', 13 | destructive: 14 | 'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80', 15 | outline: 'text-foreground' 16 | } 17 | }, 18 | defaultVariants: { 19 | variant: 'default' 20 | } 21 | } 22 | ); 23 | 24 | export interface BadgeProps 25 | extends React.HTMLAttributes, 26 | VariantProps {} 27 | 28 | function Badge({ className, variant, ...props }: BadgeProps) { 29 | return
    ; 30 | } 31 | 32 | export { Badge, badgeVariants }; 33 | -------------------------------------------------------------------------------- /webapp/src/modules/components/ui/checkbox.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as CheckboxPrimitive from '@radix-ui/react-checkbox'; 3 | import { Check } from 'lucide-react'; 4 | import cn from 'utils/cn'; 5 | 6 | const Checkbox = React.forwardRef< 7 | React.ElementRef, 8 | React.ComponentPropsWithoutRef 9 | >(({ className, ...props }, ref) => ( 10 | 18 | 19 | 20 | 21 | 22 | )); 23 | Checkbox.displayName = CheckboxPrimitive.Root.displayName; 24 | 25 | export { Checkbox }; 26 | -------------------------------------------------------------------------------- /webapp/src/modules/components/ui/collapsible.tsx: -------------------------------------------------------------------------------- 1 | import * as CollapsiblePrimitive from '@radix-ui/react-collapsible'; 2 | 3 | const Collapsible = CollapsiblePrimitive.Root; 4 | 5 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger; 6 | 7 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent; 8 | 9 | export { Collapsible, CollapsibleTrigger, CollapsibleContent }; 10 | -------------------------------------------------------------------------------- /webapp/src/modules/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import cn from 'utils/cn'; 4 | 5 | const Input = React.forwardRef>( 6 | ({ className, type, ...props }, ref) => { 7 | return ( 8 | 17 | ); 18 | } 19 | ); 20 | Input.displayName = 'Input'; 21 | 22 | export { Input }; 23 | -------------------------------------------------------------------------------- /webapp/src/modules/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | import * as LabelPrimitive from '@radix-ui/react-label'; 2 | import { cva, type VariantProps } from 'class-variance-authority'; 3 | import * as React from 'react'; 4 | import cn from 'utils/cn'; 5 | 6 | const labelVariants = cva( 7 | 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70' 8 | ); 9 | 10 | const Label = React.forwardRef< 11 | React.ElementRef, 12 | React.ComponentPropsWithoutRef & VariantProps 13 | >(({ className, ...props }, ref) => ( 14 | 15 | )); 16 | Label.displayName = LabelPrimitive.Root.displayName; 17 | 18 | export { Label }; 19 | -------------------------------------------------------------------------------- /webapp/src/modules/components/ui/progress.tsx: -------------------------------------------------------------------------------- 1 | import * as ProgressPrimitive from '@radix-ui/react-progress'; 2 | import * as React from 'react'; 3 | import cn from 'utils/cn'; 4 | 5 | const Progress = React.forwardRef< 6 | React.ElementRef, 7 | React.ComponentPropsWithoutRef 8 | >(({ className, value, ...props }, ref) => ( 9 | 14 | 18 | 19 | )); 20 | Progress.displayName = ProgressPrimitive.Root.displayName; 21 | 22 | export { Progress }; 23 | -------------------------------------------------------------------------------- /webapp/src/modules/components/ui/separator.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as SeparatorPrimitive from '@radix-ui/react-separator'; 3 | 4 | import cn from 'utils/cn'; 5 | 6 | const Separator = React.forwardRef< 7 | React.ElementRef, 8 | React.ComponentPropsWithoutRef 9 | >(({ className, orientation = 'horizontal', decorative = true, ...props }, ref) => ( 10 | 21 | )); 22 | Separator.displayName = SeparatorPrimitive.Root.displayName; 23 | 24 | export { Separator }; 25 | -------------------------------------------------------------------------------- /webapp/src/modules/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import cn from 'utils/cn'; 2 | 3 | function Skeleton({ className, ...props }: React.HTMLAttributes) { 4 | return
    ; 5 | } 6 | 7 | export { Skeleton }; 8 | -------------------------------------------------------------------------------- /webapp/src/modules/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import cn from 'utils/cn'; 3 | 4 | const Textarea = React.forwardRef>( 5 | ({ className, ...props }, ref) => { 6 | return ( 7 |