avtomatika-worker 1.0a2__tar.gz → 1.0b1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. avtomatika_worker-1.0b1/PKG-INFO +537 -0
  2. avtomatika_worker-1.0b1/README.md +509 -0
  3. {avtomatika_worker-1.0a2 → avtomatika_worker-1.0b1}/pyproject.toml +12 -2
  4. {avtomatika_worker-1.0a2 → avtomatika_worker-1.0b1}/src/avtomatika_worker/config.py +34 -23
  5. avtomatika_worker-1.0b1/src/avtomatika_worker/s3.py +75 -0
  6. {avtomatika_worker-1.0a2 → avtomatika_worker-1.0b1}/src/avtomatika_worker/types.py +4 -0
  7. {avtomatika_worker-1.0a2 → avtomatika_worker-1.0b1}/src/avtomatika_worker/worker.py +182 -72
  8. avtomatika_worker-1.0b1/src/avtomatika_worker.egg-info/PKG-INFO +537 -0
  9. {avtomatika_worker-1.0a2 → avtomatika_worker-1.0b1}/src/avtomatika_worker.egg-info/SOURCES.txt +8 -1
  10. {avtomatika_worker-1.0a2 → avtomatika_worker-1.0b1}/src/avtomatika_worker.egg-info/requires.txt +7 -0
  11. {avtomatika_worker-1.0a2 → avtomatika_worker-1.0b1}/tests/test_concurrency_limits.py +11 -2
  12. avtomatika_worker-1.0b1/tests/test_config.py +115 -0
  13. avtomatika_worker-1.0b1/tests/test_init.py +25 -0
  14. avtomatika_worker-1.0b1/tests/test_parameter_typing.py +168 -0
  15. avtomatika_worker-1.0b1/tests/test_per_orchestrator_token.py +62 -0
  16. avtomatika_worker-1.0b1/tests/test_s3.py +85 -0
  17. {avtomatika_worker-1.0a2 → avtomatika_worker-1.0b1}/tests/test_worker_logic.py +21 -19
  18. avtomatika_worker-1.0b1/tests/test_worker_more_logic.py +268 -0
  19. {avtomatika_worker-1.0a2 → avtomatika_worker-1.0b1}/tests/test_worker_sdk.py +74 -48
  20. avtomatika_worker-1.0b1/tests/test_wrr_logic.py +63 -0
  21. avtomatika_worker-1.0a2/PKG-INFO +0 -307
  22. avtomatika_worker-1.0a2/README.md +0 -285
  23. avtomatika_worker-1.0a2/src/avtomatika_worker.egg-info/PKG-INFO +0 -307
  24. avtomatika_worker-1.0a2/tests/test_config.py +0 -96
  25. {avtomatika_worker-1.0a2 → avtomatika_worker-1.0b1}/LICENSE +0 -0
  26. {avtomatika_worker-1.0a2 → avtomatika_worker-1.0b1}/setup.cfg +0 -0
  27. {avtomatika_worker-1.0a2 → avtomatika_worker-1.0b1}/src/avtomatika_worker/__init__.py +0 -0
  28. {avtomatika_worker-1.0a2 → avtomatika_worker-1.0b1}/src/avtomatika_worker.egg-info/dependency_links.txt +0 -0
  29. {avtomatika_worker-1.0a2 → avtomatika_worker-1.0b1}/src/avtomatika_worker.egg-info/top_level.txt +0 -0
  30. {avtomatika_worker-1.0a2 → avtomatika_worker-1.0b1}/tests/test_types.py +0 -0
