diff --git a/.gitea/workflows/publish.yml b/.gitea/workflows/publish.yml index e111f00..37b6430 100644 --- a/.gitea/workflows/publish.yml +++ b/.gitea/workflows/publish.yml @@ -42,4 +42,15 @@ jobs: - name: Publish env: UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }} - run: uv publish dist/* + # The worker first: `fluksio` depends on it, and for the seconds + # between two uploads the other order leaves a dependency that cannot + # be resolved — which on a first release is the whole of the window in + # which the package exists at all. + # + # `--check-url` makes a re-run skip what is already up there rather + # than failing on it, so a job that died between the two uploads can + # simply be run again. A version on PyPI can never be replaced, and + # this is the difference between retrying and burning a version. + run: | + uv publish --check-url https://pypi.org/simple/ dist/fluksio_worker-* + uv publish --check-url https://pypi.org/simple/ dist/fluksio-0* diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..86f7eca --- /dev/null +++ b/backend/README.md @@ -0,0 +1,76 @@ +# Fluksio + +A node-based automation engine: flows, dashboards and batch runs, in one +resident process with no infrastructure behind it. + +```sh +pip install fluksio +fluksio serve +``` + +That is the whole installation — no Docker, no database server, no ports to +open. It keeps a SQLite database, a git repository of your flows and an +artifact store under `~/.fluksio`, and prints an admin password once. + +## For data science + +Your functions become nodes where they already live. Install Fluksio into the +environment you work in and your nodes run on it — the packages are already +there: + +```python +# myresearch/train.py +import fluksio +from fluksio import Port, node + +@node( + requires=["dataset", Port("lr", "float")], + provides=[Port("loss", "float", stream=True), Port("weights", "artifact")], + device="gpu", device_policy="prefer", +) +def fit(dataset, lr, epochs=25): + for epoch in range(epochs): + loss = step(...) + yield {"loss": loss} # published as it happens, kept as a series + return {"weights": fluksio.save_artifact("weights.pt")} +``` + +```python +# myresearch/pipeline.py +from fluksio import Flow, Port +from myresearch.train import fit + +train = Flow("train", nodes=[prepare, fit, evaluate], + inputs=[Port("lr", "float", initial=0.01)], outputs=["score"]) +``` + +```sh +fluksio login --url http://127.0.0.1:8000 +fluksio sync myresearch +fluksio run train --lr 0.05 --wait +``` + +The decorators return your functions untouched, so everything stays callable, +testable and importable as what it was. A metric leaves through a declared +port rather than a logging call, which is why there is no `log_metric()`: the +run keeps the whole series, a chart can bind to it, and a downstream node can +consume it. + +## What else it does + +- **Flows** — typed messages between nodes, wired by name, edited on a canvas + or declared in code. Every change is a commit in a git repository you own. +- **Runs** — an experiment and a CI-style job are the same entity. Parameters, + seed, result, per-node timings, artifacts and the commit it ran at. +- **Dashboards** — charts and controls bound to the same messages the flows + carry, with no separate metrics pipeline. +- **Remote workers** — `pip install fluksio-worker` on the GPU box; it dials + *out* over one websocket, so nothing there has to be reachable. + +## Links + +- Documentation: +- Getting started (data science): +- Home: + +Python 3.10 or newer, Linux or macOS. diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 7928805..d8c4804 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -2,7 +2,25 @@ name = "fluksio" version = "0.1.0" description = "Node-based automation engine: flows, dashboards, batch runs" +readme = "README.md" requires-python = ">=3.10" +authors = [{ name = "Fluksio", email = "stroblme@posteo.de" }] +keywords = ["automation", "workflow", "dataflow", "experiment-tracking", "mlops"] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "Operating System :: MacOS", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Home Automation", + "Topic :: Scientific/Engineering", + "Topic :: System :: Distributed Computing", +] dependencies = [ "fastapi[standard]<1.0.0,>=0.114.2", "python-multipart<1.0.0,>=0.0.7", @@ -32,6 +50,11 @@ dependencies = [ "uv>=0.5", ] +[project.urls] +Homepage = "https://fluksio.com" +Documentation = "https://docs.fluksio.com" +"Getting started" = "https://docs.fluksio.com/getting-started/data-science/" + [project.scripts] fluksio = "fluksio.cli:main" diff --git a/worker/pyproject.toml b/worker/pyproject.toml index 19e9f56..47c213d 100644 --- a/worker/pyproject.toml +++ b/worker/pyproject.toml @@ -4,10 +4,31 @@ version = "0.1.0" description = "Runs Fluksio nodes on a machine the engine cannot reach" readme = "README.md" requires-python = ">=3.10" +authors = [{ name = "Fluksio", email = "stroblme@posteo.de" }] +keywords = ["automation", "workflow", "distributed", "worker", "gpu"] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "Operating System :: MacOS", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Home Automation", + "Topic :: Scientific/Engineering", + "Topic :: System :: Distributed Computing", +] dependencies = [ "websockets>=12", ] +[project.urls] +Homepage = "https://fluksio.com" +Documentation = "https://docs.fluksio.com/code/workers/" + [project.scripts] fluksio-worker = "fluksio_worker.agent:main"