Add submit_external_metrics by kozikkamil · Pull Request #2042 · Labelbox/labelbox-python

project = client.get_project(PROJECT_ID)

# Build entries using the new schema# Build entries using the new schema
entries = [
    # Entry 1: full label + review with rating, done queue
    lb.ProjectSyncEntry(
        task_id="test-task-031",
        content_url="https://storage.example.com/data/test-task-031.json",
        label=lb.ProjectSyncLabel(
            submitted_by=lb.SubmittedBy(email="user@email.com"),
            auto_qa=lb.AutoQA(
                status=lb.AutoQaStatus.Approve,
                score=0.92,
                feedback="Looks good",
                custom_scores=[
                    lb.CustomScore(name="accuracy", value=0.95),
                    lb.CustomScore(name="relevance", value=0.88),
                ],
            ),
            seconds_to_completion=45.0,
            submitted_on="2025-06-15T12:00:00.000Z",
        ),
        review=lb.ProjectSyncReview(
            reviewed_by=lb.ReviewedBy(email="user@email.com"),
            rating=lb.GranularRating(score=5, comment="Excellent work"),
        ),
        queue_type="DONE_QUEUE",
    ),
    # Entry 2: label + review without rating, done queue
    lb.ProjectSyncEntry(
        task_id="test-task-032",
        label=lb.ProjectSyncLabel(
            submitted_by=lb.SubmittedBy(email="user@email.com"),
            seconds_to_completion=30.0,
        ),
        review=lb.ProjectSyncReview(
            reviewed_by=lb.ReviewedBy(email="user@email.com"),
        ),
        queue_type="DONE_QUEUE",
    ),
    # Entry 3: label with rejected autoQA, no review, no content_url
    lb.ProjectSyncEntry(
        task_id="test-task-033",
        label=lb.ProjectSyncLabel(
            submitted_by=lb.SubmittedBy(email="user@email.com"),
            auto_qa=lb.AutoQA(
                status=lb.AutoQaStatus.Reject,
                score=0.3,
                feedback="Multiple errors found",
            ),
            seconds_to_completion=120.5,
        ),
    ),
]

print(f"Syncing {len(entries)} entries...")
result = project.sync_external_project(entries)
print(f"  submissionId={result.submission_id}")
print("Done!")

Please delete options that are not relevant.

Note

Medium Risk
Adds a new GraphQL mutation path and input serialization for project data sync; risk is mainly schema mismatch/serialization edge cases (e.g., optional vs explicit null) causing failed or incorrect syncs.

Overview
Adds a new Project.sync_external_project() method that calls a syncExternalProject GraphQL mutation to asynchronously push external label/review metadata (including AutoQA status/scores and queue state) into a project and returns a submission_id for tracking.

Introduces a new project_sync pydantic schema (ProjectSyncEntry, ProjectSyncLabel/Review, AutoQA, etc.) plus a _to_gql_input serializer (including explicit null support), and re-exports these types from labelbox.__init__ for SDK users.

Written by Cursor Bugbot for commit f0b166d. This will update automatically on new commits. Configure here.