Skip to content

Commit

Permalink
Merge pull request #2272 from Agenta-AI/feat/project-checkpoint-4
Browse files Browse the repository at this point in the history
[Feature]: Project-checkpoint-4
  • Loading branch information
jp-agenta authored Nov 22, 2024
2 parents b8fb3a3 + 0f0ead7 commit 910c0d0
Show file tree
Hide file tree
Showing 24 changed files with 517 additions and 99 deletions.
12 changes: 11 additions & 1 deletion agenta-backend/agenta_backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
configs_router,
health_router,
permissions_router,
projects_router,
)
from agenta_backend.open_api import open_api_tags_metadata
from agenta_backend.utils.common import isEE, isCloudProd, isCloudDev, isOss, isCloudEE
Expand Down Expand Up @@ -97,7 +98,16 @@ async def lifespan(application: FastAPI, cache=True):
app, allow_headers = cloud.extend_main(app)

app.include_router(health_router.router, prefix="/health")
app.include_router(permissions_router.router, prefix="/permissions")
app.include_router(
permissions_router.router,
prefix="/permissions",
tags=["Access Control"],
)
app.include_router(
projects_router.router,
prefix="/projects",
tags=["Scopes"],
)
app.include_router(user_profile.router, prefix="/profile")
app.include_router(app_router.router, prefix="/apps", tags=["Apps"])
app.include_router(variants_router.router, prefix="/variants", tags=["Variants"])
Expand Down
90 changes: 90 additions & 0 deletions agenta-backend/agenta_backend/routers/projects_router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from typing import Optional, List
from uuid import UUID

from pydantic import BaseModel

from fastapi import Request, Query, HTTPException
from fastapi.responses import JSONResponse

from agenta_backend.utils.common import isCloudEE, isOss, APIRouter
from agenta_backend.services import db_manager

if isCloudEE():
from agenta_backend.commons.services import db_manager_ee


class ProjectsResponse(BaseModel):
workspace_id: Optional[UUID] = None
workspace_name: Optional[str] = None
# is_default_workspace: Optional[bool] = None
project_id: UUID
project_name: str
# is_default_project: bool
user_role: Optional[str] = None


router = APIRouter()


@router.get(
"/",
operation_id="get_projects",
response_model=List[ProjectsResponse],
)
async def get_projects(
request: Request,
):
try:
if isOss():
_project = await db_manager.fetch_project_by_id(
project_id=request.state.project_id
)

projects = [
ProjectsResponse(
project_id=_project.id,
project_name=_project.project_name,
)
]

return projects

elif isCloudEE():
_project_memberships = (
await db_manager_ee.fetch_project_memberships_by_user_id(
user_id=request.state.user_id
)
)

if not _project_memberships:
return JSONResponse(
status_code=404,
content={"message": "No projects found."},
)

projects = [
ProjectsResponse(
workspace_id=project_membership.project.workspace.id,
workspace_name=project_membership.project.workspace.name,
project_id=project_membership.project.id,
project_name=project_membership.project.project_name,
user_role=project_membership.role,
)
for project_membership in _project_memberships
]

return projects

else:
return JSONResponse(
status_code=404,
content={"message": "No projects found."},
)

except Exception as exc: # pylint: disable=bare-except
print(exc)

return JSONResponse(
status_code=404,
content={"message": "No projects found."},
)
19 changes: 19 additions & 0 deletions agenta-backend/agenta_backend/services/db_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,25 @@
PARENT_DIRECTORY = Path(os.path.dirname(__file__)).parent


async def fetch_project_by_id(
project_id: str,
) -> ProjectDB:
async with engine.session() as session:
project = (
(
await session.execute(
select(ProjectDB).filter_by(
id=uuid.UUID(project_id),
)
)
)
.scalars()
.first()
)

return project


