project run (#3097)

This commit is contained in:
Shuchang Zheng
2025-08-04 00:33:34 -07:00
committed by GitHub
parent d01e4a0cfe
commit fe3d5cec10
7 changed files with 212 additions and 13 deletions

View File

@@ -55,6 +55,7 @@ from skyvern.forge.sdk.db.utils import (
convert_to_organization_auth_token,
convert_to_output_parameter,
convert_to_project,
convert_to_project_file,
convert_to_step,
convert_to_task,
convert_to_workflow,
@@ -99,7 +100,7 @@ from skyvern.forge.sdk.workflow.models.workflow import (
WorkflowRunStatus,
WorkflowStatus,
)
from skyvern.schemas.projects import Project
from skyvern.schemas.projects import Project, ProjectFile
from skyvern.schemas.runs import ProxyLocation, RunEngine, RunType
from skyvern.webeye.actions.actions import Action
from skyvern.webeye.actions.models import AgentStepOutput
@@ -3632,6 +3633,17 @@ class AgentDB:
LOG.error("UnexpectedError", exc_info=True)
raise
async def get_project_revision(self, project_revision_id: str, organization_id: str) -> Project | None:
async with self.Session() as session:
project = (
await session.scalars(
select(ProjectModel)
.filter_by(project_revision_id=project_revision_id)
.filter_by(organization_id=organization_id)
)
).first()
return convert_to_project(project) if project else None
async def create_project_file(
self,
project_revision_id: str,
@@ -3670,3 +3682,14 @@ class AgentDB:
except Exception:
LOG.error("UnexpectedError", exc_info=True)
raise
async def get_project_files(self, project_revision_id: str, organization_id: str) -> list[ProjectFile]:
async with self.Session() as session:
project_files = (
await session.scalars(
select(ProjectFileModel)
.filter_by(project_revision_id=project_revision_id)
.filter_by(organization_id=organization_id)
)
).all()
return [convert_to_project_file(project_file) for project_file in project_files]

View File

@@ -15,6 +15,7 @@ from skyvern.forge.sdk.db.models import (
OrganizationAuthTokenModel,
OrganizationModel,
OutputParameterModel,
ProjectFileModel,
ProjectModel,
StepModel,
TaskModel,
@@ -47,7 +48,7 @@ from skyvern.forge.sdk.workflow.models.workflow import (
WorkflowRunStatus,
WorkflowStatus,
)
from skyvern.schemas.projects import Project
from skyvern.schemas.projects import Project, ProjectFile
from skyvern.schemas.runs import ProxyLocation
from skyvern.webeye.actions.actions import (
Action,
@@ -507,6 +508,25 @@ def convert_to_project(project_model: ProjectModel) -> Project:
)
def convert_to_project_file(project_file_model: ProjectFileModel) -> ProjectFile:
return ProjectFile(
file_id=project_file_model.file_id,
project_revision_id=project_file_model.project_revision_id,
project_id=project_file_model.project_id,
organization_id=project_file_model.organization_id,
file_path=project_file_model.file_path,
file_name=project_file_model.file_name,
file_type=project_file_model.file_type,
content_hash=project_file_model.content_hash,
file_size=project_file_model.file_size,
mime_type=project_file_model.mime_type,
encoding=project_file_model.encoding,
artifact_id=project_file_model.artifact_id,
created_at=project_file_model.created_at,
modified_at=project_file_model.modified_at,
)
def hydrate_action(action_model: ActionModel) -> Action:
"""
Convert ActionModel to the appropriate Action type based on action_type.

View File

@@ -12,7 +12,7 @@ from skyvern.forge.sdk.schemas.task_v2 import TaskV2Status
from skyvern.forge.sdk.schemas.tasks import TaskStatus
from skyvern.forge.sdk.workflow.models.workflow import WorkflowRunStatus
from skyvern.schemas.runs import RunEngine, RunType
from skyvern.services import task_v2_service
from skyvern.services import project_service, task_v2_service
from skyvern.utils.files import initialize_skyvern_state_file
LOG = structlog.get_logger()
@@ -61,6 +61,17 @@ class AsyncExecutor(abc.ABC):
) -> None:
pass
@abc.abstractmethod
async def execute_project(
self,
request: Request | None,
project_id: str,
organization_id: str,
background_tasks: BackgroundTasks | None,
**kwargs: dict,
) -> None:
pass
class BackgroundTaskExecutor(AsyncExecutor):
async def execute_task(
@@ -197,3 +208,19 @@ class BackgroundTaskExecutor(AsyncExecutor):
max_steps_override=max_steps_override,
browser_session_id=browser_session_id,
)
async def execute_project(
self,
request: Request | None,
project_id: str,
organization_id: str,
background_tasks: BackgroundTasks | None,
**kwargs: dict,
) -> None:
if background_tasks:
background_tasks.add_task(
project_service.execute_project,
project_id=project_id,
organization_id=organization_id,
background_tasks=background_tasks,
)

View File

@@ -2,10 +2,11 @@ import base64
import hashlib
import structlog
from fastapi import Depends, HTTPException, Path, Query
from fastapi import BackgroundTasks, Depends, HTTPException, Path, Query, Request
from skyvern.forge import app
from skyvern.forge.sdk.routes.routers import base_router, legacy_base_router
from skyvern.forge.sdk.executor.factory import AsyncExecutorFactory
from skyvern.forge.sdk.routes.routers import base_router
from skyvern.forge.sdk.schemas.organizations import Organization
from skyvern.forge.sdk.services import org_auth_service
from skyvern.schemas.projects import CreateProjectRequest, CreateProjectResponse, DeployProjectRequest, Project
@@ -74,8 +75,6 @@ async def create_project(
raise HTTPException(status_code=500, detail="Failed to create project")
@legacy_base_router.get("/projects/{project_id}")
@legacy_base_router.get("/projects/{project_id}/", include_in_schema=False)
@base_router.get(
"/projects/{project_id}",
response_model=Project,
@@ -117,8 +116,6 @@ async def get_project(
return project
@legacy_base_router.get("/projects")
@legacy_base_router.get("/projects/", include_in_schema=False)
@base_router.get(
"/projects",
response_model=list[Project],
@@ -266,3 +263,33 @@ async def deploy_project(
except Exception as e:
LOG.error("Failed to deploy project", error=str(e), exc_info=True)
raise HTTPException(status_code=500, detail="Failed to deploy project")
@base_router.post(
"/projects/{project_id}/run",
summary="Run project",
description="Run a project",
tags=["Projects"],
)
async def run_project(
request: Request,
background_tasks: BackgroundTasks,
project_id: str = Path(
...,
description="The unique identifier of the project",
examples=["proj_abc123"],
),
current_org: Organization = Depends(org_auth_service.get_current_org),
) -> None:
"""Run a project."""
# await project_service.execute_project(
# project_id=project_id,
# organization_id=current_org.organization_id,
# background_tasks=background_tasks,
# )
await AsyncExecutorFactory.get_executor().execute_project(
request=request,
project_id=project_id,
organization_id=current_org.organization_id,
background_tasks=background_tasks,
)