Skip to content

to prefect flow, uv, ruff, ty - #69

Open
MarkTNO wants to merge 5 commits into
mainfrom
prefect
Open

to prefect flow, uv, ruff, ty#69
MarkTNO wants to merge 5 commits into
mainfrom
prefect

Conversation

@MarkTNO

@MarkTNO MarkTNO commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

No description provided.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates the simulator worker execution path to a Prefect-based flow/deployment model while modernizing development tooling (uv + ruff + ty), CI, and container build strategy.

Changes:

  • Introduces a new Prefect flow (simulator_flow) and deployment script, plus supporting env helpers and local-run entrypoint.
  • Migrates tooling/config: switches to uv build backend and dependency groups, replaces flake8/mypy with ruff/ty, and updates CI + just tasks accordingly.
  • Updates/expands tests and fixtures to exercise the Prefect flow path and deployment job variables.

Reviewed changes

Copilot reviewed 43 out of 46 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
unit_test/test_hello.py Reformats legacy unittest file (now duplicated by tests/test_hello.py).
tests/test_prefect_flow.py Adds a Prefect flow execution test with MinIO/InfluxDB mocking.
tests/test_prefect_deploy_flow.py Adds coverage for module-level job_variables structure.
tests/test_kpi_integration.py Reworks KPI integration to call Prefect flow and handle Prefect State.
tests/test_hello.py Adds new test suite for config parsing/date indexing under tests/.
tests/data/esdl/simulator_tutorial.esdl Adds ESDL fixture used by Prefect flow tests/local run.
src/simulator_worker/worker_task.py Adds a legacy worker task module, currently fully commented out.
src/simulator_worker/utils.py Refactors utilities typing/logging; adds _parse_datetime_config; tweaks ESDL output naming.
src/simulator_worker/simulator_worker.py Removes previous main worker entrypoint implementation.
src/simulator_worker/prefect_flow.py Adds Prefect flow implementation and artifact writing to MinIO.
src/simulator_worker/prefect_deploy_flow.py Adds Prefect deployment registration script and job variables.
src/simulator_worker/env.py Adds environment access helpers (require_env, EnvSettings).
src/simulator_worker/main.py Removes previous __main__ entrypoint.
src/simulator_worker/init.py Changes init behavior to load dotenv (removes logging + script export).
run.sh Removes old local run script.
run.ps1 Removes old PowerShell helper script.
requirements.txt Removes pip-compile generated runtime requirements.
README.md Updates dev workflow docs for uv/just/ruff/ty and Prefect deploy/run steps.
pyproject.toml Switches to uv build backend, updates deps, and replaces flake8/mypy config with ruff/ty-era config.
local_run/run_simulator_flow_function.py Adds local runner for calling the Prefect flow function without Prefect orchestration.
justfile Adds standardized local/CI task runner commands.
Dockerfile Reworks container build to use uv and BuildKit mounts; removes previous entrypoint-based install flow.
dev.Dockerfile Reworks dev image build to use uv and allow local SDK install.
dev-requirements.txt Removes pip-compile generated dev requirements.
ci/win32/update_dependencies.cmd Removes legacy Windows dependency update script.
ci/win32/typecheck.cmd Removes legacy Windows mypy script.
ci/win32/test_unit.cmd Removes legacy Windows unittest/pytest script.
ci/win32/lint.cmd Removes legacy Windows flake8 script.
ci/win32/install_dependencies.cmd Removes legacy Windows pip-sync script.
ci/win32/create_venv.cmd Removes legacy Windows venv bootstrap script.
ci/linux/update_dependencies.sh Removes legacy Linux dependency update script.
ci/linux/typecheck.sh Removes legacy Linux mypy script.
ci/linux/test_unit.sh Removes legacy Linux pytest runner script (unit_test-based).
ci/linux/lint.sh Removes legacy Linux flake8 script.
ci/linux/install_dependencies.sh Removes legacy Linux pip-sync script.
ci/linux/create_venv.sh Removes legacy Linux venv bootstrap script.
ci/linux/build_python_package.sh Removes legacy package build script.
ci/linux/_load_dot_env.sh Removes legacy dotenv loader script.
.vscode/settings.json Updates editor integration for ruff/ty; switches test discovery to tests/.
.vscode/launch.json Adds debug configs for Prefect deploy, local flow run, and pytest.
.gitignore Renames coverage ignore path from unit_test_coverage/ to tests_coverage/.
.github/workflows/publish_container_image.yml Modernizes GHCR publish workflow using buildx + metadata-action.
.github/workflows/ci.yml Replaces venv-tar pipeline with uv+just jobs (lint/security/test/typecheck).
.env.template.local Updates local env template for Prefect/MinIO/InfluxDB settings.
.env.template Updates env template for Prefect/MinIO/InfluxDB settings.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/simulator_worker/prefect_flow.py Outdated
Comment on lines +83 to +87
if minio_host is None or minio_port is None or minio_access_key is None or minio_secret is None:
raise ValueError(
f"MinIO credentials are not fully set. MinIO host: {minio_host}, port: {minio_port}, "
f"access key: {minio_access_key}, secret key: {minio_secret}"
)
Comment on lines +359 to +366
value = config[key]
if not isinstance(value, str):
return default

