Change log¶
Migrating from v0.5 or earlier? See the migration guide for before/after snippets covering every breaking change in v0.6–v0.7.
v0.8.2¶
Released on 05-06-2026
Added¶
Database-backed
ScenarioResultpersistence —SqlScenarioRepositorynow writes results to the database via the same dual-path strategy asDatabaseDataManager.Details
Results whose subclass implements the new
algomancy_scenario.persistence.SqlResultLayoutprotocol are written as real, externally queryable SQL tables (one per sub-table, namedresult__<session>__<scenario_id>__<sub>); everything else falls back to a JSON blob inalgomancy_scenario_runs.result_blobvia the new abstractto_json/from_jsoncontract onBaseScenarioResult. Typed rehydration on restart uses the algorithm’sresult_classattribute (defaults to the bundledScenarioResult).SqlScenarioRepository.deletecleans up any per-scenario result tables alongside the run/KPI rows.
Changed¶
Actualized the quickstart documentation page.
StatefulDataManagermarked deprecated;DatabaseDataManagershould be used for persistent deployments;StatelessDataManageris the default for in-memory use.BaseScenarioResultnow requiresto_json/from_json— abstract on the base, concrete on the bundledScenarioResult. Breaking for custom subclasses.Details
Add
to_json(self) -> strand afrom_json(cls, json_string) -> BaseScenarioResultclassmethod to existingBaseScenarioResultsubclasses, or implementalgomancy_scenario.persistence.SqlResultLayoutto land DataFrames in real SQL tables instead of a JSON blob. The contract mirrorsBaseDataSource.to_json/from_jsonexactly.BaseAlgorithm.result_class— new class attribute (defaults toScenarioResult) used by the repository to rehydrate persisted results into their original type; override on algorithms whoserun()returns a custom subclass.
Fixed¶
Fixed a bug where playwright tests would fail to initialize due to an erroneous version lookup.
Added documentation for
DatabaseDataManager.
v0.8.1¶
Released on 05-06-2026
Added¶
Data Parameters — per-scenario knobs declared by
BaseDataSourcesubclasses.Details
A concrete
BaseDataSourcesubclass can override the newinitialize_data_parameters()method to return a typedBaseParameterSetdescribing data-side knobs (date range, region filter, category whitelist, …). The framework collects user-supplied values per scenario, persists them alongside the algorithm parameters, and pushes them onto the algorithm viaBaseAlgorithm.set_data_paramsbeforerun(). Algorithms readself.data_paramsand decide whether and how to act on them — nothing is applied automatically.Surfaces:
BaseDataSource.initialize_data_parameters()— default returnsEmptyParameters(), so existing subclasses keep working unchanged.BaseAlgorithm.data_paramsproperty +set_data_params(...)method.Scenarioaccepts adata_paramskwarg and pushes it onto the algorithm before eachrun().ScenarioFactory.get_associated_parameters(algo_name, dataset_key)returns the(algo_params, data_params)tuple, resolving the data half via the selected data source.ScenarioManager.create_scenario(..., data_params=...)andScenarioManager.get_data_parameters(dataset_key).GUI: a second parameter card renders next to the algorithm card in the scenario-creation modal, populated as soon as the user picks a dataset.
API:
GET /api/v1/sessions/{sid}/data/{dataset_key}/parametersreturns the descriptor;POST /scenariosaccepts a newdata_paramsfield.Persistence: new nullable
data_parameter_values TEXTcolumn onalgomancy_scenarios, added idempotently viaALTER TABLEon startup so existing databases keep loading.
See Data Parameters for the data-source declaration pattern and Algorithms and Parameters for the algorithm-side read pattern.
SqlTableLayoutprotocol for database persistence for custom data sources.Details
DatabaseDataManagernow works for anyBaseDataSourcesubclass, not just the bundledDataSource. Two persistence paths are dispatched at write time:Subclasses that implement the new
algomancy_data.database.SqlTableLayoutprotocol (to_sql_tables() -> dict[str, DataFrame]andfrom_sql_tables(tables)) get per-sub-table SQL storage — DataFrames land in realds__{session}__{dataset}__{sub}tables and stay externally queryable.Subclasses that don’t implement it fall back to JSON-blob persistence via the abstract
to_json/from_jsonAPI already required byBaseDataSource. The catalogue grows a nullablepayloadcolumn to hold the blob.
The bundled
DataSourcesatisfiesSqlTableLayouttrivially via itstablesdict, so existing deployments see no behavioural change. Pre-existingalgomancy_datasetstables that are missing thepayloadcolumn now raise a clear error on startup directing the user at the manual-rebuild path. See Database persistence of custom data sources for the protocol and a worked example.
Added infrastructure to delete sessions.
New
SessionManager.delete_session(id)is used by a GUI interface element and theDELETE /api/v1/sessions/{session_id}API endpoint.
Breaking
Session identity is now a UUID; new property
display_nameis the mutable label.Details
Previously a session’s identifier was its user-facing string (
"main","alice_experiment"), which made renaming impossible and made URLs leak human-readable names. The new set-up features:Each session has a stable UUID
idand a separate mutabledisplay_name.API routes use UUIDs:
/api/v1/sessions/{session_uuid}/.... The list endpoint returns{sessions: [{id, display_name}, ...], default: id}.New
PATCH /api/v1/sessions/{id}renames a session (display_name only).CreateSessionRequest.nameis nowdisplay_name;CopySessionRequest.new_nameis nownew_display_name.Filesystem backend writes a small
meta.jsoninto each session directory carrying{id, display_name}. Directories withoutmeta.jsonare migrated transparently on first scan.DB backend changes
algomancy_sessionsfrom(name PK)to(id PK, display_name).SessionManagerAPI renames:sessions_names→session_ids,start_session_name→start_session_id, newlist_sessions(),get_display_name(),rename_session(),resolve_id_by_display_name().Migration: run
python scripts/migrate_m14_session_ids.py [--data-path DIR] [--database-url URL]once per deployment. The filesystem migration is also performed automatically on first scan, so upgrading without running the script will still work — the script just makes the rewrite eager and visible. For API clients, the resolver still accepts the olddisplay_namein URLs (e.g./sessions/main/...) as a soft alias, so existing integrations keep working while you migrate them to UUIDs.
Sessions are now always used
Details
The
CoreConfig.use_sessionsattribute was removed (and the same flag onApiConfiguration).SessionManageris now always constructed; single-tenant deployments simply get a single auto-created"main"session. The/healthendpoint no longer reportsuse_sessions.Migration: delete
use_sessions=...from yourCoreConfig/ApiConfiguration/AppConfigarguments. If you were usinguse_sessions=Falseto hide the session picker from the GUI admin page, setFeatureConfig(show_session_picker=False)instead — the runtime keeps a session under the hood either way.The GUI server attribute
app.server.use_sessionsis gone. Code that branched on it should useapp.server.session_managerunconditionally; code that gated picker visibility should useapp.server.show_session_picker.
v0.7.0¶
Added¶
algomancy-apipackage — new FastAPI HTTP service exposing the fullScenarioManager/SessionManagersurface over HTTP, so remote frontends can drive an Algomancy backend over the network.Details
The HTTP layer is deliberately thin — every route maps to a single manager method and responses reuse the existing
to_dict()payloads. There is no parallel domain model; clients work with the sameScenario,DataSource, andKPIconcepts the GUI does. Launch withalgomancy-api --config-callback myapp:make_config(default port8051) oralgomancy-api --examplefor the bundled wiring.ApiLauncher.build(cfg)returns a standardFastAPIinstance for use behind a production uvicorn/gunicorn process manager.Sessions router —
GET/POST /sessions,POST /sessions/{sid}/copywith session-name validation and conflict handling.Algorithm + KPI discovery router —
GET /sessions/{sid}/algorithms,GET /sessions/{sid}/algorithms/{name}/parameters(parameter descriptors derived fromBaseParameterSet), andGET /sessions/{sid}/kpis.Scenarios router — full CRUD plus
POST /sessions/{sid}/scenarios/{id}/run,GET /sessions/{sid}/scenarios/{id}/statusfor polling, andGET /sessions/{sid}/processingfor the currently running scenario.Data router — list, fetch, delete, derive,
POST /sessions/{sid}/data/from-jsonto add a dataset from aDataSource.to_json()payload, andPOST /sessions/{sid}/etlto run ETL over a multipart upload.Meta —
GET /healthliveness probe; OpenAPI schema at/openapi.jsonand Swagger UI at/docs.
Error mapping — single exception → HTTP-status map (
ValueError→ 400,ParameterError→ 400,AssertionError→ 409, anything else → 500); response shape is always{"detail": "<message>"}.End-to-end smoke test (
tests/test_smoke_live.py) drives the full session → scenario → run → poll → fetch flow against a live server.algomancy-scenariosurface —KPIFactory,ScenarioFactory, andScenarioManagernow expose algorithm/KPI template names for API discovery.Documentation — new “Frontends” fundamentals page and HTTP API reference (
docs/source/reference/api.md); refreshedalgomancy-apiREADME with quick-start and endpoint inventory.
Changed¶
SessionManagerwas lifted out ofalgomancy-guiintoalgomancy-scenarioso it can be shared by all three frontends without pulling in Dash.
Fixed¶
Bugfix to
BaseKPIsurfaced while wiring up the algorithms router.
0.6.0¶
Released on 2026-05-29
The 0.6.0 release is the first instalment of the algomancy-data ETL/schema overhaul (milestones M1–M8). The chain of changes is large but additive where possible; consult the migration guide for the breaking-change summary.
Added¶
Schema API modernization (M1) — new
Columndataclass gives schemas a declarative field model;Schemagainscolumns(),required_columns(),optional_columns(), andprimary_key()classmethods.Details
Columnfields:name,dtype,optional,primary_key,default,nullable,unique,description.Columnis exported from the publicalgomancy_dataAPI. Example schemas migrated to the Column style; legacy_DATATYPESdicts auto-convert with aDeprecationWarning.Structured validation framework (M2) —
ValidationMessagegains location fields;run_validationreturns a richValidationResultdataclass; new built-in validators added.Details
ValidationMessagegains optionaltable,column,row, andcodefields.__str__surfaces the location inline;__eq__/to_dict()added for inspection and serialisation.ValidationSequence.run_validationnow returns aValidationResultdataclass (is_valid,messages,halt_on,counts_by_severity, plusas_dataframe(),messages_by_severity(),messages_at_least(), and__bool__/__iter__/__len__helpers).ValidationSequence.halt_on(defaultCRITICAL) lets projects promoteERRORorWARNINGto a pipeline-halting severity without subclassing.New built-in validators:
RequiredColumnsValidator,OptionalColumnGuard(validator/transformer hybrid that injects missing optional columns with their declared default and dtype),PrimaryKeyValidator(joint-key uniqueness + non-null),UniqueValueValidator, andMissingValueValidator.
Predictable ETL termination (M3) —
ETLPipeline.run()returns anETLResultdataclass; data-quality failures surface cleanly rather than as exceptions.Details
ETLResultcarriesstatus,datasource,validation_result,raised, plusis_success/is_failure/messageshelpers. Data-quality failures arrive asETLResult(status='failed'); programmer errors (KeyError/AttributeError/TypeError) still propagate so real defects aren’t masked.DataManager.etl_data/ScenarioManager.etl_datareturn the result; auto-create only fires on success.DataTypeConverterrecordsConversionIssueobjects instead of silently producingNaNs; these surface asCONVERSION_FAILEDmessages.StatefulDataManager.startup()records per-item failures inself.startup_errorsand rolls back failing keys.Reduce ETL boilerplate (M4) — extractor registry and
SimpleETLFactorylet users build a full pipeline without subclassingETLFactory.Details
New extractor registry (
register_extractor,get_extractor_class,registered_keys) keyed on(FileExtension, SchemaType). The four built-in extractors (CSV single, JSON single, XLSX single, XLSX multi) populate it at import time.ETLFactorynow ships with default implementations ofcreate_extraction_sequence,create_validation_sequence,create_transformation_sequence, andcreate_loader— no longer@abstractmethod. NewSimpleETLFactory(schemas, transformers=None, loader=None, logger=None)is a concrete factory requiring no subclassing.DataManager.prepare_filesdispatches by the schema’s declared_EXTENSION, falling back to the path suffix.Flexibility & extension points (M5) —
register_extractoris now public; newForeignKeyValidatorandDataFrameExtractor; docs on extending the registry.Details
Schema.extension()accepts anyStrEnum-derived value or plain string, so user-definedFileExtensionsubclasses route through the registry without changes.ForeignKeyValidatorchecks every (non-null) value inleft_table[left_col]exists inright_table[right_col]; supports composite keys; emits structured messages withFK_VIOLATION,TABLE_NOT_FOUND, andCOLUMN_NOT_FOUNDcodes.DataFrameExtractorwraps a pre-built pandasDataFramevia the sameExtractorcontract. New docs pagefundamentals/extending.mdcovers subclassingFileExtension, registering an extractor, and extendingDataTypeConverter.Docs, tests, migration (M6) —
algomancy-datacoverage at 87%;docs/source/fundamentals/ETL.mdrewritten end-to-end; single migration guide added.Details
pytest-covadded;algomancy-datacoverage reports 87%.docs/source/fundamentals/ETL.mdopens with aSimpleETLFactoryquickstart, then covers Column-based schemas, the extractor registry, new validators, structured validation messages,ETLResult-based termination, and a full worked example. Newdocs/source/migration.mdcovers every breaking change in v0.6/0.7/0.8 with before/after snippets. Bundled example refreshed:ExampleETLFactoryinherits fromSimpleETLFactory, demonstratesOptionalColumnGuardandForeignKeyValidator.Relational cascade cleanup (M7) — declarative cascade-drop mechanism via
CascadeDropTransformerandCascadeSnapshot; drops surface as structuredValidationMessages.Details
Declare foreign keys via
Column(foreign_key=("parent_table", "parent_col")), opt parents into cascade withparent_requires_child=True, and optionallytrack_partial_loss=True.CascadeDropTransformerdrops orphans and parents-with-missing-children to a fixpoint;CascadeSnapshotenables partial-loss detection. Drop messages carry codesCASCADE_ORPHAN_DROP,CASCADE_REQUIRED_CHILD_DROP, andCASCADE_PARTIAL_LOSS_DROPatSeverity.ERROR.ForeignKeyValidator.from_schemas([...])auto-derives validators from the same declarations. Fully opt-in: pipelines without the transformer are unaffected.Quickstart modernization (M8) —
algomancy-quickstarttemplates updated to emit Column-based schemas,SimpleETLFactory-derived ETL factories, and new validation defaults out of the box.Quickstart Module (
algomancy-quickstart) — interactive setup wizard that guides new users through creating a full Algomancy application in five steps.Details
The
QuickstartWizardcovers: (1) creating folder structure and a basicmain.py; (2) generating implementation templates for schemas, algorithms, KPIs, ETL factories, and custom pages; (3) scanning data files and generating ETL pipelines with schema inference; (4) installing default assets (CSS, images) from GitHub or a bundled fallback; and (5) configuring custom styling with colors and themes. Features interactive prompts, intelligent file detection (CSV, XLSX, JSON), automatic datatype inference with column mapping, and Jinja2-based code generation.Added character-safe and existing scenario name checks (via
InputChecker), resulting in a disabled create button when invalid.Added
list_tagsfunction to the scenario registry, accessible viaScenarioManager.BaseAlgorithmnow has access to the application’s central logger throughself._logger.Added pages section to the tutorial.
Changed¶
Cleanup of dependencies between
algomancy-contentandalgomancy-gui.Details
[Breaking]
BasePageand subclasses moved toalgomancy_gui; update imports accordingly.LibraryManagermoved toalgomancy-guifromalgomancy-content.algomancy-guiinternals reorganized.
Refinement of
Schemaclass.Details
[Breaking]
_defined_datatypesmoved to_DATATYPESas a class attribute.Schemas no longer need to be instantiated before being passed to the configuration — passing types works; passing instances still works.[Breaking] Schema classproperties (
datatypes,datatype_groups,sub_names,file_name,extension) are now explicit@classmethods — call sites must use(). Theclasspropertyshim was removed;get_subschema()now returns a dynamic class rather than an instance, eliminating subschema instance state.
Made
dataset_name_invalidgeneric (name_invalid) for both datasets and scenarios.Moved
name_invalidcallback toinputchecker.pyto avoid duplicate code.Euro (€) is now the default quantity for money.
Split
AppConfigurationinto separate configuration containers for better organization. (backwards compatible)Implemented the Singleton pattern on
Loggerfor global access.BASE_DATA_BOUNDrenamed toBASEDATASOURCEfor legibility.fill_empty()intransformer.py: replaced deprecatedfillna(method=...)withDataFrame.ffill(axis=1).DataManagementImportModalnow readsETLResultand shows validation message counts on failure (back-compat catch onValidationErrorpreserved).
Fixed¶
0.4.4¶
Released on 23-2-2026
Added¶
Documentation updates: reference is now complete with some todos
0.4.3¶
Released on 18-2-2026
Added¶
Added
BaseDataSource._post_derive()method as a hook to allow subclasses to perform post-derivation processing.Added
_post_derive()PyTest to pre-merge testing protocol.
0.4.2¶
Released on 17-2-2026
Added¶
Added
test_datasource.pyto the PyTest pre-merge protocol.
Fixed¶
Fixed bug where
BaseDataSource.derive()would not updateds_typeandidappropriately.Fixed bug where
DataSource.from_json()would not preserve datatypes.
0.4.0¶
Released on 12-02-2026
Changed¶
[Breaking] Removed
InputFileConfigs, moved responsibility toSchemaclassUpdated documentation
Updated tutorial code (data, root files, assets, data handling, algorithms, KPIs)
Migration from InputFileConfiguration to Schema-only pattern¶
Prior to this release, file configurations were specified separately using SingleInputFileConfiguration and MultiInputFileConfiguration.
These have been removed, and the Schema class now contains all necessary file metadata.
Migration example
For single-file schemas (CSV, JSON, XLSX with one sheet):
Old version
from algomancy_data import Schema, FileExtension, DataType, SingleInputFileConfiguration
class WarehouseLayoutSchema(Schema):
ID = "slotid"
X = "x"
Y = "y"
ZONE = "zone"
@property
def datatypes(self) -> Dict[str, DataType]:
return {
WarehouseLayoutSchema.ID: DataType.STRING,
WarehouseLayoutSchema.ZONE: DataType.STRING,
WarehouseLayoutSchema.X: DataType.FLOAT,
WarehouseLayoutSchema.Y: DataType.FLOAT,
}
# File configuration was separate
warehouse_config = SingleInputFileConfiguration(
extension=FileExtension.CSV,
file_name="warehouse_layout",
file_schema=WarehouseLayoutSchema(),
)
New version
from algomancy_data import Schema, FileExtension, DataType
from algomancy_data.schema import SchemaType
class WarehouseLayoutSchema(Schema):
# File metadata now lives in the Schema class
_FILENAME = "warehouse_layout"
_EXTENSION = FileExtension.CSV
_SCHEMA_TYPE = SchemaType.SINGLE
ID = "slotid"
X = "x"
Y = "y"
ZONE = "zone"
def _defined_datatypes(self) -> Dict[str, DataType]:
return {
WarehouseLayoutSchema.ID: DataType.STRING,
WarehouseLayoutSchema.ZONE: DataType.STRING,
WarehouseLayoutSchema.X: DataType.FLOAT,
WarehouseLayoutSchema.Y: DataType.FLOAT,
}
# No separate configuration needed - just instantiate the schema
warehouse_schema = WarehouseLayoutSchema()
Key changes for single-file schemas:
Add
_FILENAME,_EXTENSION, and_SCHEMA_TYPE = SchemaType.SINGLEas class attributesRename
datatypesproperty to_defined_datatypes()methodRemove the separate
SingleInputFileConfigurationwrapper
For multi-sheet XLSX files:
Old version
from algomancy_data import Schema, FileExtension, DataType, MultiInputFileConfiguration
class StedenSchema(Schema):
COUNTRY = "Country"
CITY = "City"
@property
def datatypes(self) -> Dict[str, DataType]:
return {
StedenSchema.COUNTRY: DataType.STRING,
StedenSchema.CITY: DataType.STRING,
}
class KlantenSchema(Schema):
ID = "ID"
Name = "Naam"
@property
def datatypes(self) -> Dict[str, DataType]:
return {
KlantenSchema.ID: DataType.INTEGER,
KlantenSchema.Name: DataType.STRING,
}
# Separate configuration with multiple schemas
multisheet_config = MultiInputFileConfiguration(
extension=FileExtension.XLSX,
file_name="multisheet",
file_schemas={
"Steden": StedenSchema(),
"Klanten": KlantenSchema(),
},
)
New version
from algomancy_data import Schema, FileExtension, DataType
from algomancy_data.schema import SchemaType
class LocationSchema(Schema):
# File metadata in the schema
_FILENAME = "multisheet"
_EXTENSION = FileExtension.XLSX
_SCHEMA_TYPE = SchemaType.MULTI
# All column names from all sheets in one schema
# steden sheet
COUNTRY = "Country"
CITY = "City"
# klanten sheet
ID = "ID"
Name = "Naam"
def _defined_datatypes(self) -> Dict[str, Dict[str, DataType]]:
# Return a dict of sheet names to column datatypes
return {
"Steden": {
LocationSchema.COUNTRY: DataType.STRING,
LocationSchema.CITY: DataType.STRING,
},
"Klanten": {
LocationSchema.ID: DataType.INTEGER,
LocationSchema.Name: DataType.STRING,
},
}
# No separate configuration needed
location_schema = LocationSchema()
Key changes for multi-sheet schemas:
Combine all sheet schemas into a single
SchemaclassAdd
_FILENAME,_EXTENSION, and_SCHEMA_TYPE = SchemaType.MULTIas class attributesRename
datatypesproperty to_defined_datatypes()methodReturn
Dict[str, Dict[str, DataType]](sheet name → column datatypes) instead ofDict[str, DataType]Remove the separate
MultiInputFileConfigurationwrapper and individual sheet schemas
0.3.21¶
12-02-2026
Added¶
Added charactersafe & existing dataset name checks (in InputChecker Class), resulting in a disabled import button
Changed¶
Custom pages should now subclass the appropriate base classes (moved from
ProtocoltoAbstractBaseClass). Functional implementation should remain unchanged.
Fixed¶
Fixed a bug where the overview page failed to use the
OverviewPagecontent from the registry appropriately.Fixed risk of App breaking down when user tries to import/derive a new dataset with weird names (e.g. with .) or an already existing dataset name.
0.3.20¶
Fixed¶
StandardDataPagenow work again when sessions are enabled
0.3.17¶
Fixed¶
StandardHomePageandStandardDataPagenow work again when sessions are disabled
0.3.16¶
Changed¶
GuiLauncherwas moved toalgomancy-guiUpdated
numpydependency to2.4.1due to yanked version2.4.0
0.3.13¶
Added¶
Added
use_sessionsatribute toAppConfigurationto allow disabling sessions.
0.3.5 - 0.3.12¶
Revised distribution model; the package is now hosted at pypi.org/project/algomancy and installable with the terminal command:
uv add algomancy
0.3.4¶
Released at 07-01-2026
Added¶
Added
copy()toBaseParameterSetand corresponding tests.Introduced locking to
BaseParameterSetto prevent concurrent mutation.Added
get_parametersresponsibility across factories/facades and session manager.Work-in-progress Command Line Interface (CLI).
Changed¶
[Breaking] Renamed
BaseAlgorithmParameterstoBaseParameterSetand moved toalgomancy-utilsin preparation for data parametersMigration to UV workspaces for the monorepo structure.
Units/measurements refactor in
algomancy-utils: moved formatting toBaseMeasurement; removedUnit.name.Various import path updates across packages after workspace migration.
[Breaking] algomancy-data (ETL): Unified ETL pipeline concepts.
Introduced
ExtractionSequenceandTransformationSequenceas the orchestration primitives for extract and transform steps.ETLPipelinenow acceptsextraction_sequenceandtransformation_sequenceinstead of a dict ofextractorsand a list oftransformers.ETLFactoryabstract methods have been renamed:create_extractors(...)→create_extraction_sequence(...)create_transformers()→create_transformation_sequence()
Validation continues to use
ValidationSequenceunchanged.See updated examples in
example/data_handling/factories.pyand implementation in:packages/algomancy-data/src/algomancy_data/etl.pypackages/algomancy-data/src/algomancy_data/extractor.pypackages/algomancy-data/src/algomancy_data/transformer.py
Migration notes (ETL)¶
Replace factory method implementations and usages:
Implement
create_extraction_sequence(files)to build and return anExtractionSequence(usesequence.add_extractor(...)).Implement
create_transformation_sequence()to build and return aTransformationSequence(usesequence.add_transformer(...)).
When constructing
ETLPipeline, pass the sequences instead of individual collections.
Fixed¶
GUI: fixed slider issue on showcase page; ensured overview loads immediately; reduced layout snapping on data page; hidden dummy card; hid initial loader flash via CSS; cleaned up legacy callbacks; verified callback field necessity using
get_parameters.Tests: updated and fixed several tests (including utils unit tests; temporary intentionally failing test removed/fixed).
CI/CD¶
Multiple pipeline updates for CI and CD, including result publication tweaks and cache adjustments.
Misc¶
Minor documentation updates and code cleanups.
0.3.3¶
Released at 12-12-2025
Summary¶
Breaking: Content pages moved to a protocol/class-based system. Instead of passing content functions, the frontend now receives Page classes. This aligns page composition with the new
BaseAlgorithm/BaseKPIpatterns.Parameters
Added Multi-select parameter support via
MultiEnumParameter.Added time-related parameters
TimeParameterandIntervalParameterwith frontend input components.
Added a configuration option that shows/hides the Upload tab for parameter selection. Default is hidden.
Added a _ standard_ data page that uses the
tablesattribute of the data container to render simple DashTables.
Migration to Page classes (breaking)¶
Prior to this release, pages were typically supplied to the app as plain content functions. As of v0.3.2, pages are defined as classes that satisfy the page protocol and are passed to the frontend by type (class) rather than by function reference.
The content classes need to follow the appropriate Protocols. That is, implement the required fuctions with signature.
These protocols are enforced by validation of the AppConfiguration.
An outline of the expected functions is included below.
HomePage:
create_content() -> html.Divregister_callbacks() -> None
DataPage:
create_content(data: BASEDATASOURCE) -> html.Divregister_callbacks() -> None
ScenarioPage:
create_content(scenario: Scenario) -> html.Divregister_callbacks() -> None
ComparePage:
create_side_by_side_content(scenario: Scenario, side: str) -> html.Divcreate_compare_section(left: Scenario, right: Scenario) -> html.Div:create_details_section(left: Scenario, right: Scenario) -> html.Divregister_callbacks() -> None
OverviewPage:
create_content(List[Scenario]) -> html.Divregister_callbacks() -> None
Below is a conceptual before/after to illustrate the change.
Old version (functions passed to the frontend)
# example (conceptual)
from algomancy_gui.configuration.appconfig import AppConfig
class ExampleDataPage:
def create_content(self, data) -> html.Div:
...
def register_callbacks(self):
...
config = AppConfig(
home_page_content='standard',
data_page_content=ExampleDataPage.create_content, # Callable[..., Div] or str
data_page_callbacks=ExampleDataPage.register_callbacks # Callable[..., None] or str
)
New version (Page classes passed to the frontend)
# example (conceptual)
from algomancy_gui.configuration.appconfig import AppConfig
class ExampleDataPage:
def create_content(self, data) -> html.Div:
...
def register_callbacks(self):
...
config = AppConfig(
home_page_content='standard' # Protocol[HomePage] or str
data_page = ExampleDataPage, # Protocol[DataPage] or str
)
If you maintain custom pages, convert them into classes that implement the expected page protocol (constructor + render/handlers). For a minimal reference implementation, see algomancy_content.pages.placeholderscenariopage.PlaceholderScenarioPage.
0.3.2¶
Released at 16-12-2025
Summary¶
Added Sessions to the App
Sessions can be managed in the Admin page
An empty session can be created by clicking the “New Session” button in the Admin page
An copy of a session can be created by clicking the “Copy Session” button in the Compare page
This copies only the datasets and not the scenarios
Breaking: When retrieving the scenario manager
get_app().server.scenario_managerno longer worksScenario manager is now available through the session manager and the active session
0.3.1¶
Released at 03-12-2025
Summary¶
Breaking: Revised
AlgorithmTemplatepattern toBaseAlgorithmworkflow, analog toBaseDataSourceBreaking: Also revised
KPITemplatepattern toBaseKPIworkflowScenario engine
Export and typing cleanups in
scenarioto supportBaseAlgorithm/BaseKPIpatterns.Measurement/unit utilities updated in
scenario.unit.Added threshold KPIs;
ImprovementDirection.AT_LEAST,.AT_MOST, and argumentthreshold
Components and pages
Updated Compare and Scenario page callbacks and KPI Card to align with the new KPI base class and thresholds.
Minor adjustments to Standard Home and Overview pages.
Examples
Replaced example KPI template with a
BaseKPIimplementation (DelayKPI).
Migration to BaseAlgorithm¶
To align the development patterns, the template pattern has been substituted for a basemodel pattern, similarly to the
creation of custom DataSources. Below is an example of the before and after.
Old version
def batching_algorithm(
data: DataSource,
parameters: BatchingAlgorithmParameters,
set_progress: Callable[[float], None],
) -> ScenarioResult:
sleep(parameters.batch_size)
set_progress(100)
return ScenarioResult(data_id=data.id)
batching_algorithm_template = AlgorithmTemplate(
name="Batching",
param_type=BatchingAlgorithmParameters, # Parameter type used to be passed
main_method_template=batching_algorithm, # as well as the main method handle.
)
New version
class BatchingAlgorithm(BaseAlgorithm):
""" From v0.3.1, create your own algorithm by deriving BaseAlgorithm """
def __init__(self, params: BatchingAlgorithmParameters):
super().__init__("Batching", params)
@staticmethod
def initialize_parameters() -> BatchingAlgorithmParameters:
""" Minimal bit of boilerplate, necessary for internal handling """
return BatchingAlgorithmParameters()
def run(self, data: DataSource) -> ScenarioResult:
""" Derived Algorithms now have to implement their own run() method """
sleep(self.params.batch_size)
self.set_progress(100)
return ScenarioResult(data_id=data.id)
Migration to BaseKPI¶
Similarly, the KPI creation has also been moved to the basemodel pattern.
Old version
default = QUANTITIES["default"]
default_unit = BaseMeasurement(default["unit"], min_digits=1, max_digits=3, decimals=1)
def create_error_template():
def error_rate_calculation(result: ScenarioResult) -> float:
return 0.1 * (1 + 0.5 * random.random()) # placeholder
return KpiTemplate(
name="Error Rate",
better_when=ImprovementDirection.LOWER,
callback=error_rate_calculation,
measurement_base=default_unit,
)
New version
default = QUANTITIES["default"]
default_unit = BaseMeasurement(default["unit"], min_digits=1, max_digits=3, decimals=1)
class ErrorKPI(BaseKPI):
def __init__(self):
""" Basic configurations are now made by passing them to the base object """
super().__init__(
name = "Error Rate",
better_when = ImprovementDirection.LOWER,
base_measurement = default_unit,
)
def compute(self, result: ScenarioResult) -> float:
""" The user defines the compute function, as before"""
return 0.1 * (1 + 0.5 * random.random()) # placeholder
Threshold KPIs¶
A threshold KPI is initialized with a threshold value and appropriate ImprovementDirection, in addition to the usual arguments.
It is considered to be a ‘success’ if the value of the kpi exceeds (or does not exceed, in the case of AT_MOST) the threshold value.
The .pretty() function will format a threshold as either a checkmark or a cross, depending on the value relative to the threshold.
An example is included below
# noinspection PyUnresolvedReferences
class DelayKPI(BaseKPI):
def __init__(self):
super().__init__(
name="Average Delay",
better_when=ImprovementDirection.AT_MOST,
base_measurement=BaseMeasurement(QUANTITIES["time"]["s"], min_digits=1, max_digits=3, decimals=1),
threshold=1200,
)
0.2.15¶
Released at 28-11-2025
Summary¶
DataSourceandScenarioResultare now derived from the abstractBaseDataSourceandBaseScenarioResult, which are used for internal typing and should be extended for custom containers rather than DataSource and ScenarioResult. This change is backwards-compatible, and should not break existing projects
0.2.14¶
Released at 21-11-2025
Summary¶
Styling and UX improvements across default components and pages.
Restructured CSS and refined component defaults; added/updated animated spinners and modal behavior.
Data engine: improvements to extractors.
Logging and reliability fixes.
Details¶
UI/UX
Restyled default components and performed multiple styling updates.
Restructured CSS and applied small CSS tweaks to polish visuals.
Disabled page background when modals are open for better focus.
Wrapped Data page in a spinner and added further updates to animated spinners with better customizability.
Data Engine
Multiple extractor-related improvements and refactors.
Examples
Updated tests and example implementation for alignment with UI and engine changes.
Bug fixes¶
Fixed a scale assertion bug in the measurement formatting logic.
Replaced erroneous
log_exceptionusage with the correctlog_tracebackin logging.
Multi extractor update¶
This is a breaking change
InputFileConfiguration is now an abstract class; its uses should be replaced by SingelInputFileConfiguration, which
is a drop-in replacement. Its counterpart, the MultiInputFileConfiguration is used by the MultiExtractor.
Its use should be clear from the example below.
class StedenSchema(Schema):
COUNTRY = "Country"
CITY = "City"
@property
def datatypes(self) -> Dict[str, DataType]:
return {
StedenSchema.COUNTRY: DataType.STRING,
StedenSchema.CITY: DataType.STRING,
}
class KlantenSchema(Schema):
ID = 'ID'
Name = "Naam"
@property
def datatypes(self) -> Dict[str, DataType]:
return {
KlantenSchema.ID: DataType.INTEGER,
KlantenSchema.Name: DataType.STRING,
}
multisheet_config = MultiInputFileConfiguration(
extension=FileExtension.XLSX,
file_name="multisheet",
file_schemas={
"Steden": StedenSchema(),
"Klanten": KlantenSchema(),
}
)
class ExampleETLFactory(de.ETLFactory):
def __init__(self, configs, logger=None):
super().__init__(configs, logger)
def create_extractors(
self,
files: Dict[str, F], # name to path format
) -> Dict[str, de.Extractor]:
...
multisheet_extractor = de.XLSXMultiExtractor( # -- this is new
file=cast(de.XLSXFile, files[multisheet]),
schemas=self.get_schemas(multisheet),
logger=self.logger,
),
Note: the resulting DataFrames show up with keys multisheet.Steden and multisheet.Klanten in the ETL internal dictionary.
0.2.13¶
Released at 06-11-2025
Summary¶
Autocreate now supports algorithms with parameters.
Delete button is now disabled while a scenario is queued or running.
CQM-loader is now a configurable option
0.2.12¶
Released at 06-11-2025
Summary¶
Bug fixes
Bug fixes¶
Fixed an issue where
unit.pywould cause crashes in python pre-3.14.
0.2.11¶
Released at 05-11-2025
Summary¶
Several minor bug fixes and documentation updates.
implemented
__eq__forDataSource, checking the internal uuid.
Bug fixes¶
Removed redundant message from internal logging
Scenario.cancel()andScenario.refresh()now act properly when no logger is passedSecret key is now set on
BasicAuthdata path is no longer validated for configurations without persistent state.
0.2.10¶
Released at 05-11-2025
Summary¶
Introduced a unified KPI measurement framework with
BaseMeasurement, replacingUOM. This allows for automatic unit conversion and consistent representation in the UI.Renamed the remaining “performance” page references to “compare” across modules for consistency.
Improved logging: stack traces are now routed to the logger; startup log severity adjusted.
Documentation: README updates and minor cleanup.
Tests: Added pytest module
tests/test_unit_measurement_examples.pycovering Measurement examples fromunit.py.New feature: automatic creation of scenarios is now supported.
New feature: Added
refreshfunctionality to theScenariocomponent.[Breaking] New feature:
AppConfigurationclass now manages and validates the launch configuration
AppConfiguration¶
This is a breaking change.
Added AppConfiguration class to manage and validate the launch configuration. Conceptually, this class is a wrapper that provides a consistent interface for the configuration fields and their validation.
This class is now used to manage the launch sequence. In particular, DashLauncher.build(…) now takes an AppConfiguration object as an argument, instead of a dictionary.
Your main method must be migrated to use the new class. An example is shown below:
# main method: preferred version
from algomancy_gui.gui_launcher import GuiLauncher
from algomancy_gui.configuration.appconfig import AppConfig
def main():
app_cfg = AppConfig(
data_path="data",
has_persistent_state=True,
# ...
)
app = GuiLauncher.build(app_cfg)
GuiLauncher.run(app=app, host=app_cfg.host, port=app_cfg.port)
For migration, the AppConfiguration.from_dict(...) method can be used to create an AppConfiguration object from a dictionary. Note that this is not advised, as this will not allow for IDE support.
# main method: migration alternative
from algomancy_gui.gui_launcher import GuiLauncher
from algomancy_gui.configuration.appconfig import AppConfig
def main():
configuration = {
"data_path": "data",
"has_persistent_state": True,
# ...
}
app_cfg = AppConfig.from_dict(configuration)
app = GuiLauncher.build(app_cfg)
GuiLauncher.run(app=app, host=app_cfg.host, port=app_cfg.port)
Autocreate¶
Added automatic creation of scenarios. This will cause any creation of a DataSource (or derived) to spawn a Scenario with the same name (suffixed with [auto]). The algorithm template must be specified in the configuration dictionary.
To configure, add the below to the configuration.
# framework configuration
app_cfg = AppConfiguration(
# ...,
autocreate= True, # set to True for autocreate mode
default_algo= "As is", # select the name of an algorithm template to use for autocreation
# ...,
)
Added
refreshfunctionality to theScenariocomponent. This will cause theScenarioto reset its status and discard theScenarioResult. To refresh a scenario, theScenario.refresh()method is called from the Scenario management screen. The process scenario button is now context-aware. At a later time, this button will also support a cancel operation.
Interface changes¶
[Breaking] Replaced
UOMwithBaseMeasurementin KPI-related APIs and templates. Update custom KPI code to construct and returnMeasurement/BaseMeasurementinstead of the old types.[Breaking] Removed obsolete
KpiTypeenum.
Measurement framework¶
New
BaseUnit,Quantity,BaseMeasurement, andMeasurementtypes inalgomancy\\scenarioengine\\unit.pyprovide consistent formatting, auto-scaling, and unit chaining.KPI templates should be migrated to
BaseMeasurement/Measurement. Seealgomancy\\scenarioengine\\keyperformanceindicator.pyfor how KPIs surface measurements.Extensive examples are available in
algomancy\\scenarioengine\\unit.py\\example_usage().KPI Template creation should now follow the following pattern:
import random
from src.algomancy import ImprovementDirection, KpiTemplate, ScenarioResult
from scenario import QUANTITIES, BaseMeasurement
def throughput_calculation(result: ScenarioResult) -> float:
return 100 * (1 + 0.5 * random.random()) # placeholder
mass = QUANTITIES["mass"]
mass_kg = BaseMeasurement(
mass["kg"], # the default unit is kg; the associated quantity is mass
min_digits=1, # the minimum number of nonzero digits before the decimal point
max_digits=3, # the maximum number of nonzero digits before the decimal point
decimals=2, # the number of decimal places to display
smallest_unit="g", # the smallest unit to display - overrides min_digits
largest_unit="ton", # the largest unit to display - overrides max_digits
)
template = KpiTemplate(
name="Throughput",
# type=KpiType.NUMERIC, # KpiType has become redundant, formatting is now handled by Measurement
better_when=ImprovementDirection.HIGHER,
callback=throughput_calculation,
measurement_base=mass_kg, # Pass the measurement to use as a basis for the kpi value
)
Compare page naming cleanup¶
All remaining references to the performance page were renamed to compare for consistency (imports, component IDs, modules). If you import internal modules, update your imports accordingly.
Note: css classes are also affected, so you may need to update your style.css file.
Logging¶
Exceptions now include full stack traces in the central logger.
The startup message severity has been adjusted for better signal in production logs.
Docs¶
README refreshed to reflect the new measurement framework and naming.
0.2.9¶
Released at 29-10-2025
Summary¶
New features
Added internal
ContentRegistryclass, which now manages and distributes the content functions.
Bug fixes
Fixed issue where
urlcallbacks would cause conflicts.Fixed a bug where the
urlcallbacks had multiple listeners, which sometimes caused synchronization issues.
ContentRegistry¶
The ContentRegistry class is now used to manage and distribute the content functions.
These responsibilities were previously handled by the Launcher class, which has been refactored to only manage the launch sequence.
This is a purely internal change, and should not affect the user.
0.2.8¶
Released at 29-10-2025
Summary¶
New features
Added a Waitress WSGI wrapper for production servers
Interface changes
[Breaking] Added additional CLI arguments
threadsandconnectionsto the startup sequenceSimplified AlgorithmParameter syntax
Waitress WSGI wrapper¶
The Waitress WSGI wrapper is now used to run the application if debug is set to False.
This should relieve issues experienced with the Flask development server, such as the lack of thread safety that could be observed when accessing the app from multiple sources simultaneously.
The wrapper is configured through the CLI arguments; in particular, threads and connections have been added.
They control the number of threads and the maximum number of simultaneous connections, respectively.
threads defaults to 8, and connections defaults to 100.
This is a breaking change.
threadsandconnectionsare now required arguments ofLauncher.run(...)
AlgorithmParameter syntax¶
The __getitem__ method of the AlgorithmParameters class has been implemented.
Instead of accessing a parameter in key as algorithm_parameters._parameters[key].value, it can now be accessed as algorithm_parameters[key].
Note that this is optional, legacy syntax will still work.
0.2.7¶
Released at 27-10-2025
Summary¶
New features
Opened up compare page styling through style.css
The order of the main sections (side-by-side, compare, KPI cards, and details) are now configurable through the configuration dictionary
Interface changes
[Breaking] The side-by-side section of the compare page now passes
"left"and"right"to the content function.
Bug fixes
MultiExtractor no longer uses the (previously renamed)
extraction_messageandextraction_success_messagefunctions
Compare page configuration¶
The order of the main sections (side-by-side, compare, KPI cards, and details) are now configurable through the configuration dictionary.
To configure, specify the list of component keys in the order you want them to appear in the compare page, and add it to the configuration dictionary with key performance_ordered_list_components.
The expected keys are side-by-side, kpis, compare, and details. An example is shown below:
# framework configuration
configuration = {
# ...
"compare_ordered_list_components": [ 'side-by-side', 'kpis', 'compare', 'details']
# ...
}
Side-by-side section¶
The side-by-side section of the compare page now passes "left" and "right" to the content function, which allows the scenario specific section to contain their own responsive elements, such as dropdown menus.
The content function signature has changed to include the side argument.
This is a breaking change. Content functions that expect only one argument will need to be updated.
Alternatively, **kwargs can be added to the function signature to allow for side to be passed and be robust for future expansion.
See here for more details.
An example is shown below:
OLD
@staticmethod
def create_side_view(s: Scenario) -> html.Div:
""" User defined function to create the side view of the compare page."""
return html.Div(...)
NEW
@staticmethod
def create_side_view(s: Scenario, side: str) -> html.Div:
""" User defined function to create the side view of the compare page."""
return html.Div(...)
ALTERNATIVE
@staticmethod
def create_side_view(s: Scenario, **kwargs) -> html.Div:
""" User defined function to create the side view of the compare page."""
return html.Div(...)