diff --git a/agenta-web/src/components/AppSelector/AppCard.tsx b/agenta-web/src/components/AppSelector/AppCard.tsx index 0fcd89b55d..2cceb51363 100644 --- a/agenta-web/src/components/AppSelector/AppCard.tsx +++ b/agenta-web/src/components/AppSelector/AppCard.tsx @@ -1,11 +1,10 @@ import {Modal, Card, Avatar} from "antd" import {DeleteOutlined} from "@ant-design/icons" -import {removeApp} from "@/lib/services/api" +import {deleteApp} from "@/services/app-selector/api" import {useState} from "react" import Link from "next/link" import {renameVariablesCapitalizeAll} from "@/lib/helpers/utils" import {createUseStyles} from "react-jss" -import {getGradientFromStr} from "@/lib/helpers/colors" import {ListAppsItem} from "@/lib/Types" import {useAppsData} from "@/contexts/app.context" @@ -87,7 +86,7 @@ const AppCard: React.FC<{ const handleDeleteOk = async () => { setConfirmLoading(true) try { - await removeApp(app.app_id) + await deleteApp(app.app_id) mutate() } catch (error) { console.error(error) diff --git a/agenta-web/src/components/AppSelector/AppSelector.tsx b/agenta-web/src/components/AppSelector/AppSelector.tsx index a8166395ab..5cc43b43b4 100644 --- a/agenta-web/src/components/AppSelector/AppSelector.tsx +++ b/agenta-web/src/components/AppSelector/AppSelector.tsx @@ -7,12 +7,8 @@ import {useAppTheme} from "../Layout/ThemeContextProvider" import TipsAndFeatures from "./TipsAndFeatures" import Welcome from "./Welcome" import {isAppNameInputValid, isDemo, redirectIfNoLLMKeys} from "@/lib/helpers/utils" -import { - createAndStartTemplate, - getTemplates, - removeApp, - waitForAppToStart, -} from "@/lib/services/api" +import {createAndStartTemplate, fetchAllTemplates, deleteApp} from "@/services/app-selector/api" +import {waitForAppToStart} from "@/services/api" import AddNewAppModal from "./modals/AddNewAppModal" import AddAppFromTemplatedModal from "./modals/AddAppFromTemplateModal" import MaxAppModal from "./modals/MaxAppModal" @@ -22,7 +18,7 @@ import {useAppsData} from "@/contexts/app.context" import {useProfileData} from "@/contexts/profile.context" import CreateAppStatusModal from "./modals/CreateAppStatusModal" import {usePostHogAg} from "@/hooks/usePostHogAg" -import {LlmProvider, getAllProviderLlmKeys, getApikeys} from "@/lib/helpers/llmProviders" +import {LlmProvider, getAllProviderLlmKeys} from "@/lib/helpers/llmProviders" import ResultComponent from "../ResultComponent/ResultComponent" import {dynamicContext} from "@/lib/helpers/dynamic" @@ -175,7 +171,7 @@ const AppSelector: React.FC = () => { useEffect(() => { if (!isLoading) mutate() const fetchTemplates = async () => { - const data = await getTemplates() + const data = await fetchAllTemplates() if (typeof data == "object") { setTemplates(data) } else { @@ -226,7 +222,7 @@ const AppSelector: React.FC = () => { const onErrorRetry = async () => { if (statusData.appId) { setStatusData((prev) => ({...prev, status: "cleanup", details: undefined})) - await removeApp(statusData.appId).catch(console.error) + await deleteApp(statusData.appId).catch(console.error) mutate() } handleTemplateCardClick(templateId as string) diff --git a/agenta-web/src/components/EvaluationTable/ABTestingEvaluationTable.tsx b/agenta-web/src/components/EvaluationTable/ABTestingEvaluationTable.tsx index 473e9d93d5..8f209da494 100644 --- a/agenta-web/src/components/EvaluationTable/ABTestingEvaluationTable.tsx +++ b/agenta-web/src/components/EvaluationTable/ABTestingEvaluationTable.tsx @@ -15,10 +15,10 @@ import { } from "antd" import { updateEvaluationScenario, - callVariant, fetchEvaluationResults, updateEvaluation, -} from "@/lib/services/api" +} from "@/services/human-evaluations/api" +import {callVariant} from "@/services/api" import {useVariants} from "@/lib/hooks/useVariant" import {useRouter} from "next/router" import {EvaluationFlow} from "@/lib/enums" diff --git a/agenta-web/src/components/EvaluationTable/SingleModelEvaluationTable.tsx b/agenta-web/src/components/EvaluationTable/SingleModelEvaluationTable.tsx index cc32f28b5b..e6af1b5162 100644 --- a/agenta-web/src/components/EvaluationTable/SingleModelEvaluationTable.tsx +++ b/agenta-web/src/components/EvaluationTable/SingleModelEvaluationTable.tsx @@ -1,4 +1,4 @@ -import {useState, useEffect, useCallback, useMemo} from "react" +import {useState, useEffect, useCallback} from "react" import type {ColumnType} from "antd/es/table" import {CaretRightOutlined} from "@ant-design/icons" import { @@ -15,7 +15,8 @@ import { Typography, message, } from "antd" -import {updateEvaluationScenario, callVariant, updateEvaluation} from "@/lib/services/api" +import {callVariant} from "@/services/api" +import {updateEvaluationScenario, updateEvaluation} from "@/services/human-evaluations/api" import {useVariants} from "@/lib/hooks/useVariant" import {useRouter} from "next/router" import {EvaluationFlow} from "@/lib/enums" diff --git a/agenta-web/src/components/Evaluations/AutomaticEvaluationResult.tsx b/agenta-web/src/components/Evaluations/AutomaticEvaluationResult.tsx index 7ee25cc43f..251fbc6319 100644 --- a/agenta-web/src/components/Evaluations/AutomaticEvaluationResult.tsx +++ b/agenta-web/src/components/Evaluations/AutomaticEvaluationResult.tsx @@ -1,4 +1,8 @@ -import {deleteEvaluations, fetchEvaluationResults, loadEvaluations} from "@/lib/services/api" +import { + deleteEvaluations, + fetchEvaluationResults, + fetchAllLoadEvaluations, +} from "@/services/human-evaluations/api" import {Button, Spin, Statistic, Table, Typography} from "antd" import {useRouter} from "next/router" import {useEffect, useState} from "react" @@ -101,7 +105,7 @@ export default function AutomaticEvaluationResult({ const fetchEvaluations = async () => { try { setFetchingEvaluations(true) - const evals: Evaluation[] = (await loadEvaluations(app_id)).map( + const evals: Evaluation[] = (await fetchAllLoadEvaluations(app_id)).map( fromEvaluationResponseToEvaluation, ) const results = await Promise.all(evals.map((e) => fetchEvaluationResults(e.id))) diff --git a/agenta-web/src/components/Evaluations/HumanEvaluationResult.tsx b/agenta-web/src/components/Evaluations/HumanEvaluationResult.tsx index a77421dd31..5eee97ec44 100644 --- a/agenta-web/src/components/Evaluations/HumanEvaluationResult.tsx +++ b/agenta-web/src/components/Evaluations/HumanEvaluationResult.tsx @@ -1,4 +1,5 @@ -import {deleteEvaluations, fetchData} from "@/lib/services/api" +import {fetchData} from "@/services/api" +import {deleteEvaluations} from "@/services/human-evaluations/api" import {Button, Spin, Statistic, Table, Typography} from "antd" import {useRouter} from "next/router" import {useEffect, useState} from "react" diff --git a/agenta-web/src/components/HumanEvaluationModal/HumanEvaluationModal.tsx b/agenta-web/src/components/HumanEvaluationModal/HumanEvaluationModal.tsx index e546d99f0d..983324b13a 100644 --- a/agenta-web/src/components/HumanEvaluationModal/HumanEvaluationModal.tsx +++ b/agenta-web/src/components/HumanEvaluationModal/HumanEvaluationModal.tsx @@ -1,6 +1,7 @@ import React, {useEffect, useState} from "react" import {GenericObject, JSSTheme, Parameter, Variant} from "@/lib/Types" -import {createNewEvaluation, fetchVariants, useLoadTestsetsList} from "@/lib/services/api" +import {fetchVariants} from "@/services/api" +import {createNewEvaluation} from "@/services/human-evaluations/api" import {isDemo} from "@/lib/helpers/utils" import {Button, Col, Dropdown, MenuProps, Modal, ModalProps, Row, Spin, message} from "antd" import {getErrorMessage} from "@/lib/helpers/errorHandler" @@ -11,9 +12,9 @@ import {getAllVariantParameters} from "@/lib/helpers/variantHelper" import {useRouter} from "next/router" import {useAppTheme} from "../Layout/ThemeContextProvider" import {createUseStyles} from "react-jss" -import {getApikeys} from "@/lib/helpers/llmProviders" import EvaluationErrorModal from "../Evaluations/EvaluationErrorModal" import {dynamicComponent} from "@/lib/helpers/dynamic" +import {useLoadTestsetsList} from "@/services/testsets/api" type StyleProps = { themeMode: "dark" | "light" diff --git a/agenta-web/src/components/Layout/Layout.tsx b/agenta-web/src/components/Layout/Layout.tsx index 9355a0e05d..38662ec4a6 100644 --- a/agenta-web/src/components/Layout/Layout.tsx +++ b/agenta-web/src/components/Layout/Layout.tsx @@ -20,7 +20,7 @@ import {createUseStyles} from "react-jss" import NoSSRWrapper from "../NoSSRWrapper/NoSSRWrapper" import {ErrorBoundary} from "react-error-boundary" import ErrorFallback from "./ErrorFallback" -import {fetchData} from "@/lib/services/api" +import {fetchData} from "@/services/api" import {useAppsData} from "@/contexts/app.context" import {useRouter} from "next/router" import Image from "next/image" diff --git a/agenta-web/src/components/Playground/AddToTestSetDrawer/AddToTestSetDrawer.tsx b/agenta-web/src/components/Playground/AddToTestSetDrawer/AddToTestSetDrawer.tsx index 177b24a16d..ba7d4f2a8c 100644 --- a/agenta-web/src/components/Playground/AddToTestSetDrawer/AddToTestSetDrawer.tsx +++ b/agenta-web/src/components/Playground/AddToTestSetDrawer/AddToTestSetDrawer.tsx @@ -2,7 +2,12 @@ import AlertPopup from "@/components/AlertPopup/AlertPopup" import {useAppTheme} from "../../Layout/ThemeContextProvider" import {ChatMessage, ChatRole, GenericObject, testset} from "@/lib/Types" import {removeKeys, renameVariables} from "@/lib/helpers/utils" -import {createNewTestset, loadTestset, updateTestset, useLoadTestsetsList} from "@/lib/services/api" +import { + createNewTestset, + fetchTestset, + updateTestset, + useLoadTestsetsList, +} from "@/services/testsets/api" import { Button, Divider, @@ -193,7 +198,7 @@ const AddToTestSetDrawer: React.FC = ({params, isChatVariant, ...props}) if (isNew) { setNewTestsetModalOpen(true) } else { - loadTestset(selectedTestset!).then((data) => { + fetchTestset(selectedTestset!).then((data) => { const testsetCols = Object.keys(data.csvdata?.[0] || {}) const playgroundCols = Object.keys(values[0]) const missingColsTestset = testsetCols.filter( diff --git a/agenta-web/src/components/Playground/LoadTestsModal.tsx b/agenta-web/src/components/Playground/LoadTestsModal.tsx index 4510871a02..e3fcedf621 100644 --- a/agenta-web/src/components/Playground/LoadTestsModal.tsx +++ b/agenta-web/src/components/Playground/LoadTestsModal.tsx @@ -1,5 +1,5 @@ -import {loadTestset, useLoadTestsetsList} from "@/lib/services/api" -import {Button, Divider, Dropdown, Modal, Select, Space} from "antd" +import {fetchTestset, useLoadTestsetsList} from "@/services/testsets/api" +import {Button, Divider, Modal, Select} from "antd" import {useRouter} from "next/router" import {PropsWithChildren, useState} from "react" import {createUseStyles} from "react-jss" @@ -38,7 +38,7 @@ const LoadTestsModal: React.FC = (props) => { })) const handleClick = (shouldReplace: boolean) => { - loadTestset(selectedSet).then((data) => { + fetchTestset(selectedSet).then((data) => { onLoad(data.csvdata, shouldReplace) }) setIsOpen(false) diff --git a/agenta-web/src/components/Playground/Playground.tsx b/agenta-web/src/components/Playground/Playground.tsx index 70110249c1..1777822149 100644 --- a/agenta-web/src/components/Playground/Playground.tsx +++ b/agenta-web/src/components/Playground/Playground.tsx @@ -2,7 +2,8 @@ import React, {useState, useEffect, useRef} from "react" import {Button, Tabs, message} from "antd" import ViewNavigation from "./ViewNavigation" import NewVariantModal from "./NewVariantModal" -import {fetchEnvironments, fetchVariants, saveNewVariant} from "@/lib/services/api" +import {fetchVariants} from "@/services/api" +import {fetchEnvironments} from "@/services/deployment/api" import {Variant, PlaygroundTabsItem, Environment} from "@/lib/Types" import {AppstoreOutlined, SyncOutlined} from "@ant-design/icons" import {useRouter} from "next/router" @@ -17,6 +18,7 @@ import {useLocalStorage} from "usehooks-ts" import TestContextProvider from "./TestContextProvider" import {checkIfResourceValidForDeletion} from "@/lib/helpers/evaluate" import ResultComponent from "../ResultComponent/ResultComponent" +import {createNewVariant} from "@/services/playground/api" const Playground: React.FC = () => { const router = useRouter() @@ -78,7 +80,7 @@ const Playground: React.FC = () => { } try { - await saveNewVariant( + await createNewVariant( newVariant.baseId!, newVariant.variantName!, newVariant.configName!, diff --git a/agenta-web/src/components/Playground/ViewNavigation.tsx b/agenta-web/src/components/Playground/ViewNavigation.tsx index 57b74058ef..8bd925e18f 100644 --- a/agenta-web/src/components/Playground/ViewNavigation.tsx +++ b/agenta-web/src/components/Playground/ViewNavigation.tsx @@ -8,16 +8,16 @@ import {useRouter} from "next/router" import {useState} from "react" import axios from "axios" import {createUseStyles} from "react-jss" -import { - fetchVariantLogs, - getAppContainerURL, - removeVariant, - restartAppVariantContainer, - waitForAppToStart, -} from "@/lib/services/api" + +import {fetchAppContainerURL, waitForAppToStart} from "@/services/api" import {useAppsData} from "@/contexts/app.context" import {isDemo} from "@/lib/helpers/utils" import ResultComponent from "../ResultComponent/ResultComponent" +import { + deleteSingleVariant, + fetchVariantLogs, + restartAppVariantContainer, +} from "@/services/playground/api" const {Text} = Typography @@ -141,7 +141,7 @@ const ViewNavigation: React.FC = ({ } const variantContainerPath = async () => { - const url = await getAppContainerURL(appId, variant.variantId, variant.baseId) + const url = await fetchAppContainerURL(appId, variant.variantId, variant.baseId) setContainerURI(url) } if (!containerURI) { @@ -231,7 +231,7 @@ const ViewNavigation: React.FC = ({ type="primary" danger onClick={() => { - deleteVariant(() => removeVariant(variant.variantId)) + deleteVariant(() => deleteSingleVariant(variant.variantId)) }} > diff --git a/agenta-web/src/components/Playground/Views/ParametersView.tsx b/agenta-web/src/components/Playground/Views/ParametersView.tsx index 04c3899f14..08ad60b802 100644 --- a/agenta-web/src/components/Playground/Views/ParametersView.tsx +++ b/agenta-web/src/components/Playground/Views/ParametersView.tsx @@ -5,16 +5,17 @@ import React, {useEffect, useState} from "react" import {createUseStyles} from "react-jss" import {ModelParameters, ObjectParameters, StringParameters} from "./ParametersCards" import PublishVariantModal from "./PublishVariantModal" -import {promptVersioning, removeVariant} from "@/lib/services/api" +import {deleteSingleVariant} from "@/services/playground/api" import {CloudUploadOutlined, DeleteOutlined, HistoryOutlined, SaveOutlined} from "@ant-design/icons" import {usePostHogAg} from "@/hooks/usePostHogAg" import {isDemo} from "@/lib/helpers/utils" import {useQueryParam} from "@/hooks/useQuery" -import {dynamicComponent} from "@/lib/helpers/dynamic" +import {dynamicComponent, dynamicService} from "@/lib/helpers/dynamic" const PromptVersioningDrawer: any = dynamicComponent( `PromptVersioningDrawer/PromptVersioningDrawer`, ) +const promptVersioning: any = dynamicService("promptVersioning/api") interface Props { variant: Variant @@ -133,7 +134,7 @@ const ParametersView: React.FC = ({ const handleDelete = () => { deleteVariant(() => { if (variant.persistent) { - return removeVariant(variant.variantId).then(() => { + return deleteSingleVariant(variant.variantId).then(() => { onStateChange(false) }) } @@ -151,10 +152,13 @@ const ParametersView: React.FC = ({ setHistoryStatus({loading: true, error: false}) setIsDrawerOpen(true) try { - if (variant.variantId && isDemo()) { - const revisions = await promptVersioning(variant.variantId) - setPromptRevisions(revisions) - } + await promptVersioning.then(async (module: any) => { + if (!module) return + if (variant.variantId && isDemo()) { + const revisions = await module.fetchAllPromptVersioning(variant.variantId) + setPromptRevisions(revisions) + } + }) setHistoryStatus({loading: false, error: false}) } catch (error) { setHistoryStatus({loading: false, error: true}) diff --git a/agenta-web/src/components/Playground/Views/PublishVariantModal.tsx b/agenta-web/src/components/Playground/Views/PublishVariantModal.tsx index 288ec5261b..f4f03608a8 100644 --- a/agenta-web/src/components/Playground/Views/PublishVariantModal.tsx +++ b/agenta-web/src/components/Playground/Views/PublishVariantModal.tsx @@ -1,7 +1,7 @@ import {usePostHogAg} from "@/hooks/usePostHogAg" import {Environment, Variant} from "@/lib/Types" import {variantNameWithRev} from "@/lib/helpers/variantHelper" -import {fetchEnvironments, publishVariant} from "@/lib/services/api" +import {fetchEnvironments, createPublishVariant} from "@/services/deployment/api" import {Button, Checkbox, Modal, Space, Typography, message} from "antd" import type {CheckboxChangeEvent} from "antd/es/checkbox" import {useRouter} from "next/router" @@ -54,7 +54,7 @@ const PublishVariantModal: React.FC = ({ const publishVariants = async () => { selectedEnvs.forEach(async (envName) => { - await publishVariant(variant.variantId, envName) + await createPublishVariant(variant.variantId, envName) closeModal() await loadEnvironments() message.success(`Published ${variant.variantName} to ${envName}`) diff --git a/agenta-web/src/components/Playground/Views/TestView.tsx b/agenta-web/src/components/Playground/Views/TestView.tsx index 3c1633cefa..4d9cb7ae61 100644 --- a/agenta-web/src/components/Playground/Views/TestView.tsx +++ b/agenta-web/src/components/Playground/Views/TestView.tsx @@ -1,7 +1,7 @@ import React, {useContext, useEffect, useRef, useState} from "react" import {Button, Input, Card, Row, Col, Space, Form, Modal} from "antd" import {CaretRightOutlined, CloseCircleOutlined, PlusOutlined} from "@ant-design/icons" -import {callVariant, promptRevision} from "@/lib/services/api" +import {callVariant} from "@/services/api" import {ChatMessage, ChatRole, GenericObject, JSSTheme, Parameter, Variant} from "@/lib/Types" import {batchExecute, randString, removeKeys} from "@/lib/helpers/utils" import LoadTestsModal from "../LoadTestsModal" @@ -23,6 +23,9 @@ import relativeTime from "dayjs/plugin/relativeTime" import duration from "dayjs/plugin/duration" import {useQueryParam} from "@/hooks/useQuery" import {formatCurrency, formatLatency, formatTokenUsage} from "@/lib/helpers/formatters" +import {dynamicService} from "@/lib/helpers/dynamic" + +const promptRevision: any = dynamicService("promptVersioning/api") dayjs.extend(relativeTime) dayjs.extend(duration) @@ -333,42 +336,50 @@ const App: React.FC = ({ usage: {completion_tokens: number; prompt_tokens: number; total_tokens: number} | null }> >(testList.map(() => ({cost: null, latency: null, usage: null}))) - const [revisionNum, setRevisionNum] = useQueryParam("revision") + const [revisionNum] = useQueryParam("revision") useEffect(() => { if (!revisionNum) return const fetchData = async () => { - const revision = await promptRevision(variant.variantId, parseInt(revisionNum)) - if (!revision) return + await promptRevision.then(async (module: any) => { + if (!module) return - setPromptOptParams((prevState: Parameter[] | null) => { - if (!prevState) { - return prevState - } + const revision = await module.fetchPromptRevision( + variant.variantId, + parseInt(revisionNum), + ) - const parameterNames = [ - "temperature", - "model", - "max_tokens", - "prompt_system", - "prompt_user", - "top_p", - "frequence_penalty", - "presence_penalty", - "inputs", - ] - - return prevState.map((param: Parameter) => { - if (parameterNames.includes(param.name)) { - const newValue = (revision?.config.parameters as Record)[ - param.name - ] - if (newValue !== undefined) { - param.default = newValue - } + if (!revision) return + + setPromptOptParams((prevState: Parameter[] | null) => { + if (!prevState) { + return prevState } - return param + + const parameterNames = [ + "temperature", + "model", + "max_tokens", + "prompt_system", + "prompt_user", + "top_p", + "frequence_penalty", + "presence_penalty", + "inputs", + ] + + return prevState.map((param: Parameter) => { + if (parameterNames.includes(param.name)) { + const newValue = (revision?.config.parameters as Record)[ + param.name + ] + if (newValue !== undefined) { + param.default = newValue + } + } + return param + }) }) }) } diff --git a/agenta-web/src/components/SaveTestsetModal/SaveTestsetModal.tsx b/agenta-web/src/components/SaveTestsetModal/SaveTestsetModal.tsx index e6a14f758b..2526ff6c93 100644 --- a/agenta-web/src/components/SaveTestsetModal/SaveTestsetModal.tsx +++ b/agenta-web/src/components/SaveTestsetModal/SaveTestsetModal.tsx @@ -2,7 +2,7 @@ import React, {useEffect, useState} from "react" import {useAppId} from "@/hooks/useAppId" import {Evaluation, EvaluationScenario} from "@/lib/Types" import {EvaluationFlow} from "@/lib/enums" -import {createNewTestset} from "@/lib/services/api" +import {createNewTestset} from "@/services/testsets/api" import {Form, Input, Modal} from "antd" type EvaluationRow = EvaluationScenario & { diff --git a/agenta-web/src/components/TestSetTable/TestsetTable.tsx b/agenta-web/src/components/TestSetTable/TestsetTable.tsx index 29c3b9c4f5..dad1316aca 100644 --- a/agenta-web/src/components/TestSetTable/TestsetTable.tsx +++ b/agenta-web/src/components/TestSetTable/TestsetTable.tsx @@ -4,7 +4,8 @@ import {createUseStyles} from "react-jss" import {Button, Input, Tooltip, Typography, message} from "antd" import TestsetMusHaveNameModal from "./InsertTestsetNameModal" import {DeleteOutlined, EditOutlined, PlusOutlined} from "@ant-design/icons" -import {createNewTestset, fetchVariants, loadTestset, updateTestset} from "@/lib/services/api" +import {fetchVariants} from "@/services/api" +import {createNewTestset, fetchTestset, updateTestset} from "@/services/testsets/api" import {useRouter} from "next/router" import {useAppTheme} from "../Layout/ThemeContextProvider" import useBlockNavigation from "@/hooks/useBlockNavigation" @@ -214,7 +215,7 @@ const TestsetTable: React.FC = ({mode}) => { if (mode === "edit" && testset_id) { setLoading(true) - loadTestset(testset_id as string).then((data) => { + fetchTestset(testset_id as string).then((data) => { setTestsetName(data.name) setRowData(data.csvdata) applyColData( diff --git a/agenta-web/src/components/pages/evaluations/evaluationCompare/EvaluationCompare.tsx b/agenta-web/src/components/pages/evaluations/evaluationCompare/EvaluationCompare.tsx index 428c6dea0a..0069b9fe0b 100644 --- a/agenta-web/src/components/pages/evaluations/evaluationCompare/EvaluationCompare.tsx +++ b/agenta-web/src/components/pages/evaluations/evaluationCompare/EvaluationCompare.tsx @@ -8,7 +8,7 @@ import { _Evaluation, _EvaluationScenario, } from "@/lib/Types" -import {fetchAllComparisonResults} from "@/services/evaluations" +import {fetchAllComparisonResults} from "@/services/evaluations/api" import {ColDef} from "ag-grid-community" import {AgGridReact} from "ag-grid-react" import {Button, DropdownProps, Space, Spin, Tag, Tooltip, Typography} from "antd" diff --git a/agenta-web/src/components/pages/evaluations/evaluationResults/EvaluationResults.tsx b/agenta-web/src/components/pages/evaluations/evaluationResults/EvaluationResults.tsx index a3245c19cd..ee243c0eee 100644 --- a/agenta-web/src/components/pages/evaluations/evaluationResults/EvaluationResults.tsx +++ b/agenta-web/src/components/pages/evaluations/evaluationResults/EvaluationResults.tsx @@ -18,7 +18,11 @@ import relativeTime from "dayjs/plugin/relativeTime" import duration from "dayjs/plugin/duration" import NewEvaluationModal from "./NewEvaluationModal" import {useAppId} from "@/hooks/useAppId" -import {deleteEvaluations, fetchAllEvaluations, fetchEvaluationStatus} from "@/services/evaluations" +import { + deleteEvaluations, + fetchAllEvaluations, + fetchEvaluationStatus, +} from "@/services/evaluations/api" import {useUpdateEffect} from "usehooks-ts" import {shortPoll} from "@/lib/helpers/utils" import AlertPopup from "@/components/AlertPopup/AlertPopup" diff --git a/agenta-web/src/components/pages/evaluations/evaluationResults/NewEvaluationModal.tsx b/agenta-web/src/components/pages/evaluations/evaluationResults/NewEvaluationModal.tsx index 3da4f6629d..cb1f8a1276 100644 --- a/agenta-web/src/components/pages/evaluations/evaluationResults/NewEvaluationModal.tsx +++ b/agenta-web/src/components/pages/evaluations/evaluationResults/NewEvaluationModal.tsx @@ -2,8 +2,9 @@ import {useAppId} from "@/hooks/useAppId" import {JSSTheme, Variant, LLMRunRateLimit, testset} from "@/lib/Types" import {evaluatorConfigsAtom, evaluatorsAtom} from "@/lib/atoms/evaluation" import {apiKeyObject, redirectIfNoLLMKeys} from "@/lib/helpers/utils" -import {fetchTestsets, fetchVariants} from "@/lib/services/api" -import {CreateEvaluationData, createEvalutaiton} from "@/services/evaluations" +import {fetchVariants} from "@/services/api" +import {CreateEvaluationData, createEvalutaiton} from "@/services/evaluations/api" +import {fetchTestsets} from "@/services/testsets/api" import {PlusOutlined, QuestionCircleOutlined} from "@ant-design/icons" import { Divider, diff --git a/agenta-web/src/components/pages/evaluations/evaluationScenarios/EvaluationScenarios.tsx b/agenta-web/src/components/pages/evaluations/evaluationScenarios/EvaluationScenarios.tsx index 0d774a5ee7..573491eb47 100644 --- a/agenta-web/src/components/pages/evaluations/evaluationScenarios/EvaluationScenarios.tsx +++ b/agenta-web/src/components/pages/evaluations/evaluationScenarios/EvaluationScenarios.tsx @@ -5,7 +5,7 @@ import { deleteEvaluations, fetchAllEvaluationScenarios, fetchAllEvaluators, -} from "@/services/evaluations" +} from "@/services/evaluations/api" import {CheckOutlined, DeleteOutlined, DownloadOutlined} from "@ant-design/icons" import {ColDef} from "ag-grid-community" import {AgGridReact} from "ag-grid-react" diff --git a/agenta-web/src/components/pages/evaluations/evaluators/EvaluatorCard.tsx b/agenta-web/src/components/pages/evaluations/evaluators/EvaluatorCard.tsx index bd3550bd0b..28a11f35bc 100644 --- a/agenta-web/src/components/pages/evaluations/evaluators/EvaluatorCard.tsx +++ b/agenta-web/src/components/pages/evaluations/evaluators/EvaluatorCard.tsx @@ -6,7 +6,7 @@ import {createUseStyles} from "react-jss" import dayjs from "dayjs" import Image from "next/image" import AlertPopup from "@/components/AlertPopup/AlertPopup" -import {deleteEvaluatorConfig} from "@/services/evaluations" +import {deleteEvaluatorConfig} from "@/services/evaluations/api" import {useAtom} from "jotai" import {evaluatorsAtom} from "@/lib/atoms/evaluation" import {checkIfResourceValidForDeletion} from "@/lib/helpers/evaluate" diff --git a/agenta-web/src/components/pages/evaluations/evaluators/Evaluators.tsx b/agenta-web/src/components/pages/evaluations/evaluators/Evaluators.tsx index fc6c242df9..d02fa6a569 100644 --- a/agenta-web/src/components/pages/evaluations/evaluators/Evaluators.tsx +++ b/agenta-web/src/components/pages/evaluations/evaluators/Evaluators.tsx @@ -5,7 +5,7 @@ import {Button, Empty, Input, Space, Spin} from "antd" import {PlusCircleOutlined} from "@ant-design/icons" import NewEvaluatorModal from "./NewEvaluatorModal" import {useAppId} from "@/hooks/useAppId" -import {fetchAllEvaluatorConfigs} from "@/services/evaluations" +import {fetchAllEvaluatorConfigs} from "@/services/evaluations/api" import {useAtom} from "jotai" import {evaluatorConfigsAtom} from "@/lib/atoms/evaluation" import {JSSTheme} from "@/lib/Types" diff --git a/agenta-web/src/components/pages/evaluations/evaluators/NewEvaluatorModal.tsx b/agenta-web/src/components/pages/evaluations/evaluators/NewEvaluatorModal.tsx index b6fdd618fe..d96675aa4b 100644 --- a/agenta-web/src/components/pages/evaluations/evaluators/NewEvaluatorModal.tsx +++ b/agenta-web/src/components/pages/evaluations/evaluators/NewEvaluatorModal.tsx @@ -7,7 +7,7 @@ import { CreateEvaluationConfigData, createEvaluatorConfig, updateEvaluatorConfig, -} from "@/services/evaluations" +} from "@/services/evaluations/api" import {ArrowLeftOutlined, EditOutlined, InfoCircleOutlined, PlusOutlined} from "@ant-design/icons" import {Editor} from "@monaco-editor/react" import {Button, Form, Input, InputNumber, Modal, Switch, Table, Tooltip, message, theme} from "antd" diff --git a/agenta-web/src/contexts/app.context.tsx b/agenta-web/src/contexts/app.context.tsx index 7cf2990e8d..67314cbad3 100644 --- a/agenta-web/src/contexts/app.context.tsx +++ b/agenta-web/src/contexts/app.context.tsx @@ -1,6 +1,6 @@ import {ListAppsItem} from "@/lib/Types" import {getAgentaApiUrl, isDemo} from "@/lib/helpers/utils" -import {axiosFetcher} from "@/lib/services/api" +import {axiosFetcher} from "@/services/api" import {useRouter} from "next/router" import {PropsWithChildren, createContext, useContext, useEffect, useMemo, useState} from "react" import useSWR from "swr" diff --git a/agenta-web/src/contexts/profile.context.tsx b/agenta-web/src/contexts/profile.context.tsx index d39c3f9b8d..a59ffa4229 100644 --- a/agenta-web/src/contexts/profile.context.tsx +++ b/agenta-web/src/contexts/profile.context.tsx @@ -2,7 +2,7 @@ import {usePostHogAg} from "@/hooks/usePostHogAg" import {useSession} from "@/hooks/useSession" import useStateCallback from "@/hooks/useStateCallback" import {isDemo} from "@/lib/helpers/utils" -import {getProfile} from "@/lib/services/api" +import {fetchProfile} from "@/services/api" import {User} from "@/lib/Types" import {PropsWithChildren, createContext, useState, useContext, useEffect, useCallback} from "react" @@ -36,7 +36,7 @@ const ProfileContextProvider: React.FC = ({children}) => { const fetcher = useCallback((onSuccess?: () => void) => { setLoading(true) - getProfile() + fetchProfile() .then((profile) => { posthog.identify() setUser(profile.data, onSuccess) diff --git a/agenta-web/src/lib/helpers/evaluate.ts b/agenta-web/src/lib/helpers/evaluate.ts index 915bc44ed0..60b8975cc2 100644 --- a/agenta-web/src/lib/helpers/evaluate.ts +++ b/agenta-web/src/lib/helpers/evaluate.ts @@ -8,7 +8,7 @@ import { EvaluationScenario, } from "../Types" import {convertToCsv, downloadCsv} from "./fileManipulations" -import {fetchEvaluatonIdsByResource} from "@/services/evaluations" +import {fetchEvaluatonIdsByResource} from "@/services/evaluations/api" import {getAppValues} from "@/contexts/app.context" import AlertPopup from "@/components/AlertPopup/AlertPopup" import {capitalize, round} from "lodash" diff --git a/agenta-web/src/lib/helpers/variantHelper.ts b/agenta-web/src/lib/helpers/variantHelper.ts index dcfdc205e0..9e3b9fe126 100644 --- a/agenta-web/src/lib/helpers/variantHelper.ts +++ b/agenta-web/src/lib/helpers/variantHelper.ts @@ -1,6 +1,5 @@ import {Variant, Parameter, InputParameter} from "@/lib/Types" -import {getVariantParametersFromOpenAPI} from "@/lib/services/api" -import {globalErrorHandler} from "./errorHandler" +import {fetchVariantParametersFromOpenAPI} from "@/services/api" const inputParamsToParameters = (additionalInputs: InputParameter[]): Parameter[] => { return additionalInputs.map((value) => ({ @@ -49,7 +48,7 @@ export const getAllVariantParameters = async (appId: string, variant: Variant) = let parameters: Parameter[] = [] let inputs: Parameter[] = [] try { - const {initOptParams, inputParams, isChatVariant} = await getVariantParametersFromOpenAPI( + const {initOptParams, inputParams, isChatVariant} = await fetchVariantParametersFromOpenAPI( appId, variant.variantId, variant.baseId, diff --git a/agenta-web/src/lib/hooks/useVariant.ts b/agenta-web/src/lib/hooks/useVariant.ts index b05e4f164f..82522d4841 100644 --- a/agenta-web/src/lib/hooks/useVariant.ts +++ b/agenta-web/src/lib/hooks/useVariant.ts @@ -1,8 +1,8 @@ import {useState, useEffect} from "react" -import {saveNewVariant, updateVariantParams} from "@/lib/services/api" import {Variant, Parameter} from "@/lib/Types" import {getAllVariantParameters, updateInputParams} from "@/lib/helpers/variantHelper" import {PERMISSION_ERR_MSG} from "../helpers/axiosConfig" +import {createNewVariant, updateVariantParams} from "@/services/playground/api" /** * Hook for using the variant. @@ -74,7 +74,7 @@ export function useVariant(appId: string, variant: Variant) { try { if (persist) { if (!updateVariant) { - await saveNewVariant( + await createNewVariant( variant.baseId, variant.variantName, variant.configName, @@ -185,7 +185,7 @@ export function useVariants(appId: string, variants: Variant[]) { try { if (persist) { if (!updateVariant) { - await saveNewVariant( + await createNewVariant( variant.baseId, variant.variantName, variant.configName, diff --git a/agenta-web/src/lib/services/api.ts b/agenta-web/src/lib/services/api.ts deleted file mode 100644 index 9bdcc66189..0000000000 --- a/agenta-web/src/lib/services/api.ts +++ /dev/null @@ -1,706 +0,0 @@ -import useSWR from "swr" -import axios from "@/lib//helpers/axiosConfig" -import { - detectChatVariantFromOpenAISchema, - openAISchemaToParameters, -} from "@/lib/helpers/openapi_parser" -import { - Variant, - Parameter, - EvaluationResponseType, - Evaluation, - AppTemplate, - GenericObject, - Environment, - CreateCustomEvaluation, - ExecuteCustomEvalCode, - AICritiqueCreate, - ChatMessage, - KeyValuePair, -} from "@/lib/Types" -import { - fromEvaluationResponseToEvaluation, - fromEvaluationScenarioResponseToEvaluationScenario, -} from "../transformers" -import {EvaluationFlow, EvaluationType} from "../enums" -import {LlmProvider} from "../helpers/llmProviders" -import {getAgentaApiUrl, removeKeys, shortPoll} from "../helpers/utils" -import {dynamicContext} from "../helpers/dynamic" -/** - * Raw interface for the parameters parsed from the openapi.json - */ - -export const axiosFetcher = (url: string) => axios.get(url).then((res) => res.data) - -export async function fetchVariants( - appId: string, - ignoreAxiosError: boolean = false, -): Promise { - const response = await axios.get(`${getAgentaApiUrl()}/api/apps/${appId}/variants/`, { - _ignoreError: ignoreAxiosError, - } as any) - - if (response.data && Array.isArray(response.data) && response.data.length > 0) { - return response.data.map((variant: Record) => { - let v: Variant = { - variantName: variant.variant_name, - templateVariantName: variant.previous_variant_name, - persistent: true, - parameters: variant.parameters, - previousVariantName: variant.previous_variant_name || null, - variantId: variant.variant_id, - baseId: variant.base_id, - baseName: variant.base_name, - configName: variant.config_name, - } - return v - }) - } - - return [] -} - -export const fetchVariantLogs = async (variantId: string, ignoreAxiosError: boolean = false) => { - const response = await axios.get(`${getAgentaApiUrl()}/api/variants/${variantId}/logs`, { - _ignoreError: ignoreAxiosError, - } as any) - return response.data -} - -export function restartAppVariantContainer(variantId: string) { - return axios.post(`${getAgentaApiUrl()}/api/containers/restart_container/`, { - variant_id: variantId, - }) -} - -/** - * Calls the variant endpoint with the input parameters and the optional parameters and returns the response. - * @param inputParametersDict A dictionary of the input parameters to be passed to the variant endpoint - * @param inputParamDefinition A list of the parameters that are defined in the openapi.json (these are only part of the input params, the rest is defined by the user in the optparms) - * @param optionalParameters The optional parameters (prompt, models, AND DICTINPUTS WHICH ARE TO BE USED TO ADD INPUTS ) - * @param appId - The ID of the app. - * @param baseId - The base ID. - * @param chatMessages - An optional array of chat messages. - * @returns A Promise that resolves with the response data from the POST request. - */ -export async function callVariant( - inputParametersDict: KeyValuePair, - inputParamDefinition: Parameter[], - optionalParameters: Parameter[], - appId: string, - baseId: string, - chatMessages?: ChatMessage[], - signal?: AbortSignal, - ignoreAxiosError?: boolean, -) { - const isChatVariant = Array.isArray(chatMessages) && chatMessages.length > 0 - // Separate input parameters into two dictionaries based on the 'input' property - const mainInputParams: Record = {} // Parameters with input = true - const secondaryInputParams: Record = {} // Parameters with input = false - - for (let key of Object.keys(inputParametersDict)) { - const paramDefinition = inputParamDefinition.find((param) => param.name === key) - - // If parameter definition is found and its 'input' property is false, - // then it goes to 'secondaryInputParams', otherwise to 'mainInputParams' - if (paramDefinition && !paramDefinition.input) { - secondaryInputParams[key] = inputParametersDict[key] - } else { - mainInputParams[key] = inputParametersDict[key] - } - } - optionalParameters = optionalParameters || [] - - const optParams = optionalParameters - .filter((param) => param.type !== "object") // remove dicts from optional parameters - .reduce((acc: any, param) => { - acc[param.name] = param.default - return acc - }, {}) - const requestBody = { - ...mainInputParams, - ...optParams, - ["inputs"]: isChatVariant - ? chatMessages.filter((item) => item.content).map((item) => removeKeys(item, ["id"])) - : secondaryInputParams, - } - - const appContainerURI = await getAppContainerURL(appId, undefined, baseId) - - return axios - .post(`${appContainerURI}/generate`, requestBody, { - signal, - _ignoreError: ignoreAxiosError, - } as any) - .then((res) => { - return res.data - }) -} - -/** - * Parses the openapi.json from a variant and returns the parameters as an array of objects. - * @param app - * @param variantName - * @returns - */ -export const getVariantParametersFromOpenAPI = async ( - appId: string, - variantId?: string, - baseId?: string, - ignoreAxiosError: boolean = false, -) => { - const appContainerURI = await getAppContainerURL(appId, variantId, baseId) - const url = `${appContainerURI}/openapi.json` - const response = await axios.get(url, {_ignoreError: ignoreAxiosError} as any) - const isChatVariant = detectChatVariantFromOpenAISchema(response.data) - let APIParams = openAISchemaToParameters(response.data) - // we create a new param for DictInput that will contain the name of the inputs - APIParams = APIParams.map((param) => { - if (param.type === "object") { - // if param.default is defined - if (param?.default) { - param.default = param.default.map((item: string) => { - return {name: item} - }) - } else { - param.default = [] - } - } - return param - }) - if (isChatVariant) APIParams = APIParams.filter((param) => param.name !== "inputs") - const initOptParams = APIParams.filter((param) => !param.input) // contains the default values too! - const inputParams = APIParams.filter((param) => param.input) // don't have input values - return { - initOptParams, - inputParams, - isChatVariant, - } -} - -/** - * Retries the container url for an app - * @param {string} appId - The id of the app - * @param {string} variantId - The id of the variant - * @returns {Promise} - Returns the URL path or an empty string - * @throws {Error} - Throws an error if the request fails - */ -export const getAppContainerURL = async ( - appId: string, - variantId?: string, - baseId?: string, -): Promise => { - try { - // Null-check for the environment variable - if (!getAgentaApiUrl()) { - throw new Error("Environment variable NEXT_PUBLIC_AGENTA_API_URL is not set.") - } - - // Retrieve container URL from backend - const url = `${getAgentaApiUrl()}/api/containers/container_url/` - const response = await axios.get(url, {params: {variant_id: variantId, base_id: baseId}}) - if (response.status === 200 && response.data && response.data.uri) { - return response.data.uri - } else { - return "" - } - } catch (error) { - // Forward the error so it can be handled by the calling function - throw error - } -} - -/** - * Saves a new variant to the database based on previous - */ -export async function saveNewVariant( - baseId: string, - newVariantName: string, - newConfigName: string, - parameters: Parameter[], -) { - await axios.post(`${getAgentaApiUrl()}/api/variants/from-base/`, { - base_id: baseId, - new_variant_name: newVariantName, - new_config_name: newConfigName, - parameters: parameters.reduce((acc, param) => { - return {...acc, [param.name]: param.default} - }, {}), - }) -} - -export async function updateVariantParams(variantId: string, parameters: Parameter[]) { - await axios.put(`${getAgentaApiUrl()}/api/variants/${variantId}/parameters/`, { - parameters: parameters.reduce((acc, param) => { - return {...acc, [param.name]: param.default} - }, {}), - }) -} - -export async function removeApp(appId: string) { - await axios.delete(`${getAgentaApiUrl()}/api/apps/${appId}/`, { - data: {app_id: appId}, - }) -} - -export async function removeVariant(variantId: string) { - await axios.delete(`${getAgentaApiUrl()}/api/variants/${variantId}/`) -} - -/** - * Loads the list of testsets - * @returns - */ -export const useLoadTestsetsList = (appId: string) => { - const {data, error, mutate, isLoading} = useSWR( - `${getAgentaApiUrl()}/api/testsets/?app_id=${appId}`, - axiosFetcher, - {revalidateOnFocus: false, shouldRetryOnError: false}, - ) - - return { - testsets: data || [], - isTestsetsLoading: isLoading, - isTestsetsLoadingError: error, - mutate, - } -} - -export const fetchTestsets = async (appId: string) => { - const response = await axios.get(`${getAgentaApiUrl()}/api/testsets/?app_id=${appId}`) - return response.data -} - -export async function createNewTestset(appId: string, testsetName: string, testsetData: any) { - const response = await axios.post(`${getAgentaApiUrl()}/api/testsets/${appId}/`, { - name: testsetName, - csvdata: testsetData, - }) - return response -} - -export async function updateTestset(testsetId: String, testsetName: string, testsetData: any) { - const response = await axios.put(`${getAgentaApiUrl()}/api/testsets/${testsetId}/`, { - name: testsetName, - csvdata: testsetData, - }) - return response -} - -export const loadTestset = async (testsetId: string | null) => { - if (!testsetId) { - return { - id: undefined, - name: "No Test Set Associated", - created_at: "", - updated_at: "", - csvdata: [], - } - } - const response = await axios.get(`${getAgentaApiUrl()}/api/testsets/${testsetId}/`) - return response.data -} - -export const deleteTestsets = async (ids: string[]) => { - const response = await axios({ - method: "delete", - url: `${getAgentaApiUrl()}/api/testsets/`, - data: {testset_ids: ids}, - }) - return response.data -} - -export const loadEvaluations = async (appId: string, ignoreAxiosError: boolean = false) => { - const response = await axios.get( - `${getAgentaApiUrl()}/api/human-evaluations/?app_id=${appId}`, - { - _ignoreError: ignoreAxiosError, - } as any, - ) - return response.data -} - -export const loadEvaluation = async (evaluationId: string) => { - return await axios - .get(`${getAgentaApiUrl()}/api/human-evaluations/${evaluationId}/`) - .then((responseData) => { - return fromEvaluationResponseToEvaluation(responseData.data) - }) -} - -export const deleteEvaluations = async (ids: string[]) => { - const response = await axios({ - method: "delete", - url: `${getAgentaApiUrl()}/api/human-evaluations/`, - data: {evaluations_ids: ids}, - }) - return response.data -} - -export const loadEvaluationsScenarios = async ( - evaluationTableId: string, - evaluation: Evaluation, -) => { - return await axios - .get( - `${getAgentaApiUrl()}/api/human-evaluations/${evaluationTableId}/evaluation_scenarios/`, - ) - .then((responseData) => { - const evaluationsRows = responseData.data.map((item: any) => { - return fromEvaluationScenarioResponseToEvaluationScenario(item, evaluation) - }) - - return evaluationsRows - }) -} - -export const createNewEvaluation = async ( - { - variant_ids, - appId, - evaluationType, - evaluationTypeSettings, - inputs, - llmAppPromptTemplate, - selectedCustomEvaluationID, - testsetId, - }: { - variant_ids: string[] - appId: string - evaluationType: string - evaluationTypeSettings: Partial - inputs: string[] - llmAppPromptTemplate?: string - selectedCustomEvaluationID?: string - testsetId: string - }, - ignoreAxiosError: boolean = false, -) => { - const data = { - variant_ids, - app_id: appId, - inputs: inputs, - evaluation_type: evaluationType, - evaluation_type_settings: { - ...evaluationTypeSettings, - custom_code_evaluation_id: selectedCustomEvaluationID, - llm_app_prompt_template: llmAppPromptTemplate, - }, - testset_id: testsetId, - status: EvaluationFlow.EVALUATION_INITIALIZED, - } - - const response = await axios.post(`${getAgentaApiUrl()}/api/human-evaluations/`, data, { - _ignoreError: ignoreAxiosError, - } as any) - return response.data.id -} - -export const updateEvaluation = async (evaluationId: string, data: GenericObject) => { - const response = await axios.put( - `${getAgentaApiUrl()}/api/human-evaluations/${evaluationId}/`, - data, - ) - return response.data -} - -export const updateEvaluationScenario = async ( - evaluationTableId: string, - evaluationScenarioId: string, - data: GenericObject, - evaluationType: EvaluationType, -) => { - const response = await axios.put( - `${getAgentaApiUrl()}/api/human-evaluations/${evaluationTableId}/evaluation_scenario/${evaluationScenarioId}/${evaluationType}/`, - data, - ) - return response.data -} - -export const postEvaluationScenario = async (evaluationTableId: string, data: GenericObject) => { - const response = await axios.post( - `${getAgentaApiUrl()}/api/human-evaluations/${evaluationTableId}/evaluation_scenario/`, - data, - ) - return response.data -} - -export const evaluateAICritiqueForEvalScenario = async ( - data: AICritiqueCreate, - ignoreAxiosError: boolean = false, -) => { - const response = await axios.post( - `${getAgentaApiUrl()}/api/human-evaluations/evaluation_scenario/ai_critique/`, - data, - {_ignoreError: ignoreAxiosError} as any, - ) - return response -} - -export const fetchEvaluationResults = async ( - evaluationId: string, - ignoreAxiosError: boolean = false, -) => { - const response = await axios.get( - `${getAgentaApiUrl()}/api/human-evaluations/${evaluationId}/results/`, - { - _ignoreError: ignoreAxiosError, - } as any, - ) - return response.data -} - -export const fetchEvaluationScenarioResults = async (evaluation_scenario_id: string) => { - const response = await axios.get( - `${getAgentaApiUrl()}/api/human-evaluations/evaluation_scenario/${evaluation_scenario_id}/score/`, - ) - return response -} - -export const saveCustomCodeEvaluation = async ( - payload: CreateCustomEvaluation, - ignoreAxiosError: boolean = false, -) => { - const response = await axios.post( - `${getAgentaApiUrl()}/api/human-evaluations/custom_evaluation/`, - payload, - {_ignoreError: ignoreAxiosError} as any, - ) - return response -} - -export const editCustomEvaluationDetail = async ( - id: string, - payload: CreateCustomEvaluation, - ignoreAxiosError: boolean = false, -) => { - const response = await axios.put( - `${getAgentaApiUrl()}/api/human-evaluations/custom_evaluation/${id}`, - payload, - {_ignoreError: ignoreAxiosError} as any, - ) - return response -} - -export const fetchCustomEvaluations = async (app_id: string, ignoreAxiosError: boolean = false) => { - const response = await axios.get( - `${getAgentaApiUrl()}/api/human-evaluations/custom_evaluation/list/${app_id}/`, - {_ignoreError: ignoreAxiosError} as any, - ) - return response -} - -export const fetchCustomEvaluationDetail = async ( - id: string, - ignoreAxiosError: boolean = false, -) => { - const response = await axios.get( - `${getAgentaApiUrl()}/api/human-evaluations/custom_evaluation/${id}/`, - {_ignoreError: ignoreAxiosError} as any, - ) - return response.data -} - -export const fetchCustomEvaluationNames = async ( - app_id: string, - ignoreAxiosError: boolean = false, -) => { - const response = await axios.get( - `${getAgentaApiUrl()}/api/human-evaluations/custom_evaluation/${app_id}/names/`, - {_ignoreError: ignoreAxiosError} as any, - ) - return response -} - -export const executeCustomEvaluationCode = async ( - payload: ExecuteCustomEvalCode, - ignoreAxiosError: boolean = false, -) => { - const response = await axios.post( - `${getAgentaApiUrl()}/api/human-evaluations/custom_evaluation/execute/${ - payload.evaluation_id - }/`, - payload, - {_ignoreError: ignoreAxiosError} as any, - ) - return response -} - -export const updateEvaluationScenarioScore = async ( - evaluation_scenario_id: string, - score: number, - ignoreAxiosError: boolean = false, -) => { - const response = await axios.put( - `${getAgentaApiUrl()}/api/human-evaluations/evaluation_scenario/${evaluation_scenario_id}/score/`, - {score}, - {_ignoreError: ignoreAxiosError} as any, - ) - return response -} - -export const getProfile = async (ignoreAxiosError: boolean = false) => { - return axios.get(`${getAgentaApiUrl()}/api/profile/`, { - _ignoreError: ignoreAxiosError, - } as any) -} - -export const getTemplates = async () => { - const response = await axios.get(`${getAgentaApiUrl()}/api/containers/templates/`) - return response.data -} - -export const createAppFromTemplate = async ( - templateObj: AppTemplate, - ignoreAxiosError: boolean = false, -) => { - const response = await axios.post( - `${getAgentaApiUrl()}/api/apps/app_and_variant_from_template/`, - templateObj, - {_ignoreError: ignoreAxiosError} as any, - ) - return response -} - -export const fetchData = async (url: string): Promise => { - const response = await fetch(url) - return response.json() -} - -export const waitForAppToStart = async ({ - appId, - variant, - timeout = 20000, - interval = 2000, -}: { - appId: string - variant?: Variant - timeout?: number - interval?: number -}) => { - const _variant = variant || (await fetchVariants(appId, true))[0] - if (_variant) { - const {stopper, promise} = shortPoll( - () => - getVariantParametersFromOpenAPI( - appId, - _variant.variantId, - _variant.baseId, - true, - ).then(() => stopper()), - {delayMs: interval, timeoutMs: timeout}, - ) - await promise - } -} - -export const createAndStartTemplate = async ({ - appName, - providerKey, - templateId, - timeout, - onStatusChange, -}: { - appName: string - providerKey: Array - templateId: string - timeout?: number - onStatusChange?: ( - status: "creating_app" | "starting_app" | "success" | "bad_request" | "timeout" | "error", - details?: any, - appId?: string, - ) => void -}) => { - const apiKeys = providerKey.reduce( - (acc, {key, name}) => { - if (key) acc[name] = key - return acc - }, - {} as Record, - ) - - try { - const {getOrgValues} = await dynamicContext("org.context", { - getOrgValues: () => ({ - selectedOrg: {id: undefined, default_workspace: {id: undefined}}, - }), - }) - const {selectedOrg} = getOrgValues() - onStatusChange?.("creating_app") - let app - try { - app = await createAppFromTemplate( - { - app_name: appName, - template_id: templateId, - organization_id: selectedOrg.id, - workspace_id: selectedOrg.default_workspace.id, - env_vars: apiKeys, - }, - true, - ) - } catch (error: any) { - if (error?.response?.status === 400) { - onStatusChange?.("bad_request", error) - return - } - throw error - } - - onStatusChange?.("starting_app", "", app?.data?.app_id) - try { - await waitForAppToStart({appId: app?.data?.app_id, timeout}) - } catch (error: any) { - if (error.message === "timeout") { - onStatusChange?.("timeout", "", app?.data?.app_id) - return - } - throw error - } - - onStatusChange?.("success", "", app?.data?.app_id) - } catch (error) { - onStatusChange?.("error", error) - } -} - -export const fetchEnvironments = async (appId: string): Promise => { - const response = await fetch(`${getAgentaApiUrl()}/api/apps/${appId}/environments/`) - - if (response.status !== 200) { - throw new Error("Failed to fetch environments") - } - - const data: Environment[] = await response.json() - return data -} - -export const publishVariant = async (variantId: string, environmentName: string) => { - await axios.post(`${getAgentaApiUrl()}/api/environments/deploy/`, { - environment_name: environmentName, - variant_id: variantId, - }) -} - -export const promptVersioning = async (variantId: string, ignoreAxiosError: boolean = false) => { - const {data} = await axios.get(`${getAgentaApiUrl()}/api/variants/${variantId}/revisions/`, { - _ignoreError: ignoreAxiosError, - } as any) - - return data -} - -export const promptRevision = async ( - variantId: string, - revisionNumber: number, - ignoreAxiosError: boolean = false, -) => { - const {data} = await axios.get( - `${getAgentaApiUrl()}/api/variants/${variantId}/revisions/${revisionNumber}/`, - { - _ignoreError: ignoreAxiosError, - } as any, - ) - - return data -} diff --git a/agenta-web/src/lib/services/evaluations.ts b/agenta-web/src/lib/services/evaluations.ts deleted file mode 100644 index d7b12df2f7..0000000000 --- a/agenta-web/src/lib/services/evaluations.ts +++ /dev/null @@ -1,37 +0,0 @@ -import {GenericObject, KeyValuePair} from "../Types" - -export const evaluateWithExactMatch = (string1: string, string2: string): Boolean => { - return string1 === string2 -} - -export const evaluateWithSimilarityMatch = (string1: string, string2: string): number => { - let set1 = new Set(string1.split(" ")) - let set2 = new Set(string2.split(" ")) - let intersect = new Set(Array.from(set1).filter((x) => set2.has(x))) - let union = new Set(Array.from(set1).concat(Array.from(set2))) - - const similarity = intersect.size / union.size - return similarity -} - -export const evaluateWithRegex = (testString: string, regex: string, shouldMatch: boolean) => { - //case insensitive regex - const re = new RegExp(regex, "i") - const result = re.test(testString) - return result === shouldMatch -} - -export const evaluateWithWebhook = async (webhookUrl: string, body: GenericObject) => { - return fetch(webhookUrl, { - method: "POST", - body: JSON.stringify(body), - headers: {"Content-Type": "application/json", Accept: "application/json"}, - }) - .then((res) => res.json()) - .then((data) => { - if (isNaN(data.score)) throw new Error("Webhook did not return a score") - if (data.score < 0 || data.score > 1) - throw new Error("Webhook returned an invalid score. Score must be between 0 and 1") - return data.score - }) -} diff --git a/agenta-web/src/pages/apps/[app_id]/annotations/human_a_b_testing/[evaluation_id]/index.tsx b/agenta-web/src/pages/apps/[app_id]/annotations/human_a_b_testing/[evaluation_id]/index.tsx index 5232dc4675..33f8a33a68 100644 --- a/agenta-web/src/pages/apps/[app_id]/annotations/human_a_b_testing/[evaluation_id]/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/annotations/human_a_b_testing/[evaluation_id]/index.tsx @@ -1,9 +1,13 @@ import ABTestingEvaluationTable from "@/components/EvaluationTable/ABTestingEvaluationTable" import type {Evaluation} from "@/lib/Types" -import {loadEvaluation, loadEvaluationsScenarios, loadTestset} from "@/lib/services/api" +import { + fetchLoadEvaluation, + fetchAllLoadEvaluationsScenarios, +} from "@/services/human-evaluations/api" +import {fetchTestset} from "@/services/testsets/api" import {useRouter} from "next/router" import {useEffect, useState} from "react" -import {fetchVariants} from "@/lib/services/api" +import {fetchVariants} from "@/services/api" import {useAtom} from "jotai" import {evaluationAtom, evaluationScenariosAtom} from "@/lib/atoms/evaluation" import {getTestsetChatColumn} from "@/lib/helpers/testset" @@ -26,7 +30,7 @@ export default function Evaluation() { const init = async () => { setIsLoading(true) try { - const data = await loadEvaluationsScenarios(evaluationTableId, evaluation) + const data = await fetchAllLoadEvaluationsScenarios(evaluationTableId, evaluation) setEvaluationScenarios(data) } finally { setTimeout(() => setIsLoading(false), 1000) @@ -40,9 +44,9 @@ export default function Evaluation() { return } const init = async () => { - const evaluation: Evaluation = await loadEvaluation(evaluationTableId) + const evaluation: Evaluation = await fetchLoadEvaluation(evaluationTableId) const backendVariants = await fetchVariants(appId) - const testset = await loadTestset(evaluation.testset._id) + const testset = await fetchTestset(evaluation.testset._id) // Create a map for faster access to first array elements let backendVariantsMap = new Map() backendVariants.forEach((obj) => backendVariantsMap.set(obj.variantId, obj)) diff --git a/agenta-web/src/pages/apps/[app_id]/annotations/single_model_test/[evaluation_id]/index.tsx b/agenta-web/src/pages/apps/[app_id]/annotations/single_model_test/[evaluation_id]/index.tsx index 3f609d6839..c367c77136 100644 --- a/agenta-web/src/pages/apps/[app_id]/annotations/single_model_test/[evaluation_id]/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/annotations/single_model_test/[evaluation_id]/index.tsx @@ -1,8 +1,12 @@ -import type {Evaluation, EvaluationScenario, GenericObject} from "@/lib/Types" -import {loadEvaluation, loadEvaluationsScenarios, loadTestset} from "@/lib/services/api" +import {Evaluation, EvaluationScenario, GenericObject} from "@/lib/Types" +import { + fetchLoadEvaluation, + fetchAllLoadEvaluationsScenarios, +} from "@/services/human-evaluations/api" +import {fetchTestset} from "@/services/testsets/api" import {useRouter} from "next/router" import {useEffect, useState} from "react" -import {fetchVariants} from "@/lib/services/api" +import {fetchVariants} from "@/services/api" import {getTestsetChatColumn} from "@/lib/helpers/testset" import SingleModelEvaluationTable from "@/components/EvaluationTable/SingleModelEvaluationTable" @@ -23,7 +27,7 @@ export default function Evaluation() { const init = async () => { setIsLoading(true) try { - const data = await loadEvaluationsScenarios(evaluationTableId, evaluation) + const data = await fetchAllLoadEvaluationsScenarios(evaluationTableId, evaluation) setEvaluationScenarios( data.map((item: GenericObject) => { const numericScore = parseInt(item.score) @@ -42,9 +46,9 @@ export default function Evaluation() { return } const init = async () => { - const evaluation: Evaluation = await loadEvaluation(evaluationTableId) + const evaluation: Evaluation = await fetchLoadEvaluation(evaluationTableId) const backendVariants = await fetchVariants(appId) - const testset = await loadTestset(evaluation.testset._id) + const testset = await fetchTestset(evaluation.testset._id) // Create a map for faster access to first array elements let backendVariantsMap = new Map() backendVariants.forEach((obj) => backendVariantsMap.set(obj.variantId, obj)) diff --git a/agenta-web/src/pages/apps/[app_id]/endpoints/index.tsx b/agenta-web/src/pages/apps/[app_id]/endpoints/index.tsx index 6c1b2aba85..4dc3815eaf 100644 --- a/agenta-web/src/pages/apps/[app_id]/endpoints/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/endpoints/index.tsx @@ -10,7 +10,8 @@ import {Environment, GenericObject, JSSTheme, Parameter, Variant} from "@/lib/Ty import {isDemo} from "@/lib/helpers/utils" import {dynamicComponent} from "@/lib/helpers/dynamic" import {useVariant} from "@/lib/hooks/useVariant" -import {fetchEnvironments, fetchVariants, getAppContainerURL} from "@/lib/services/api" +import {fetchVariants, fetchAppContainerURL} from "@/services/api" +import {fetchEnvironments} from "@/services/deployment/api" import {ApiOutlined, AppstoreOutlined, HistoryOutlined} from "@ant-design/icons" import {Alert, Collapse, CollapseProps, Empty, Radio, Tabs, Tooltip, Typography} from "antd" import {useRouter} from "next/router" @@ -50,7 +51,7 @@ export default function VariantEndpoint() { const [uri, setURI] = useState(null) const loadURL = async (environment: Environment) => { if (environment.deployed_app_variant_id) { - const url = await getAppContainerURL(appId, environment.deployed_app_variant_id) + const url = await fetchAppContainerURL(appId, environment.deployed_app_variant_id) setURI(`${url}/generate_deployed`) } } diff --git a/agenta-web/src/pages/apps/[app_id]/evaluations/new-evaluator.tsx b/agenta-web/src/pages/apps/[app_id]/evaluations/new-evaluator.tsx index c951cd492a..5637267752 100644 --- a/agenta-web/src/pages/apps/[app_id]/evaluations/new-evaluator.tsx +++ b/agenta-web/src/pages/apps/[app_id]/evaluations/new-evaluator.tsx @@ -1,7 +1,7 @@ import Evaluators from "@/components/pages/evaluations/evaluators/Evaluators" import {useAppId} from "@/hooks/useAppId" import {evaluatorConfigsAtom, evaluatorsAtom} from "@/lib/atoms/evaluation" -import {fetchAllEvaluatorConfigs, fetchAllEvaluators} from "@/services/evaluations" +import {fetchAllEvaluatorConfigs, fetchAllEvaluators} from "@/services/evaluations/api" import {useAtom} from "jotai" import React, {useEffect} from "react" diff --git a/agenta-web/src/pages/apps/[app_id]/evaluations/results.tsx b/agenta-web/src/pages/apps/[app_id]/evaluations/results.tsx index 3ff2ab20df..ae10ff2b50 100644 --- a/agenta-web/src/pages/apps/[app_id]/evaluations/results.tsx +++ b/agenta-web/src/pages/apps/[app_id]/evaluations/results.tsx @@ -1,7 +1,7 @@ import EvaluationResults from "@/components/pages/evaluations/evaluationResults/EvaluationResults" import {useAppId} from "@/hooks/useAppId" import {evaluatorConfigsAtom, evaluatorsAtom} from "@/lib/atoms/evaluation" -import {fetchAllEvaluatorConfigs, fetchAllEvaluators} from "@/services/evaluations" +import {fetchAllEvaluatorConfigs, fetchAllEvaluators} from "@/services/evaluations/api" import {useAtom} from "jotai" import React, {useEffect} from "react" diff --git a/agenta-web/src/pages/apps/[app_id]/testsets/index.tsx b/agenta-web/src/pages/apps/[app_id]/testsets/index.tsx index 3faf4680d6..71b4adcc36 100644 --- a/agenta-web/src/pages/apps/[app_id]/testsets/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/testsets/index.tsx @@ -5,7 +5,7 @@ import {ColumnsType} from "antd/es/table" import {useState} from "react" import {formatDate} from "@/lib/helpers/dateTimeHelper" import {DeleteOutlined} from "@ant-design/icons" -import {deleteTestsets, useLoadTestsetsList} from "@/lib/services/api" +import {deleteTestsets, useLoadTestsetsList} from "@/services/testsets/api" import {createUseStyles} from "react-jss" import {testset} from "@/lib/Types" import {isDemo} from "@/lib/helpers/utils" diff --git a/agenta-web/src/services/api.ts b/agenta-web/src/services/api.ts new file mode 100644 index 0000000000..34196f2a06 --- /dev/null +++ b/agenta-web/src/services/api.ts @@ -0,0 +1,223 @@ +import axios from "@/lib//helpers/axiosConfig" +import { + detectChatVariantFromOpenAISchema, + openAISchemaToParameters, +} from "@/lib/helpers/openapi_parser" +import {getAgentaApiUrl, removeKeys, shortPoll} from "@/lib/helpers/utils" +import {Variant, Parameter, ChatMessage, KeyValuePair} from "@/lib/Types" + +//Prefix convention: +// - fetch: GET single entity from server +// - fetchAll: GET all entities from server +// - create: POST data to server +// - update: PUT data to server +// - delete: DELETE data from server + +/** + * Raw interface for the parameters parsed from the openapi.json + */ + +export const axiosFetcher = (url: string) => axios.get(url).then((res) => res.data) + +export async function fetchVariants( + appId: string, + ignoreAxiosError: boolean = false, +): Promise { + const response = await axios.get(`${getAgentaApiUrl()}/api/apps/${appId}/variants/`, { + _ignoreError: ignoreAxiosError, + } as any) + + if (response.data && Array.isArray(response.data) && response.data.length > 0) { + return response.data.map((variant: Record) => { + let v: Variant = { + variantName: variant.variant_name, + templateVariantName: variant.previous_variant_name, + persistent: true, + parameters: variant.parameters, + previousVariantName: variant.previous_variant_name || null, + variantId: variant.variant_id, + baseId: variant.base_id, + baseName: variant.base_name, + configName: variant.config_name, + } + return v + }) + } + + return [] +} + +/** + * Calls the variant endpoint with the input parameters and the optional parameters and returns the response. + * @param inputParametersDict A dictionary of the input parameters to be passed to the variant endpoint + * @param inputParamDefinition A list of the parameters that are defined in the openapi.json (these are only part of the input params, the rest is defined by the user in the optparms) + * @param optionalParameters The optional parameters (prompt, models, AND DICTINPUTS WHICH ARE TO BE USED TO ADD INPUTS ) + * @param appId - The ID of the app. + * @param baseId - The base ID. + * @param chatMessages - An optional array of chat messages. + * @returns A Promise that resolves with the response data from the POST request. + */ +export async function callVariant( + inputParametersDict: KeyValuePair, + inputParamDefinition: Parameter[], + optionalParameters: Parameter[], + appId: string, + baseId: string, + chatMessages?: ChatMessage[], + signal?: AbortSignal, + ignoreAxiosError?: boolean, +) { + const isChatVariant = Array.isArray(chatMessages) && chatMessages.length > 0 + // Separate input parameters into two dictionaries based on the 'input' property + const mainInputParams: Record = {} // Parameters with input = true + const secondaryInputParams: Record = {} // Parameters with input = false + + for (let key of Object.keys(inputParametersDict)) { + const paramDefinition = inputParamDefinition.find((param) => param.name === key) + + // If parameter definition is found and its 'input' property is false, + // then it goes to 'secondaryInputParams', otherwise to 'mainInputParams' + if (paramDefinition && !paramDefinition.input) { + secondaryInputParams[key] = inputParametersDict[key] + } else { + mainInputParams[key] = inputParametersDict[key] + } + } + optionalParameters = optionalParameters || [] + + const optParams = optionalParameters + .filter((param) => param.type !== "object") // remove dicts from optional parameters + .reduce((acc: any, param) => { + acc[param.name] = param.default + return acc + }, {}) + const requestBody = { + ...mainInputParams, + ...optParams, + ["inputs"]: isChatVariant + ? chatMessages.filter((item) => item.content).map((item) => removeKeys(item, ["id"])) + : secondaryInputParams, + } + + const appContainerURI = await fetchAppContainerURL(appId, undefined, baseId) + + return axios + .post(`${appContainerURI}/generate`, requestBody, { + signal, + _ignoreError: ignoreAxiosError, + } as any) + .then((res) => { + return res.data + }) +} + +/** + * Parses the openapi.json from a variant and returns the parameters as an array of objects. + * @param app + * @param variantName + * @returns + */ +export const fetchVariantParametersFromOpenAPI = async ( + appId: string, + variantId?: string, + baseId?: string, + ignoreAxiosError: boolean = false, +) => { + const appContainerURI = await fetchAppContainerURL(appId, variantId, baseId) + const url = `${appContainerURI}/openapi.json` + const response = await axios.get(url, {_ignoreError: ignoreAxiosError} as any) + const isChatVariant = detectChatVariantFromOpenAISchema(response.data) + let APIParams = openAISchemaToParameters(response.data) + // we create a new param for DictInput that will contain the name of the inputs + APIParams = APIParams.map((param) => { + if (param.type === "object") { + // if param.default is defined + if (param?.default) { + param.default = param.default.map((item: string) => { + return {name: item} + }) + } else { + param.default = [] + } + } + return param + }) + if (isChatVariant) APIParams = APIParams.filter((param) => param.name !== "inputs") + const initOptParams = APIParams.filter((param) => !param.input) // contains the default values too! + const inputParams = APIParams.filter((param) => param.input) // don't have input values + return { + initOptParams, + inputParams, + isChatVariant, + } +} + +/** + * Retries the container url for an app + * @param {string} appId - The id of the app + * @param {string} variantId - The id of the variant + * @returns {Promise} - Returns the URL path or an empty string + * @throws {Error} - Throws an error if the request fails + */ +export const fetchAppContainerURL = async ( + appId: string, + variantId?: string, + baseId?: string, +): Promise => { + try { + // Null-check for the environment variable + if (!getAgentaApiUrl()) { + throw new Error("Environment variable NEXT_PUBLIC_AGENTA_API_URL is not set.") + } + + // Retrieve container URL from backend + const url = `${getAgentaApiUrl()}/api/containers/container_url/` + const response = await axios.get(url, {params: {variant_id: variantId, base_id: baseId}}) + if (response.status === 200 && response.data && response.data.uri) { + return response.data.uri + } else { + return "" + } + } catch (error) { + // Forward the error so it can be handled by the calling function + throw error + } +} + +export const fetchProfile = async (ignoreAxiosError: boolean = false) => { + return axios.get(`${getAgentaApiUrl()}/api/profile/`, { + _ignoreError: ignoreAxiosError, + } as any) +} + +export const fetchData = async (url: string): Promise => { + const response = await fetch(url) + return response.json() +} + +export const waitForAppToStart = async ({ + appId, + variant, + timeout = 20000, + interval = 2000, +}: { + appId: string + variant?: Variant + timeout?: number + interval?: number +}) => { + const _variant = variant || (await fetchVariants(appId, true))[0] + if (_variant) { + const {stopper, promise} = shortPoll( + () => + fetchVariantParametersFromOpenAPI( + appId, + _variant.variantId, + _variant.baseId, + true, + ).then(() => stopper()), + {delayMs: interval, timeoutMs: timeout}, + ) + await promise + } +} diff --git a/agenta-web/src/services/app-selector/api/index.ts b/agenta-web/src/services/app-selector/api/index.ts new file mode 100644 index 0000000000..5bc8d63fc3 --- /dev/null +++ b/agenta-web/src/services/app-selector/api/index.ts @@ -0,0 +1,106 @@ +import {AppTemplate} from "@/lib/Types" +import axios from "@/lib/helpers/axiosConfig" +import {dynamicContext} from "@/lib/helpers/dynamic" +import {LlmProvider} from "@/lib/helpers/llmProviders" +import {getAgentaApiUrl} from "@/lib/helpers/utils" +import {waitForAppToStart} from "@/services/api" + +//Prefix convention: +// - fetch: GET single entity from server +// - fetchAll: GET all entities from server +// - create: POST data to server +// - update: PUT data to server +// - delete: DELETE data from server + +export const fetchAllTemplates = async () => { + const response = await axios.get(`${getAgentaApiUrl()}/api/containers/templates/`) + return response.data +} + +export async function deleteApp(appId: string) { + await axios.delete(`${getAgentaApiUrl()}/api/apps/${appId}/`, { + data: {app_id: appId}, + }) +} + +export const createAppFromTemplate = async ( + templateObj: AppTemplate, + ignoreAxiosError: boolean = false, +) => { + const response = await axios.post( + `${getAgentaApiUrl()}/api/apps/app_and_variant_from_template/`, + templateObj, + {_ignoreError: ignoreAxiosError} as any, + ) + return response +} + +export const createAndStartTemplate = async ({ + appName, + providerKey, + templateId, + timeout, + onStatusChange, +}: { + appName: string + providerKey: Array + templateId: string + timeout?: number + onStatusChange?: ( + status: "creating_app" | "starting_app" | "success" | "bad_request" | "timeout" | "error", + details?: any, + appId?: string, + ) => void +}) => { + const apiKeys = providerKey.reduce( + (acc, {key, name}) => { + if (key) acc[name] = key + return acc + }, + {} as Record, + ) + + try { + const {getOrgValues} = await dynamicContext("org.context", { + getOrgValues: () => ({ + selectedOrg: {id: undefined, default_workspace: {id: undefined}}, + }), + }) + const {selectedOrg} = getOrgValues() + onStatusChange?.("creating_app") + let app + try { + app = await createAppFromTemplate( + { + app_name: appName, + template_id: templateId, + organization_id: selectedOrg.id, + workspace_id: selectedOrg.default_workspace.id, + env_vars: apiKeys, + }, + true, + ) + } catch (error: any) { + if (error?.response?.status === 400) { + onStatusChange?.("bad_request", error) + return + } + throw error + } + + onStatusChange?.("starting_app", "", app?.data?.app_id) + try { + await waitForAppToStart({appId: app?.data?.app_id, timeout}) + } catch (error: any) { + if (error.message === "timeout") { + onStatusChange?.("timeout", "", app?.data?.app_id) + return + } + throw error + } + + onStatusChange?.("success", "", app?.data?.app_id) + } catch (error) { + onStatusChange?.("error", error) + } +} diff --git a/agenta-web/src/services/deployment/api/index.ts b/agenta-web/src/services/deployment/api/index.ts new file mode 100644 index 0000000000..2094ea1db3 --- /dev/null +++ b/agenta-web/src/services/deployment/api/index.ts @@ -0,0 +1,26 @@ +import {Environment} from "@/lib/Types" +import axios from "@/lib/helpers/axiosConfig" +import {getAgentaApiUrl} from "@/lib/helpers/utils" + +//Prefix convention: +// - fetch: GET single entity from server +// - fetchAll: GET all entities from server +// - create: POST data to server +// - update: PUT data to server +// - delete: DELETE data from server + +export const fetchEnvironments = async (appId: string): Promise => { + try { + const response = await axios.get(`${getAgentaApiUrl()}/api/apps/${appId}/environments/`) + return response.data + } catch (error) { + throw new Error("Failed to fetch environments") + } +} + +export const createPublishVariant = async (variantId: string, environmentName: string) => { + await axios.post(`${getAgentaApiUrl()}/api/environments/deploy/`, { + environment_name: environmentName, + variant_id: variantId, + }) +} diff --git a/agenta-web/src/services/evaluations/index.ts b/agenta-web/src/services/evaluations/api/index.ts similarity index 97% rename from agenta-web/src/services/evaluations/index.ts rename to agenta-web/src/services/evaluations/api/index.ts index 625c7d4645..301bf6c994 100644 --- a/agenta-web/src/services/evaluations/index.ts +++ b/agenta-web/src/services/evaluations/api/index.ts @@ -21,9 +21,7 @@ import webhookImg from "@/media/link.png" import aiImg from "@/media/artificial-intelligence.png" import codeImg from "@/media/browser.png" import bracketCurlyImg from "@/media/bracket-curly.png" -import dayjs from "dayjs" -import {loadTestset} from "@/lib/services/api" -import {runningStatuses} from "@/components/pages/evaluations/cellRenderers/cellRenderers" +import {fetchTestset} from "@/services/testsets/api" import {calcEvalDuration} from "@/lib/helpers/evaluate" import _ from "lodash" @@ -213,7 +211,7 @@ export const updateAnnotationScenario = async ( // Comparison export const fetchAllComparisonResults = async (evaluationIds: string[]) => { const scenarioGroups = await Promise.all(evaluationIds.map(fetchAllEvaluationScenarios)) - const testset: TestSet = await loadTestset(scenarioGroups[0][0].evaluation?.testset?.id) + const testset: TestSet = await fetchTestset(scenarioGroups[0][0].evaluation?.testset?.id) const inputsNameSet = new Set() scenarioGroups.forEach((group) => { diff --git a/agenta-web/src/services/human-evaluations/api/index.ts b/agenta-web/src/services/human-evaluations/api/index.ts new file mode 100644 index 0000000000..a5c98a6be1 --- /dev/null +++ b/agenta-web/src/services/human-evaluations/api/index.ts @@ -0,0 +1,251 @@ +import axios from "@/lib//helpers/axiosConfig" +import { + EvaluationResponseType, + Evaluation, + GenericObject, + CreateCustomEvaluation, + ExecuteCustomEvalCode, + AICritiqueCreate, +} from "@/lib/Types" +import { + fromEvaluationResponseToEvaluation, + fromEvaluationScenarioResponseToEvaluationScenario, +} from "@/lib/transformers" +import {EvaluationFlow, EvaluationType} from "@/lib/enums" +import {getAgentaApiUrl} from "@/lib/helpers/utils" + +//Prefix convention: +// - fetch: GET single entity from server +// - fetchAll: GET all entities from server +// - create: POST data to server +// - update: PUT data to server +// - delete: DELETE data from server + +export const fetchAllLoadEvaluations = async (appId: string, ignoreAxiosError: boolean = false) => { + const response = await axios.get( + `${getAgentaApiUrl()}/api/human-evaluations/?app_id=${appId}`, + { + _ignoreError: ignoreAxiosError, + } as any, + ) + return response.data +} + +export const fetchLoadEvaluation = async (evaluationId: string) => { + return await axios + .get(`${getAgentaApiUrl()}/api/human-evaluations/${evaluationId}/`) + .then((responseData) => { + return fromEvaluationResponseToEvaluation(responseData.data) + }) +} + +export const deleteEvaluations = async (ids: string[]) => { + const response = await axios({ + method: "delete", + url: `${getAgentaApiUrl()}/api/human-evaluations/`, + data: {evaluations_ids: ids}, + }) + return response.data +} + +export const fetchAllLoadEvaluationsScenarios = async ( + evaluationTableId: string, + evaluation: Evaluation, +) => { + return await axios + .get( + `${getAgentaApiUrl()}/api/human-evaluations/${evaluationTableId}/evaluation_scenarios/`, + ) + .then((responseData) => { + const evaluationsRows = responseData.data.map((item: any) => { + return fromEvaluationScenarioResponseToEvaluationScenario(item, evaluation) + }) + + return evaluationsRows + }) +} + +export const createNewEvaluation = async ( + { + variant_ids, + appId, + evaluationType, + evaluationTypeSettings, + inputs, + llmAppPromptTemplate, + selectedCustomEvaluationID, + testsetId, + }: { + variant_ids: string[] + appId: string + evaluationType: string + evaluationTypeSettings: Partial + inputs: string[] + llmAppPromptTemplate?: string + selectedCustomEvaluationID?: string + testsetId: string + }, + ignoreAxiosError: boolean = false, +) => { + const data = { + variant_ids, + app_id: appId, + inputs: inputs, + evaluation_type: evaluationType, + evaluation_type_settings: { + ...evaluationTypeSettings, + custom_code_evaluation_id: selectedCustomEvaluationID, + llm_app_prompt_template: llmAppPromptTemplate, + }, + testset_id: testsetId, + status: EvaluationFlow.EVALUATION_INITIALIZED, + } + + const response = await axios.post(`${getAgentaApiUrl()}/api/human-evaluations/`, data, { + _ignoreError: ignoreAxiosError, + } as any) + return response.data.id +} + +export const updateEvaluation = async (evaluationId: string, data: GenericObject) => { + const response = await axios.put( + `${getAgentaApiUrl()}/api/human-evaluations/${evaluationId}/`, + data, + ) + return response.data +} + +export const updateEvaluationScenario = async ( + evaluationTableId: string, + evaluationScenarioId: string, + data: GenericObject, + evaluationType: EvaluationType, +) => { + const response = await axios.put( + `${getAgentaApiUrl()}/api/human-evaluations/${evaluationTableId}/evaluation_scenario/${evaluationScenarioId}/${evaluationType}/`, + data, + ) + return response.data +} + +export const createEvaluationScenario = async (evaluationTableId: string, data: GenericObject) => { + const response = await axios.post( + `${getAgentaApiUrl()}/api/human-evaluations/${evaluationTableId}/evaluation_scenario/`, + data, + ) + return response.data +} + +export const createEvaluateAICritiqueForEvalScenario = async ( + data: AICritiqueCreate, + ignoreAxiosError: boolean = false, +) => { + const response = await axios.post( + `${getAgentaApiUrl()}/api/human-evaluations/evaluation_scenario/ai_critique/`, + data, + {_ignoreError: ignoreAxiosError} as any, + ) + return response +} + +export const fetchEvaluationResults = async ( + evaluationId: string, + ignoreAxiosError: boolean = false, +) => { + const response = await axios.get( + `${getAgentaApiUrl()}/api/human-evaluations/${evaluationId}/results/`, + { + _ignoreError: ignoreAxiosError, + } as any, + ) + return response.data +} + +export const fetchEvaluationScenarioResults = async (evaluation_scenario_id: string) => { + const response = await axios.get( + `${getAgentaApiUrl()}/api/human-evaluations/evaluation_scenario/${evaluation_scenario_id}/score/`, + ) + return response +} + +export const createCustomCodeEvaluation = async ( + payload: CreateCustomEvaluation, + ignoreAxiosError: boolean = false, +) => { + const response = await axios.post( + `${getAgentaApiUrl()}/api/human-evaluations/custom_evaluation/`, + payload, + {_ignoreError: ignoreAxiosError} as any, + ) + return response +} + +export const updateCustomEvaluationDetail = async ( + id: string, + payload: CreateCustomEvaluation, + ignoreAxiosError: boolean = false, +) => { + const response = await axios.put( + `${getAgentaApiUrl()}/api/human-evaluations/custom_evaluation/${id}`, + payload, + {_ignoreError: ignoreAxiosError} as any, + ) + return response +} + +export const fetchCustomEvaluations = async (app_id: string, ignoreAxiosError: boolean = false) => { + const response = await axios.get( + `${getAgentaApiUrl()}/api/human-evaluations/custom_evaluation/list/${app_id}/`, + {_ignoreError: ignoreAxiosError} as any, + ) + return response +} + +export const fetchCustomEvaluationDetail = async ( + id: string, + ignoreAxiosError: boolean = false, +) => { + const response = await axios.get( + `${getAgentaApiUrl()}/api/human-evaluations/custom_evaluation/${id}/`, + {_ignoreError: ignoreAxiosError} as any, + ) + return response.data +} + +export const fetchCustomEvaluationNames = async ( + app_id: string, + ignoreAxiosError: boolean = false, +) => { + const response = await axios.get( + `${getAgentaApiUrl()}/api/human-evaluations/custom_evaluation/${app_id}/names/`, + {_ignoreError: ignoreAxiosError} as any, + ) + return response +} + +export const createExecuteCustomEvaluationCode = async ( + payload: ExecuteCustomEvalCode, + ignoreAxiosError: boolean = false, +) => { + const response = await axios.post( + `${getAgentaApiUrl()}/api/human-evaluations/custom_evaluation/execute/${ + payload.evaluation_id + }/`, + payload, + {_ignoreError: ignoreAxiosError} as any, + ) + return response +} + +export const updateEvaluationScenarioScore = async ( + evaluation_scenario_id: string, + score: number, + ignoreAxiosError: boolean = false, +) => { + const response = await axios.put( + `${getAgentaApiUrl()}/api/human-evaluations/evaluation_scenario/${evaluation_scenario_id}/score/`, + {score}, + {_ignoreError: ignoreAxiosError} as any, + ) + return response +} diff --git a/agenta-web/src/services/playground/api/index.ts b/agenta-web/src/services/playground/api/index.ts new file mode 100644 index 0000000000..34d1380f96 --- /dev/null +++ b/agenta-web/src/services/playground/api/index.ts @@ -0,0 +1,54 @@ +import {Parameter} from "@/lib/Types" +import axios from "@/lib/helpers/axiosConfig" +import {getAgentaApiUrl} from "@/lib/helpers/utils" + +//Prefix convention: +// - fetch: GET single entity from server +// - fetchAll: GET all entities from server +// - create: POST data to server +// - update: PUT data to server +// - delete: DELETE data from server + +export function restartAppVariantContainer(variantId: string) { + return axios.post(`${getAgentaApiUrl()}/api/containers/restart_container/`, { + variant_id: variantId, + }) +} + +export async function deleteSingleVariant(variantId: string) { + await axios.delete(`${getAgentaApiUrl()}/api/variants/${variantId}/`) +} + +export async function updateVariantParams(variantId: string, parameters: Parameter[]) { + await axios.put(`${getAgentaApiUrl()}/api/variants/${variantId}/parameters/`, { + parameters: parameters.reduce((acc, param) => { + return {...acc, [param.name]: param.default} + }, {}), + }) +} + +/** + * Saves a new variant to the database based on previous + */ +export async function createNewVariant( + baseId: string, + newVariantName: string, + newConfigName: string, + parameters: Parameter[], +) { + await axios.post(`${getAgentaApiUrl()}/api/variants/from-base/`, { + base_id: baseId, + new_variant_name: newVariantName, + new_config_name: newConfigName, + parameters: parameters.reduce((acc, param) => { + return {...acc, [param.name]: param.default} + }, {}), + }) +} + +export const fetchVariantLogs = async (variantId: string, ignoreAxiosError: boolean = false) => { + const response = await axios.get(`${getAgentaApiUrl()}/api/variants/${variantId}/logs`, { + _ignoreError: ignoreAxiosError, + } as any) + return response.data +} diff --git a/agenta-web/src/services/testsets/api/index.ts b/agenta-web/src/services/testsets/api/index.ts new file mode 100644 index 0000000000..b4806ccc1f --- /dev/null +++ b/agenta-web/src/services/testsets/api/index.ts @@ -0,0 +1,70 @@ +import useSWR from "swr" +import axios from "@/lib/helpers/axiosConfig" +import {getAgentaApiUrl} from "@/lib/helpers/utils" +import {axiosFetcher} from "@/services/api" + +//Prefix convention: +// - fetch: GET single entity from server +// - fetchAll: GET all entities from server +// - create: POST data to server +// - update: PUT data to server +// - delete: DELETE data from server + +export const useLoadTestsetsList = (appId: string) => { + const {data, error, mutate, isLoading} = useSWR( + `${getAgentaApiUrl()}/api/testsets/?app_id=${appId}`, + axiosFetcher, + {revalidateOnFocus: false, shouldRetryOnError: false}, + ) + + return { + testsets: data || [], + isTestsetsLoading: isLoading, + isTestsetsLoadingError: error, + mutate, + } +} + +export const fetchTestsets = async (appId: string) => { + const response = await axios.get(`${getAgentaApiUrl()}/api/testsets/?app_id=${appId}`) + return response.data +} + +export async function createNewTestset(appId: string, testsetName: string, testsetData: any) { + const response = await axios.post(`${getAgentaApiUrl()}/api/testsets/${appId}/`, { + name: testsetName, + csvdata: testsetData, + }) + return response +} + +export async function updateTestset(testsetId: String, testsetName: string, testsetData: any) { + const response = await axios.put(`${getAgentaApiUrl()}/api/testsets/${testsetId}/`, { + name: testsetName, + csvdata: testsetData, + }) + return response +} + +export const fetchTestset = async (testsetId: string | null) => { + if (!testsetId) { + return { + id: undefined, + name: "No Test Set Associated", + created_at: "", + updated_at: "", + csvdata: [], + } + } + const response = await axios.get(`${getAgentaApiUrl()}/api/testsets/${testsetId}/`) + return response.data +} + +export const deleteTestsets = async (ids: string[]) => { + const response = await axios({ + method: "delete", + url: `${getAgentaApiUrl()}/api/testsets/`, + data: {testset_ids: ids}, + }) + return response.data +}