fix: frontend - backend issues and styles
This commit is contained in:
parent
e9b0ad42f0
commit
77629bc89e
Binary file not shown.
|
|
@ -203,20 +203,17 @@ class TradingAgentsGraph:
|
||||||
)
|
)
|
||||||
args = self.propagator.get_graph_args()
|
args = self.propagator.get_graph_args()
|
||||||
|
|
||||||
if self.debug:
|
if on_step_callback or self.debug:
|
||||||
# Debug mode with tracing
|
# Stream mode for callbacks or debug mode
|
||||||
trace = []
|
trace = []
|
||||||
for s in self.graph.stream(init_agent_state, **args):
|
for s in self.graph.stream(init_agent_state, **args):
|
||||||
trace.append(s)
|
trace.append(s)
|
||||||
if on_step_callback:
|
if on_step_callback:
|
||||||
on_step_callback(s)
|
on_step_callback(s)
|
||||||
final_state = trace[-1]
|
final_state = trace[-1] if trace else {}
|
||||||
else:
|
else:
|
||||||
# Standard mode without tracing
|
# Standard mode without tracing
|
||||||
final_state = self.graph.invoke(init_agent_state, **args)
|
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
|
# Store current state for reflection
|
||||||
self.curr_state = final_state
|
self.curr_state = final_state
|
||||||
|
|
|
||||||
129
webapp/main.py
129
webapp/main.py
|
|
@ -50,44 +50,103 @@ jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir))
|
||||||
def update_execution_state(state: Dict[str, Any]):
|
def update_execution_state(state: Dict[str, Any]):
|
||||||
"""Callback function to update the app_state based on LangGraph's state."""
|
"""Callback function to update the app_state based on LangGraph's state."""
|
||||||
with app_state_lock:
|
with app_state_lock:
|
||||||
current_step_name = None
|
# Initialize the root node if it doesn't exist
|
||||||
# LangGraph state typically has a single key for the current node's output
|
if not app_state["execution_tree"]:
|
||||||
# We need to find which agent just ran
|
app_state["execution_tree"].append({
|
||||||
for key, value in state.items():
|
"id": "root",
|
||||||
if key != "__end__": # Ignore the special __end__ key
|
"name": f"Trading Analysis for {app_state['company_symbol']}",
|
||||||
current_step_name = key
|
"status": "in_progress",
|
||||||
break
|
"content": f"Analyzing {app_state['company_symbol']} using multiple trading agents",
|
||||||
|
|
||||||
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
|
|
||||||
"children": [],
|
"children": [],
|
||||||
"timestamp": time.time()
|
"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)
|
root_node = app_state["execution_tree"][0]
|
||||||
# In a real scenario, you'd have a predefined number of steps
|
|
||||||
app_state["overall_progress"] = min(100, app_state["overall_progress"] + 5)
|
# 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):
|
def run_trading_process(company_symbol: str):
|
||||||
"""Runs the TradingAgentsGraph in a separate thread."""
|
"""Runs the TradingAgentsGraph in a separate thread."""
|
||||||
|
|
|
||||||
|
|
@ -205,6 +205,81 @@ body {
|
||||||
font-size: 1.1em;
|
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 */
|
/* Animations */
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
0% {
|
0% {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,15 @@
|
||||||
{% macro render_item(item) %}
|
{% macro render_item(item) %}
|
||||||
<li class="process-item status-{{ item.status }}">
|
<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 %}
|
{% if item.children %}
|
||||||
<ul>
|
<ul>
|
||||||
{% for child in item.children %}
|
{% for child in item.children %}
|
||||||
|
|
@ -17,7 +26,7 @@
|
||||||
<div id="left-panel-content" hx-get="/status" hx-trigger="every 2s" hx-swap="innerHTML">
|
<div id="left-panel-content" hx-get="/status" hx-trigger="every 2s" hx-swap="innerHTML">
|
||||||
<h2>Execution Status</h2>
|
<h2>Execution Status</h2>
|
||||||
{% if tree %}
|
{% if tree %}
|
||||||
<ul>
|
<ul class="execution-tree">
|
||||||
{% for item in tree %}
|
{% for item in tree %}
|
||||||
{{ render_item(item) }}
|
{{ render_item(item) }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
||||||
|
|
@ -19,15 +19,17 @@
|
||||||
<div id="left-panel">
|
<div id="left-panel">
|
||||||
<h2>Configuration</h2>
|
<h2>Configuration</h2>
|
||||||
<div id="config-form">
|
<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>
|
<label for="company_symbol">Company Symbol:</label>
|
||||||
<input type="text" id="company_symbol" name="company_symbol" value="AAPL" required>
|
<input type="text" id="company_symbol" name="company_symbol" value="AAPL" required>
|
||||||
<button type="submit">Start Process</button>
|
<button type="submit">Start Process</button>
|
||||||
|
<div id="loading" class="htmx-indicator">Starting process...</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="right-panel">
|
<div id="right-panel">
|
||||||
<p>Welcome! Please set your configuration and start the process.</p>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue