Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 1 addition & 2 deletions .github/workflows/lbox-develop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.ACTIONS_ACCESS_TOKEN }}
ref: ${{ github.head_ref }}
- uses: ./.github/actions/python-package-shared-setup
with:
Expand Down Expand Up @@ -163,4 +162,4 @@ jobs:
- name: Build and push (Pull Request) Output
if: github.event_name == 'pull_request'
run: |
echo "ghcr.io/labelbox/${{ matrix.package }}:${{ github.sha }}" >> "$GITHUB_STEP_SUMMARY"
echo "ghcr.io/labelbox/${{ matrix.package }}:${{ github.sha }}" >> "$GITHUB_STEP_SUMMARY"
4 changes: 1 addition & 3 deletions .github/workflows/lbox-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,6 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.ACTIONS_ACCESS_TOKEN }}
# ref: ${{ inputs.tag }}
ref: ${{ inputs.tag }}
- uses: ./.github/actions/python-package-shared-setup
with:
Expand Down Expand Up @@ -190,4 +188,4 @@ jobs:
id: image
run: |
echo "ghcr.io/labelbox/${{ matrix.package }}:latest" >> "$GITHUB_STEP_SUMMARY"
echo "ghcr.io/labelbox/${{ matrix.package }}:${{ inputs.tag }}" >> "$GITHUB_STEP_SUMMARY"
echo "ghcr.io/labelbox/${{ matrix.package }}:${{ inputs.tag }}" >> "$GITHUB_STEP_SUMMARY"
5 changes: 5 additions & 0 deletions libs/labelbox/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ dependencies = [
"tqdm>=4.66.2",
"geojson>=3.1.0",
"lbox-clients==1.1.2",
"PyYAML>=6.0"
]
readme = "README.md"
requires-python = ">=3.9,<3.14"
Expand Down Expand Up @@ -55,6 +56,9 @@ data = [
"typing-extensions>=4.10.0",
"opencv-python-headless>=4.9.0.80",
]
alignerr = [
"lbox-alignerr>=0.1.0",
]

[build-system]
requires = ["hatchling"]
Expand All @@ -68,6 +72,7 @@ dev-dependencies = [
"types-python-dateutil>=2.9.0.20240316",
"types-requests>=2.31.0.20240311",
"types-tqdm>=4.66.0.20240106",
"types-PyYAML>=6.0.12.20240311",
]

[tool.ruff]
Expand Down
1 change: 0 additions & 1 deletion libs/labelbox/src/labelbox/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@
ResponseOption,
PromptResponseClassification,
)
from lbox.exceptions import *
from labelbox.schema.taskstatus import TaskStatus
from labelbox.schema.api_key import ApiKey
from labelbox.schema.timeunit import TimeUnit
Expand Down
5 changes: 5 additions & 0 deletions libs/labelbox/src/labelbox/schema/role.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ def format_role(name: str):
class Role(DbObject):
name = Field.String("name")

@classmethod
def from_name(cls, client: "Client", name: str) -> Optional["Role"]:
roles = get_roles(client)
return roles.get(name.upper())


class OrgRole(Role): ...

Expand Down
9 changes: 9 additions & 0 deletions libs/lbox-alignerr/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Alignerr

Alignerr workspace management for Labelbox.

This package provides functionality for managing Alignerr projects, including:
- Project creation and configuration
- Rate management for labelers and reviewers
- Domain and tag management
- Workforce management
73 changes: 73 additions & 0 deletions libs/lbox-alignerr/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
[project]
name = "lbox-alignerr"
version = "0.1.0"
description = "Alignerr workspace management for Labelbox"
authors = [
{ name = "Labelbox", email = "[email protected]" }
]
dependencies = [
"labelbox>=0.1.0",
"pydantic>=2.0.0",
"pyyaml>=6.0",
]
readme = "README.md"
requires-python = ">= 3.9"

classifiers=[
# How mature is this project?
"Development Status :: 2 - Pre-Alpha",
# Indicate who your project is intended for
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
# Pick your license as you wish
"License :: OSI Approved :: Apache Software License",
# Specify the Python versions you support here.
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
keywords = ["ml", "ai", "labelbox", "labeling", "llm", "machinelearning", "alignerr", "lbox-alignerr"]

[project.urls]
Homepage = "https://labelbox.com/"
Documentation = "https://labelbox-python.readthedocs.io/en/latest/"
Repository = "https://github.com/Labelbox/labelbox-python"
Issues = "https://github.com/Labelbox/labelbox-python/issues"
Changelog = "https://github.com/Labelbox/labelbox-python/blob/develop/libs/labelbox/CHANGELOG.md"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.rye]
managed = true
dev-dependencies = [
"pytest>=8.1.1",
"pytest-cases>=3.8.4",
"pytest-rerunfailures>=14.0",
"pytest-snapshot>=0.9.0",
"pytest-cov>=4.1.0",
"pytest-xdist>=3.5.0",
"faker>=25.5.0",
"pytest-timestamper>=0.0.10",
"pytest-timeout>=2.3.1",
"pytest-order>=1.2.1",
"pyjwt>=2.9.0",
]

[tool.rye.scripts]
unit = "pytest tests/unit"
integration = "pytest tests/integration"

[tool.hatch.metadata]
allow-direct-references = true

[tool.hatch.build.targets.wheel]
packages = ["src/alignerr"]

[tool.pytest.ini_options]
addopts = "-rP -vvv --durations=20 --cov=alignerr --import-mode=importlib"
3 changes: 3 additions & 0 deletions libs/lbox-alignerr/src/alignerr/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from alignerr.alignerr_project import AlignerrWorkspace

__all__ = ["AlignerrWorkspace"]
177 changes: 177 additions & 0 deletions libs/lbox-alignerr/src/alignerr/alignerr_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
from enum import Enum
from typing import TYPE_CHECKING, Optional

import logging

from alignerr.schema.project_rate import ProjectRateV2
from alignerr.schema.project_domain import ProjectDomain
from alignerr.schema.enchanced_resource_tags import (
EnhancedResourceTag,
ResourceTagType,
)
from alignerr.schema.project_boost_workforce import (
ProjectBoostWorkforce,
)
from labelbox.pagination import PaginatedCollection

logger = logging.getLogger(__name__)


if TYPE_CHECKING:
from labelbox import Client
from labelbox.schema.project import Project
from alignerr.schema.project_domain import ProjectDomain


class AlignerrRole(Enum):
Labeler = "LABELER"
Reviewer = "REVIEWER"
Admin = "ADMIN"
ProjectCoordinator = "PROJECT_COORDINATOR"
AlignerrLabeler = "ALIGNERR_LABELER"
EndLabellingRole = "ENDLABELLINGROLE"


class AlignerrProject:
def __init__(self, client: "Client", project: "Project", _internal: bool = False):
if not _internal:
raise RuntimeError(
"AlignerrProject cannot be initialized directly. "
"Use AlignerrProjectBuilder or AlignerrProjectFactory to create instances."
)
self.client = client
self.project = project

@property
def project(self) -> "Project":
return self._project

@project.setter
def project(self, project: "Project"):
self._project = project

def domains(self) -> PaginatedCollection:
return ProjectDomain.get_by_project_id(
client=self.client, project_id=self.project.uid
)

def add_domain(self, project_domain: ProjectDomain):
return ProjectDomain.connect_project_to_domains(
client=self.client,
project_id=self.project.uid,
domain_ids=[project_domain.uid],
)

def get_project_rates(self) -> list["ProjectRateV2"]:
return ProjectRateV2.get_by_project_id(
client=self.client, project_id=self.project.uid
)

def set_project_rate(self, project_rate_input):
return ProjectRateV2.set_project_rate(
client=self.client,
project_id=self.project.uid,
project_rate_input=project_rate_input,
)

def set_tags(self, tag_names: list[str], tag_type: ResourceTagType):
# Convert tag names to tag IDs
tag_ids = []
for tag_name in tag_names:
# Search for the tag by text to get its ID
found_tags = EnhancedResourceTag.search_by_text(
self.client, search_text=tag_name, tag_type=tag_type
)
if found_tags:
tag_ids.append(found_tags[0].id)

# Use the existing project resource tag functionality with IDs
self.project.update_project_resource_tags(tag_ids)
return self

def get_tags(self) -> list[EnhancedResourceTag]:
"""Get enhanced resource tags associated with this project.

Returns:
List of EnhancedResourceTag instances
"""
# Get project resource tags and convert to EnhancedResourceTag instances
project_resource_tags = self.project.get_resource_tags()
enhanced_tags = []
for tag in project_resource_tags:
# Search for the corresponding EnhancedResourceTag by text (try different types)
found_tags = []
for tag_type in [ResourceTagType.Default, ResourceTagType.Billing]:
found_tags = EnhancedResourceTag.search_by_text(
self.client, search_text=tag.text, tag_type=tag_type
)
if found_tags:
break
if found_tags:
enhanced_tags.extend(found_tags)
return enhanced_tags

def add_tag(self, tag: EnhancedResourceTag):
"""Add a single enhanced resource tag to the project.

Args:
tag: EnhancedResourceTag instance to add

Returns:
Self for method chaining
"""
current_tags = self.get_tags()
current_tag_names = [t.text for t in current_tags]

if tag.text not in current_tag_names:
current_tag_names.append(tag.text)
self.set_tags(current_tag_names, tag.type)

return self

def remove_tag(self, tag: EnhancedResourceTag):
"""Remove a single enhanced resource tag from the project.

Args:
tag: EnhancedResourceTag instance to remove

Returns:
Self for method chaining
"""
current_tags = self.get_tags()
current_tag_names = [t.text for t in current_tags if t.uid != tag.uid]
self.set_tags(current_tag_names, tag.type)
return self

def get_project_owner(self) -> Optional[ProjectBoostWorkforce]:
"""Get the ProjectBoostWorkforce for this project.

Returns:
ProjectBoostWorkforce instance or None if not found
"""
return ProjectBoostWorkforce.get_by_project_id(
client=self.client, project_id=self.project.uid
)


class AlignerrWorkspace:
def __init__(self, client: "Client"):
self.client = client

def project_builder(self):
from alignerr.alignerr_project_builder import (
AlignerrProjectBuilder,
)

return AlignerrProjectBuilder(self.client)

def project_prototype(self):
from alignerr.alignerr_project_factory import (
AlignerrProjectFactory,
)

return AlignerrProjectFactory(self.client)

@classmethod
def from_labelbox(cls, client: "Client") -> "AlignerrWorkspace":
return cls(client)
Loading
Loading