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
21 changes: 21 additions & 0 deletions demos/rfe-builder/.streamlit/secrets.toml
Original file line number Diff line number Diff line change
@@ -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
21 changes: 21 additions & 0 deletions demos/rfe-builder/.streamlit/secrets.toml.template
Original file line number Diff line number Diff line change
@@ -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
54 changes: 44 additions & 10 deletions demos/rfe-builder/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,23 @@

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
- **πŸ”„ State Management**: Persistent RFE data with comprehensive history tracking
- **πŸ“‹ 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
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand Down
Empty file.
196 changes: 196 additions & 0 deletions demos/rfe-builder/ai_models/cost_tracker.py
Original file line number Diff line number Diff line change
@@ -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]
Loading