diff --git a/demos/rfe-builder/.streamlit/secrets.toml b/demos/rfe-builder/.streamlit/secrets.toml
new file mode 100644
index 000000000..da6c1a38b
--- /dev/null
+++ b/demos/rfe-builder/.streamlit/secrets.toml
@@ -0,0 +1,21 @@
+# Streamlit Secrets Configuration Template
+# Copy this file to .streamlit/secrets.toml and add your API keys
+# Note: secrets.toml is automatically ignored by git for security
+
+# Anthropic Claude API Configuration
+# Get your API key from: https://console.anthropic.com/
+ANTHROPIC_API_KEY = "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"
+
+# Optional: Configure Claude model preferences
+ANTHROPIC_MODEL = "claude-4-sonnet-20250514" # Latest Claude 4 Sonnet model
+# Alternative models:
+# - "claude-3-sonnet-20240229" # Balanced performance
+# - "claude-3-opus-20240229" # Highest capability
+
+# Optional: Cost management settings
+MAX_MONTHLY_COST = 50.0 # Maximum monthly cost in USD
+ENABLE_COST_ALERTS = true
+
+# Optional: Development settings
+ENABLE_AI_DEBUG = false
+CACHE_AI_RESPONSES = true
diff --git a/demos/rfe-builder/.streamlit/secrets.toml.template b/demos/rfe-builder/.streamlit/secrets.toml.template
new file mode 100644
index 000000000..4b865538a
--- /dev/null
+++ b/demos/rfe-builder/.streamlit/secrets.toml.template
@@ -0,0 +1,21 @@
+# Streamlit Secrets Configuration Template
+# Copy this file to .streamlit/secrets.toml and add your API keys
+# Note: secrets.toml is automatically ignored by git for security
+
+# Anthropic Claude API Configuration
+# Get your API key from: https://console.anthropic.com/
+ANTHROPIC_API_KEY = "your-anthropic-api-key-here"
+
+# Optional: Configure Claude model preferences
+ANTHROPIC_MODEL = "claude-3-haiku-20240307" # Cost-effective default
+# Alternative models:
+# - "claude-3-sonnet-20240229" # Balanced performance
+# - "claude-3-opus-20240229" # Highest capability
+
+# Optional: Cost management settings
+MAX_MONTHLY_COST = 50.0 # Maximum monthly cost in USD
+ENABLE_COST_ALERTS = true
+
+# Optional: Development settings
+ENABLE_AI_DEBUG = false
+CACHE_AI_RESPONSES = true
diff --git a/demos/rfe-builder/README.md b/demos/rfe-builder/README.md
index ee5ac6f4c..08a10a2a1 100644
--- a/demos/rfe-builder/README.md
+++ b/demos/rfe-builder/README.md
@@ -6,8 +6,9 @@
RFE Builder is an interactive Streamlit web application that guides Request for Enhancement (RFE) submissions through a structured 7-step council review process with AI-powered assistance and enterprise integration.
-## ๐ Features (Phase 1)
+## ๐ Features
+### Phase 1 (Foundation)
- **๐ Visual Workflow Management**: Interactive Mermaid diagram showing the complete RFE Council process
- **๐ฅ Multi-Agent System**: 7 specialized agent roles with dedicated dashboards
- **๐ Progress Tracking**: Real-time status updates and step-by-step progression
@@ -15,6 +16,13 @@ RFE Builder is an interactive Streamlit web application that guides Request for
- **๐ Role-Based Interfaces**: Customized dashboards for each workflow participant
- **โ
Comprehensive Testing**: Full test coverage with CI/CD pipeline
+### Phase 2 (Conversational AI) โจ **NEW**
+- **๐ฌ AI-Powered RFE Creation**: Natural language RFE creation using Anthropic Claude
+- **๐ค Intelligent Agent Assistants**: Role-specific AI guidance for all 7 workflow agents
+- **๐ฏ Smart Recommendations**: Context-aware suggestions based on RFE content
+- **๐ฐ Cost Optimization**: Token usage tracking and intelligent prompt management
+- **๐ Dynamic Prompting**: Workflow-aware AI assistance that adapts to current step
+
## ๐๏ธ Architecture
### Agent Roles
@@ -62,21 +70,39 @@ The RFE Builder implements a 7-agent workflow system:
3. **Install dependencies**
```bash
- pip install -r requirements.txt
+ # Using uv (recommended for speed)
+ uv pip install -r requirements.txt
+ # OR: pip install -r requirements.txt
```
-4. **Run the application**
+4. **Configure AI features (Phase 2 - Optional)**
+ ```bash
+ # Copy secrets template
+ cp .streamlit/secrets.toml.template .streamlit/secrets.toml
+
+ # Edit .streamlit/secrets.toml and add your Anthropic API key
+ # Get your key from: https://console.anthropic.com/
+ ```
+
+5. **Run the application**
```bash
streamlit run app.py
```
-5. **Open in browser**
+6. **Open in browser**
- The app will automatically open at `http://localhost:8501`
## ๐ Usage
### Creating an RFE
+#### Option 1: AI-Powered Conversational Creation (Phase 2) โญ **Recommended**
+1. Navigate to **"๐ฌ AI Chat RFE"** in the sidebar
+2. Describe your enhancement idea in natural language
+3. The AI assistant will guide you through gathering all necessary information
+4. Review the generated RFE draft and create when ready
+
+#### Option 2: Traditional Form-Based Creation
1. Navigate to **"๐ Create RFE"** in the sidebar
2. Fill in the required fields:
- **Title**: Brief descriptive title
@@ -94,12 +120,20 @@ The RFE Builder implements a 7-agent workflow system:
### Agent-Specific Actions
-Each agent role has specific capabilities:
-
-- **Parker (PM)**: Prioritize new RFEs, communicate decisions to stakeholders
-- **Archie (Architect)**: Review technical feasibility, validate acceptance criteria
-- **Stella (Staff Engineer)**: Assess completeness, make final accept/reject decisions
-- **Other Agents**: Specialized assessment and support functions
+Each agent role has specific capabilities with AI-powered assistance:
+
+- **Parker (PM)**:
+ - Prioritize new RFEs with AI business impact analysis
+ - Draft stakeholder communications with AI assistance
+- **Archie (Architect)**:
+ - Review technical feasibility with AI architectural guidance
+ - Validate acceptance criteria with AI recommendations
+- **Stella (Staff Engineer)**:
+ - Assess RFE completeness with AI gap analysis
+ - Make final accept/reject decisions with AI decision support
+- **Derek (Delivery Owner)**:
+ - Generate JIRA tickets and development tasks with AI assistance
+- **Other Agents**: Specialized assessment functions with role-specific AI guidance
## ๐งช Testing
diff --git a/demos/rfe-builder/ai_models/__init__.py b/demos/rfe-builder/ai_models/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/demos/rfe-builder/ai_models/cost_tracker.py b/demos/rfe-builder/ai_models/cost_tracker.py
new file mode 100644
index 000000000..c97270447
--- /dev/null
+++ b/demos/rfe-builder/ai_models/cost_tracker.py
@@ -0,0 +1,196 @@
+"""
+Cost tracking and optimization for AI API usage
+"""
+
+import time
+from dataclasses import asdict, dataclass
+from datetime import datetime
+from typing import Any, Dict, Optional
+
+import tiktoken
+from data.rfe_models import AgentRole
+
+
+@dataclass
+class APIUsage:
+ """Track individual API call usage"""
+
+ timestamp: datetime
+ agent_role: str
+ task: str
+ prompt_tokens: int
+ completion_tokens: int
+ total_tokens: int
+ cost_estimate: float
+ response_time: float
+ rfe_id: Optional[str] = None
+
+
+class CostTracker:
+ """Track and optimize AI API costs"""
+
+ # Anthropic Claude pricing (as of 2024 - update as needed)
+ CLAUDE_PRICING = {
+ "claude-3-haiku": {"input": 0.00025, "output": 0.00125}, # per 1K tokens
+ "claude-3-sonnet": {"input": 0.003, "output": 0.015},
+ "claude-3-opus": {"input": 0.015, "output": 0.075},
+ }
+
+ def __init__(self, model_name: str = "claude-3-haiku"):
+ self.model_name = model_name
+ self.usage_log: list[APIUsage] = []
+ self.cache: Dict[str, Any] = {} # Simple response cache
+ self.tokenizer = tiktoken.get_encoding("cl100k_base") # Approximate
+
+ def count_tokens(self, text: str) -> int:
+ """Count tokens in text using tiktoken (approximate for Claude)"""
+ return len(self.tokenizer.encode(text))
+
+ def estimate_cost(self, prompt_tokens: int, completion_tokens: int) -> float:
+ """Estimate cost based on token usage"""
+ if self.model_name not in self.CLAUDE_PRICING:
+ return 0.0 # Unknown model
+
+ pricing = self.CLAUDE_PRICING[self.model_name]
+ input_cost = (prompt_tokens / 1000) * pricing["input"]
+ output_cost = (completion_tokens / 1000) * pricing["output"]
+ return input_cost + output_cost
+
+ def check_cache(self, cache_key: str) -> Optional[Any]:
+ """Check if response is cached to avoid API call"""
+ return self.cache.get(cache_key)
+
+ def cache_response(self, cache_key: str, response: Any, ttl_seconds: int = 3600):
+ """Cache response with optional TTL"""
+ self.cache[cache_key] = {
+ "response": response,
+ "timestamp": time.time(),
+ "ttl": ttl_seconds,
+ }
+
+ # Simple cache cleanup - remove expired entries
+ current_time = time.time()
+ expired_keys = [
+ key
+ for key, value in self.cache.items()
+ if current_time - value["timestamp"] > value["ttl"]
+ ]
+ for key in expired_keys:
+ del self.cache[key]
+
+ def generate_cache_key(
+ self, agent: AgentRole, task: str, context: Dict[str, Any]
+ ) -> str:
+ """Generate cache key for identical requests"""
+ # Create deterministic key from agent, task, and key context elements
+ key_elements = [
+ agent.value,
+ task,
+ str(sorted(context.items())), # Ensure consistent ordering
+ ]
+ return "|".join(key_elements)
+
+ def log_usage(
+ self,
+ agent_role: AgentRole,
+ task: str,
+ prompt_tokens: int,
+ completion_tokens: int,
+ response_time: float,
+ rfe_id: Optional[str] = None,
+ ) -> APIUsage:
+ """Log API usage for cost tracking"""
+
+ total_tokens = prompt_tokens + completion_tokens
+ cost_estimate = self.estimate_cost(prompt_tokens, completion_tokens)
+
+ usage = APIUsage(
+ timestamp=datetime.now(),
+ agent_role=agent_role.value,
+ task=task,
+ prompt_tokens=prompt_tokens,
+ completion_tokens=completion_tokens,
+ total_tokens=total_tokens,
+ cost_estimate=cost_estimate,
+ response_time=response_time,
+ rfe_id=rfe_id,
+ )
+
+ self.usage_log.append(usage)
+ return usage
+
+ def get_usage_summary(self, hours: int = 24) -> Dict[str, Any]:
+ """Get usage summary for the last N hours"""
+ cutoff_time = datetime.now().timestamp() - (hours * 3600)
+ recent_usage = [
+ usage
+ for usage in self.usage_log
+ if usage.timestamp.timestamp() > cutoff_time
+ ]
+
+ if not recent_usage:
+ return {"total_calls": 0, "total_cost": 0.0, "total_tokens": 0}
+
+ summary: Dict[str, Any] = {
+ "total_calls": len(recent_usage),
+ "total_cost": sum(usage.cost_estimate for usage in recent_usage),
+ "total_tokens": sum(usage.total_tokens for usage in recent_usage),
+ "avg_response_time": sum(usage.response_time for usage in recent_usage)
+ / len(recent_usage),
+ "by_agent": {},
+ "by_task": {},
+ }
+
+ # Group by agent
+ for usage in recent_usage:
+ agent = usage.agent_role
+ if agent not in summary["by_agent"]:
+ summary["by_agent"][agent] = {"calls": 0, "cost": 0.0, "tokens": 0}
+ summary["by_agent"][agent]["calls"] += 1
+ summary["by_agent"][agent]["cost"] += usage.cost_estimate
+ summary["by_agent"][agent]["tokens"] += usage.total_tokens
+
+ # Group by task
+ for usage in recent_usage:
+ task = usage.task
+ if task not in summary["by_task"]:
+ summary["by_task"][task] = {"calls": 0, "cost": 0.0, "tokens": 0}
+ summary["by_task"][task]["calls"] += 1
+ summary["by_task"][task]["cost"] += usage.cost_estimate
+ summary["by_task"][task]["tokens"] += usage.total_tokens
+
+ return summary
+
+ def optimize_prompt(self, prompt: str, max_tokens: int = 4000) -> str:
+ """
+ Optimize prompt for cost by truncating if necessary
+ Keep the most important parts (system message + recent context)
+ """
+ token_count = self.count_tokens(prompt)
+
+ if token_count <= max_tokens:
+ return prompt
+
+ # Simple optimization: truncate middle, keep beginning and end
+ lines = prompt.split("\n")
+ if len(lines) <= 3:
+ # If very few lines, just truncate
+ tokens_per_char = token_count / len(prompt)
+ target_chars = int(max_tokens / tokens_per_char * 0.9) # 90% safety margin
+ return prompt[:target_chars] + "...[truncated for cost optimization]"
+
+ # Keep first few and last few lines, truncate middle
+ keep_start = len(lines) // 4
+ keep_end = len(lines) // 4
+
+ optimized_lines = (
+ lines[:keep_start]
+ + ["...[content truncated for cost optimization]..."]
+ + lines[-keep_end:]
+ )
+
+ return "\n".join(optimized_lines)
+
+ def export_usage_data(self) -> list:
+ """Export usage data for analysis"""
+ return [asdict(usage) for usage in self.usage_log]
diff --git a/demos/rfe-builder/ai_models/prompt_manager.py b/demos/rfe-builder/ai_models/prompt_manager.py
new file mode 100644
index 000000000..89673604c
--- /dev/null
+++ b/demos/rfe-builder/ai_models/prompt_manager.py
@@ -0,0 +1,174 @@
+"""
+Prompt Management System for RFE Builder
+Hybrid approach: Enum-based mapping with workflow-aware templates
+"""
+
+from pathlib import Path
+from typing import Any, Dict, Optional
+
+import yaml
+from data.rfe_models import RFE, AgentRole
+
+
+class PromptManager:
+ """Centralized prompt template management with cost optimization"""
+
+ def __init__(self, prompts_dir: Optional[Path] = None):
+ if prompts_dir is None:
+ # Default to prompts/ directory relative to this file
+ self.prompts_dir = Path(__file__).parent.parent / "prompts"
+ else:
+ self.prompts_dir = Path(prompts_dir)
+
+ self.templates: Dict[AgentRole, Dict[str, Any]] = {}
+ self._load_all_templates()
+
+ # Workflow step to agent/task mapping
+ self.workflow_step_mapping = {
+ 1: (AgentRole.PARKER_PM, "prioritization"),
+ 2: (AgentRole.ARCHIE_ARCHITECT, "technical_review"),
+ 3: (AgentRole.STELLA_STAFF_ENGINEER, "completeness_check"),
+ 4: (AgentRole.ARCHIE_ARCHITECT, "acceptance_criteria"),
+ 5: (AgentRole.STELLA_STAFF_ENGINEER, "final_decision"),
+ 6: (AgentRole.PARKER_PM, "communication"),
+ 7: (AgentRole.DEREK_DELIVERY_OWNER, "ticket_creation"),
+ }
+
+ def _load_all_templates(self):
+ """Load all prompt templates from the prompts directory"""
+ if not self.prompts_dir.exists():
+ print(f"Warning: Prompts directory {self.prompts_dir} does not exist")
+ return
+
+ agents_dir = self.prompts_dir / "agents"
+ if not agents_dir.exists():
+ print(f"Warning: Agents directory {agents_dir} does not exist")
+ return
+
+ for agent_role in AgentRole:
+ agent_name = self._get_agent_dir_name(agent_role)
+ agent_dir = agents_dir / agent_name
+
+ if agent_dir.exists():
+ self.templates[agent_role] = {}
+ # Load all YAML files in the agent directory
+ for yaml_file in agent_dir.glob("*.yaml"):
+ task_name = yaml_file.stem
+ try:
+ with open(yaml_file, "r") as f:
+ template_data = yaml.safe_load(f)
+ self.templates[agent_role][task_name] = template_data
+ except Exception as e:
+ print(f"Error loading template {yaml_file}: {e}")
+
+ def _get_agent_dir_name(self, agent_role: AgentRole) -> str:
+ """Convert AgentRole enum to directory name"""
+ return agent_role.value.lower()
+
+ def get_agent_prompt(
+ self, agent: AgentRole, context: str, rfe: Optional[RFE] = None
+ ) -> Dict[str, Any]:
+ """
+ Get appropriate prompt template for an agent
+
+ Args:
+ agent: The agent role requesting the prompt
+ context: The task context (e.g., 'prioritization', 'review')
+ rfe: Optional RFE object for workflow-aware prompting
+
+ Returns:
+ Dictionary containing prompt template with system, user, and metadata
+ """
+ # First try workflow-aware prompting if RFE is provided
+ if rfe and rfe.current_step and rfe.current_step in self.workflow_step_mapping:
+ workflow_agent, workflow_task = self.workflow_step_mapping[rfe.current_step]
+ if workflow_agent == agent:
+ # Use workflow-specific template if available
+ step_template_name = f"step{rfe.current_step}_{workflow_task}"
+ if (
+ agent in self.templates
+ and step_template_name in self.templates[agent]
+ ):
+ template = self.templates[agent][step_template_name].copy()
+ template["workflow_aware"] = True
+ return template
+
+ # Fallback to general context-based template
+ if agent in self.templates and context in self.templates[agent]:
+ template = self.templates[agent][context].copy()
+ template["workflow_aware"] = False
+ return template
+
+ # Final fallback - return a basic template structure
+ return {
+ "system": (
+ f"You are {agent.value.replace('_', ' ').title()}, "
+ "assisting with RFE workflow."
+ ),
+ "user": "Please help with the following RFE task: {context}",
+ "context": context,
+ "workflow_aware": False,
+ "metadata": {"agent": agent.value, "task": context, "fallback": True},
+ }
+
+ def get_workflow_prompt(self, rfe: RFE) -> Optional[Dict[str, Any]]:
+ """Get the appropriate prompt for the current workflow step"""
+ if rfe.current_step not in self.workflow_step_mapping:
+ return None
+
+ agent, task = self.workflow_step_mapping[rfe.current_step]
+ return self.get_agent_prompt(agent, task, rfe)
+
+ def format_prompt(self, template: Dict[str, Any], **kwargs) -> Dict[str, str]:
+ """
+ Format prompt template with provided context variables
+
+ Args:
+ template: Template dictionary from get_agent_prompt
+ **kwargs: Context variables for template formatting
+
+ Returns:
+ Formatted prompt with system and user messages
+ """
+ formatted = {}
+
+ for key in ["system", "user"]:
+ if key in template:
+ try:
+ formatted[key] = template[key].format(**kwargs)
+ except KeyError as e:
+ print(f"Warning: Missing template variable {e} in {key} prompt")
+ formatted[key] = template[
+ key
+ ] # Return unformatted if variables missing
+
+ # Include metadata
+ formatted["metadata"] = template.get("metadata", {})
+ formatted["workflow_aware"] = template.get("workflow_aware", False)
+
+ return formatted
+
+ def list_available_templates(self) -> Dict[str, list]:
+ """List all available templates by agent"""
+ available = {}
+ for agent_role, templates in self.templates.items():
+ available[agent_role.value] = list(templates.keys())
+ return available
+
+ def validate_templates(self) -> Dict[str, list]:
+ """Validate all templates and return any issues"""
+ issues = {}
+
+ for agent_role, templates in self.templates.items():
+ agent_issues = []
+ for template_name, template_data in templates.items():
+ # Check required fields
+ if "system" not in template_data:
+ agent_issues.append(f"{template_name}: Missing 'system' field")
+ if "user" not in template_data:
+ agent_issues.append(f"{template_name}: Missing 'user' field")
+
+ if agent_issues:
+ issues[agent_role.value] = agent_issues
+
+ return issues
diff --git a/demos/rfe-builder/app.py b/demos/rfe-builder/app.py
index e1d1c3de9..2408a126f 100644
--- a/demos/rfe-builder/app.py
+++ b/demos/rfe-builder/app.py
@@ -6,6 +6,7 @@
from datetime import datetime
import streamlit as st
+from components.chat_interface import ChatInterface
from data.rfe_models import RFEStatus, WorkflowState
# Page configuration
@@ -46,23 +47,46 @@ def main():
st.sidebar.markdown("---")
+ # Initialize current page if not set
+ if "current_page" not in st.session_state:
+ st.session_state.current_page = "๐ Home"
+
+ # Check if navigation was triggered by button
+ if "nav_target" in st.session_state:
+ target_page = st.session_state.nav_target
+ st.session_state.current_page = target_page # Update current page
+ del st.session_state.nav_target # Clear after use
+ else:
+ target_page = st.session_state.current_page # Use current page as fallback
+
+ page_options = [
+ "๐ Home",
+ "๐ Create RFE",
+ "๐ฌ AI Chat RFE",
+ "๐ Workflow Overview",
+ "๐ฅ Agent Dashboard",
+ "๐ RFE List",
+ ]
+
# Page selection
page = st.sidebar.selectbox(
"Select View",
- [
- "๐ Home",
- "๐ Create RFE",
- "๐ Workflow Overview",
- "๐ฅ Agent Dashboard",
- "๐ RFE List",
- ],
+ page_options,
+ index=page_options.index(target_page),
+ key="page_selector",
)
+ # Update current page if user changed selection via sidebar
+ if page != st.session_state.current_page:
+ st.session_state.current_page = page
+
# Route to appropriate page
if page == "๐ Home":
show_home_page()
elif page == "๐ Create RFE":
show_create_rfe_page()
+ elif page == "๐ฌ AI Chat RFE":
+ show_ai_chat_rfe_page()
elif page == "๐ Workflow Overview":
show_workflow_overview()
elif page == "๐ฅ Agent Dashboard":
@@ -82,20 +106,28 @@ def show_home_page():
"""
### ๐ฏ What is RFE Builder?
- RFE Builder is an AI-powered workflow platform that guides Request for Enhancement (RFE)
- submissions through a structured 7-step council review process.
+ RFE Builder is an AI-powered workflow platform that guides Request for
+ Enhancement (RFE) submissions through a structured 7-step council review
+ process.
**Key Features:**
- ๐ฅ Multi-agent workflow with 7 specialized roles
- ๐ Visual workflow tracking and status updates
- - ๐ค AI-powered guidance and recommendations
+ - ๐ค AI-powered conversational RFE creation
+ - ๐ฌ Intelligent agent assistants with Claude AI
- ๐ Automated step progression and validation
"""
)
- if st.button("๐ Create New RFE", type="primary"):
- st.session_state.page = "create_rfe"
- st.rerun()
+ col1, col2 = st.columns(2)
+ with col1:
+ if st.button("๐ Create New RFE", type="primary"):
+ st.session_state.nav_target = "๐ Create RFE"
+ st.rerun()
+ with col2:
+ if st.button("๐ฌ Try AI Chat RFE", type="secondary"):
+ st.session_state.nav_target = "๐ฌ AI Chat RFE"
+ st.rerun()
with col2:
st.markdown("### ๐ Quick Stats")
@@ -139,7 +171,10 @@ def show_create_rfe_page():
with col1:
business_justification = st.text_area(
"Business Justification",
- placeholder="Why is this enhancement needed? What business value does it provide?",
+ placeholder=(
+ "Why is this enhancement needed? "
+ "What business value does it provide?"
+ ),
height=100,
)
@@ -175,7 +210,7 @@ def show_create_rfe_page():
rfe.success_criteria = success_criteria
st.success(f"โ
RFE Created: {rfe.id}")
- st.info(f"Assigned to: ๐ Parker (PM) - Step 1: Prioritize RFE")
+ st.info("Assigned to: ๐ Parker (PM) - Step 1: Prioritize RFE")
# Show next steps
st.markdown("### Next Steps")
@@ -187,6 +222,12 @@ def show_create_rfe_page():
st.markdown("- Monitor updates in the **RFE List**")
+def show_ai_chat_rfe_page():
+ """AI-powered conversational RFE creation page"""
+ chat_interface = ChatInterface()
+ chat_interface.render_conversational_rfe_creator()
+
+
def show_workflow_overview():
"""Visual workflow overview with mermaid diagram"""
st.header("๐ RFE Council Workflow Overview")
@@ -196,7 +237,8 @@ def show_workflow_overview():
st.markdown(f"### Current RFE: {current_rfe.title}")
st.markdown(
- f"**Status:** `{current_rfe.current_status.value}` | **Step:** {current_rfe.current_step}/7"
+ f"**Status:** `{current_rfe.current_status.value}` | "
+ f"**Step:** {current_rfe.current_step}/7"
)
# Progress bar
@@ -339,7 +381,7 @@ def show_agent_dashboard():
col1, col2 = st.columns(2)
with col1:
- if st.button(f"Complete Step", key=f"complete_{rfe.id}"):
+ if st.button("Complete Step", key=f"complete_{rfe.id}"):
workflow_state.advance_workflow_step(
rfe.id, f"Completed by {selected_agent}"
)
@@ -347,7 +389,7 @@ def show_agent_dashboard():
st.rerun()
with col2:
- if st.button(f"View Details", key=f"details_{rfe.id}"):
+ if st.button("View Details", key=f"details_{rfe.id}"):
st.session_state.selected_rfe = rfe.id
st.rerun()
else:
@@ -436,7 +478,7 @@ def show_rfe_list():
with col3:
if rfe.assigned_agent:
- st.markdown(f"**Agent:**")
+ st.markdown("**Agent:**")
st.markdown(f"{rfe.assigned_agent.value}")
with col4:
diff --git a/demos/rfe-builder/components/ai_assistants.py b/demos/rfe-builder/components/ai_assistants.py
new file mode 100644
index 000000000..b12f4b12a
--- /dev/null
+++ b/demos/rfe-builder/components/ai_assistants.py
@@ -0,0 +1,366 @@
+"""
+AI-powered assistants for each RFE workflow agent
+Provides role-specific guidance and decision support
+"""
+
+from typing import Any, Dict, List, Optional
+
+import streamlit as st
+from ai_models.cost_tracker import CostTracker
+from ai_models.prompt_manager import PromptManager
+from anthropic import Anthropic
+from data.rfe_models import RFE, AgentRole
+
+
+class AgentAIAssistant:
+ """Base class for agent-specific AI assistants"""
+
+ def __init__(self, agent_role: AgentRole):
+ self.agent_role = agent_role
+ self.prompt_manager = PromptManager()
+ self.cost_tracker = CostTracker()
+
+ # Initialize Anthropic client
+ self.anthropic_client = self._get_anthropic_client()
+
+ def _get_anthropic_client(self) -> Optional[Anthropic]:
+ """Get Anthropic client with error handling"""
+ try:
+ if hasattr(st, "secrets") and "ANTHROPIC_API_KEY" in st.secrets:
+ return Anthropic(api_key=st.secrets["ANTHROPIC_API_KEY"])
+ else:
+ import os
+
+ api_key = os.getenv("ANTHROPIC_API_KEY")
+ if api_key:
+ return Anthropic(api_key=api_key)
+ except Exception:
+ pass
+ return None
+
+ def render_assistance_panel(
+ self, rfe: RFE, context: Optional[Dict[str, Any]] = None
+ ):
+ """Render the AI assistance panel for this agent"""
+ if not self.anthropic_client:
+ st.warning("๐ค AI assistant requires Anthropic API key configuration")
+ return
+
+ with st.expander("๐ค AI Assistant", expanded=False):
+ self._render_assistant_interface(rfe, context)
+
+ def _render_assistant_interface(
+ self, rfe: RFE, context: Optional[Dict[str, Any]] = None
+ ):
+ """Render the specific assistant interface (to be overridden)"""
+ st.info("AI assistant available for this agent")
+
+ if st.button(f"Get {self.agent_role.value.replace('_', ' ').title()} Guidance"):
+ with st.spinner("Getting AI guidance..."):
+ guidance = self.get_agent_guidance(rfe, context)
+ st.markdown("### ๐ก AI Guidance")
+ st.write(guidance)
+
+ def get_agent_guidance(
+ self, rfe: RFE, context: Optional[Dict[str, Any]] = None
+ ) -> str:
+ """Get AI guidance for this agent and RFE"""
+ if not self.anthropic_client:
+ return "AI guidance unavailable - missing API configuration"
+
+ try:
+ # Get appropriate prompt template
+ prompt_template = self.prompt_manager.get_agent_prompt(
+ self.agent_role, self._get_task_context(rfe), rfe
+ )
+
+ # Format prompt with RFE and context data
+ prompt_context = self._build_prompt_context(rfe, context)
+ formatted_prompt = self.prompt_manager.format_prompt(
+ prompt_template, **prompt_context
+ )
+
+ # Make API call
+ response = self.anthropic_client.messages.create(
+ model="claude-3-haiku-20240307",
+ max_tokens=800,
+ system=formatted_prompt["system"],
+ messages=[{"role": "user", "content": formatted_prompt["user"]}],
+ )
+
+ return response.content[0].text
+
+ except Exception as e:
+ return f"Error getting AI guidance: {e}"
+
+ def _get_task_context(self, rfe: RFE) -> str:
+ """Get the current task context for this agent (to be overridden)"""
+ return "general_assistance"
+
+ def _build_prompt_context(
+ self, rfe: RFE, additional_context: Optional[Dict[str, Any]] = None
+ ) -> Dict[str, Any]:
+ """Build context dictionary for prompt formatting"""
+ context = {
+ "title": rfe.title,
+ "description": rfe.description,
+ "business_justification": rfe.business_justification or "Not provided",
+ "technical_requirements": rfe.technical_requirements or "Not provided",
+ "success_criteria": rfe.success_criteria or "Not provided",
+ "current_step": rfe.current_step,
+ "rfe_id": rfe.id,
+ "priority": getattr(rfe, "priority", "Not set"),
+ "current_status": rfe.current_status.value,
+ }
+
+ # Add RFE history context
+ if rfe.history:
+ recent_history = []
+ for entry in rfe.history[-3:]: # Last 3 history entries
+ timestamp = entry["timestamp"].strftime("%Y-%m-%d %H:%M")
+ action = entry.get("action", "Unknown action")
+ notes = entry.get("notes", "")
+ recent_history.append(f"{timestamp}: {action} - {notes}")
+ context["decision_history"] = "\n".join(recent_history)
+ else:
+ context["decision_history"] = "No previous decisions recorded"
+
+ # Add additional context if provided
+ if additional_context:
+ context.update(additional_context)
+
+ return context
+
+
+class ParkerAIAssistant(AgentAIAssistant):
+ """AI Assistant for Parker (Product Manager)"""
+
+ def __init__(self):
+ super().__init__(AgentRole.PARKER_PM)
+
+ def _get_task_context(self, rfe: RFE) -> str:
+ """Get Parker's current task context"""
+ if rfe.current_step == 1:
+ return "step1_prioritization"
+ elif rfe.current_step == 6:
+ return "step6_communication"
+ else:
+ return "general_pm_guidance"
+
+ def _render_assistant_interface(
+ self, rfe: RFE, context: Optional[Dict[str, Any]] = None
+ ):
+ """Parker-specific assistant interface"""
+ if rfe.current_step == 1:
+ st.markdown("**๐ฏ Prioritization Assistant**")
+ st.markdown("I can help you assess business priority and impact.")
+
+ col1, col2 = st.columns(2)
+ with col1:
+ if st.button("๐ Analyze Business Impact"):
+ guidance = self.get_business_impact_analysis(rfe)
+ st.markdown("### Business Impact Analysis")
+ st.write(guidance)
+
+ with col2:
+ if st.button("๐ฏ Suggest Priority Level"):
+ guidance = self.get_priority_recommendation(rfe)
+ st.markdown("### Priority Recommendation")
+ st.write(guidance)
+
+ elif rfe.current_step == 6:
+ st.markdown("**๐ข Communication Assistant**")
+ st.markdown("I can help draft stakeholder communications.")
+
+ stakeholder_type = st.selectbox(
+ "Stakeholder Type",
+ ["RFE Submitter", "Engineering Team", "Management", "All Stakeholders"],
+ )
+
+ if st.button("โ๏ธ Draft Communication"):
+ context_update = {"stakeholder_type": stakeholder_type}
+ guidance = self.get_agent_guidance(rfe, context_update)
+ st.markdown("### Draft Communication")
+ st.write(guidance)
+ else:
+ super()._render_assistant_interface(rfe, context)
+
+ def get_business_impact_analysis(self, rfe: RFE) -> str:
+ """Get specific business impact analysis"""
+ context = {"analysis_type": "business_impact"}
+ return self.get_agent_guidance(rfe, context)
+
+ def get_priority_recommendation(self, rfe: RFE) -> str:
+ """Get priority level recommendation"""
+ context = {"analysis_type": "priority_recommendation"}
+ return self.get_agent_guidance(rfe, context)
+
+
+class ArchieAIAssistant(AgentAIAssistant):
+ """AI Assistant for Archie (Architect)"""
+
+ def __init__(self):
+ super().__init__(AgentRole.ARCHIE_ARCHITECT)
+
+ def _get_task_context(self, rfe: RFE) -> str:
+ """Get Archie's current task context"""
+ if rfe.current_step == 2:
+ return "step2_technical_review"
+ elif rfe.current_step == 4:
+ return "step4_acceptance_criteria"
+ else:
+ return "general_architecture_guidance"
+
+ def _render_assistant_interface(
+ self, rfe: RFE, context: Optional[Dict[str, Any]] = None
+ ):
+ """Archie-specific assistant interface"""
+ if rfe.current_step == 2:
+ st.markdown("**๐๏ธ Technical Review Assistant**")
+
+ col1, col2 = st.columns(2)
+ with col1:
+ if st.button("๐ Assess Feasibility"):
+ guidance = self.get_feasibility_assessment(rfe)
+ st.markdown("### Technical Feasibility")
+ st.write(guidance)
+
+ with col2:
+ if st.button("๐๏ธ Architecture Impact"):
+ guidance = self.get_architecture_impact(rfe)
+ st.markdown("### Architecture Impact")
+ st.write(guidance)
+
+ elif rfe.current_step == 4:
+ st.markdown("**โ
Acceptance Criteria Assistant**")
+
+ if st.button("๐ Evaluate Acceptance Criteria"):
+ guidance = self.get_agent_guidance(rfe)
+ st.markdown("### Acceptance Criteria Evaluation")
+ st.write(guidance)
+ else:
+ super()._render_assistant_interface(rfe, context)
+
+ def get_feasibility_assessment(self, rfe: RFE) -> str:
+ """Get technical feasibility assessment"""
+ context = {"analysis_type": "feasibility"}
+ return self.get_agent_guidance(rfe, context)
+
+ def get_architecture_impact(self, rfe: RFE) -> str:
+ """Get architecture impact analysis"""
+ context = {"analysis_type": "architecture_impact"}
+ return self.get_agent_guidance(rfe, context)
+
+
+class StellaAIAssistant(AgentAIAssistant):
+ """AI Assistant for Stella (Staff Engineer)"""
+
+ def __init__(self):
+ super().__init__(AgentRole.STELLA_STAFF_ENGINEER)
+
+ def _get_task_context(self, rfe: RFE) -> str:
+ """Get Stella's current task context"""
+ if rfe.current_step == 3:
+ return "step3_completeness_check"
+ elif rfe.current_step == 5:
+ return "step5_final_decision"
+ else:
+ return "general_engineering_guidance"
+
+ def _render_assistant_interface(
+ self, rfe: RFE, context: Optional[Dict[str, Any]] = None
+ ):
+ """Stella-specific assistant interface"""
+ if rfe.current_step == 3:
+ st.markdown("**๐ Completeness Check Assistant**")
+
+ if st.button("๐ Check RFE Completeness"):
+ guidance = self.get_completeness_analysis(rfe)
+ st.markdown("### Completeness Analysis")
+ st.write(guidance)
+
+ elif rfe.current_step == 5:
+ st.markdown("**โ๏ธ Final Decision Assistant**")
+
+ if st.button("๐ฏ Final Decision Analysis"):
+ guidance = self.get_agent_guidance(rfe)
+ st.markdown("### Final Decision Analysis")
+ st.write(guidance)
+ else:
+ super()._render_assistant_interface(rfe, context)
+
+ def get_completeness_analysis(self, rfe: RFE) -> str:
+ """Get RFE completeness analysis"""
+ context = {"analysis_type": "completeness"}
+ return self.get_agent_guidance(rfe, context)
+
+
+class DerekAIAssistant(AgentAIAssistant):
+ """AI Assistant for Derek (Delivery Owner)"""
+
+ def __init__(self):
+ super().__init__(AgentRole.DEREK_DELIVERY_OWNER)
+
+ def _get_task_context(self, rfe: RFE) -> str:
+ """Get Derek's current task context"""
+ if rfe.current_step == 7:
+ return "step7_ticket_creation"
+ else:
+ return "general_delivery_guidance"
+
+ def _render_assistant_interface(
+ self, rfe: RFE, context: Optional[Dict[str, Any]] = None
+ ):
+ """Derek-specific assistant interface"""
+ if rfe.current_step == 7:
+ st.markdown("**๐ซ Ticket Creation Assistant**")
+
+ col1, col2 = st.columns(2)
+ with col1:
+ if st.button("๐ Generate Epic Template"):
+ guidance = self.get_epic_template(rfe)
+ st.markdown("### JIRA Epic Template")
+ st.code(guidance)
+
+ with col2:
+ if st.button("๐ Break Down Tasks"):
+ guidance = self.get_task_breakdown(rfe)
+ st.markdown("### Development Task Breakdown")
+ st.write(guidance)
+ else:
+ super()._render_assistant_interface(rfe, context)
+
+ def get_epic_template(self, rfe: RFE) -> str:
+ """Get JIRA epic template"""
+ context = {"output_type": "epic_template"}
+ return self.get_agent_guidance(rfe, context)
+
+ def get_task_breakdown(self, rfe: RFE) -> str:
+ """Get development task breakdown"""
+ context = {"output_type": "task_breakdown"}
+ return self.get_agent_guidance(rfe, context)
+
+
+class AIAssistantFactory:
+ """Factory class to create appropriate AI assistants"""
+
+ _assistants = {
+ AgentRole.PARKER_PM: ParkerAIAssistant,
+ AgentRole.ARCHIE_ARCHITECT: ArchieAIAssistant,
+ AgentRole.STELLA_STAFF_ENGINEER: StellaAIAssistant,
+ AgentRole.DEREK_DELIVERY_OWNER: DerekAIAssistant,
+ }
+
+ @classmethod
+ def create_assistant(cls, agent_role: AgentRole) -> AgentAIAssistant:
+ """Create AI assistant for the specified agent role"""
+ assistant_class = cls._assistants.get(agent_role, AgentAIAssistant)
+ if assistant_class == AgentAIAssistant:
+ return AgentAIAssistant(agent_role) # Fallback for other agents
+ else:
+ return assistant_class() # Specialized classes call super().__init__()
+
+ @classmethod
+ def get_available_agents(cls) -> List[AgentRole]:
+ """Get list of agents with AI assistants"""
+ return list(cls._assistants.keys())
diff --git a/demos/rfe-builder/components/chat_interface.py b/demos/rfe-builder/components/chat_interface.py
new file mode 100644
index 000000000..f184fc39b
--- /dev/null
+++ b/demos/rfe-builder/components/chat_interface.py
@@ -0,0 +1,575 @@
+"""
+Conversational Chat Interface for RFE Builder
+Uses Anthropic Claude API for natural language RFE creation and agent assistance
+"""
+
+import json
+import time
+from datetime import datetime
+from typing import Any, Dict, Optional
+
+import streamlit as st
+import yaml
+from ai_models.cost_tracker import CostTracker
+from ai_models.prompt_manager import PromptManager
+from anthropic import Anthropic
+from data.rfe_models import RFE, AgentRole
+
+
+class ChatInterface:
+ """Main chat interface for conversational RFE creation and agent assistance"""
+
+ def __init__(self):
+ self.prompt_manager = PromptManager()
+ self.cost_tracker = CostTracker()
+
+ # Initialize Anthropic client if API key is available
+ self.anthropic_client = None
+ self._initialize_anthropic()
+
+ # Session state keys for chat
+ if "chat_history" not in st.session_state:
+ st.session_state.chat_history = []
+ if "current_rfe_draft" not in st.session_state:
+ st.session_state.current_rfe_draft = {}
+
+ def _initialize_anthropic(self):
+ """Initialize Anthropic client with API key from environment or secrets"""
+ try:
+ # Try to get API key from Streamlit secrets first
+ if hasattr(st, "secrets") and "ANTHROPIC_API_KEY" in st.secrets:
+ api_key = st.secrets["ANTHROPIC_API_KEY"]
+ self.anthropic_client = Anthropic(api_key=api_key)
+ else:
+ # Fallback to environment variable
+ import os
+
+ api_key = os.getenv("ANTHROPIC_API_KEY")
+ if api_key:
+ self.anthropic_client = Anthropic(api_key=api_key)
+ else:
+ st.warning(
+ "โ ๏ธ Anthropic API key not found. Please set "
+ "ANTHROPIC_API_KEY in secrets.toml or environment variables."
+ )
+ except Exception as e:
+ st.error(f"Failed to initialize Anthropic client: {e}")
+
+ def render_conversational_rfe_creator(self):
+ """Render the main conversational RFE creation interface"""
+ st.header("๐ฌ Create RFE - Conversational Assistant")
+ st.markdown(
+ "*Describe your enhancement idea naturally - "
+ "I'll help you create a complete RFE*"
+ )
+
+ # Display API provider and model information
+ self._render_model_info()
+
+ # Check if we have API access
+ if not self.anthropic_client:
+ st.error(
+ "๐ซ Cannot create conversational interface without Anthropic API access"
+ )
+ self._render_fallback_form()
+ return
+
+ # Chat container
+ chat_container = st.container()
+
+ # Display chat history
+ with chat_container:
+ self._render_chat_history()
+
+ # Chat input
+ self._render_chat_input()
+
+ # RFE draft status
+ if st.session_state.current_rfe_draft:
+ self._render_rfe_draft_status()
+
+ def _render_model_info(self):
+ """Display API provider and model information"""
+ # Get current model configuration
+ model = getattr(st.secrets, "ANTHROPIC_MODEL", "claude-4-sonnet-20250514")
+
+ # Connection status
+ status_icon = "๐ข" if self.anthropic_client else "๐ด"
+ status_text = "Connected" if self.anthropic_client else "Disconnected"
+
+ # Format model name for display
+ model_display = model.replace("-", " ").title()
+
+ # Render info bar
+ # Build the info bar HTML in parts to avoid line length issues
+ div_style = (
+ "background-color: #f0f2f6; padding: 8px 12px; border-radius: 6px; "
+ "font-size: 0.85em; color: #666; margin-bottom: 16px;"
+ )
+ status_span = (
+ f"{status_icon} "
+ f"API Status: {status_text}"
+ )
+ provider_span = (
+ "๐ค "
+ "Provider: Anthropic Claude"
+ )
+ model_span = f"๐ Model: {model_display}"
+
+ info_html = (
+ f"
{status_span}{provider_span}{model_span}
"
+ )
+
+ st.markdown(info_html, unsafe_allow_html=True)
+
+ def _render_chat_history(self):
+ """Render the conversation history"""
+ for i, message in enumerate(st.session_state.chat_history):
+ if message["role"] == "user":
+ with st.chat_message("user"):
+ st.write(message["content"])
+ elif message["role"] == "assistant":
+ with st.chat_message("assistant"):
+ st.write(message["content"])
+
+ # Show any structured data if available
+ if "structured_data" in message and message["structured_data"]:
+ with st.expander("๐ Extracted Information"):
+ try:
+ st.json(message["structured_data"])
+ except Exception as e:
+ st.error(f"Could not display extracted data: {e}")
+ st.text(f"Raw data: {message['structured_data']}")
+
+ def _render_chat_input(self):
+ """Render chat input and handle user messages"""
+ user_input = st.chat_input(
+ "Describe your enhancement idea or ask me questions..."
+ )
+
+ if user_input:
+ self._handle_user_message(user_input)
+
+ def _handle_user_message(self, user_input: str):
+ """Process user message and generate AI response"""
+ # Add user message to history
+ st.session_state.chat_history.append(
+ {"role": "user", "content": user_input, "timestamp": datetime.now()}
+ )
+
+ # Generate AI response
+ try:
+ response = self._generate_ai_response(user_input)
+
+ # Add assistant response to history
+ st.session_state.chat_history.append(
+ {
+ "role": "assistant",
+ "content": response["content"],
+ "timestamp": datetime.now(),
+ "structured_data": response.get("structured_data"),
+ "usage": response.get("usage"),
+ }
+ )
+
+ # Update RFE draft if structured data was extracted
+ if response.get("structured_data"):
+ self._update_rfe_draft(response["structured_data"])
+
+ except Exception as e:
+ st.error(f"Error generating response: {e}")
+ # Add error message to chat
+ st.session_state.chat_history.append(
+ {
+ "role": "assistant",
+ "content": (
+ f"I apologize, but I encountered an error: {e}. "
+ "Please try again."
+ ),
+ "timestamp": datetime.now(),
+ "error": True,
+ }
+ )
+
+ # Trigger rerun to show new messages
+ st.rerun()
+
+ def _generate_ai_response(self, user_input: str) -> Dict[str, Any]:
+ """Generate AI response using Claude API"""
+ # Get conversational RFE creation prompt
+ prompt_template = self._load_conversational_template()
+
+ # Format prompt with context
+ context = {
+ "user_input": user_input,
+ "current_rfe_draft": (
+ json.dumps(st.session_state.current_rfe_draft, indent=2)
+ if st.session_state.current_rfe_draft
+ else "None"
+ ),
+ "conversation_history": self._get_conversation_context(),
+ }
+
+ formatted_prompt = self.prompt_manager.format_prompt(prompt_template, **context)
+
+ # Check cache first
+ cache_key = self.cost_tracker.generate_cache_key(
+ AgentRole.PARKER_PM, # Use PM as default for RFE creation
+ "rfe_creation",
+ {"input": user_input[:100]}, # Use first 100 chars for cache key
+ )
+
+ cached_response = self.cost_tracker.check_cache(cache_key)
+ if cached_response:
+ return cached_response["response"]
+
+ # Make API call
+ start_time = time.time()
+
+ try:
+ # Get model from secrets or use default
+ model = getattr(st.secrets, "ANTHROPIC_MODEL", "claude-4-sonnet-20250514")
+
+ response = self.anthropic_client.messages.create(
+ model=model,
+ max_tokens=1000,
+ system=formatted_prompt["system"],
+ messages=[{"role": "user", "content": formatted_prompt["user"]}],
+ )
+
+ response_time = time.time() - start_time
+
+ # Extract response content
+ response_content = response.content[0].text
+
+ # Try to extract structured data if present
+ structured_data = self._extract_structured_data(response_content)
+
+ # Log usage
+ prompt_tokens = self.cost_tracker.count_tokens(
+ formatted_prompt["system"] + formatted_prompt["user"]
+ )
+ completion_tokens = self.cost_tracker.count_tokens(response_content)
+
+ usage = self.cost_tracker.log_usage(
+ AgentRole.PARKER_PM,
+ "rfe_creation",
+ prompt_tokens,
+ completion_tokens,
+ response_time,
+ )
+
+ result = {
+ "content": response_content,
+ "structured_data": structured_data,
+ "usage": {
+ "prompt_tokens": prompt_tokens,
+ "completion_tokens": completion_tokens,
+ "cost_estimate": usage.cost_estimate,
+ "response_time": response_time,
+ },
+ }
+
+ # Cache the result
+ self.cost_tracker.cache_response(
+ cache_key, result, ttl_seconds=1800
+ ) # 30 min cache
+
+ return result
+
+ except Exception as e:
+ st.error(f"API call failed: {e}")
+ # Return fallback response
+ return {
+ "content": (
+ "I'm having trouble connecting to the AI service. "
+ "Could you please rephrase your request?"
+ ),
+ "error": True,
+ }
+
+ def _load_conversational_template(self) -> Dict[str, Any]:
+ """Load conversational RFE creation template"""
+ try:
+ template_path = (
+ self.prompt_manager.prompts_dir / "conversational_rfe_creation.yaml"
+ )
+ with open(template_path, "r") as f:
+ return yaml.safe_load(f)
+ except Exception:
+ # Fallback template
+ return {
+ "system": (
+ "You are an AI assistant helping create RFE submissions. "
+ "Guide users through the process naturally."
+ ),
+ "user": "Help me create an RFE based on this input: {user_input}",
+ "metadata": {"fallback": True},
+ }
+
+ def _get_conversation_context(self) -> str:
+ """Get relevant conversation context for the AI"""
+ if not st.session_state.chat_history:
+ return "No previous conversation"
+
+ # Get last few messages for context
+ recent_messages = st.session_state.chat_history[-4:] # Last 2 exchanges
+ context_parts = []
+
+ for msg in recent_messages:
+ role = "User" if msg["role"] == "user" else "Assistant"
+ context_parts.append(
+ f"{role}: {msg['content'][:200]}..."
+ ) # Truncate for cost
+
+ return "\n".join(context_parts)
+
+ def _extract_structured_data(
+ self, response_content: str
+ ) -> Optional[Dict[str, Any]]:
+ """Extract structured RFE data from AI response"""
+ try:
+ # Look for JSON blocks in the response
+ import re
+
+ json_pattern = r"```json\n(.*?)\n```"
+ json_matches = re.findall(json_pattern, response_content, re.DOTALL)
+
+ if json_matches:
+ try:
+ return json.loads(json_matches[0])
+ except json.JSONDecodeError as e:
+ print(f"JSON parsing error: {e}")
+ return None
+
+ # Look for structured sections
+ structured_data = {}
+ patterns = {
+ "title": r"Title:\s*(.+)",
+ "description": r"Description:\s*(.+)",
+ "business_justification": r"Business Justification:\s*(.+)",
+ "technical_requirements": r"Technical Requirements:\s*(.+)",
+ "success_criteria": r"Success Criteria:\s*(.+)",
+ }
+
+ for field, pattern in patterns.items():
+ match = re.search(pattern, response_content, re.IGNORECASE)
+ if match:
+ structured_data[field] = match.group(1).strip()
+
+ return structured_data if structured_data else None
+
+ except Exception:
+ return None
+
+ def _update_rfe_draft(self, structured_data: Dict[str, Any]):
+ """Update the current RFE draft with extracted data"""
+ if not st.session_state.current_rfe_draft:
+ st.session_state.current_rfe_draft = {}
+
+ # Update draft with new information
+ for key, value in structured_data.items():
+ if value and value.strip(): # Only update if there's actual content
+ st.session_state.current_rfe_draft[key] = value.strip()
+
+ def _render_rfe_draft_status(self):
+ """Show current RFE draft status and completion"""
+ st.markdown("---")
+ st.subheader("๐ Current RFE Draft")
+
+ col1, col2 = st.columns([3, 1])
+
+ with col1:
+ # Show draft content
+ draft = st.session_state.current_rfe_draft
+
+ completion_status = self._calculate_completion_status(draft)
+ progress = completion_status["percentage"] / 100
+
+ st.progress(
+ progress, text=f"RFE Completion: {completion_status['percentage']:.0f}%"
+ )
+
+ # Show what we have so far
+ for field in [
+ "title",
+ "description",
+ "business_justification",
+ "technical_requirements",
+ "success_criteria",
+ ]:
+ if field in draft and draft[field]:
+ st.text_area(
+ field.replace("_", " ").title(),
+ value=draft[field],
+ height=60,
+ disabled=True,
+ key=f"draft_{field}",
+ )
+
+ with col2:
+ st.markdown("**Missing:**")
+ for missing_field in completion_status["missing_fields"]:
+ st.markdown(f"โข {missing_field.replace('_', ' ').title()}")
+
+ # Create RFE button if complete enough
+ if completion_status["percentage"] >= 80: # 80% complete threshold
+ if st.button("โ
Create RFE", type="primary"):
+ self._create_rfe_from_draft()
+
+ def _calculate_completion_status(self, draft: Dict[str, Any]) -> Dict[str, Any]:
+ """Calculate how complete the RFE draft is"""
+ required_fields = [
+ "title",
+ "description",
+ "business_justification",
+ "technical_requirements",
+ "success_criteria",
+ ]
+
+ completed_fields = [
+ field
+ for field in required_fields
+ if field in draft and draft[field] and draft[field].strip()
+ ]
+ missing_fields = [
+ field for field in required_fields if field not in completed_fields
+ ]
+
+ percentage = (len(completed_fields) / len(required_fields)) * 100
+
+ return {
+ "percentage": percentage,
+ "completed_fields": completed_fields,
+ "missing_fields": missing_fields,
+ "total_fields": len(required_fields),
+ }
+
+ def _create_rfe_from_draft(self):
+ """Create actual RFE from the draft"""
+ try:
+ draft = st.session_state.current_rfe_draft
+ workflow_state = st.session_state.workflow_state
+
+ # Create RFE
+ rfe = workflow_state.create_rfe(
+ title=draft.get("title", "Untitled RFE"),
+ description=draft.get("description", "No description provided"),
+ )
+
+ # Add optional fields
+ if draft.get("business_justification"):
+ rfe.business_justification = draft["business_justification"]
+ if draft.get("technical_requirements"):
+ rfe.technical_requirements = draft["technical_requirements"]
+ if draft.get("success_criteria"):
+ rfe.success_criteria = draft["success_criteria"]
+
+ # Clear the draft
+ st.session_state.current_rfe_draft = {}
+ st.session_state.chat_history = []
+
+ st.success(f"๐ RFE Created Successfully: {rfe.id}")
+ st.info("Your RFE has been assigned to Parker (PM) for prioritization")
+
+ # Switch to workflow view
+ time.sleep(1) # Brief pause to show success message
+ st.rerun()
+
+ except Exception as e:
+ st.error(f"Failed to create RFE: {e}")
+
+ def _render_fallback_form(self):
+ """Fallback form-based RFE creation when AI is not available"""
+ st.warning("๐ก AI assistant unavailable - using standard form")
+
+ with st.form("fallback_rfe_form"):
+ title = st.text_input("RFE Title*", placeholder="Brief descriptive title")
+ description = st.text_area(
+ "Description*", placeholder="Detailed description", height=150
+ )
+
+ col1, col2 = st.columns(2)
+ with col1:
+ business_justification = st.text_area(
+ "Business Justification", height=100
+ )
+ with col2:
+ technical_requirements = st.text_area(
+ "Technical Requirements", height=100
+ )
+
+ success_criteria = st.text_area("Success Criteria", height=100)
+
+ if st.form_submit_button("Create RFE", type="primary"):
+ if not title or not description:
+ st.error("Title and Description are required")
+ else:
+ # Use existing RFE creation logic from main app
+ workflow_state = st.session_state.workflow_state
+ rfe = workflow_state.create_rfe(title, description)
+
+ if business_justification:
+ rfe.business_justification = business_justification
+ if technical_requirements:
+ rfe.technical_requirements = technical_requirements
+ if success_criteria:
+ rfe.success_criteria = success_criteria
+
+ st.success(f"โ
RFE Created: {rfe.id}")
+
+ def render_agent_assistant(self, agent_role: AgentRole, rfe: RFE):
+ """Render AI assistant for specific agent role and RFE"""
+ st.subheader(
+ f"๐ค AI Assistant for {agent_role.value.replace('_', ' ').title()}"
+ )
+
+ if not self.anthropic_client:
+ st.warning("AI assistant requires Anthropic API access")
+ return
+
+ # Get agent-specific prompt
+ prompt_template = self.prompt_manager.get_agent_prompt(
+ agent_role, "assistance", rfe
+ )
+
+ # Simple chat interface for agent assistance
+ if st.button("Get AI Recommendation"):
+ with st.spinner("Getting AI assistance..."):
+ recommendation = self._get_agent_recommendation(
+ agent_role, rfe, prompt_template
+ )
+ st.markdown("### ๐ก AI Recommendation")
+ st.write(recommendation)
+
+ def _get_agent_recommendation(
+ self, agent_role: AgentRole, rfe: RFE, prompt_template: Dict[str, Any]
+ ) -> str:
+ """Get AI recommendation for agent"""
+ try:
+ # Format prompt with RFE context
+ context = {
+ "title": rfe.title,
+ "description": rfe.description,
+ "business_justification": rfe.business_justification or "Not provided",
+ "technical_requirements": rfe.technical_requirements or "Not provided",
+ "success_criteria": rfe.success_criteria or "Not provided",
+ "current_step": rfe.current_step,
+ "rfe_id": rfe.id,
+ }
+
+ formatted_prompt = self.prompt_manager.format_prompt(
+ prompt_template, **context
+ )
+
+ response = self.anthropic_client.messages.create(
+ model="claude-3-haiku-20240307",
+ max_tokens=500,
+ system=formatted_prompt["system"],
+ messages=[{"role": "user", "content": formatted_prompt["user"]}],
+ )
+
+ return response.content[0].text
+
+ except Exception as e:
+ return f"Error getting AI recommendation: {e}"
diff --git a/demos/rfe-builder/components/workflow.py b/demos/rfe-builder/components/workflow.py
index 1547b9a58..5286957e9 100644
--- a/demos/rfe-builder/components/workflow.py
+++ b/demos/rfe-builder/components/workflow.py
@@ -3,7 +3,7 @@
"""
import streamlit as st
-from data.rfe_models import RFE, WorkflowStep
+from data.rfe_models import RFE
from streamlit_mermaid import st_mermaid
@@ -20,30 +20,30 @@ def get_step_color(step_num: int) -> str:
return "fill:#6c757d,stroke:#545b62,color:#fff" # Pending - gray
# Build the mermaid diagram with dynamic styling
- mermaid_code = f"""
+ mermaid_code = f""" # noqa: E501
flowchart TD
Start([Start]) --> PrioritizeRFE["1๏ธโฃ ๐ Parker (PM)
Prioritize RFEs"]
- PrioritizeRFE --> ReviewRFE["2๏ธโฃ RFE Council
๐๏ธ Archie (Architect)
Review RFE"]
- ReviewRFE -.->|2a if necessary| AssessImpact["2a ๐ฅ Lee (Team Lead) +
๐ป Taylor (Team Member)
Assess Impact"]
+ PrioritizeRFE --> ReviewRFE["2๏ธโฃ RFE Council
๐๏ธ Archie (Architect)
Review RFE"] # noqa: E501
+ ReviewRFE -.->|2a if necessary| AssessImpact["2a ๐ฅ Lee (Team Lead) +
๐ป Taylor (Team Member)
Assess Impact"] # noqa: E501
AssessImpact -.->|return to 2๏ธโฃ| ReviewRFE
- ReviewRFE --> RFEComplete{{"3๏ธโฃ โญ Stella (Staff Engineer)
RFE is Complete?"}}
- RFEComplete -->|3a missing details| AddInfo["3a ๐ Olivia (PO)
Add missing information"]
+ ReviewRFE --> RFEComplete{{"3๏ธโฃ โญ Stella (Staff Engineer)
RFE is Complete?"}} # noqa: E501
+ RFEComplete -->|3a missing details| AddInfo["3a ๐ Olivia (PO)
Add missing information"] # noqa: E501
AddInfo -->|return to 1๏ธโฃ| PrioritizeRFE
- RFEComplete -->|3b Yes| RFEMeets{{"4๏ธโฃ RFE Council
๐๏ธ Archie (Architect)
RFE meets acceptance
criteria?"}}
+ RFEComplete -->|3b Yes| RFEMeets{{"4๏ธโฃ RFE Council
๐๏ธ Archie (Architect)
RFE meets acceptance
criteria?"}} # noqa: E501
- RFEMeets -->|4a Yes| AcceptRFE["5๏ธโฃ โญ Stella (Staff Engineer)
Accept RFE
(update ticket with
assessment info)"]
- RFEMeets -->|4b No| RejectRFE["4b RFE Council
๐๏ธ Archie (Architect)
Reject RFE
(update ticket with
assessment info)"]
+ RFEMeets -->|4a Yes| AcceptRFE["5๏ธโฃ โญ Stella (Staff Engineer)
Accept RFE
(update ticket with
assessment info)"] # noqa: E501
+ RFEMeets -->|4b No| RejectRFE["4b RFE Council
๐๏ธ Archie (Architect)
Reject RFE
(update ticket with
assessment info)"] # noqa: E501
- RejectRFE --> CanChange{{"4c ๐ Olivia (PO)
Can RFE be changed
to remedy concerns?"}}
+ RejectRFE --> CanChange{{"4c ๐ Olivia (PO)
Can RFE be changed
to remedy concerns?"}} # noqa: E501
CanChange -->|4d Yes - return to 3a| AddInfo
- CanChange -->|4e No| CommReject["6๏ธโฃ ๐ Parker (PM)
Communicate assessment
to requester"]
+ CanChange -->|4e No| CommReject["6๏ธโฃ ๐ Parker (PM)
Communicate assessment
to requester"] # noqa: E501
- AcceptRFE --> CommAccept["6๏ธโฃ ๐ Parker (PM)
Communicate assessment
to requester"]
+ AcceptRFE --> CommAccept["6๏ธโฃ ๐ Parker (PM)
Communicate assessment
to requester"] # noqa: E501
- CommReject --> CreateTicket["7๏ธโฃ ๐ Derek (Delivery Owner)
Create Feature ticket
and assign to owner"]
+ CommReject --> CreateTicket["7๏ธโฃ ๐ Derek (Delivery Owner)
Create Feature ticket
and assign to owner"] # noqa: E501
CommAccept --> CreateTicket
CreateTicket --> End([End])
diff --git a/demos/rfe-builder/data/__init__.py b/demos/rfe-builder/data/__init__.py
new file mode 100644
index 000000000..76554cd0e
--- /dev/null
+++ b/demos/rfe-builder/data/__init__.py
@@ -0,0 +1 @@
+# Data models package
diff --git a/demos/rfe-builder/data/rfe_models.py b/demos/rfe-builder/data/rfe_models.py
index 7670c16f1..41d864454 100644
--- a/demos/rfe-builder/data/rfe_models.py
+++ b/demos/rfe-builder/data/rfe_models.py
@@ -127,7 +127,10 @@ def create_rfe(self, title: str, description: str) -> RFE:
WorkflowStep(
step_number=7,
name="Create Feature Ticket",
- description="๐ Derek (Delivery Owner) - Create Feature ticket and assign to owner",
+ description=(
+ "๐ Derek (Delivery Owner) - "
+ "Create Feature ticket and assign to owner"
+ ),
responsible_agent=AgentRole.DEREK_DELIVERY_OWNER,
),
]
diff --git a/demos/rfe-builder/pages/parker_pm.py b/demos/rfe-builder/pages/parker_pm.py
index fe15dd3a8..b25839fbb 100644
--- a/demos/rfe-builder/pages/parker_pm.py
+++ b/demos/rfe-builder/pages/parker_pm.py
@@ -6,8 +6,8 @@
from datetime import datetime
import streamlit as st
-from components.workflow import render_step_progress
-from data.rfe_models import AgentRole, RFEStatus, WorkflowState
+from components.ai_assistants import AIAssistantFactory
+from data.rfe_models import AgentRole, RFEStatus
def show_parker_dashboard():
@@ -101,6 +101,10 @@ def show_active_tasks(parker_rfes, workflow_state):
st.markdown("**Current Task:**")
st.info(current_step.description)
+ # AI Assistant integration
+ parker_assistant = AIAssistantFactory.create_assistant(AgentRole.PARKER_PM)
+ parker_assistant.render_assistance_panel(rfe)
+
# Action buttons based on current step
if rfe.current_step == 1: # Prioritization step
show_prioritization_actions(rfe, workflow_state)
@@ -150,12 +154,15 @@ def show_prioritization_interface(parker_rfes, workflow_state):
key=f"impact_{rfe.id}",
)
- if st.button(f"Complete Prioritization", key=f"prioritize_{rfe.id}"):
+ if st.button("Complete Prioritization", key=f"prioritize_{rfe.id}"):
# Update RFE with priority information
rfe.priority = f"{priority} (Impact: {business_impact})"
# Add notes about prioritization decision
- notes = f"Prioritized as {priority} with {business_impact} business impact by Parker (PM)"
+ notes = (
+ f"Prioritized as {priority} with {business_impact} "
+ "business impact by Parker (PM)"
+ )
# Advance to next step
workflow_state.advance_workflow_step(rfe.id, notes)
@@ -164,7 +171,8 @@ def show_prioritization_interface(parker_rfes, workflow_state):
)
st.success(
- f"โ
RFE prioritized and forwarded to Archie (Architect) for review"
+ "โ
RFE prioritized and forwarded to Archie (Architect) "
+ "for review"
)
st.rerun()
@@ -197,9 +205,8 @@ def show_communication_interface(parker_rfes, workflow_state):
st.markdown("**Decision History:**")
for entry in rfe.history[-3:]: # Show last 3 entries
timestamp = entry["timestamp"].strftime("%Y-%m-%d %H:%M")
- st.markdown(
- f"- {timestamp}: {entry.get('notes', entry.get('action', 'No details'))}"
- )
+ detail = entry.get("notes", entry.get("action", "No details"))
+ st.markdown(f"- {timestamp}: {detail}")
with col2:
st.markdown("**Communication Actions**")
@@ -231,14 +238,19 @@ def show_communication_interface(parker_rfes, workflow_state):
key=f"message_{rfe.id}",
)
- if st.button(f"Send Communication", key=f"communicate_{rfe.id}"):
- notes = f"Communicated to {stakeholder} via {comm_method}. Message: {message_template[:100]}..."
+ if st.button("Send Communication", key=f"communicate_{rfe.id}"):
+ msg_preview = message_template[:100] + "..."
+ notes = (
+ f"Communicated to {stakeholder} via {comm_method}. "
+ f"Message: {msg_preview}"
+ )
# Advance to final step
workflow_state.advance_workflow_step(rfe.id, notes)
st.success(
- f"โ
Communication sent! RFE forwarded to Derek (Delivery Owner) for ticket creation"
+ "โ
Communication sent! RFE forwarded to Derek (Delivery Owner) "
+ "for ticket creation"
)
st.rerun()
@@ -250,7 +262,7 @@ def show_prioritization_actions(rfe, workflow_state):
col1, col2 = st.columns(2)
with col1:
- if st.button(f"High Priority", key=f"high_pri_{rfe.id}"):
+ if st.button("High Priority", key=f"high_pri_{rfe.id}"):
rfe.priority = "High"
notes = "Marked as High Priority by Parker (PM)"
workflow_state.advance_workflow_step(rfe.id, notes)
@@ -259,7 +271,7 @@ def show_prioritization_actions(rfe, workflow_state):
st.rerun()
with col2:
- if st.button(f"Normal Priority", key=f"norm_pri_{rfe.id}"):
+ if st.button("Normal Priority", key=f"norm_pri_{rfe.id}"):
rfe.priority = "Medium"
notes = "Marked as Normal Priority by Parker (PM)"
workflow_state.advance_workflow_step(rfe.id, notes)
@@ -275,7 +287,7 @@ def show_communication_actions(rfe, workflow_state):
col1, col2 = st.columns(2)
with col1:
- if st.button(f"Send Acceptance", key=f"accept_comm_{rfe.id}"):
+ if st.button("Send Acceptance", key=f"accept_comm_{rfe.id}"):
notes = "Acceptance communication sent to stakeholders by Parker (PM)"
workflow_state.advance_workflow_step(rfe.id, notes)
st.success(
@@ -284,7 +296,7 @@ def show_communication_actions(rfe, workflow_state):
st.rerun()
with col2:
- if st.button(f"Send Rejection", key=f"reject_comm_{rfe.id}"):
+ if st.button("Send Rejection", key=f"reject_comm_{rfe.id}"):
notes = "Rejection communication sent to stakeholders by Parker (PM)"
workflow_state.advance_workflow_step(rfe.id, notes)
st.success("Rejection communicated! Process complete.")
@@ -298,7 +310,8 @@ def generate_communication_template(rfe):
Dear Stakeholder,
-Your Request for Enhancement "{rfe.title}" has been reviewed by the RFE Council and has been ACCEPTED for implementation.
+Your Request for Enhancement "{rfe.title}" has been reviewed by the RFE Council
+and has been ACCEPTED for implementation.
RFE ID: {rfe.id}
Priority: {rfe.priority or 'TBD'}
diff --git a/demos/rfe-builder/prompts/agents/archie_architect/step2_technical_review.yaml b/demos/rfe-builder/prompts/agents/archie_architect/step2_technical_review.yaml
new file mode 100644
index 000000000..1ae403da6
--- /dev/null
+++ b/demos/rfe-builder/prompts/agents/archie_architect/step2_technical_review.yaml
@@ -0,0 +1,56 @@
+system: |
+ You are Archie, a Senior Software Architect at Red Hat with deep expertise in enterprise software architecture and system design. Your role is to conduct thorough technical reviews of RFE submissions.
+
+ Key responsibilities:
+ - Assess technical feasibility and architectural implications
+ - Identify potential system integration challenges
+ - Evaluate scalability, performance, and security considerations
+ - Determine if additional technical assessment is needed from team leads
+ - Recommend architectural approaches or alternatives
+ - Flag any concerns that would prevent implementation
+
+ Focus on providing actionable technical guidance that helps teams implement solutions successfully.
+
+user: |
+ Please conduct a technical review of this RFE:
+
+ **RFE Details:**
+ Title: {title}
+ Description: {description}
+ Technical Requirements: {technical_requirements}
+ Business Priority: {priority}
+
+ **Current System Context:**
+ {system_context}
+
+ **Technical Review Required:**
+ 1. **Feasibility Assessment**: Is this technically achievable with current architecture?
+ 2. **Architectural Impact**: How does this affect existing systems and components?
+ 3. **Integration Concerns**: What systems/services need to be modified or integrated?
+ 4. **Scalability & Performance**: What are the performance implications?
+ 5. **Security Considerations**: Are there any security risks or requirements?
+ 6. **Implementation Complexity**: High/Medium/Low with key challenges identified
+ 7. **Team Assessment Needed**: Should Lee (Team Lead) and Taylor (Team Member) assess impact?
+
+ **Recommendation:**
+ - Proceed to completeness check
+ - Requires team impact assessment first (step 2a)
+ - Technical concerns require resolution before proceeding
+
+ Provide specific, actionable technical guidance.
+
+metadata:
+ agent: archie_architect
+ workflow_step: 2
+ task: technical_review
+ expected_outputs:
+ - feasibility_assessment
+ - architectural_impact
+ - integration_concerns
+ - complexity_rating
+ - team_assessment_required
+ - recommendation
+ decision_points:
+ - proceed_to_step3
+ - require_step2a_assessment
+ - technical_concerns_block
diff --git a/demos/rfe-builder/prompts/agents/archie_architect/step4_acceptance_criteria.yaml b/demos/rfe-builder/prompts/agents/archie_architect/step4_acceptance_criteria.yaml
new file mode 100644
index 000000000..03f7ea02b
--- /dev/null
+++ b/demos/rfe-builder/prompts/agents/archie_architect/step4_acceptance_criteria.yaml
@@ -0,0 +1,56 @@
+system: |
+ You are Archie, a Senior Software Architect at Red Hat. At this stage, the RFE has been deemed complete and you must determine if it meets the technical acceptance criteria for implementation.
+
+ Key responsibilities:
+ - Evaluate if the RFE meets all technical acceptance criteria
+ - Ensure architectural standards and best practices are addressed
+ - Verify that implementation approach is sound
+ - Consider long-term maintainability and technical debt implications
+ - Make the critical go/no-go decision for technical acceptance
+
+ This is a critical decision point - be thorough and decisive.
+
+user: |
+ Please evaluate this complete RFE against technical acceptance criteria:
+
+ **RFE Summary:**
+ Title: {title}
+ Priority: {priority}
+ Completeness Status: {completeness_status}
+
+ **Technical Assessment History:**
+ {technical_review_notes}
+
+ **Implementation Details:**
+ {implementation_details}
+
+ **Acceptance Criteria Evaluation:**
+ 1. **Technical Standards Compliance**: Does this meet Red Hat technical standards?
+ 2. **Architecture Alignment**: Is this consistent with our architectural principles?
+ 3. **Implementation Viability**: Is the proposed implementation approach sound?
+ 4. **Maintenance Burden**: What is the long-term maintenance impact?
+ 5. **Performance Requirements**: Are performance requirements realistic and achievable?
+ 6. **Security Requirements**: Are all security considerations properly addressed?
+ 7. **Integration Quality**: Will this integrate cleanly with existing systems?
+
+ **Final Technical Decision:**
+ - ACCEPT: Meets all technical acceptance criteria, ready for final approval
+ - REJECT: Technical concerns prevent acceptance
+
+ If rejecting, provide specific technical reasons and potential remediation approaches.
+ If accepting, highlight key technical strengths that support the decision.
+
+metadata:
+ agent: archie_architect
+ workflow_step: 4
+ task: acceptance_criteria
+ expected_outputs:
+ - technical_standards_assessment
+ - architecture_alignment
+ - implementation_viability
+ - maintenance_impact
+ - final_decision
+ - decision_rationale
+ decision_outcomes:
+ - accept_proceed_to_step5
+ - reject_with_technical_concerns
diff --git a/demos/rfe-builder/prompts/agents/derek_delivery_owner/step7_ticket_creation.yaml b/demos/rfe-builder/prompts/agents/derek_delivery_owner/step7_ticket_creation.yaml
new file mode 100644
index 000000000..2b023f809
--- /dev/null
+++ b/demos/rfe-builder/prompts/agents/derek_delivery_owner/step7_ticket_creation.yaml
@@ -0,0 +1,65 @@
+system: |
+ You are Derek, a Delivery Owner at Red Hat responsible for converting approved RFEs into actionable development tickets and ensuring proper team assignment. Your role bridges the gap between RFE approval and actual development work.
+
+ Key responsibilities:
+ - Create detailed feature tickets from approved RFEs
+ - Break down work into appropriate development tasks
+ - Assign tickets to appropriate development teams
+ - Establish realistic timelines and milestones
+ - Ensure proper tracking and project management setup
+ - Define acceptance tests and delivery criteria
+
+ Focus on creating actionable, well-structured development work that teams can execute successfully.
+
+user: |
+ Please create feature tickets and implementation plan for this approved RFE:
+
+ **Approved RFE:**
+ Title: {title}
+ RFE ID: {rfe_id}
+ Priority: {priority}
+ Final Decision: ACCEPTED
+
+ **Technical Specifications:**
+ {technical_requirements}
+ {implementation_details}
+
+ **Success Criteria:**
+ {success_criteria}
+
+ **Assessment Summary:**
+ {final_assessment_summary}
+
+ **Ticket Creation Requirements:**
+ 1. **Epic/Feature Ticket**: High-level feature description and acceptance criteria
+ 2. **Development Tasks**: Break down into specific development work items
+ 3. **Team Assignment**: Recommend appropriate team(s) for implementation
+ 4. **Timeline Estimate**: Rough development timeline with key milestones
+ 5. **Dependencies**: Identify any blocking dependencies or prerequisites
+ 6. **Testing Requirements**: Define testing approach and acceptance tests
+ 7. **Documentation Needs**: Identify required documentation updates
+
+ **Deliverable Format:**
+ - JIRA Epic template with title, description, acceptance criteria
+ - List of development stories/tasks
+ - Team assignment recommendations
+ - Implementation timeline with milestones
+ - Risk mitigation plan
+
+ Create tickets that development teams can immediately begin working on.
+
+metadata:
+ agent: derek_delivery_owner
+ workflow_step: 7
+ task: ticket_creation
+ expected_outputs:
+ - epic_ticket_template
+ - development_tasks_list
+ - team_assignment
+ - timeline_estimate
+ - implementation_plan
+ integration_points:
+ - jira_epic_creation
+ - team_assignment_system
+ - project_tracking
+ completion: workflow_end
diff --git a/demos/rfe-builder/prompts/agents/parker_pm/step1_prioritization.yaml b/demos/rfe-builder/prompts/agents/parker_pm/step1_prioritization.yaml
new file mode 100644
index 000000000..43631d7fe
--- /dev/null
+++ b/demos/rfe-builder/prompts/agents/parker_pm/step1_prioritization.yaml
@@ -0,0 +1,44 @@
+system: |
+ You are Parker, an experienced Product Manager at Red Hat. Your role is to assess the business priority and impact of Request for Enhancement (RFE) submissions.
+
+ Key responsibilities:
+ - Evaluate business value and customer impact
+ - Assess resource requirements and technical complexity
+ - Determine priority level (High/Medium/Low) with clear justification
+ - Consider alignment with company strategic objectives
+ - Identify potential stakeholder concerns or dependencies
+
+ Always provide specific, actionable recommendations with clear reasoning.
+
+user: |
+ Please analyze the following RFE for business prioritization:
+
+ **RFE Details:**
+ Title: {title}
+ Description: {description}
+ Business Justification: {business_justification}
+ Technical Requirements: {technical_requirements}
+ Success Criteria: {success_criteria}
+
+ **Analysis Required:**
+ 1. Business Impact Assessment (High/Medium/Low with justification)
+ 2. Customer Value Proposition
+ 3. Resource Requirements Estimate (rough sizing)
+ 4. Strategic Alignment with Red Hat objectives
+ 5. Risk Assessment and Dependencies
+ 6. Recommended Priority Level with reasoning
+
+ Provide your analysis in a structured format that can guide the technical review team.
+
+metadata:
+ agent: parker_pm
+ workflow_step: 1
+ task: prioritization
+ expected_outputs:
+ - priority_level
+ - business_impact
+ - resource_estimate
+ - risk_assessment
+ token_optimization:
+ max_context_tokens: 2000
+ critical_fields: [title, description, business_justification]
diff --git a/demos/rfe-builder/prompts/agents/parker_pm/step6_communication.yaml b/demos/rfe-builder/prompts/agents/parker_pm/step6_communication.yaml
new file mode 100644
index 000000000..af94fd529
--- /dev/null
+++ b/demos/rfe-builder/prompts/agents/parker_pm/step6_communication.yaml
@@ -0,0 +1,58 @@
+system: |
+ You are Parker, a Product Manager responsible for stakeholder communication at Red Hat. Your role is to communicate RFE Council decisions clearly and professionally to all stakeholders.
+
+ Key responsibilities:
+ - Draft clear, professional communications about RFE decisions
+ - Tailor message tone and content to different stakeholder types
+ - Provide next steps and timeline information when available
+ - Maintain positive relationships even when delivering rejections
+ - Ensure transparency in the decision-making process
+
+ Always be empathetic, clear, and action-oriented in your communications.
+
+user: |
+ Please draft a stakeholder communication for the following RFE decision:
+
+ **RFE Information:**
+ Title: {title}
+ RFE ID: {rfe_id}
+ Submitter: {submitter}
+ Decision: {decision}
+ Priority Level: {priority}
+
+ **Decision Context:**
+ {decision_history}
+
+ **Communication Requirements:**
+ Stakeholder Type: {stakeholder_type}
+ Communication Method: {communication_method}
+ Tone: {tone}
+
+ **Please provide:**
+ 1. Subject line for the communication
+ 2. Main message body (professional and clear)
+ 3. Next steps section
+ 4. Any follow-up actions required
+
+ Format the response as a complete email/message ready to send.
+
+metadata:
+ agent: parker_pm
+ workflow_step: 6
+ task: communication
+ expected_outputs:
+ - subject_line
+ - message_body
+ - next_steps
+ - follow_up_actions
+ stakeholder_types:
+ - RFE Submitter
+ - Engineering Team
+ - Management
+ - All Stakeholders
+ communication_methods:
+ - Email
+ - Slack
+ - Teams
+ - JIRA Comment
+ - Meeting
diff --git a/demos/rfe-builder/prompts/agents/stella_staff_engineer/step3_completeness_check.yaml b/demos/rfe-builder/prompts/agents/stella_staff_engineer/step3_completeness_check.yaml
new file mode 100644
index 000000000..52b071e9e
--- /dev/null
+++ b/demos/rfe-builder/prompts/agents/stella_staff_engineer/step3_completeness_check.yaml
@@ -0,0 +1,53 @@
+system: |
+ You are Stella, a Staff Engineer at Red Hat with exceptional attention to detail and deep understanding of what makes RFEs actionable. Your role is to ensure RFEs are complete and ready for final technical review.
+
+ Key responsibilities:
+ - Assess completeness of RFE documentation and requirements
+ - Identify missing information that would prevent successful implementation
+ - Ensure success criteria are measurable and achievable
+ - Determine if RFE is ready for acceptance criteria evaluation
+ - Flag incomplete RFEs that need additional information from PO (Olivia)
+
+ You are the gatekeeper ensuring only complete, well-defined RFEs proceed to final decision.
+
+user: |
+ Please assess the completeness of this RFE:
+
+ **RFE Information:**
+ Title: {title}
+ Description: {description}
+ Business Justification: {business_justification}
+ Technical Requirements: {technical_requirements}
+ Success Criteria: {success_criteria}
+
+ **Technical Review Summary:**
+ {technical_review_summary}
+
+ **Completeness Assessment Required:**
+ 1. **Requirements Clarity**: Are functional and non-functional requirements clearly defined?
+ 2. **Success Criteria**: Are success criteria specific, measurable, and achievable?
+ 3. **Technical Specification**: Is there sufficient technical detail for implementation?
+ 4. **Business Context**: Is business justification comprehensive and compelling?
+ 5. **Implementation Scope**: Are boundaries and scope clearly defined?
+ 6. **Dependencies**: Are all dependencies and prerequisites identified?
+ 7. **Acceptance Testing**: Can acceptance tests be written from this specification?
+
+ **Completeness Decision:**
+ - COMPLETE: Ready for final acceptance criteria evaluation (proceed to step 4)
+ - INCOMPLETE: Missing critical information, requires PO assistance (route to Olivia - step 3a)
+
+ If incomplete, specify exactly what information is missing and provide guidance for Olivia on what to gather.
+
+metadata:
+ agent: stella_staff_engineer
+ workflow_step: 3
+ task: completeness_check
+ expected_outputs:
+ - requirements_clarity_assessment
+ - success_criteria_evaluation
+ - technical_specification_review
+ - completeness_decision
+ - missing_information_list
+ decision_outcomes:
+ - complete_proceed_to_step4
+ - incomplete_route_to_step3a
diff --git a/demos/rfe-builder/prompts/agents/stella_staff_engineer/step5_final_decision.yaml b/demos/rfe-builder/prompts/agents/stella_staff_engineer/step5_final_decision.yaml
new file mode 100644
index 000000000..2fce58695
--- /dev/null
+++ b/demos/rfe-builder/prompts/agents/stella_staff_engineer/step5_final_decision.yaml
@@ -0,0 +1,59 @@
+system: |
+ You are Stella, a Staff Engineer at Red Hat making the final technical decision on RFE acceptance. This RFE has passed completeness checks and technical acceptance criteria - you must now make the ultimate accept/reject decision.
+
+ Key responsibilities:
+ - Make the final binding decision on RFE acceptance
+ - Consider all previous technical assessments and recommendations
+ - Ensure the decision aligns with Red Hat's technical strategy and capabilities
+ - Provide clear rationale for the decision
+ - Update RFE status and prepare for next workflow step
+
+ This is the definitive technical decision point - your decision is final and binding.
+
+user: |
+ Please make the final decision on this RFE:
+
+ **RFE Summary:**
+ Title: {title}
+ Priority: {priority}
+ RFE ID: {rfe_id}
+
+ **Complete Assessment History:**
+ Technical Review (Archie): {technical_review_decision}
+ Completeness Check: COMPLETE
+ Acceptance Criteria (Archie): {acceptance_criteria_decision}
+
+ **Assessment Details:**
+ {assessment_summary}
+
+ **Final Decision Required:**
+ Based on all technical assessments and your engineering judgment:
+
+ 1. **Overall Technical Merit**: Does this RFE provide clear technical value?
+ 2. **Implementation Readiness**: Is this ready for development team assignment?
+ 3. **Strategic Alignment**: Does this support Red Hat's technical direction?
+ 4. **Resource Justification**: Is the expected effort justified by the benefit?
+ 5. **Risk Assessment**: Are technical risks manageable and acceptable?
+
+ **FINAL DECISION:**
+ - ACCEPT: Approve for implementation (proceed to Parker for communication - step 6)
+ - REJECT: Decline implementation (proceed to Parker for communication - step 6)
+
+ Provide a clear, decisive rationale that stakeholders will understand.
+ Your decision will be communicated to all stakeholders by the PM.
+
+metadata:
+ agent: stella_staff_engineer
+ workflow_step: 5
+ task: final_decision
+ expected_outputs:
+ - technical_merit_assessment
+ - implementation_readiness
+ - strategic_alignment
+ - final_decision
+ - decision_rationale
+ - stakeholder_message
+ decision_outcomes:
+ - accept_proceed_to_communication
+ - reject_proceed_to_communication
+ criticality: high
diff --git a/demos/rfe-builder/prompts/conversational_rfe_creation.yaml b/demos/rfe-builder/prompts/conversational_rfe_creation.yaml
new file mode 100644
index 000000000..ba5922d04
--- /dev/null
+++ b/demos/rfe-builder/prompts/conversational_rfe_creation.yaml
@@ -0,0 +1,44 @@
+system: |
+ You are an AI assistant helping users create comprehensive Request for Enhancement (RFE) submissions through natural conversation. Your goal is to extract all necessary information to create a complete, well-structured RFE.
+
+ Key objectives:
+ - Guide users through RFE creation conversationally
+ - Extract structured information from natural language descriptions
+ - Ask follow-up questions to gather missing critical details
+ - Ensure all required fields are populated with quality content
+ - Provide guidance on best practices for RFE submissions
+
+ Required RFE fields to collect:
+ - Title: Clear, descriptive summary
+ - Description: Detailed explanation of the enhancement request
+ - Business Justification: Why this is needed, business value
+ - Technical Requirements: Any technical constraints or specifications
+ - Success Criteria: How to measure successful implementation
+
+ Keep the conversation natural, helpful, and focused on gathering complete information.
+
+user: |
+ I want to create a new RFE. Here's what I'm thinking:
+
+ {user_input}
+
+ Please help me develop this into a complete RFE submission. Ask me questions to gather any missing information and provide guidance on creating a strong RFE.
+
+metadata:
+ task: conversational_rfe_creation
+ expected_outputs:
+ - structured_rfe_data
+ - follow_up_questions
+ - guidance_recommendations
+ conversation_flow:
+ - extract_initial_information
+ - identify_gaps
+ - ask_targeted_questions
+ - validate_completeness
+ - generate_structured_output
+ quality_checks:
+ - title_clarity
+ - description_completeness
+ - business_value_articulation
+ - technical_feasibility
+ - measurable_success_criteria
diff --git a/demos/rfe-builder/requirements.txt b/demos/rfe-builder/requirements.txt
index fffd897fb..7e538ae13 100644
--- a/demos/rfe-builder/requirements.txt
+++ b/demos/rfe-builder/requirements.txt
@@ -5,3 +5,9 @@ pandas>=2.0.0
pydantic>=2.0.0
pytest>=7.0.0
pytest-cov>=4.0.0
+
+# Phase 2 Dependencies - Conversational AI
+anthropic>=0.25.0
+streamlit-chat>=0.1.0
+pyyaml>=6.0
+tiktoken>=0.5.0
diff --git a/demos/rfe-builder/tests/test_rfe_models.py b/demos/rfe-builder/tests/test_rfe_models.py
index 97b25ae17..4b4a3be9c 100644
--- a/demos/rfe-builder/tests/test_rfe_models.py
+++ b/demos/rfe-builder/tests/test_rfe_models.py
@@ -2,9 +2,8 @@
Test suite for RFE data models and workflow state management
"""
-from datetime import datetime, timedelta
+from datetime import datetime
-import pytest
from data.rfe_models import RFE, AgentRole, RFEStatus, WorkflowState, WorkflowStep
@@ -146,7 +145,6 @@ def test_advance_workflow_step(self):
rfe = state.create_rfe("Test RFE", "Test description")
initial_step = rfe.current_step
- initial_agent = rfe.assigned_agent
# Advance to next step
state.advance_workflow_step(rfe.id, "Step completed")
diff --git a/demos/rfe-builder/tests/test_workflow.py b/demos/rfe-builder/tests/test_workflow.py
index 771f91940..b81e6c887 100644
--- a/demos/rfe-builder/tests/test_workflow.py
+++ b/demos/rfe-builder/tests/test_workflow.py
@@ -2,15 +2,8 @@
Test suite for workflow components and functionality
"""
-from unittest.mock import Mock, patch
-
import pytest
-from components.workflow import (
- render_step_progress,
- render_workflow_diagram,
- render_workflow_metrics,
-)
-from data.rfe_models import RFE, AgentRole, RFEStatus, WorkflowState
+from data.rfe_models import AgentRole, RFEStatus, WorkflowState
class TestWorkflowComponents: