Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
192 commits
Select commit Hold shift + click to select a range
3bfe09a
feat: FIT-587: FSM Architecture setup
bmartel Aug 25, 2025
0260e65
include organization on base state model
bmartel Aug 25, 2025
f359607
refactoring core definitions
bmartel Aug 26, 2025
a475d63
define core entity states in full
bmartel Aug 26, 2025
0c31d1f
regen migrations for fsm core
bmartel Aug 26, 2025
15f5872
regen migrations for fsm core
bmartel Aug 26, 2025
5bc0cae
regen migrations for fsm core
bmartel Aug 26, 2025
1c1ced9
fixed imports
bmartel Aug 26, 2025
2e69735
Merge remote-tracking branch 'origin/develop' into fb-fit-587-fsm
bmartel Aug 26, 2025
6c8d40e
fix imports
bmartel Aug 26, 2025
71bc9a1
fix imports
bmartel Aug 27, 2025
f99302a
fix fsm tests
bmartel Aug 27, 2025
68bce45
fix fsm tests
bmartel Aug 27, 2025
d07ea13
fix linting errors
bmartel Aug 27, 2025
46e1597
fix fsm concurrency tests
bmartel Aug 27, 2025
21f6558
fix remaining fsm tests
bmartel Aug 27, 2025
8b1bf57
Merge remote-tracking branch 'origin/develop' into fb-fit-587-fsm
bmartel Aug 27, 2025
7fa40b2
removing implementation details of fsm to break up the PR
bmartel Aug 27, 2025
d134a1e
removing implementation details of fsm to break up the PR
bmartel Aug 27, 2025
a453ab6
removing implementation details of fsm to break up the PR
bmartel Aug 27, 2025
6dc9933
updating doc strings
bmartel Aug 27, 2025
6f73a74
fixing registry
bmartel Aug 27, 2025
3869f79
fixing registry
bmartel Aug 27, 2025
0c37517
fixing tests
bmartel Aug 27, 2025
c23c13f
updating README
bmartel Aug 27, 2025
1fee6de
Apply suggestion from @bmartel
bmartel Aug 27, 2025
dec91f9
avoiding import cycle due to types
bmartel Aug 27, 2025
129e6b1
feat: FIT-587: FSM core model setup
bmartel Aug 27, 2025
b80f523
fix imports
bmartel Aug 27, 2025
0d1a901
Merge branch 'fb-fit-587-fsm' into fb-fit-587-fsm-models
bmartel Aug 27, 2025
9b5cd8d
fix imports
bmartel Aug 27, 2025
975b178
Merge branch 'fb-fit-587-fsm' into fb-fit-587-fsm-models
bmartel Aug 27, 2025
08f9908
fix typing and decorator definition for state model
bmartel Aug 27, 2025
343accf
Merge branch 'fb-fit-587-fsm' into fb-fit-587-fsm-models
bmartel Aug 27, 2025
56f0014
adding migrations
bmartel Aug 27, 2025
4a87c01
Merge remote-tracking branch 'origin/develop' into fb-fit-587-fsm
bmartel Aug 27, 2025
bf13a5d
Merge branch 'fb-fit-587-fsm' into fb-fit-587-fsm-models
bmartel Aug 27, 2025
5dc5d80
removing unused code
bmartel Aug 27, 2025
73b8e4e
moving settings from extensions to settings file
bmartel Aug 27, 2025
dd539fd
Merge branch 'fb-fit-587-fsm' into fb-fit-587-fsm-models
bmartel Aug 27, 2025
dd9dcfa
moving the API integration FSM out of this PR
bmartel Aug 28, 2025
0650ce0
Merge branch 'fb-fit-587-fsm' into fb-fit-587-fsm-models
bmartel Aug 28, 2025
35adf8d
updating readme
bmartel Aug 28, 2025
f35b937
Merge branch 'fb-fit-587-fsm' into fb-fit-587-fsm-models
bmartel Aug 28, 2025
afc800f
removing unused code
bmartel Aug 28, 2025
a5f3fd3
Merge branch 'fb-fit-587-fsm' into fb-fit-587-fsm-models
bmartel Aug 28, 2025
ab62c9e
renaming transition decorator for consistency
bmartel Aug 28, 2025
6a9ae4c
Merge branch 'fb-fit-587-fsm' into fb-fit-587-fsm-models
bmartel Aug 28, 2025
3310d7f
fixing tests to align with refactors and renames of transition functions
bmartel Aug 28, 2025
437fdc9
Merge remote-tracking branch 'origin/develop' into fb-fit-587-fsm
bmartel Aug 28, 2025
cd0194a
Merge branch 'fb-fit-587-fsm' into fb-fit-587-fsm-models
bmartel Aug 28, 2025
c46062a
removing unused code
bmartel Aug 28, 2025
0e0d6fe
Merge branch 'fb-fit-587-fsm' into fb-fit-587-fsm-models
bmartel Aug 28, 2025
3f7c288
removing serializers from this PR, will be included in a different one
bmartel Aug 28, 2025
1c5bb42
Merge remote-tracking branch 'origin/develop' into fb-fit-587-fsm
bmartel Aug 28, 2025
9329e7b
Merge branch 'fb-fit-587-fsm' into fb-fit-587-fsm-models
bmartel Aug 28, 2025
72863bc
Merge remote-tracking branch 'origin/develop' into fb-fit-587-fsm
bmartel Sep 2, 2025
c11a684
Merge branch 'fb-fit-587-fsm' into fb-fit-610-fsm-models
bmartel Sep 2, 2025
9c7b742
Update label_studio/fsm/state_manager.py
bmartel Sep 3, 2025
d9376e6
Merge remote-tracking branch 'origin/develop' into fb-fit-587-fsm
bmartel Sep 3, 2025
43ecd7b
updating based on feedback about test assertions/file names
bmartel Sep 3, 2025
2de2dec
updating based on feedback about test file names
bmartel Sep 3, 2025
d0ea3be
consolidate transition util get_valid_states to being the same functi…
bmartel Sep 3, 2025
458e407
improving transition handling
bmartel Sep 3, 2025
1e17716
improving transition handling by consolidating everything to use a si…
bmartel Sep 3, 2025
7552fbb
ensure write through cache sets on_commit of the transaction
bmartel Sep 3, 2025
add664d
Merge remote-tracking branch 'origin/develop' into fb-fit-587-fsm
bmartel Sep 3, 2025
f73583d
type fixes
bmartel Sep 3, 2025
89ddaf5
Merge branch 'fb-fit-587-fsm' into fb-fit-610-fsm-models
bmartel Sep 3, 2025
493f136
update tests to ensure coverage
bmartel Sep 3, 2025
9caa780
fixing lint errors
bmartel Sep 3, 2025
4170f56
Sync Follow Merge dependencies
bmartel Sep 3, 2025
afafec1
Merge branch 'develop' into 'fb-fit-587-fsm'
bmartel Sep 3, 2025
7990f3e
reverting poetry.lock to develop to regenerate it on CI
bmartel Sep 3, 2025
beb30a6
Merge branch 'fb-fit-587-fsm' of github.com:HumanSignal/label-studio …
bmartel Sep 3, 2025
81bc609
Sync Follow Merge dependencies
bmartel Sep 3, 2025
e70416c
Merge branch 'fb-fit-587-fsm' into fb-fit-610-fsm-models
bmartel Sep 3, 2025
e207db8
updating docs
bmartel Sep 4, 2025
b146cc5
Merge branch 'fb-fit-587-fsm' into fb-fit-610-fsm-models
bmartel Sep 4, 2025
bf969de
removing unused code
bmartel Sep 4, 2025
e9d145b
applying feedback from CR
bmartel Sep 4, 2025
833c5cf
Merge branch 'fb-fit-587-fsm' into fb-fit-610-fsm-models
bmartel Sep 4, 2025
89cbd6e
fixing tests
bmartel Sep 4, 2025
c8be29c
Merge remote-tracking branch 'origin/develop' into fb-fit-587-fsm
bmartel Sep 4, 2025
5514c8d
Merge branch 'fb-fit-587-fsm' into fb-fit-610-fsm-models
bmartel Sep 4, 2025
3319e90
structure the logging so it can be made useful for metrics in grafana
bmartel Sep 4, 2025
0d81600
Merge branch 'fb-fit-587-fsm' into fb-fit-610-fsm-models
bmartel Sep 4, 2025
4d42dcb
fix tests
bmartel Sep 4, 2025
d251c61
fix tests
bmartel Sep 4, 2025
9120a18
fix implementation
bmartel Sep 4, 2025
9d3fb7f
fix implementation
bmartel Sep 4, 2025
8cdefcf
Merge branch 'develop' into fb-fit-587-fsm
bmartel Sep 5, 2025
e9dfe89
Sync Follow Merge dependencies
bmartel Sep 5, 2025
8748dbd
Merge branch 'develop' into 'fb-fit-587-fsm'
bmartel Sep 5, 2025
76ab102
Merge branch 'fb-fit-587-fsm' into fb-fit-610-fsm-models
bmartel Sep 5, 2025
e34002c
fix initial migration
bmartel Sep 5, 2025
6f21e6b
Sync Follow Merge dependencies
bmartel Sep 5, 2025
3b86726
Merge branch 'develop' into 'fb-fit-587-fsm'
bmartel Sep 5, 2025
b4a1e91
Merge branch 'fb-fit-587-fsm' into fb-fit-610-fsm-models
bmartel Sep 5, 2025
db1ed5f
refactor code to remove function imports
bmartel Sep 5, 2025
69bee71
refactor code to remove redundant code
bmartel Sep 5, 2025
d03f268
Merge branch 'fb-fit-587-fsm' into fb-fit-610-fsm-models
bmartel Sep 5, 2025
9e6ade7
remove no longer required test
bmartel Sep 5, 2025
25e655e
updating docs
bmartel Sep 5, 2025
c804f60
Merge branch 'fb-fit-587-fsm' into fb-fit-610-fsm-models
bmartel Sep 5, 2025
9c6ee34
fixing lint errors
bmartel Sep 5, 2025
0dcbcde
fixing lint errors
bmartel Sep 5, 2025
778a6f3
Merge branch 'fb-fit-587-fsm' into fb-fit-610-fsm-models
bmartel Sep 5, 2025
61e04a8
Merge remote-tracking branch 'origin/develop' into fb-fit-610-fsm-models
bmartel Sep 5, 2025
0f48c67
Sync Follow Merge dependencies
bmartel Sep 8, 2025
0d0dfe9
Merge branch 'develop' into 'fb-fit-610-fsm-models'
bmartel Sep 8, 2025
f0756a0
fixing tests for windows SQLITE
bmartel Sep 8, 2025
e53ac3b
Merge branch 'fb-fit-610-fsm-models' of github.com:HumanSignal/label-…
bmartel Sep 8, 2025
20b0c06
Update label_studio/fsm/models.py
bmartel Sep 8, 2025
5559707
Update label_studio/fsm/models.py
bmartel Sep 8, 2025
61a89d4
applying feedback to remove the additional method get_current_state a…
bmartel Sep 8, 2025
bc6e871
removing redundant db_index=True on task fk
bmartel Sep 8, 2025
bf73eb3
using factories as mentioned in PR feedback
bmartel Sep 8, 2025
ae401d0
feat: FIT-671: Tracking entity state change with transitions
bmartel Sep 10, 2025
8e7b0a8
feature flagging changes
bmartel Sep 11, 2025
e1ef236
Merge remote-tracking branch 'origin/develop' into fb-fit-671
bmartel Sep 11, 2025
cef9d12
ensure integrations are behind FF
bmartel Sep 11, 2025
d3a6472
test fixes
bmartel Sep 11, 2025
5502da8
improve coverage
bmartel Sep 11, 2025
77cc53f
fix init of integrations to only include the correct version of integ…
bmartel Sep 11, 2025
07b8a3e
Sync Follow Merge dependencies
bmartel Sep 11, 2025
33cceb9
Merge branch 'develop' into 'fb-fit-671'
bmartel Sep 11, 2025
a4a719e
fixing deletion counts to not include cascade records
bmartel Sep 12, 2025
974ee14
Merge branch 'fb-fit-671' of github.com:HumanSignal/label-studio into…
bmartel Sep 12, 2025
c17e9d1
Sync Follow Merge dependencies
bmartel Sep 12, 2025
0ed60f2
Merge branch 'fb-fit-671' of github.com:HumanSignal/label-studio into…
bmartel Sep 12, 2025
24965d7
removing md file
bmartel Sep 12, 2025
30cd1ce
Sync Follow Merge dependencies
bmartel Sep 12, 2025
05908e4
Merge branch 'develop' into 'fb-fit-671'
bmartel Sep 12, 2025
ead3845
updating logging to ensure it is following the same structure everywhere
bmartel Sep 12, 2025
082baf4
Merge branch 'fb-fit-671' of github.com:HumanSignal/label-studio into…
bmartel Sep 12, 2025
411ccee
Merge remote-tracking branch 'origin/develop' into fb-fit-671
bmartel Sep 15, 2025
3be397e
cleanup move to native python asserts for tests
bmartel Sep 16, 2025
a001770
Merge remote-tracking branch 'origin/develop' into fb-fit-671
bmartel Sep 16, 2025
e56ea45
Merge remote-tracking branch 'origin/develop' into fb-fit-671
bmartel Sep 16, 2025
7b80980
Merge remote-tracking branch 'origin/develop' into fb-fit-671
bmartel Sep 16, 2025
a4f74dd
Sync Follow Merge dependencies
bmartel Sep 17, 2025
87fbebc
Merge branch 'develop' into 'fb-fit-671'
bmartel Sep 17, 2025
c8ea238
Merge remote-tracking branch 'origin/develop' into fb-fit-671
bmartel Sep 18, 2025
57860f0
adding context cache for organization_id lookup to avoid n+1
bmartel Sep 18, 2025
0bf9498
Apply pre-commit linters
bmartel Sep 18, 2025
b8b95b4
Sync Follow Merge dependencies
bmartel Sep 18, 2025
37b9556
Merge branch 'develop' into 'fb-fit-671'
bmartel Sep 18, 2025
ac039f9
fix writethrough cache concurrency
bmartel Sep 19, 2025
38e2dc5
Merge remote-tracking branch 'origin/develop' into fb-fit-671
bmartel Sep 19, 2025
7b02dd2
fixing tests
bmartel Sep 19, 2025
6470df2
fixing tests
bmartel Sep 19, 2025
2b863fe
Merge remote-tracking branch 'origin/develop' into fb-fit-671
bmartel Sep 22, 2025
f5f69c9
switch to a more flexible current context that can work either in req…
bmartel Sep 22, 2025
35260c2
Merge remote-tracking branch 'origin/develop' into fb-fit-671
bmartel Sep 22, 2025
f82026b
Merge remote-tracking branch 'origin/develop' into fb-fit-671
bmartel Sep 22, 2025
a5c65f6
Merge remote-tracking branch 'origin/develop' into fb-fit-671
bmartel Sep 23, 2025
9502efe
trying another way of sharing context
bmartel Sep 23, 2025
d63fccf
trying another solution to shared context
bmartel Sep 23, 2025
611b970
another try
bmartel Sep 23, 2025
14c1214
the imports of CurrentContext have to be lazy
bmartel Sep 23, 2025
7f6ef93
split job data and thread local data so we can retain the requesting …
bmartel Sep 23, 2025
2b2bb13
must use safe access
bmartel Sep 23, 2025
c18f252
wrong method name
bmartel Sep 23, 2025
af03ae1
removing unused code
bmartel Sep 23, 2025
764e263
Merge remote-tracking branch 'origin/develop' into fb-fit-671
bmartel Sep 23, 2025
ecd671f
removing unused code
bmartel Sep 23, 2025
2bffcf9
Merge remote-tracking branch 'origin/develop' into fb-fit-671-thread-…
bmartel Sep 25, 2025
e4bafbb
trying with the other method of cleanup
bmartel Sep 25, 2025
72a6447
trying different import
bmartel Sep 25, 2025
924ad9f
simplify all the current context back to the same file
bmartel Sep 25, 2025
db45c0e
Mocked up CurrentContext
Sep 25, 2025
c82ff1a
Unstubbing other methods
Sep 25, 2025
ecca463
trying without job data:
bmartel Sep 26, 2025
e423f0b
clear all data from current context on request end
bmartel Sep 26, 2025
69d1362
fsm context with worker jobs
bmartel Sep 26, 2025
01b0b4c
Sync Follow Merge dependencies
bmartel Sep 26, 2025
a6a786b
Merge branch 'develop' into 'fb-fit-671-thread-context'
bmartel Sep 26, 2025
30bbc09
allow detection for async job
bmartel Sep 26, 2025
a5fd019
Merge remote-tracking branch 'origin/develop' into fb-fit-671-thread-…
bmartel Sep 26, 2025
411037f
Merge remote-tracking branch 'origin/develop' into fb-fit-671-thread-…
bmartel Sep 26, 2025
d11d49c
Sync Follow Merge dependencies
bmartel Sep 29, 2025
c817ae5
Merge branch 'develop' into 'fb-fit-671-thread-context'
bmartel Sep 29, 2025
410c130
Merge remote-tracking branch 'origin/develop' into fb-fit-671-thread-…
bmartel Sep 29, 2025
772a143
Merge branch 'fb-fit-671-thread-context' of github.com:HumanSignal/la…
bmartel Sep 29, 2025
c7751fc
unused code
bmartel Sep 29, 2025
0b834fa
use current context for org id
bmartel Oct 1, 2025
30b6bfa
Merge remote-tracking branch 'origin/develop' into fb-fit-671-thread-…
bmartel Oct 1, 2025
5d20a35
Merge remote-tracking branch 'origin/develop' into fb-fit-671-thread-…
bmartel Oct 1, 2025
ceb7a0a
Merge remote-tracking branch 'origin/develop' into fb-fit-671-thread-…
bmartel Oct 2, 2025
04fc959
Sync Follow Merge dependencies
bmartel Oct 2, 2025
17918ca
Merge branch 'develop' into 'fb-fit-671-thread-context'
bmartel Oct 2, 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
76 changes: 72 additions & 4 deletions label_studio/core/current_request.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from threading import local
from typing import Any

