Spaces:
Runtime error
Runtime error
Update main.py
Browse files
main.py
CHANGED
|
@@ -25,6 +25,7 @@ from datetime import datetime, timedelta
|
|
| 25 |
from googletrans import Translator
|
| 26 |
from starlette.concurrency import run_in_threadpool
|
| 27 |
import FinanceDataReader as fdr
|
|
|
|
| 28 |
|
| 29 |
app = FastAPI()
|
| 30 |
|
|
@@ -33,15 +34,16 @@ app = FastAPI()
|
|
| 33 |
logging.basicConfig(level=logging.INFO)
|
| 34 |
logger = logging.getLogger(__name__)
|
| 35 |
|
| 36 |
-
API_KEY = os.getenv("GEMINI_API_KEY")
|
|
|
|
| 37 |
|
| 38 |
if not API_KEY:
|
| 39 |
# API 키가 없으면 에러를 발생시키거나 경고
|
| 40 |
-
print("❌
|
| 41 |
|
| 42 |
else:
|
| 43 |
-
|
| 44 |
-
logger.info("✅
|
| 45 |
|
| 46 |
|
| 47 |
|
|
@@ -146,20 +148,44 @@ def parse_article_all(url: str) -> Dict[str, Any]:
|
|
| 146 |
# 회사명 추론 (Gemini)
|
| 147 |
# ---------------------------------------
|
| 148 |
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
|
| 156 |
-
"{text_for_company}"
|
| 157 |
-
"""
|
| 158 |
-
response = model.generate_content(prompt)
|
| 159 |
try:
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
|
| 164 |
# ---------------------------------------
|
| 165 |
# 1) 요약 단계
|
|
@@ -192,7 +218,7 @@ def step_company(inp: CompanyInput):
|
|
| 192 |
text = ", ".join(inp.keywords)
|
| 193 |
else:
|
| 194 |
raise HTTPException(status_code=400, detail="summary 또는 keywords 중 하나가 필요합니다.")
|
| 195 |
-
company =
|
| 196 |
return {"company": company}
|
| 197 |
|
| 198 |
# 4) 감정 단계
|
|
@@ -270,7 +296,7 @@ def parse_news(req: NewsRequest):
|
|
| 270 |
|
| 271 |
# 키워드/요약(기존 resultKeyword 사용)
|
| 272 |
rk = resultKeyword(meta["content"])
|
| 273 |
-
targetCompany =
|
| 274 |
|
| 275 |
# 감정(기존 로직)
|
| 276 |
s = analyze_sentiment(meta["content"])
|
|
@@ -360,22 +386,30 @@ async def load_initial_data():
|
|
| 360 |
logger.error(f"🚨 초기 데이터 로딩 오류: {e}", exc_info=True)
|
| 361 |
|
| 362 |
def get_stock_info(company_name: str) -> Dict[str, str] | None:
|
| 363 |
-
kr_match = krx_listings[krx_listings['Name'].str.contains(company_name, case=False, na=False)]
|
| 364 |
-
if not kr_match.empty:
|
| 365 |
-
s = kr_match.iloc[0]
|
| 366 |
-
return {"market": "KRX", "symbol": s['Code'], "name": s['Name']}
|
| 367 |
try:
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 376 |
except Exception as e:
|
| 377 |
-
logger.error(f"
|
|
|
|
| 378 |
return None
|
|
|
|
| 379 |
|
| 380 |
def fetch_stock_prices_sync(symbol: str, days: int = 365) -> Optional[pd.DataFrame]:
|
| 381 |
end_date = datetime.today()
|
|
|
|
| 25 |
from googletrans import Translator
|
| 26 |
from starlette.concurrency import run_in_threadpool
|
| 27 |
import FinanceDataReader as fdr
|
| 28 |
+
from groq import Groq
|
| 29 |
|
| 30 |
app = FastAPI()
|
| 31 |
|
|
|
|
| 34 |
logging.basicConfig(level=logging.INFO)
|
| 35 |
logger = logging.getLogger(__name__)
|
| 36 |
|
| 37 |
+
API_KEY = os.getenv("GEMINI_API_KEY")
|
| 38 |
+
|
| 39 |
|
| 40 |
if not API_KEY:
|
| 41 |
# API 키가 없으면 에러를 발생시키거나 경고
|
| 42 |
+
print("❌ Groq_API_KEY 환경 변수가 설정되지 않았습니다.")
|
| 43 |
|
| 44 |
else:
|
| 45 |
+
groq_client = Groq(api_key=API_KEY)
|
| 46 |
+
logger.info("✅ Groq API 설정 완료 (환경 변수 사용)")
|
| 47 |
|
| 48 |
|
| 49 |
|
|
|
|
| 148 |
# 회사명 추론 (Gemini)
|
| 149 |
# ---------------------------------------
|
| 150 |
|
| 151 |
+
# 3. 함수 이름 및 내용 변경 (gemini_use -> groq_use)
|
| 152 |
+
def groq_use(text_content: Any) -> str:
|
| 153 |
+
# 텍스트 추출 및 정제
|
| 154 |
+
if isinstance(text_content, dict):
|
| 155 |
+
text_for_ai = text_content.get('summary', '')
|
| 156 |
+
else:
|
| 157 |
+
text_for_ai = str(text_content)
|
| 158 |
+
|
| 159 |
+
# 프롬프트 구성 (불필요한 특수문자 제거 및 슬라이싱)
|
| 160 |
+
clean_text = text_for_ai[:500].replace('\n', ' ')
|
| 161 |
+
prompt = f"상장사 이름을 '회사명' 형식으로 하나만 답해줘: {clean_text}"
|
| 162 |
|
|
|
|
|
|
|
|
|
|
| 163 |
try:
|
| 164 |
+
chat_completion = groq_client.chat.completions.create(
|
| 165 |
+
messages=[{
|
| 166 |
+
"role": "user",
|
| 167 |
+
"content": prompt
|
| 168 |
+
}],
|
| 169 |
+
model="llama-3.3-70b-versatile",
|
| 170 |
+
)
|
| 171 |
+
return chat_completion.choices[0].message.content.strip()
|
| 172 |
+
|
| 173 |
+
except Exception as e:
|
| 174 |
+
# 에러 객체를 직접 출력하지 말고 repr()을 사용해 ASCII 충돌 방지
|
| 175 |
+
logger.error(f"🚨 Groq 호출 실패: {repr(e)}")
|
| 176 |
+
return "추출 실패"
|
| 177 |
+
|
| 178 |
+
def get_stock_info(company_info: str) -> Dict[str, str] | None:
|
| 179 |
+
try:
|
| 180 |
+
if krx_listings is not None:
|
| 181 |
+
# 포함 관계 확인
|
| 182 |
+
for _, row in krx_listings.iterrows():
|
| 183 |
+
if row['Name'] in company_info:
|
| 184 |
+
return {"market": "KRX", "symbol": row['Code'], "name": row['Name']}
|
| 185 |
+
except Exception as e:
|
| 186 |
+
logger.error(f"주식 검색 에러: {e}")
|
| 187 |
+
return None
|
| 188 |
+
|
| 189 |
|
| 190 |
# ---------------------------------------
|
| 191 |
# 1) 요약 단계
|
|
|
|
| 218 |
text = ", ".join(inp.keywords)
|
| 219 |
else:
|
| 220 |
raise HTTPException(status_code=400, detail="summary 또는 keywords 중 하나가 필요합니다.")
|
| 221 |
+
company = groq_use(text)
|
| 222 |
return {"company": company}
|
| 223 |
|
| 224 |
# 4) 감정 단계
|
|
|
|
| 296 |
|
| 297 |
# 키워드/요약(기존 resultKeyword 사용)
|
| 298 |
rk = resultKeyword(meta["content"])
|
| 299 |
+
targetCompany = groq_use(rk) # 텍스트 변환은 f-string 내부에서 처리됨
|
| 300 |
|
| 301 |
# 감정(기존 로직)
|
| 302 |
s = analyze_sentiment(meta["content"])
|
|
|
|
| 386 |
logger.error(f"🚨 초기 데이터 로딩 오류: {e}", exc_info=True)
|
| 387 |
|
| 388 |
def get_stock_info(company_name: str) -> Dict[str, str] | None:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 389 |
try:
|
| 390 |
+
# 1. 한국 주식(KRX) 검색
|
| 391 |
+
if krx_listings is not None:
|
| 392 |
+
kr_match = krx_listings[krx_listings['Name'].str.contains(company_name, case=False, na=False)]
|
| 393 |
+
if not kr_match.empty:
|
| 394 |
+
s = kr_match.iloc[0]
|
| 395 |
+
return {"market": "KRX", "symbol": s['Code'], "name": s['Name']}
|
| 396 |
+
|
| 397 |
+
# 2. 미국 주식 검색 (번역기 없이 이름으로 직접 검색)
|
| 398 |
+
if us_listings is not None:
|
| 399 |
+
# 영어 이름이 포함되어 있을 수 있으므로 대소문자 무시하고 검색
|
| 400 |
+
us_match = us_listings[
|
| 401 |
+
us_listings['Name'].str.contains(company_name, case=False, na=False) |
|
| 402 |
+
us_listings['Symbol'].str.fullmatch(company_name, case=False)
|
| 403 |
+
]
|
| 404 |
+
if not us_match.empty:
|
| 405 |
+
s = us_match.iloc[0]
|
| 406 |
+
return {"market": "US", "symbol": s['Symbol'], "name": s['Name']}
|
| 407 |
+
|
| 408 |
except Exception as e:
|
| 409 |
+
logger.error(f"주식 종목 검색 중 오류 발생: {e}")
|
| 410 |
+
|
| 411 |
return None
|
| 412 |
+
|
| 413 |
|
| 414 |
def fetch_stock_prices_sync(symbol: str, days: int = 365) -> Optional[pd.DataFrame]:
|
| 415 |
end_date = datetime.today()
|