import gradio as gr import asyncio import json import time import io from typing import List from langchain_core.messages import AIMessage, HumanMessage, ToolMessage from core.agents.SkeletonGraphAgent import SkeletonGraphAgent from core.config.prompt import get_default_system_prompt from core.config.metadata import create_metadata # ----- State ----- conversation_history = [] # ----- Agent Initialization ----- async def initialize_agent_async(metadata): try: new_agent = await SkeletonGraphAgent.create(metadata) return new_agent, None except Exception as e: return None, str(e) def initialize_agent_handler(model_name, temperature, max_tokens, system_prompt, api_key): metadata = create_metadata(model_name, temperature, max_tokens, system_prompt, api_key) # Use the existing event loop if available, otherwise create a new one try: loop = asyncio.get_event_loop() except RuntimeError: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) agent, error = loop.run_until_complete(initialize_agent_async(metadata)) if agent: return agent, "✅ Agent initialized successfully!", "🟢 Agent Ready" else: return None, f"❌ Failed to initialize agent: {error}", "🔴 Agent Failed" # ----- Chat Handler ----- async def get_agent_response_async(agent, user_input): global conversation_history human_msg = HumanMessage(content=user_input) conversation_history.append(human_msg) input_message = {"messages": conversation_history.copy()} try: result = await agent.workflow.ainvoke(input=input_message) if result and 'messages' in result: for msg in reversed(result['messages']): if isinstance(msg, AIMessage): conversation_history.append(msg) return msg.content, None fallback = AIMessage(content="No response generated") conversation_history.append(fallback) return fallback.content, None except Exception as e: conversation_history.pop() return None, str(e) def chat_with_agent(agent, user_input, chat_history): if not agent: chat_history.append([user_input, "❌ Please initialize your agent first."]) return chat_history, "" if not user_input.strip(): return chat_history, "" # Use the existing event loop if available, otherwise create a new one try: loop = asyncio.get_event_loop() except RuntimeError: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) response, error = loop.run_until_complete(get_agent_response_async(agent, user_input)) if response: chat_history.append([user_input, response]) else: chat_history.append([user_input, f"❌ Error: {error}"]) return chat_history, "" def reset_conversation(): global conversation_history conversation_history = [] return [], "🔄 Conversation reset!", "🟢 Agent Ready" # ----- Gradio Interface ----- def create_interface(): with gr.Blocks( title="TrackMate AI", theme=gr.themes.Soft(), css=""" /* Dark theme colors */ :root { --bg-primary: #121212; --bg-secondary: #1e1e1e; --bg-tertiary: #252525; --text-primary: #e0e0e0; --text-secondary: #a0a0a0; --accent-primary: #3a506b; --accent-secondary: #1c2541; --accent-tertiary: #0b132b; --highlight: #5bc0be; --error: #ff6b6b; --success: #6bd425; } .documentation-container { background-color: var(--bg-secondary); color: var(--text-primary); padding: 2rem; border-radius: 15px; margin-bottom: 2rem; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); } .documentation-header { text-align: center; margin-bottom: 1.5rem; } .documentation-section { background-color: var(--bg-tertiary); padding: 1.5rem; border-radius: 10px; margin-bottom: 1.5rem; border-left: 4px solid var(--highlight); } .config-panel { background-color: var(--bg-secondary); padding: 1.5rem; border-radius: 15px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); height: 100%; display: flex; flex-direction: column; } .chat-panel { background-color: var(--bg-secondary); padding: 1.5rem; border-radius: 15px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); height: 100%; display: flex; flex-direction: column; } .panel-header { text-align: center; padding: 1rem; background-color: var(--accent-primary); color: var(--text-primary); border-radius: 10px; margin-bottom: 1rem; } .tools-table { width: 100%; border-collapse: collapse; margin-top: 1rem; } .tools-table th, .tools-table td { padding: 0.75rem; text-align: left; border-bottom: 1px solid var(--accent-secondary); } .tools-table th { font-weight: bold; background-color: var(--accent-tertiary); } .feature-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 0.5rem; margin-top: 1rem; } .feature-item { background-color: var(--accent-secondary); padding: 0.75rem; border-radius: 10px; } a { color: var(--highlight); text-decoration: none; font-weight: bold; } a:hover { text-decoration: underline; } /* Chat avatar styling */ .chatbot .avatar { display: flex !important; align-items: center; justify-content: center; width: 40px !important; height: 40px !important; border-radius: 50%; background-color: var(--accent-secondary); color: var(--text-primary); font-size: 20px; margin-right: 10px; } /* Ensure chat messages have proper contrast */ .chatbot .user-message { background-color: var(--accent-primary) !important; color: var(--text-primary) !important; border-radius: 10px !important; } .chatbot .bot-message { background-color: var(--accent-secondary) !important; color: var(--text-primary) !important; border-radius: 10px !important; } /* Override any theme styles that might hide avatars */ .chatbot .message-container { display: flex !important; align-items: flex-start !important; } """ ) as app: agent_state = gr.State(value=None) # Consolidated Documentation Section gr.HTML("""

