Skip to content

Commit 232474e

Browse files
committed
Merge remote-tracking branch 'origin/main'
2 parents 29a59b8 + 347f52d commit 232474e

129 files changed

Lines changed: 1851 additions & 549 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/checks.yml‎

Lines changed: 25 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -11,63 +11,31 @@ permissions:
1111
packages: write
1212

1313
jobs:
14-
experiment-tools:
15-
name: Test experiment tools
16-
runs-on: ubuntu-latest
17-
steps:
18-
- uses: actions/checkout@v4
19-
- uses: actions/setup-python@v5
20-
with:
21-
python-version: "3.12"
22-
- name: Run Python tests
23-
run: python -m unittest discover -s experiments -p 'test_*.py'
14+
checks:
15+
name: Central AkôFlow example CI
16+
uses: UFFeScience/akoflow/.github/workflows/examples-ci.yml@main
17+
with:
18+
examples_ref: ${{ github.event.pull_request.head.sha || github.sha }}
19+
publish: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
20+
permissions:
21+
contents: read
22+
packages: write
2423

25-
showcase:
26-
name: Validate ${{ matrix.showcase }}
24+
notify-documentation:
25+
name: Notify AkôFlow documentation
26+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
2727
runs-on: ubuntu-latest
28-
strategy:
29-
fail-fast: false
30-
matrix:
31-
include:
32-
- {category: machine-learning, showcase: training-with-mlflow}
33-
- {category: machine-learning, showcase: hyperparameter-search}
34-
- {category: machine-learning, showcase: batch-inference}
35-
- {category: machine-learning, showcase: model-comparison}
36-
- {category: machine-learning, showcase: anomaly-detection}
37-
- {category: machine-learning, showcase: computer-vision}
38-
- {category: machine-learning, showcase: model-ensemble}
39-
- {category: machine-learning, showcase: model-distillation}
40-
- {category: machine-learning, showcase: inference-benchmark}
41-
- {category: machine-learning, showcase: lora-fine-tuning}
42-
- {category: machine-learning, showcase: model-conversion}
43-
- {category: machine-learning, showcase: infrastructure-selection}
44-
- {category: generative-ai, showcase: rag-indexing}
45-
- {category: generative-ai, showcase: synthetic-dataset}
46-
- {category: generative-ai, showcase: audio-transcription}
47-
- {category: generative-ai, showcase: document-extraction}
48-
- {category: agentic-workflows, showcase: bounded-agent}
49-
- {category: agentic-workflows, showcase: supervised-tool-loop}
50-
- {category: agentic-workflows, showcase: multi-agent-review}
51-
- {category: scientific-ai, showcase: ai-assisted-analysis}
52-
- {category: scientific-ai, showcase: surrogate-model}
53-
- {category: scientific-ai, showcase: experiment-feedback-loop}
5428
steps:
55-
- uses: actions/checkout@v4
56-
- name: Execute regression fixture
57-
working-directory: ${{ matrix.category }}/${{ matrix.showcase }}
58-
run: ./run.sh
59-
- uses: docker/login-action@v3
60-
if: github.event_name == 'push'
61-
with:
62-
registry: ghcr.io
63-
username: ${{ github.actor }}
64-
password: ${{ secrets.GITHUB_TOKEN }}
65-
- uses: docker/build-push-action@v6
66-
with:
67-
context: ${{ matrix.category }}/${{ matrix.showcase }}
68-
file: ${{ matrix.category }}/${{ matrix.showcase }}/docker/Dockerfile
69-
push: ${{ github.event_name == 'push' }}
70-
tags: |
71-
ghcr.io/uffescience/akoflow-showcase-${{ matrix.showcase }}:latest
72-
ghcr.io/uffescience/akoflow-showcase-${{ matrix.showcase }}:v1
73-
ghcr.io/uffescience/akoflow-showcase-${{ matrix.showcase }}:v1.0.0
29+
- name: Dispatch documentation rebuild
30+
env:
31+
GH_TOKEN: ${{ secrets.AKOFLOW_DOCS_DISPATCH_TOKEN }}
32+
SOURCE_SHA: ${{ github.sha }}
33+
run: |
34+
if [ -z "$GH_TOKEN" ]; then
35+
echo "AKOFLOW_DOCS_DISPATCH_TOKEN is not configured; the daily reconciliation will pick up this change."
36+
exit 0
37+
fi
38+
gh api repos/UFFeScience/akoflow/dispatches \
39+
--method POST \
40+
--field event_type=examples-updated \
41+
--field "client_payload[source_sha]=$SOURCE_SHA"

