A structured workflow runtime for Python and TypeScript.
Caskada runs ordinary functions as nodes in nested directed graphs. It provides explicit branching, structured fan-out and joining, retries, recovery, local concurrency, and typed execution results without depending on an LLM provider or application framework.
Install
pip install caskada
npm install caskada
Python 3.13 or newer is required. The TypeScript package ships ESM and CommonJS builds.
Python
import asyncio
from caskada import Context, Flow, node
@node
async def answer(context: Context) -> None:
question = context.state["question"]
context.state["answer"] = await model.answer(question)
async def main() -> None:
state = await Flow(answer).run({"question": "Why?"})
print(state["answer"])
asyncio.run(main())
TypeScript
import { Flow, node } from 'caskada'
interface State {
question: string
answer?: string
}
const answer = node<State>(async (context) => {
context.state.answer = await model.answer(context.state.question)
})
const state = await new Flow(answer).run({ question: 'Why?' })
console.log(state.answer)
Core Model
Nodes
node(handler) turns a function into one graph occurrence. A handler receives
one Context and returns no application value.
@node
def decide(context):
if needs_review(context.state):
context.emit("review")
Nodes connect target first:
decide.link(review, "review")
decide.link(publish) # unlabelled link
The equivalent TypeScript spelling is identical:
decide.link(review, 'review')
decide.link(publish)
Control
context.emit()selects the unlabelled link.context.emit("review")selects a named link.context.emit("work", item)also supplies the next branch'scontext.input.- A successful normal handler with no control call behaves like one implicit
emit(). context.end(value)creates a hard terminal for the current branch and bypasses links. It does not stop the handler function, so use a normalreturnwhen later statements should not run.
Several emit() or end() calls in one handler create an atomic fan-out.
Data
context.state is the one mutable top-level map shared by every branch in one
run. Caskada shallow-copies the caller's initial mapping once, so run() returns
the authoritative final state and does not mutate the caller's top-level map.
Nested values remain shared references.
context.input is the value carried by one branch. Omitted input forwards the
current input. Caskada preserves application values but does not validate their
schema or prove payload compatibility between links.
Flows And Combine
A Flow is a structured scope. It waits until all of its branches settle, then
optionally invokes one combine callback.
def combine(context, result):
context.state["total"] = sum(result.outputs)
context.emit()
batch = Flow(dispatch, combine=combine, concurrency=8)
Worker branches publish values with context.end(value). The combiner reads
those values through result.outputs. Zero combiner emissions preserve the
original terminals; one or more emissions replace them with new outward
continuations.
Results
await flow.run(initial_state) is the simple API. It returns the final state for
every completed run and raises RunError on failure. The error retains the
failed result and exposes a controlling application error through standard
native exception chaining.
flow.start(initial_state) returns a RunHandle whose result() method exposes
the completed or failed result, including state and settled terminals.
Learn
License
Caskada is licensed under the Mozilla Public License 2.0.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sley-0.0.1.tar.gz.
File metadata
- Download URL: sley-0.0.1.tar.gz
- Upload date:
- Size: 14.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"NixOS","version":"26.05","id":"yarara","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98a0287e51de1ccd4940de4560a586eb1bf8d7ea526002f0ffaa217679dc8dbc
|
|
| MD5 |
0821448447e511f93d6e6cff36dbe0e3
|
|
| BLAKE2b-256 |
f7c24defcea5eec893277619a416ffd729e1dbb7fb405d546d4b2bb3a2d5410a
|
File details
Details for the file sley-0.0.1-py3-none-any.whl.
File metadata
- Download URL: sley-0.0.1-py3-none-any.whl
- Upload date:
- Size: 14.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"NixOS","version":"26.05","id":"yarara","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
83b909ac4e4de10ec2ab379d5ef7f7cd588403d08222bb56623471cc521646a4
|
|
| MD5 |
95d02194002075a9ca4d5db24157e0e9
|
|
| BLAKE2b-256 |
186020e8d925802f03eb8c41f057026272e03557cc47ecd8a59b6c7665f61327
|