🤖 TrackMate AI

Your smart companion for tracking and managing orders.

📋 About TrackMate AI

TrackMate AI is a Gradio-powered autonomous agent designed to interface with an ERP system via MCP tools. It connects to a custom MCP Server and lets users chat with an LLM to place, modify, or track orders — all through natural language. Designed for seamless interaction with ERP infrastructure, TrackMate AI empowers users to manage operational workflows via a conversational interface — no dashboards, no dropdowns, just intelligent dialogue. Whether you're tracking an order, generating invoices, or accessing global risk intelligence, TrackMate AI brings an intuitive, natural language layer to your business operations.

📺 Demo Video

📹 Watch Demo Video - TrackMate AI Agent

✨ What It Can Do

🔍 Query live ERP data (orders, invoices, customers)
�� Place or cancel orders
🌐 Check for geopolitical disruptions
🧠 Leverage multiple tools via MCP + Gradio agent framework
🧮 Use a calculator for quick computations
☀️🌧️ Get live weather updates for any location

🔌 Connected Tools

TrackMate AI uses a bunch of tools provided by the TrackMate AI MCP Server :

Tool Name Description
list_tables Lists all ERP database tables
execute_query Executes a custom SQL query
get_order_status Gets status of an order
place_order Places a new order
cancel_order Cancels an existing order
get_active_disruptions Returns geopolitical disruptions between nations
add, subtract, multiple Use a simple calculator
""") # Main Interface with gr.Row(equal_height=True): # Configuration Panel with gr.Column(scale=1): with gr.Group(elem_classes="config-panel"): gr.HTML('

⚙️ Agent Configuration

') model_name = gr.Dropdown( choices=["gpt-4o-mini", "gpt-4o", "gpt-4"], value="gpt-4o", label="🤖 Model", info="Select the OpenAI model to use" ) temperature = gr.Slider( 0.0, 1.0, value=0.7, step=0.1, label="🌡️ Temperature", info="Controls randomness (0=deterministic, 1=creative)" ) max_tokens = gr.Number( value=1000, minimum=100, maximum=4000, step=100, label="📏 Max Tokens", info="Maximum response length" ) api_key = gr.Textbox( value="", label="🔑 API Key", placeholder="Enter your OpenAI API key", type="password", interactive=True, info="Your OpenAI API key (kept secure)" ) with gr.Accordion("📝 System Prompt", open=False): system_prompt = gr.Textbox( value=get_default_system_prompt(), lines=10, label="System Prompt", interactive=True, info="Customize the agent's behavior and instructions" ) init_btn = gr.Button("🚀 Initialize Agent", variant="primary", size="lg") with gr.Group(): status_display = gr.Textbox( label="📊 Status", interactive=False, info="Current operation status" ) agent_status = gr.Textbox( label="🤖 Agent Status", interactive=False, info="Agent readiness indicator" ) # Chat Panel with gr.Column(scale=3, min_width=700): with gr.Group(elem_classes="chat-panel"): gr.HTML('

💬 Chat Interface

') chatbot = gr.Chatbot( label="TrackMate AI Chat", height=650, show_label=False, show_copy_button=True, elem_classes="chatbot" ) # Make the message input take full width msg_input = gr.Textbox( label="💭 Message", placeholder="Ask me about orders, inventory, weather, or anything else...", show_label=False ) send_btn = gr.Button("📤 Send", variant="primary", scale=1) reset_btn = gr.Button("🔄 Reset Chat", variant="secondary", scale=1) # Event handlers init_btn.click( fn=initialize_agent_handler, inputs=[model_name, temperature, max_tokens, system_prompt, api_key], outputs=[agent_state, status_display, agent_status] ) reset_btn.click( fn=reset_conversation, outputs=[chatbot, status_display, agent_status] ) send_btn.click( fn=chat_with_agent, inputs=[agent_state, msg_input, chatbot], outputs=[chatbot, msg_input] ) msg_input.submit( fn=chat_with_agent, inputs=[agent_state, msg_input, chatbot], outputs=[chatbot, msg_input] ) return app if __name__ == "__main__": app = create_interface() app.launch(server_name="0.0.0.0", server_port=7860, ssr_mode=False, share=True)