‎AGENTS.md‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Repository working conventions
2+
3+
## Branch names
4+
5+
- Every change associated with a GitHub issue must use the exact branch name
6+
`AKO-<N>`, where `<N>` is that issue's numeric identifier.
7+
- Examples: issue `#5` uses `AKO-5`; issue `#142` uses `AKO-142`.
8+
- Do not add personal, tool, or category prefixes such as `codex/`, `feat/`, or
9+
`docs/` to issue branches.
10+
- Create the issue before creating its implementation branch so the identifier
11+
is authoritative.
12+

‎README.md‎

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ assets, and validation workflows can evolve independently from the engine.
1111

1212
- `simulation/`, `local/`, `kind/`, `slurm/`, and `real/`: execution and
1313
infrastructure examples.
14-
- `machine-learning/`, `generative-ai/`, `agentic-workflows/`, and
15-
`scientific-ai/`: complete showcase workflows.
14+
- `machine-learning/`, `generative-ai/`, `document-ai/`, `media/`, `rendering/`,
15+
`agentic-workflows/`, and `scientific-ai/`: complete showcase workflows.
1616
- `connections/`, `onboarding/`, and `server-instance/`: reusable connection
1717
and installation templates.
1818
- `experiments/`: Python and Node.js tools used to launch, collect, analyze,
@@ -23,4 +23,30 @@ own directory with `./run.sh`. Most API-driven examples expect an AkôFlow
2323
daemon at `http://localhost:8080` and accept the usual AkôFlow environment
2424
variables for endpoint and token overrides.
2525

