-
Notifications
You must be signed in to change notification settings - Fork 8
BO / Prefect HiTL Slack integration tutorial #456
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Copilot
wants to merge
39
commits into
main
Choose a base branch
from
copilot/fix-382
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
1e60493
Initial plan
Copilot ad0c718
Revise copilot instructions for clarity and detail
sgbaird e5222e7
Create BO/Prefect HiTL Slack integration tutorial with Ax Service API
Copilot 83fd619
Update GitHub Actions workflow for Copilot setup
sgbaird 233f2b3
Update dependency installation for specific branch
sgbaird 027376e
Update GitHub Actions workflow for branch handling
sgbaird d77fa4a
Update dependencies in copilot-setup-steps.yml
sgbaird 849630c
Remove duplicate package installation command
sgbaird c0626fd
Fix formatting in copilot-setup-steps.yml
sgbaird 95c2449
Remove all mocking, simplify code, reduce verbose logging as requested
Copilot 780062b
Add robust error handling, parameterization, and input validation
Copilot bcaeeba
Improve input validation and failed trial handling as requested
Copilot 11d898e
precect + ax workflow with HiTL slack
Daniel0813 98b6cad
Update README_BO_HITL_Tutorial.md with comprehensive documentation
Daniel0813 d57a293
Add Bayesian Optimization HITL deployment script with GitRepository b…
Daniel0813 4b88b9a
feat: Add containerized BO HITL workflow with Docker
Daniel0813 a39f6d0
Fix deployment entrypoint to use correct bo_hitl_slack_tutorial.py path
Daniel0813 86d0947
Convert Slack integration from Prefect blocks to environment variables
Daniel0813 af7cccb
Fix Slack message URL to use external accessible Prefect UI address
Daniel0813 c23d856
Fix Prefect UI URL format for flow runs
Daniel0813 00e6ae7
Fix Docker Prefect UI URL configuration for flow resumption
Daniel0813 3bc4c82
Update requirements.txt files with SQLAlchemy 2.x and prefect-slack d…
Daniel0813 f4b3ddd
Implement secure Slack notifications using Prefect Variables
Daniel0813 9010aaa
Fix variable name to use lowercase with dashes
Daniel0813 a8b384d
Fix Slack URL to use localhost instead of Docker IP
Daniel0813 0971c0a
Clean up unnecessary directories and add Docker containerization setup
Daniel0813 7d5e8d8
Enhance BO HITL deployment script with Unicode fixes and dependency i…
Daniel0813 072d8a0
Merge branch 'main' into copilot/fix-382
sgbaird a9d0ef0
Fix Slack webhook integration in BO HITL deployment script
Daniel0813 5eef28a
Clean up repository: remove duplicate directories
Daniel0813 4997797
Reorganize prefect scripts: move samples to dedicated folder
Daniel0813 b5b5e8f
Add MongoDB database integration module for BO experiments
Daniel0813 15a6b62
Implement comprehensive JSON storage system for BO campaigns
Daniel0813 12c89c4
Add dual storage system (JSON + MongoDB) to BO HITL tutorial
Daniel0813 4551125
Add experiment configuration and trial data for human-in-the-loop BO …
Daniel0813 7492a28
Simplify BO HITL tutorial: remove unnecessary wrappers, fix copilot i…
Copilot 8221939
Prefect Cloud-friendly fixes: MongoDB checks and clearer Slack block …
Daniel0813 d66830c
slack webhook URL and mongodb uri both stored in prefect block. expor…
Daniel0813 f8c6da2
correct json data format
Daniel0813 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,30 @@ | ||
| # CHANGELOG | ||
| # Changelog | ||
|
|
||
| All notable changes to this project will be documented in this file. | ||
|
|
||
| The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | ||
| and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||
|
|
||
| ## [Unreleased] | ||
|
|
||
| ### Added | ||
| - Support for both `rpicam-vid` (Raspberry Pi OS Trixie) and `libcamera-vid` (Raspberry Pi OS Bookworm) camera commands in `src/ac_training_lab/picam/device.py` to ensure compatibility across different OS versions. | ||
| - BO / Prefect HiTL Slack integration tutorial (2025-12-01) | ||
| - Added `scripts/prefect_scripts/bo_hitl_slack_tutorial.py` - Bayesian Optimization with human-in-the-loop evaluation via Slack | ||
| - Uses Ax Service API for Bayesian optimization | ||
| - Integrates Prefect interactive workflows with pause_flow_run | ||
| - Slack notifications for experiment suggestions | ||
| - MongoDB Atlas storage for experiment data | ||
| - Evaluation via HuggingFace Branin space | ||
|
|
||
| ### Changed | ||
| - Support for both `rpicam-vid` (Raspberry Pi OS Trixie) and `libcamera-vid` (Raspberry Pi OS Bookworm) camera commands | ||
|
|
||
| ### Fixed | ||
| - Ctrl+C interrupt handling in `src/ac_training_lab/picam/device.py` now properly exits the streaming loop instead of restarting. | ||
| - Ctrl+C interrupt handling in `src/ac_training_lab/picam/device.py` | ||
|
|
||
| ## [1.1.0] - 2024-06-11 | ||
| ### Added | ||
| - Imperial (10-32 thread) alternative design to SEM door automation bill of materials in `docs/sem-door-automation-components.md`. | ||
| - Validated McMaster-Carr part numbers and direct links for all imperial components. | ||
|
|
||
| ### Changed | ||
| - No changes to metric design section. | ||
| - Imperial (10-32 thread) alternative design to SEM door automation bill of materials | ||
|
|
||
| ### Notes | ||
| - All components sourced from McMaster-Carr for reliability and reproducibility. | ||
| - All components sourced from McMaster-Carr for reliability and reproducibility |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| { | ||
| "executionEnvironments": [ | ||
| { | ||
| "root": ".", | ||
| "pythonPath": "python", | ||
| "extraPaths": ["./src"] | ||
| } | ||
| ] | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| # Bayesian Optimization Human-in-the-Loop Slack Integration Tutorial | ||
|
|
||
| Demonstrates a BO campaign with human evaluation via Slack and Prefect. | ||
|
|
||
| ## Workflow | ||
|
|
||
| 1. **Run script** - starts BO campaign via Ax Service API | ||
| 2. **Ax suggests parameters** - sends Slack notification with x1, x2 values | ||
| 3. **User evaluates** - uses HuggingFace Branin space | ||
| 4. **User resumes** - enters objective value in Prefect UI via Slack link | ||
| 5. **Loop continues** - 5 iterations to find optimal parameters | ||
|
|
||
| ## Setup | ||
|
|
||
| ### 1. Install Dependencies | ||
|
|
||
| ```bash | ||
| pip install ax-platform prefect prefect-slack pymongo | ||
| ``` | ||
|
|
||
| ### 2. Start Prefect Server | ||
|
|
||
| ```bash | ||
| prefect server start | ||
| ``` | ||
|
|
||
| ### 3. Configure Slack Webhook Block | ||
|
|
||
| ```python | ||
| from prefect.blocks.notifications import SlackWebhook | ||
|
|
||
| slack_webhook_block = SlackWebhook(url="YOUR_SLACK_WEBHOOK_URL") | ||
| slack_webhook_block.save("prefect-test") | ||
| ``` | ||
|
|
||
| Get webhook URL from https://api.slack.com/apps | ||
|
|
||
| ### 4. Configure MongoDB (Optional) | ||
|
|
||
| Set environment variable for experiment storage: | ||
| ```bash | ||
| export MONGODB_URI="mongodb+srv://user:pass@cluster.mongodb.net/" | ||
| ``` | ||
|
|
||
| ### 5. Run | ||
|
|
||
| ```bash | ||
| python bo_hitl_slack_tutorial.py | ||
| ``` | ||
|
|
||
| ## Files | ||
|
|
||
| - `bo_hitl_slack_tutorial.py` - Main tutorial (single file implementation) | ||
| - `requirements.txt` - Dependencies | ||
|
|
||
| ## Demo Video | ||
|
|
||
| Show: | ||
| 1. Running script | ||
| 2. Receiving Slack notification | ||
| 3. Evaluating on HuggingFace Branin space | ||
| 4. Clicking Slack link to Prefect UI | ||
| 5. Entering objective value | ||
| 6. Repeat 4-5 times | ||
|
|
||
| ## References | ||
|
|
||
| - [Ax Documentation](https://ax.dev/) | ||
| - [Prefect Interactive Workflows](https://docs.prefect.io/latest/guides/creating-interactive-workflows/) | ||
| - [HuggingFace Branin Space](https://huggingface.co/spaces/AccelerationConsortium/branin) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,189 @@ | ||
| """ | ||
| Human-in-the-Loop Bayesian Optimization with Ax, Prefect and Slack | ||
|
|
||
| Demonstrates a BO campaign where: | ||
| 1. Ax suggests parameters via Service API | ||
| 2. Slack notification sent with parameters | ||
| 3. Human evaluates via HuggingFace Branin space | ||
| 4. Human enters objective value in Prefect UI | ||
| 5. Loop continues for n iterations | ||
| """ | ||
|
|
||
| import os | ||
| import json | ||
| from datetime import datetime | ||
| from pymongo import MongoClient | ||
| from ax.service.ax_client import AxClient, ObjectiveProperties | ||
| from prefect import flow, get_run_logger | ||
| from prefect.blocks.notifications import SlackWebhook | ||
| from prefect.context import get_run_context | ||
| from prefect.input import RunInput | ||
| from prefect.flow_runs import pause_flow_run | ||
| from prefect.blocks.system import Secret | ||
|
|
||
|
|
||
| class ExperimentInput(RunInput): | ||
| """Input model for experiment evaluation""" | ||
| objective_value: float | ||
| notes: str = "" | ||
|
|
||
|
|
||
| @flow(name="bo-hitl-slack-campaign") | ||
| def run_bo_campaign(n_iterations: int = 5, random_seed: int = 42, slack_block_name: str = "tutorial-slack-webhook-url", mongodb_block_name: str = "tutorial-mongodb-uri"): | ||
| """ | ||
| Bayesian Optimization campaign with human-in-the-loop evaluation via Slack | ||
|
|
||
| Args: | ||
| n_iterations: Number of BO iterations to run | ||
| random_seed: Seed for Ax reproducibility | ||
| slack_block_name: Name of the Prefect Slack webhook block | ||
| mongodb_block_name: Name of the Prefect Secret block containing MongoDB URI | ||
| """ | ||
| logger = get_run_logger() | ||
| logger.info(f"Starting BO campaign with {n_iterations} iterations") | ||
|
|
||
| # Initialize Ax client with Service API | ||
| ax_client = AxClient(random_seed=random_seed) | ||
| ax_client.create_experiment( | ||
| name="branin_bo_experiment", | ||
| parameters=[ | ||
| {"name": "x1", "type": "range", "bounds": [-5.0, 10.0], "value_type": "float"}, | ||
| {"name": "x2", "type": "range", "bounds": [0.0, 15.0], "value_type": "float"}, | ||
| ], | ||
| objectives={"branin": ObjectiveProperties(minimize=True)} | ||
| ) | ||
|
|
||
| # Load Slack webhook | ||
| slack_block = SlackWebhook.load(slack_block_name) | ||
|
|
||
| # Connect to MongoDB Atlas for storage using Prefect Secret block | ||
| mongodb_secret = Secret.load(mongodb_block_name) | ||
| mongodb_uri = mongodb_secret.get() | ||
| mongo_client = MongoClient(mongodb_uri) if mongodb_uri else None | ||
| logger.info(f"Connected to MongoDB using block '{mongodb_block_name}'") | ||
|
|
||
| db = mongo_client["bo_experiments"] if mongo_client else None | ||
|
|
||
| # Create experiment record | ||
| experiment_id = f"exp_{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}" | ||
| if db is not None: | ||
| db.experiments.insert_one({ | ||
| "experiment_id": experiment_id, | ||
| "created_at": datetime.utcnow(), | ||
| "n_iterations": n_iterations, | ||
| "random_seed": random_seed, | ||
| "status": "running" | ||
| }) | ||
|
|
||
| results = [] | ||
|
|
||
| for iteration in range(n_iterations): | ||
| logger.info(f"Iteration {iteration + 1}/{n_iterations}") | ||
|
|
||
| # Get next suggestion from Ax | ||
| parameters, trial_index = ax_client.get_next_trial() | ||
| x1, x2 = parameters['x1'], parameters['x2'] | ||
|
|
||
| logger.info(f"Suggested: x1={x1}, x2={x2}") | ||
|
|
||
| # Build Prefect Cloud UI URL - use workspace ID format | ||
| flow_run = get_run_context().flow_run | ||
| # Default to generic dashboard URL that will handle authentication and routing | ||
| account_id = os.getenv("PREFECT_ACCOUNT_ID", "5b838504-64cf-4297-9b35-b881ac6169b3") | ||
| workspace_id = os.getenv("PREFECT_WORKSPACE_ID", "d2718b4c-b49a-43ce-83c2-baf6fb3b9665") | ||
| base_url = os.getenv("PREFECT_UI_URL", "https://app.prefect.cloud") | ||
| flow_run_url = f"{base_url}/account/{account_id}/workspace/{workspace_id}/flow-runs/flow-run/{flow_run.id}" if flow_run else "" | ||
|
|
||
| # Send Slack notification | ||
| message = f"""*BO Iteration {iteration + 1}/{n_iterations}* | ||
|
|
||
| Evaluate Branin function at: | ||
| - x1 = {x1} | ||
| - x2 = {x2} | ||
|
|
||
| Use: https://huggingface.co/spaces/AccelerationConsortium/branin | ||
|
|
||
| <{flow_run_url}|Click here to resume> and enter the objective value.""" | ||
|
|
||
| slack_block.notify(message) | ||
|
|
||
| # Pause for human input | ||
| logger.info("Waiting for human evaluation...") | ||
| user_input = pause_flow_run( | ||
| wait_for_input=ExperimentInput.with_initial_data( | ||
| description=f"Enter objective value for x1={x1}, x2={x2}" | ||
| ) | ||
| ) | ||
|
|
||
| objective_value = user_input.objective_value | ||
| logger.info(f"Received: {objective_value}") | ||
|
|
||
| # Complete trial in Ax | ||
| ax_client.complete_trial(trial_index=trial_index, raw_data=objective_value) | ||
|
|
||
| # Store trial result | ||
| trial_result = { | ||
| "iteration": iteration + 1, | ||
| "trial_index": trial_index, | ||
| "parameters": parameters, | ||
| "objective_value": objective_value, | ||
| "notes": user_input.notes, | ||
| "timestamp": datetime.utcnow() | ||
| } | ||
| results.append(trial_result) | ||
|
|
||
| # Save to MongoDB | ||
| if db is not None: | ||
| db.trials.insert_one({ | ||
| "experiment_id": experiment_id, | ||
| **trial_result | ||
| }) | ||
|
|
||
| logger.info(f"Completed iteration {iteration + 1}") | ||
|
|
||
| # Get best parameters | ||
| best_parameters, best_values = ax_client.get_best_parameters() | ||
|
|
||
| # Update experiment status | ||
| if db is not None: | ||
| db.experiments.update_one( | ||
| {"experiment_id": experiment_id}, | ||
| {"$set": {"status": "completed", "completed_at": datetime.utcnow(), | ||
| "best_parameters": best_parameters, "best_value": best_values[0]['branin']}} | ||
| ) | ||
|
|
||
| # Send completion notification | ||
| slack_block.notify(f"""*BO Campaign Completed* | ||
|
|
||
| Best parameters: x1={best_parameters['x1']}, x2={best_parameters['x2']} | ||
| Best value: {best_values[0]['branin']} | ||
| Experiment ID: {experiment_id}""") | ||
|
|
||
| logger.info(f"Campaign complete. Best: {best_parameters}, Value: {best_values}") | ||
|
|
||
| # Export data to JSON files | ||
| if db is not None: | ||
| # Create experiment_data folder if it doesn't exist | ||
| os.makedirs('experiment_data', exist_ok=True) | ||
|
|
||
| logger.info("Exporting experiments...") | ||
| experiments = list(db.experiments.find()) | ||
| with open('experiment_data/bo_experiments.json', 'w') as f: | ||
| json.dump(experiments, f, indent=2, default=str) | ||
|
|
||
| logger.info("Exporting trials...") | ||
| trials = list(db.trials.find()) | ||
| with open('experiment_data/bo_trials.json', 'w') as f: | ||
| json.dump(trials, f, indent=2, default=str) | ||
|
|
||
| summary = {"experiments": experiments, "trials": trials} | ||
| with open('experiment_data/bo_data_complete.json', 'w') as f: | ||
| json.dump(summary, f, indent=2, default=str) | ||
|
|
||
| if mongo_client: | ||
| mongo_client.close() | ||
|
|
||
| return ax_client, results, experiment_id | ||
|
|
||
|
|
||
| run_bo_campaign(n_iterations=5, random_seed=42) |
This file was deleted.
Oops, something went wrong.
17 changes: 0 additions & 17 deletions
17
scripts/prefect_scripts/client_scripts/prefect_client_basic.py
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are 30
trystatements in this file. Violates custom copilot instructionsThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simplified script from 1137 to 161 lines. Removed all 30 try statements. Now follows copilot instructions for minimal defensive error handling. Commit: 7492a28