from django.core.signals import request_finished
from django.dispatch import receiver
Expand All @@ -7,18 +8,85 @@
_thread_locals = local()


class CurrentContext:
@classmethod
def set(cls, key: str, value: Any, shared: bool = True) -> None:
if not hasattr(_thread_locals, 'data'):
_thread_locals.data = {}
if not hasattr(_thread_locals, 'job_data'):
_thread_locals.job_data = {}

if shared:
_thread_locals.job_data[key] = value
else:
_thread_locals.data[key] = value

@classmethod
def get(cls, key: str, default=None):
return getattr(_thread_locals, 'job_data', {}).get(key, getattr(_thread_locals, 'data', {}).get(key, default))

@classmethod
def set_request(cls, request):
_thread_locals.request = request
if request.user:
cls.set_user(request.user)

@classmethod
def get_organization_id(cls):
return cls.get('organization_id')

@classmethod
def set_organization_id(cls, organization_id: int):
cls.set('organization_id', organization_id)

@classmethod
def get_user(cls):
return cls.get('user')

@classmethod
def set_user(cls, user):
cls.set('user', user)
if getattr(user, 'active_organization_id', None):
cls.set_organization_id(user.active_organization_id)

@classmethod
def is_async_job(cls) -> bool:
return cls.get_request() is None

@classmethod
def get_job_data(cls) -> dict:
"""
This data will be shared to jobs spawned by the current thread.
"""
return getattr(_thread_locals, 'job_data', {})

@classmethod
def clear(cls) -> None:
if hasattr(_thread_locals, 'data'):
delattr(_thread_locals, 'data')

if hasattr(_thread_locals, 'job_data'):
delattr(_thread_locals, 'job_data')

if hasattr(_thread_locals, 'request'):
del _thread_locals.request

@classmethod
def get_request(cls):
return getattr(_thread_locals, 'request', None)


def get_current_request():
"""returns the request object for this thread"""
result = getattr(_thread_locals, 'request', None)
result = CurrentContext.get_request()
return result


class ThreadLocalMiddleware(CommonMiddleware):
def process_request(self, request):
_thread_locals.request = request
CurrentContext.set_request(request)


@receiver(request_finished)
def clean_request(sender, **kwargs):
if hasattr(_thread_locals, 'request'):
del _thread_locals.request
CurrentContext.clear()
53 changes: 52 additions & 1 deletion label_studio/core/redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import sys
from datetime import timedelta
from functools import partial
from typing import Any

import django_rq
import redis
from core.current_request import CurrentContext
from django.conf import settings
from django_rq import get_connection
from rq.command import send_stop_job_command
Expand Down Expand Up @@ -80,6 +82,40 @@ def redis_connected():
return redis_healthcheck()


def _is_serializable(value: Any) -> bool:
"""Check if a value can be serialized for job context."""
return isinstance(value, (str, int, float, bool, list, dict, type(None)))


def _capture_context() -> dict:
"""
Capture the current context for passing to a job.
Returns a dictionary of context data that can be serialized.
"""
context_data = {}

# Get user information
if user := CurrentContext.get_user():
context_data['user_id'] = user.id

# Get organization if set separately
if org_id := CurrentContext.get_organization_id():
context_data['organization_id'] = org_id

# If organization_id is not set, try to get it from the user, this ensures that we have an organization_id for the job
# And it prefers the original requesting user's organization_id over the current active organization_id of the user which could change during async jobs
if not org_id and user and hasattr(user, 'active_organization_id') and user.active_organization_id:
context_data['organization_id'] = user.active_organization_id

# Get any custom context values (exclude non-serializable objects)
job_data = CurrentContext.get_job_data()
for key, value in job_data.items():
if key not in ['user', 'request'] and _is_serializable(value):
context_data[key] = value

return context_data


def redis_get(key):
if not redis_healthcheck():
return
Expand Down Expand Up @@ -112,7 +148,9 @@ def redis_delete(key):

def start_job_async_or_sync(job, *args, in_seconds=0, **kwargs):
"""
Start job async with redis or sync if redis is not connected
Start job async with redis or sync if redis is not connected.
Automatically preserves context for async jobs and clears it after completion.

:param job: Job function
:param args: Function arguments
:param in_seconds: Job will be delayed for in_seconds
Expand All @@ -122,15 +160,25 @@ def start_job_async_or_sync(job, *args, in_seconds=0, **kwargs):

redis = redis_connected() and kwargs.get('redis', True)
queue_name = kwargs.get('queue_name', 'default')

if 'queue_name' in kwargs:
del kwargs['queue_name']
if 'redis' in kwargs:
del kwargs['redis']

job_timeout = None
if 'job_timeout' in kwargs:
job_timeout = kwargs['job_timeout']
del kwargs['job_timeout']

if redis:
# Async execution with Redis - wrap job for context management
context_data = _capture_context()

if context_data:
# Only wrap if we have context to preserve
kwargs['_context_data'] = context_data

try:
args_info = _truncate_args_for_logging(args, kwargs)
logger.info(f'Start async job {job.__name__} on queue {queue_name} with {args_info}.')
Expand All @@ -140,6 +188,7 @@ def start_job_async_or_sync(job, *args, in_seconds=0, **kwargs):
enqueue_method = queue.enqueue
if in_seconds > 0:
enqueue_method = partial(queue.enqueue_in, timedelta(seconds=in_seconds))

job = enqueue_method(
job,
*args,
Expand All @@ -149,6 +198,8 @@ def start_job_async_or_sync(job, *args, in_seconds=0, **kwargs):
)
return job
else:
# Sync execution - context is already available from request thread
# No need to wrap or modify the job function
on_failure = kwargs.pop('on_failure', None)
try:
return job(*args, **kwargs)
Expand Down
4 changes: 3 additions & 1 deletion label_studio/data_manager/actions/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ def delete_tasks_annotations(project, queryset, **kwargs):
drafts = drafts.filter(user=int(annotator_id))
project.summary.remove_created_drafts_and_labels(drafts)

count, _ = annotations.delete()
# count before delete to return the number of deleted items, not including cascade deletions
count = annotations.count()
annotations.delete()
drafts.delete() # since task-level annotation drafts will not have been deleted by CASCADE
emit_webhooks_for_instance(project.organization, project, WebhookAction.ANNOTATIONS_DELETED, annotations_ids)
request = kwargs['request']
Expand Down
39 changes: 39 additions & 0 deletions label_studio/fsm/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,42 @@ class FsmConfig(AppConfig):
default_auto_field = 'django.db.models.UUIDField'
name = 'fsm'
verbose_name = 'Label Studio FSM'

def ready(self):
"""Initialize FSM integration when the app is ready"""
# Check if this is Community edition - only register signals for Community
from django.conf import settings

version_edition = getattr(settings, 'VERSION_EDITION', 'Community')

if version_edition != 'Community':
logger.info(f'Label Studio FSM: Skipping initialization for {version_edition} edition')
return

# Additional safety check: if LSE FSM apps are present, don't initialize
try:
from django.apps import apps

if apps.is_installed('lse_fsm'):
logger.info('Label Studio FSM: LSE FSM detected, skipping LSO FSM initialization to avoid conflicts')
return
except Exception as e:
# Log but continue - this shouldn't prevent initialization in pure LSO environments
logger.debug(f'Label Studio FSM: Error checking for LSE apps: {e}')

logger.info('Label Studio FSM app ready, initializing core integrations for Community edition')

# Import signal handlers to register them (only for Community edition)
try:
from . import signals # noqa: F401

logger.info('Label Studio FSM signal handlers registered successfully')
except Exception as e:
logger.error(
'FSM: Failed to register Label Studio FSM signal handlers',
extra={
'event': 'fsm.signal_registration_error',
'error': str(e),
},
exc_info=True,
)
Loading
Loading