Download app.py from FlyingNunchucks/09-agentic-workflow-sys: direct link, hf CLI and curl.
- Browser
- Download file 17.9 kB
-
https://huggingface.co/spaces/FlyingNunchucks/09-agentic-workflow-sys/resolve/main/app.py
- Command line
-
hf download hf://spaces/FlyingNunchucks/09-agentic-workflow-sys/app.py
-
curl -L -o app.py https://huggingface.co/spaces/FlyingNunchucks/09-agentic-workflow-sys/resolve/main/app.py
17.9 kB
| """Gradio demo for Agent 9 — Agentic Workflow System.""" | |
| from __future__ import annotations | |
| import os | |
| import time | |
| from typing import Any | |
| import gradio as gr | |
| from src.demo_presentation import ( | |
| APP_CSS, | |
| EMPTY_ACTIVITY_HTML, | |
| EMPTY_AI_HTML, | |
| EMPTY_JOURNEY_HTML, | |
| EMPTY_OUTCOME_HTML, | |
| EMPTY_PATH_HTML, | |
| EMPTY_ROUTING_HTML, | |
| HERO_HTML, | |
| PATTERN_HTML, | |
| activity_html, | |
| activity_start_html, | |
| playback_delay, | |
| ) | |
| from src.demo_workflow import build_onboarding_registry, build_onboarding_workflow | |
| from src.evaluation import run_evaluation_suite | |
| from src.executor import ( | |
| WorkflowExecutionError, | |
| WorkflowHumanDecisionError, | |
| execute_workflow, | |
| submit_human_decision, | |
| ) | |
| from src.hf_provider import ( | |
| build_hf_onboarding_model_from_env, | |
| configured_model_id, | |
| live_ai_enabled_from_env, | |
| ) | |
| from src.persistence import RunNotFoundError, SQLiteStateStore | |
| from src.schemas import HumanDecision, WorkflowRun | |
| from src.ui_presenters import ( | |
| CASE_HEADERS, | |
| EVENT_HEADERS, | |
| METRIC_HEADERS, | |
| NODE_HEADERS, | |
| REVIEW_HEADERS, | |
| ai_insight_html, | |
| business_journey_html, | |
| business_outcome_html, | |
| evaluation_case_rows, | |
| evaluation_metric_rows, | |
| routing_explanation_html, | |
| run_bundle, | |
| workflow_path_html, | |
| ) | |
| WORKFLOW = build_onboarding_workflow() | |
| DATABASE_PATH = os.getenv("WORKFLOW_DB_PATH", "workflow_runs.db") | |
| STATE_STORE = SQLiteStateStore(DATABASE_PATH) | |
| LIVE_MODEL_ID = configured_model_id() | |
| LIVE_ONBOARDING_MODEL = build_hf_onboarding_model_from_env() | |
| STORY_SPECS: dict[str, dict[str, Any]] = { | |
| "Harbor Family — straightforward household": { | |
| "household_id": "HH-HARBOR-STANDARD", | |
| "household_type": "JOINT", | |
| "onboarding_notes": "The fictional Harbor family is beginning a standard advisory relationship. The household has two adults, straightforward planning needs, and a complete synthetic intake.", | |
| "documents_complete": True, | |
| "identity_status": "VERIFIED", | |
| "relationship_complexity": "STANDARD", | |
| "simulation_mode": "NONE", | |
| }, | |
| "Harbor Family — temporary onboarding-service issue": { | |
| "household_id": "HH-HARBOR-RETRY", | |
| "household_type": "JOINT", | |
| "onboarding_notes": "The fictional Harbor family has a straightforward synthetic intake, but the demo will simulate one temporary package-preparation service interruption.", | |
| "documents_complete": True, | |
| "identity_status": "VERIFIED", | |
| "relationship_complexity": "STANDARD", | |
| "simulation_mode": "TRANSIENT_ONCE", | |
| }, | |
| "Redwood Family Trust — human review required": { | |
| "household_id": "HH-REDWOOD-TRUST", | |
| "household_type": "TRUST", | |
| "onboarding_notes": "The fictional Redwood Family Trust has multiple interested parties and a more complex planning relationship. The synthetic intake is complete but should demonstrate an exception-review path.", | |
| "documents_complete": True, | |
| "identity_status": "VERIFIED", | |
| "relationship_complexity": "COMPLEX", | |
| "simulation_mode": "NONE", | |
| }, | |
| "Cedar Household — permanent dependency failure": { | |
| "household_id": "HH-CEDAR-FAILURE", | |
| "household_type": "JOINT", | |
| "onboarding_notes": "The fictional Cedar household has a standard synthetic intake, but the demo will simulate a permanent downstream package-preparation failure.", | |
| "documents_complete": True, | |
| "identity_status": "VERIFIED", | |
| "relationship_complexity": "STANDARD", | |
| "simulation_mode": "PERMANENT", | |
| }, | |
| } | |
| RunBundle = tuple[str, str, list[list[Any]], list[list[str]], list[list[str]], dict[str, Any]] | |
| AppBundle = tuple[ | |
| str, | |
| str, | |
| list[list[Any]], | |
| list[list[str]], | |
| list[list[str]], | |
| dict[str, Any], | |
| str, | |
| str, | |
| str, | |
| str, | |
| str, | |
| ] | |
| def _ai_runtime_html() -> str: | |
| if LIVE_ONBOARDING_MODEL is not None: | |
| return ( | |
| '<div class="ai-runtime ai-runtime-live"><strong>● Live AI connected</strong><br>' | |
| f'The AI Intake Organizer is using Hugging Face Inference Providers with <code>{LIVE_MODEL_ID}</code>. ' | |
| 'Its output still must pass the same strict Pydantic schema before the workflow can use it.</div>' | |
| ) | |
| if live_ai_enabled_from_env(): | |
| return ( | |
| '<div class="ai-runtime ai-runtime-waiting"><strong>◐ Live AI requested but not connected</strong><br>' | |
| 'LIVE_AI_ENABLED is on, but no usable HF_TOKEN was found. The demo is using its explicit deterministic fallback.</div>' | |
| ) | |
| return ( | |
| '<div class="ai-runtime"><strong>○ AI boundary ready; deterministic fallback active</strong><br>' | |
| 'Live inference is deliberately opt-in. The same bounded node can use Hugging Face Inference Providers when the deployment sets <code>LIVE_AI_ENABLED=true</code> and supplies <code>HF_TOKEN</code>.</div>' | |
| ) | |
| def _build_registry(): | |
| return build_onboarding_registry( | |
| onboarding_model=LIVE_ONBOARDING_MODEL, | |
| model_label=LIVE_MODEL_ID if LIVE_ONBOARDING_MODEL is not None else None, | |
| ) | |
| def run_request_ui( | |
| household_id: str, | |
| household_type: str, | |
| onboarding_notes: str, | |
| documents_complete: bool, | |
| identity_status: str, | |
| relationship_complexity: str, | |
| simulation_mode: str, | |
| ) -> AppBundle: | |
| context = { | |
| "household_id": household_id, | |
| "household_type": household_type, | |
| "onboarding_notes": onboarding_notes, | |
| "documents_complete": documents_complete, | |
| "identity_status": identity_status, | |
| "relationship_complexity": relationship_complexity, | |
| "simulation_mode": simulation_mode, | |
| } | |
| try: | |
| run = execute_workflow(WORKFLOW, _build_registry(), context=context, state_store=STATE_STORE) | |
| return _app_bundle(run) | |
| except WorkflowExecutionError as exc: | |
| return _bundle_with_notice(exc.run, "The onboarding workflow stopped after a controlled failure.") | |
| except Exception as exc: | |
| return _empty_bundle(f"Unable to start onboarding workflow: {type(exc).__name__}") | |
| def run_story_ui(story: str) -> AppBundle: | |
| spec = STORY_SPECS.get(story) | |
| if spec is None: | |
| return _empty_bundle("Choose one of the four onboarding stories before running the workflow.") | |
| return run_request_ui(**spec) | |
| def refresh_run_ui(run_id: str) -> AppBundle: | |
| if not run_id or not run_id.strip(): | |
| return _empty_bundle("Enter a run ID to load a persisted workflow.") | |
| try: | |
| return _app_bundle(STATE_STORE.load(run_id.strip())) | |
| except RunNotFoundError: | |
| return _empty_bundle("No persisted workflow run was found for that ID.", run_id.strip()) | |
| except Exception as exc: | |
| return _empty_bundle(f"Unable to load workflow: {type(exc).__name__}", run_id.strip()) | |
| def submit_human_decision_ui(run_id: str, decision: str) -> AppBundle: | |
| if not run_id or not run_id.strip(): | |
| return _empty_bundle("Enter a run ID before submitting a human decision.") | |
| normalized_run_id = run_id.strip() | |
| try: | |
| run = STATE_STORE.load(normalized_run_id) | |
| except RunNotFoundError: | |
| return _empty_bundle("No persisted workflow run was found for that ID.", normalized_run_id) | |
| open_reviews = [review for review in run.human_reviews if review.decision is None] | |
| if len(open_reviews) != 1: | |
| return _bundle_with_notice(run, "This run does not have exactly one open human review. Reload the run and inspect the Human Review table.") | |
| try: | |
| updated = submit_human_decision( | |
| WORKFLOW, | |
| _build_registry(), | |
| run_id=normalized_run_id, | |
| review_id=open_reviews[0].review_id, | |
| decision=HumanDecision(decision), | |
| state_store=STATE_STORE, | |
| ) | |
| return _app_bundle(updated) | |
| except (WorkflowHumanDecisionError, WorkflowExecutionError) as exc: | |
| return _bundle_with_notice(exc.run, str(exc)) | |
| except ValueError: | |
| return _bundle_with_notice(run, "Unknown human decision.") | |
| except Exception as exc: | |
| return _bundle_with_notice(run, f"Unable to apply decision: {type(exc).__name__}") | |
| def run_evaluation_ui() -> tuple[str, list[list[str]], list[list[str]]]: | |
| report = run_evaluation_suite() | |
| summary = ( | |
| "### Wealth Onboarding Workflow Evaluation\n" | |
| f"**Cases:** {report.passed_cases}/{report.total_cases} passed \n" | |
| f"**Pass rate:** {report.case_pass_rate:.2f} \n" | |
| f"**Duplicate executions:** {report.metrics['duplicate_execution_count']} \n" | |
| f"**Invalid transitions:** {report.metrics['invalid_transition_count']}" | |
| ) | |
| return summary, evaluation_metric_rows(report), evaluation_case_rows(report) | |
| def _app_bundle(run: WorkflowRun) -> AppBundle: | |
| technical: RunBundle = run_bundle(run) | |
| return ( | |
| *technical, | |
| business_outcome_html(run), | |
| workflow_path_html(run), | |
| ai_insight_html(run), | |
| routing_explanation_html(run), | |
| business_journey_html(run), | |
| ) | |
| def _bundle_with_notice(run: WorkflowRun, notice: str) -> AppBundle: | |
| technical = run_bundle(run) | |
| return ( | |
| f"> {notice}\n\n{technical[0]}", | |
| *technical[1:], | |
| business_outcome_html(run), | |
| workflow_path_html(run), | |
| ai_insight_html(run), | |
| routing_explanation_html(run), | |
| business_journey_html(run), | |
| ) | |
| def _empty_bundle(message: str, run_id: str = "") -> AppBundle: | |
| return ( | |
| f"### Workflow Status\n{message}", run_id, [], [], [], {}, | |
| EMPTY_OUTCOME_HTML, EMPTY_PATH_HTML, EMPTY_AI_HTML, EMPTY_ROUTING_HTML, EMPTY_JOURNEY_HTML, | |
| ) | |
| def _running_bundle(run_id: str = "") -> AppBundle: | |
| return ( | |
| "### Workflow running…\nThe workflow engine is controlling the approved next steps.", | |
| run_id, | |
| [], [], [], {}, | |
| EMPTY_OUTCOME_HTML, EMPTY_PATH_HTML, EMPTY_AI_HTML, EMPTY_ROUTING_HTML, EMPTY_JOURNEY_HTML, | |
| ) | |
| def _stream_bundle(bundle: AppBundle): | |
| run_id = bundle[1] | |
| if not run_id: | |
| yield ('<div class="activity-card"><div class="activity-badge">STOPPED</div><div class="activity-title">Workflow did not start</div></div>', *bundle) | |
| return | |
| try: | |
| run = STATE_STORE.load(run_id) | |
| except Exception: | |
| yield ('<div class="activity-card"><div class="activity-badge">STOPPED</div><div class="activity-title">Workflow trace unavailable</div></div>', *bundle) | |
| return | |
| running = _running_bundle(run_id) | |
| for index, event in enumerate(run.events, start=1): | |
| yield (activity_html(run, index), *running) | |
| time.sleep(playback_delay(event.event_type.value)) | |
| yield (activity_html(run, len(run.events), complete=True), *bundle) | |
| def stream_story_ui(story: str): | |
| yield (activity_start_html(story), *_running_bundle()) | |
| yield from _stream_bundle(run_story_ui(story)) | |
| def stream_request_ui( | |
| household_id: str, | |
| household_type: str, | |
| onboarding_notes: str, | |
| documents_complete: bool, | |
| identity_status: str, | |
| relationship_complexity: str, | |
| simulation_mode: str, | |
| ): | |
| yield (activity_start_html("Custom synthetic household"), *_running_bundle()) | |
| bundle = run_request_ui( | |
| household_id, household_type, onboarding_notes, documents_complete, | |
| identity_status, relationship_complexity, simulation_mode, | |
| ) | |
| yield from _stream_bundle(bundle) | |
| def build_app() -> gr.Blocks: | |
| with gr.Blocks(title="Agent 9 — Wealth Onboarding Workflow", analytics_enabled=False) as demo: | |
| gr.HTML(HERO_HTML) | |
| gr.HTML(PATTERN_HTML) | |
| gr.HTML(_ai_runtime_html()) | |
| gr.Markdown("## Try the onboarding pattern in one click") | |
| gr.Markdown("Choose a fictional household and watch the workflow control plane move it through AI assistance, deterministic rules, retry/recovery, and human escalation.") | |
| story = gr.Radio(choices=list(STORY_SPECS), value="Harbor Family — straightforward household", label="Choose an onboarding story") | |
| story_button = gr.Button("Run this onboarding workflow", variant="primary") | |
| live_activity = gr.HTML(EMPTY_ACTIVITY_HTML) | |
| business_outcome = gr.HTML(EMPTY_OUTCOME_HTML) | |
| workflow_path = gr.HTML(EMPTY_PATH_HTML) | |
| ai_insight = gr.HTML(EMPTY_AI_HTML) | |
| routing_explanation = gr.HTML(EMPTY_ROUTING_HTML) | |
| business_journey = gr.HTML(EMPTY_JOURNEY_HTML) | |
| with gr.Accordion("Reload a saved run", open=False): | |
| gr.Markdown("Every run receives a random ID and is checkpointed in SQLite. Reloading a completed run shows the same state without replaying completed work.") | |
| with gr.Row(): | |
| current_run_id = gr.Textbox(label="Run ID", placeholder="A random run ID appears here after execution.") | |
| refresh_button = gr.Button("Reload Saved Run") | |
| with gr.Tabs(): | |
| with gr.Tab("Customize Scenario"): | |
| gr.Markdown("### Build your own synthetic onboarding scenario\nThese inputs are fictional and exist only to demonstrate workflow behavior.") | |
| household_id = gr.Textbox(label="Household ID", value="HH-DEMO-001") | |
| household_type = gr.Dropdown(choices=["INDIVIDUAL", "JOINT", "TRUST", "ENTITY"], value="JOINT", label="Household Type") | |
| documents_complete = gr.Checkbox(label="Onboarding documents complete", value=True) | |
| onboarding_notes = gr.Textbox(label="Synthetic Onboarding Notes", value="Fictional household seeking a standard advisory relationship. All information in this demo is synthetic.", lines=4) | |
| identity_status = gr.Dropdown(choices=["VERIFIED", "REVIEW_REQUIRED"], value="VERIFIED", label="Identity Check Status") | |
| relationship_complexity = gr.Dropdown(choices=["STANDARD", "COMPLEX"], value="STANDARD", label="Relationship Complexity") | |
| simulation_mode = gr.Dropdown(choices=["NONE", "TRANSIENT_ONCE", "PERMANENT"], value="NONE", label="Failure Simulation") | |
| gr.Markdown("**Helpful shortcuts:** TRUST/ENTITY, COMPLEX, missing documents, or REVIEW_REQUIRED demonstrates human review. TRANSIENT_ONCE demonstrates bounded retry recovery.") | |
| run_button = gr.Button("Run custom workflow", variant="primary") | |
| with gr.Tab("Engineering State"): | |
| gr.Markdown("### Engineering evidence\nInspect exact workflow status, node state, attempt counts, errors, and structured outputs—including the AI Intake Organizer output and its source.") | |
| status = gr.Markdown("### Workflow Status\nRun a synthetic household to begin.") | |
| node_table = gr.Dataframe(headers=NODE_HEADERS, interactive=False, label="Node Runtime State") | |
| final_output = gr.JSON(label="Final Output") | |
| with gr.Tab("Audit Trail"): | |
| gr.Markdown("### Why the workflow is auditable\nEvery meaningful control transition is recorded. The audit timeline contains control metadata only; onboarding context is not copied into event details.") | |
| event_table = gr.Dataframe(headers=EVENT_HEADERS, interactive=False, label="Append-Only Workflow Events") | |
| with gr.Tab("Human Review"): | |
| gr.Markdown("### The human remains in control\nA fictional exception case pauses here. The workflow cannot mark the package ready until a person explicitly decides what should happen.") | |
| review_table = gr.Dataframe(headers=REVIEW_HEADERS, interactive=False, label="Human Review History") | |
| human_decision = gr.Dropdown(choices=["APPROVE", "REJECT", "RETRY"], value="APPROVE", label="What should the reviewer do?") | |
| decision_button = gr.Button("Submit Human Decision", variant="primary") | |
| gr.Markdown("**APPROVE** continues the approved exception path. **REJECT** ends the fictional process as `REJECTED`, not `FAILED`. **RETRY** opens a fresh review without replaying completed upstream work. This is illustrative, not any firm's actual regulatory procedure.") | |
| with gr.Tab("Evaluation"): | |
| gr.Markdown("### Reliability evidence\nThe same onboarding engine is exercised across success, retry, human review, persistence, failure, and adversarial-control scenarios.") | |
| evaluation_summary = gr.Markdown("Run the deterministic evaluation harness to inspect system-level metrics.") | |
| evaluation_button = gr.Button("Run 10-Case Evaluation") | |
| metric_table = gr.Dataframe(headers=METRIC_HEADERS, interactive=False, label="Evaluation Metrics") | |
| case_table = gr.Dataframe(headers=CASE_HEADERS, interactive=False, label="Evaluation Cases") | |
| run_outputs = [ | |
| status, current_run_id, node_table, event_table, review_table, final_output, | |
| business_outcome, workflow_path, ai_insight, routing_explanation, business_journey, | |
| ] | |
| stream_outputs = [live_activity, *run_outputs] | |
| story_button.click(fn=stream_story_ui, inputs=[story], outputs=stream_outputs, show_progress="hidden") | |
| run_button.click( | |
| fn=stream_request_ui, | |
| inputs=[household_id, household_type, onboarding_notes, documents_complete, identity_status, relationship_complexity, simulation_mode], | |
| outputs=stream_outputs, | |
| show_progress="hidden", | |
| ) | |
| refresh_button.click(fn=refresh_run_ui, inputs=[current_run_id], outputs=run_outputs) | |
| decision_button.click(fn=submit_human_decision_ui, inputs=[current_run_id, human_decision], outputs=run_outputs) | |
| evaluation_button.click(fn=run_evaluation_ui, outputs=[evaluation_summary, metric_table, case_table]) | |
| return demo | |
| demo = build_app() | |
| if __name__ == "__main__": | |
| demo.queue().launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", "7860")), css=APP_CSS) | |