From f32638b87a2b45635364b15d5f777567fb206393 Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Mon, 9 Dec 2024 15:03:33 +0100 Subject: [PATCH 01/18] enhance(frontend): cleanup _app and move posthog initialization to a custom provider --- .../helpers/analytics/AgPosthogProvider.tsx | 51 +++++++++++++++++++ .../src/lib/helpers/analytics/store/atoms.ts | 4 ++ agenta-web/src/pages/_app.tsx | 34 ++----------- 3 files changed, 58 insertions(+), 31 deletions(-) create mode 100644 agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx create mode 100644 agenta-web/src/lib/helpers/analytics/store/atoms.ts diff --git a/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx b/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx new file mode 100644 index 0000000000..b58e822175 --- /dev/null +++ b/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx @@ -0,0 +1,51 @@ +import {useCallback, useEffect, useRef} from "react" +import {useRouter} from "next/router" +import {useAtom} from "jotai" +import {posthogAtom} from "./store/atoms" + +const CustomPosthogProvider = ({children}: {children: React.ReactNode}) => { + const router = useRouter() + const loadingPosthog = useRef(false) + const [posthogClient, setPosthogClient] = useAtom(posthogAtom) + + const initPosthog = useCallback(async () => { + if (!!posthogClient) return + if (loadingPosthog.current) return + + loadingPosthog.current = true + + const posthog = (await import("posthog-js")).default + + posthog.init(process.env.NEXT_PUBLIC_POSTHOG_API_KEY!, { + api_host: "https://app.posthog.com", + // Enable debug mode in development + loaded: (posthog) => { + console.log("initialized posthog", process.env.NEXT_PUBLIC_POSTHOG_API_KEY) + setPosthogClient(posthog) + if (process.env.NODE_ENV === "development") posthog.debug() + }, + capture_pageview: false, + persistence: "localStorage+cookie", + }) + }, [posthogClient, setPosthogClient]) + + useEffect(() => { + initPosthog() + }, [initPosthog]) + + const handleRouteChange = useCallback(() => { + posthogClient?.capture("$pageview", {$current_url: window.location.href}) + }, [posthogClient]) + + useEffect(() => { + router.events.on("routeChangeComplete", handleRouteChange) + + return () => { + router.events.off("routeChangeComplete", handleRouteChange) + } + }, [handleRouteChange, router.events]) + + return <>{children} +} + +export default CustomPosthogProvider diff --git a/agenta-web/src/lib/helpers/analytics/store/atoms.ts b/agenta-web/src/lib/helpers/analytics/store/atoms.ts new file mode 100644 index 0000000000..5907eb709b --- /dev/null +++ b/agenta-web/src/lib/helpers/analytics/store/atoms.ts @@ -0,0 +1,4 @@ +import {atom} from "jotai" +import {type PostHog} from "posthog-js" + +export const posthogAtom = atom(null) diff --git a/agenta-web/src/pages/_app.tsx b/agenta-web/src/pages/_app.tsx index c84846caf9..ca98a01c65 100644 --- a/agenta-web/src/pages/_app.tsx +++ b/agenta-web/src/pages/_app.tsx @@ -1,12 +1,7 @@ -import {useEffect} from "react" import type {AppProps} from "next/app" -import {useRouter} from "next/router" import Head from "next/head" import dynamic from "next/dynamic" -import posthog from "posthog-js" -import {PostHogProvider} from "posthog-js/react" - import "@/styles/globals.css" import Layout from "@/components/Layout/Layout" import {dynamicComponent} from "@/lib/helpers/dynamic" @@ -19,37 +14,14 @@ import "ag-grid-community/styles/ag-theme-alpine.css" import {Inter} from "next/font/google" const NoMobilePageWrapper = dynamicComponent("NoMobilePageWrapper/NoMobilePageWrapper") +const CustomPosthogProvider = dynamic(() => import("@/lib/helpers/analytics/AgPosthogProvider")) const inter = Inter({ subsets: ["latin"], variable: "--font-inter", }) -// Initialize the Posthog client -if (typeof window !== "undefined") { - posthog.init(process.env.NEXT_PUBLIC_POSTHOG_API_KEY!, { - api_host: "https://app.posthog.com", - // Enable debug mode in development - loaded: (posthog) => { - if (process.env.NODE_ENV === "development") posthog.debug() - }, - capture_pageview: false, - persistence: "localStorage+cookie", - }) -} - export default function App({Component, pageProps}: AppProps) { - const router = useRouter() - - useEffect(() => { - const handleRouteChange = () => - posthog.capture("$pageview", {$current_url: window.location.href}) - router.events.on("routeChangeComplete", handleRouteChange) - - return () => { - router.events.off("routeChangeComplete", handleRouteChange) - } - }, []) return ( <> @@ -57,7 +29,7 @@ export default function App({Component, pageProps}: AppProps) {
- + @@ -70,7 +42,7 @@ export default function App({Component, pageProps}: AppProps) { - +
) From 5aa394e4043ba06fea67167f0b1acfd8cedc9d48 Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Mon, 9 Dec 2024 15:03:58 +0100 Subject: [PATCH 02/18] enhance(frontend): update usePostHogAg usage --- .../components/AppSelector/AppSelector.tsx | 2 +- .../Playground/Views/ParametersView.tsx | 2 +- .../Playground/Views/PublishVariantModal.tsx | 2 +- agenta-web/src/contexts/profile.context.tsx | 2 +- agenta-web/src/hooks/useSession.ts | 6 ++-- .../helpers/analytics}/hooks/usePostHogAg.ts | 30 +++++++++++-------- 6 files changed, 25 insertions(+), 19 deletions(-) rename agenta-web/src/{ => lib/helpers/analytics}/hooks/usePostHogAg.ts (51%) diff --git a/agenta-web/src/components/AppSelector/AppSelector.tsx b/agenta-web/src/components/AppSelector/AppSelector.tsx index 6282d8d8b2..abfc24e13f 100644 --- a/agenta-web/src/components/AppSelector/AppSelector.tsx +++ b/agenta-web/src/components/AppSelector/AppSelector.tsx @@ -16,7 +16,7 @@ import {createUseStyles} from "react-jss" import {useAppsData} from "@/contexts/app.context" import {useProfileData} from "@/contexts/profile.context" import CreateAppStatusModal from "./modals/CreateAppStatusModal" -import {usePostHogAg} from "@/hooks/usePostHogAg" +import {usePostHogAg} from "@/lib/helpers/analytics/hooks/usePostHogAg" import {LlmProvider, getAllProviderLlmKeys} from "@/lib/helpers/llmProviders" import ResultComponent from "../ResultComponent/ResultComponent" import {dynamicContext} from "@/lib/helpers/dynamic" diff --git a/agenta-web/src/components/Playground/Views/ParametersView.tsx b/agenta-web/src/components/Playground/Views/ParametersView.tsx index 1cc74ab20e..a9ba316b5f 100644 --- a/agenta-web/src/components/Playground/Views/ParametersView.tsx +++ b/agenta-web/src/components/Playground/Views/ParametersView.tsx @@ -7,7 +7,7 @@ import {ModelParameters, ObjectParameters, StringParameters} from "./ParametersC import PublishVariantModal from "./PublishVariantModal" import {deleteSingleVariant} from "@/services/playground/api" import {CloudUploadOutlined, DeleteOutlined, HistoryOutlined, SaveOutlined} from "@ant-design/icons" -import {usePostHogAg} from "@/hooks/usePostHogAg" +import {usePostHogAg} from "@/lib/helpers/analytics/hooks/usePostHogAg" import {isDemo} from "@/lib/helpers/utils" import {useQueryParam} from "@/hooks/useQuery" import {dynamicComponent, dynamicService} from "@/lib/helpers/dynamic" diff --git a/agenta-web/src/components/Playground/Views/PublishVariantModal.tsx b/agenta-web/src/components/Playground/Views/PublishVariantModal.tsx index f4f03608a8..dede33f89c 100644 --- a/agenta-web/src/components/Playground/Views/PublishVariantModal.tsx +++ b/agenta-web/src/components/Playground/Views/PublishVariantModal.tsx @@ -1,4 +1,4 @@ -import {usePostHogAg} from "@/hooks/usePostHogAg" +import {usePostHogAg} from "@/lib/helpers/analytics/hooks/usePostHogAg" import {Environment, Variant} from "@/lib/Types" import {variantNameWithRev} from "@/lib/helpers/variantHelper" import {fetchEnvironments, createPublishVariant} from "@/services/deployment/api" diff --git a/agenta-web/src/contexts/profile.context.tsx b/agenta-web/src/contexts/profile.context.tsx index a59ffa4229..bcb6fd8bb7 100644 --- a/agenta-web/src/contexts/profile.context.tsx +++ b/agenta-web/src/contexts/profile.context.tsx @@ -1,4 +1,4 @@ -import {usePostHogAg} from "@/hooks/usePostHogAg" +import {usePostHogAg} from "@/lib/helpers/analytics/hooks/usePostHogAg" import {useSession} from "@/hooks/useSession" import useStateCallback from "@/hooks/useStateCallback" import {isDemo} from "@/lib/helpers/utils" diff --git a/agenta-web/src/hooks/useSession.ts b/agenta-web/src/hooks/useSession.ts index df7fb53c03..827702d408 100644 --- a/agenta-web/src/hooks/useSession.ts +++ b/agenta-web/src/hooks/useSession.ts @@ -1,9 +1,8 @@ import {useProfileData} from "@/contexts/profile.context" import {isDemo} from "@/lib/helpers/utils" import {useRouter} from "next/router" -import posthog from "posthog-js" import {useSessionContext} from "supertokens-auth-react/recipe/session" -import {signOut} from "supertokens-auth-react/recipe/session" +import {signOut} from "supertokens-auth-react/recipe/session/signOut" export const useSession: () => {loading: boolean; doesSessionExist: boolean; logout: () => void} = isDemo() @@ -17,7 +16,8 @@ export const useSession: () => {loading: boolean; doesSessionExist: boolean; log doesSessionExist: (res as any).doesSessionExist, logout: () => { signOut() - .then(() => { + .then(async () => { + const posthog = (await import("posthog-js")).default posthog.reset() reset() router.push("/auth") diff --git a/agenta-web/src/hooks/usePostHogAg.ts b/agenta-web/src/lib/helpers/analytics/hooks/usePostHogAg.ts similarity index 51% rename from agenta-web/src/hooks/usePostHogAg.ts rename to agenta-web/src/lib/helpers/analytics/hooks/usePostHogAg.ts index 8b3a4655e6..d1ea894de8 100644 --- a/agenta-web/src/hooks/usePostHogAg.ts +++ b/agenta-web/src/lib/helpers/analytics/hooks/usePostHogAg.ts @@ -1,35 +1,41 @@ import {useLayoutEffect} from "react" import {isDemo, generateOrRetrieveDistinctId} from "@/lib/helpers/utils" -import {usePostHog} from "posthog-js/react" import {useProfileData} from "@/contexts/profile.context" +import {useAtom} from "jotai" +import {posthogAtom} from "../store/atoms" +import {type PostHog} from "posthog-js" export const usePostHogAg = () => { const trackingEnabled = process.env.NEXT_PUBLIC_TELEMETRY_TRACKING_ENABLED === "true" const {user} = useProfileData() - const posthog = usePostHog() + const [posthog] = useAtom(posthogAtom) const _id: string | undefined = isDemo() ? user?.email : generateOrRetrieveDistinctId() - - const capture: typeof posthog.capture = (...args) => { + const capture: PostHog["capture"] = (...args) => { if (trackingEnabled && user?.id) { - return posthog.capture(...args) + return posthog?.capture?.(...args) } return undefined } - - const identify: typeof posthog.identify = (id, ...args) => { + const identify: PostHog["identify"] = (id, ...args) => { if (trackingEnabled && user?.id) { - posthog.identify(_id !== undefined ? _id : id, ...args) + console.log("POSTHOG: identify") + posthog?.identify?.(_id !== undefined ? _id : id, ...args) } } - useLayoutEffect(() => { - if (!trackingEnabled) posthog.opt_out_capturing() - }, [trackingEnabled]) + if (!posthog) return + + if (!trackingEnabled) { + console.log("POSTHOG: opt_out_capturing") + posthog.opt_out_capturing() + } + }, [posthog, trackingEnabled]) useLayoutEffect(() => { + if (!posthog) return if (posthog.get_distinct_id() !== _id) identify() - }, [user?.id]) + }, [posthog, _id]) return {...posthog, identify, capture} } From cd680e2dd50a20799dafa66dfbef55715ed98f53 Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Mon, 9 Dec 2024 15:33:39 +0100 Subject: [PATCH 03/18] fix(frontend): broken signOut function import --- agenta-web/src/hooks/useSession.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-web/src/hooks/useSession.ts b/agenta-web/src/hooks/useSession.ts index 827702d408..8312247920 100644 --- a/agenta-web/src/hooks/useSession.ts +++ b/agenta-web/src/hooks/useSession.ts @@ -2,7 +2,7 @@ import {useProfileData} from "@/contexts/profile.context" import {isDemo} from "@/lib/helpers/utils" import {useRouter} from "next/router" import {useSessionContext} from "supertokens-auth-react/recipe/session" -import {signOut} from "supertokens-auth-react/recipe/session/signOut" +import {signOut} from "supertokens-auth-react/recipe/session" export const useSession: () => {loading: boolean; doesSessionExist: boolean; logout: () => void} = isDemo() From 0f874b5ea5e7cf1a973c8de069f5d353e1a66398 Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Mon, 9 Dec 2024 16:19:50 +0100 Subject: [PATCH 04/18] chore(frontend): update import path on changed file --- agenta-web/src/components/pages/app-management/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-web/src/components/pages/app-management/index.tsx b/agenta-web/src/components/pages/app-management/index.tsx index 72b760b8ac..1e557487a2 100644 --- a/agenta-web/src/components/pages/app-management/index.tsx +++ b/agenta-web/src/components/pages/app-management/index.tsx @@ -7,7 +7,7 @@ import {waitForAppToStart} from "@/services/api" import {createUseStyles} from "react-jss" import {useAppsData} from "@/contexts/app.context" import {useProfileData} from "@/contexts/profile.context" -import {usePostHogAg} from "@/hooks/usePostHogAg" +import {usePostHogAg} from "@/lib/helpers/analytics/hooks/usePostHogAg" import {LlmProvider, getAllProviderLlmKeys} from "@/lib/helpers/llmProviders" import {dynamicComponent, dynamicContext} from "@/lib/helpers/dynamic" import dayjs from "dayjs" From 9bbb90c61f98222f53ee8968a709b39b3f89838f Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Mon, 9 Dec 2024 18:57:20 +0100 Subject: [PATCH 05/18] improve for null returns --- .../Playground/Views/ParametersView.tsx | 2 +- .../Playground/Views/PublishVariantModal.tsx | 2 +- .../components/pages/app-management/index.tsx | 2 +- agenta-web/src/contexts/profile.context.tsx | 2 +- .../lib/helpers/analytics/hooks/usePostHogAg.ts | 16 +++++++++++++--- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/agenta-web/src/components/Playground/Views/ParametersView.tsx b/agenta-web/src/components/Playground/Views/ParametersView.tsx index a9ba316b5f..72da9eda90 100644 --- a/agenta-web/src/components/Playground/Views/ParametersView.tsx +++ b/agenta-web/src/components/Playground/Views/ParametersView.tsx @@ -128,7 +128,7 @@ const ParametersView: React.FC = ({ onStateChange(false) res(true) }) - posthog.capture("variant_saved", {variant_id: variant.variantId}) + posthog?.capture?.("variant_saved", {variant_id: variant.variantId}) }) } diff --git a/agenta-web/src/components/Playground/Views/PublishVariantModal.tsx b/agenta-web/src/components/Playground/Views/PublishVariantModal.tsx index dede33f89c..ab4221e04c 100644 --- a/agenta-web/src/components/Playground/Views/PublishVariantModal.tsx +++ b/agenta-web/src/components/Playground/Views/PublishVariantModal.tsx @@ -58,7 +58,7 @@ const PublishVariantModal: React.FC = ({ closeModal() await loadEnvironments() message.success(`Published ${variant.variantName} to ${envName}`) - posthog.capture("app_deployed", {app_id: appId, environment: envName}) + posthog?.capture?.("app_deployed", {app_id: appId, environment: envName}) }) } diff --git a/agenta-web/src/components/pages/app-management/index.tsx b/agenta-web/src/components/pages/app-management/index.tsx index 1e557487a2..6a69ef89a7 100644 --- a/agenta-web/src/components/pages/app-management/index.tsx +++ b/agenta-web/src/components/pages/app-management/index.tsx @@ -130,7 +130,7 @@ const AppManagement: React.FC = () => { setFetchingTemplate(false) if (status === "success") { mutate() - posthog.capture("app_deployment", { + posthog?.capture?.("app_deployment", { properties: { app_id: appId, environment: "UI", diff --git a/agenta-web/src/contexts/profile.context.tsx b/agenta-web/src/contexts/profile.context.tsx index bcb6fd8bb7..815b616fcf 100644 --- a/agenta-web/src/contexts/profile.context.tsx +++ b/agenta-web/src/contexts/profile.context.tsx @@ -38,7 +38,7 @@ const ProfileContextProvider: React.FC = ({children}) => { setLoading(true) fetchProfile() .then((profile) => { - posthog.identify() + posthog?.identify?.() setUser(profile.data, onSuccess) }) .catch((error) => { diff --git a/agenta-web/src/lib/helpers/analytics/hooks/usePostHogAg.ts b/agenta-web/src/lib/helpers/analytics/hooks/usePostHogAg.ts index d1ea894de8..a7ef761edb 100644 --- a/agenta-web/src/lib/helpers/analytics/hooks/usePostHogAg.ts +++ b/agenta-web/src/lib/helpers/analytics/hooks/usePostHogAg.ts @@ -5,7 +5,12 @@ import {useAtom} from "jotai" import {posthogAtom} from "../store/atoms" import {type PostHog} from "posthog-js" -export const usePostHogAg = () => { +interface ExtendedPostHog extends PostHog { + identify: PostHog["identify"] + capture: PostHog["capture"] +} + +export const usePostHogAg = (): ExtendedPostHog | null => { const trackingEnabled = process.env.NEXT_PUBLIC_TELEMETRY_TRACKING_ENABLED === "true" const {user} = useProfileData() const [posthog] = useAtom(posthogAtom) @@ -19,7 +24,6 @@ export const usePostHogAg = () => { } const identify: PostHog["identify"] = (id, ...args) => { if (trackingEnabled && user?.id) { - console.log("POSTHOG: identify") posthog?.identify?.(_id !== undefined ? _id : id, ...args) } } @@ -37,5 +41,11 @@ export const usePostHogAg = () => { if (posthog.get_distinct_id() !== _id) identify() }, [posthog, _id]) - return {...posthog, identify, capture} + return posthog + ? ({ + ...posthog, + identify, + capture, + } as ExtendedPostHog) + : null } From a81cdffe7f8227628b5e83769d1b623938d80839 Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Tue, 10 Dec 2024 11:57:01 +0100 Subject: [PATCH 06/18] chore(frontend): remove console log --- agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx b/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx index b58e822175..f7b1011560 100644 --- a/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx +++ b/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx @@ -20,7 +20,6 @@ const CustomPosthogProvider = ({children}: {children: React.ReactNode}) => { api_host: "https://app.posthog.com", // Enable debug mode in development loaded: (posthog) => { - console.log("initialized posthog", process.env.NEXT_PUBLIC_POSTHOG_API_KEY) setPosthogClient(posthog) if (process.env.NODE_ENV === "development") posthog.debug() }, From bfaf2838103da927225ad625b85f6d8d87b5afa1 Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Tue, 10 Dec 2024 12:23:36 +0100 Subject: [PATCH 07/18] enhance(frontend): allow posthog provider accept config options --- .../src/lib/helpers/analytics/AgPosthogProvider.tsx | 9 +++++---- agenta-web/src/lib/helpers/analytics/store/atoms.ts | 3 ++- agenta-web/src/lib/helpers/analytics/types.d.ts | 7 +++++++ agenta-web/src/pages/_app.tsx | 6 +++++- 4 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 agenta-web/src/lib/helpers/analytics/types.d.ts diff --git a/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx b/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx index f7b1011560..2f6849c1fc 100644 --- a/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx +++ b/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx @@ -1,9 +1,10 @@ import {useCallback, useEffect, useRef} from "react" import {useRouter} from "next/router" import {useAtom} from "jotai" -import {posthogAtom} from "./store/atoms" +import {posthogAtom, type PostHogConfig} from "./store/atoms" +import {CustomPosthogProviderType} from "./types" -const CustomPosthogProvider = ({children}: {children: React.ReactNode}) => { +const CustomPosthogProvider: CustomPosthogProviderType = ({children, config}) => { const router = useRouter() const loadingPosthog = useRef(false) const [posthogClient, setPosthogClient] = useAtom(posthogAtom) @@ -24,9 +25,9 @@ const CustomPosthogProvider = ({children}: {children: React.ReactNode}) => { if (process.env.NODE_ENV === "development") posthog.debug() }, capture_pageview: false, - persistence: "localStorage+cookie", + ...config, }) - }, [posthogClient, setPosthogClient]) + }, [config, posthogClient, setPosthogClient]) useEffect(() => { initPosthog() diff --git a/agenta-web/src/lib/helpers/analytics/store/atoms.ts b/agenta-web/src/lib/helpers/analytics/store/atoms.ts index 5907eb709b..55acac9d8c 100644 --- a/agenta-web/src/lib/helpers/analytics/store/atoms.ts +++ b/agenta-web/src/lib/helpers/analytics/store/atoms.ts @@ -1,4 +1,5 @@ import {atom} from "jotai" -import {type PostHog} from "posthog-js" +import {type PostHog, type PostHogConfig} from "posthog-js" +export type {PostHogConfig} export const posthogAtom = atom(null) diff --git a/agenta-web/src/lib/helpers/analytics/types.d.ts b/agenta-web/src/lib/helpers/analytics/types.d.ts new file mode 100644 index 0000000000..d55cc0d99c --- /dev/null +++ b/agenta-web/src/lib/helpers/analytics/types.d.ts @@ -0,0 +1,7 @@ +import {type PostHogConfig} from "./store/atoms" + +export interface CustomPosthogProviderType + extends React.FC<{ + children: React.ReactNode + config: Partial + }> {} diff --git a/agenta-web/src/pages/_app.tsx b/agenta-web/src/pages/_app.tsx index ca98a01c65..8866c081f8 100644 --- a/agenta-web/src/pages/_app.tsx +++ b/agenta-web/src/pages/_app.tsx @@ -29,7 +29,11 @@ export default function App({Component, pageProps}: AppProps) {
- + From 85ad2901ca3eee88ebb540e9195575d8375938a9 Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Tue, 10 Dec 2024 22:38:00 +0100 Subject: [PATCH 08/18] feat(frontend): shared SWR config for the application --- agenta-web/src/lib/api/SWRConfig.tsx | 19 +++++++++++++++++++ .../{helpers => api/assets}/axiosConfig.ts | 4 ++-- 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 agenta-web/src/lib/api/SWRConfig.tsx rename agenta-web/src/lib/{helpers => api/assets}/axiosConfig.ts (94%) diff --git a/agenta-web/src/lib/api/SWRConfig.tsx b/agenta-web/src/lib/api/SWRConfig.tsx new file mode 100644 index 0000000000..b393aedbdf --- /dev/null +++ b/agenta-web/src/lib/api/SWRConfig.tsx @@ -0,0 +1,19 @@ +import {SWRConfig, type SWRConfiguration} from "swr" +import axios from "@/lib/api/assets/axiosConfig" + +const config: SWRConfiguration = { + fetcher: (url: string) => axios.get(url).then((res) => res.data), +} + +const AgSWRConfig = ({ + children, + config: passedConfig = {}, +}: { + children: React.ReactNode + config?: Partial +}) => { + const mergedConfig = {...config, ...passedConfig} + return {children} +} + +export default AgSWRConfig diff --git a/agenta-web/src/lib/helpers/axiosConfig.ts b/agenta-web/src/lib/api/assets/axiosConfig.ts similarity index 94% rename from agenta-web/src/lib/helpers/axiosConfig.ts rename to agenta-web/src/lib/api/assets/axiosConfig.ts index 0961ebe87c..3da6bbe2a5 100644 --- a/agenta-web/src/lib/helpers/axiosConfig.ts +++ b/agenta-web/src/lib/api/assets/axiosConfig.ts @@ -1,8 +1,8 @@ import axiosApi from "axios" -import {getErrorMessage, globalErrorHandler} from "./errorHandler" +import {getErrorMessage, globalErrorHandler} from "../../helpers/errorHandler" import {signOut} from "supertokens-auth-react/recipe/session" import router from "next/router" -import {getAgentaApiUrl} from "./utils" +import {getAgentaApiUrl} from "../../helpers/utils" import isObject from "lodash/isObject" import AlertPopup from "@/components/AlertPopup/AlertPopup" From 91fb899918da318d875581f3c626e65d43f420f4 Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Tue, 10 Dec 2024 22:38:33 +0100 Subject: [PATCH 09/18] chore(frontend): update existing useSWR hooks --- agenta-web/src/contexts/app.context.tsx | 7 ++----- agenta-web/src/services/testsets/api/index.ts | 11 +++++------ 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/agenta-web/src/contexts/app.context.tsx b/agenta-web/src/contexts/app.context.tsx index 548aa34f39..e0e6768dc7 100644 --- a/agenta-web/src/contexts/app.context.tsx +++ b/agenta-web/src/contexts/app.context.tsx @@ -45,12 +45,9 @@ const useApps = () => { const isMockProjectId = projectId === DEFAULT_UUID const {selectedOrg, loading} = useOrgData() + const shouldFetch = !!user && (!isDemo() || !!selectedOrg?.id) const {data, error, isLoading, mutate} = useSWR( - !!user - ? `${getAgentaApiUrl()}/api/apps?` + - (!isMockProjectId ? `project_id=${projectId}&` : "") - : null, - !!user ? (isDemo() ? (selectedOrg?.id ? axiosFetcher : () => {}) : axiosFetcher) : null, + shouldFetch ? `/api/apps?` + (!isMockProjectId ? `project_id=${projectId}&` : "") : null, { shouldRetryOnError: false, }, diff --git a/agenta-web/src/services/testsets/api/index.ts b/agenta-web/src/services/testsets/api/index.ts index e6fb42f774..8527bd46a3 100644 --- a/agenta-web/src/services/testsets/api/index.ts +++ b/agenta-web/src/services/testsets/api/index.ts @@ -1,5 +1,5 @@ import useSWR from "swr" -import axios from "@/lib/helpers/axiosConfig" +import axios from "@/lib/api/assets/axiosConfig" import {getAgentaApiUrl} from "@/lib/helpers/utils" import {axiosFetcher} from "@/services/api" import {getCurrentProject} from "@/contexts/project.context" @@ -14,11 +14,10 @@ import {getCurrentProject} from "@/contexts/project.context" export const useLoadTestsetsList = () => { const {projectId} = getCurrentProject() - const {data, error, mutate, isLoading} = useSWR( - `${getAgentaApiUrl()}/api/testsets?project_id=${projectId}`, - axiosFetcher, - {revalidateOnFocus: false, shouldRetryOnError: false}, - ) + const {data, error, mutate, isLoading} = useSWR(`/api/testsets?project_id=${projectId}`, { + revalidateOnFocus: false, + shouldRetryOnError: false, + }) return { testsets: data || [], From c4836fbc637e3bc6afe72500a0e1b5f5dc3cd4f5 Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Tue, 10 Dec 2024 22:38:52 +0100 Subject: [PATCH 10/18] chore(frontend): update axios imports --- .../components/HumanEvaluationModal/HumanEvaluationModal.tsx | 2 +- agenta-web/src/lib/hooks/useVariant.ts | 2 +- agenta-web/src/services/api.ts | 2 +- agenta-web/src/services/app-selector/api/index.ts | 2 +- agenta-web/src/services/deployment/api/index.ts | 2 +- agenta-web/src/services/evaluations/api/index.ts | 2 +- agenta-web/src/services/human-evaluations/api/index.ts | 2 +- agenta-web/src/services/observability/core/index.ts | 2 +- agenta-web/src/services/playground/api/index.ts | 2 +- agenta-web/src/services/project/index.ts | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/agenta-web/src/components/HumanEvaluationModal/HumanEvaluationModal.tsx b/agenta-web/src/components/HumanEvaluationModal/HumanEvaluationModal.tsx index 2383cac732..65ad58c868 100644 --- a/agenta-web/src/components/HumanEvaluationModal/HumanEvaluationModal.tsx +++ b/agenta-web/src/components/HumanEvaluationModal/HumanEvaluationModal.tsx @@ -6,7 +6,7 @@ import {isDemo} from "@/lib/helpers/utils" import {Button, Col, Dropdown, MenuProps, Modal, ModalProps, Row, Spin, message} from "antd" import {getErrorMessage} from "@/lib/helpers/errorHandler" import {EvaluationType} from "@/lib/enums" -import {PERMISSION_ERR_MSG} from "@/lib/helpers/axiosConfig" +import {PERMISSION_ERR_MSG} from "@/lib/api/assets/axiosConfig" import {getAllVariantParameters} from "@/lib/helpers/variantHelper" import {useRouter} from "next/router" import {useAppTheme} from "../Layout/ThemeContextProvider" diff --git a/agenta-web/src/lib/hooks/useVariant.ts b/agenta-web/src/lib/hooks/useVariant.ts index 676fa64899..525f54d6bd 100644 --- a/agenta-web/src/lib/hooks/useVariant.ts +++ b/agenta-web/src/lib/hooks/useVariant.ts @@ -1,7 +1,7 @@ import {useState, useEffect, useRef} from "react" import {Variant, Parameter} from "@/lib/Types" import {getAllVariantParameters, updateInputParams} from "@/lib/helpers/variantHelper" -import {PERMISSION_ERR_MSG} from "../helpers/axiosConfig" +import {PERMISSION_ERR_MSG} from "../api/assets/axiosConfig" import {createNewVariant, fetchVariantLogs, updateVariantParams} from "@/services/playground/api" /** diff --git a/agenta-web/src/services/api.ts b/agenta-web/src/services/api.ts index 4031e76103..acbf35af51 100644 --- a/agenta-web/src/services/api.ts +++ b/agenta-web/src/services/api.ts @@ -1,5 +1,5 @@ import {getCurrentProject} from "@/contexts/project.context" -import axios from "@/lib//helpers/axiosConfig" +import axios from "@/lib/api/assets/axiosConfig" import Session from "supertokens-auth-react/recipe/session" import {formatDay} from "@/lib/helpers/dateTimeHelper" import { diff --git a/agenta-web/src/services/app-selector/api/index.ts b/agenta-web/src/services/app-selector/api/index.ts index f7aa7b63d2..fe28fd4550 100644 --- a/agenta-web/src/services/app-selector/api/index.ts +++ b/agenta-web/src/services/app-selector/api/index.ts @@ -1,6 +1,6 @@ import {getCurrentProject} from "@/contexts/project.context" import {AppTemplate} from "@/lib/Types" -import axios from "@/lib/helpers/axiosConfig" +import axios from "@/lib/api/assets/axiosConfig" import {dynamicContext} from "@/lib/helpers/dynamic" import {LlmProvider} from "@/lib/helpers/llmProviders" import {getAgentaApiUrl} from "@/lib/helpers/utils" diff --git a/agenta-web/src/services/deployment/api/index.ts b/agenta-web/src/services/deployment/api/index.ts index 6a7153c883..97ac167543 100644 --- a/agenta-web/src/services/deployment/api/index.ts +++ b/agenta-web/src/services/deployment/api/index.ts @@ -1,6 +1,6 @@ import {getCurrentProject} from "@/contexts/project.context" import {Environment} from "@/lib/Types" -import axios from "@/lib/helpers/axiosConfig" +import axios from "@/lib/api/assets/axiosConfig" import {getAgentaApiUrl} from "@/lib/helpers/utils" //Prefix convention: diff --git a/agenta-web/src/services/evaluations/api/index.ts b/agenta-web/src/services/evaluations/api/index.ts index e747856832..13981ae6bb 100644 --- a/agenta-web/src/services/evaluations/api/index.ts +++ b/agenta-web/src/services/evaluations/api/index.ts @@ -1,4 +1,4 @@ -import axios from "@/lib//helpers/axiosConfig" +import axios from "@/lib/api/assets/axiosConfig" import { ComparisonResultRow, Evaluator, diff --git a/agenta-web/src/services/human-evaluations/api/index.ts b/agenta-web/src/services/human-evaluations/api/index.ts index c4a8cb0e03..1c3e647ed8 100644 --- a/agenta-web/src/services/human-evaluations/api/index.ts +++ b/agenta-web/src/services/human-evaluations/api/index.ts @@ -1,4 +1,4 @@ -import axios from "@/lib//helpers/axiosConfig" +import axios from "@/lib/api/assets/axiosConfig" import { EvaluationResponseType, Evaluation, diff --git a/agenta-web/src/services/observability/core/index.ts b/agenta-web/src/services/observability/core/index.ts index 40bd3f77c5..714fed4c16 100644 --- a/agenta-web/src/services/observability/core/index.ts +++ b/agenta-web/src/services/observability/core/index.ts @@ -1,6 +1,6 @@ import {getAgentaApiUrl} from "@/lib/helpers/utils" import {_AgentaRootsResponse} from "../types" -import axios from "@/lib/helpers/axiosConfig" +import axios from "@/lib/api/assets/axiosConfig" import {getCurrentProject} from "@/contexts/project.context" //Prefix convention: diff --git a/agenta-web/src/services/playground/api/index.ts b/agenta-web/src/services/playground/api/index.ts index f04fc7aefb..404392eb4b 100644 --- a/agenta-web/src/services/playground/api/index.ts +++ b/agenta-web/src/services/playground/api/index.ts @@ -1,6 +1,6 @@ import {getCurrentProject} from "@/contexts/project.context" import {Parameter} from "@/lib/Types" -import axios from "@/lib/helpers/axiosConfig" +import axios from "@/lib/api/assets/axiosConfig" import {getAgentaApiUrl} from "@/lib/helpers/utils" //Prefix convention: diff --git a/agenta-web/src/services/project/index.ts b/agenta-web/src/services/project/index.ts index 95d629d118..cad28a54be 100644 --- a/agenta-web/src/services/project/index.ts +++ b/agenta-web/src/services/project/index.ts @@ -1,4 +1,4 @@ -import axios from "@/lib/helpers/axiosConfig" +import axios from "@/lib/api/assets/axiosConfig" import {getAgentaApiUrl} from "@/lib/helpers/utils" import {ProjectsResponse} from "./types" From 03bc2769a8f4e81b125791856f511302513dde29 Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Tue, 10 Dec 2024 22:39:21 +0100 Subject: [PATCH 11/18] feat(frontend): update _app to include new swr config provider --- agenta-web/src/pages/_app.tsx | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/agenta-web/src/pages/_app.tsx b/agenta-web/src/pages/_app.tsx index c84846caf9..c3dcd378f0 100644 --- a/agenta-web/src/pages/_app.tsx +++ b/agenta-web/src/pages/_app.tsx @@ -17,6 +17,7 @@ import ProjectContextProvider from "@/contexts/project.context" import "ag-grid-community/styles/ag-grid.css" import "ag-grid-community/styles/ag-theme-alpine.css" import {Inter} from "next/font/google" +import AgSWRConfig from "@/lib/api/SWRConfig" const NoMobilePageWrapper = dynamicComponent("NoMobilePageWrapper/NoMobilePageWrapper") @@ -57,20 +58,22 @@ export default function App({Component, pageProps}: AppProps) {
- - - - - - - - - - - - - - + + + + + + + + + + + + + + + +
) From 55e8968aed0a3c0787061e36be59fac45c40c290 Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Wed, 11 Dec 2024 13:07:00 +0100 Subject: [PATCH 12/18] enhance(frontend): add import error handling to AgPosthogProvider --- .../helpers/analytics/AgPosthogProvider.tsx | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx b/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx index 2f6849c1fc..fbd649e547 100644 --- a/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx +++ b/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx @@ -1,4 +1,4 @@ -import {useCallback, useEffect, useRef} from "react" +import {useCallback, useEffect, useRef, useState} from "react" import {useRouter} from "next/router" import {useAtom} from "jotai" import {posthogAtom, type PostHogConfig} from "./store/atoms" @@ -6,28 +6,33 @@ import {CustomPosthogProviderType} from "./types" const CustomPosthogProvider: CustomPosthogProviderType = ({children, config}) => { const router = useRouter() - const loadingPosthog = useRef(false) + const [loadingPosthog, setLoadingPosthog] = useState(false) const [posthogClient, setPosthogClient] = useAtom(posthogAtom) const initPosthog = useCallback(async () => { if (!!posthogClient) return - if (loadingPosthog.current) return - - loadingPosthog.current = true - - const posthog = (await import("posthog-js")).default - - posthog.init(process.env.NEXT_PUBLIC_POSTHOG_API_KEY!, { - api_host: "https://app.posthog.com", - // Enable debug mode in development - loaded: (posthog) => { - setPosthogClient(posthog) - if (process.env.NODE_ENV === "development") posthog.debug() - }, - capture_pageview: false, - ...config, - }) - }, [config, posthogClient, setPosthogClient]) + if (loadingPosthog) return + + setLoadingPosthog(true) + + try { + const posthog = (await import("posthog-js")).default + + posthog.init(process.env.NEXT_PUBLIC_POSTHOG_API_KEY!, { + api_host: "https://app.posthog.com", + // Enable debug mode in development + loaded: (posthog) => { + setPosthogClient(posthog) + if (process.env.NODE_ENV === "development") posthog.debug() + setLoadingPosthog(false) + }, + capture_pageview: false, + ...config, + }) + } catch (loadError) { + setLoadingPosthog(false) + } + }, [loadingPosthog, config, posthogClient, setPosthogClient]) useEffect(() => { initPosthog() From 3aef1fdb5fb0818030df90f7f6912f6d865921ba Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Wed, 11 Dec 2024 14:37:15 +0100 Subject: [PATCH 13/18] utilize finally --- agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx b/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx index fbd649e547..0bceff8190 100644 --- a/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx +++ b/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx @@ -18,18 +18,17 @@ const CustomPosthogProvider: CustomPosthogProviderType = ({children, config}) => try { const posthog = (await import("posthog-js")).default - posthog.init(process.env.NEXT_PUBLIC_POSTHOG_API_KEY!, { + posthog.init("test", { api_host: "https://app.posthog.com", // Enable debug mode in development loaded: (posthog) => { setPosthogClient(posthog) if (process.env.NODE_ENV === "development") posthog.debug() - setLoadingPosthog(false) }, capture_pageview: false, ...config, }) - } catch (loadError) { + } finally { setLoadingPosthog(false) } }, [loadingPosthog, config, posthogClient, setPosthogClient]) From 69dc703307ae42cf48d7a528e8dc1758888ae51f Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Wed, 11 Dec 2024 14:45:30 +0100 Subject: [PATCH 14/18] chore(frontend): code cleanup --- agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx b/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx index 0bceff8190..f4d0b3f7f3 100644 --- a/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx +++ b/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx @@ -18,7 +18,7 @@ const CustomPosthogProvider: CustomPosthogProviderType = ({children, config}) => try { const posthog = (await import("posthog-js")).default - posthog.init("test", { + posthog.init(process.env.NEXT_PUBLIC_POSTHOG_API_KEY!, { api_host: "https://app.posthog.com", // Enable debug mode in development loaded: (posthog) => { From 44aa9816af87df6b5284fab520a6a5e5409bc3b0 Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Wed, 11 Dec 2024 15:59:45 +0100 Subject: [PATCH 15/18] chore(frontend): fix conflict and bring useIsomorphicLayoutEffect improvements here --- agenta-web/src/lib/helpers/analytics/hooks/usePostHogAg.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agenta-web/src/lib/helpers/analytics/hooks/usePostHogAg.ts b/agenta-web/src/lib/helpers/analytics/hooks/usePostHogAg.ts index a7ef761edb..ab3bb2a08f 100644 --- a/agenta-web/src/lib/helpers/analytics/hooks/usePostHogAg.ts +++ b/agenta-web/src/lib/helpers/analytics/hooks/usePostHogAg.ts @@ -1,9 +1,9 @@ -import {useLayoutEffect} from "react" import {isDemo, generateOrRetrieveDistinctId} from "@/lib/helpers/utils" import {useProfileData} from "@/contexts/profile.context" import {useAtom} from "jotai" import {posthogAtom} from "../store/atoms" import {type PostHog} from "posthog-js" +import useIsomorphicLayoutEffect from "@/hooks/useIsomorphicLayoutEffect" interface ExtendedPostHog extends PostHog { identify: PostHog["identify"] @@ -27,7 +27,7 @@ export const usePostHogAg = (): ExtendedPostHog | null => { posthog?.identify?.(_id !== undefined ? _id : id, ...args) } } - useLayoutEffect(() => { + useIsomorphicLayoutEffect(() => { if (!posthog) return if (!trackingEnabled) { @@ -36,7 +36,7 @@ export const usePostHogAg = (): ExtendedPostHog | null => { } }, [posthog, trackingEnabled]) - useLayoutEffect(() => { + useIsomorphicLayoutEffect(() => { if (!posthog) return if (posthog.get_distinct_id() !== _id) identify() }, [posthog, _id]) From 09b6188e539f21bb54e6e09d992d67921552a7ea Mon Sep 17 00:00:00 2001 From: Abram Date: Thu, 12 Dec 2024 10:02:54 +0100 Subject: [PATCH 16/18] refactor (sdk): regenerate sdk backend with fern --- agenta-cli/agenta/client/backend/__init__.py | 35 +- .../client/backend/access_control/__init__.py | 1 + .../client/backend/access_control/client.py | 167 +++++ .../agenta/client/backend/apps/client.py | 80 +- agenta-cli/agenta/client/backend/client.py | 106 +-- .../agenta/client/backend/configs/client.py | 6 + .../client/backend/containers/client.py | 6 + agenta-cli/agenta/client/backend/core/file.py | 21 +- .../client/backend/environments/client.py | 6 + .../client/backend/evaluations/client.py | 15 +- .../client/backend/evaluators/client.py | 24 + .../client/backend/observability/client.py | 38 +- .../backend/observability_v_1/__init__.py | 4 +- .../backend/observability_v_1/client.py | 203 ++++++ .../observability_v_1/types/__init__.py | 3 +- .../backend/observability_v_1/types/format.py | 2 +- .../types/query_analytics_response.py | 7 + .../agenta/client/backend/scopes/__init__.py | 1 + .../agenta/client/backend/scopes/client.py | 114 +++ .../agenta/client/backend/testsets/client.py | 426 +++++++---- .../agenta/client/backend/types/__init__.py | 26 +- .../backend/types/analytics_response.py | 24 + agenta-cli/agenta/client/backend/types/app.py | 3 +- .../backend/types/body_import_testset.py | 1 - .../agenta/client/backend/types/bucket_dto.py | 26 + .../agenta/client/backend/types/header_dto.py | 22 + .../types/legacy_analytics_response.py | 29 + .../client/backend/types/legacy_data_point.py | 27 + .../client/backend/types/lm_providers_enum.py | 21 - .../client/backend/types/metrics_dto.py | 24 + .../agenta/client/backend/types/permission.py | 1 + .../client/backend/types/projects_response.py | 28 + .../client/backend/types/provider_key_dto.py | 23 + .../client/backend/types/provider_kind.py | 21 + .../agenta/client/backend/types/secret_dto.py | 24 + .../client/backend/types/secret_kind.py | 5 + .../backend/types/secret_response_dto.py | 27 + .../agenta/client/backend/variants/client.py | 66 ++ .../agenta/client/backend/vault/__init__.py | 1 + .../agenta/client/backend/vault/client.py | 685 ++++++++++++++++++ 40 files changed, 2116 insertions(+), 233 deletions(-) create mode 100644 agenta-cli/agenta/client/backend/access_control/__init__.py create mode 100644 agenta-cli/agenta/client/backend/access_control/client.py create mode 100644 agenta-cli/agenta/client/backend/observability_v_1/types/query_analytics_response.py create mode 100644 agenta-cli/agenta/client/backend/scopes/__init__.py create mode 100644 agenta-cli/agenta/client/backend/scopes/client.py create mode 100644 agenta-cli/agenta/client/backend/types/analytics_response.py create mode 100644 agenta-cli/agenta/client/backend/types/bucket_dto.py create mode 100644 agenta-cli/agenta/client/backend/types/header_dto.py create mode 100644 agenta-cli/agenta/client/backend/types/legacy_analytics_response.py create mode 100644 agenta-cli/agenta/client/backend/types/legacy_data_point.py delete mode 100644 agenta-cli/agenta/client/backend/types/lm_providers_enum.py create mode 100644 agenta-cli/agenta/client/backend/types/metrics_dto.py create mode 100644 agenta-cli/agenta/client/backend/types/projects_response.py create mode 100644 agenta-cli/agenta/client/backend/types/provider_key_dto.py create mode 100644 agenta-cli/agenta/client/backend/types/provider_kind.py create mode 100644 agenta-cli/agenta/client/backend/types/secret_dto.py create mode 100644 agenta-cli/agenta/client/backend/types/secret_kind.py create mode 100644 agenta-cli/agenta/client/backend/types/secret_response_dto.py create mode 100644 agenta-cli/agenta/client/backend/vault/__init__.py create mode 100644 agenta-cli/agenta/client/backend/vault/client.py diff --git a/agenta-cli/agenta/client/backend/__init__.py b/agenta-cli/agenta/client/backend/__init__.py index 8907b11d29..ad2226148b 100644 --- a/agenta-cli/agenta/client/backend/__init__.py +++ b/agenta-cli/agenta/client/backend/__init__.py @@ -10,11 +10,13 @@ AgentaTreesResponse, AggregatedResult, AggregatedResultEvaluatorConfig, + AnalyticsResponse, App, AppVariantResponse, AppVariantRevision, BaseOutput, BodyImportTestset, + BucketDto, CollectStatusResponse, ConfigDb, ConfigDto, @@ -42,6 +44,7 @@ EvaluatorOutputInterface, ExceptionDto, GetConfigResponse, + HeaderDto, HttpValidationError, HumanEvaluation, HumanEvaluationScenario, @@ -51,12 +54,14 @@ HumanEvaluationUpdate, Image, InviteRequest, + LegacyAnalyticsResponse, + LegacyDataPoint, LifecycleDto, LinkDto, ListApiKeysResponse, LlmRunRateLimit, LlmTokens, - LmProvidersEnum, + MetricsDto, NewHumanEvaluation, NewTestset, NodeDto, @@ -74,11 +79,17 @@ Outputs, ParentDto, Permission, + ProjectsResponse, + ProviderKeyDto, + ProviderKind, ReferenceDto, ReferenceRequestModel, Result, RootDto, Score, + SecretDto, + SecretKind, + SecretResponseDto, SimpleEvaluationOutput, Span, SpanDetail, @@ -111,6 +122,7 @@ ) from .errors import UnprocessableEntityError from . import ( + access_control, apps, bases, configs, @@ -120,12 +132,14 @@ evaluators, observability, observability_v_1, + scopes, testsets, variants, + vault, ) from .client import AgentaApi, AsyncAgentaApi from .containers import ContainerTemplatesResponse -from .observability_v_1 import Format, QueryTracesResponse +from .observability_v_1 import Format, QueryAnalyticsResponse, QueryTracesResponse from .variants import AddVariantFromBaseAndConfigResponse __all__ = [ @@ -140,12 +154,14 @@ "AgentaTreesResponse", "AggregatedResult", "AggregatedResultEvaluatorConfig", + "AnalyticsResponse", "App", "AppVariantResponse", "AppVariantRevision", "AsyncAgentaApi", "BaseOutput", "BodyImportTestset", + "BucketDto", "CollectStatusResponse", "ConfigDb", "ConfigDto", @@ -175,6 +191,7 @@ "ExceptionDto", "Format", "GetConfigResponse", + "HeaderDto", "HttpValidationError", "HumanEvaluation", "HumanEvaluationScenario", @@ -184,12 +201,14 @@ "HumanEvaluationUpdate", "Image", "InviteRequest", + "LegacyAnalyticsResponse", + "LegacyDataPoint", "LifecycleDto", "LinkDto", "ListApiKeysResponse", "LlmRunRateLimit", "LlmTokens", - "LmProvidersEnum", + "MetricsDto", "NewHumanEvaluation", "NewTestset", "NodeDto", @@ -207,12 +226,19 @@ "Outputs", "ParentDto", "Permission", + "ProjectsResponse", + "ProviderKeyDto", + "ProviderKind", + "QueryAnalyticsResponse", "QueryTracesResponse", "ReferenceDto", "ReferenceRequestModel", "Result", "RootDto", "Score", + "SecretDto", + "SecretKind", + "SecretResponseDto", "SimpleEvaluationOutput", "Span", "SpanDetail", @@ -243,6 +269,7 @@ "WorkspaceResponse", "WorkspaceRole", "WorkspaceRoleResponse", + "access_control", "apps", "bases", "configs", @@ -252,6 +279,8 @@ "evaluators", "observability", "observability_v_1", + "scopes", "testsets", "variants", + "vault", ] diff --git a/agenta-cli/agenta/client/backend/access_control/__init__.py b/agenta-cli/agenta/client/backend/access_control/__init__.py new file mode 100644 index 0000000000..67a41b2742 --- /dev/null +++ b/agenta-cli/agenta/client/backend/access_control/__init__.py @@ -0,0 +1 @@ +# This file was auto-generated by Fern from our API Definition. diff --git a/agenta-cli/agenta/client/backend/access_control/client.py b/agenta-cli/agenta/client/backend/access_control/client.py new file mode 100644 index 0000000000..0a23474e41 --- /dev/null +++ b/agenta-cli/agenta/client/backend/access_control/client.py @@ -0,0 +1,167 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.client_wrapper import SyncClientWrapper +import typing +from ..core.request_options import RequestOptions +from ..core.pydantic_utilities import parse_obj_as +from ..errors.unprocessable_entity_error import UnprocessableEntityError +from ..types.http_validation_error import HttpValidationError +from json.decoder import JSONDecodeError +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper + + +class AccessControlClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def verify_permissions( + self, + *, + action: typing.Optional[str] = None, + resource_type: typing.Optional[str] = None, + resource_id: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[typing.Any]: + """ + Parameters + ---------- + action : typing.Optional[str] + + resource_type : typing.Optional[str] + + resource_id : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[typing.Any] + Successful Response + + Examples + -------- + from agenta import AgentaApi + + client = AgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + client.access_control.verify_permissions() + """ + _response = self._client_wrapper.httpx_client.request( + "permissions/verify", + method="GET", + params={ + "action": action, + "resource_type": resource_type, + "resource_id": resource_id, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + +class AsyncAccessControlClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def verify_permissions( + self, + *, + action: typing.Optional[str] = None, + resource_type: typing.Optional[str] = None, + resource_id: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[typing.Any]: + """ + Parameters + ---------- + action : typing.Optional[str] + + resource_type : typing.Optional[str] + + resource_id : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[typing.Any] + Successful Response + + Examples + -------- + import asyncio + + from agenta import AsyncAgentaApi + + client = AsyncAgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.access_control.verify_permissions() + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + "permissions/verify", + method="GET", + params={ + "action": action, + "resource_type": resource_type, + "resource_id": resource_id, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) diff --git a/agenta-cli/agenta/client/backend/apps/client.py b/agenta-cli/agenta/client/backend/apps/client.py index b709a8538e..b85b34d6cf 100644 --- a/agenta-cli/agenta/client/backend/apps/client.py +++ b/agenta-cli/agenta/client/backend/apps/client.py @@ -216,7 +216,9 @@ def list_apps( _response = self._client_wrapper.httpx_client.request( "apps", method="GET", - params={"app_name": app_name}, + params={ + "app_name": app_name, + }, request_options=request_options, ) try: @@ -248,13 +250,15 @@ def create_app( *, app_name: str, project_id: typing.Optional[str] = OMIT, + workspace_id: typing.Optional[str] = OMIT, + organization_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> CreateAppOutput: """ - Create a new app for a user. + Create a new app for a user or organization. Args: - payload (CreateApp): The payload containing the app name. + payload (CreateApp): The payload containing the app name and organization ID (optional). stoken_session (SessionContainer): The session container containing the user's session token. Returns: @@ -269,6 +273,10 @@ def create_app( project_id : typing.Optional[str] + workspace_id : typing.Optional[str] + + organization_id : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -295,6 +303,11 @@ def create_app( json={ "app_name": app_name, "project_id": project_id, + "workspace_id": workspace_id, + "organization_id": organization_id, + }, + headers={ + "content-type": "application/json", }, request_options=request_options, omit=OMIT, @@ -393,7 +406,7 @@ def update_app( request_options: typing.Optional[RequestOptions] = None, ) -> UpdateAppOutput: """ - Update an app for a user. + Update an app for a user or organization. Args: app_id (str): The ID of the app. @@ -439,6 +452,9 @@ def update_app( json={ "app_name": app_name, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -538,6 +554,9 @@ def add_variant_from_image( "base_name": base_name, "config_name": config_name, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -572,6 +591,8 @@ def create_app_and_variant_from_template( template_id: str, env_vars: typing.Dict[str, str], project_id: typing.Optional[str] = OMIT, + workspace_id: typing.Optional[str] = OMIT, + organization_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> AppVariantResponse: """ @@ -597,6 +618,10 @@ def create_app_and_variant_from_template( project_id : typing.Optional[str] + workspace_id : typing.Optional[str] + + organization_id : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -626,7 +651,12 @@ def create_app_and_variant_from_template( "app_name": app_name, "template_id": template_id, "project_id": project_id, + "workspace_id": workspace_id, "env_vars": env_vars, + "organization_id": organization_id, + }, + headers={ + "content-type": "application/json", }, request_options=request_options, omit=OMIT, @@ -752,7 +782,7 @@ def environment_revisions( base_url="https://yourhost.com/path/to/api", ) client.apps.environment_revisions( - app_id="string", + app_id="app_id", environment_name={"key": "value"}, ) """ @@ -1005,7 +1035,9 @@ async def main() -> None: _response = await self._client_wrapper.httpx_client.request( "apps", method="GET", - params={"app_name": app_name}, + params={ + "app_name": app_name, + }, request_options=request_options, ) try: @@ -1037,13 +1069,15 @@ async def create_app( *, app_name: str, project_id: typing.Optional[str] = OMIT, + workspace_id: typing.Optional[str] = OMIT, + organization_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> CreateAppOutput: """ - Create a new app for a user. + Create a new app for a user or organization. Args: - payload (CreateApp): The payload containing the app name. + payload (CreateApp): The payload containing the app name and organization ID (optional). stoken_session (SessionContainer): The session container containing the user's session token. Returns: @@ -1058,6 +1092,10 @@ async def create_app( project_id : typing.Optional[str] + workspace_id : typing.Optional[str] + + organization_id : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1092,6 +1130,11 @@ async def main() -> None: json={ "app_name": app_name, "project_id": project_id, + "workspace_id": workspace_id, + "organization_id": organization_id, + }, + headers={ + "content-type": "application/json", }, request_options=request_options, omit=OMIT, @@ -1198,7 +1241,7 @@ async def update_app( request_options: typing.Optional[RequestOptions] = None, ) -> UpdateAppOutput: """ - Update an app for a user. + Update an app for a user or organization. Args: app_id (str): The ID of the app. @@ -1252,6 +1295,9 @@ async def main() -> None: json={ "app_name": app_name, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1359,6 +1405,9 @@ async def main() -> None: "base_name": base_name, "config_name": config_name, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1393,6 +1442,8 @@ async def create_app_and_variant_from_template( template_id: str, env_vars: typing.Dict[str, str], project_id: typing.Optional[str] = OMIT, + workspace_id: typing.Optional[str] = OMIT, + organization_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> AppVariantResponse: """ @@ -1418,6 +1469,10 @@ async def create_app_and_variant_from_template( project_id : typing.Optional[str] + workspace_id : typing.Optional[str] + + organization_id : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1455,7 +1510,12 @@ async def main() -> None: "app_name": app_name, "template_id": template_id, "project_id": project_id, + "workspace_id": workspace_id, "env_vars": env_vars, + "organization_id": organization_id, + }, + headers={ + "content-type": "application/json", }, request_options=request_options, omit=OMIT, @@ -1594,7 +1654,7 @@ async def environment_revisions( async def main() -> None: await client.apps.environment_revisions( - app_id="string", + app_id="app_id", environment_name={"key": "value"}, ) diff --git a/agenta-cli/agenta/client/backend/client.py b/agenta-cli/agenta/client/backend/client.py index 9fc9784d8a..2f022a7f4b 100644 --- a/agenta-cli/agenta/client/backend/client.py +++ b/agenta-cli/agenta/client/backend/client.py @@ -4,6 +4,9 @@ import httpx from .core.client_wrapper import SyncClientWrapper from .observability.client import ObservabilityClient +from .vault.client import VaultClient +from .access_control.client import AccessControlClient +from .scopes.client import ScopesClient from .apps.client import AppsClient from .variants.client import VariantsClient from .evaluations.client import EvaluationsClient @@ -19,9 +22,9 @@ from .core.pydantic_utilities import parse_obj_as from json.decoder import JSONDecodeError from .core.api_error import ApiError +from .core.jsonable_encoder import jsonable_encoder from .errors.unprocessable_entity_error import UnprocessableEntityError from .types.http_validation_error import HttpValidationError -from .core.jsonable_encoder import jsonable_encoder from .types.organization import Organization from .types.organization_output import OrganizationOutput from .types.invite_request import InviteRequest @@ -32,6 +35,9 @@ from .types.permission import Permission from .core.client_wrapper import AsyncClientWrapper from .observability.client import AsyncObservabilityClient +from .vault.client import AsyncVaultClient +from .access_control.client import AsyncAccessControlClient +from .scopes.client import AsyncScopesClient from .apps.client import AsyncAppsClient from .variants.client import AsyncVariantsClient from .evaluations.client import AsyncEvaluationsClient @@ -101,6 +107,9 @@ def __init__( timeout=_defaulted_timeout, ) self.observability = ObservabilityClient(client_wrapper=self._client_wrapper) + self.vault = VaultClient(client_wrapper=self._client_wrapper) + self.access_control = AccessControlClient(client_wrapper=self._client_wrapper) + self.scopes = ScopesClient(client_wrapper=self._client_wrapper) self.apps = AppsClient(client_wrapper=self._client_wrapper) self.variants = VariantsClient(client_wrapper=self._client_wrapper) self.evaluations = EvaluationsClient(client_wrapper=self._client_wrapper) @@ -166,10 +175,7 @@ def list_api_keys( raise ApiError(status_code=_response.status_code, body=_response_json) def create_api_key( - self, - *, - workspace_id: str, - request_options: typing.Optional[RequestOptions] = None, + self, *, request_options: typing.Optional[RequestOptions] = None ) -> str: """ Creates an API key for a user. @@ -182,8 +188,6 @@ def create_api_key( Parameters ---------- - workspace_id : str - request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -200,16 +204,11 @@ def create_api_key( api_key="YOUR_API_KEY", base_url="https://yourhost.com/path/to/api", ) - client.create_api_key( - workspace_id="workspace_id", - ) + client.create_api_key() """ _response = self._client_wrapper.httpx_client.request( "keys", method="POST", - params={ - "workspace_id": workspace_id, - }, request_options=request_options, ) try: @@ -221,16 +220,6 @@ def create_api_key( object_=_response.json(), ), ) - if _response.status_code == 422: - raise UnprocessableEntityError( - typing.cast( - HttpValidationError, - parse_obj_as( - type_=HttpValidationError, # type: ignore - object_=_response.json(), - ), - ) - ) _response_json = _response.json() except JSONDecodeError: raise ApiError(status_code=_response.status_code, body=_response.text) @@ -476,6 +465,9 @@ def create_organization( "description": description, "type": type, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -660,6 +652,9 @@ def update_organization( "description": description, "updated_at": updated_at, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -821,6 +816,9 @@ def resend_invitation( json={ "email": email, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -910,6 +908,9 @@ def accept_invitation( json={ "token": token, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -986,6 +987,9 @@ def create_workspace( "description": description, "type": type, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1065,6 +1069,9 @@ def update_workspace( "description": description, "updated_at": updated_at, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1262,6 +1269,9 @@ def assign_role_to_user( "organization_id": organization_id, "role": role, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1632,6 +1642,11 @@ def __init__( self.observability = AsyncObservabilityClient( client_wrapper=self._client_wrapper ) + self.vault = AsyncVaultClient(client_wrapper=self._client_wrapper) + self.access_control = AsyncAccessControlClient( + client_wrapper=self._client_wrapper + ) + self.scopes = AsyncScopesClient(client_wrapper=self._client_wrapper) self.apps = AsyncAppsClient(client_wrapper=self._client_wrapper) self.variants = AsyncVariantsClient(client_wrapper=self._client_wrapper) self.evaluations = AsyncEvaluationsClient(client_wrapper=self._client_wrapper) @@ -1705,10 +1720,7 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response_json) async def create_api_key( - self, - *, - workspace_id: str, - request_options: typing.Optional[RequestOptions] = None, + self, *, request_options: typing.Optional[RequestOptions] = None ) -> str: """ Creates an API key for a user. @@ -1721,8 +1733,6 @@ async def create_api_key( Parameters ---------- - workspace_id : str - request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1744,9 +1754,7 @@ async def create_api_key( async def main() -> None: - await client.create_api_key( - workspace_id="workspace_id", - ) + await client.create_api_key() asyncio.run(main()) @@ -1754,9 +1762,6 @@ async def main() -> None: _response = await self._client_wrapper.httpx_client.request( "keys", method="POST", - params={ - "workspace_id": workspace_id, - }, request_options=request_options, ) try: @@ -1768,16 +1773,6 @@ async def main() -> None: object_=_response.json(), ), ) - if _response.status_code == 422: - raise UnprocessableEntityError( - typing.cast( - HttpValidationError, - parse_obj_as( - type_=HttpValidationError, # type: ignore - object_=_response.json(), - ), - ) - ) _response_json = _response.json() except JSONDecodeError: raise ApiError(status_code=_response.status_code, body=_response.text) @@ -2055,6 +2050,9 @@ async def main() -> None: "description": description, "type": type, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2263,6 +2261,9 @@ async def main() -> None: "description": description, "updated_at": updated_at, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2440,6 +2441,9 @@ async def main() -> None: json={ "email": email, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2537,6 +2541,9 @@ async def main() -> None: json={ "token": token, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2621,6 +2628,9 @@ async def main() -> None: "description": description, "type": type, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2708,6 +2718,9 @@ async def main() -> None: "description": description, "updated_at": updated_at, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2929,6 +2942,9 @@ async def main() -> None: "organization_id": organization_id, "role": role, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) diff --git a/agenta-cli/agenta/client/backend/configs/client.py b/agenta-cli/agenta/client/backend/configs/client.py index e034ff37b0..ab54844eb9 100644 --- a/agenta-cli/agenta/client/backend/configs/client.py +++ b/agenta-cli/agenta/client/backend/configs/client.py @@ -143,6 +143,9 @@ def save_config( "parameters": parameters, "overwrite": overwrite, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -434,6 +437,9 @@ async def main() -> None: "parameters": parameters, "overwrite": overwrite, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) diff --git a/agenta-cli/agenta/client/backend/containers/client.py b/agenta-cli/agenta/client/backend/containers/client.py index c7180c0a4b..0b62a90e86 100644 --- a/agenta-cli/agenta/client/backend/containers/client.py +++ b/agenta-cli/agenta/client/backend/containers/client.py @@ -156,6 +156,9 @@ def restart_container( json={ "variant_id": variant_id, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -468,6 +471,9 @@ async def main() -> None: json={ "variant_id": variant_id, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) diff --git a/agenta-cli/agenta/client/backend/core/file.py b/agenta-cli/agenta/client/backend/core/file.py index a9623d336a..3467175cb7 100644 --- a/agenta-cli/agenta/client/backend/core/file.py +++ b/agenta-cli/agenta/client/backend/core/file.py @@ -43,23 +43,28 @@ def convert_file_dict_to_httpx_tuples( return httpx_tuples -def with_content_type(*, file: File, content_type: str) -> File: - """ """ +def with_content_type(*, file: File, default_content_type: str) -> File: + """ + This function resolves to the file's content type, if provided, and defaults + to the default_content_type value if not. + """ if isinstance(file, tuple): if len(file) == 2: filename, content = cast(Tuple[Optional[str], FileContent], file) # type: ignore - return (filename, content, content_type) + return (filename, content, default_content_type) elif len(file) == 3: - filename, content, _ = cast( + filename, content, file_content_type = cast( Tuple[Optional[str], FileContent, Optional[str]], file ) # type: ignore - return (filename, content, content_type) + out_content_type = file_content_type or default_content_type + return (filename, content, out_content_type) elif len(file) == 4: - filename, content, _, headers = cast( # type: ignore + filename, content, file_content_type, headers = cast( # type: ignore Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], file, ) - return (filename, content, content_type, headers) + out_content_type = file_content_type or default_content_type + return (filename, content, out_content_type, headers) else: raise ValueError(f"Unexpected tuple length: {len(file)}") - return (None, file, content_type) + return (None, file, default_content_type) diff --git a/agenta-cli/agenta/client/backend/environments/client.py b/agenta-cli/agenta/client/backend/environments/client.py index 1cfbb07010..f9cc94afa7 100644 --- a/agenta-cli/agenta/client/backend/environments/client.py +++ b/agenta-cli/agenta/client/backend/environments/client.py @@ -70,6 +70,9 @@ def deploy_to_environment( "environment_name": environment_name, "variant_id": variant_id, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -162,6 +165,9 @@ async def main() -> None: "environment_name": environment_name, "variant_id": variant_id, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) diff --git a/agenta-cli/agenta/client/backend/evaluations/client.py b/agenta-cli/agenta/client/backend/evaluations/client.py index a8190dd1f2..c8f39d56f2 100644 --- a/agenta-cli/agenta/client/backend/evaluations/client.py +++ b/agenta-cli/agenta/client/backend/evaluations/client.py @@ -34,6 +34,7 @@ def fetch_evaluation_ids( Fetches evaluation ids for a given resource type and id. Arguments: + app_id (str): The ID of the app for which to fetch evaluations. resource_type (str): The type of resource for which to fetch evaluations. resource_ids List[ObjectId]: The IDs of resource for which to fetch evaluations. @@ -250,6 +251,9 @@ def create_evaluation( "lm_providers_keys": lm_providers_keys, "correct_answer_column": correct_answer_column, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -322,6 +326,9 @@ def delete_evaluations( json={ "evaluations_ids": evaluations_ids, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -716,6 +723,7 @@ async def fetch_evaluation_ids( Fetches evaluation ids for a given resource type and id. Arguments: + app_id (str): The ID of the app for which to fetch evaluations. resource_type (str): The type of resource for which to fetch evaluations. resource_ids List[ObjectId]: The IDs of resource for which to fetch evaluations. @@ -727,7 +735,6 @@ async def fetch_evaluation_ids( Parameters ---------- - resource_type : str resource_ids : typing.Optional[typing.Union[str, typing.Sequence[str]]] @@ -957,6 +964,9 @@ async def main() -> None: "lm_providers_keys": lm_providers_keys, "correct_answer_column": correct_answer_column, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1037,6 +1047,9 @@ async def main() -> None: json={ "evaluations_ids": evaluations_ids, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) diff --git a/agenta-cli/agenta/client/backend/evaluators/client.py b/agenta-cli/agenta/client/backend/evaluators/client.py index 29bafb0305..bad5cab677 100644 --- a/agenta-cli/agenta/client/backend/evaluators/client.py +++ b/agenta-cli/agenta/client/backend/evaluators/client.py @@ -122,6 +122,9 @@ def evaluator_data_map( "inputs": inputs, "mapping": mapping, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -209,6 +212,9 @@ def evaluator_run( "settings": settings, "credentials": credentials, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -364,6 +370,9 @@ def create_new_evaluator_config( "evaluator_key": evaluator_key, "settings_values": settings_values, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -511,6 +520,9 @@ def update_evaluator_config( "evaluator_key": evaluator_key, "settings_values": settings_values, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -726,6 +738,9 @@ async def main() -> None: "inputs": inputs, "mapping": mapping, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -821,6 +836,9 @@ async def main() -> None: "settings": settings, "credentials": credentials, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -992,6 +1010,9 @@ async def main() -> None: "evaluator_key": evaluator_key, "settings_values": settings_values, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1155,6 +1176,9 @@ async def main() -> None: "evaluator_key": evaluator_key, "settings_values": settings_values, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) diff --git a/agenta-cli/agenta/client/backend/observability/client.py b/agenta-cli/agenta/client/backend/observability/client.py index aebe134924..dbce982f6b 100644 --- a/agenta-cli/agenta/client/backend/observability/client.py +++ b/agenta-cli/agenta/client/backend/observability/client.py @@ -66,7 +66,7 @@ def dashboard( ) """ _response = self._client_wrapper.httpx_client.request( - "observability/dashboard/", + "observability/dashboard", method="GET", params={ "app_id": app_id, @@ -152,7 +152,7 @@ def create_traces( ) """ _response = self._client_wrapper.httpx_client.request( - "observability/trace/", + "observability/trace", method="POST", json={ "trace": trace, @@ -162,6 +162,9 @@ def create_traces( direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -242,7 +245,7 @@ def get_traces( ) """ _response = self._client_wrapper.httpx_client.request( - "observability/traces/", + "observability/traces", method="GET", params={ "app_id": app_id, @@ -312,7 +315,7 @@ def delete_traces_legacy( ) """ _response = self._client_wrapper.httpx_client.request( - "observability/traces/", + "observability/traces", method="DELETE", json=request, request_options=request_options, @@ -371,7 +374,7 @@ def get_trace_detail( ) """ _response = self._client_wrapper.httpx_client.request( - f"observability/traces/{jsonable_encoder(trace_id)}/", + f"observability/traces/{jsonable_encoder(trace_id)}", method="GET", request_options=request_options, ) @@ -452,7 +455,7 @@ def get_spans_of_generation( ) """ _response = self._client_wrapper.httpx_client.request( - "observability/spans/", + "observability/spans", method="GET", params={ "app_id": app_id, @@ -522,7 +525,7 @@ def delete_spans_of_trace( ) """ _response = self._client_wrapper.httpx_client.request( - "observability/spans/", + "observability/spans", method="DELETE", json=request, request_options=request_options, @@ -587,7 +590,7 @@ def get_span_of_generation( ) """ _response = self._client_wrapper.httpx_client.request( - f"observability/spans/{jsonable_encoder(span_id)}/", + f"observability/spans/{jsonable_encoder(span_id)}", method="GET", params={ "type": type, @@ -672,7 +675,7 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - "observability/dashboard/", + "observability/dashboard", method="GET", params={ "app_id": app_id, @@ -765,7 +768,7 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - "observability/trace/", + "observability/trace", method="POST", json={ "trace": trace, @@ -775,6 +778,9 @@ async def main() -> None: direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -863,7 +869,7 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - "observability/traces/", + "observability/traces", method="GET", params={ "app_id": app_id, @@ -941,7 +947,7 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - "observability/traces/", + "observability/traces", method="DELETE", json=request, request_options=request_options, @@ -1008,7 +1014,7 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - f"observability/traces/{jsonable_encoder(trace_id)}/", + f"observability/traces/{jsonable_encoder(trace_id)}", method="GET", request_options=request_options, ) @@ -1097,7 +1103,7 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - "observability/spans/", + "observability/spans", method="GET", params={ "app_id": app_id, @@ -1175,7 +1181,7 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - "observability/spans/", + "observability/spans", method="DELETE", json=request, request_options=request_options, @@ -1248,7 +1254,7 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - f"observability/spans/{jsonable_encoder(span_id)}/", + f"observability/spans/{jsonable_encoder(span_id)}", method="GET", params={ "type": type, diff --git a/agenta-cli/agenta/client/backend/observability_v_1/__init__.py b/agenta-cli/agenta/client/backend/observability_v_1/__init__.py index aceeca0c75..780ddec649 100644 --- a/agenta-cli/agenta/client/backend/observability_v_1/__init__.py +++ b/agenta-cli/agenta/client/backend/observability_v_1/__init__.py @@ -1,5 +1,5 @@ # This file was auto-generated by Fern from our API Definition. -from .types import Format, QueryTracesResponse +from .types import Format, QueryAnalyticsResponse, QueryTracesResponse -__all__ = ["Format", "QueryTracesResponse"] +__all__ = ["Format", "QueryAnalyticsResponse", "QueryTracesResponse"] diff --git a/agenta-cli/agenta/client/backend/observability_v_1/client.py b/agenta-cli/agenta/client/backend/observability_v_1/client.py index 5fa38f8c3c..ce252ea18b 100644 --- a/agenta-cli/agenta/client/backend/observability_v_1/client.py +++ b/agenta-cli/agenta/client/backend/observability_v_1/client.py @@ -11,6 +11,7 @@ from .types.query_traces_response import QueryTracesResponse from ..errors.unprocessable_entity_error import UnprocessableEntityError from ..types.http_validation_error import HttpValidationError +from .types.query_analytics_response import QueryAnalyticsResponse from ..core.client_wrapper import AsyncClientWrapper @@ -270,6 +271,103 @@ def delete_traces( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + def query_analytics( + self, + *, + format: typing.Optional[Format] = None, + focus: typing.Optional[str] = None, + oldest: typing.Optional[str] = None, + newest: typing.Optional[str] = None, + window: typing.Optional[int] = None, + filtering: typing.Optional[str] = None, + time_range: typing.Optional[str] = None, + app_id: typing.Optional[str] = None, + environment: typing.Optional[str] = None, + variant: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> QueryAnalyticsResponse: + """ + Parameters + ---------- + format : typing.Optional[Format] + + focus : typing.Optional[str] + + oldest : typing.Optional[str] + + newest : typing.Optional[str] + + window : typing.Optional[int] + + filtering : typing.Optional[str] + + time_range : typing.Optional[str] + + app_id : typing.Optional[str] + + environment : typing.Optional[str] + + variant : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + QueryAnalyticsResponse + Successful Response + + Examples + -------- + from agenta import AgentaApi + + client = AgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + client.observability_v_1.query_analytics() + """ + _response = self._client_wrapper.httpx_client.request( + "observability/v1/analytics", + method="GET", + params={ + "format": format, + "focus": focus, + "oldest": oldest, + "newest": newest, + "window": window, + "filtering": filtering, + "timeRange": time_range, + "app_id": app_id, + "environment": environment, + "variant": variant, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + QueryAnalyticsResponse, + parse_obj_as( + type_=QueryAnalyticsResponse, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + class AsyncObservabilityV1Client: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -558,3 +656,108 @@ async def main() -> None: except JSONDecodeError: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + + async def query_analytics( + self, + *, + format: typing.Optional[Format] = None, + focus: typing.Optional[str] = None, + oldest: typing.Optional[str] = None, + newest: typing.Optional[str] = None, + window: typing.Optional[int] = None, + filtering: typing.Optional[str] = None, + time_range: typing.Optional[str] = None, + app_id: typing.Optional[str] = None, + environment: typing.Optional[str] = None, + variant: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> QueryAnalyticsResponse: + """ + Parameters + ---------- + format : typing.Optional[Format] + + focus : typing.Optional[str] + + oldest : typing.Optional[str] + + newest : typing.Optional[str] + + window : typing.Optional[int] + + filtering : typing.Optional[str] + + time_range : typing.Optional[str] + + app_id : typing.Optional[str] + + environment : typing.Optional[str] + + variant : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + QueryAnalyticsResponse + Successful Response + + Examples + -------- + import asyncio + + from agenta import AsyncAgentaApi + + client = AsyncAgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.observability_v_1.query_analytics() + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + "observability/v1/analytics", + method="GET", + params={ + "format": format, + "focus": focus, + "oldest": oldest, + "newest": newest, + "window": window, + "filtering": filtering, + "timeRange": time_range, + "app_id": app_id, + "environment": environment, + "variant": variant, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + QueryAnalyticsResponse, + parse_obj_as( + type_=QueryAnalyticsResponse, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) diff --git a/agenta-cli/agenta/client/backend/observability_v_1/types/__init__.py b/agenta-cli/agenta/client/backend/observability_v_1/types/__init__.py index 7303a90f08..bf6cbe689d 100644 --- a/agenta-cli/agenta/client/backend/observability_v_1/types/__init__.py +++ b/agenta-cli/agenta/client/backend/observability_v_1/types/__init__.py @@ -1,6 +1,7 @@ # This file was auto-generated by Fern from our API Definition. from .format import Format +from .query_analytics_response import QueryAnalyticsResponse from .query_traces_response import QueryTracesResponse -__all__ = ["Format", "QueryTracesResponse"] +__all__ = ["Format", "QueryAnalyticsResponse", "QueryTracesResponse"] diff --git a/agenta-cli/agenta/client/backend/observability_v_1/types/format.py b/agenta-cli/agenta/client/backend/observability_v_1/types/format.py index ed6f7db216..b3b01bea42 100644 --- a/agenta-cli/agenta/client/backend/observability_v_1/types/format.py +++ b/agenta-cli/agenta/client/backend/observability_v_1/types/format.py @@ -2,4 +2,4 @@ import typing -Format = typing.Union[typing.Literal["opentelemetry", "agenta"], typing.Any] +Format = typing.Union[typing.Literal["legacy", "agenta"], typing.Any] diff --git a/agenta-cli/agenta/client/backend/observability_v_1/types/query_analytics_response.py b/agenta-cli/agenta/client/backend/observability_v_1/types/query_analytics_response.py new file mode 100644 index 0000000000..ada2858ab8 --- /dev/null +++ b/agenta-cli/agenta/client/backend/observability_v_1/types/query_analytics_response.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from ...types.legacy_analytics_response import LegacyAnalyticsResponse +from ...types.analytics_response import AnalyticsResponse + +QueryAnalyticsResponse = typing.Union[LegacyAnalyticsResponse, AnalyticsResponse] diff --git a/agenta-cli/agenta/client/backend/scopes/__init__.py b/agenta-cli/agenta/client/backend/scopes/__init__.py new file mode 100644 index 0000000000..67a41b2742 --- /dev/null +++ b/agenta-cli/agenta/client/backend/scopes/__init__.py @@ -0,0 +1 @@ +# This file was auto-generated by Fern from our API Definition. diff --git a/agenta-cli/agenta/client/backend/scopes/client.py b/agenta-cli/agenta/client/backend/scopes/client.py new file mode 100644 index 0000000000..7c437da103 --- /dev/null +++ b/agenta-cli/agenta/client/backend/scopes/client.py @@ -0,0 +1,114 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.client_wrapper import SyncClientWrapper +import typing +from ..core.request_options import RequestOptions +from ..types.projects_response import ProjectsResponse +from ..core.pydantic_utilities import parse_obj_as +from json.decoder import JSONDecodeError +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper + + +class ScopesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get_projects( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.List[ProjectsResponse]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.List[ProjectsResponse] + Successful Response + + Examples + -------- + from agenta import AgentaApi + + client = AgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + client.scopes.get_projects() + """ + _response = self._client_wrapper.httpx_client.request( + "projects", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + typing.List[ProjectsResponse], + parse_obj_as( + type_=typing.List[ProjectsResponse], # type: ignore + object_=_response.json(), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + +class AsyncScopesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get_projects( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.List[ProjectsResponse]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.List[ProjectsResponse] + Successful Response + + Examples + -------- + import asyncio + + from agenta import AsyncAgentaApi + + client = AsyncAgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.scopes.get_projects() + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + "projects", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + typing.List[ProjectsResponse], + parse_obj_as( + type_=typing.List[ProjectsResponse], # type: ignore + object_=_response.json(), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) diff --git a/agenta-cli/agenta/client/backend/testsets/client.py b/agenta-cli/agenta/client/backend/testsets/client.py index fc10205700..091705f1ef 100644 --- a/agenta-cli/agenta/client/backend/testsets/client.py +++ b/agenta-cli/agenta/client/backend/testsets/client.py @@ -10,8 +10,8 @@ from ..types.http_validation_error import HttpValidationError from json.decoder import JSONDecodeError from ..core.api_error import ApiError -from ..core.jsonable_encoder import jsonable_encoder from ..types.test_set_output_response import TestSetOutputResponse +from ..core.jsonable_encoder import jsonable_encoder from ..core.client_wrapper import AsyncClientWrapper # this is used as the default value for optional parameters @@ -106,7 +106,11 @@ def upload_file( raise ApiError(status_code=_response.status_code, body=_response_json) def import_testset( - self, *, request_options: typing.Optional[RequestOptions] = None + self, + *, + endpoint: typing.Optional[str] = OMIT, + testset_name: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, ) -> TestSetSimpleResponse: """ Import JSON testset data from an endpoint and save it to MongoDB. @@ -120,6 +124,10 @@ def import_testset( Parameters ---------- + endpoint : typing.Optional[str] + + testset_name : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -141,7 +149,12 @@ def import_testset( _response = self._client_wrapper.httpx_client.request( "testsets/endpoint", method="POST", + json={ + "endpoint": endpoint, + "testset_name": testset_name, + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: @@ -167,6 +180,59 @@ def import_testset( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + def get_testsets( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.List[TestSetOutputResponse]: + """ + Get all testsets. + + Returns: + + - A list of testset objects. + + Raises: + + - `HTTPException` with status code 404 if no testsets are found. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.List[TestSetOutputResponse] + Successful Response + + Examples + -------- + from agenta import AgentaApi + + client = AgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + client.testsets.get_testsets() + """ + _response = self._client_wrapper.httpx_client.request( + "testsets", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + typing.List[TestSetOutputResponse], + parse_obj_as( + type_=typing.List[TestSetOutputResponse], # type: ignore + object_=_response.json(), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + def create_testset( self, *, @@ -245,31 +311,31 @@ def create_testset( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - def get_single_testset( + def delete_testsets( self, - testset_id: str, *, + testset_ids: typing.Sequence[str], request_options: typing.Optional[RequestOptions] = None, - ) -> typing.Optional[typing.Any]: + ) -> typing.List[str]: """ - Fetch a specific testset in a MongoDB collection using its \_id. + Delete specific testsets based on their unique IDs. Args: - testset_id (str): The \_id of the testset to fetch. + testset_ids (List[str]): The unique identifiers of the testsets to delete. Returns: - The requested testset if found, else an HTTPException. + A list of the deleted testsets' IDs. Parameters ---------- - testset_id : str + testset_ids : typing.Sequence[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.Optional[typing.Any] + typing.List[str] Successful Response Examples @@ -280,21 +346,28 @@ def get_single_testset( api_key="YOUR_API_KEY", base_url="https://yourhost.com/path/to/api", ) - client.testsets.get_single_testset( - testset_id="testset_id", + client.testsets.delete_testsets( + testset_ids=["testset_ids"], ) """ _response = self._client_wrapper.httpx_client.request( - f"testsets/{jsonable_encoder(testset_id)}", - method="GET", + "testsets", + method="DELETE", + json={ + "testset_ids": testset_ids, + }, + headers={ + "content-type": "application/json", + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: return typing.cast( - typing.Optional[typing.Any], + typing.List[str], parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.List[str], # type: ignore object_=_response.json(), ), ) @@ -313,27 +386,27 @@ def get_single_testset( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - def update_testset( + def deprecating_create_testset( self, - testset_id: str, + app_id: str, *, name: str, csvdata: typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]], request_options: typing.Optional[RequestOptions] = None, - ) -> typing.Optional[typing.Any]: + ) -> TestSetSimpleResponse: """ - Update a testset with given id, update the testset in MongoDB. + Create a testset with given name, save the testset to MongoDB. Args: - testset_id (str): id of the test set to be updated. - csvdata (NewTestset): New data to replace the old testset. + name (str): name of the test set. + testset (Dict[str, str]): test set data. Returns: - str: The id of the test set updated. + str: The id of the test set created. Parameters ---------- - testset_id : str + app_id : str name : str @@ -344,7 +417,7 @@ def update_testset( Returns ------- - typing.Optional[typing.Any] + TestSetSimpleResponse Successful Response Examples @@ -355,15 +428,15 @@ def update_testset( api_key="YOUR_API_KEY", base_url="https://yourhost.com/path/to/api", ) - client.testsets.update_testset( - testset_id="testset_id", + client.testsets.deprecating_create_testset( + app_id="app_id", name="name", csvdata=[{"key": "value"}], ) """ _response = self._client_wrapper.httpx_client.request( - f"testsets/{jsonable_encoder(testset_id)}", - method="PUT", + f"testsets/{jsonable_encoder(app_id)}", + method="POST", json={ "name": name, "csvdata": csvdata, @@ -374,9 +447,9 @@ def update_testset( try: if 200 <= _response.status_code < 300: return typing.cast( - typing.Optional[typing.Any], + TestSetSimpleResponse, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=TestSetSimpleResponse, # type: ignore object_=_response.json(), ), ) @@ -395,28 +468,31 @@ def update_testset( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - def get_testsets( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> typing.List[TestSetOutputResponse]: + def get_single_testset( + self, + testset_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[typing.Any]: """ - Get all testsets. - - Returns: - - - A list of testset objects. + Fetch a specific testset in a MongoDB collection using its \_id. - Raises: + Args: + testset_id (str): The \_id of the testset to fetch. - - `HTTPException` with status code 404 if no testsets are found. + Returns: + The requested testset if found, else an HTTPException. Parameters ---------- + testset_id : str + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[TestSetOutputResponse] + typing.Optional[typing.Any] Successful Response Examples @@ -427,19 +503,21 @@ def get_testsets( api_key="YOUR_API_KEY", base_url="https://yourhost.com/path/to/api", ) - client.testsets.get_testsets() + client.testsets.get_single_testset( + testset_id="testset_id", + ) """ _response = self._client_wrapper.httpx_client.request( - "testsets", + f"testsets/{jsonable_encoder(testset_id)}", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: return typing.cast( - typing.List[TestSetOutputResponse], + typing.Optional[typing.Any], parse_obj_as( - type_=typing.List[TestSetOutputResponse], # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -458,31 +536,38 @@ def get_testsets( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - def delete_testsets( + def update_testset( self, + testset_id: str, *, - testset_ids: typing.Sequence[str], + name: str, + csvdata: typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]], request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[str]: + ) -> typing.Optional[typing.Any]: """ - Delete specific testsets based on their unique IDs. + Update a testset with given id, update the testset in MongoDB. Args: - testset_ids (List[str]): The unique identifiers of the testsets to delete. + testset_id (str): id of the test set to be updated. + csvdata (NewTestset): New data to replace the old testset. Returns: - A list of the deleted testsets' IDs. + str: The id of the test set updated. Parameters ---------- - testset_ids : typing.Sequence[str] + testset_id : str + + name : str + + csvdata : typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[str] + typing.Optional[typing.Any] Successful Response Examples @@ -493,15 +578,18 @@ def delete_testsets( api_key="YOUR_API_KEY", base_url="https://yourhost.com/path/to/api", ) - client.testsets.delete_testsets( - testset_ids=["testset_ids"], + client.testsets.update_testset( + testset_id="testset_id", + name="name", + csvdata=[{"key": "value"}], ) """ _response = self._client_wrapper.httpx_client.request( - "testsets", - method="DELETE", + f"testsets/{jsonable_encoder(testset_id)}", + method="PUT", json={ - "testset_ids": testset_ids, + "name": name, + "csvdata": csvdata, }, request_options=request_options, omit=OMIT, @@ -509,9 +597,9 @@ def delete_testsets( try: if 200 <= _response.status_code < 300: return typing.cast( - typing.List[str], + typing.Optional[typing.Any], parse_obj_as( - type_=typing.List[str], # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -627,7 +715,11 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response_json) async def import_testset( - self, *, request_options: typing.Optional[RequestOptions] = None + self, + *, + endpoint: typing.Optional[str] = OMIT, + testset_name: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, ) -> TestSetSimpleResponse: """ Import JSON testset data from an endpoint and save it to MongoDB. @@ -641,6 +733,10 @@ async def import_testset( Parameters ---------- + endpoint : typing.Optional[str] + + testset_name : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -670,7 +766,12 @@ async def main() -> None: _response = await self._client_wrapper.httpx_client.request( "testsets/endpoint", method="POST", + json={ + "endpoint": endpoint, + "testset_name": testset_name, + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: @@ -696,6 +797,67 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + async def get_testsets( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.List[TestSetOutputResponse]: + """ + Get all testsets. + + Returns: + + - A list of testset objects. + + Raises: + + - `HTTPException` with status code 404 if no testsets are found. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.List[TestSetOutputResponse] + Successful Response + + Examples + -------- + import asyncio + + from agenta import AsyncAgentaApi + + client = AsyncAgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.testsets.get_testsets() + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + "testsets", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + typing.List[TestSetOutputResponse], + parse_obj_as( + type_=typing.List[TestSetOutputResponse], # type: ignore + object_=_response.json(), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + async def create_testset( self, *, @@ -782,31 +944,31 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - async def get_single_testset( + async def delete_testsets( self, - testset_id: str, *, + testset_ids: typing.Sequence[str], request_options: typing.Optional[RequestOptions] = None, - ) -> typing.Optional[typing.Any]: + ) -> typing.List[str]: """ - Fetch a specific testset in a MongoDB collection using its \_id. + Delete specific testsets based on their unique IDs. Args: - testset_id (str): The \_id of the testset to fetch. + testset_ids (List[str]): The unique identifiers of the testsets to delete. Returns: - The requested testset if found, else an HTTPException. + A list of the deleted testsets' IDs. Parameters ---------- - testset_id : str + testset_ids : typing.Sequence[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.Optional[typing.Any] + typing.List[str] Successful Response Examples @@ -822,24 +984,31 @@ async def get_single_testset( async def main() -> None: - await client.testsets.get_single_testset( - testset_id="testset_id", + await client.testsets.delete_testsets( + testset_ids=["testset_ids"], ) asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - f"testsets/{jsonable_encoder(testset_id)}", - method="GET", + "testsets", + method="DELETE", + json={ + "testset_ids": testset_ids, + }, + headers={ + "content-type": "application/json", + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: return typing.cast( - typing.Optional[typing.Any], + typing.List[str], parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.List[str], # type: ignore object_=_response.json(), ), ) @@ -858,27 +1027,27 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - async def update_testset( + async def deprecating_create_testset( self, - testset_id: str, + app_id: str, *, name: str, csvdata: typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]], request_options: typing.Optional[RequestOptions] = None, - ) -> typing.Optional[typing.Any]: + ) -> TestSetSimpleResponse: """ - Update a testset with given id, update the testset in MongoDB. + Create a testset with given name, save the testset to MongoDB. Args: - testset_id (str): id of the test set to be updated. - csvdata (NewTestset): New data to replace the old testset. + name (str): name of the test set. + testset (Dict[str, str]): test set data. Returns: - str: The id of the test set updated. + str: The id of the test set created. Parameters ---------- - testset_id : str + app_id : str name : str @@ -889,7 +1058,7 @@ async def update_testset( Returns ------- - typing.Optional[typing.Any] + TestSetSimpleResponse Successful Response Examples @@ -905,8 +1074,8 @@ async def update_testset( async def main() -> None: - await client.testsets.update_testset( - testset_id="testset_id", + await client.testsets.deprecating_create_testset( + app_id="app_id", name="name", csvdata=[{"key": "value"}], ) @@ -915,8 +1084,8 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - f"testsets/{jsonable_encoder(testset_id)}", - method="PUT", + f"testsets/{jsonable_encoder(app_id)}", + method="POST", json={ "name": name, "csvdata": csvdata, @@ -927,9 +1096,9 @@ async def main() -> None: try: if 200 <= _response.status_code < 300: return typing.cast( - typing.Optional[typing.Any], + TestSetSimpleResponse, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=TestSetSimpleResponse, # type: ignore object_=_response.json(), ), ) @@ -948,28 +1117,31 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - async def get_testsets( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> typing.List[TestSetOutputResponse]: + async def get_single_testset( + self, + testset_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[typing.Any]: """ - Get all testsets. - - Returns: - - - A list of testset objects. + Fetch a specific testset in a MongoDB collection using its \_id. - Raises: + Args: + testset_id (str): The \_id of the testset to fetch. - - `HTTPException` with status code 404 if no testsets are found. + Returns: + The requested testset if found, else an HTTPException. Parameters ---------- + testset_id : str + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[TestSetOutputResponse] + typing.Optional[typing.Any] Successful Response Examples @@ -985,22 +1157,24 @@ async def get_testsets( async def main() -> None: - await client.testsets.get_testsets() + await client.testsets.get_single_testset( + testset_id="testset_id", + ) asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - "testsets", + f"testsets/{jsonable_encoder(testset_id)}", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: return typing.cast( - typing.List[TestSetOutputResponse], + typing.Optional[typing.Any], parse_obj_as( - type_=typing.List[TestSetOutputResponse], # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1019,31 +1193,38 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - async def delete_testsets( + async def update_testset( self, + testset_id: str, *, - testset_ids: typing.Sequence[str], + name: str, + csvdata: typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]], request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[str]: + ) -> typing.Optional[typing.Any]: """ - Delete specific testsets based on their unique IDs. + Update a testset with given id, update the testset in MongoDB. Args: - testset_ids (List[str]): The unique identifiers of the testsets to delete. + testset_id (str): id of the test set to be updated. + csvdata (NewTestset): New data to replace the old testset. Returns: - A list of the deleted testsets' IDs. + str: The id of the test set updated. Parameters ---------- - testset_ids : typing.Sequence[str] + testset_id : str + + name : str + + csvdata : typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[str] + typing.Optional[typing.Any] Successful Response Examples @@ -1059,18 +1240,21 @@ async def delete_testsets( async def main() -> None: - await client.testsets.delete_testsets( - testset_ids=["testset_ids"], + await client.testsets.update_testset( + testset_id="testset_id", + name="name", + csvdata=[{"key": "value"}], ) asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - "testsets", - method="DELETE", + f"testsets/{jsonable_encoder(testset_id)}", + method="PUT", json={ - "testset_ids": testset_ids, + "name": name, + "csvdata": csvdata, }, request_options=request_options, omit=OMIT, @@ -1078,9 +1262,9 @@ async def main() -> None: try: if 200 <= _response.status_code < 300: return typing.cast( - typing.List[str], + typing.Optional[typing.Any], parse_obj_as( - type_=typing.List[str], # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) diff --git a/agenta-cli/agenta/client/backend/types/__init__.py b/agenta-cli/agenta/client/backend/types/__init__.py index b10c09c61b..6ef4a84f0a 100644 --- a/agenta-cli/agenta/client/backend/types/__init__.py +++ b/agenta-cli/agenta/client/backend/types/__init__.py @@ -9,11 +9,13 @@ from .agenta_trees_response import AgentaTreesResponse from .aggregated_result import AggregatedResult from .aggregated_result_evaluator_config import AggregatedResultEvaluatorConfig +from .analytics_response import AnalyticsResponse from .app import App from .app_variant_response import AppVariantResponse from .app_variant_revision import AppVariantRevision from .base_output import BaseOutput from .body_import_testset import BodyImportTestset +from .bucket_dto import BucketDto from .collect_status_response import CollectStatusResponse from .config_db import ConfigDb from .config_dto import ConfigDto @@ -41,6 +43,7 @@ from .evaluator_output_interface import EvaluatorOutputInterface from .exception_dto import ExceptionDto from .get_config_response import GetConfigResponse +from .header_dto import HeaderDto from .http_validation_error import HttpValidationError from .human_evaluation import HumanEvaluation from .human_evaluation_scenario import HumanEvaluationScenario @@ -50,12 +53,14 @@ from .human_evaluation_update import HumanEvaluationUpdate from .image import Image from .invite_request import InviteRequest +from .legacy_analytics_response import LegacyAnalyticsResponse +from .legacy_data_point import LegacyDataPoint from .lifecycle_dto import LifecycleDto from .link_dto import LinkDto from .list_api_keys_response import ListApiKeysResponse from .llm_run_rate_limit import LlmRunRateLimit from .llm_tokens import LlmTokens -from .lm_providers_enum import LmProvidersEnum +from .metrics_dto import MetricsDto from .new_human_evaluation import NewHumanEvaluation from .new_testset import NewTestset from .node_dto import NodeDto @@ -73,11 +78,17 @@ from .outputs import Outputs from .parent_dto import ParentDto from .permission import Permission +from .projects_response import ProjectsResponse +from .provider_key_dto import ProviderKeyDto +from .provider_kind import ProviderKind from .reference_dto import ReferenceDto from .reference_request_model import ReferenceRequestModel from .result import Result from .root_dto import RootDto from .score import Score +from .secret_dto import SecretDto +from .secret_kind import SecretKind +from .secret_response_dto import SecretResponseDto from .simple_evaluation_output import SimpleEvaluationOutput from .span import Span from .span_detail import SpanDetail @@ -118,11 +129,13 @@ "AgentaTreesResponse", "AggregatedResult", "AggregatedResultEvaluatorConfig", + "AnalyticsResponse", "App", "AppVariantResponse", "AppVariantRevision", "BaseOutput", "BodyImportTestset", + "BucketDto", "CollectStatusResponse", "ConfigDb", "ConfigDto", @@ -150,6 +163,7 @@ "EvaluatorOutputInterface", "ExceptionDto", "GetConfigResponse", + "HeaderDto", "HttpValidationError", "HumanEvaluation", "HumanEvaluationScenario", @@ -159,12 +173,14 @@ "HumanEvaluationUpdate", "Image", "InviteRequest", + "LegacyAnalyticsResponse", + "LegacyDataPoint", "LifecycleDto", "LinkDto", "ListApiKeysResponse", "LlmRunRateLimit", "LlmTokens", - "LmProvidersEnum", + "MetricsDto", "NewHumanEvaluation", "NewTestset", "NodeDto", @@ -182,11 +198,17 @@ "Outputs", "ParentDto", "Permission", + "ProjectsResponse", + "ProviderKeyDto", + "ProviderKind", "ReferenceDto", "ReferenceRequestModel", "Result", "RootDto", "Score", + "SecretDto", + "SecretKind", + "SecretResponseDto", "SimpleEvaluationOutput", "Span", "SpanDetail", diff --git a/agenta-cli/agenta/client/backend/types/analytics_response.py b/agenta-cli/agenta/client/backend/types/analytics_response.py new file mode 100644 index 0000000000..87195da026 --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/analytics_response.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import typing +from .bucket_dto import BucketDto +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import pydantic + + +class AnalyticsResponse(UniversalBaseModel): + version: str + count: typing.Optional[int] = None + buckets: typing.List[BucketDto] + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/types/app.py b/agenta-cli/agenta/client/backend/types/app.py index 1a16548531..c98d4e9faf 100644 --- a/agenta-cli/agenta/client/backend/types/app.py +++ b/agenta-cli/agenta/client/backend/types/app.py @@ -1,14 +1,15 @@ # This file was auto-generated by Fern from our API Definition. from ..core.pydantic_utilities import UniversalBaseModel -from ..core.pydantic_utilities import IS_PYDANTIC_V2 import typing +from ..core.pydantic_utilities import IS_PYDANTIC_V2 import pydantic class App(UniversalBaseModel): app_id: str app_name: str + app_type: typing.Optional[str] = None updated_at: str if IS_PYDANTIC_V2: diff --git a/agenta-cli/agenta/client/backend/types/body_import_testset.py b/agenta-cli/agenta/client/backend/types/body_import_testset.py index 12f7ad7453..9243142a3e 100644 --- a/agenta-cli/agenta/client/backend/types/body_import_testset.py +++ b/agenta-cli/agenta/client/backend/types/body_import_testset.py @@ -9,7 +9,6 @@ class BodyImportTestset(UniversalBaseModel): endpoint: typing.Optional[str] = None testset_name: typing.Optional[str] = None - app_id: typing.Optional[str] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( diff --git a/agenta-cli/agenta/client/backend/types/bucket_dto.py b/agenta-cli/agenta/client/backend/types/bucket_dto.py new file mode 100644 index 0000000000..fe02e3e5bd --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/bucket_dto.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import datetime as dt +from .metrics_dto import MetricsDto +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import typing +import pydantic + + +class BucketDto(UniversalBaseModel): + timestamp: dt.datetime + window: int + total: MetricsDto + error: MetricsDto + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/types/header_dto.py b/agenta-cli/agenta/client/backend/types/header_dto.py new file mode 100644 index 0000000000..31f6050a21 --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/header_dto.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import typing +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import pydantic + + +class HeaderDto(UniversalBaseModel): + name: typing.Optional[str] = None + description: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/types/legacy_analytics_response.py b/agenta-cli/agenta/client/backend/types/legacy_analytics_response.py new file mode 100644 index 0000000000..8201c64567 --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/legacy_analytics_response.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import typing +from .legacy_data_point import LegacyDataPoint +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import pydantic + + +class LegacyAnalyticsResponse(UniversalBaseModel): + total_count: int + failure_rate: float + total_cost: float + avg_cost: float + avg_latency: float + total_tokens: int + avg_tokens: float + data: typing.List[LegacyDataPoint] + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/types/legacy_data_point.py b/agenta-cli/agenta/client/backend/types/legacy_data_point.py new file mode 100644 index 0000000000..26c987726a --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/legacy_data_point.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import datetime as dt +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import typing +import pydantic + + +class LegacyDataPoint(UniversalBaseModel): + timestamp: dt.datetime + success_count: int + failure_count: int + cost: float + latency: float + total_tokens: int + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/types/lm_providers_enum.py b/agenta-cli/agenta/client/backend/types/lm_providers_enum.py deleted file mode 100644 index 6aa756ba0e..0000000000 --- a/agenta-cli/agenta/client/backend/types/lm_providers_enum.py +++ /dev/null @@ -1,21 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -LmProvidersEnum = typing.Union[ - typing.Literal[ - "OPENAI_API_KEY", - "MISTRAL_API_KEY", - "COHERE_API_KEY", - "ANTHROPIC_API_KEY", - "ANYSCALE_API_KEY", - "PERPLEXITYAI_API_KEY", - "DEEPINFRA_API_KEY", - "TOGETHERAI_API_KEY", - "ALEPHALPHA_API_KEY", - "OPENROUTER_API_KEY", - "GROQ_API_KEY", - "GEMINI_API_KEY", - ], - typing.Any, -] diff --git a/agenta-cli/agenta/client/backend/types/metrics_dto.py b/agenta-cli/agenta/client/backend/types/metrics_dto.py new file mode 100644 index 0000000000..34be8ae995 --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/metrics_dto.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import typing +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import pydantic + + +class MetricsDto(UniversalBaseModel): + count: typing.Optional[int] = None + duration: typing.Optional[float] = None + cost: typing.Optional[float] = None + tokens: typing.Optional[int] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/types/permission.py b/agenta-cli/agenta/client/backend/types/permission.py index a32616dd16..59f87e22b4 100644 --- a/agenta-cli/agenta/client/backend/types/permission.py +++ b/agenta-cli/agenta/client/backend/types/permission.py @@ -13,6 +13,7 @@ "delete_app_variant", "modify_variant_configurations", "delete_application_variant", + "run_service", "view_app_environment_deployment", "edit_app_environment_deployment", "create_app_environment_deployment", diff --git a/agenta-cli/agenta/client/backend/types/projects_response.py b/agenta-cli/agenta/client/backend/types/projects_response.py new file mode 100644 index 0000000000..31fd82d4ac --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/projects_response.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import typing +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import pydantic + + +class ProjectsResponse(UniversalBaseModel): + organization_id: typing.Optional[str] = None + organization_name: typing.Optional[str] = None + workspace_id: typing.Optional[str] = None + workspace_name: typing.Optional[str] = None + project_id: str + project_name: str + user_role: typing.Optional[str] = None + is_demo: typing.Optional[bool] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/types/provider_key_dto.py b/agenta-cli/agenta/client/backend/types/provider_key_dto.py new file mode 100644 index 0000000000..d22e947f2f --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/provider_key_dto.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +from .provider_kind import ProviderKind +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import typing +import pydantic + + +class ProviderKeyDto(UniversalBaseModel): + provider: ProviderKind + key: str + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/types/provider_kind.py b/agenta-cli/agenta/client/backend/types/provider_kind.py new file mode 100644 index 0000000000..d469462030 --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/provider_kind.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ProviderKind = typing.Union[ + typing.Literal[ + "openai", + "cohere", + "anyscale", + "deepinfra", + "alephalpha", + "groq", + "mistralai", + "anthropic", + "perplexityai", + "togetherai", + "openrouter", + "gemini", + ], + typing.Any, +] diff --git a/agenta-cli/agenta/client/backend/types/secret_dto.py b/agenta-cli/agenta/client/backend/types/secret_dto.py new file mode 100644 index 0000000000..a980b9073d --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/secret_dto.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +from .secret_kind import SecretKind +from .provider_key_dto import ProviderKeyDto +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import typing +import pydantic + + +class SecretDto(UniversalBaseModel): + kind: SecretKind = "provider_key" + data: ProviderKeyDto + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/types/secret_kind.py b/agenta-cli/agenta/client/backend/types/secret_kind.py new file mode 100644 index 0000000000..8d44867226 --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/secret_kind.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +SecretKind = typing.Literal["provider_key"] diff --git a/agenta-cli/agenta/client/backend/types/secret_response_dto.py b/agenta-cli/agenta/client/backend/types/secret_response_dto.py new file mode 100644 index 0000000000..1c0a850295 --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/secret_response_dto.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import typing +from .header_dto import HeaderDto +from .secret_dto import SecretDto +from .lifecycle_dto import LifecycleDto +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import pydantic + + +class SecretResponseDto(UniversalBaseModel): + header: typing.Optional[HeaderDto] = None + secret: SecretDto + id: str + lifecycle: typing.Optional[LifecycleDto] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/variants/client.py b/agenta-cli/agenta/client/backend/variants/client.py index 389a77f778..c5fa02fa8e 100644 --- a/agenta-cli/agenta/client/backend/variants/client.py +++ b/agenta-cli/agenta/client/backend/variants/client.py @@ -97,6 +97,9 @@ def add_variant_from_base_and_config( "new_config_name": new_config_name, "parameters": parameters, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -249,6 +252,9 @@ def start_variant( object_=env_vars, annotation=DockerEnvVars, direction="write" ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -399,6 +405,9 @@ def update_variant_parameters( json={ "parameters": parameters, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -757,6 +766,9 @@ def configs_add( direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -839,6 +851,9 @@ def configs_fetch( direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -921,6 +936,9 @@ def configs_fork( direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -989,6 +1007,9 @@ def configs_commit( object_=config, annotation=ConfigDto, direction="write" ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1074,6 +1095,9 @@ def configs_deploy( direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1150,6 +1174,9 @@ def configs_delete( direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1216,6 +1243,9 @@ def configs_list( object_=application_ref, annotation=ReferenceDto, direction="write" ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1292,6 +1322,9 @@ def configs_history( direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1397,6 +1430,9 @@ async def main() -> None: "new_config_name": new_config_name, "parameters": parameters, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1565,6 +1601,9 @@ async def main() -> None: object_=env_vars, annotation=DockerEnvVars, direction="write" ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1731,6 +1770,9 @@ async def main() -> None: json={ "parameters": parameters, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2129,6 +2171,9 @@ async def main() -> None: direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2219,6 +2264,9 @@ async def main() -> None: direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2309,6 +2357,9 @@ async def main() -> None: direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2385,6 +2436,9 @@ async def main() -> None: object_=config, annotation=ConfigDto, direction="write" ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2478,6 +2532,9 @@ async def main() -> None: direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2562,6 +2619,9 @@ async def main() -> None: direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2636,6 +2696,9 @@ async def main() -> None: object_=application_ref, annotation=ReferenceDto, direction="write" ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2720,6 +2783,9 @@ async def main() -> None: direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) diff --git a/agenta-cli/agenta/client/backend/vault/__init__.py b/agenta-cli/agenta/client/backend/vault/__init__.py new file mode 100644 index 0000000000..67a41b2742 --- /dev/null +++ b/agenta-cli/agenta/client/backend/vault/__init__.py @@ -0,0 +1 @@ +# This file was auto-generated by Fern from our API Definition. diff --git a/agenta-cli/agenta/client/backend/vault/client.py b/agenta-cli/agenta/client/backend/vault/client.py new file mode 100644 index 0000000000..0c2a31100e --- /dev/null +++ b/agenta-cli/agenta/client/backend/vault/client.py @@ -0,0 +1,685 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from ..core.client_wrapper import SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.secret_response_dto import SecretResponseDto +from ..core.pydantic_utilities import parse_obj_as +from json.decoder import JSONDecodeError +from ..core.api_error import ApiError +from ..types.secret_dto import SecretDto +from ..types.header_dto import HeaderDto +from ..core.serialization import convert_and_respect_annotation_metadata +from ..errors.unprocessable_entity_error import UnprocessableEntityError +from ..types.http_validation_error import HttpValidationError +from ..core.jsonable_encoder import jsonable_encoder +from ..core.client_wrapper import AsyncClientWrapper + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class VaultClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_secrets( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.List[SecretResponseDto]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.List[SecretResponseDto] + Successful Response + + Examples + -------- + from agenta import AgentaApi + + client = AgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + client.vault.list_secrets() + """ + _response = self._client_wrapper.httpx_client.request( + "vault/v1/secrets", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + typing.List[SecretResponseDto], + parse_obj_as( + type_=typing.List[SecretResponseDto], # type: ignore + object_=_response.json(), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + def create_secret( + self, + *, + secret: SecretDto, + header: typing.Optional[HeaderDto] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> SecretResponseDto: + """ + Parameters + ---------- + secret : SecretDto + + header : typing.Optional[HeaderDto] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SecretResponseDto + Successful Response + + Examples + -------- + from agenta import AgentaApi, ProviderKeyDto, SecretDto + + client = AgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + client.vault.create_secret( + secret=SecretDto( + data=ProviderKeyDto( + provider="openai", + key="key", + ), + ), + ) + """ + _response = self._client_wrapper.httpx_client.request( + "vault/v1/secrets", + method="POST", + json={ + "header": convert_and_respect_annotation_metadata( + object_=header, annotation=HeaderDto, direction="write" + ), + "secret": convert_and_respect_annotation_metadata( + object_=secret, annotation=SecretDto, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + SecretResponseDto, + parse_obj_as( + type_=SecretResponseDto, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + def read_secret( + self, secret_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> SecretResponseDto: + """ + Parameters + ---------- + secret_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SecretResponseDto + Successful Response + + Examples + -------- + from agenta import AgentaApi + + client = AgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + client.vault.read_secret( + secret_id="secret_id", + ) + """ + _response = self._client_wrapper.httpx_client.request( + f"vault/v1/secrets/{jsonable_encoder(secret_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + SecretResponseDto, + parse_obj_as( + type_=SecretResponseDto, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + def update_secret( + self, + secret_id: str, + *, + header: typing.Optional[HeaderDto] = OMIT, + secret: typing.Optional[SecretDto] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> SecretResponseDto: + """ + Parameters + ---------- + secret_id : str + + header : typing.Optional[HeaderDto] + + secret : typing.Optional[SecretDto] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SecretResponseDto + Successful Response + + Examples + -------- + from agenta import AgentaApi + + client = AgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + client.vault.update_secret( + secret_id="secret_id", + ) + """ + _response = self._client_wrapper.httpx_client.request( + f"vault/v1/secrets/{jsonable_encoder(secret_id)}", + method="PUT", + json={ + "header": convert_and_respect_annotation_metadata( + object_=header, annotation=HeaderDto, direction="write" + ), + "secret": convert_and_respect_annotation_metadata( + object_=secret, annotation=SecretDto, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + SecretResponseDto, + parse_obj_as( + type_=SecretResponseDto, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + def delete_secret( + self, secret_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + Parameters + ---------- + secret_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from agenta import AgentaApi + + client = AgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + client.vault.delete_secret( + secret_id="secret_id", + ) + """ + _response = self._client_wrapper.httpx_client.request( + f"vault/v1/secrets/{jsonable_encoder(secret_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + +class AsyncVaultClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_secrets( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.List[SecretResponseDto]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.List[SecretResponseDto] + Successful Response + + Examples + -------- + import asyncio + + from agenta import AsyncAgentaApi + + client = AsyncAgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.vault.list_secrets() + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + "vault/v1/secrets", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + typing.List[SecretResponseDto], + parse_obj_as( + type_=typing.List[SecretResponseDto], # type: ignore + object_=_response.json(), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + async def create_secret( + self, + *, + secret: SecretDto, + header: typing.Optional[HeaderDto] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> SecretResponseDto: + """ + Parameters + ---------- + secret : SecretDto + + header : typing.Optional[HeaderDto] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SecretResponseDto + Successful Response + + Examples + -------- + import asyncio + + from agenta import AsyncAgentaApi, ProviderKeyDto, SecretDto + + client = AsyncAgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.vault.create_secret( + secret=SecretDto( + data=ProviderKeyDto( + provider="openai", + key="key", + ), + ), + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + "vault/v1/secrets", + method="POST", + json={ + "header": convert_and_respect_annotation_metadata( + object_=header, annotation=HeaderDto, direction="write" + ), + "secret": convert_and_respect_annotation_metadata( + object_=secret, annotation=SecretDto, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + SecretResponseDto, + parse_obj_as( + type_=SecretResponseDto, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + async def read_secret( + self, secret_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> SecretResponseDto: + """ + Parameters + ---------- + secret_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SecretResponseDto + Successful Response + + Examples + -------- + import asyncio + + from agenta import AsyncAgentaApi + + client = AsyncAgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.vault.read_secret( + secret_id="secret_id", + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + f"vault/v1/secrets/{jsonable_encoder(secret_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + SecretResponseDto, + parse_obj_as( + type_=SecretResponseDto, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + async def update_secret( + self, + secret_id: str, + *, + header: typing.Optional[HeaderDto] = OMIT, + secret: typing.Optional[SecretDto] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> SecretResponseDto: + """ + Parameters + ---------- + secret_id : str + + header : typing.Optional[HeaderDto] + + secret : typing.Optional[SecretDto] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SecretResponseDto + Successful Response + + Examples + -------- + import asyncio + + from agenta import AsyncAgentaApi + + client = AsyncAgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.vault.update_secret( + secret_id="secret_id", + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + f"vault/v1/secrets/{jsonable_encoder(secret_id)}", + method="PUT", + json={ + "header": convert_and_respect_annotation_metadata( + object_=header, annotation=HeaderDto, direction="write" + ), + "secret": convert_and_respect_annotation_metadata( + object_=secret, annotation=SecretDto, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + SecretResponseDto, + parse_obj_as( + type_=SecretResponseDto, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + async def delete_secret( + self, secret_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + Parameters + ---------- + secret_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from agenta import AsyncAgentaApi + + client = AsyncAgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.vault.delete_secret( + secret_id="secret_id", + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + f"vault/v1/secrets/{jsonable_encoder(secret_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) From c7c33754c38e8178c896ab91be52cba66bf88744 Mon Sep 17 00:00:00 2001 From: Abram Date: Fri, 13 Dec 2024 14:09:58 +0100 Subject: [PATCH 17/18] refactor (tests): extend event_loop fixture in oss to work with cloud --- agenta-backend/agenta_backend/tests/conftest.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/agenta-backend/agenta_backend/tests/conftest.py b/agenta-backend/agenta_backend/tests/conftest.py index 36866f2d45..22f08588aa 100644 --- a/agenta-backend/agenta_backend/tests/conftest.py +++ b/agenta-backend/agenta_backend/tests/conftest.py @@ -1,7 +1,10 @@ import pytest import asyncio -from agenta_backend.tests.engine import test_db_engine as db_engine +from agenta_backend.utils.common import isOss + +if isOss(): + from agenta_backend.tests.engine import test_db_engine as db_engine @pytest.fixture(scope="session", autouse=True) @@ -17,6 +20,8 @@ def event_loop(): yield res - res.run_until_complete(db_engine.remove_db()) # drop database - res.run_until_complete(db_engine.close_db()) # close connections to database + if isOss(): + res.run_until_complete(db_engine.remove_db()) # drop database + res.run_until_complete(db_engine.close_db()) # close connections to database + res._close() # close event loop # type: ignore From efdeb2e813cda10f47415dd9eb3cea636ccfd551 Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Mon, 16 Dec 2024 13:13:38 +0100 Subject: [PATCH 18/18] added a new env var AGENTA_RUNTIME --- agenta-backend/agenta_backend/services/app_manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/agenta-backend/agenta_backend/services/app_manager.py b/agenta-backend/agenta_backend/services/app_manager.py index f88e474b8a..ed7eacc63f 100644 --- a/agenta-backend/agenta_backend/services/app_manager.py +++ b/agenta-backend/agenta_backend/services/app_manager.py @@ -101,6 +101,7 @@ async def start_variant( "AGENTA_BASE_ID": str(db_app_variant.base_id), "AGENTA_APP_ID": str(db_app_variant.app_id), "AGENTA_HOST": domain_name, + "AGENTA_RUNTIME": "true", } ) if isCloudEE():