Skip to content

Commit a661b4b

Browse files
authored
Update for support (#10)
* feat: Add QA tickets management with new TicketList component and API integration * feat: Add initial diagram for project planning in explain.drawio Signed-off-by: Andre Bossard <anbossar@microsoft.com> * feat: Add RULES.md to document project guidelines Signed-off-by: Andre Bossard <anbossar@microsoft.com> * feat: Add ticket models and reminder functionality for "Assigned without Assignee" Signed-off-by: Andre Bossard <anbossar@microsoft.com> * refactor: Rearrange imports and enhance startup logging for REST API and MCP JSON-RPC Signed-off-by: Andre Bossard <anbossar@microsoft.com> * feat: Enhance ticket handling by adding mapping functions and updating QA tickets endpoint Signed-off-by: Andre Bossard <anbossar@microsoft.com> * feat: Add TicketsWithoutAnAssignee component to display unassigned tickets Signed-off-by: Andre Bossard <anbossar@microsoft.com> * refactor: Clean up code formatting and improve ticket handling in various components Signed-off-by: Andre Bossard <anbossar@microsoft.com> * Refactor Ollama integration to use Azure OpenAI agent; remove OllamaChat component and related API calls, add AgentChat component for task management; update frontend routing and backend operations accordingly. Signed-off-by: Andre Bossard <anbossar@microsoft.com> * feat: Enhance AgentService with detailed logging for MCP tool calls and agent execution Signed-off-by: Andre Bossard <anbossar@microsoft.com> --------- Signed-off-by: Andre Bossard <anbossar@microsoft.com>
1 parent 529a1c4 commit a661b4b

18 files changed

Lines changed: 2560 additions & 1196 deletions

File tree

.vscode/launch.json

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,7 @@
1919
"request": "launch",
2020
"cwd": "${workspaceFolder}/frontend",
2121
"runtimeExecutable": "npm",
22-
"runtimeArgs": [
23-
"run",
24-
"dev"
25-
],
22+
"runtimeArgs": ["run", "dev"],
2623
"console": "integratedTerminal"
2724
},
2825
{
@@ -42,11 +39,7 @@
4239
"compounds": [
4340
{
4441
"name": "Full Stack: Backend + Frontend",
45-
"configurations": [
46-
"Python: Quart Backend",
47-
"Frontend: Vite Dev Server",
48-
"Frontend: Chromium"
49-
]
42+
"configurations": ["Python: Quart Backend", "Frontend: Vite Dev Server"]
5043
}
5144
]
5245
}

backend/agents.py

Lines changed: 77 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,20 @@
3939

4040
load_dotenv()
4141

42+
# Ensure operations register before we request LangChain tools
43+
import operations # noqa: F401
44+
4245
# Local - Import operations registry for automatic tool discovery
4346
from api_decorators import get_langchain_tools
47+
4448
# Third-party - FastMCP client for external MCP servers
4549
from fastmcp import Client as MCPClient
4650
from langchain_core.tools import StructuredTool
51+
4752
# Third-party - LangChain and LangGraph
4853
from langchain_openai import AzureChatOpenAI
4954
from langgraph.prebuilt import create_react_agent
55+
5056
# Third-party - Pydantic for validation
5157
from pydantic import BaseModel, Field, create_model, field_validator
5258

@@ -193,12 +199,31 @@ def _mcp_tool_to_langchain(mcp_client: MCPClient, tool: Any) -> StructuredTool:
193199

194200
# Create async wrapper that calls MCP server
195201
async def call_mcp_tool(**kwargs) -> str:
202+
import json as _json
203+
print(f"\n{'='*60}")
204+
print(f"🔧 MCP TOOL CALL: {tool_name}")
205+
print(f"{'='*60}")
206+
print(f"📤 REQUEST:")
207+
print(f" Tool: {tool_name}")
208+
print(f" Args: {_json.dumps(kwargs, indent=6, default=str)}")
209+
196210
result = await mcp_client.call_tool(tool_name, kwargs)
211+
212+
print(f"\n📥 RESPONSE:")
197213
# Extract text from MCP response
198214
if hasattr(result, 'content') and result.content:
199215
texts = [c.text for c in result.content if hasattr(c, 'text')]
200-
return "\n".join(texts) if texts else str(result)
201-
return str(result)
216+
response_text = "\n".join(texts) if texts else str(result)
217+
# Truncate for display if too long
218+
display_text = response_text[:500] + "..." if len(response_text) > 500 else response_text
219+
print(f" Content items: {len(result.content)}")
220+
print(f" Text preview: {display_text}")
221+
else:
222+
response_text = str(result)
223+
print(f" Raw: {response_text[:500]}")
224+
225+
print(f"{'='*60}\n")
226+
return response_text
202227

203228
# Build Pydantic model from input schema
204229
args_model = _schema_to_pydantic(tool_name, input_schema)
@@ -282,11 +307,16 @@ async def _ensure_ticket_mcp_connection(self):
282307

283308
# Fetch and convert ticket MCP tools
284309
mcp_tools = await client.list_tools()
310+
print(f"\n{'='*60}")
311+
print(f"🎫 TICKET MCP SERVER CONNECTED")
312+
print(f"{'='*60}")
313+
print(f" URL: {TICKET_MCP_SERVER_URL}")
314+
print(f" Tools available: {len(mcp_tools)}")
285315
for tool in mcp_tools:
286316
lc_tool = _mcp_tool_to_langchain(client, tool)
287317
self.tools.append(lc_tool)
288-
289-
print(f"DEBUG: Loaded {len(mcp_tools)} ticket tools from MCP server {TICKET_MCP_SERVER_URL}")
318+
print(f" ✓ {tool.name}: {(tool.description or '')[:60]}...")
319+
print(f"{'='*60}\n")
290320
self._ticket_mcp_tools_loaded = True
291321

292322
except Exception as e:
@@ -336,28 +366,58 @@ async def run_agent(self, request: AgentRequest) -> AgentResponse:
336366

337367
# System message to guide the agent's behavior
338368
system_msg = (
339-
"You are a helpful task management assistant. "
340-
"You MUST use the available tools to perform all actions. "
341-
"NEVER pretend or simulate creating, updating, or listing tasks - you MUST call the actual tools. "
342-
"Available tools: create_task, update_task, delete_task, list_tasks, get_task, get_task_stats. "
343-
"When asked to create tasks, call create_task for each one. "
344-
"When asked to list tasks, call list_tasks. "
345-
"Always use tools and confirm what you've done based on the tool results."
369+
"You are a support ticket management assistant. "
370+
"Your primary role is to help users manage, search, and evaluate support tickets. "
371+
"You MUST use the available tools to perform all actions - NEVER simulate or pretend. "
372+
"\n\n"
373+
"TICKET CAPABILITIES:\n"
374+
"- Search and list tickets by status, priority, city, service\n"
375+
"- Get detailed ticket information and work logs\n"
376+
"- Analyze ticket statistics and trends\n"
377+
"- Request modifications to tickets (status, priority, assignee, etc.)\n"
378+
"- Review and approve/reject modification requests\n"
379+
"\n"
380+
"TASK CAPABILITIES:\n"
381+
"- Create, update, delete, and list tasks\n"
382+
"- Get task statistics\n"
383+
"\n"
384+
"When users ask about tickets, use the ticket tools (list_tickets, get_ticket, search_tickets, etc.). "
385+
"When users ask about tasks, use task tools (create_task, list_tasks, etc.). "
386+
"Always confirm actions based on actual tool results."
346387
)
347388

348389
# Execute agent with user prompt
349-
print(f"DEBUG: Running agent with {len(self.tools)} tools")
350-
print(f"DEBUG: Tools: {[t.name if hasattr(t, 'name') else str(t) for t in self.tools]}")
390+
print(f"\n{'='*60}")
391+
print(f"🤖 AGENT EXECUTION START")
392+
print(f"{'='*60}")
393+
print(f" Prompt: {request.prompt[:100]}{'...' if len(request.prompt) > 100 else ''}")
394+
print(f" Agent type: {request.agent_type}")
395+
print(f" Available tools ({len(self.tools)}):")
396+
for t in self.tools:
397+
name = t.name if hasattr(t, 'name') else str(t)
398+
print(f" • {name}")
399+
print(f"{'='*60}\n")
351400

352401
result = await agent.ainvoke(
353402
{"messages": [("system", system_msg), ("user", request.prompt)]}
354403
)
355404

356-
print(f"DEBUG: Agent result messages: {len(result['messages'])}")
405+
print(f"\n{'='*60}")
406+
print(f"📋 AGENT EXECUTION COMPLETE")
407+
print(f"{'='*60}")
408+
print(f" Total messages: {len(result['messages'])}")
357409
for i, msg in enumerate(result["messages"]):
358-
print(f"DEBUG: Message {i}: type={type(msg).__name__}, has_tool_calls={hasattr(msg, 'tool_calls')}")
359-
if hasattr(msg, 'tool_calls') and msg.tool_calls:
360-
print(f"DEBUG: Tool calls: {msg.tool_calls}")
410+
msg_type = type(msg).__name__
411+
has_tool_calls = hasattr(msg, 'tool_calls') and msg.tool_calls
412+
content_preview = ""
413+
if hasattr(msg, 'content') and msg.content:
414+
content_preview = str(msg.content)[:80] + "..." if len(str(msg.content)) > 80 else str(msg.content)
415+
print(f" [{i}] {msg_type}: {content_preview}")
416+
if has_tool_calls:
417+
for tc in msg.tool_calls:
418+
tc_name = tc.get('name', tc) if isinstance(tc, dict) else str(tc)
419+
print(f" 🔧 Tool call: {tc_name}")
420+
print(f"{'='*60}\n")
361421

362422
# Extract the agent's final response
363423
final_message = result["messages"][-1]

0 commit comments

Comments
 (0)