cjoyy/ops-agent
0
1from uuid import uuid42 3import gradio as gr4from langgraph.checkpoint.sqlite import SqliteSaver5from langgraph.types import Command6 7from graph_supervisor import get_app, get_message_content8from main import build_initial_state9 10 11def format_interrupt(interrupts) -> str:12 if not interrupts:13 return "Graph paused, but no interrupt payload was returned."14 15 payload = interrupts[0].value16 return (17 "Security approval required\n\n"18 f"Action: {payload.get('action')}\n"19 f"Ticket summary: {payload.get('ticket_summary')}\n"20 f"Proposed action: {payload.get('proposed_action')}"21 )22 23 24def format_final_result(result: dict) -> str:25 return (26 f"Category: {result.get('category')}\n"27 f"Resolved categories: {result.get('resolved_categories', [])}\n\n"28 f"{get_message_content(result['messages'][-1])}"29 )30 31 32def run_ticket(ticket: str):33 if not ticket.strip():34 return "Please enter a ticket.", None, gr.update(visible=False), gr.update(visible=False)35 36 thread_id = f"space-{uuid4()}"37 config = {"configurable": {"thread_id": thread_id}}38 39 with SqliteSaver.from_conn_string("checkpoints.sqlite") as checkpointer:40 app = get_app(checkpointer)41 result = app.invoke(build_initial_state(ticket), config=config)42 43 if "__interrupt__" in result:44 return (45 format_interrupt(result["__interrupt__"]),46 thread_id,47 gr.update(visible=True),48 gr.update(visible=True),49 )50 51 return format_final_result(result), None, gr.update(visible=False), gr.update(visible=False)52 53 54def resume_ticket(thread_id: str, approved: bool):55 if not thread_id:56 return "No paused security ticket to resume.", None, gr.update(visible=False), gr.update(visible=False)57 58 config = {"configurable": {"thread_id": thread_id}}59 60 with SqliteSaver.from_conn_string("checkpoints.sqlite") as checkpointer:61 app = get_app(checkpointer)62 result = app.invoke(Command(resume={"approved": approved}), config=config)63 64 if "__interrupt__" in result:65 return (66 format_interrupt(result["__interrupt__"]),67 thread_id,68 gr.update(visible=True),69 gr.update(visible=True),70 )71 72 return format_final_result(result), None, gr.update(visible=False), gr.update(visible=False)73 74 75with gr.Blocks(title="Ops Agent") as demo:76 gr.Markdown("# Ops Agent")77 gr.Markdown(78 "Multi-agent IT/ops assistant with LangGraph routing, RAG runbooks, tools, "79 "human approval, and SQLite checkpointing."80 )81 82 ticket = gr.Textbox(83 label="Ticket",84 placeholder="Service auth-api saya stuck, gimana cara restart yang benar?",85 lines=4,86 )87 submit = gr.Button("Run Ticket", variant="primary")88 output = gr.Textbox(label="Result", lines=14)89 thread_state = gr.State(None)90 91 with gr.Row():92 approve = gr.Button("Approve Security Action", visible=False)93 reject = gr.Button("Reject Security Action", visible=False)94 95 submit.click(96 run_ticket,97 inputs=[ticket],98 outputs=[output, thread_state, approve, reject],99 )100 approve.click(101 lambda thread_id: resume_ticket(thread_id, True),102 inputs=[thread_state],103 outputs=[output, thread_state, approve, reject],104 )105 reject.click(106 lambda thread_id: resume_ticket(thread_id, False),107 inputs=[thread_state],108 outputs=[output, thread_state, approve, reject],109 )110 111 112if __name__ == "__main__":113 demo.launch(server_name="0.0.0.0", server_port=7860)114 