async def add_testset_to_app_variant(
template_name: str, app_name: str, project_id: str
):
Expand Down
2 changes: 1 addition & 1 deletion agenta-cli/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "agenta"
version = "0.27.3"
version = "0.27.7a0"
description = "The SDK for agenta is an open-source LLMOps platform."
readme = "README.md"
authors = ["Mahmoud Mabrouk <[email protected]>"]
Expand Down
2 changes: 0 additions & 2 deletions agenta-web/cypress/e2e/ab-testing-evaluation.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,6 @@ describe("A/B Testing Evaluation workflow", () => {
cy.wait(1000)
cy.get('[data-cy="abTesting-run-all-button"]').click()

cy.wait("@generateRequest")

cy.get('[data-cy="evaluation-vote-panel-comparison-vote-button"]').eq(0).click()
cy.get('[data-cy="evaluation-vote-panel-comparison-vote-button"]').eq(1).click()
cy.get(
Expand Down
2 changes: 1 addition & 1 deletion agenta-web/cypress/e2e/eval.comparison.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe("Evaluation Comparison Test", function () {

it("Should create 2 new Evaluations", () => {
cy.request({
url: `${Cypress.env().baseApiURL}/evaluations/?app_id=${app_id}`,
url: `${Cypress.env().baseApiURL}/evaluations?app_id=${app_id}`,
method: "GET",
}).then((resp) => {
cy.get('[data-cy="new-evaluation-button"]').click()
Expand Down
2 changes: 0 additions & 2 deletions agenta-web/cypress/e2e/single-model-test-evaluation.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ describe("Single Model Test workflow", () => {

cy.wait(1000)
cy.get('[data-cy="single-model-run-all-button"]').click()

cy.wait("@generateRequest")
cy.get('[data-cy="evaluation-vote-panel-numeric-vote-input"]').type("100")
})

Expand Down
2 changes: 1 addition & 1 deletion agenta-web/cypress/support/commands/evaluations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Cypress.Commands.add("removeLlmProviderKey", () => {

Cypress.Commands.add("createNewEvaluation", (evaluatorName = "Exact Match") => {
cy.request({
url: `${Cypress.env().baseApiURL}/evaluations/?app_id=${app_id}`,
url: `${Cypress.env().baseApiURL}/evaluations?app_id=${app_id}`,
method: "GET",
}).then((resp) => {
cy.get('[data-cy="new-evaluation-button"]').click()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ const EvaluationCompareMode: React.FC<Props> = () => {
}
>
<Link
href={`/apps/${appId}/playground/?variant=${v.variants[0].variantName}`}
href={`/apps/${appId}/playground?variant=${v.variants[0].variantName}`}
>
{variantNameWithRev({
variant_name: v.variants[0].variantName ?? "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ const EvaluationScenarios: React.FC<Props> = () => {
<Space>
<Typography.Text strong>Variant:</Typography.Text>
<Typography.Link
href={`/apps/${appId}/playground/?variant=${evalaution?.variants[0].variantName}`}
href={`/apps/${appId}/playground?variant=${evalaution?.variants[0].variantName}`}
>
{variantNameWithRev({
variant_name: evalaution?.variants[0].variantName ?? "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {useVariant} from "@/lib/hooks/useVariant"
import {isDemo} from "@/lib/helpers/utils"
import {dynamicComponent} from "@/lib/helpers/dynamic"
import VariantPopover from "../variants/VariantPopover"
import {getCurrentProject} from "@/contexts/project.context"
import {useAppsData} from "@/contexts/app.context"

const DeploymentHistoryModal: any = dynamicComponent(
Expand Down Expand Up @@ -130,6 +131,7 @@ const DeploymentDrawer = ({
const [uri, setURI] = useState<string | null>(null)
const [variant, setVariant] = useState<Variant | null>(null)
const [isHistoryModalOpen, setIsHistoryModalOpen] = useState(false)
const {projectId} = getCurrentProject()

useEffect(() => {
loadURL(selectedEnvironment)
Expand All @@ -147,7 +149,7 @@ const DeploymentDrawer = ({
const loadURL = async (environment: Environment) => {
if (environment.deployed_app_variant_id) {
const url = await fetchAppContainerURL(appId, environment.deployed_app_variant_id)
setURI(`${url}/generate_deployed`)
setURI(`${url}/generate_deployed?project_id=${projectId}`)
}
}

Expand Down
6 changes: 4 additions & 2 deletions agenta-web/src/contexts/app.context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {dynamicContext} from "@/lib/helpers/dynamic"
import {HookAPI} from "antd/es/modal/useModal"
import {useLocalStorage} from "usehooks-ts"
import {useProfileData} from "./profile.context"
import {useProjectData} from "./project.context"

type AppContextType = {
currentApp: ListAppsItem | null
Expand All @@ -32,6 +33,7 @@ const initialValues: AppContextType = {

const useApps = () => {
const [useOrgData, setUseOrgData] = useState<Function>(() => () => "")
const {projectId} = useProjectData()
const {user} = useProfileData()

useEffect(() => {
Expand All @@ -43,9 +45,9 @@ const useApps = () => {
const {selectedOrg, loading} = useOrgData()
const {data, error, isLoading, mutate} = useSWR(
!!user
? `${getAgentaApiUrl()}/api/apps/` +
? `${getAgentaApiUrl()}/api/apps?project_id=${projectId}` +
(isDemo()
? `?org_id=${selectedOrg?.id}&workspace_id=${selectedOrg?.default_workspace.id}`
? `&org_id=${selectedOrg?.id}&workspace_id=${selectedOrg?.default_workspace.id}`
: "")
: null,
!!user ? (isDemo() ? (selectedOrg?.id ? axiosFetcher : () => {}) : axiosFetcher) : null,
Expand Down
111 changes: 111 additions & 0 deletions agenta-web/src/contexts/project.context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import {useSession} from "@/hooks/useSession"
import {PropsWithChildren, createContext, useState, useContext, useEffect, useCallback} from "react"
import {fetchAllProjects} from "@/services/project"
import useStateCallback from "@/hooks/useStateCallback"
import {dynamicContext} from "@/lib/helpers/dynamic"
import {isDemo} from "@/lib/helpers/utils"

const DEFAULT_UUID = "00000000-0000-0000-0000-000000000000"

type Project = {
workspace_id: string | null
workspace_name: string | null
project_id: string | null
project_name: string | null
user_role: string | null
}

type ProjectContextType = {
project: Project | null
isProjectId: boolean
projectId: string
isLoading: boolean
reset: () => void
refetch: (onSuccess?: () => void) => void
}

const initialValues: ProjectContextType = {
project: null,
isProjectId: false,
projectId: "",
isLoading: false,
reset: () => {},
refetch: () => {},
}

export const ProjectContext = createContext<ProjectContextType>(initialValues)

export const useProjectData = () => useContext(ProjectContext)

const projectContextValues = {...initialValues}

export const getCurrentProject = () => projectContextValues

const ProjectContextProvider: React.FC<PropsWithChildren> = ({children}) => {
const [project, setProject] = useStateCallback<Project | null>(null)
const [useOrgData, setUseOrgData] = useState<Function>(() => () => "")
const [isLoading, setIsLoading] = useState(false)
const {doesSessionExist} = useSession()

useEffect(() => {
dynamicContext("org.context", {useOrgData}).then((context) => {
setUseOrgData(() => context.useOrgData)
})
}, [])

const {selectedOrg} = useOrgData()

const workspaceId: string = selectedOrg?.default_workspace.id || DEFAULT_UUID

const isProjectId = !isLoading && Boolean(project?.project_id)
const projectId = (project?.project_id as string) || DEFAULT_UUID

const fetcher = async (onSuccess?: () => void) => {
setIsLoading(true)
try {
const data = await fetchAllProjects()

const _project = isDemo()
? data.find((p: {workspace_id: string}) => p.workspace_id === workspaceId)
: data[0]

setProject(_project, onSuccess)
} catch (error) {
console.error(error)
} finally {
setIsLoading(false)
}
}

useEffect(() => {
if (doesSessionExist) {
fetcher()
}
}, [doesSessionExist, selectedOrg])

const reset = () => {
setProject(initialValues.project)
}

projectContextValues.project = project
projectContextValues.isLoading = isLoading
projectContextValues.isProjectId = isProjectId
projectContextValues.projectId = projectId

return (
<ProjectContext.Provider
value={{
project,
isProjectId,
projectId,
isLoading,
reset,
refetch: fetcher,
}}
>
{children}
</ProjectContext.Provider>
)
}

export default ProjectContextProvider
13 changes: 8 additions & 5 deletions agenta-web/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Layout from "@/components/Layout/Layout"
import ThemeContextProvider from "@/components/Layout/ThemeContextProvider"
import AppContextProvider from "@/contexts/app.context"
import ProfileContextProvider from "@/contexts/profile.context"
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"
Expand Down Expand Up @@ -55,11 +56,13 @@ export default function App({Component, pageProps}: AppProps) {
<PostHogProvider client={posthog}>
<ThemeContextProvider>
<ProfileContextProvider>
<AppContextProvider>
<Layout>
<Component {...pageProps} />
</Layout>
</AppContextProvider>
<ProjectContextProvider>
<AppContextProvider>
<Layout>
<Component {...pageProps} />
</Layout>
</AppContextProvider>
</ProjectContextProvider>
</ProfileContextProvider>
</ThemeContextProvider>
</PostHogProvider>
Expand Down
Loading

0 comments on commit 910c0d0

Please sign in to comment.