fix: frontend - backend issues and styles

This commit is contained in:
Kevin Bruton 2025-09-29 08:41:10 +02:00
parent e9b0ad42f0
commit 77629bc89e
6 changed files with 187 additions and 45 deletions

Binary file not shown.

View File

@ -203,20 +203,17 @@ class TradingAgentsGraph:
)
args = self.propagator.get_graph_args()
if self.debug:
# Debug mode with tracing
if on_step_callback or self.debug:
# Stream mode for callbacks or debug mode
trace = []
for s in self.graph.stream(init_agent_state, **args):
trace.append(s)
if on_step_callback:
on_step_callback(s)
final_state = trace[-1]
final_state = trace[-1] if trace else {}
else:
# Standard mode without tracing
final_state = self.graph.invoke(init_agent_state, **args)
# If not in debug mode, we still want to call the callback for the final state
if on_step_callback:
on_step_callback(final_state)
# Store current state for reflection
self.curr_state = final_state

View File

@ -50,44 +50,103 @@ jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir))
def update_execution_state(state: Dict[str, Any]):
"""Callback function to update the app_state based on LangGraph's state."""
with app_state_lock:
current_step_name = None
# LangGraph state typically has a single key for the current node's output
# We need to find which agent just ran
for key, value in state.items():
if key != "__end__": # Ignore the special __end__ key
current_step_name = key
break
if current_step_name:
# Find the root node or create it if it doesn't exist
if not app_state["execution_tree"]:
app_state["execution_tree"].append({
"id": "root",
"name": f"Trading Analysis for {app_state['company_symbol']}",
"status": "in_progress",
"content": "",
"children": [],
"timestamp": time.time()
})
root_node = app_state["execution_tree"][0]
# Check if this step already exists (e.g., if an agent runs multiple times)
# For simplicity, we'll just append for now. A more robust solution would update existing.
new_item = {
"id": f"{current_step_name}-{len(root_node['children'])}", # Simple unique ID
"name": current_step_name,
"status": "completed", # Assume completed for now
"content": str(state.get(current_step_name, "No specific output")), # Store the agent's output
# Initialize the root node if it doesn't exist
if not app_state["execution_tree"]:
app_state["execution_tree"].append({
"id": "root",
"name": f"Trading Analysis for {app_state['company_symbol']}",
"status": "in_progress",
"content": f"Analyzing {app_state['company_symbol']} using multiple trading agents",
"children": [],
"timestamp": time.time()
}
root_node["children"].append(new_item)
root_node["status"] = "in_progress" # Keep root in progress until final
# Update overall progress (very basic, just increments)
# In a real scenario, you'd have a predefined number of steps
app_state["overall_progress"] = min(100, app_state["overall_progress"] + 5)
})
root_node = app_state["execution_tree"][0]
# Define the expected phases and their order
phase_map = {
"market_analyst": {"name": "Market Analysis", "phase": "data_collection"},
"social_analyst": {"name": "Social Media Analysis", "phase": "data_collection"},
"news_analyst": {"name": "News Analysis", "phase": "data_collection"},
"fundamentals_analyst": {"name": "Fundamental Analysis", "phase": "data_collection"},
"bull_researcher": {"name": "Bull Case Research", "phase": "research"},
"bear_researcher": {"name": "Bear Case Research", "phase": "research"},
"research_manager": {"name": "Research Synthesis", "phase": "research"},
"trade_planner": {"name": "Trade Planning", "phase": "planning"},
"trader": {"name": "Trade Execution", "phase": "execution"},
"risky_analyst": {"name": "Risk Assessment (Aggressive)", "phase": "risk_analysis"},
"neutral_analyst": {"name": "Risk Assessment (Neutral)", "phase": "risk_analysis"},
"safe_analyst": {"name": "Risk Assessment (Conservative)", "phase": "risk_analysis"},
"risk_judge": {"name": "Final Risk Evaluation", "phase": "risk_analysis"}
}
# Find which agent just completed by examining the state
for key, value in state.items():
if key in ["__end__", "messages"]:
continue
# Map the key to a more user-friendly name
agent_key = key.lower().replace(" ", "_").replace("_agent", "").replace("_node", "")
if agent_key in phase_map:
phase_info = phase_map[agent_key]
# Find or create phase category
phase_category = None
for child in root_node["children"]:
if child["id"] == phase_info["phase"]:
phase_category = child
break
if not phase_category:
phase_names = {
"data_collection": "📊 Data Collection",
"research": "🔍 Research & Analysis",
"planning": "📋 Trade Planning",
"execution": "⚡ Trade Execution",
"risk_analysis": "⚠️ Risk Management"
}
phase_category = {
"id": phase_info["phase"],
"name": phase_names.get(phase_info["phase"], phase_info["phase"]),
"status": "in_progress",
"content": f"Phase: {phase_names.get(phase_info['phase'], phase_info['phase'])}",
"children": [],
"timestamp": time.time()
}
root_node["children"].append(phase_category)
# Check if this specific step already exists
step_exists = False
for step in phase_category["children"]:
if step["name"] == phase_info["name"]:
step["status"] = "completed"
step["content"] = str(value) if value else "Completed successfully"
step_exists = True
break
if not step_exists:
# Add new step
new_step = {
"id": f"{phase_info['phase']}_{agent_key}_{len(phase_category['children'])}",
"name": phase_info["name"],
"status": "completed",
"content": str(value) if value else "Completed successfully",
"children": [],
"timestamp": time.time()
}
phase_category["children"].append(new_step)
# Check if phase is complete (simple heuristic)
completed_steps = sum(1 for step in phase_category["children"] if step["status"] == "completed")
if completed_steps >= len(phase_category["children"]):
phase_category["status"] = "completed"
# Update overall progress based on completed phases
total_phases = len([p for p in phase_map.values()])
completed_agents = sum(len(child["children"]) for child in root_node["children"]
if child.get("children"))
app_state["overall_progress"] = min(100, int((completed_agents / max(total_phases, 1)) * 100))
def run_trading_process(company_symbol: str):
"""Runs the TradingAgentsGraph in a separate thread."""

View File

@ -205,6 +205,81 @@ body {
font-size: 1.1em;
}
/* Execution Tree Styles */
.execution-tree {
margin-top: 20px;
}
.execution-tree ul {
margin-left: 20px;
border-left: 2px solid var(--border-color);
padding-left: 15px;
}
.process-item {
position: relative;
margin-bottom: 5px;
}
.process-item::before {
content: '';
position: absolute;
left: -18px;
top: 12px;
width: 8px;
height: 2px;
background-color: var(--border-color);
}
.clickable {
cursor: pointer;
transition: all 0.2s ease;
}
.clickable:hover {
transform: translateX(3px);
}
.status-icon {
margin-right: 8px;
font-size: 0.9em;
}
/* Loading indicator */
.htmx-indicator {
display: none;
color: var(--accent-color);
font-size: 0.9em;
margin-top: 10px;
animation: pulse 2s infinite;
}
.htmx-request .htmx-indicator {
display: block;
}
/* Content display improvements */
#right-panel pre {
background-color: var(--bg-secondary);
padding: 20px;
border-radius: 8px;
border: 1px solid var(--border-color);
color: var(--text-primary);
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
font-size: 0.9em;
line-height: 1.4;
overflow-x: auto;
white-space: pre-wrap;
word-wrap: break-word;
}
#right-panel h3 {
color: var(--text-primary);
margin-bottom: 15px;
font-size: 1.3em;
font-weight: 600;
}
/* Animations */
@keyframes pulse {
0% {

View File

@ -1,6 +1,15 @@
{% macro render_item(item) %}
<li class="process-item status-{{ item.status }}">
<span hx-get="/content/{{ item.id }}" hx-target="#right-panel" hx-swap="innerHTML" class="item-name">{{ item.name }}</span>
<span hx-get="/content/{{ item.id }}" hx-target="#right-panel" hx-swap="innerHTML" class="item-name clickable">
<span class="status-icon">
{% if item.status == 'completed' %}✓
{% elif item.status == 'in_progress' %}⏳
{% elif item.status == 'error' %}❌
{% else %}⏸️
{% endif %}
</span>
{{ item.name }}
</span>
{% if item.children %}
<ul>
{% for child in item.children %}
@ -17,7 +26,7 @@
<div id="left-panel-content" hx-get="/status" hx-trigger="every 2s" hx-swap="innerHTML">
<h2>Execution Status</h2>
{% if tree %}
<ul>
<ul class="execution-tree">
{% for item in tree %}
{{ render_item(item) }}
{% endfor %}

View File

@ -19,15 +19,17 @@
<div id="left-panel">
<h2>Configuration</h2>
<div id="config-form">
<form hx-post="/start" hx-target="#left-panel" hx-swap="innerHTML">
<form hx-post="/start" hx-target="#left-panel" hx-swap="innerHTML" hx-indicator="#loading">
<label for="company_symbol">Company Symbol:</label>
<input type="text" id="company_symbol" name="company_symbol" value="AAPL" required>
<button type="submit">Start Process</button>
<div id="loading" class="htmx-indicator">Starting process...</div>
</form>
</div>
</div>
<div id="right-panel">
<p>Welcome! Please set your configuration and start the process.</p>
<p>Enter a company symbol (e.g., AAPL, MSFT, GOOGL) and click "Start Process" to begin the trading analysis.</p>
</div>
</div>
</body>