For developers

A small Python library. Bring your own models.

The core needs no web server, database server, or hosted account. Give it a decision-model adapter and, for optimization, a completion function for the LLM you choose. The web workspace is optional.

pip install "decision-flywheel @ git+https://github.com/AnthusAI/Cyclotron.git"
Decision model
Jev · TypeSafe System One
Decision model
Kev · System One over HTTP
Decision model
Laya · runs locally
Decision model
System One clients · any Jev- or Kev-style client
Optimizer LLM
OpenAI · opt-in only
Optimizer LLM
LiteLLM · one interface to many providers

Another provider needs one small adapter class. MIT license. Python 3.10 or later.

import asyncio

from decision_flywheel import ClassifierConfig, DecisionFlywheel
from decision_flywheel.classifier_config import ClassifiedAnswers
from decision_flywheel.models import DecisionResult, DecisionTask, Item


class ScriptedModel:
    """Replace this with JevAdapter, KevAdapter, LayaAdapter, or your adapter."""
    model_identity = "scripted-v1"

    async def classify(self, config, target, training, *, now=None, event_sink=None):
        answer = DecisionResult("include", {"include": 0.8, "exclude": 0.2})
        return ClassifiedAnswers({"decision": answer}, self.model_identity, {}, 0)


task = DecisionTask("library", ("include", "exclude"), "Should this item be included?")
wheel = DecisionFlywheel("local.sqlite3", ClassifierConfig(task), ScriptedModel())
prediction = asyncio.run(wheel.predict(Item("item-1", {"text": "A useful paper"}), ()))
print(prediction.label, prediction.confidence)  # include 0.8
print(wheel.history()[-1]["kind"])             # prediction
wheel.close()
The quickstart from the Cyclotron README. Cyclotron's test suite runs this exact code.

The shape of it

Cyclotrons, classifiers, and the loop around them.

A cyclotron is an ordered group of classifiers, each with its own rubric and examples, supporting questions, and ML model.
From the Cyclotron repository.

One item through several classifiers.

All classifiers share one decision-model request. In a store created with sharing, the default for new stores, a supporting question that several classifiers need is asked once and feeds each one's ML model. Review routing is per classifier: each is selected on its own confidence and its own audit sample, and one checklist review can label several. Each classifier has its own store, rubric and ML model. Watch one item go through them →

Learning after the review.

Open a store with learning="deferred" and review() only records the labels and queues a learning pass for each labeled classifier, then returns. await cyclotron.learn() runs the queue, each classifier at most once per call, and pending_learning() lists what is waiting. With an asynchronous optimizer transport, the store is free while a pass waits on the optimizer, so reviews and decisions keep completing. The default is inline learning, which runs inside the review.

If a label the pass rests on is corrected, or a classifier's criteria change, during the wait, the pass is superseded: it is recorded, stays queued and runs again, up to a cap (max_superseded_passes). If a pass is interrupted, its own paid request is recorded as abandoned, and a repeat charge is possible; it is never silently re-paid.

your appcyclotronLLMoptimizerlearning="deferred"a pass landsreview()

Illustrative example. Deferred learning is opt-in; the default is inline learning, inside the review.