factoryflow / src /demo /app.py
oabolade23's picture
Honor GRADIO_SHARE and GRADIO_PORT env vars; add live demo links
751f645
Raw
History Blame Contribute Delete
7.14 kB
"""FactoryFlow Gradio demo — single-page judge-facing UI.
Run: PYTHONPATH=. python -m src.demo.app
Then open http://localhost:7860
Layout (4 panels):
1. Sensor feed — live anomaly score line chart, polled every 2s
2. Inference panel — score gauge + RUL countdown
3. Agent activity log — scrolling text of CrewAI agent steps + auth/pay
4. Procurement result — supplier card with auth + payment status
"""
from __future__ import annotations
import asyncio
import json
import os
from typing import Any, Generator
import gradio as gr
import numpy as np
import structlog
from dotenv import load_dotenv
from src.agents.orchestrator import run_cycle
from src.agents.tools import sensor_tool
from src.auth.proxlock import request_authorization
from src.demo.components import DemoState, format_gauge, format_supplier_card
from src.inference.anomaly_detector import detect
from src.payments.x402_client import execute_purchase
from src.sensor.simulator import BearingFaultSimulator
load_dotenv()
log = structlog.get_logger()
POLL_INTERVAL_S = 2.0
_demo_simulator = BearingFaultSimulator() # used by the auto-poller only
def _poll_sensor(state: DemoState) -> DemoState:
window = _demo_simulator.generate_window()
fft = np.array(window.fft_window, dtype=np.float32)
result = detect(fft)
state.last_state_label = _demo_simulator.state
state.last_dominant_hz = window.dominant_freq_hz
state.append_score(result.score, result.rul_hours)
return state
def _set_state(label: str, state: DemoState) -> DemoState:
_demo_simulator.set_state(label)
sensor_tool.force_state(label) # keep agent's in-process simulator in sync
state.log(f"sensor state forced → {label}")
return state
def on_poll(state: DemoState):
state = _poll_sensor(state)
df = state.score_dataframe()
last_score = state.score_points[-1][1] if state.score_points else 0.0
last_rul = state.score_points[-1][2] if state.score_points else 0.0
gauge = format_gauge(last_score, last_rul, state.last_state_label, state.last_dominant_hz)
return state, df, gauge
def on_force_state(label: str, state: DemoState):
state = _set_state(label, state)
return state, state.log_text()
def on_run_cycle(state: DemoState) -> Generator[tuple[Any, ...], None, None]:
"""Generator: streams updates to log + supplier card as the pipeline runs."""
state.log("▶ kicking off CrewAI cycle (Engineer → Procurement)")
yield state, state.log_text(), format_supplier_card(state)
try:
crew_out = run_cycle(force_state=None)
except Exception as exc:
state.log(f"✗ Crew failed: {exc}")
yield state, state.log_text(), format_supplier_card(state)
return
eng = crew_out.get("engineer", {})
proc = crew_out.get("procurement", {})
state.log(f"engineer → SKU={eng.get('part_sku')} urgency={eng.get('urgency')}")
state.log(f"procurement → {proc.get('selected_supplier')} @ ${proc.get('unit_price_usd')}")
state.procurement = proc
yield state, state.log_text(), format_supplier_card(state)
if not proc.get("selected_supplier"):
state.log("· no procurement action required, stopping")
yield state, state.log_text(), format_supplier_card(state)
return
sku = proc.get("part_sku") or eng.get("part_sku") or ""
amount = float(proc.get("unit_price_usd") or 0.0)
supplier = proc.get("selected_supplier") or ""
purchase_url = proc.get("purchase_url") or ""
state.log(f"requesting Proxlock authorization for ${amount:.2f}…")
yield state, state.log_text(), format_supplier_card(state)
auth = asyncio.run(request_authorization(sku, amount))
state.auth = auth.as_dict()
state.log(f"auth → authorized={auth.authorized} approver={auth.approver}")
yield state, state.log_text(), format_supplier_card(state)
if not auth.authorized:
state.log("✗ authorization denied — payment skipped")
yield state, state.log_text(), format_supplier_card(state)
return
state.log("executing X402 payment…")
yield state, state.log_text(), format_supplier_card(state)
pay = asyncio.run(execute_purchase(sku, amount, supplier, purchase_url, auth))
state.payment = pay.as_dict()
state.log(f"payment → status={pay.status} txn={pay.transaction_id}")
yield state, state.log_text(), format_supplier_card(state)
def build_app() -> gr.Blocks:
with gr.Blocks(title="FactoryFlow — Autonomous Predictive Maintenance") as app:
gr.Markdown(
"# FactoryFlow\n"
"Sensor → MOMENT anomaly detection (AMD GPU) → CrewAI agents → "
"Proxlock auth → X402 payment. End-to-end autonomous procurement."
)
state = gr.State(DemoState())
with gr.Row():
with gr.Column(scale=2):
gr.Markdown("### Vibration sensor — anomaly score (live)")
chart = gr.LinePlot(
x="tick",
y="anomaly_score",
x_title="window #",
y_title="anomaly score",
y_lim=[0.0, 1.0],
height=260,
show_label=False,
)
with gr.Column(scale=1):
gauge = gr.Markdown(format_gauge(0.0, 48.0, "normal", 0.0))
with gr.Row():
btn_normal = gr.Button("Normal", size="sm")
btn_degrade = gr.Button("Degrading", size="sm")
btn_fail = gr.Button("Imminent failure", size="sm", variant="stop")
with gr.Row():
with gr.Column():
gr.Markdown("### Agent activity log")
log_box = gr.Textbox(
value="",
lines=14,
max_lines=14,
interactive=False,
show_label=False,
)
run_btn = gr.Button("▶ Run agent cycle", variant="primary")
with gr.Column():
gr.Markdown("### Procurement")
card = gr.Markdown(format_supplier_card(DemoState()))
timer = gr.Timer(POLL_INTERVAL_S)
timer.tick(on_poll, inputs=[state], outputs=[state, chart, gauge])
btn_normal.click(on_force_state, inputs=[gr.State("normal"), state],
outputs=[state, log_box])
btn_degrade.click(on_force_state, inputs=[gr.State("degrading"), state],
outputs=[state, log_box])
btn_fail.click(on_force_state, inputs=[gr.State("imminent_failure"), state],
outputs=[state, log_box])
run_btn.click(on_run_cycle, inputs=[state], outputs=[state, log_box, card])
return app
def main() -> None:
app = build_app()
share = os.getenv("GRADIO_SHARE", "false").lower() in ("1", "true", "yes")
port = int(os.getenv("GRADIO_PORT", "7860"))
app.queue().launch(
server_name="0.0.0.0",
server_port=port,
share=share,
show_error=True,
)
if __name__ == "__main__":
main()