Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First attempt at a parametrized JobCreate #740

Merged
merged 23 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
97fa336
First attempt at a parametrized JobCreate
javiermtorres Jan 24, 2025
c451eba
Replace templates with pydantic models
javiermtorres Jan 27, 2025
26bbe30
Adapt SDK and SDK tests
javiermtorres Jan 28, 2025
56d37a3
Fix sdk unit tests
javiermtorres Jan 28, 2025
87f9e39
Fix notebook tests
javiermtorres Jan 29, 2025
93c10ec
Fix tests
javiermtorres Jan 29, 2025
33ba0dd
Fix job definition in workflows
javiermtorres Jan 29, 2025
47da6a4
Fix job unit test
javiermtorres Feb 3, 2025
16ed6d7
Start a default workflow for experiments
javiermtorres Feb 10, 2025
ae5ff85
Rebase to main
javiermtorres Feb 10, 2025
d7c37cb
Align with routes in main
javiermtorres Feb 11, 2025
5d89417
Move to experiments new endpoint
javiermtorres Feb 11, 2025
02c0b79
Streamline new experiments api
javiermtorres Feb 11, 2025
af77fd2
Add dataset/samples to experiment, test background tasks
javiermtorres Feb 12, 2025
64a5e2d
Factor out experiment formatting
javiermtorres Feb 12, 2025
dff173a
Add lumigator specific tag
javiermtorres Feb 13, 2025
148ca0a
Add job timings
javiermtorres Feb 14, 2025
5235c4b
Merge branch 'main' into javiermtorres/issue-706-organize-creation-re…
javiermtorres Feb 14, 2025
f33c120
Merge branch 'main' into javiermtorres/issue-706-organize-creation-re…
javiermtorres Feb 14, 2025
3169093
Fix missing default prompt for external APIs
javiermtorres Feb 14, 2025
dea433d
Fix notebook
javiermtorres Feb 14, 2025
a93988e
Merge branch 'main' into javiermtorres/issue-706-organize-creation-re…
javiermtorres Feb 14, 2025
79cc19d
refactor: migrate to the new workflows api (#847)
khaledosman Feb 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions lumigator/backend/backend/api/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import Annotated

import boto3
from fastapi import Depends
from fastapi import BackgroundTasks, Depends
from mypy_boto3_s3.client import S3Client
from ray.job_submission import JobSubmissionClient
from s3fs import S3FileSystem
Expand Down Expand Up @@ -59,11 +59,13 @@ def get_dataset_service(
DatasetServiceDep = Annotated[DatasetService, Depends(get_dataset_service)]


def get_job_service(session: DBSessionDep, dataset_service: DatasetServiceDep) -> JobService:
def get_job_service(
session: DBSessionDep, dataset_service: DatasetServiceDep, background_tasks: BackgroundTasks
) -> JobService:
job_repo = JobRepository(session)
result_repo = JobResultRepository(session)
ray_client = JobSubmissionClient(settings.RAY_DASHBOARD_URL)
return JobService(job_repo, result_repo, ray_client, dataset_service)
return JobService(job_repo, result_repo, ray_client, dataset_service, background_tasks)


JobServiceDep = Annotated[JobService, Depends(get_job_service)]
Expand All @@ -87,9 +89,12 @@ def get_workflow_service(
tracking_client: TrackingClientDep,
job_service: JobServiceDep,
dataset_service: DatasetServiceDep,
background_tasks: BackgroundTasks,
) -> WorkflowService:
job_repo = JobRepository(session)
return WorkflowService(job_repo, job_service, dataset_service, tracking_client=tracking_client)
return WorkflowService(
job_repo, job_service, dataset_service, background_tasks, tracking_client=tracking_client
)


WorkflowServiceDep = Annotated[WorkflowService, Depends(get_workflow_service)]
30 changes: 13 additions & 17 deletions lumigator/backend/backend/api/routes/experiments.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@
from lumigator_schemas.experiments import (
ExperimentCreate,
ExperimentIdCreate,
ExperimentIdResponse,
ExperimentResponse,
GetExperimentResponse,
)
from lumigator_schemas.extras import ListingResponse
from lumigator_schemas.jobs import (
JobEvalCreate,
JobCreate,
JobResponse,
JobResultDownloadResponse,
JobResultResponse,
Expand All @@ -31,13 +29,16 @@ def experiment_exception_mappings() -> dict[type[ServiceError], HTTPStatus]:


@router.post("/", status_code=status.HTTP_201_CREATED)
def create_experiment(service: JobServiceDep, request: ExperimentCreate) -> JobResponse:
return service.create_job(JobEvalCreate.model_validate(request.model_dump()))
def create_experiment(
service: JobServiceDep,
request: ExperimentCreate,
) -> GetExperimentResponse:
return service.create_job(JobCreate.model_validate(request.model_dump()))


@router.get("/{experiment_id}")
def get_experiment(service: JobServiceDep, experiment_id: UUID) -> JobResponse:
return ExperimentResponse.model_validate(service.get_job(experiment_id).model_dump())
return GetExperimentResponse.model_validate(service.get_job(experiment_id).model_dump())


@router.get("/")
Expand All @@ -64,9 +65,7 @@ def get_experiment_result_download(
experiment_id: UUID,
) -> JobResultDownloadResponse:
"""Return experiment results file URL for downloading."""
return JobResultDownloadResponse.model_validate(
service.get_job_result_download(experiment_id).model_dump()
)
return JobResultDownloadResponse.model_validate(service.get_job_result_download(experiment_id).model_dump())


####################################################################################################
Expand All @@ -78,11 +77,10 @@ def get_experiment_result_download(
# but right now it is a placeholder while we build up the Workflows routes
# It's not included in the OpenAPI schema for now so it's not visible in the docs
@router.post("/new", status_code=status.HTTP_201_CREATED, include_in_schema=True)
def create_experiment_id(
service: ExperimentServiceDep, request: ExperimentIdCreate
) -> ExperimentIdResponse:
def create_experiment_id(service: ExperimentServiceDep, request: ExperimentIdCreate) -> GetExperimentResponse:
"""Create an experiment ID."""
return ExperimentIdResponse.model_validate(service.create_experiment(request).model_dump())
# FIXME Shouldn't the model set this
return GetExperimentResponse.model_validate(service.create_experiment(request).model_dump())


# TODO: FIXME this should not need the /all suffix.
Expand All @@ -92,11 +90,9 @@ def list_experiments_new(
service: ExperimentServiceDep,
skip: int = 0,
limit: int = 100,
) -> ListingResponse[ExperimentResponse]:
) -> ListingResponse[GetExperimentResponse]:
"""List all experiments."""
return ListingResponse[ExperimentResponse].model_validate(
service.list_experiments(skip, limit).model_dump()
)
return ListingResponse[GetExperimentResponse].model_validate(service.list_experiments(skip, limit).model_dump())


@router.get("/new/{experiment_id}", include_in_schema=False)
Expand Down
43 changes: 18 additions & 25 deletions lumigator/backend/backend/api/routes/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,21 @@

import loguru
import requests
from fastapi import APIRouter, BackgroundTasks, HTTPException, Query, status
from fastapi import APIRouter, HTTPException, Query, status
from lumigator_schemas.datasets import DatasetResponse
from lumigator_schemas.extras import ListingResponse
from lumigator_schemas.jobs import (
Job,
JobAnnotateCreate,
JobEvalCreate,
JobCreate,
JobEvalLiteCreate,
JobEvaluateCreate,
JobInferenceCreate,
JobLogsResponse,
JobResponse,
JobResultDownloadResponse,
JobResultResponse,
JobType,
)
from ray.job_submission import JobDetails as RayJobDetails
from starlette.requests import Request
Expand All @@ -37,11 +39,9 @@
router = APIRouter()


def job_exception_mappings() -> (
dict[
type[JobNotFoundError] | type[JobTypeUnsupportedError] | type[JobUpstreamError] | type[JobValidationError], int
]
):
def job_exception_mappings() -> dict[
type[JobNotFoundError] | type[JobTypeUnsupportedError] | type[JobUpstreamError] | type[JobValidationError], int
]:
return {
JobNotFoundError: status.HTTP_404_NOT_FOUND,
JobTypeUnsupportedError: status.HTTP_501_NOT_IMPLEMENTED,
Expand All @@ -56,12 +56,9 @@ def create_inference_job(
job_create_request: JobInferenceCreate,
request: Request,
response: Response,
background_tasks: BackgroundTasks,
) -> JobResponse:
job_response = service.create_job(job_create_request)

service.add_background_task(background_tasks, service.handle_inference_job, job_response.id, job_create_request)

url = request.url_for(get_job.__name__, job_id=job_response.id)
response.headers[HttpHeaders.LOCATION] = f"{url}"

Expand All @@ -74,26 +71,22 @@ def create_annotation_job(
job_create_request: JobAnnotateCreate,
request: Request,
response: Response,
background_tasks: BackgroundTasks,
) -> JobResponse:
"""This uses a hardcoded model, that is, Lumigator's opinion on what
reference model should be used to generate annotations.
See more: https://blog.mozilla.ai/lets-build-an-app-for-evaluating-llms/
"""
inference_job_create_request = JobInferenceCreate(
**job_create_request.model_dump(),
model="hf://facebook/bart-large-cnn",
output_field="ground_truth",
)
inference_job_create_request.store_to_dataset = True
job_response = service.create_job(inference_job_create_request)
inference_job_create_config_dict = job_create_request.job_config.dict()
inference_job_create_config_dict["model"] = "hf://facebook/bart-large-cnn"
inference_job_create_config_dict["output_field"] = "ground_truth"
inference_job_create_config_dict["store_to_dataset"] = True
inference_job_create_config_dict["job_type"] = JobType.INFERENCE

service.add_background_task(
background_tasks,
service.handle_inference_job,
job_response.id,
inference_job_create_request,
)
inference_job_create_request_dict = job_create_request.model_dump()
inference_job_create_request_dict["job_config"] = inference_job_create_config_dict

inference_job_create_request = JobCreate(**inference_job_create_request_dict)
job_response = service.create_job(inference_job_create_request)

url = request.url_for(get_job.__name__, job_id=job_response.id)
response.headers[HttpHeaders.LOCATION] = f"{url}"
Expand All @@ -104,7 +97,7 @@ def create_annotation_job(
@router.post("/evaluate/", status_code=status.HTTP_201_CREATED)
def create_evaluation_job(
service: JobServiceDep,
job_create_request: JobEvalCreate,
job_create_request: JobEvaluateCreate,
request: Request,
response: Response,
) -> JobResponse:
Expand Down
6 changes: 3 additions & 3 deletions lumigator/backend/backend/api/routes/workflows.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from http import HTTPStatus

from fastapi import APIRouter, BackgroundTasks, status
from fastapi import APIRouter, status
from lumigator_schemas.jobs import JobLogsResponse
from lumigator_schemas.workflows import (
WorkflowCreateRequest,
Expand All @@ -27,14 +27,14 @@ def workflow_exception_mappings() -> dict[type[ServiceError], HTTPStatus]:

@router.post("/", status_code=status.HTTP_201_CREATED)
async def create_workflow(
service: WorkflowServiceDep, request: WorkflowCreateRequest, background_tasks: BackgroundTasks
service: WorkflowServiceDep, request: WorkflowCreateRequest
) -> WorkflowResponse:
"""A workflow is a single execution for an experiment.
A workflow is a collection of 1 or more jobs.
It must be associated with an experiment id,
which means you must already have created an experiment and have that ID in the request.
"""
return WorkflowResponse.model_validate(service.create_workflow(request, background_tasks))
return WorkflowResponse.model_validate(service.create_workflow(request))


@router.get("/{workflow_id}")
Expand Down
Loading
Loading