[add] ai 분석 됨. 프론트엔드만 다듬자
This commit is contained in:
parent
2c77fcef1f
commit
42371adff9
File diff suppressed because it is too large
Load Diff
|
|
@ -119,3 +119,47 @@ Adhere strictly to these instructions, and ensure your output is detailed, accur
|
|||
"RISK JUDGE", judge_decision, situation, returns_losses
|
||||
)
|
||||
risk_manager_memory.add_situations([(situation, result)])
|
||||
|
||||
@staticmethod
|
||||
def generate_final_report(final_state: dict) -> str:
|
||||
"""Generate a final, comprehensive report from the final state."""
|
||||
|
||||
report_parts = []
|
||||
report_parts.append(f"# 최종 분석 보고서: {final_state.get('company_of_interest', 'N/A')}")
|
||||
report_parts.append(f"**분석 기준일:** {final_state.get('trade_date', 'N/A')}")
|
||||
report_parts.append("---")
|
||||
|
||||
# 각 분석가 리포트 추가
|
||||
if final_state.get('market_report'):
|
||||
report_parts.append("## 시장 분석가 리포트")
|
||||
report_parts.append(final_state['market_report'])
|
||||
|
||||
if final_state.get('sentiment_report'):
|
||||
report_parts.append("## 소셜 미디어 분석가 리포트")
|
||||
report_parts.append(final_state['sentiment_report'])
|
||||
|
||||
if final_state.get('news_report'):
|
||||
report_parts.append("## 뉴스 분석가 리포트")
|
||||
report_parts.append(final_state['news_report'])
|
||||
|
||||
if final_state.get('fundamentals_report'):
|
||||
report_parts.append("## 재무 분석가 리포트")
|
||||
report_parts.append(final_state['fundamentals_report'])
|
||||
|
||||
# 투자 토론 요약 추가
|
||||
if final_state.get('investment_debate_state'):
|
||||
debate = final_state['investment_debate_state']
|
||||
report_parts.append("## 투자 결정 토론 요약")
|
||||
report_parts.append(f"**심사위원 최종 결정:** {debate.get('judge_decision', 'N/A')}")
|
||||
|
||||
# 최종 투자 계획 및 결정 추가
|
||||
if final_state.get('investment_plan'):
|
||||
report_parts.append("## 최종 투자 계획")
|
||||
report_parts.append(final_state['investment_plan'])
|
||||
|
||||
if final_state.get('final_trade_decision'):
|
||||
report_parts.append("## 최종 거래 결정")
|
||||
report_parts.append(final_state['final_trade_decision'])
|
||||
|
||||
report = "\n\n".join(report_parts)
|
||||
return report
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# TradingAgents/graph/setup.py
|
||||
|
||||
from typing import Dict, Any
|
||||
from typing import Dict, Any, List
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.graph import END, StateGraph, START
|
||||
from langgraph.prebuilt import ToolNode
|
||||
|
|
@ -40,9 +40,7 @@ class GraphSetup:
|
|||
self.risk_manager_memory = risk_manager_memory
|
||||
self.conditional_logic = conditional_logic
|
||||
|
||||
def setup_graph(
|
||||
self, selected_analysts=["market", "social", "news", "fundamentals"]
|
||||
):
|
||||
def setup_graph(self, selected_analysts: List[str]):
|
||||
"""Set up and compile the agent workflow graph.
|
||||
|
||||
Args:
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from pathlib import Path
|
|||
import json
|
||||
from datetime import date
|
||||
from typing import Dict, Any, Tuple, List, Optional
|
||||
import asyncio
|
||||
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.prebuilt import ToolNode
|
||||
|
|
@ -31,19 +32,20 @@ class TradingAgentsGraph:
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
selected_analysts=["market", "social", "news", "fundamentals"],
|
||||
debug=False,
|
||||
config: Dict[str, Any] = None,
|
||||
progress_callback=None,
|
||||
debug=False,
|
||||
):
|
||||
"""Initialize the trading agents graph and components.
|
||||
|
||||
Args:
|
||||
selected_analysts: List of analyst types to include
|
||||
debug: Whether to run in debug mode
|
||||
config: Configuration dictionary. If None, uses default config
|
||||
progress_callback: Async function to send progress updates
|
||||
debug: Whether to run in debug mode
|
||||
"""
|
||||
self.debug = debug
|
||||
self.config = config or DEFAULT_CONFIG
|
||||
self.progress_callback = progress_callback
|
||||
|
||||
# Update the interface's config
|
||||
set_config(self.config)
|
||||
|
|
@ -95,8 +97,9 @@ class TradingAgentsGraph:
|
|||
self.ticker = None
|
||||
self.log_states_dict = {} # date to full state dict
|
||||
|
||||
# Set up the graph
|
||||
self.graph = self.graph_setup.setup_graph(selected_analysts)
|
||||
# Set up the graph with default analysts initially
|
||||
default_analysts = ["market", "social", "news", "fundamentals"]
|
||||
self.graph = self.graph_setup.setup_graph(default_analysts)
|
||||
|
||||
def _create_tool_nodes(self) -> Dict[str, ToolNode]:
|
||||
"""Create tool nodes for different data sources."""
|
||||
|
|
@ -143,8 +146,55 @@ class TradingAgentsGraph:
|
|||
),
|
||||
}
|
||||
|
||||
def invoke(self, input_data: Dict) -> Dict:
|
||||
"""Run the trading agents graph for a web-based request."""
|
||||
|
||||
self.ticker = input_data.get("ticker", "UNKNOWN")
|
||||
trade_date = input_data.get("date", date.today().strftime("%Y-%m-%d"))
|
||||
selected_analysts = input_data.get("selected_analysts", [])
|
||||
|
||||
self.graph = self.graph_setup.setup_graph(selected_analysts)
|
||||
|
||||
init_agent_state = self.propagator.create_initial_state(
|
||||
self.ticker, trade_date
|
||||
)
|
||||
args = self.propagator.get_graph_args()
|
||||
|
||||
final_report = ""
|
||||
final_state_result = None
|
||||
|
||||
# 진행률 계산을 위한 변수
|
||||
total_steps = len(self.graph.nodes)
|
||||
step_count = 0
|
||||
|
||||
# Stream the graph execution to get real-time updates
|
||||
for chunk in self.graph.stream(init_agent_state, **args):
|
||||
# 1 청크당 1단계로 간주
|
||||
step_count += 1
|
||||
for node_name, node_output in chunk.items():
|
||||
if self.progress_callback:
|
||||
agent_name = node_name.replace("_node", "").replace("_", " ").title()
|
||||
message = f"Step {step_count}/{total_steps}: {agent_name} is working..."
|
||||
|
||||
# 계산된 진행률과 함께 콜백 호출
|
||||
asyncio.run(self.progress_callback(
|
||||
"agent_update",
|
||||
message,
|
||||
agent_name,
|
||||
step=step_count,
|
||||
total=total_steps
|
||||
))
|
||||
|
||||
final_state_result = chunk
|
||||
|
||||
if final_state_result:
|
||||
final_report = self.reflector.generate_final_report(final_state_result)
|
||||
self._log_state(trade_date, final_state_result)
|
||||
|
||||
return {"final_report": final_report}
|
||||
|
||||
def propagate(self, company_name, trade_date):
|
||||
"""Run the trading agents graph for a company on a specific date."""
|
||||
"""Run the trading agents graph for a company on a specific date (CLI)."""
|
||||
|
||||
self.ticker = company_name
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ from rest_framework import serializers
|
|||
from django.contrib.auth import authenticate
|
||||
from django.contrib.auth.password_validation import validate_password
|
||||
from .models import User, UserProfile, AnalysisSession
|
||||
from datetime import date
|
||||
|
||||
|
||||
class UserRegistrationSerializer(serializers.ModelSerializer):
|
||||
|
|
@ -121,11 +122,17 @@ class CreateAnalysisSessionSerializer(serializers.ModelSerializer):
|
|||
class Meta:
|
||||
model = AnalysisSession
|
||||
fields = (
|
||||
'ticker', 'analysis_date',
|
||||
'ticker',
|
||||
'analysts_selected', 'research_depth',
|
||||
'shallow_thinker', 'deep_thinker'
|
||||
)
|
||||
|
||||
# analysis_date는 create 시점에 자동 생성되므로 필드에서 제외
|
||||
|
||||
def create(self, validated_data):
|
||||
"""오늘 날짜를 추가하여 세션 생성"""
|
||||
validated_data['analysis_date'] = date.today()
|
||||
return super().create(validated_data)
|
||||
|
||||
def validate_analysts_selected(self, value):
|
||||
"""선택된 분석가들 검증"""
|
||||
if not isinstance(value, list) or len(value) == 0:
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import datetime
|
|||
from typing import Dict, List, Optional
|
||||
from django.conf import settings
|
||||
from channels.layers import get_channel_layer
|
||||
from channels.db import database_sync_to_async
|
||||
from asgiref.sync import async_to_sync
|
||||
|
||||
# CLI 모듈 import (경로 조정 필요)
|
||||
|
|
@ -25,41 +26,50 @@ class TradingAnalysisService:
|
|||
self.channel_layer = get_channel_layer()
|
||||
self.user_channel_group = f"user_{user.id}"
|
||||
|
||||
@database_sync_to_async
|
||||
def _update_session_status(self, status: str, error_message: Optional[str] = None, final_report: Optional[str] = None):
|
||||
"""세션 상태 업데이트 (비동기 안전)"""
|
||||
self.session.status = status
|
||||
now = datetime.datetime.now()
|
||||
if status == 'running':
|
||||
self.session.started_at = now
|
||||
else:
|
||||
self.session.completed_at = now
|
||||
|
||||
if error_message:
|
||||
self.session.error_message = error_message
|
||||
if final_report:
|
||||
self.session.final_report = final_report
|
||||
|
||||
self.session.save()
|
||||
|
||||
@database_sync_to_async
|
||||
def _get_user_profile_and_key(self):
|
||||
"""사용자 프로필 및 API 키 조회 (비동기 안전)"""
|
||||
profile = self.user.profile
|
||||
return profile.get_effective_openai_api_key()
|
||||
|
||||
async def run_analysis(self):
|
||||
"""분석 실행"""
|
||||
try:
|
||||
# 세션 상태 업데이트
|
||||
self.session.status = 'running'
|
||||
self.session.started_at = datetime.datetime.now()
|
||||
self.session.save()
|
||||
await self._update_session_status('running')
|
||||
|
||||
# WebSocket으로 시작 알림
|
||||
await self._send_websocket_message({
|
||||
'type': 'analysis_started',
|
||||
'session_id': self.session.id,
|
||||
'message': '분석을 시작합니다...'
|
||||
})
|
||||
|
||||
# 사용자 프로필에서 OpenAI API 키 가져오기
|
||||
profile = self.user.profile
|
||||
api_key = profile.get_effective_openai_api_key()
|
||||
api_key = await self._get_user_profile_and_key()
|
||||
|
||||
if not api_key:
|
||||
raise Exception("OpenAI API 키가 설정되지 않았습니다.")
|
||||
|
||||
# CLI 설정 준비
|
||||
config = self._prepare_analysis_config(api_key)
|
||||
|
||||
# 분석 실행
|
||||
result = await self._execute_trading_analysis(config)
|
||||
|
||||
# 결과 저장
|
||||
self.session.final_report = result
|
||||
self.session.status = 'completed'
|
||||
self.session.completed_at = datetime.datetime.now()
|
||||
self.session.save()
|
||||
await self._update_session_status('completed', final_report=result)
|
||||
|
||||
# WebSocket으로 완료 알림
|
||||
await self._send_websocket_message({
|
||||
'type': 'analysis_completed',
|
||||
'session_id': self.session.id,
|
||||
|
|
@ -70,17 +80,13 @@ class TradingAnalysisService:
|
|||
return result
|
||||
|
||||
except Exception as e:
|
||||
# 에러 처리
|
||||
self.session.status = 'failed'
|
||||
self.session.error_message = str(e)
|
||||
self.session.completed_at = datetime.datetime.now()
|
||||
self.session.save()
|
||||
error_msg = str(e)
|
||||
await self._update_session_status('failed', error_message=error_msg)
|
||||
|
||||
# WebSocket으로 에러 알림
|
||||
await self._send_websocket_message({
|
||||
'type': 'analysis_failed',
|
||||
'session_id': self.session.id,
|
||||
'message': f'분석 중 오류가 발생했습니다: {str(e)}'
|
||||
'message': f'분석 중 오류가 발생했습니다: {error_msg}'
|
||||
})
|
||||
|
||||
raise e
|
||||
|
|
@ -115,9 +121,28 @@ class TradingAnalysisService:
|
|||
'deep_thinking_model': config['deep_thinker'],
|
||||
})
|
||||
|
||||
# TradingAgentsGraph 초기화
|
||||
trading_graph = TradingAgentsGraph(analysis_config)
|
||||
|
||||
# 진행 상황 콜백 함수 수정
|
||||
async def progress_callback(message_type: str, content: str, agent: str = None, step: int = 0, total: int = 0):
|
||||
# 백엔드에서 진행률 계산
|
||||
progress_percent = int((step / total) * 99) if total > 0 else 0 # 100%는 완료 시에만
|
||||
await self._send_websocket_message({
|
||||
'type': 'analysis_progress',
|
||||
'session_id': self.session.id,
|
||||
'message_type': message_type,
|
||||
'content': content,
|
||||
'agent': agent,
|
||||
'progress': progress_percent,
|
||||
})
|
||||
|
||||
# TradingAgentsGraph 초기화 (더 상세한 예외 처리)
|
||||
try:
|
||||
trading_graph = TradingAgentsGraph(
|
||||
config=analysis_config,
|
||||
progress_callback=progress_callback
|
||||
)
|
||||
except Exception as e:
|
||||
raise Exception(f"TradingAgentsGraph 초기화 실패: {str(e)}")
|
||||
|
||||
# 분석 입력 데이터 준비
|
||||
input_data = {
|
||||
'ticker': config['ticker'],
|
||||
|
|
@ -126,50 +151,22 @@ class TradingAnalysisService:
|
|||
'research_depth': config['research_depth'],
|
||||
}
|
||||
|
||||
# 진행 상황 콜백 함수
|
||||
async def progress_callback(message_type: str, content: str, agent: str = None):
|
||||
await self._send_websocket_message({
|
||||
'type': 'analysis_progress',
|
||||
'session_id': self.session.id,
|
||||
'message_type': message_type,
|
||||
'content': content,
|
||||
'agent': agent
|
||||
})
|
||||
|
||||
# 분석 실행 (실제 CLI 로직 호출)
|
||||
# 여기서는 간단화된 버전으로 구현
|
||||
# 실제로는 trading_graph.invoke(input_data) 형태로 호출
|
||||
|
||||
# 분석 진행 상황 시뮬레이션
|
||||
analysis_steps = [
|
||||
("Market Analyst", "시장 데이터 분석 중..."),
|
||||
("Social Analyst", "소셜 센티멘트 분석 중..."),
|
||||
("News Analyst", "뉴스 분석 중..."),
|
||||
("Fundamentals Analyst", "기본 분석 중..."),
|
||||
("Research Manager", "연구 결과 종합 중..."),
|
||||
("Trader", "거래 전략 수립 중..."),
|
||||
("Portfolio Manager", "포트폴리오 최적화 중...")
|
||||
]
|
||||
|
||||
final_report_parts = []
|
||||
|
||||
for agent, message in analysis_steps:
|
||||
await progress_callback("agent_update", message, agent)
|
||||
|
||||
# 실제 분석 로직 호출 (여기서는 시뮬레이션)
|
||||
await asyncio.sleep(2) # 실제 분석 시간 시뮬레이션
|
||||
|
||||
# 각 단계별 결과 생성 (실제로는 trading_graph의 결과)
|
||||
step_result = f"## {agent} 분석 결과\n\n{config['ticker']} 종목에 대한 {agent.lower()} 분석을 완료했습니다.\n"
|
||||
final_report_parts.append(step_result)
|
||||
|
||||
# 최종 보고서 생성
|
||||
final_report = "\n\n".join(final_report_parts)
|
||||
|
||||
return final_report
|
||||
|
||||
try:
|
||||
# 여기서 trading_graph.invoke를 비동기로 실행해야 합니다.
|
||||
# 현재 trading_graph.invoke가 동기 함수라고 가정하고,
|
||||
# asyncio.to_thread를 사용해 비동기 컨텍스트에서 실행합니다.
|
||||
result = await asyncio.to_thread(
|
||||
trading_graph.invoke,
|
||||
input_data
|
||||
)
|
||||
return result['final_report'] # 결과 형식에 따라 조정 필요
|
||||
except Exception as e:
|
||||
raise Exception(f"trading_graph.invoke 실행 실패: {str(e)}")
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"분석 실행 중 오류 발생: {str(e)}")
|
||||
# 에러 메시지를 명확하게 다시 던짐
|
||||
raise Exception(f"분석 실행 중 오류: {str(e)}")
|
||||
|
||||
async def _send_websocket_message(self, message: Dict):
|
||||
"""WebSocket으로 메시지 전송"""
|
||||
|
|
@ -189,24 +186,15 @@ class TradingAnalysisManager:
|
|||
"""거래 분석 관리자"""
|
||||
|
||||
@staticmethod
|
||||
def create_analysis_session(user, analysis_data: Dict) -> AnalysisSession:
|
||||
"""분석 세션 생성"""
|
||||
session = AnalysisSession.objects.create(
|
||||
user=user,
|
||||
ticker=analysis_data['ticker'],
|
||||
analysis_date=analysis_data['analysis_date'],
|
||||
analysts_selected=analysis_data['analysts_selected'],
|
||||
research_depth=analysis_data['research_depth'],
|
||||
shallow_thinker=analysis_data['shallow_thinker'],
|
||||
deep_thinker=analysis_data['deep_thinker'],
|
||||
)
|
||||
return session
|
||||
|
||||
@database_sync_to_async
|
||||
def _get_session(user, session_id):
|
||||
return AnalysisSession.objects.get(id=session_id, user=user)
|
||||
|
||||
@staticmethod
|
||||
async def start_analysis(user, session_id: int):
|
||||
"""분석 시작"""
|
||||
try:
|
||||
session = AnalysisSession.objects.get(id=session_id, user=user)
|
||||
session = await TradingAnalysisManager._get_session(user, session_id)
|
||||
service = TradingAnalysisService(user, session)
|
||||
result = await service.run_analysis()
|
||||
return result
|
||||
|
|
@ -214,11 +202,13 @@ class TradingAnalysisManager:
|
|||
raise Exception("분석 세션을 찾을 수 없습니다.")
|
||||
|
||||
@staticmethod
|
||||
@database_sync_to_async
|
||||
def get_user_analysis_sessions(user) -> List[AnalysisSession]:
|
||||
"""사용자의 분석 세션 목록 조회"""
|
||||
return AnalysisSession.objects.filter(user=user).order_by('-created_at')
|
||||
return list(AnalysisSession.objects.filter(user=user).order_by('-created_at'))
|
||||
|
||||
@staticmethod
|
||||
@database_sync_to_async
|
||||
def cancel_analysis(user, session_id: int):
|
||||
"""분석 취소"""
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from rest_framework.views import APIView
|
|||
from django.shortcuts import get_object_or_404
|
||||
from asgiref.sync import sync_to_async
|
||||
import asyncio
|
||||
import threading
|
||||
from datetime import datetime
|
||||
|
||||
from apps.authentication.models import AnalysisSession
|
||||
|
|
@ -56,15 +57,15 @@ class StartAnalysisView(APIView):
|
|||
|
||||
def post(self, request):
|
||||
"""새로운 분석 시작"""
|
||||
print(f"request.data: {request.data}")
|
||||
serializer = CreateAnalysisSessionSerializer(data=request.data)
|
||||
|
||||
if serializer.is_valid():
|
||||
# 분석 세션 생성
|
||||
session = serializer.save(user=request.user)
|
||||
|
||||
# 백그라운드에서 분석 실행
|
||||
# 실제 환경에서는 Celery나 다른 task queue를 사용하는 것이 좋습니다
|
||||
asyncio.create_task(self._start_analysis_async(request.user, session.id))
|
||||
# 별도의 스레드에서 비동기 작업 실행
|
||||
thread = threading.Thread(target=self.run_async_task, args=(self._start_analysis_async(request.user, session.id),))
|
||||
thread.start()
|
||||
|
||||
return Response({
|
||||
'message': '분석이 시작되었습니다.',
|
||||
|
|
@ -74,6 +75,13 @@ class StartAnalysisView(APIView):
|
|||
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def run_async_task(self, coro):
|
||||
"""새 이벤트 루프에서 비동기 코루틴 실행"""
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
loop.run_until_complete(coro)
|
||||
loop.close()
|
||||
|
||||
async def _start_analysis_async(self, user, session_id):
|
||||
"""비동기 분석 실행"""
|
||||
try:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,32 +1,32 @@
|
|||
#!/bin/bash
|
||||
# #!/bin/bash
|
||||
|
||||
echo "🚀 Django 서버 시작 - 데이터베이스 초기화"
|
||||
# echo "🚀 Django 서버 시작 - 데이터베이스 초기화"
|
||||
|
||||
# Django 설정 모듈 환경 변수 설정
|
||||
export DJANGO_SETTINGS_MODULE=tradingagents_web.settings
|
||||
# # Django 설정 모듈 환경 변수 설정
|
||||
# export DJANGO_SETTINGS_MODULE=tradingagents_web.settings
|
||||
|
||||
# 1. 데이터베이스 초기화
|
||||
echo "🔄 데이터베이스 초기화 중..."
|
||||
docker exec -i tradingagents_mysql mysql -u root -ppassword -e "
|
||||
DROP DATABASE IF EXISTS tradingagents_db;
|
||||
CREATE DATABASE tradingagents_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
"
|
||||
# # 1. 데이터베이스 초기화
|
||||
# echo "🔄 데이터베이스 초기화 중..."
|
||||
# docker exec -i tradingagents_mysql mysql -u root -ppassword -e "
|
||||
# DROP DATABASE IF EXISTS tradingagents_db;
|
||||
# CREATE DATABASE tradingagents_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
# "
|
||||
|
||||
# 2. 마이그레이션
|
||||
echo "🔄 마이그레이션 중..."
|
||||
python manage.py makemigrations authentication
|
||||
python manage.py makemigrations
|
||||
python manage.py migrate
|
||||
# # 2. 마이그레이션
|
||||
# echo "🔄 마이그레이션 중..."
|
||||
# python manage.py makemigrations authentication
|
||||
# python manage.py makemigrations
|
||||
# python manage.py migrate
|
||||
|
||||
# 3. 관리자 계정 생성
|
||||
echo "🔄 관리자 계정 생성 중..."
|
||||
python manage.py shell -c "
|
||||
from django.contrib.auth import get_user_model;
|
||||
User = get_user_model();
|
||||
if not User.objects.filter(email='admin@example.com').exists():
|
||||
User.objects.create_superuser('admin@example.com', 'admin', 'admin123!');
|
||||
print('✅ 관리자: admin@example.com / admin123!');
|
||||
"
|
||||
# # 3. 관리자 계정 생성
|
||||
# echo "🔄 관리자 계정 생성 중..."
|
||||
# python manage.py shell -c "
|
||||
# from django.contrib.auth import get_user_model;
|
||||
# User = get_user_model();
|
||||
# if not User.objects.filter(email='admin@example.com').exists():
|
||||
# User.objects.create_superuser('admin@example.com', 'admin', 'admin123!');
|
||||
# print('✅ 관리자: admin@example.com / admin123!');
|
||||
# "
|
||||
|
||||
# 4. 서버 시작 (환경 변수와 함께)
|
||||
echo "🎉 서버 시작!"
|
||||
|
|
|
|||
|
|
@ -136,9 +136,10 @@ export const WebSocketProvider = ({ children }) => {
|
|||
...prev,
|
||||
[data.session_id]: {
|
||||
...prev[data.session_id],
|
||||
status: 'running',
|
||||
message: data.content,
|
||||
agent: data.agent,
|
||||
progress: prev[data.session_id]?.progress + 10 || 10
|
||||
progress: data.progress,
|
||||
}
|
||||
}));
|
||||
|
||||
|
|
@ -160,7 +161,7 @@ export const WebSocketProvider = ({ children }) => {
|
|||
status: 'completed',
|
||||
message: data.message,
|
||||
progress: 100,
|
||||
result: data.result
|
||||
result: data.result,
|
||||
}
|
||||
}));
|
||||
message.success(data.message);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// web/frontend/src/pages/Analysis/Analysis.js
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Card, Divider, Spin, Alert, Typography } from 'antd';
|
||||
import { Card, Divider, Spin, Alert, Typography, message } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
import api from '../../services/api';
|
||||
import { useWebSocket } from '../../contexts/WebSocketContext';
|
||||
|
|
@ -54,7 +54,7 @@ const Analysis = () => {
|
|||
if(currentSessionId) clearAnalysisProgress(currentSessionId);
|
||||
|
||||
try {
|
||||
const response = await api.post('/api/trading/start-analysis/', values);
|
||||
const response = await api.post('/api/trading/start/', values);
|
||||
const { session_id } = response.data;
|
||||
|
||||
setCurrentSessionId(session_id);
|
||||
|
|
@ -78,6 +78,20 @@ const Analysis = () => {
|
|||
clearMessages();
|
||||
};
|
||||
|
||||
// 분석 취소 핸들러
|
||||
const handleCancelAnalysis = async () => {
|
||||
if (!currentSessionId) return;
|
||||
|
||||
try {
|
||||
await api.post(`/api/trading/cancel/${currentSessionId}/`);
|
||||
message.success('분석이 성공적으로 중단되었습니다.');
|
||||
handleNewAnalysis(); // 상태를 초기화하고 폼으로 돌아감
|
||||
} catch (err) {
|
||||
const errorMessage = err.response?.data?.error || '분석 취소에 실패했습니다.';
|
||||
setError(errorMessage);
|
||||
}
|
||||
};
|
||||
|
||||
const renderContent = () => {
|
||||
if (analysisStatus === 'starting') {
|
||||
return <div style={{ textAlign: 'center', padding: '50px' }}><Spin size="large" tip="분석을 시작하고 있습니다..." /></div>;
|
||||
|
|
@ -92,6 +106,7 @@ const Analysis = () => {
|
|||
messages={messages.filter(m => m.sessionId === currentSessionId)}
|
||||
finalReport={finalReport}
|
||||
onNewAnalysis={handleNewAnalysis}
|
||||
onCancelAnalysis={handleCancelAnalysis}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
// web/frontend/src/pages/Analysis/components/AnalysisForm.js
|
||||
|
||||
import React from 'react';
|
||||
import { Form, Input, Button, Card, Select, Slider, Checkbox, Row, Col, Typography } from 'antd';
|
||||
import { FundOutlined, SendOutlined } from '@ant-design/icons';
|
||||
|
|
@ -18,6 +20,24 @@ const analystsOptions = [
|
|||
{ label: '재무 분석가 (Fundamentals)', value: 'fundamentals' },
|
||||
];
|
||||
|
||||
const shallowThinkerOptions = [
|
||||
{ value: 'gpt-4o-mini', label: 'GPT-4o-mini - 빠르고 효율적인 모델' },
|
||||
{ value: 'gpt-4.1-nano', label: 'GPT-4.1-nano - 초경량 모델' },
|
||||
{ value: 'gpt-4.1-mini', label: 'GPT-4.1-mini - 준수한 성능의 컴팩트 모델' },
|
||||
{ value: 'gpt-4o', label: 'GPT-4o - 표준 모델' },
|
||||
];
|
||||
|
||||
const deepThinkerOptions = [
|
||||
{ value: 'gpt-4.1-nano', label: 'GPT-4.1-nano - 초경량 모델' },
|
||||
{ value: 'gpt-4.1-mini', label: 'GPT-4.1-mini - 준수한 성능의 컴팩트 모델' },
|
||||
{ value: 'gpt-4o', label: 'GPT-4o - 표준 모델' },
|
||||
{ value: 'o4-mini', label: 'o4-mini - 특화된 소형 추론 모델' },
|
||||
{ value: 'o3-mini', label: 'o3-mini - 경량 고급 추론 모델' },
|
||||
{ value: 'o3', label: 'o3 - 전체 고급 추론 모델' },
|
||||
{ value: 'o1', label: 'o1 - 최상위 추론 및 문제 해결 모델' },
|
||||
];
|
||||
|
||||
|
||||
const AnalysisForm = ({ onStartAnalysis, loading }) => {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
|
|
@ -83,17 +103,22 @@ const AnalysisForm = ({ onStartAnalysis, loading }) => {
|
|||
<Col span={12}>
|
||||
<Form.Item name="shallow_thinker" label="Shallow Thinker 모델">
|
||||
<Select>
|
||||
<Option value="gpt-4o-mini">GPT-4o Mini</Option>
|
||||
<Option value="gpt-4o">GPT-4o</Option>
|
||||
<Option value="gpt-4-turbo">GPT-4 Turbo</Option>
|
||||
{shallowThinkerOptions.map(option => (
|
||||
<Option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="deep_thinker" label="Deep Thinker 모델">
|
||||
<Select>
|
||||
<Option value="gpt-4o">GPT-4o</Option>
|
||||
<Option value="gpt-4-turbo">GPT-4 Turbo</Option>
|
||||
{deepThinkerOptions.map(option => (
|
||||
<Option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
|
@ -116,4 +141,4 @@ const AnalysisForm = ({ onStartAnalysis, loading }) => {
|
|||
);
|
||||
};
|
||||
|
||||
export default AnalysisForm;
|
||||
export default AnalysisForm;
|
||||
Loading…
Reference in New Issue