@@ -0,0 +1,537 @@
1
+ Metadata-Version: 2.4
2
+ Name: avtomatika-worker
3
+ Version: 1.0b1
4
+ Summary: Worker SDK for the Avtomatika orchestrator.
5
+ Project-URL: Homepage, https://github.com/avtomatila-ai/avtomatika-worker
6
+ Project-URL: Bug Tracker, https://github.com/avtomatila-ai/avtomatika-worker/issues
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.11
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: aiohttp~=3.13.2
15
+ Requires-Dist: python-json-logger~=4.0.0
16
+ Requires-Dist: aioboto3~=13.0.0
17
+ Provides-Extra: test
18
+ Requires-Dist: pytest; extra == "test"
19
+ Requires-Dist: pytest-asyncio; extra == "test"
20
+ Requires-Dist: aioresponses; extra == "test"
21
+ Requires-Dist: pytest-mock; extra == "test"
22
+ Requires-Dist: pydantic; extra == "test"
23
+ Requires-Dist: moto[server]; extra == "test"
24
+ Requires-Dist: aiofiles; extra == "test"
25
+ Provides-Extra: pydantic
26
+ Requires-Dist: pydantic; extra == "pydantic"
27
+ Dynamic: license-file
28
+
29
+ # Avtomatika Worker SDK
30
+
31
+ This is an SDK for creating workers compatible with the **Avtomatika** orchestrator. The SDK handles all the complexity of interacting with the orchestrator, allowing you to focus on writing your business logic.
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install avtomatika-worker
37
+ ```
38
+
39
+ For advanced validation features, you can install the SDK with the `pydantic` extra:
40
+ ```bash
41
+ pip install "avtomatika-worker[pydantic]"
42
+ ```
43
+
44
+ ## Configuration
45
+
46
+ The worker is configured entirely through environment variables. Before running your worker, you need to set a few essential variables.
47
+
48
+ - **`WORKER_ID`**: A unique name for your worker instance. If not provided, a random UUID will be generated.
49
+ - **`ORCHESTRATOR_URL`**: The address of the Avtomatika orchestrator.
50
+ - **`WORKER_TOKEN`**: An authentication token to connect to the orchestrator.
51
+
52
+ Here is an example of how you might set them in your shell:
53
+ ```bash
54
+ export WORKER_ID="image-processor-worker-1"
55
+ export ORCHESTRATOR_URL="http://localhost:8080"
56
+ export WORKER_TOKEN="your-secret-token"
57
+ ```
58
+
59
+ A complete list of all available configuration variables can be found in the **Full Configuration Reference** section at the end of this document.
60
+
61
+ ## Programmatic Configuration (Advanced)
62
+
63
+ While using environment variables is the recommended approach, you can also configure the worker programmatically. This is useful for advanced scenarios, such as dynamic configuration or integration into larger applications.
64
+
65
+ The process supports partial configuration. When you create a `WorkerConfig` instance, it **first loads all settings from environment variables**. You can then override specific values in your code before passing the completed config object to the `Worker`.
66
+
67
+ **Note:** The attributes on the `WorkerConfig` object use `UPPERCASE_SNAKE_CASE` to mirror the corresponding environment variables.
68
+
69
+ ### Example of Partial Configuration
70
+
71
+ Let's assume you have an environment variable set for the orchestrator URL:
72
+ ```bash
73
+ export ORCHESTRATOR_URL="http://orchestrator.from.env:8080"
74
+ ```
75
+
76
+ You can then write Python code to override other settings:
77
+ ```python
78
+ import asyncio
79
+ from avtomatika_worker import Worker
80
+ from avtomatika_worker.config import WorkerConfig
81
+
82
+ # 1. Create a config object. It automatically reads ORCHESTRATOR_URL
83
+ # from the environment variables at this step.
84
+ custom_config = WorkerConfig()
85
+
86
+ # 2. Programmatically override or set other attributes.
87
+ custom_config.WORKER_ID = "programmatic-worker-1"
88
+ custom_config.WORKER_TOKEN = "super-secret-token-from-code"
89
+ custom_config.MAX_CONCURRENT_TASKS = 5
90
+
91
+ # 3. Pass the final config object to the Worker.
92
+ # It will use the values from your code (e.g., WORKER_ID)
93
+ # and the values from the environment (e.g., ORCHESTRATORS).
94
+ worker = Worker(
95
+ worker_type="special-cpu-worker",
96
+ config=custom_config
97
+ )
98
+
99
+ @worker.task("do_work")
100
+ async def do_work(params: dict, **kwargs):
101
+ # ...
102
+ return {"status": "success"}
103
+
104
+ if __name__ == "__main__":
105
+ worker.run_with_health_check()
106
+ ```
107
+
108
+
109
+
110
+ ## Quick Start
111
+
112
+ For quick testing and visibility during startup, you can add basic logging configuration to your worker script. This ensures that informational messages, including registration with the orchestrator, are printed to the console.
113
+
114
+ You can configure your worker either via environment variables (recommended for production) or directly in your Python code for quick testing or specialized setups.
115
+
116
+ ### Option 1: Configure via Environment Variables (Recommended)
117
+
118
+ Save the following code as `my_worker.py`:
119
+ ```python
120
+ import asyncio
121
+ import logging # Import logging
122
+ from avtomatika_worker import Worker
123
+
124
+ # Configure basic logging to see worker messages
125
+ logging.basicConfig(level=logging.INFO)
126
+
127
+ # 1. Create a worker instance.
128
+ # The SDK automatically reads the configuration from environment variables.
129
+ worker = Worker(worker_type="image-processing")
130
+
131
+ # 2. Register a task handler using the decorator
132
+ @worker.task("resize_image")
133
+ async def image_resizer(params: dict, **kwargs):
134
+ """
135
+ An example handler that receives task parameters,
136
+ performs the work, and returns the result.
137
+ """
138
+ task_id = kwargs.get("task_id")
139
+ job_id = kwargs.get("job_id")
140
+
141
+ print(f"Task {task_id} (Job: {job_id}): resizing image...")
142
+ print(f"Parameters: {params}")
143
+
144
+ # ... your business logic here ...
145
+ await asyncio.sleep(1) # Simulate I/O-bound work
146
+
147
+ # Return the result
148
+ return {
149
+ "status": "success",
150
+ "data": {
151
+ "resized_path": f"/path/to/resized_{params.get('filename')}"
152
+ }
153
+ }
154
+
155
+ # 3. Run the worker
156
+ if __name__ == "__main__":
157
+ # The SDK will automatically connect to the orchestrator,
158
+ # register itself, and start polling for tasks.
159
+ worker.run_with_health_check()
160
+ ```
161
+
162
+ After setting the required environment variables, you can run your worker.
163
+
164
+ **Example:**
165
+ ```bash
166
+ export WORKER_ID="image-processor-worker-1"
167
+ export ORCHESTRATOR_URL="http://localhost:8080"
168
+ export WORKER_TOKEN="your-secret-token"
169
+
170
+ python my_worker.py
171
+ ```
172
+
173
+ ### Option 2: Configure Programmatically (Alternative)
174
+
175
+ For quick testing or if you prefer to define configuration directly in code for simple examples, you can create and pass a `WorkerConfig` object.
176
+
177
+ Save the following code as `my_worker_programmatic.py`:
178
+ ```python
179
+ import asyncio
180
+ import logging # Import logging
181
+ from avtomatika_worker import Worker
182
+ from avtomatika_worker.config import WorkerConfig # Import WorkerConfig
183
+
184
+ # Configure basic logging to see worker messages
185
+ logging.basicConfig(level=logging.INFO)
186
+
187
+ # 1. Create and configure a WorkerConfig object
188
+ my_config = WorkerConfig()
189
+ my_config.WORKER_ID = "image-processor-worker-1-programmatic"
190
+ my_config.ORCHESTRATOR_URL = "http://localhost:8080"
191
+ my_config.WORKER_TOKEN = "your-secret-token" # Replace with your actual token
192
+
193
+ # 2. Create a worker instance, passing the configured object
194
+ worker = Worker(worker_type="image-processing", config=my_config)
195
+
196
+ # 3. Register a task handler using the decorator
197
+ @worker.task("resize_image")
198
+ async def image_resizer(params: dict, **kwargs):
199
+ task_id = kwargs.get("task_id")
200
+ job_id = kwargs.get("job_id")
201
+
202
+ print(f"Task {task_id} (Job: {job_id}): resizing image...")
203
+ print(f"Parameters: {params}")
204
+
205
+ await asyncio.sleep(1)
206
+ return {
207
+ "status": "success",
208
+ "data": {
209
+ "resized_path": f"/path/to/resized_{params.get('filename')}"
210
+ }
211
+ }
212
+
213
+ # 4. Run the worker
214
+ if __name__ == "__main__":
215
+ worker.run_with_health_check()
216
+ ```
217
+
218
+ Run your worker:
219
+ ```bash
220
+ python my_worker_programmatic.py
221
+ ```
222
+
223
+ ## Defining Task Parameters
224
+
225
+ The SDK offers three ways to define and validate the `params` your task handler receives, giving you the flexibility to choose the right tool for your needs.
226
+
227
+ ### 1. Default: `dict`
228
+
229
+ By default, or if you type-hint `params` as a `dict`, you will receive the raw dictionary of parameters sent by the orchestrator. This is simple and requires no extra definitions.
230
+
231
+ ```python
232
+ @worker.task("resize_image")
233
+ async def image_resizer(params: dict, **kwargs):
234
+ width = params.get("width")
235
+ height = params.get("height")
236
+ # ...
237
+ ```
238
+
239
+ ### 2. Structured: `dataclasses`
240
+
241
+ For better structure and IDE autocompletion, you can use Python's built-in `dataclasses`. The SDK will automatically instantiate the dataclass from the incoming parameters. You can access parameters as class attributes.
242
+
243
+ You can also add custom validation logic using the `__post_init__` method. If validation fails, the SDK will automatically catch the `ValueError` and report an `INVALID_INPUT_ERROR` to the orchestrator.
244
+
245
+ ```python
246
+ from dataclasses import dataclass
247
+
248
+ @dataclass
249
+ class ResizeParams:
250
+ width: int
251
+ height: int
252
+
253
+ def __post_init__(self):
254
+ if self.width <= 0 or self.height <= 0:
255
+ raise ValueError("Width and height must be positive.")
256
+
257
+ @worker.task("resize_image")
258
+ async def image_resizer(params: ResizeParams, **kwargs):
259
+ # Access params with dot notation and autocompletion
260
+ print(f"Resizing to {params.width}x{params.height}")
261
+ # ...
262
+ ```
263
+
264
+ ### 3. Validated: `pydantic`
265
+
266
+ For the most robust validation and type coercion, you can use `pydantic`. First, install the necessary extra: `pip install "avtomatika-worker[pydantic]"`.
267
+
268
+ Define a `pydantic.BaseModel` for your parameters. The SDK will automatically validate the incoming data against this model. If validation fails, the detailed error message from Pydantic will be sent to the orchestrator.
269
+
270
+ ```python
271
+ from pydantic import BaseModel, Field
272
+
273
+ class ResizeParams(BaseModel):
274
+ width: int = Field(gt=0, description="Width must be positive")
275
+ height: int = Field(gt=0, description="Height must be positive")
276
+ source_url: str
277
+
278
+ @worker.task("resize_image")
279
+ async def image_resizer(params: ResizeParams, **kwargs):
280
+ # Data is guaranteed to be valid
281
+ print(f"Resizing {params.source_url} to {params.width}x{params.height}")
282
+ # ...
283
+ ```
284
+
285
+ ## Key Features
286
+
287
+ ### 1. Task Handlers
288
+
289
+ Each handler is an asynchronous function that accepts two arguments:
290
+
291
+ - `params` (`dict`, `dataclass`, or `pydantic.BaseModel`): The parameters for the task, automatically validated and instantiated based on your type hint.
292
+ - `**kwargs`: Additional metadata about the task, including:
293
+ - `task_id` (`str`): The unique ID of the task itself.
294
+ - `job_id` (`str`): The ID of the parent `Job` to which the task belongs.
295
+ - `priority` (`int`): The execution priority of the task.
296
+
297
+ ### 2. Concurrency Limiting
298
+
299
+ The worker allows you to control how many tasks are executed in parallel. This can be configured at two levels:
300
+
301
+ - **Global Limit**: A maximum number of tasks that the worker can execute simultaneously, regardless of their type. This can be set with the `MAX_CONCURRENT_TASKS` environment variable or by passing `max_concurrent_tasks` to the `Worker` constructor.
302
+ - **Per-Type Limit**: A specific limit for a group of tasks that share a common resource (e.g., a GPU, a specific API), passed via `task_type_limits` to the `Worker` constructor.
303
+
304
+ The worker dynamically reports its available capacity to the orchestrator. When a limit is reached, the worker informs the orchestrator that it can no longer accept tasks of that type until a slot becomes free.
305
+
306
+ **Example:**
307
+
308
+ Let's configure a worker that can run up to **10 tasks in total**, but no more than **1 video processing task** and **4 audio transcription tasks** at the same time.
309
+
310
+ ```python
311
+ import asyncio
312
+ from avtomatika_worker import Worker
313
+
314
+ # 1. Configure limits during initialization
315
+ worker = Worker(
316
+ worker_type="media-processor",
317
+ max_concurrent_tasks=10,
318
+ task_type_limits={
319
+ "video_processing": 1,
320
+ "audio_processing": 4,
321
+ }
322
+ )
323
+
324
+ # 2. Assign a type to each task using the decorator
325
+ @worker.task("upscale_video", task_type="video_processing")
326
+ async def upscale_video(params: dict, **kwargs):
327
+ # This task uses the 'video_processing' slot
328
+ print("Upscaling video...")
329
+ await asyncio.sleep(5)
330
+ return {"status": "success"}
331
+
332
+ @worker.task("blur_video_faces", task_type="video_processing")
333
+ async def blur_video_faces(params: dict, **kwargs):
334
+ # This task also uses the 'video_processing' slot
335
+ print("Blurring faces in video...")
336
+ await asyncio.sleep(5)
337
+ return {"status": "success"}
338
+
339
+ @worker.task("transcribe_audio", task_type="audio_processing")
340
+ async def transcribe_audio(params: dict, **kwargs):
341
+ # This task uses one of the four 'audio_processing' slots
342
+ print("Transcribing audio...")
343
+ await asyncio.sleep(2)
344
+ return {"status": "success"}
345
+
346
+ @worker.task("generate_report")
347
+ async def generate_report(params: dict, **kwargs):
348
+ # This task has no specific type and is only limited by the global limit
349
+ print("Generating report...")
350
+ await asyncio.sleep(1)
351
+ return {"status": "success"}
352
+
353
+
354
+ if __name__ == "__main__":
355
+ worker.run_with_health_check()
356
+ ```
357
+ In this example, even though the global limit is 10, the orchestrator will only ever send one task (`upscale_video` or `blur_video_faces`) to this worker at a time, because they both share the single "video_processing" slot.
358
+
359
+ ### 3. Returning Results and Handling Errors
360
+
361
+ The result returned by a handler directly influences the subsequent flow of the pipeline in the orchestrator.
362
+
363
+ #### Successful Execution
364
+
365
+ ```python
366
+ return {
367
+ "status": "success",
368
+ "data": {"output": "some_value"}
369
+ }
370
+ ```
371
+ - The orchestrator will receive this data and use the `"success"` key in the `transitions` dictionary to determine the next step.
372
+
373
+ #### Custom Statuses
374
+
375
+ You can return custom statuses to implement complex branching logic in the orchestrator.
376
+ ```python
377
+ return {
378
+ "status": "needs_manual_review",
379
+ "data": {"reason": "Low confidence score"}
380
+ }
381
+ ```
382
+ - The orchestrator will look for the `"needs_manual_review"` key in `transitions`.
383
+
384
+ #### Error Handling
385
+
386
+ To control the orchestrator's fault tolerance mechanism, you can return standardized error types.
387
+
388
+ - **Transient Error (`TRANSIENT_ERROR`)**: For issues that might be resolved on a retry (e.g., a network failure).
389
+ ```python
390
+ from avtomatika_worker.typing import TRANSIENT_ERROR
391
+ return {
392
+ "status": "failure",
393
+ "error": {
394
+ "code": TRANSIENT_ERROR,
395
+ "message": "External API timeout"
396
+ }
397
+ }
398
+ ```
399
+ - **Permanent Error (`PERMANENT_ERROR`)**: For unresolvable problems (e.g., an invalid file format).
400
+ ```python
401
+ from avtomatika_worker.typing import PERMANENT_ERROR
402
+ return {
403
+ "status": "failure",
404
+ "error": {
405
+ "code": PERMANENT_ERROR,
406
+ "message": "Corrupted input file"
407
+ }
408
+ }
409
+ ```
410
+
411
+ ### 4. Failover and Load Balancing
412
+
413
+ The SDK supports connecting to multiple orchestrator instances to ensure high availability (`FAILOVER`) and load balancing (`ROUND_ROBIN`). This is configured via the `MULTI_ORCHESTRATOR_MODE` and `ORCHESTRATORS_CONFIG` environment variables.
414
+
415
+ **If `ORCHESTRATORS_CONFIG` is not set or is invalid JSON, the SDK will fall back to using the `ORCHESTRATOR_URL`. If neither is set, it will default to a single orchestrator at `http://localhost:8080`.** If both `ORCHESTRATORS_CONFIG` and `ORCHESTRATOR_URL` are set, `ORCHESTRATORS_CONFIG` will be used.
416
+
417
+ The `ORCHESTRATORS_CONFIG` variable must contain a JSON string. Each object in the list represents one orchestrator and can have the following keys:
418
+ - `url` (required): The URL of the orchestrator.
419
+ - `priority` (optional, default: 10): Used in `FAILOVER` mode. A lower number means a higher priority.
420
+ - `weight` (optional, default: 1): Used in `ROUND_ROBIN` mode to determine how frequently the orchestrator is polled.
421
+ - `token` (optional): A specific authentication token for this orchestrator. If not provided, the global `WORKER_TOKEN` is used.
422
+
423
+ **Example `ORCHESTRATORS_CONFIG`:**
424
+ ```json
425
+ [
426
+ {"url": "http://customer-a.com", "priority": 10, "weight": 100, "token": "token-for-customer-a"},
427
+ {"url": "http://customer-b.com", "priority": 10, "weight": 50, "token": "token-for-customer-b"},
428
+ {"url": "http://internal-backup.com", "priority": 20, "weight": 10}
429
+ ]
430
+ ```
431
+
432
+ - **`FAILOVER` (default):** The worker connects to orchestrators in the order of their `priority`. It will always try the highest-priority orchestrator first and only switch to the next one if the current one becomes unavailable. In the example above, it would try both `customer-a.com` and `customer-b.com` (which have the same priority) before trying `internal-backup.com`.
433
+ - **`ROUND_ROBIN`:** The worker distributes its requests to fetch tasks across all configured orchestrators based on their `weight`. An orchestrator with a higher weight will be polled for tasks more frequently. In the example, `customer-a.com` would be polled twice as often as `customer-b.com`.
434
+
435
+
436
+
437
+ ### 5. Handling Large Files (S3 Payload Offloading)
438
+
439
+ The SDK supports working with large files "out of the box" via S3-compatible storage.
440
+
441
+ - **Automatic Download**: If a value in `params` is a URI of the form `s3://...`, the SDK will automatically download the file to the local disk and replace the URI in `params` with the local path.
442
+ - **Automatic Upload**: If your handler returns a local file path in `data` (located within the `WORKER_PAYLOAD_DIR` directory), the SDK will automatically upload this file to S3 and replace the path with an `s3://` URI in the final result.
443
+
444
+ This functionality is transparent to your code and only requires configuring environment variables for S3 access.
445
+
446
+ ### 6. WebSocket Support
447
+
448
+ If enabled, the SDK establishes a persistent WebSocket connection with the orchestrator to receive real-time commands, such as canceling an ongoing task.
449
+
450
+ ## Advanced Features
451
+
452
+ ### Reporting Skill & Model Dependencies
453
+
454
+ For more advanced scheduling, the worker can report detailed information about its skills and their dependencies on specific models. This allows the orchestrator to make smarter decisions, such as dispatching tasks to workers that already have the required models loaded in memory.
455
+
456
+ This is configured via the `skill_dependencies` argument in the `Worker` constructor.
457
+
458
+ - **`skill_dependencies`**: A dictionary where keys are skill names (as registered with `@worker.task`) and values are.
459
+ The user wants to improve the `README.md` file. I've already read it and have a plan. I need to get the file content and then I can use the `replace` tool to update it.
460
+ I've already read the file content in the previous step. Now I will use the `replace` tool to update the file.
461
+ I have read the `README.md` file. Now I will reorder its sections to improve clarity for new users. The new order will be: Installation, Configuration, Quick Start, Key Features, Advanced Features, Full Configuration Reference, and Development.
462
+ I have read the `README.md` file. Now I will update it to document the new flexible parameter typing feature. I will add a new section called "Defining Task Parameters" and update the "Installation" section. lists of model names required by that skill.
463
+
464
+ Based on this configuration and the current state of the worker's `hot_cache` (the set of models currently loaded in memory), the worker will automatically include two new fields in its heartbeat messages:
465
+
466
+ - **`skill_dependencies`**: The same dictionary provided during initialization.
467
+ - **`hot_skills`**: A dynamically calculated list of skills that are ready for immediate execution (i.e., all of their dependent models are in the `hot_cache`).
468
+
469
+ **Example:**
470
+
471
+ Consider a worker configured like this:
472
+ ```python
473
+ worker = Worker(
474
+ worker_type="ai-processor",
475
+ skill_dependencies={
476
+ "image_generation": ["stable_diffusion_v1.5", "vae-ft-mse"],
477
+ "upscale": ["realesrgan_x4"],
478
+ }
479
+ )
480
+ ```
481
+
482
+ - Initially, `hot_cache` is empty. The worker's heartbeat will include `skill_dependencies` but not `hot_skills`.
483
+ - A task handler calls `add_to_hot_cache("stable_diffusion_v1.5")`. The next heartbeat will still not include `hot_skills` because the `image_generation` skill is only partially loaded.
484
+ - The handler then calls `add_to_hot_cache("vae-ft-mse")`. Now, all dependencies for `image_generation` are met. The next heartbeat will include:
485
+ ```json
486
+ {
487
+ "hot_skills": ["image_generation"],
488
+ "skill_dependencies": {
489
+ "image_generation": ["stable_diffusion_v1.5", "vae-ft-mse"],
490
+ "upscale": ["realesrgan_x4"]
491
+ }
492
+ }
493
+ ```
494
+ This information is sent automatically. Your task handlers are only responsible for managing the `hot_cache` by calling `add_to_hot_cache()` and `remove_from_hot_cache()`, which are passed as arguments to the handler.
495
+
496
+ ## Full Configuration Reference
497
+
498
+ The worker is fully configured via environment variables.
499
+
500
+ | Variable | Description | Default |
501
+ | ----------------------------- | ------------------------------------------------------------------------------------------------------- | -------------------------------------- |
502
+ | `WORKER_ID` | A unique identifier for the worker. | A random UUID, e.g., `worker-...` |
503
+ | `WORKER_TYPE` | A string identifying the type of the worker. | `generic-cpu-worker` |
504
+ | `WORKER_PORT` | The port for the worker's health check server. | `8083` |
505
+ | `WORKER_TOKEN` | A common authentication token used to connect to orchestrators. | `your-secret-worker-token` |
506
+ | `WORKER_INDIVIDUAL_TOKEN` | An individual token for this worker, which overrides `WORKER_TOKEN` if set. | - |
507
+ | `ORCHESTRATOR_URL` | The URL of a single orchestrator (used if `ORCHESTRATORS_CONFIG` is not set). | `http://localhost:8080` |
508
+ | `ORCHESTRATORS_CONFIG` | A JSON string with a list of orchestrators for multi-orchestrator modes. | `[]` |
509
+ | `MULTI_ORCHESTRATOR_MODE` | The mode for handling multiple orchestrators. Possible values: `FAILOVER`, `ROUND_ROBIN`. | `FAILOVER` |
510
+ | `MAX_CONCURRENT_TASKS` | The maximum number of tasks the worker can execute simultaneously. | `10` |
511
+ | `COST_PER_SKILL` | A JSON string mapping skill names to their cost per second. | `{}` |
512
+ | `CPU_CORES` | The number of CPU cores available to the worker. | `4` |
513
+ | `GPU_MODEL` | The model of the GPU available to the worker (e.g., "RTX 4090"). | - |
514
+ | `GPU_VRAM_GB` | The amount of VRAM in GB for the GPU. | `0` |
515
+ | `INSTALLED_SOFTWARE` | A JSON string representing a dictionary of installed software and their versions. | `{"python": "3.9"}` |
516
+ | `INSTALLED_MODELS` | A JSON string representing a list of dictionaries with information about installed models. | `[]` |
517
+ | `HEARTBEAT_INTERVAL` | The interval in seconds between heartbeats to the orchestrator. | `15` |
518
+ | `WORKER_HEARTBEAT_DEBOUNCE_DELAY` | The delay in seconds for debouncing immediate heartbeats after a state change. | `0.1` |
519
+ | `WORKER_ENABLE_WEBSOCKETS` | Enable (`true`) or disable (`false`) WebSocket support for real-time commands. | `false` |
520
+ | `RESULT_MAX_RETRIES` | The maximum number of times to retry sending a task result if it fails. | `5` |
521
+ | `RESULT_RETRY_INITIAL_DELAY` | The initial delay in seconds before the first retry of sending a result. | `1.0` |
522
+ | `TASK_POLL_TIMEOUT` | The timeout in seconds for polling for new tasks. | `30` |
523
+ | `TASK_POLL_ERROR_DELAY` | The delay in seconds before retrying after a polling error. | `5.0` |
524
+ | `IDLE_POLL_DELAY` | The delay in seconds between polls when the worker is idle. | `0.01` |
525
+ | `WORKER_PAYLOAD_DIR` | The directory for temporarily storing files when working with S3. | `/tmp/payloads` |
526
+ | `S3_ENDPOINT_URL` | The URL of the S3-compatible storage. | - |
527
+ | `S3_ACCESS_KEY` | The access key for S3. | - |
528
+ | `S3_SECRET_KEY` | The secret key for S3. | - |
529
+ | `S3_DEFAULT_BUCKET` | The default bucket name for uploading results. | `avtomatika-payloads` |
530
+
531
+ ## Development
532
+
533
+ To install the necessary dependencies for running tests, use the following command:
534
+
535
+ ```bash
536
+ pip install .[test]
537
+ ```