HumanSignal / HumanSignal/label-studio-sdk

Objects with Typed Attributes for Tasks, Annotations and Predictions

Open
#12 0 comments 10 reactions 0 assignees View on GitHub
Community
Dominant language
Python
Stars
192
Forks
127
Avg merge
1d 18h
Merged PRs (30d)
1

Description

Hi Label Studio team 👋

We are super happy to see you invest and open source a Label Studio SDK, and plan to adopt it soon. We have been building a Python Label Studio client internally, as probably many other Label Studio users have.

One core feature that we deemed essential was the ability to work with objects with typed attributes as opposed to "raw" dictionaries. In our team we found that having attributes enables developers to work 10x faster by leveraging IDE autocompletion and avoiding bugs. This statement is true in general, but even more relevant for complex, highly nested data structures like the ones any labeling tool has to generate.

Having types and default values adds an extra layer of safety and convenience.

To achieve this in our internal client we used Pydantic (which has the extra benefit of providing data parsing and validation), but the core of the problem can be addressed with native dataclasses or just regular classes. You'll find below a copy of the module (partially) implementing Label Studio 1.0 data structures.

I understand the SDK is still super early stage and I'm sure you already have this in mind for the future, but I thought it was worth mentioning.

```python
from datetime import datetime
from typing import Any, Dict, Generic, List, Optional, TypeVar, Union

from pydantic import BaseModel
from pydantic.generics import GenericModel

# Results

class LabelStudioResult(BaseModel):
type: str
value: dict
to_name: str
from_name: str

class LabelStudioChoiceResultValue(BaseModel):
choices: List[str]

class LabelStudioChoiceResult(LabelStudioResult):
type: str = "choices"
value: LabelStudioChoiceResultValue

class LabelStudioTaxonomyResultValue(BaseModel):
taxonomy: List[List[str]]

class LabelStudioTaxonomyResult(LabelStudioResult):
type: str = "taxonomy"
value: LabelStudioTaxonomyResultValue

# Annotation / Prediction base

class LabelStudioAnnotationPredictionBase(BaseModel):
id: int
created_ago: Optional[str] # not available in projects export
result: List[Union[LabelStudioChoiceResult, LabelStudioTaxonomyResult]]
task: int
created_at: datetime
updated_at: datetime

# Annotation

class LabelStudioAnnotation(LabelStudioAnnotationPredictionBase):
created_username: Optional[str] # not available in projects export
completed_by: int
ground_truth: bool
was_cancelled: bool = False
lead_time: float

# Prediction

class LabelStudioPrediction(LabelStudioAnnotationPredictionBase):
model_version: str
score: float
cluster: Any
neighbors: Any
mislabeling: float

# Task

TaskDataType = TypeVar("TaskDataType")

class LabelStudioTaskBase(GenericModel, Generic[TaskDataType]):
data: TaskDataType
meta: dict = {}
is_labeled: bool = False
overlap: int = 0
project: Optional[int]
file_upload: Optional[int]

class LabelStudioNewTask(LabelStudioTaskBase, Generic[TaskDataType]):
...

class LabelStudioExistingTask(LabelStudioTaskBase, Generic[TaskDataType]):
id: int
created_at: datetime
updated_at: datetime
annotations: Optional[List[LabelStudioAnnotation]] = []
predictions: Optional[List[LabelStudioPrediction]] = []

# Project

class LabelStudioProjectBase(BaseModel):
title: str
description: str
label_config: str
color: str = "#FFFFFF"

class LabelStudioNewProject(LabelStudioProjectBase):
...

class LabelStudioCreatedBy(BaseModel):
id: int
first_name: str
last_name: str
email: str
avatar: Optional[Any]

class LabelStudioLabelAttributes(BaseModel):
value: str
shortcut: Optional[str]

class LabelStudioParsedLabelConfigTask(BaseModel):
type: str
to_name: List[str]
inputs: List[Dict]
labels: List[str]
labels_attrs: Dict[str, LabelStudioLabelAttributes]

class LabelStudioExistingProject(LabelStudioProjectBase):
id: int
expert_instruction: str
show_instruction: bool
show_skip_button: bool
enable_empty_annotation: bool
show_annotation_history: bool
organization: int
maximum_annotations: int
is_published: bool
model_version: str
is_draft: bool
created_by: LabelStudioCreatedBy
created_at: datetime
min_annotations_to_start_training: int
show_collab_predictions: bool
num_tasks_with_annotations: int
task_number: int
useful_annotation_number: int
ground_truth_number: int
skipped_annotations_number: int
total_annotations_number: int
total_predictions_number: int
sampling: str
show_ground_truth_first: bool
show_overlap_first: bool
overlap_cohort_percentage: int
task_data_login: Optional[Any]
task_data_password: Optional[Any]
# control_weights is actually a complex model, but not required for now
control_weights: Optional[Any]
parsed_label_config: Dict[str, LabelStudioParsedLabelConfigTask]

# Project data

class LabelStudioProjectData(GenericModel, Generic[TaskDataType]):
project: LabelStudioExistingProject
tasks: List[LabelStudioExistingTask[TaskDataType]]

class LabelStudioProjectsData(GenericModel, Generic[TaskDataType]):
projects: List[LabelStudioProjectData[TaskDataType]] = []
```

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.