Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
126 commits
Select commit Hold shift + click to select a range
ada3cc1
add celery-library as api-server dependency
bisgaard-itis Aug 14, 2025
1350f84
initial setup of celery worker
bisgaard-itis Aug 14, 2025
b8c494e
Merge branch 'master' into 1973-add-celery-worker-to-api-server
bisgaard-itis Aug 19, 2025
8813fa5
fix openapi-specs make target in api-server
bisgaard-itis Aug 19, 2025
4a822e5
fix openapi specs generation in api-server
bisgaard-itis Aug 19, 2025
22c6c2e
name fix
bisgaard-itis Aug 19, 2025
b8325d8
first attempt at definining run function task
bisgaard-itis Aug 19, 2025
1b43976
minor adjustments
bisgaard-itis Aug 19, 2025
7e57cd9
Merge branch 'master' into 1973-add-celery-worker-to-api-server
bisgaard-itis Aug 19, 2025
25eaaaa
improve docs and minor changes
bisgaard-itis Aug 20, 2025
ed6ed1e
cleanup and reset docs
bisgaard-itis Aug 20, 2025
2f57d5e
register task
bisgaard-itis Aug 20, 2025
f4b4af8
add celery client inside api-server
bisgaard-itis Aug 20, 2025
8b3ed88
ensure requirements are set
bisgaard-itis Aug 20, 2025
94324cf
cleanup fixtures
bisgaard-itis Aug 20, 2025
e279206
insert fake run_function
bisgaard-itis Aug 20, 2025
8e49543
check mock
bisgaard-itis Aug 20, 2025
80a7cca
transform run function endpoint
bisgaard-itis Aug 20, 2025
a06118f
add pytest-celery plugin
bisgaard-itis Aug 20, 2025
f55363c
can run empty test
bisgaard-itis Aug 20, 2025
8f847f6
minor changes
bisgaard-itis Aug 20, 2025
5a6d47e
test fix
bisgaard-itis Aug 20, 2025
1d4df87
further improvements to test
bisgaard-itis Aug 20, 2025
33653cd
add fakeredis dependency
bisgaard-itis Aug 20, 2025
c91abad
further additions to tests
bisgaard-itis Aug 20, 2025
653dbbc
fix for logstreamer
bisgaard-itis Aug 20, 2025
676aa32
do full round trip in test
bisgaard-itis Aug 20, 2025
162c8ee
test passing
bisgaard-itis Aug 20, 2025
18effae
start converting task tests
bisgaard-itis Aug 20, 2025
c297397
minor change
bisgaard-itis Aug 20, 2025
0683715
minor changes
bisgaard-itis Aug 21, 2025
2021ac0
add examples to celery-library models and use them in mocks
bisgaard-itis Aug 21, 2025
51e0b95
first test passing
bisgaard-itis Aug 21, 2025
b471434
further corrections to tests
bisgaard-itis Aug 21, 2025
47e9fd1
add fixture
bisgaard-itis Aug 21, 2025
7d329f6
further additions
bisgaard-itis Aug 21, 2025
ca2c013
ensure all task tests pass
bisgaard-itis Aug 21, 2025
5824f2b
finish task tests
bisgaard-itis Aug 21, 2025
77ac520
add test for exception propagation from celery
bisgaard-itis Aug 21, 2025
91ed6f1
add inputs in run function test
bisgaard-itis Aug 21, 2025
95de5a5
add api-worker to docker compose
bisgaard-itis Aug 21, 2025
e6eb80c
add new fixtures and model examples
bisgaard-itis Aug 21, 2025
bc263b0
first attempt to add boot script for api-server worker
bisgaard-itis Aug 21, 2025
6b73791
add health check to api-server celery worker
bisgaard-itis Aug 21, 2025
6c10aed
add asgi_lifespan to servicelib.fastapi reqs as it was missing
bisgaard-itis Aug 21, 2025
157bbac
fixes to make services functional again
bisgaard-itis Aug 21, 2025
568817b
add redis env vars to api-server
bisgaard-itis Aug 21, 2025
329354f
Merge branch 'master' into 1973-add-celery-worker-to-api-server
bisgaard-itis Aug 22, 2025
e8683a9
add separate rabbitmq queue for api-worker
bisgaard-itis Aug 22, 2025
1400a42
add test of task function itself
bisgaard-itis Aug 22, 2025
7d6d4c9
register job pydantic types for serialization - can run function via …
bisgaard-itis Aug 22, 2025
4a327f8
cosmetic fix
bisgaard-itis Aug 22, 2025
4dcf9bd
Merge branch 'master' into 1973-add-celery-worker-to-api-server
bisgaard-itis Aug 22, 2025
3efc130
change boot process for celery worker
bisgaard-itis Aug 22, 2025
75a06d5
fix test_tasks.py
bisgaard-itis Aug 22, 2025
c1a67d5
add test for full round trip of running function
bisgaard-itis Aug 22, 2025
4acf14a
fix typecheck
bisgaard-itis Aug 25, 2025
bec7cf2
Merge branch 'master' into 1973-add-celery-worker-to-api-server
bisgaard-itis Aug 25, 2025
f49f9ef
update openapi specs
bisgaard-itis Aug 25, 2025
42a6133
add example in RegisteredProjectFunctionGet
bisgaard-itis Aug 25, 2025
60a0b8b
fix example of TaskStatus
bisgaard-itis Aug 25, 2025
7fbaab6
Merge branch 'master' into 1973-add-celery-worker-to-api-server
bisgaard-itis Aug 25, 2025
ab428ac
upgrade fakeredis dependency in api-server
bisgaard-itis Aug 25, 2025
9709eec
fix pylinting
bisgaard-itis Aug 25, 2025
53645b6
make pylint happy
bisgaard-itis Aug 25, 2025
b1d306e
fix pylint after formatting
bisgaard-itis Aug 25, 2025
5a2b652
pylint fix
bisgaard-itis Aug 25, 2025
ff376a7
remove worker services from public api integration tests
bisgaard-itis Aug 25, 2025
8391dfd
add api-worker in docker-compose.local.yml
bisgaard-itis Aug 25, 2025
7ece751
add api-worker in docker-compose.devel.yml
bisgaard-itis Aug 25, 2025
cf57e04
Merge branch 'master' into 1973-add-celery-worker-to-api-server
bisgaard-itis Aug 25, 2025
4eb2262
factor out function run pre check
bisgaard-itis Aug 26, 2025
b306b19
add test for checking function job patch method
bisgaard-itis Aug 26, 2025
0a94743
start creating patch endpoint for function jobs
bisgaard-itis Aug 26, 2025
8c3b881
missing files
bisgaard-itis Aug 26, 2025
d829b01
Merge branch 'master' into 1973-add-celery-worker-to-api-server
bisgaard-itis Aug 26, 2025
10df578
Merge branch 'master' into 1973-add-celery-worker-to-api-server
bisgaard-itis Aug 27, 2025
981736f
add mehod for patching a registered function job to function job service
bisgaard-itis Aug 27, 2025
4f989b6
implement run function workflow
bisgaard-itis Aug 27, 2025
0a75a8d
minor fix
bisgaard-itis Aug 27, 2025
1230692
typecheck fix
bisgaard-itis Aug 28, 2025
e18088a
Merge branch 'master' into 1973-add-celery-worker-to-api-server
bisgaard-itis Aug 28, 2025
52288be
propagate pre_registered_function_job_id properly
bisgaard-itis Aug 28, 2025
07f7ec8
cleanup
bisgaard-itis Aug 28, 2025
bf6bb7e
fix fake run fcn test
bisgaard-itis Aug 28, 2025
ff8b50a
fix tests
bisgaard-itis Aug 28, 2025
34ee07f
test fix
bisgaard-itis Aug 28, 2025
eebdf2f
pylint fix
bisgaard-itis Aug 28, 2025
50695b3
Merge branch 'master' into 1973-add-celery-worker-to-api-server
bisgaard-itis Aug 28, 2025
fc7b39e
implement function job status endpoint
bisgaard-itis Aug 28, 2025
8ed5bbd
start implementing test for getting function job status
bisgaard-itis Aug 28, 2025
eab5e08
finish test for getting function job status
bisgaard-itis Aug 29, 2025
9401f48
fix get function job outputs and test
bisgaard-itis Aug 29, 2025
55df241
add implementation for map endpoint
bisgaard-itis Aug 29, 2025
95ae30d
move test so that it uses celery worker
bisgaard-itis Aug 29, 2025
21cd8ae
Merge branch 'master' into 1973-add-celery-worker-to-api-server
bisgaard-itis Aug 29, 2025
2ce52b6
update openapi-specs
bisgaard-itis Aug 29, 2025
979cc2c
make pylint happy
bisgaard-itis Aug 30, 2025
bd64fea
typecheck
bisgaard-itis Aug 30, 2025
3774f00
celery -> celery_worker
bisgaard-itis Sep 1, 2025
b8b69fe
correct examples added to pydantic models @sanderegg
bisgaard-itis Sep 1, 2025
b41eb5a
improve dependency-injection system comment @sanderegg
bisgaard-itis Sep 1, 2025
b36d5ff
massage -> preprocess
bisgaard-itis Sep 1, 2025
9c154e4
@pcrespov use contextlib.suppress
bisgaard-itis Sep 1, 2025
313cc1d
avoid converting CeleryError to HTTPException directly in endpoint ha…
bisgaard-itis Sep 1, 2025
fb2e994
return 503 instead of 500 when celery task fails @sanderegg
bisgaard-itis Sep 1, 2025
f272e6f
Merge branch 'master' into 1973-add-celery-worker-to-api-server
bisgaard-itis Sep 1, 2025
a640f4c
test fixes
bisgaard-itis Sep 1, 2025
7b5cda8
return 503 in case of CeleryError @sanderegg
bisgaard-itis Sep 1, 2025
4e09ac3
use pytest-simcore fixture
bisgaard-itis Sep 2, 2025
46f1bd4
typecheck issue fix
bisgaard-itis Sep 2, 2025
02a9860
Merge branch 'master' into 1973-add-celery-worker-to-api-server
bisgaard-itis Sep 2, 2025
098018a
fix the typecheck fix
bisgaard-itis Sep 2, 2025
228fae9
move pytest_plugins to toplevel conftest
bisgaard-itis Sep 2, 2025
e057a26
fix tests
bisgaard-itis Sep 2, 2025
eb6de3b
fix mock
bisgaard-itis Sep 2, 2025
22a2226
Merge branch 'master' into 1973-add-celery-worker-to-api-server
bisgaard-itis Sep 2, 2025
b356c2b
@pcrespov simply reraise exception to return 500 status cod
bisgaard-itis Sep 2, 2025
4eaea73
@pcrespov decouple setup of celery task manager from dependencies sub…
bisgaard-itis Sep 2, 2025
faeac06
@GitHK poll_task_until_done -> wait_for_task_result
bisgaard-itis Sep 2, 2025
a51a42c
@GitHK wait fixed 1 sec
bisgaard-itis Sep 2, 2025
7f20a84
pylint fix
bisgaard-itis Sep 2, 2025
76aecfe
ensure ordering of jobs is preserved
bisgaard-itis Sep 2, 2025
312e853
first attempt to test job_id order
bisgaard-itis Sep 2, 2025
fa4f7ad
Revert "first attempt to test job_id order"
bisgaard-itis Sep 3, 2025
9172a39
fix fixture for creating celery app
bisgaard-itis Sep 3, 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
27 changes: 25 additions & 2 deletions .github/prompts/update-user-messages.prompt.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
mode: 'edit'
description: 'Update user messages'
model: Claude Sonnet 3.5
---

This prompt guide is for updating user-facing messages in ${file} or ${selection}
Expand Down Expand Up @@ -43,7 +44,17 @@ When modifying user messages, follow **as close as possible** these rules:
user_message("Unable to load project.", _version=1)
```

3. **Message Style**: Follow **strictly** the guidelines in `${workspaceFolder}/docs/user-messages-guidelines.md`
3. **Message Style**: Follow **STRICTLY ALL 10 GUIDELINES** in `${workspaceFolder}/docs/user-messages-guidelines.md`:
- Be Clear and Concise
- Provide Specific and Actionable Information
- Avoid Technical Jargon
- Use a Polite and Non-Blaming Tone
- Avoid Negative Words and Phrases
- Place Messages Appropriately
- Use Inline Validation When Possible
- Avoid Using All-Caps and Excessive Punctuation
- **Use Humor Sparingly** - Avoid casual phrases like "Oops!", "Whoops!", or overly informal language
- Offer Alternative Solutions or Support

4. **Preserve Context**: Ensure the modified message conveys the same meaning and context as the original.

Expand All @@ -56,8 +67,10 @@ When modifying user messages, follow **as close as possible** these rules:
# After
user_message("Your session has expired. Please log in again.", _version=3)
```

6. **Replace 'Study' by 'Project'**: If the message contains the word 'Study', replace it with 'Project' to align with our terminology.