try:
return datetime.fromisoformat(value)
except ValueError:
return default
Comment thread pyproject.toml
Comment thread README.md Outdated
Comment on lines +61 to +63
Returns:
SimulatorFlowResult | State[Any] | None: Failed state when execution fails; otherwise no value is
returned from this flow function.
Comment thread src/simulator_worker/worker_task.py Outdated
Comment on lines +16 to +20
# """Main python file for Simulator-worker."""
# import logging
# import math
# import traceback
# from datetime import datetime, timedelta

@jarsarasty jarsarasty left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work on the migration overall - the flow/deploy split is clean and EnvSettings is a big improvement over scattered os.getenv calls. A few things I'd like fixed before merge, plus one optional cleanup. Left them inline.

One coordination note that isn't a defect in this PR: the Rosim JAR download lands in this image (uv sync installs omotes-simulator-core==0.0.30 from PyPI), which is where the KeyError: 'assets' in the omotes-system system tests comes from. That's fixed upstream in simulator-core and will need a pin bump here once released. Be aware the ATES tests also fail on main through a different mechanism (a 120s timeout at wait_until_result, no KeyError), so the pin bump is necessary but may not be sufficient.

output_esdl_with_kpis = build_esdl_string_with_kpis(output_esdl, kpi_results)
except Exception:
logging.exception("KPI calculation failed. Results will be returned without KPIs.")
output_esdl_with_kpis = output_esdl

@jarsarasty jarsarasty Sep 3, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix: this makes a KPI failure invisible. The except Exception logs and assigns output_esdl_with_kpis = output_esdl, then execution continues unbroken to the artifact write and return success_result - so the flow reports success with KPIs silently missing, and nothing downstream inspects the ESDL for them.

That matters because test__simulator__kpis_present_in_output in omotes-system asserts exact CAPEX/OPEX values, so a regression here surfaces as a confusing system-test failure rather than a clear worker error.

Suggestion: keep the broad catch (a KPI failure arguably shouldn't sink an otherwise-good simulation) but surface it in esdl_messages instead of only the log. That field is declared on SimulatorFlowResult and currently hardcoded to [] at both return sites, so it's an existing channel that carries nothing today.

optimizer-worker/src/omotes_optimizer_worker/prefect_flow.py already does exactly this - collects list[EsdlMessage] and serialises with [m.model_dump(mode="json") for m in esdl_messages]. Same shape would work here:

except Exception as kpi_error:
    logging.exception("KPI calculation failed. Results will be returned without KPIs.")
    output_esdl_with_kpis = output_esdl
    esdl_messages.append(
        EsdlMessage(
            technical_message=f"KPI calculation failed: {kpi_error}",
            severity=MessageSeverity.WARNING,
        )
    )

(from omotes_sdk.esdl_messages import EsdlMessage, MessageSeverity, then pass the dumped list into SimulatorFlowResult rather than [].) That way the run still succeeds, but the caller can tell KPIs are absent and why.


# Debug output: save ESDL files if enabled (can be controlled via workflow_config)
# debug_enabled = _parse_bool_config(workflow_config, "debug_esdl", False)
debug_enabled = False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix: debug_enabled = False is hardcoded, so the if debug_enabled: branch below is statically unreachable - save_debug_esdl (imported at the top) and the debug_esdl_dir read are dead code.

This one has a real consequence rather than just being untidy: tests/test_kpi_integration.py sets config["debug_esdl"] = True and config["debug_esdl_dir"] = debug_dir in setUpClass, and its class docstring says "It enables debug_esdl so the output ESDL is saved as a CI artifact for inspection." That's now a silent no-op - the temp dir is created, cleaned up, and nothing is ever written to it.

Either restore the commented-out _parse_bool_config line above, or drop the debug path and update that test's docstring so it doesn't claim something it no longer does.

workflow_type_name: Name of the workflow.

Returns:
SimulatorFlowResult | State[Any] | None: Failed state when execution fails; otherwise no value is

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix: the docstring says "otherwise no value is returned from this flow function", but the body ends with return success_result. Worth correcting since this is the module's main entry point.

logging.info(f"workflow config: {workflow_config}")
# timestep = workflow_config.get("timestep", 3600) # default to 1 hour in seconds
# start = datetime.fromisoformat(workflow_config.get("start_time", "2019-01-01T00:00:00+00:00"))
# end = datetime.fromisoformat(workflow_config.get("end_time", "2019-01-31T01:00:00+00:00"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix: these three commented-out lines can go - git history has them if they're ever needed.

# timestep = workflow_config.get("timestep", 3600) # default to 1 hour in seconds
# start = datetime.fromisoformat(workflow_config.get("start_time", "2019-01-01T00:00:00+00:00"))
# end = datetime.fromisoformat(workflow_config.get("end_time", "2019-01-31T01:00:00+00:00"))
timestep = workflow_config["timestep"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleanup, your call: timestep is the only config value read as a bare dict index while start_time/end_time go through _parse_datetime_config with defaults.

Not a bug - workflow_config_nwn.json declares timestep with "default": 3600, so the orchestrator always supplies it, and a KeyError would be caught by the outer handler into a Failed state anyway. Slightly funny that the config gives timestep a default while start_time/end_time have none, which is the opposite of how they're treated here. Just flagging the inconsistency.

Comment thread README.md
│ │ ├── prefect_flow.py # Prefect flow entry point for simulator runs
│ │ ├── py.typed # Marker for typed package consumers
│ │ ├── utils.py # ESDL/profile/KPI support utilities
│ │ └── worker_task.py # Legacy worker task module

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix: worker_task.py doesn't exist on this branch (or on main), so the structure diagram documents a module that isn't there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants