Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
app-logs.zip
LogFiles
deploy.log
__pycache__
*.log

# .tfstate files
*.tfstate
Expand Down
4 changes: 2 additions & 2 deletions TROUBLESHOOTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ terraform apply

<!-- START BADGE -->
<div align="center">
<img src="https://img.shields.io/badge/Total%20views-1543-limegreen" alt="Total views">
<p>Refresh Date: 2025-11-25</p>
<img src="https://img.shields.io/badge/Total%20views-1557-limegreen" alt="Total views">
<p>Refresh Date: 2025-11-28</p>
</div>
<!-- END BADGE -->
4 changes: 2 additions & 2 deletions src/DATA_PIPELINE.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ az search index show-statistics \

<!-- START BADGE -->
<div align="center">
<img src="https://img.shields.io/badge/Total%20views-1543-limegreen" alt="Total views">
<p>Refresh Date: 2025-11-25</p>
<img src="https://img.shields.io/badge/Total%20views-1557-limegreen" alt="Total views">
<p>Refresh Date: 2025-11-28</p>
</div>
<!-- END BADGE -->
3 changes: 2 additions & 1 deletion src/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ COPY . .
EXPOSE 8000

# Run the application
CMD ["uvicorn", "chat_app:app", "--host", "0.0.0.0", "--port", "8000"]
# Multi-agent entrypoint (falls back internally if multi disabled)
CMD ["uvicorn", "chat_app_multi_agent:app", "--host", "0.0.0.0", "--port", "8000"]
1 change: 1 addition & 0 deletions src/app/agents/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Agent initialization module
23 changes: 23 additions & 0 deletions src/app/agents/agent_initializer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""Local agent initializer stub.

This replaces remote Microsoft Foundry agent creation with deterministic
pseudo agent IDs for environments where the Agents API is unavailable.
It preserves the AGENT_ID: output pattern used by Terraform provisioner
scripts so existing parsing logic continues to work.
"""
import os

def initialize_local_agent(env_var_name: str, name: str) -> str:
pseudo_id = f"asst_local_{env_var_name}".replace('-', '_')
# Persist to environment for current process (optional)
os.environ[env_var_name] = pseudo_id
print("=" * 60)
print(f"Local pseudo agent ready: {name}")
print(f"Environment Variable: {env_var_name}")
print(f"AGENT_ID:{pseudo_id}")
print("=" * 60)
return pseudo_id

# Backwards compatibility name
def initialize_agent(**kwargs): # type: ignore
return initialize_local_agent(kwargs.get("env_var_name", "unknown"), kwargs.get("name", "Unnamed Agent"))
174 changes: 174 additions & 0 deletions src/app/agents/agent_processor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
"""
Agent processor for handling interactions with Microsoft Foundry agents.
Includes MCP (Model Context Protocol) integration for tool calling.
"""
import os
import json
from typing import List, Dict, Any
try:
from azure.ai.projects import AIProjectClient # type: ignore
from azure.identity import DefaultAzureCredential # type: ignore
_REMOTE_AVAILABLE = True
except Exception:
_REMOTE_AVAILABLE = False


def create_function_tool_for_agent(agent_name: str) -> List[Dict[str, Any]]:
"""
Create function tools for a specific agent using MCP.

Args:
agent_name: Name of the agent (e.g., 'interior_designer', 'inventory_agent')

Returns:
List of function tool definitions
"""
# Placeholder for MCP tool integration
# In production, this would connect to MCP servers to get available tools
tools = []

# Define tools based on agent type
if agent_name == "interior_designer":
tools.append({
"type": "function",
"function": {
"name": "create_image",
"description": "Create or modify images based on user requirements",
"parameters": {
"type": "object",
"properties": {
"prompt": {"type": "string", "description": "Image generation prompt"},
"path": {"type": "string", "description": "Path to existing image (optional)"}
},
"required": ["prompt"]
}
}
})

elif agent_name == "inventory_agent":
tools.append({
"type": "function",
"function": {
"name": "inventory_check",
"description": "Check inventory levels for products",
"parameters": {
"type": "object",
"properties": {
"product_dict": {
"type": "object",
"description": "Dictionary mapping product names to product IDs"
}
},
"required": ["product_dict"]
}
}
})

elif agent_name == "customer_loyalty":
tools.append({
"type": "function",
"function": {
"name": "customer_loyalty_check",
"description": "Check customer loyalty status and calculate discount",
"parameters": {
"type": "object",
"properties": {
"customer_id": {"type": "string", "description": "Customer ID"}
},
"required": ["customer_id"]
}
}
})

elif agent_name == "cora":
# Cora (shopper agent) might have general query tools
tools.append({
"type": "function",
"function": {
"name": "search_products",
"description": "Search for products in catalog",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search query"}
},
"required": ["query"]
}
}
})

return tools


class AgentProcessor:
"""Handles communication with Microsoft Foundry agents"""

def __init__(self, agent_id: str, project_endpoint: str = None):
"""
Initialize agent processor.

Args:
agent_id: The agent ID from Microsoft Foundry
project_endpoint: Optional project endpoint (reads from env if not provided)
"""
self.agent_id = agent_id
self.project_endpoint = project_endpoint or os.environ.get("AZURE_AI_AGENT_ENDPOINT")

if not self.project_endpoint or not _REMOTE_AVAILABLE:
raise ValueError("Remote agent support unavailable (endpoint or SDK missing)")
self.client = AIProjectClient(endpoint=self.project_endpoint, credential=DefaultAzureCredential())

def run_conversation_with_text_stream(
self,
user_message: str,
conversation_history: List[Dict[str, str]] = None,
additional_context: Dict[str, Any] = None
):
"""
Run a conversation with the agent and stream the response.

Args:
user_message: The user's message
conversation_history: Optional conversation history
additional_context: Additional context to provide to the agent

Yields:
Chunks of the agent's response
"""
try:
# Create a thread for this conversation
thread = self.client.agents.create_thread()

# Build the message content
message_content = user_message
if additional_context:
message_content = f"Context: {json.dumps(additional_context)}\n\nUser: {user_message}"

# Add message to thread
self.client.agents.create_message(
thread_id=thread.id,
role="user",
content=message_content
)

# Run the agent
run = self.client.agents.create_and_process_run(
thread_id=thread.id,
assistant_id=self.agent_id
)

# Get messages
messages = self.client.agents.list_messages(thread_id=thread.id)

# Find the assistant's response
for message in messages:
if message.role == "assistant":
for content in message.content:
if hasattr(content, 'text'):
yield content.text.value

# Clean up
self.client.agents.delete_thread(thread.id)

except Exception as e:
yield f"Error communicating with agent: {str(e)}"
25 changes: 25 additions & 0 deletions src/app/agents/agents_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Domain-specific agent instruction consolidation.

Uses Cora (shopper) prompt as baseline and applies lightweight
specialization for each additional domain.
"""
import os

PROMPTS_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'prompts')

def _read(name: str) -> str:
path = os.path.join(PROMPTS_DIR, name)
if os.path.exists(path):
with open(path, 'r', encoding='utf-8') as f:
return f.read().strip()
return ""

BASE_CORA = _read('ShopperAgentPrompt.txt') or "You are Cora, a helpful shopping assistant for Zava DIY."

AGENT_INSTRUCTIONS = {
'cora': BASE_CORA,
'interior_design': _read('InteriorDesignAgentPrompt.txt') or "Provide interior design guidance tied to product suggestions.",
'inventory': _read('InventoryAgentPrompt.txt') or "Report mock inventory status with concise JSON if feasible.",
'customer_loyalty': _read('CustomerLoyaltyAgentPrompt.txt') or "Determine a loyalty discount (default 10%).",
'cart_management': _read('CartManagerAgentPrompt.txt') or "Maintain a cart list; support 'add <item>' and 'remove <item>'."
}
27 changes: 27 additions & 0 deletions src/app/agents/agents_state.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"cora": {
"id": "asst_xPTPWSqJrKKTxhweUZMwXxNb",
"hash": "ec1323afb9692d92de14373b05eb60026bb3738f5fb3303976f74d5c40536092",
"status": "created"
},
"interior_designer": {
"id": "asst_6YVlwwf9MRv91UCEZhKXYggr",
"hash": "0fbee2d10b87eee5a9d0bd87a9d7af60f327c9093edd47d8e6683505db5aabba",
"status": "created"
},
"inventory_agent": {
"id": "asst_cFo2Nh5ncBmw9ZimlOkzBITv",
"hash": "87deafceb6532b78ef075dd0e084909c4177286a0808cb9755c9a289f0076ba3",
"status": "created"
},
"customer_loyalty": {
"id": "asst_4yiWqVI0AyJ46yKFwpnNjRrS",
"hash": "1e0ffd8c5b4dac8cd247b90966b513f89b5c1c366edd476ca164d27b86dc276c",
"status": "created"
},
"cart_manager": {
"id": "asst_QChARJsqcQrWKLRhOUt7ojOG",
"hash": "bd3985311b2b5e0d4d88ae4583c5c3b36113d6ddbbee67e6d36924f34292ccab",
"status": "created"
}
}
22 changes: 22 additions & 0 deletions src/app/agents/cartManagerAgent_initializer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential
from dotenv import load_dotenv
from agent_initializer import initialize_agent
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from dotenv import load_dotenv
from agent_initializer import initialize_local_agent

load_dotenv()

PROMPT_TARGET = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'prompts', 'CartManagerAgentPrompt.txt')
if os.path.exists(PROMPT_TARGET):
with open(PROMPT_TARGET, 'r', encoding='utf-8') as f:
_ = f.read()

initialize_local_agent(env_var_name="cart_manager", name="Cart Manager Agent")
initialize_agent(
22 changes: 22 additions & 0 deletions src/app/agents/customerLoyaltyAgent_initializer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential
from dotenv import load_dotenv
from agent_initializer import initialize_agent
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from dotenv import load_dotenv
from agent_initializer import initialize_local_agent

load_dotenv()

PROMPT_TARGET = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'prompts', 'CustomerLoyaltyAgentPrompt.txt')
if os.path.exists(PROMPT_TARGET):
with open(PROMPT_TARGET, 'r', encoding='utf-8') as f:
_ = f.read()

initialize_local_agent(env_var_name="customer_loyalty", name="Customer Loyalty Agent")
initialize_agent(
Loading