{"id":42201569,"url":"https://github.com/plugboard-dev/plugboard","last_synced_at":"2026-01-27T00:26:27.221Z","repository":{"id":282571928,"uuid":"845106138","full_name":"plugboard-dev/plugboard","owner":"plugboard-dev","description":"Plugboard is an event driven modelling and orchestration framework in Python for simulating and driving complex processes with many interconnected stateful components.","archived":false,"fork":false,"pushed_at":"2026-01-21T13:24:11.000Z","size":5263,"stargazers_count":28,"open_issues_count":13,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-01-22T01:27:14.101Z","etag":null,"topics":["agentic-ai","ai","digital-twin","digital-twins","distributed-systems","event-driven","framework","graphs","llm","modelling","python","simulation"],"latest_commit_sha":null,"homepage":"https://docs.plugboard.dev","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/plugboard-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-08-20T15:37:21.000Z","updated_at":"2026-01-21T13:23:50.000Z","dependencies_parsed_at":"2025-03-29T21:24:53.948Z","dependency_job_id":"0e2a06c1-5ef4-4013-b098-f08ba7f2783a","html_url":"https://github.com/plugboard-dev/plugboard","commit_stats":null,"previous_names":["plugboard-dev/plugboard"],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/plugboard-dev/plugboard","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plugboard-dev%2Fplugboard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plugboard-dev%2Fplugboard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plugboard-dev%2Fplugboard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plugboard-dev%2Fplugboard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/plugboard-dev","download_url":"https://codeload.github.com/plugboard-dev/plugboard/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plugboard-dev%2Fplugboard/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28792989,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-26T21:49:50.245Z","status":"ssl_error","status_checked_at":"2026-01-26T21:48:29.455Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agentic-ai","ai","digital-twin","digital-twins","distributed-systems","event-driven","framework","graphs","llm","modelling","python","simulation"],"created_at":"2026-01-27T00:26:26.609Z","updated_at":"2026-01-27T00:26:27.212Z","avatar_url":"https://github.com/plugboard-dev.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cpicture align=\"center\"\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/plugboard-dev/plugboard/refs/heads/main/docs/assets/plugboard-logo-dark.svg\" width=\"65%\" height=\"auto\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://raw.githubusercontent.com/plugboard-dev/plugboard/refs/heads/main/docs/assets/plugboard-logo-light.svg\" width=\"65%\" height=\"auto\"\u003e\n    \u003cimg alt=\"Plugboard\" src=\"docs/assets/plugboard-logo.jpeg\" width=\"80%\" height=\"auto\"\u003e\n  \u003c/picture\u003e\n\u003c/div\u003e\n\n\u003cdiv align=\"center\" class=\"badge-section\"\u003e\n  \u003cbr\u003e\n  \u003ca href=\"https://pypi.org/project/plugboard/\" alt=\"PyPI version\"\u003e\n    \u003cimg alt=\"PyPI\" src=\"https://img.shields.io/pypi/v/plugboard?labelColor=075D7A\u0026color=CC9C4A\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.python.org/\" alt=\"Python versions\"\u003e\n    \u003cimg alt=\"Python\" src=\"https://img.shields.io/pypi/pyversions/plugboard?labelColor=075D7A\u0026color=CC9C4A\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/plugboard-dev/plugboard?tab=Apache-2.0-1-ov-file#readme\" alt=\"License\"\u003e\n    \u003cimg alt=\"License\" src=\"https://img.shields.io/github/license/plugboard-dev/plugboard?labelColor=075D7A\u0026color=CC9C4A\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/plugboard-dev/plugboard\" alt=\"Typed\"\u003e\n    \u003cimg alt=\"Typed\" src=\"https://img.shields.io/pypi/types/plugboard?labelColor=075D7A\u0026color=CC9C4A\"\u003e\u003c/a\u003e\n  \u003cbr\u003e\n  \u003ca href=\"https://github.com/plugboard-dev/plugboard/actions/workflows/lint-test.yaml\" alt=\"Lint and test\"\u003e\n    \u003cimg alt=\"Lint and Test\" src=\"https://github.com/plugboard-dev/plugboard/actions/workflows/lint-test.yaml/badge.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/plugboard-dev/plugboard/actions/workflows/github-code-scanning/codeql\" alt=\"CodeQL\"\u003e\n    \u003cimg alt=\"CodeQL\" src=\"https://github.com/plugboard-dev/plugboard/actions/workflows/github-code-scanning/codeql/badge.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://codecov.io/gh/plugboard-dev/plugboard\" \u003e\n    \u003cimg src=\"https://codecov.io/gh/plugboard-dev/plugboard/graph/badge.svg?token=4LU4K6TOLQ\"/\u003e\u003c/a\u003e\n  \u003cbr\u003e\n  \u003ca href=\"https://docs.plugboard.dev\" alt=\"Documentation\"\u003e\n    \u003cimg alt=\"Docs\" src=\"https://github.com/plugboard-dev/plugboard/actions/workflows/docs.yaml/badge.svg\"\u003e\u003c/a\u003e\n\u003c/div\u003e\n\n\u003chr\u003e\n\nPlugboard is an **event-driven modelling and orchestration framework** in Python for simulating and driving complex processes with many interconnected stateful components.\n\nYou can use it to **define models** in Python and **connect them together easily** so that data automatically moves between them. After running your model on a laptop, you can then scale out on multiple processors, or go to a compute cluster in the cloud.\n\nSome examples of what you can build with Plugboard include:\n\n- Digital twin models of complex processes:\n    - It can easily handle common problems in industrial process simulation like material recirculation;\n    - Models can be composed from different underlying components, e.g. physics-based simulations, machine-learning, AI models;\n- AI integrations:\n    - You can feed data to/from different LLMs using Plugboard components;\n    - Easily reconfigure and swap model providers for optimal performance.\n\n## 🖋️ Key Features\n\n- **Reusable classes** containing the core framework, which you can extend to define your own model logic;\n- Support for different simulation paradigms: **discrete time** and **event based**.\n- **YAML model specification** format for saving model definitions, allowing you to run the same model locally or in cloud infrastructure;\n- A **command line interface** for executing models;\n- Built to handle the **data intensive simulation** requirements of industrial process applications;\n- Modern implementation with **Python 3.12 and above** based around **asyncio** with complete type annotation coverage;\n- Built-in integrations for **loading/saving data** from cloud storage and SQL databases;\n- **Detailed logging** of component inputs, outputs and state for monitoring and process mining or surrogate modelling use-cases.\n\n## 🔌 Installation\n\nPlugboard requires Python \u003e= 3.12. Install the package with pip inside a virtual env as below.\n```shell\npython -m pip install plugboard\n```\n\nOptional integrations for different cloud providers can be installed using `plugboard[aws]`, `plugboard[azure]` or `plugboard[gcp]`.\n\nSupport for parallelisation can be installed using `plugboard[ray]`.\n\n## 🚀 Usage\n\n\u003ca href=\"https://colab.research.google.com/github/plugboard-dev/plugboard/blob/main/\"\u003e\u003cimg src=\"https://colab.research.google.com/assets/colab-badge.svg\"\u003e\u003c/a\u003e\n\nPlugboard is built to help you with two things: defining process models, and executing those models. There are two main ways to interact with plugboard: via the Python API; or, via the CLI using model definitions saved in yaml format.\n\n### Building models with the Python API\n\nA model is made up of one or more components, though Plugboard really shines when you have many! First we start by defining the `Component`s within our model. Components can have only inputs, only outputs, or both. To keep it simple we just have two components here, showing the most basic functionality. Each component has several methods which are called at different stages during model execution: `init` for optional initialisation actions; `step` to take a single step forward through time; `run` to execute all steps; and `destroy` for optional teardown actions.\n```python\nimport typing as _t\nfrom plugboard.component import Component, IOController as IO\nfrom plugboard.schemas import ComponentArgsDict\n\nclass A(Component):\n    io = IO(outputs=[\"out_1\"])\n\n    def __init__(self, iters: int, **kwargs: _t.Unpack[ComponentArgsDict]) -\u003e None:\n        super().__init__(**kwargs)\n        self._iters = iters\n\n    async def init(self) -\u003e None:\n        self._seq = iter(range(self._iters))\n\n    async def step(self) -\u003e None:\n        try:\n            self.out_1 = next(self._seq)\n        except StopIteration:\n            await self.io.close()\n\n\nclass B(Component):\n    io = IO(inputs=[\"in_1\"])\n\n    def __init__(self, path: str, **kwargs: _t.Unpack[ComponentArgsDict]) -\u003e None:\n        super().__init__(**kwargs)\n        self._path = path\n\n    async def init(self) -\u003e None:\n        self._f = open(self._path, \"w\")\n\n    async def step(self) -\u003e None:\n        out = 2 * self.in_1\n        self._f.write(f\"{out}\\n\")\n\n    async def destroy(self) -\u003e None:\n        self._f.close()\n```\n\nThere is also a `@component` decorator which simplifies creating `Component`s for small stateless transform type functions. A component instance can be created by calling the `.component` method of the object returned by the decorator. The wrapped function can be sync or async and will be called as the step method with the named inputs being passed in. Inputs must be specified matching function args. Outputs must be specified and the function must return a dictionary where the keys match the outputs.\n```python\n@component(inputs=[\"in_1\"], outputs=[\"out_1\"])\ndef pow2(in_1: int) -\u003e int:\n  return {\"out_1\": in_1 ** 2}\n\nresult = pow2(2)  # Preserves original function call -\u003e result = {\"out_1\": 4}\ncomp_pow2 = pow2.component(name=\"component-pow2\")\n```\n\nNow we take these components, connect them up as a `Process`, and fire off the model. Using the `Process` context handler takes care of calling `init` at the beginning and `destroy` at the end for all `Component`s. Calling `Process.run` triggers all the components to start iterating through all their inputs until a termination condition is reached. Simulations proceed in an event-driven manner: when inputs arrive, the components are triggered to step forward in time. The framework handles the details of the inter-component communication, you just need to specify the logic of your components, and the connections between them.\n```python\nfrom plugboard.connector import AsyncioConnector\nfrom plugboard.process import LocalProcess\nfrom plugboard.schemas import ConnectorSpec\n\nprocess = LocalProcess(\n    components=[A(name=\"component-a\", iters=5), B(name=\"component-b\", path=\"b.txt\"), comp_pow2],\n    connectors=[\n        AsyncioConnector(\n            spec=ConnectorSpec(source=\"component-a.out_1\", target=\"component-b.in_1\"),\n        ),\n        AsyncioConnector(\n            spec=ConnectorSpec(source=\"component-a.out_1\", target=f\"{comp_pow2.name}.in_1\"),\n        )\n    ],\n)\nasync with process:\n    await process.run()\n```\n\nVisually, we've created the model below, with Plugboard automatically handling the flow of data between the components.\n```mermaid\nflowchart LR\n  subgraph Process\n    direction LR\n    comp_a(A\u003cbr\u003e**component-a**)\n    comp_b(B\u003cbr\u003e**component-b**)\n    comp_pow2(pow2\u003cbr\u003e**component-pow2**)\n  end\n  comp_a -- out_1 --\u003e comp_b\n  comp_a -- out_1 --\u003e comp_pow2\n```\n\n### Executing pre-defined models on the CLI\n\nIn many cases, we want to define components once, with suitable parameters, and then use them repeatedly in different simulations. Plugboard enables this workflow with model specification files in yaml format. Once the components have been defined, the simple model above can be represented as follows. Components auto-generated with the `@component` decorator can be referenced by the name of the wrapped function.\n```yaml\n# my-model.yaml\nplugboard:\n  process:\n    args:\n      components:\n      - type: hello_world.A\n        args:\n          name: \"component-a\"\n          iters: 10\n      - type: hello_world.B\n        args:\n          name: \"component-b\"\n          path: \"./b.txt\"\n      - type: hello_world.pow2\n        args:\n          name: \"component-pow2\"\n      connectors:\n      - source: \"component-a.out_1\"\n        target: \"component-b.in_1\"\n      - source: \"component-a.out_1\"\n        target: \"component-pow2.in_1\"\n```\n\nWe can now run this model using the plugboard CLI with the command:\n```shell\nplugboard process run my-model.yaml\n```\n\n## 📖 Documentation\n\nFor more information including a detailed API reference and step-by-step usage examples, refer to the [documentation site](https://docs.plugboard.dev). We recommend diving into the [tutorials](https://docs.plugboard.dev/latest/examples/tutorials/hello-world/) for a step-by-step to getting started.\n\n## 🐾 Roadmap\n\nPlugboard is under active development, with new features in the works:\n\n- Support for strongly typed data messages and validation based on pydantic.\n- Support for different parallelisation patterns such as: single-threaded with coroutines, single-host multi process, or distributed with Ray in Kubernetes.\n- Data exchange between components with popular messaging technologies like RabbitMQ and Google Pub/Sub.\n- Support for different message exchange patterns such as: one-to-one, one-to-many, many-to-one etc via a broker; or peer-to-peer with http requests.\n\n## 👋 Contributions\n\nContributions are welcomed and warmly received! For bug fixes and smaller feature requests feel free to open an issue on this repo. For any larger changes please get in touch with us to discuss first. More information for developers can be found in [the contributing section](https://docs.plugboard.dev/latest/contributing/) of the docs.\n\n## ⚖️ Licence\n\nPlugboard is offered under the [Apache 2.0 Licence](https://www.apache.org/licenses/LICENSE-2.0) so it's free for personal or commercial use within those terms.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplugboard-dev%2Fplugboard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplugboard-dev%2Fplugboard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplugboard-dev%2Fplugboard/lists"}