Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 4 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ mypy taskmaestro # type check (strict mode)
| `taskmaestro/workflow.py` | `Workflow` (linear + DAG), `WorkflowBuilder`, validation (cycles, types, fan-in) |
| `taskmaestro/dependencies.py`, `taskmaestro/mapping.py` | Task handles, output references, `collect()`, mapped-task configuration |
| `taskmaestro/workflow_task.py` | Nested workflows wrapped as tasks |
| `taskmaestro/yaml_config.py` | YAML parsing, task imports, workflow and input validation |
| `taskmaestro/cli.py`, `taskmaestro/discovery.py` | CLI (`run`, `validate`, `graph`, `tasks list/describe`, `workflow describe`), plugin entry-point discovery |
| `taskmaestro/yaml_config.py` | YAML parsing, task imports, workflow and input validation; `load_registered_workflow` pairs an entry-point workflow with an input file |
| `taskmaestro/cli.py`, `taskmaestro/discovery.py` | CLI (`run`, `validate`, `graph`, `tasks list/describe`, `workflow list/describe`; workflow arguments accept a YAML path or a registered workflow name), plugin entry-point discovery (`load_registry` collects broken entry points instead of raising) |
| `taskmaestro/introspection.py` | Public JSON helpers: `describe_workflow`, `describe_yaml_workflow`, `describe_registered_workflow`, `task_descriptor`, `TaskSchemaGenerator` (subclass and override `type_annotations` to add schema keys), `jsonable`, `error_payload` |
| `taskmaestro/definition.py` | Editable graph form of workflow YAML: `load_definition`, `definition_from_workflow`, `write_definition` (atomic, optional `.bak`, `value_filter`); raises `DefinitionError` |
| `taskmaestro/job.py` | `Job[C]`, `JobStatus`, `TaskStatus`, `TaskResult` dataclass |
| `taskmaestro/runner.py` | `Runner` — topological execution, timeout via `signal.alarm`, hook dispatch |
| `taskmaestro/hooks/base.py` | `Event` StrEnum, `Hook` protocol, `BaseHook` no-op base |
Expand Down
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,7 @@ WorkflowRunnerError (base)
│ └── IncompleteInputError # Missing fan-in field mappings
├── JobStateError # e.g., re-running a completed job
├── ConfigLoadError # YAML config loading failure
│ └── WorkflowLookupError # Unknown or broken registered workflow name
└── TaskExecutionError # Runtime task failure
├── MappedTaskExecutionError # One or more mapped items failed
├── TaskOutputTypeError # Output type mismatch
Expand All @@ -635,15 +636,35 @@ WorkflowRunnerError (base)

## Command-Line Interface

Installed packages provide a `taskmaestro` command for YAML workflows:
Installed packages provide a `taskmaestro` command for YAML and registered workflows:

```bash
taskmaestro validate workflow.yaml --input input.yaml
taskmaestro graph workflow.yaml --input input.yaml
taskmaestro run workflow.yaml --input input.yaml --log-level INFO
taskmaestro workflow describe workflow.yaml --json
taskmaestro workflow list --json
```

Wherever a workflow path is accepted, you can also give the name of a workflow
registered in the `taskmaestro.workflows` entry-point group (see
[Plugin discovery](#plugin-discovery)):

```bash
taskmaestro workflow describe resinsight.completions --input input.yaml --json
taskmaestro run resinsight.completions --input input.yaml --json
```

The argument is a YAML path if it ends in `.yaml`/`.yml` or names an existing
file; otherwise it is looked up as a registered name. An unknown or broken name
is a configuration error whose message lists the registered workflows. Input
files work the same for both kinds, but registered workflows always run with a
default `Runner` and `ExecutionContext`; their root tasks take input values only
for declared `config_fields`. `workflow list` prints the registered names (one
per line, or `{"workflows": [...]}` with `--json`) without importing the
plugins, so one broken plugin does not break the list. In Python, use
`load_registered_workflow(name, input_path)`.

By default, `run` prints the final output as JSON and reports errors on stderr;
`validate` prints a human-readable confirmation. `graph` prints Mermaid markup.

Expand All @@ -669,7 +690,9 @@ failures have status `failed` and `code: "task_failed"`. If a completed result
cannot be encoded as JSON (e.g. a Python-only object), `run --json` returns
`code: "serialization_error"`. Missing configuration fields produce `issues`
with code `missing`; when no structured field metadata is available, `field`
is `null`. Use text mode when you need the original exception message.
is `null`. Use text mode when you need the original exception message. The
exception is an unknown or broken registered workflow name: its `message`
names the registered workflows.

Exit codes: `0` success, `1` task or result-serialization failure, `2` workflow
configuration failure. In JSON mode logs and ordinary Python `print()` output
Expand All @@ -686,6 +709,8 @@ instance includes its `name`, `python_type`, `depends_on` references (`task` /
and Pydantic input/output schemas. Collection dependencies include their kind
(`positional` or `keyed`) and member references. `python_type` identifies the
loaded class; it may differ from the plugin entry-point identifier used in YAML.
`source` tells where the workflow came from: `{"kind": "yaml", "path": "/abs/workflow.yaml"}`
or `{"kind": "entry_point", "name": "resinsight.completions"}`.

Pass `--input input.yaml` to check the input against the workflow. The input
may be incomplete (for example a template whose runtime-picker fields a UI
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "taskmaestro"
version = "0.3.0"
version = "0.4.0"
description = "A Python 3.12+ library for defining and executing typed DAG task workflows"
readme = "README.md"
requires-python = ">=3.12"
Expand All @@ -24,7 +24,7 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
"pydantic>=2.0,<3.0",
"pydantic>=2.4,<3.0",
"pyyaml>=6.0,<7.0",
]

Expand Down
8 changes: 7 additions & 1 deletion taskmaestro/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Workflow Runner: typed DAG task workflows with Pydantic models."""

__version__ = "0.3.0"
__version__ = "0.4.0"

from taskmaestro.context import ExecutionContext
from taskmaestro.dependencies import OutputHandle, TaskHandle, collect
Expand All @@ -17,6 +17,7 @@
from taskmaestro.exceptions import (
ConfigLoadError,
CycleDetectedError,
DefinitionError,
IncompleteInputError,
JobStateError,
MappedTaskExecutionError,
Expand All @@ -25,6 +26,7 @@
TaskOutputTypeError,
TaskTimeoutError,
WorkflowDefinitionError,
WorkflowLookupError,
WorkflowRunnerError,
WorkflowTaskError,
)
Expand All @@ -38,6 +40,7 @@
from taskmaestro.workflow_task import workflow_task
from taskmaestro.yaml_config import (
LoadedWorkflow,
load_registered_workflow,
load_workflow_from_yaml,
run_workflow_from_yaml,
)
Expand All @@ -47,6 +50,7 @@
"WORKFLOW_ENTRY_POINT_GROUP",
"ConfigLoadError",
"CycleDetectedError",
"DefinitionError",
"EmptyConfig",
"ExecutionContext",
"HookError",
Expand All @@ -73,11 +77,13 @@
"Workflow",
"WorkflowBuilder",
"WorkflowDefinitionError",
"WorkflowLookupError",
"WorkflowRunnerError",
"WorkflowTaskError",
"collect",
"get_registered_task",
"get_registered_workflow",
"load_registered_workflow",
"load_workflow_from_yaml",
"registered_task_names",
"registered_tasks",
Expand Down
Loading
Loading