7. **Professional Tone**: Maintain a professional, helpful tone. Avoid humor, casual expressions, or overly informal language that might not be appropriate for all users or situations.

## Examples

Expand Down Expand Up @@ -91,4 +104,14 @@ return HttpErrorInfo(status.HTTP_404_NOT_FOUND, user_message("User not found.",
return HttpErrorInfo(status.HTTP_404_NOT_FOUND, user_message("The requested user could not be found.", _version=2))
```

Remember: The goal is to improve clarity and helpfulness for end-users while maintaining accurate versioning for tracking changes.
### Example 4: Removing Humor (Guideline 9)

```python
# Before
user_message("Oops! Something went wrong, but we've noted it down and we'll sort it out ASAP. Thanks for your patience!")

# After
user_message("Something went wrong on our end. We've been notified and will resolve this issue as soon as possible. Thank you for your patience.", _version=1)
```

Remember: The goal is to improve clarity and helpfulness for end-users while maintaining accurate versioning for tracking changes. **Always check that your updated messages comply with ALL 10 guidelines, especially avoiding humor and maintaining a professional tone.**
4 changes: 2 additions & 2 deletions packages/celery-library/tests/unit/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from random import randint

import pytest
from celery import Celery, Task
from celery.contrib.abortable import AbortableTask
from celery import Celery, Task # pylint: disable=no-name-in-module
from celery.contrib.abortable import AbortableTask # pylint: disable=no-name-in-module
from celery_library.errors import TransferrableCeleryError
from celery_library.task import register_task
from celery_library.task_manager import CeleryTaskManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
from collections.abc import Awaitable, Callable
from typing import Annotated, TypeAlias

from pydantic import BaseModel, Field, field_validator, validate_call
from pydantic import BaseModel, ConfigDict, Field, field_validator, validate_call
from pydantic.config import JsonDict

_logger = logging.getLogger(__name__)

Expand All @@ -23,6 +24,22 @@ class TaskProgress(BaseModel):
message: ProgressMessage = ""
percent: ProgressPercent = 0.0

@staticmethod
def _update_json_schema_extra(schema: JsonDict) -> None:
schema.update(
{
"examples": [
{
"task_id": "3ac48b54-a48d-4c5e-a6ac-dcaddb9eaa59",
"message": "Halfway done",
"percent": 0.5,
}
]
}
)

model_config = ConfigDict(json_schema_extra=_update_json_schema_extra)

# used to propagate progress updates internally
_update_callback: Callable[["TaskProgress"], Awaitable[None]] | None = None

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import datetime
from typing import Annotated, TypeAlias

from pydantic import Field, HttpUrl
from pydantic import ConfigDict, Field, HttpUrl

from ..functions import (
Function,
Expand Down Expand Up @@ -141,6 +141,45 @@ class RegisteredProjectFunctionGet(RegisteredProjectFunction, OutputSchema):
modified_at: Annotated[datetime.datetime, Field(alias="lastChangeDate")]
access_rights: dict[GroupID, FunctionGroupAccessRightsGet]
thumbnail: HttpUrl | None = None
model_config = ConfigDict(
populate_by_name=True,
json_schema_extra={
"examples": [
{
"function_class": "PROJECT",
"title": "Example Project Function",
"description": "This is an example project function.",
"input_schema": {
"schema_content": {
"type": "object",
"properties": {"input1": {"type": "integer"}},
},
"schema_class": "application/schema+json",
},
"output_schema": {
"schema_content": {
"type": "object",
"properties": {"output1": {"type": "string"}},
},
"schema_class": "application/schema+json",
},
"default_inputs": None,
"project_id": "11111111-1111-1111-1111-111111111111",
"uid": "22222222-2222-2222-2222-222222222222",
"created_at": "2024-01-01T12:00:00",
"modified_at": "2024-01-02T12:00:00",
"access_rights": {
"5": {
"read": True,
"write": False,
"execute": True,
}
},
"thumbnail": None,
},
]
},
)


class SolverFunctionToRegister(SolverFunction, InputSchema): ...
Expand Down
32 changes: 31 additions & 1 deletion packages/models-library/src/models_library/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,37 @@ class ProjectFunction(FunctionBase):


class RegisteredProjectFunction(ProjectFunction, RegisteredFunctionBase):
pass
model_config = ConfigDict(
populate_by_name=True,
json_schema_extra={
"examples": [
{
"function_class": "PROJECT",
"title": "Example Project Function",
"description": "This is an example project function.",
"input_schema": {
"schema_content": {
"type": "object",
"properties": {"input1": {"type": "integer"}},
},
"schema_class": "application/schema+json",
},
"output_schema": {
"schema_content": {
"type": "object",
"properties": {"output1": {"type": "string"}},
},
"schema_class": "application/schema+json",
},
"default_inputs": None,
"project_id": "11111111-1111-1111-1111-111111111111",
"uid": "22222222-2222-2222-2222-222222222222",
"created_at": "2024-01-01T12:00:00",
"modified_at": "2024-01-02T12:00:00",
},
]
},
)


SolverJobID: TypeAlias = UUID
Expand Down
25 changes: 19 additions & 6 deletions packages/models-library/src/models_library/progress_bar.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Literal, TypeAlias

from pydantic import BaseModel, ConfigDict
from pydantic.config import JsonDict

# NOTE: keep a list of possible unit, and please use correct official unit names
ProgressUnit: TypeAlias = Literal["Byte"]
Expand All @@ -13,9 +14,10 @@ class ProgressStructuredMessage(BaseModel):
unit: str | None = None
sub: "ProgressStructuredMessage | None" = None

model_config = ConfigDict(
json_schema_extra={
"examples": [
@staticmethod
def _update_json_schema_extra(schema: JsonDict) -> None:
schema.update(
examples=[
{
"description": "some description",
"current": 12.2,
Expand All @@ -39,8 +41,9 @@ class ProgressStructuredMessage(BaseModel):
},
},
]
}
)
)

model_config = ConfigDict(json_schema_extra=_update_json_schema_extra)


UNITLESS = None
Expand Down Expand Up @@ -96,7 +99,17 @@ def composed_message(self) -> str:
{
"actual_value": 0.3,
"total": 1.0,
"message": ProgressStructuredMessage.model_config["json_schema_extra"]["examples"][2], # type: ignore [index]
"message": {
"description": "downloading",
"current": 2.0,
"total": 5,
"sub": {
"description": "port 2",
"current": 12.2,
"total": 123,
"unit": "Byte",
},
},
},
]
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# pylint: disable=redefined-outer-name

from collections.abc import Callable

import pytest
from faker import Faker
from pytest_mock import MockerFixture, MockType
from servicelib.celery.models import TaskStatus, TaskUUID
from servicelib.celery.task_manager import Task, TaskManager

_faker = Faker()


@pytest.fixture
def submit_task_return_value() -> TaskUUID:
return TaskUUID(_faker.uuid4())


@pytest.fixture
def cancel_task_return_value() -> None:
return None


@pytest.fixture
def get_task_result_return_value() -> dict:
return {"result": "example"}


@pytest.fixture
def get_task_status_return_value() -> TaskStatus:
example = TaskStatus.model_json_schema()["examples"][0]
return TaskStatus.model_validate(example)


@pytest.fixture
def list_tasks_return_value() -> list[Task]:
examples = Task.model_json_schema()["examples"]
assert len(examples) > 0
return [Task.model_validate(example) for example in examples]


@pytest.fixture
def set_task_progress_return_value() -> None:
return None


@pytest.fixture
def mock_task_manager_object(
mocker: MockerFixture,
submit_task_return_value: TaskUUID,
cancel_task_return_value: None,
get_task_result_return_value: dict,
get_task_status_return_value: TaskStatus,
list_tasks_return_value: list[Task],
set_task_progress_return_value: None,
) -> MockType:
"""
Returns a TaskManager mock with overridable return values for each method.
If a return value is an Exception, the method will raise it.
"""
mock = mocker.Mock(spec=TaskManager)

def _set_return_or_raise(method, value):
if isinstance(value, Exception):
method.side_effect = lambda *a, **kw: (_ for _ in ()).throw(value)
else:
method.return_value = value

_set_return_or_raise(mock.submit_task, submit_task_return_value)
_set_return_or_raise(mock.cancel_task, cancel_task_return_value)
_set_return_or_raise(mock.get_task_result, get_task_result_return_value)
_set_return_or_raise(mock.get_task_status, get_task_status_return_value)
_set_return_or_raise(mock.list_tasks, list_tasks_return_value)
_set_return_or_raise(mock.set_task_progress, set_task_progress_return_value)
return mock


@pytest.fixture
def mock_task_manager_object_raising_factory(
mocker: MockerFixture,
) -> Callable[[Exception], MockType]:
def _factory(task_manager_exception: Exception) -> MockType:
mock = mocker.Mock(spec=TaskManager)

def _raise_exc(*args, **kwargs):
raise task_manager_exception

mock.submit_task.side_effect = _raise_exc
mock.cancel_task.side_effect = _raise_exc
mock.get_task_result.side_effect = _raise_exc
mock.get_task_status.side_effect = _raise_exc
mock.list_tasks.side_effect = _raise_exc
mock.set_task_progress.side_effect = _raise_exc
return mock

return _factory
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@


_SERVICES_TO_SKIP: Final[set[str]] = {
"api-worker",
"agent", # global mode deploy (NO exposed ports, has http API)
"dask-sidecar", # global mode deploy (NO exposed ports, **NO** http API)
"migration",
Expand Down
Loading
Loading