At Common Workflow
Description
At Common Workflow is a workflow management system that allows users to define and execute tasks in a directed acyclic graph (DAG) structure. It supports parallel execution and provides a context manager for managing task inputs and outputs.
Installation
To set up the project, follow these steps:
- Clone the repository:
git clone <repository-url> cd at-common-workflow
- Install the dependencies:
pip install -r requirements.txt
Usage
There are two ways to define tasks: using class inheritance or using the builder pattern.
Class-based Approach
from at_common_workflow.core.task.processing_task import ProcessingTask
from pydantic import BaseModel
class AddInputModel(BaseModel):
a: int
b: int
class AddOutputModel(BaseModel):
result: int
class AddTask(ProcessingTask[AddInputModel, AddOutputModel]):
def __init__(self, name: str):
super().__init__(
name=name,
input_model=AddInputModel,
output_model=AddOutputModel,
processor_function=self._execute
)
async def _execute(self, input: AddInputModel) -> AddOutputModel:
return AddOutputModel(result=input.a + input.b)
# Run workflow
from at_common_workflow.core.workflow.base import Workflow
workflow = Workflow()
task = AddTask("add_numbers")
workflow.add_task(task, argument_mappings={"a": 5, "b": 3}, result_mapping="result")
async for event in workflow.execute():
pass
print(workflow.context.get("result").result) # Output: 8
Builder Pattern Approach (Recommended)
from at_common_workflow.core.workflow.builder import WorkflowBuilder
from pydantic import BaseModel
class AddInputModel(BaseModel):
a: int
b: int
class AddOutputModel(BaseModel):
result: int
async def execute_add(input: AddInputModel) -> AddOutputModel:
return AddOutputModel(result=input.a + input.b)
# Create and execute workflow
workflow = (WorkflowBuilder()
.task("add_numbers")
.input_model(AddInputModel)
.output_model(AddOutputModel)
.processor(execute_add)
.arg("a", 5)
.arg("b", 3)
.output("result")
.build())
async for event in workflow.execute():
pass
print(workflow.context.get("result").result) # Output: 8
Tasks with No Output
For tasks that perform actions but don't need to store their result in the workflow context (like sending notifications or logging), you can call output() with no parameters:
workflow = (WorkflowBuilder()
.task("send_notification")
.input_model(NotificationInput)
# output_model is optional when not storing results
.processor(send_notification)
.arg("message", "Hello!")
.arg("recipient", "user@example.com")
.output() # Don't store the output
.build())
This will execute the task but won't store its result in the workflow context. This is useful for tasks that have side effects (like sending notifications) but don't produce meaningful output that other tasks need.
For tasks that don't produce meaningful output, you can also skip defining an output model entirely or explicitly set it to None:
# Task function that doesn't produce meaningful output
async def send_notification(input: NotificationInput) -> None:
print(f"Sending notification to {input.recipient}: {input.message}")
# Some side effect...
return None # or just omit the return statement
Note that output models are still required when you need to store results in the workflow context.
Tasks with No Input
The workflow system allows you to define tasks that don't require any input. This can be useful for tasks that:
- Generate data without external input (like current time, random values, etc.)
- Perform standalone operations not dependent on previous task outputs
- Initialize resources or set up environments
Creating Tasks with No Input
When defining a task with no input, you have two options for your processor function:
Option 1: Processor with Empty Input Parameter
You can define a processor function that has an input parameter, which will receive an empty dictionary ({}):
async def processor_with_empty_input(input: dict) -> YourOutputModel:
# The input parameter is an empty dict
# Process without input
return YourOutputModel(...)
Option 2: Processor with No Parameters
Alternatively, you can define a processor function with no parameters at all:
async def processor_with_no_parameters() -> YourOutputModel:
# No input parameter
# Process without input
return YourOutputModel(...)
Both approaches are valid and supported by the framework.
Building a Task with No Input
When using the workflow builder, simply omit the input_model method call:
workflow = (WorkflowBuilder()
.task("no_input_task")
# No input_model call here
.output_model(YourOutputModel)
.processor(processor_with_no_parameters) # or processor_with_empty_input
.output("result")
.build()
)
Example
Here is a complete example of a task with no input that returns the current time:
from pydantic import BaseModel
from at_common_workflow.core.workflow.builder import WorkflowBuilder
import asyncio
from datetime import datetime
# Define the output model
class TimeInfo(BaseModel):
current_time: str
# Define a processor function with no parameters
async def get_current_time() -> TimeInfo:
return TimeInfo(
current_time=datetime.now().strftime("%Y-%m-%d %H:%M:%S")
)
# Build and run the workflow
async def main():
workflow = (WorkflowBuilder()
.task("get_time")
.output_model(TimeInfo)
.processor(get_current_time)
.output("time_info")
.build()
)
# Execute workflow
async for event in workflow.execute():
pass
# Access the result
time_info = workflow.context.get("time_info")
print(f"Current time: {time_info.current_time}")
if __name__ == "__main__":
asyncio.run(main())
Notes
- Task functions without input parameters must still be async functions.
- All other task configuration (output model, progress model, etc.) remains the same.
- The framework continues to support legacy code that uses the empty dict approach.
Examples
Check out the examples directory for more in-depth examples:
example1_basic_workflow.py- Basic workflow setup and executionexample2_progress_updates.py- Using progress updates in tasksexample3_parallel_tasks.py- Running tasks in parallelexample4_nested_data.py- Working with nested data structuresexample5_error_handling.py- Handling errors in workflowsexample6_arithmetic_operations.py- Performing arithmetic operationsexample7_no_output.py- Tasks that don't store their outputexample8_no_input_parameter.py- Tasks with no input parameters
Metadata
Release files for at-common-workflow 1.5.8
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| at_common_workflow-1.5.8.tar.gz | 47.5 kB | Without provenance | Details for at_common_workflow-1.5.8.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| at_common_workflow-1.5.8-py3-none-any.whl | Python 3 | none | any | Details for at_common_workflow-1.5.8-py3-none-any.whl |
Total release size: 85.7 kB
Release files / at_common_workflow-1.5.8.tar.gz
| Download URL | at_common_workflow-1.5.8.tar.gz |
|---|---|
| Size | 47.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
58a51481898763273863c65de43af55dac74c8d530a7c434d42a48a40ce18249
|
|
BLAKE2b-256 checksum How to use checksums |
c37f937dc3df874ff896ef65e89915d6ab319d317f0510f081cdc9978c5ef337
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.2
|
Release files / at_common_workflow-1.5.8-py3-none-any.whl
| Download URL | at_common_workflow-1.5.8-py3-none-any.whl |
|---|---|
| Size | 38.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c065cd31ca21395e3185de02268d86fa612594ce62663b63a480e3c8ce480a0a
|
|
BLAKE2b-256 checksum How to use checksums |
bd727406e61c5fa3cff7089912b2dde84731030f84c035ac35514e2183aeaf5f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.2
|
Security for at-common-workflow 1.5.8
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.