26+
Showcase workflow documents declare an OCI image and a structured command for
27+
each activity. They never invoke `docker run`: AkôFlow owns image delivery and
28+
container startup for the selected runtime. The local `run.sh` fixture executes
29+
the deterministic workload directly, while CI builds and validates the image as
30+
a separate concern.
31+
2632
Product documentation is published at [akoflow.com](https://akoflow.com).
33+
34+
New runnable examples follow the [showcase standard](SHOWCASE_STANDARD.md):
35+
offline smoke execution, real computation, observable stages, stable artifact
36+
contracts, and optional larger profiles.
37+
38+
## Continuous integration
39+
40+
The example files live in this repository, while the reusable CI implementation
41+
is maintained in `UFFeScience/akoflow/.github/workflows/examples-ci.yml`. The
42+
small workflow in this repository passes the exact pull-request or `main`
43+
commit to that central workflow. Runnable showcases are discovered from their
44+
`docker/Dockerfile` and executable `run.sh`; adding a showcase does not require
45+
editing a duplicated matrix.
46+
47+
After a push to `main`, CI publishes validated showcase images to GHCR and
48+
notifies the AkôFlow documentation workflow. Configure the repository secret
49+
`AKOFLOW_DOCS_DISPATCH_TOKEN` with access to dispatch workflows in
50+
`UFFeScience/akoflow`. If the secret is temporarily unavailable, the daily
51+
documentation reconciliation still checks out this repository and validates
52+
all referenced example files.

‎SHOWCASE_STANDARD.md‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Runnable showcase standard
2+
3+
New end-to-end showcases must be reproducible workloads, not timing simulations.
4+
They follow this contract:
5+
6+
1. **Correct domain ownership.** Store the example under its technical domain
7+
(`generative-ai`, `document-ai`, `media`, `rendering`, and so on), never a
8+
marketing category.
9+
2. **Offline execution.** `run.sh` must complete without network access. Small
10+
fixtures belong in `data/`; large public datasets may have an explicit
11+
preparation script, checksum, license note, and deterministic local fallback.
12+
3. **Observable stages.** Each meaningful computation is a separate AkôFlow
13+
activity. Parallel work is expressed with `dependsOn`, and activities exchange
14+
artifacts only through the execution workspace.
15+
4. **Real computation.** Stages process data with the declared toolchain. Do not
16+
use `sleep` or precomputed outputs to imitate work.
17+
5. **Portable smoke profile.** The default run is CPU-compatible and small enough
18+
for CI. `AKOFLOW_PROFILE=demo`, `standard`, or `stress` may increase dataset
19+
size, frame count, model size, or repetitions without changing the DAG.
20+
6. **Stable result contract.** A successful run writes `outputs/manifest.json`
21+
with `showcase`, `status`, and `artifacts`. `validate.sh` checks semantics and
22+
non-empty artifacts, not only process exit status.
23+
7. **Container parity.** `docker/Dockerfile` contains every runtime dependency and
24+
invokes the same stage runner used by `run.sh`.
25+
8. **Complete AkôFlow inputs.** Every showcase includes `workflow.yaml`,
26+
`environment.yaml`, `scope.yaml`, and `topology.yaml` plus a concise README.
27+
9. **Safe optional integrations.** Hosted APIs, Ollama, GPU acceleration, and
28+
external storage are opt-in adapters. Secrets never appear in fixtures,
29+
commands, logs, or committed output.
30+
31+
The five reference implementations are AI Video Studio, Document Intelligence,
32+
Local RAG Factory, Video Intelligence, and Blender Render Farm.
33+
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
#!/bin/sh
22
set -eu
33
root=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
4-
image="akoflow-showcase-bounded-agent:dev"
54
rm -rf "$root/outputs" "$root/screenshots"
65
mkdir -p "$root/outputs" "$root/screenshots"
7-
docker build --tag "$image" --file "$root/docker/Dockerfile" "$root"
8-
docker run --rm --mount "type=bind,src=$root/outputs,dst=/outputs" "$image"
6+
python3 "$root/scripts/run_showcase.py" --output "$root/outputs"
97
node "$root/scripts/capture_screenshots.mjs"
108
"$root/validate.sh"
11-

‎agentic-workflows/bounded-agent/scripts/run_showcase.py‎

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,30 @@
11
#!/usr/bin/env python3
22
"""Deterministic, dependency-free executable for an AkôFlow showcase image."""
33
from __future__ import annotations
4-
import csv, hashlib, json, pickle, sys
4+
import argparse, csv, hashlib, json, pickle
55
from pathlib import Path
66

7-
root = Path("/app")
8-
config = json.loads((root / "scenario.json").read_text())
9-
out = Path(sys.argv[1] if len(sys.argv) > 1 else "/outputs")
7+
source_root = Path(__file__).resolve().parents[1]
8+
container_config = Path("/app/scenario.json")
9+
config_path = container_config if container_config.exists() else source_root / "data/input.json"
10+
config = json.loads(config_path.read_text())
11+
12+
parser = argparse.ArgumentParser()
13+
parser.add_argument("--stage", choices=config["stages"])
14+
parser.add_argument("--output", default="/outputs" if container_config.exists() else str(source_root / "outputs"))
15+
args = parser.parse_args()
16+
17+
stage = args.stage
18+
if stage is not None and stage not in config["stages"]:
19+
raise SystemExit(f"unknown stage: {stage}")
20+
out = Path(args.output)
1021
out.mkdir(parents=True, exist_ok=True)
1122
digest = hashlib.sha256(json.dumps(config, sort_keys=True).encode()).hexdigest()
12-
record = {"showcase": config["slug"], "digest": digest, "stages": config["stages"], "status": "success"}
13-
for name in config["artifacts"]:
23+
selected_stages = [stage] if stage is not None else config["stages"]
24+
record = {"showcase": config["slug"], "digest": digest, "stages": selected_stages, "status": "success"}
25+
if stage is not None:
26+
(out / f"stage-{stage}.json").write_text(json.dumps({**record, "stage": stage}, indent=2) + "\n")
27+
for name in config["artifacts"] if stage is None or stage == config["stages"][-1] else []:
1428
path = out / name
1529
if name.endswith(".json"):
1630
path.write_text(json.dumps({**record, "artifact": name}, indent=2) + "\n")
@@ -27,5 +41,7 @@
2741
path.write_text("# " + config["title"] + "\n\n" + json.dumps(record, indent=2) + "\n")
2842
else:
2943
path.write_text(json.dumps({**record, "artifact": name}) + "\n")
30-
(out / "manifest.json").write_text(json.dumps({**record, "artifacts": config["artifacts"]}, indent=2) + "\n")
31-
44+
if stage is None or stage == config["stages"][-1]:
45+
(out / "manifest.json").write_text(
46+
json.dumps({**record, "artifacts": config["artifacts"]}, indent=2) + "\n"
47+
)

‎agentic-workflows/bounded-agent/workflow.yaml‎

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,33 @@
11
name: showcase-bounded-agent
22
spec:
33
namespace: showcase
4-
# Published by .github/workflows/build-showcase.yml.
5-
image: ghcr.io/uffescience/akoflow-showcase-bounded-agent:v1
4+
# AkôFlow starts this OCI image; activities only declare commands inside it.
5+
image: ghcr.io/uffescience/akoflow-examples/showcase-bounded-agent:v1
66
activities:
77
- name: goal
88
runtime: local
9-
# The local executor launches this OCI image through Docker in run.sh.
10-
run: docker run --rm ghcr.io/uffescience/akoflow-showcase-bounded-agent:v1 goal
9+
command:
10+
entrypoint: python
11+
arguments: ["/app/run_showcase.py", "--stage", "goal", "--output", "."]
12+
workingDirectory: /workspace
1113
cpuLimit: "1"
1214
memoryLimit: 256Mi
1315
- name: agent
1416
runtime: local
15-
# The local executor launches this OCI image through Docker in run.sh.
16-
run: docker run --rm ghcr.io/uffescience/akoflow-showcase-bounded-agent:v1 agent
17+
command:
18+
entrypoint: python
19+
arguments: ["/app/run_showcase.py", "--stage", "agent", "--output", "."]
20+
workingDirectory: /workspace
1721
cpuLimit: "1"
1822
memoryLimit: 256Mi
1923
dependsOn:
2024
- goal
2125
- name: validator
2226
runtime: local
23-
# The local executor launches this OCI image through Docker in run.sh.
24-
run: docker run --rm ghcr.io/uffescience/akoflow-showcase-bounded-agent:v1 validator
27+
command:
28+
entrypoint: python
29+
arguments: ["/app/run_showcase.py", "--stage", "validator", "--output", "."]
30+
workingDirectory: /workspace
2531
cpuLimit: "1"
2632
memoryLimit: 256Mi
2733
dependsOn:
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
#!/bin/sh
22
set -eu
33
root=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
4-
image="akoflow-showcase-multi-agent-review:dev"
54
rm -rf "$root/outputs" "$root/screenshots"
65
mkdir -p "$root/outputs" "$root/screenshots"
7-
docker build --tag "$image" --file "$root/docker/Dockerfile" "$root"
8-
docker run --rm --mount "type=bind,src=$root/outputs,dst=/outputs" "$image"
6+
python3 "$root/scripts/run_showcase.py" --output "$root/outputs"
97
node "$root/scripts/capture_screenshots.mjs"
108
"$root/validate.sh"
11-

‎agentic-workflows/multi-agent-review/scripts/run_showcase.py‎

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,30 @@
11
#!/usr/bin/env python3
22
"""Deterministic, dependency-free executable for an AkôFlow showcase image."""
33
from __future__ import annotations
4-
import csv, hashlib, json, pickle, sys
4+
import argparse, csv, hashlib, json, pickle
55
from pathlib import Path
66

7-
root = Path("/app")
8-
config = json.loads((root / "scenario.json").read_text())
9-
out = Path(sys.argv[1] if len(sys.argv) > 1 else "/outputs")
7+
source_root = Path(__file__).resolve().parents[1]
8+
container_config = Path("/app/scenario.json")
9+
config_path = container_config if container_config.exists() else source_root / "data/input.json"
10+
config = json.loads(config_path.read_text())
11+
12+
parser = argparse.ArgumentParser()
13+
parser.add_argument("--stage", choices=config["stages"])
14+
parser.add_argument("--output", default="/outputs" if container_config.exists() else str(source_root / "outputs"))
15+
args = parser.parse_args()
16+
17+
stage = args.stage
18+
if stage is not None and stage not in config["stages"]:
19+
raise SystemExit(f"unknown stage: {stage}")
20+
out = Path(args.output)
1021
out.mkdir(parents=True, exist_ok=True)
1122
digest = hashlib.sha256(json.dumps(config, sort_keys=True).encode()).hexdigest()
12-
record = {"showcase": config["slug"], "digest": digest, "stages": config["stages"], "status": "success"}
13-
for name in config["artifacts"]:
23+
selected_stages = [stage] if stage is not None else config["stages"]
24+
record = {"showcase": config["slug"], "digest": digest, "stages": selected_stages, "status": "success"}
25+
if stage is not None:
26+
(out / f"stage-{stage}.json").write_text(json.dumps({**record, "stage": stage}, indent=2) + "\n")
27+
for name in config["artifacts"] if stage is None or stage == config["stages"][-1] else []:
1428
path = out / name
1529
if name.endswith(".json"):
1630
path.write_text(json.dumps({**record, "artifact": name}, indent=2) + "\n")
@@ -27,5 +41,7 @@
2741
path.write_text("# " + config["title"] + "\n\n" + json.dumps(record, indent=2) + "\n")
2842
else:
2943
path.write_text(json.dumps({**record, "artifact": name}) + "\n")
30-
(out / "manifest.json").write_text(json.dumps({**record, "artifacts": config["artifacts"]}, indent=2) + "\n")
31-
44+
if stage is None or stage == config["stages"][-1]:
45+
(out / "manifest.json").write_text(
46+
json.dumps({**record, "artifacts": config["artifacts"]}, indent=2) + "\n"
47+
)

‎agentic-workflows/multi-agent-review/workflow.yaml‎

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,43 @@
11
name: showcase-multi-agent-review
22
spec:
33
namespace: showcase
4-
# Published by .github/workflows/build-showcase.yml.
5-
image: ghcr.io/uffescience/akoflow-showcase-multi-agent-review:v1
4+
# AkôFlow starts this OCI image; activities only declare commands inside it.
5+
image: ghcr.io/uffescience/akoflow-examples/showcase-multi-agent-review:v1
66
activities:
77
- name: researcher
88
runtime: local
9-
# The local executor launches this OCI image through Docker in run.sh.
10-
run: docker run --rm ghcr.io/uffescience/akoflow-showcase-multi-agent-review:v1 researcher
9+
command:
10+
entrypoint: python
11+
arguments: ["/app/run_showcase.py", "--stage", "researcher", "--output", "."]
12+
workingDirectory: /workspace
1113
cpuLimit: "1"
1214
memoryLimit: 256Mi
1315
- name: reviewer
1416
runtime: local
15-
# The local executor launches this OCI image through Docker in run.sh.
16-
run: docker run --rm ghcr.io/uffescience/akoflow-showcase-multi-agent-review:v1 reviewer
17+
command:
18+
entrypoint: python
19+
arguments: ["/app/run_showcase.py", "--stage", "reviewer", "--output", "."]
20+
workingDirectory: /workspace
1721
cpuLimit: "1"
1822
memoryLimit: 256Mi
1923
dependsOn:
2024
- researcher
2125
- name: critic
2226
runtime: local
23-
# The local executor launches this OCI image through Docker in run.sh.
24-
run: docker run --rm ghcr.io/uffescience/akoflow-showcase-multi-agent-review:v1 critic
27+
command:
28+
entrypoint: python
29+
arguments: ["/app/run_showcase.py", "--stage", "critic", "--output", "."]
30+
workingDirectory: /workspace
2531
cpuLimit: "1"
2632
memoryLimit: 256Mi
2733
dependsOn:
2834
- reviewer
2935
- name: synthesis
3036
runtime: local
31-
# The local executor launches this OCI image through Docker in run.sh.
32-
run: docker run --rm ghcr.io/uffescience/akoflow-showcase-multi-agent-review:v1 synthesis
37+
command:
38+
entrypoint: python
39+
arguments: ["/app/run_showcase.py", "--stage", "synthesis", "--output", "."]
40+
workingDirectory: /workspace
3341
cpuLimit: "1"
3442
memoryLimit: 256Mi
3543
dependsOn:

0 commit comments

Comments
 (0)