[AI] SISC2-35 [FIX] AI 파트 통합 파이프라인#61
Conversation
- finder, transform, xai 모듈을 순차 실행하는 기본 파이프라인 구조를 구현합니다. - DB 스키마 및 import 경로 등 발견된 오류를 수정합니다. - 향후 실제 모듈 테스트를 위해 임시 데이터는 주석으로 남겨둡니다.
- 기존 selenium 기반 뉴스 수집 방식은 유지합니다. - requests와 beautifulsoup를 사용하는 새로운 수집 방식(news_processing_requests.py)을 추가하여, import 구문 변경만으로 수집 방식을 선택할 수 있도록 개선합니다.
Walkthrough파이프라인이 주간 발굴, 신호 변환, XAI 리포트의 모듈식 단계로 재구성되었고, 새로운 상위 오케스트레이터 run_pipeline이 단계들을 순차 실행하도록 추가되었습니다. OHLCV 데이터 조회 SQL과 뉴스 수집/요약 모듈, 요구 패키지 목록도 변경되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Pipeline
participant Finder
participant Transform
participant OHLCV
participant XAI
participant Config
Client->>Pipeline: run_pipeline()
Pipeline->>Config: Load configs/config.json (optional)
Config-->>Pipeline: config dict or {}
rect rgb(220, 235, 255)
Note over Pipeline,Finder: Phase 1 — Weekly Finder
Pipeline->>Finder: run_weekly_finder()
Finder-->>Pipeline: List[tickers]
end
rect rgb(220, 235, 255)
Note over Pipeline,Transform: Phase 2 — Signal Transform
Pipeline->>Transform: run_signal_transform(tickers, config)
loop per ticker
Transform->>OHLCV: fetch_ohlcv(ticker)
OHLCV-->>Transform: DataFrame
end
Transform-->>Pipeline: decision_log (DataFrame)
end
rect rgb(220, 235, 255)
Note over Pipeline,XAI: Phase 3 — XAI Report
Pipeline->>XAI: run_xai_report(decision_log)
XAI-->>Pipeline: List[reports] or error entries
end
Pipeline-->>Client: reports or None
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 분 주의할 파일/영역:
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
AI/transform/modules/main.py (1)
163-170: 컬럼 소문자화 이전에 타임스탬프 컬럼 탐지 → 대소문자 혼용 입력 시 실패 위험.현재
ts_col을 찾은 뒤에 컬럼을 소문자화합니다. 입력 DF에Date등 대문자 컬럼이 오면ts_col is None로 예외가 납니다. 소문자화 → 컬럼 탐지 순서로 교체하세요.- df = raw_data.copy() - ts_col = "ts_local" if "ts_local" in df.columns else ("date" if "date" in df.columns else None) - if ts_col is None: - raise ValueError("raw_data에 'ts_local' 또는 'date' 컬럼이 필요합니다.") - df[ts_col] = pd.to_datetime(df[ts_col]) - df = df.rename(columns={c: c.lower() for c in df.columns}) + df = raw_data.copy() + # 1) 우선 컬럼명을 소문자화 + df = df.rename(columns={c: c.lower() for c in df.columns}) + # 2) 그 후 타임스탬프 컬럼 탐지 + ts_col = "ts_local" if "ts_local" in df.columns else ("date" if "date" in df.columns else None) + if ts_col is None: + raise ValueError("raw_data에 'ts_local' 또는 'date' 컬럼이 필요합니다.") + df[ts_col] = pd.to_datetime(df[ts_col])
🧹 Nitpick comments (10)
AI/libs/utils/data/fetch_ohlcv.py (2)
17-23: interval 인자/설명 불일치(더 이상 사용 안 함).SQL에서 인터벌 필터가 제거되었는데 함수 시그니처와 Docstring에는 여전히 남아 있습니다. 혼선을 줄이기 위해 Docstring에 “deprecated/미사용” 표기 또는 인자 제거를 제안합니다(제거 시 상위 호출부 영향 확인 필요).
- interval: str = "1d", + interval: str = "1d", # deprecated, unused @@ - interval (str): 데이터 간격 ('1d' 등) + interval (str): (deprecated) 현재 무시됩니다.Also applies to: 27-36
37-51: DB 커넥션 누수 방지 및 예외 안전성 향상(컨텍스트 매니저).
with문을 사용해 커넥션을 자동 종료하면 예외 시에도 안전합니다.- conn = get_db_conn(config) - - query = """ + query = """ SELECT date, open, high, low, close, volume FROM public.price_data WHERE ticker = %s AND date BETWEEN %s AND %s ORDER BY date; """ - # 파라미터 바인딩 (%s) 사용 → SQL injection 방지 - df = pd.read_sql(query, conn, params=(ticker, start, end)) - - conn.close() - return df + # 파라미터 바인딩 (%s) 사용 → SQL injection 방지 + with get_db_conn(config) as conn: + df = pd.read_sql(query, conn, params=(ticker, start, end)) + return dfAI/libs/core/pipeline.py (3)
9-11: sys.path 주입 중복 방지.동일 경로가 여러 번 추가되지 않도록 가드하세요.
-project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -sys.path.append(project_root) +project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +if project_root not in sys.path: + sys.path.append(project_root)
24-28: 불필요한 f-string 제거(F541).플레이스홀더 없는 f-string은 일반 문자열로 변경하세요. Ruff F541.
- print("--- [PIPELINE-STEP 1] Finder 모듈 실행 시작 ---") + print("--- [PIPELINE-STEP 1] Finder 모듈 실행 시작 ---") @@ - print(f"--- [PIPELINE-STEP 1] Finder 모듈 실행 완료 ---") + print("--- [PIPELINE-STEP 1] Finder 모듈 실행 완료 ---")
86-114: XAI 단계: f-string(F541) 및 광범위 예외(BLE001).
- 플레이스홀더 없는 f-string을 일반 문자열로 교체하세요. Ruff F541.
- 광범위
except Exception은 가능한 한 구체화가 바람직합니다. 당장 축소가 어렵다면 TODO로 명시해 추후run_xai가 던질 수 있는 예외군으로 좁히는 것을 권장합니다.- print("--- [PIPELINE-STEP 3] XAI 리포트 생성 시작 ---") + print("--- [PIPELINE-STEP 3] XAI 리포트 생성 시작 ---") @@ - print(f"--- [PIPELINE-STEP 3] XAI 리포트 생성 완료 ---") + print("--- [PIPELINE-STEP 3] XAI 리포트 생성 완료 ---")추가로,
except Exception as e:구문 위에 다음과 같은 주석을 남겨 향후 축소 계획을 표시하는 것을 권장합니다.# TODO: run_xai에서 발생 가능한 예외군으로 예외 범위 축소(ValueError, TimeoutError 등)AI/libs/utils/news_processing_requests.py (4)
63-67: wdate 선택자 None 가드 추가.요약 영역에
span.wdate가 없는 경우AttributeError가 발생할 수 있습니다. 안전 가드를 추가하세요.- if i < len(summaries): - time_list.append(summaries[i].find('span', class_='wdate').text.strip()) + if i < len(summaries): + w = summaries[i].find('span', class_='wdate') + time_list.append(w.text.strip() if w else '') else: time_list.append('')
154-155: 불리언 비교 스타일(E712).
== False대신notnull()/isnull()을 사용하세요. 현재 표현식은 가독성과 정확성이 떨어집니다.- valid_df = href_df[href_df['content'].isnull()==False].reset_index(drop=True) + valid_df = href_df[href_df['content'].notnull()].reset_index(drop=True)
17-29: 타입 힌트/사용 불일치: target_date는 문자열(YYYYMMDD)로 사용됩니다.함수는
datetime을 받도록 선언했지만 실제로는 문자열을 전달/사용합니다. 타입 일치로 혼란을 줄이세요.-def _news_href_crawl(target_date: datetime) -> pd.DataFrame: +def _news_href_crawl(target_date: str) -> pd.DataFrame: @@ - "date": target_date + "date": target_date
143-161: 타입 힌트/사용 불일치:_collect_and_summarize_news의 target_date.여기도 동일하게 문자열을 사용합니다. 시그니처/Docstring을 정합화하세요.
-def _collect_and_summarize_news(target_date: datetime, llm_client) -> pd.DataFrame: +def _collect_and_summarize_news(target_date: str, llm_client) -> pd.DataFrame: @@ - 지정된 날짜의 해외증시 뉴스를 수집하고 LLM으로 요약하는 함수 + 지정된 날짜(YYYYMMDD)의 해외증시 뉴스를 수집하고 LLM으로 요약하는 함수AI/transform/modules/main.py (1)
145-154: 미사용 매개변수pred_h,interval정리 권장 확인됨.코드 검증 결과, 두 매개변수 모두 함수 내에서 실제로 사용되지 않습니다:
pred_h: 호출부(AI/libs/core/pipeline.py:54)에서pred_h=1로 전달되지만, 함수 본체에서 참조 없음interval: 기본값"1d"로 정의되어 있으나 함수 로직 어디서도 사용되지 않음즉시 제거하거나, 향후 사용을 대비한 의도 표시로
_ = (pred_h, interval)추가를 권장합니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
AI/libs/core/pipeline.py(1 hunks)AI/libs/utils/data/fetch_ohlcv.py(1 hunks)AI/libs/utils/news_processing_requests.py(1 hunks)AI/requirements.txt(1 hunks)AI/transform/modules/main.py(1 hunks)AI/xai/requirements.txt(0 hunks)
💤 Files with no reviewable changes (1)
- AI/xai/requirements.txt
🧰 Additional context used
🧬 Code graph analysis (1)
AI/libs/core/pipeline.py (4)
AI/finder/main.py (1)
run_finder(14-44)AI/transform/modules/main.py (1)
run_transform(145-272)AI/libs/utils/data/fetch_ohlcv.py (1)
fetch_ohlcv(17-51)AI/xai/run_xai.py (1)
run_xai(29-33)
🪛 Ruff (0.14.1)
AI/libs/utils/news_processing_requests.py
37-37: Probable use of requests call without timeout
(S113)
79-79: Probable use of requests call without timeout
(S113)
154-154: Avoid equality comparisons to False; use not href_df['content'].isnull(): for false checks
Replace with not href_df['content'].isnull()
(E712)
183-183: Do not catch blind exception: Exception
(BLE001)
211-211: Do not catch blind exception: Exception
(BLE001)
AI/libs/core/pipeline.py
27-27: f-string without any placeholders
Remove extraneous f prefix
(F541)
79-79: f-string without any placeholders
Remove extraneous f prefix
(F541)
89-89: Avoid specifying long messages outside the exception class
(TRY003)
109-109: Do not catch blind exception: Exception
(BLE001)
113-113: f-string without any placeholders
Remove extraneous f prefix
(F541)
| def run_signal_transform(tickers: List[str], config: Dict) -> pd.DataFrame: | ||
| """ | ||
| 종목 리스트를 받아 Transform 모듈을 실행하고, 신호(결정 로그)를 반환합니다. | ||
| """ | ||
| print("--- [PIPELINE-STEP 2] Transform 모듈 실행 시작 ---") | ||
|
|
||
| # --- 실제 Transform 모듈 호출 --- | ||
| end_date = datetime.now() | ||
| start_date = end_date - timedelta(days=600) | ||
| all_ohlcv_df = [] | ||
| for ticker in tickers: | ||
| ohlcv_df = fetch_ohlcv( | ||
| ticker=ticker, | ||
| start=start_date.strftime('%Y-%m-%d'), | ||
| end=end_date.strftime('%Y-%m-%d'), | ||
| config=config | ||
| ) | ||
| ohlcv_df['ticker'] = ticker | ||
| all_ohlcv_df.append(ohlcv_df) | ||
| if not all_ohlcv_df: | ||
| print("OHLCV 데이터를 가져오지 못했습니다.") | ||
| return pd.DataFrame() | ||
| raw_data = pd.concat(all_ohlcv_df, ignore_index=True) | ||
| finder_df = pd.DataFrame(tickers, columns=['ticker']) | ||
| transform_result = run_transform( | ||
| finder_df=finder_df, | ||
| seq_len=60, | ||
| pred_h=1, | ||
| raw_data=raw_data, | ||
| config=config | ||
| ) | ||
| logs_df: pd.DataFrame = tr["logs"] # (종목,날짜,매매여부,가격,비중,피쳐...,확률...) | ||
|
|
||
| # Transform 로그 저장 (Parquet) | ||
| out_dir = os.path.join(config["storage"]["out_dir"], "transform") | ||
| os.makedirs(out_dir, exist_ok=True) | ||
| log_path = os.path.join(out_dir, f"logs_{run_date}.parquet") | ||
| logs_df.to_parquet(log_path, index=False) | ||
|
|
||
| # XAI 리포트 생성 + 저장 (JSON per ticker) | ||
| xai_out_dir = os.path.join(config["storage"]["out_dir"], "xai", run_date) | ||
| os.makedirs(xai_out_dir, exist_ok=True) | ||
|
|
||
| xai_reports = run_xai(logs_df) | ||
| for ticker, report in xai_reports.items(): | ||
| with open(os.path.join(xai_out_dir, f"{ticker}.json"), "w", encoding="utf-8") as f: | ||
| json.dump(report, f, ensure_ascii=False, indent=2) | ||
|
|
||
| _log(f"[DAILY] Transform 로그 + XAI 저장 완료 ({run_date})") | ||
|
|
||
| # ============================================== | ||
| # 메인 파이프라인 | ||
| # ============================================== | ||
| def run_pipeline(config: dict) -> bool: | ||
| run_date = dt.datetime.now(dt.timezone(dt.timedelta(hours=9))).strftime("%Y-%m-%d") | ||
|
|
||
| logs_df = transform_result.get("logs", pd.DataFrame()) |
There was a problem hiding this comment.
DB 설정 누락 시 즉시 크래시(핵심 경로 가드 필요).
config=None인 상태에서 fetch_ohlcv 호출 시 TypeError: 'NoneType' is not subscriptable로 파이프라인이 중단됩니다. 설정 부재 시 조기 반환하도록 가드하세요. 이는 데모/테스트 실행 안정성에 중요합니다.
-def run_signal_transform(tickers: List[str], config: Dict) -> pd.DataFrame:
+def run_signal_transform(tickers: List[str], config: Dict | None) -> pd.DataFrame:
@@
- print("--- [PIPELINE-STEP 2] Transform 모듈 실행 시작 ---")
+ print("--- [PIPELINE-STEP 2] Transform 모듈 실행 시작 ---")
+ if not config or "db" not in config:
+ print("[WARN] DB 설정이 없어 Transform 단계를 건너뜁니다.")
+ return pd.DataFrame()
@@
- for ticker in tickers:
- ohlcv_df = fetch_ohlcv(
- ticker=ticker,
- start=start_date.strftime('%Y-%m-%d'),
- end=end_date.strftime('%Y-%m-%d'),
- config=config
- )
- ohlcv_df['ticker'] = ticker
- all_ohlcv_df.append(ohlcv_df)
+ for ticker in tickers:
+ try:
+ ohlcv_df = fetch_ohlcv(
+ ticker=ticker,
+ start=start_date.strftime('%Y-%m-%d'),
+ end=end_date.strftime('%Y-%m-%d'),
+ config=config,
+ )
+ if ohlcv_df.empty:
+ continue
+ ohlcv_df['ticker'] = ticker
+ all_ohlcv_df.append(ohlcv_df)
+ except Exception as e:
+ print(f"[WARN] {ticker} OHLCV 조회 실패: {e}")
+ continue📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def run_signal_transform(tickers: List[str], config: Dict) -> pd.DataFrame: | |
| """ | |
| 종목 리스트를 받아 Transform 모듈을 실행하고, 신호(결정 로그)를 반환합니다. | |
| """ | |
| print("--- [PIPELINE-STEP 2] Transform 모듈 실행 시작 ---") | |
| # --- 실제 Transform 모듈 호출 --- | |
| end_date = datetime.now() | |
| start_date = end_date - timedelta(days=600) | |
| all_ohlcv_df = [] | |
| for ticker in tickers: | |
| ohlcv_df = fetch_ohlcv( | |
| ticker=ticker, | |
| start=start_date.strftime('%Y-%m-%d'), | |
| end=end_date.strftime('%Y-%m-%d'), | |
| config=config | |
| ) | |
| ohlcv_df['ticker'] = ticker | |
| all_ohlcv_df.append(ohlcv_df) | |
| if not all_ohlcv_df: | |
| print("OHLCV 데이터를 가져오지 못했습니다.") | |
| return pd.DataFrame() | |
| raw_data = pd.concat(all_ohlcv_df, ignore_index=True) | |
| finder_df = pd.DataFrame(tickers, columns=['ticker']) | |
| transform_result = run_transform( | |
| finder_df=finder_df, | |
| seq_len=60, | |
| pred_h=1, | |
| raw_data=raw_data, | |
| config=config | |
| ) | |
| logs_df: pd.DataFrame = tr["logs"] # (종목,날짜,매매여부,가격,비중,피쳐...,확률...) | |
| # Transform 로그 저장 (Parquet) | |
| out_dir = os.path.join(config["storage"]["out_dir"], "transform") | |
| os.makedirs(out_dir, exist_ok=True) | |
| log_path = os.path.join(out_dir, f"logs_{run_date}.parquet") | |
| logs_df.to_parquet(log_path, index=False) | |
| # XAI 리포트 생성 + 저장 (JSON per ticker) | |
| xai_out_dir = os.path.join(config["storage"]["out_dir"], "xai", run_date) | |
| os.makedirs(xai_out_dir, exist_ok=True) | |
| xai_reports = run_xai(logs_df) | |
| for ticker, report in xai_reports.items(): | |
| with open(os.path.join(xai_out_dir, f"{ticker}.json"), "w", encoding="utf-8") as f: | |
| json.dump(report, f, ensure_ascii=False, indent=2) | |
| _log(f"[DAILY] Transform 로그 + XAI 저장 완료 ({run_date})") | |
| # ============================================== | |
| # 메인 파이프라인 | |
| # ============================================== | |
| def run_pipeline(config: dict) -> bool: | |
| run_date = dt.datetime.now(dt.timezone(dt.timedelta(hours=9))).strftime("%Y-%m-%d") | |
| logs_df = transform_result.get("logs", pd.DataFrame()) | |
| def run_signal_transform(tickers: List[str], config: Dict | None) -> pd.DataFrame: | |
| """ | |
| 종목 리스트를 받아 Transform 모듈을 실행하고, 신호(결정 로그)를 반환합니다. | |
| """ | |
| print("--- [PIPELINE-STEP 2] Transform 모듈 실행 시작 ---") | |
| if not config or "db" not in config: | |
| print("[WARN] DB 설정이 없어 Transform 단계를 건너뜁니다.") | |
| return pd.DataFrame() | |
| # --- 실제 Transform 모듈 호출 --- | |
| end_date = datetime.now() | |
| start_date = end_date - timedelta(days=600) | |
| all_ohlcv_df = [] | |
| for ticker in tickers: | |
| try: | |
| ohlcv_df = fetch_ohlcv( | |
| ticker=ticker, | |
| start=start_date.strftime('%Y-%m-%d'), | |
| end=end_date.strftime('%Y-%m-%d'), | |
| config=config, | |
| ) | |
| if ohlcv_df.empty: | |
| continue | |
| ohlcv_df['ticker'] = ticker | |
| all_ohlcv_df.append(ohlcv_df) | |
| except Exception as e: | |
| print(f"[WARN] {ticker} OHLCV 조회 실패: {e}") | |
| continue | |
| if not all_ohlcv_df: | |
| print("OHLCV 데이터를 가져오지 못했습니다.") | |
| return pd.DataFrame() | |
| raw_data = pd.concat(all_ohlcv_df, ignore_index=True) | |
| finder_df = pd.DataFrame(tickers, columns=['ticker']) | |
| transform_result = run_transform( | |
| finder_df=finder_df, | |
| seq_len=60, | |
| pred_h=1, | |
| raw_data=raw_data, | |
| config=config | |
| ) | |
| logs_df = transform_result.get("logs", pd.DataFrame()) |
🤖 Prompt for AI Agents
In AI/libs/core/pipeline.py around lines 30 to 61, the function
run_signal_transform assumes config is present and passes it to fetch_ohlcv,
causing a TypeError when config is None; add an early guard at the top of the
function that checks for a missing or invalid config (e.g., if config is None or
required keys like DB/settings are absent), log/print a clear message about the
missing configuration, and immediately return an empty pd.DataFrame to avoid
calling fetch_ohlcv with None; this prevents crashes for demo/test runs and
ensures the pipeline fails gracefully.
| response = requests.get(base_url, params=params, headers={'User-Agent': 'Mozilla/5.0'}) | ||
| response.raise_for_status() | ||
| soup = BeautifulSoup(response.content, 'html.parser') | ||
|
|
There was a problem hiding this comment.
네트워크 호출 타임아웃 누락(S113).
외부 호출에 타임아웃이 없어 무기한 블로킹 위험이 있습니다. 합리적인 타임아웃을 지정하세요.
- response = requests.get(base_url, params=params, headers={'User-Agent': 'Mozilla/5.0'})
+ response = requests.get(
+ base_url,
+ params=params,
+ headers={'User-Agent': 'Mozilla/5.0'},
+ timeout=10,
+ )📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| response = requests.get(base_url, params=params, headers={'User-Agent': 'Mozilla/5.0'}) | |
| response.raise_for_status() | |
| soup = BeautifulSoup(response.content, 'html.parser') | |
| response = requests.get( | |
| base_url, | |
| params=params, | |
| headers={'User-Agent': 'Mozilla/5.0'}, | |
| timeout=10, | |
| ) | |
| response.raise_for_status() | |
| soup = BeautifulSoup(response.content, 'html.parser') |
🧰 Tools
🪛 Ruff (0.14.1)
37-37: Probable use of requests call without timeout
(S113)
🤖 Prompt for AI Agents
In AI/libs/utils/news_processing_requests.py around lines 37-40, the
requests.get call lacks a timeout which can block indefinitely; add a reasonable
timeout to the call (for example timeout=10 or a connect/read tuple like
timeout=(5,10)) and update the call to requests.get(base_url, params=params,
headers=..., timeout=...) ; also wrap the network call in a try/except to catch
requests.Timeout and requests.RequestException and handle/log or re-raise
appropriately so callers won't hang indefinitely.
| response = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'}) | ||
| response.raise_for_status() | ||
| soup = BeautifulSoup(response.content, 'html.parser') | ||
|
|
There was a problem hiding this comment.
네트워크 호출 타임아웃 누락(S113).
본문 수집 요청에도 동일하게 타임아웃을 지정하세요.
- response = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'})
+ response = requests.get(
+ url,
+ headers={'User-Agent': 'Mozilla/5.0'},
+ timeout=10,
+ )📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| response = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'}) | |
| response.raise_for_status() | |
| soup = BeautifulSoup(response.content, 'html.parser') | |
| response = requests.get( | |
| url, | |
| headers={'User-Agent': 'Mozilla/5.0'}, | |
| timeout=10, | |
| ) | |
| response.raise_for_status() | |
| soup = BeautifulSoup(response.content, 'html.parser') |
🧰 Tools
🪛 Ruff (0.14.1)
79-79: Probable use of requests call without timeout
(S113)
🤖 Prompt for AI Agents
In AI/libs/utils/news_processing_requests.py around lines 79 to 82, the
requests.get call lacks a timeout which can hang the process; update the call to
include a timeout parameter (e.g., timeout=10 or use a configured TIMEOUT
constant) so requests.get(url, headers=..., timeout=TIMEOUT). Ensure the chosen
timeout value is reasonable for your environment and keep
response.raise_for_status() and parsing logic unchanged.
| pandas | ||
| psycopg2-binary | ||
| langchain-community | ||
| tqdm | ||
| selenium | ||
| webdriver-manager | ||
| numpy | ||
| scikit-learn | ||
| tensorflow | ||
| yfinance | ||
| groq |
There was a problem hiding this comment.
🧩 Analysis chain
뉴스 수집 모듈 필수 의존성 누락(런타임 실패 가능).
AI/libs/utils/news_processing_requests.py에서 requests, bs4(beautifulsoup4)를 사용하지만 본 파일에 미기재입니다. 현재 상태로는 파이프라인 1단계(Finder) 실행 시 ImportError/ModuleNotFoundError가 납니다. 최소한 아래 패키지를 추가하세요. 또한 버전 고정은 추후 constraints.txt로 관리 권장.
pandas
psycopg2-binary
langchain-community
tqdm
selenium
webdriver-manager
numpy
scikit-learn
tensorflow
yfinance
groq
+requests
+beautifulsoup4
+# HTML parser 가속/안정성 개선용 (선택)
+lxml다음 스크립트로 의존성-사용처 미스매치 여부를 점검해 주세요.
🏁 Script executed:
#!/bin/bash
echo "[imports needing deps]"
rg -nP --type=python -C1 '(^\s*import requests\b)|(^\s*from\s+bs4\s+import\s+BeautifulSoup\b)' AI | sed 's/^/ /'
echo
echo "[requirements]"
nl -ba AI/requirements.txt
echo
echo "[check presence]"
grep -Eq '^\s*requests(\b|[=<>~])' AI/requirements.txt || echo "MISSING: requests"
grep -Eq '^\s*beautifulsoup4(\b|[=<>~])' AI/requirements.txt || echo "MISSING: beautifulsoup4"Length of output: 170
🏁 Script executed:
#!/bin/bash
# Find the file and check its imports
echo "=== Checking for news_processing_requests.py ==="
find . -name "news_processing_requests.py" -type f 2>/dev/null | head -5
echo
echo "=== Content of AI/libs/utils/news_processing_requests.py ==="
if [ -f "AI/libs/utils/news_processing_requests.py" ]; then
head -50 AI/libs/utils/news_processing_requests.py
else
echo "File not found"
fi
echo
echo "=== Current AI/requirements.txt ==="
cat AI/requirements.txt
echo
echo "=== Verification: Search for requests/bs4 imports ==="
grep -n "import requests\|from bs4" AI/libs/utils/news_processing_requests.py 2>/dev/null || echo "No matches found"Length of output: 334
---
🏁 Script executed:
```shell
#!/bin/bash
# Use corrected paths based on find output
echo "=== Content of news_processing_requests.py ==="
cat ./git/AI/libs/utils/news_processing_requests.py
echo
echo "=== Current AI/requirements.txt ==="
cat ./git/AI/requirements.txt
echo
echo "=== Search for requests/bs4 imports ==="
grep -n "import requests\|from bs4" ./git/AI/libs/utils/news_processing_requests.py
Length of output: 343
뉴스 수집 모듈 필수 의존성 누락 - 즉시 추가 필요.
AI/requirements.txt에서 requests와 beautifulsoup4가 누락되어 있습니다. 런타임에 ImportError/ModuleNotFoundError가 발생합니다. 다음 패키지를 추가하세요.
pandas
psycopg2-binary
langchain-community
tqdm
selenium
webdriver-manager
numpy
scikit-learn
tensorflow
yfinance
groq
+requests
+beautifulsoup4
+lxmlrequests: HTTP 요청 라이브러리beautifulsoup4: HTML 파싱 라이브러리 (bs4 모듈 제공)lxml: 선택사항, HTML 파서 성능 개선용
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| pandas | |
| psycopg2-binary | |
| langchain-community | |
| tqdm | |
| selenium | |
| webdriver-manager | |
| numpy | |
| scikit-learn | |
| tensorflow | |
| yfinance | |
| groq | |
| pandas | |
| psycopg2-binary | |
| langchain-community | |
| tqdm | |
| selenium | |
| webdriver-manager | |
| numpy | |
| scikit-learn | |
| tensorflow | |
| yfinance | |
| groq | |
| requests | |
| beautifulsoup4 | |
| lxml |
🤖 Prompt for AI Agents
In AI/requirements.txt lines 1-11, the news-collection module is missing
HTTP/HTML parsing deps; add requests and beautifulsoup4 to the requirements (and
optionally lxml for improved parsing) so imports don't raise
ImportError/ModuleNotFoundError at runtime; append these packages to the file
(or insert in alphabetical order) and run your dependency installation/test to
confirm the news scraper imports successfully.
requests beautifulsoup4 누락 되어 있어서 수정
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
AI/requirements.txt (2)
1-13: 버전 고정 없음 — 재현성 및 안정성 저하 가능.모든 의존성이 버전 지정자 없이 나열되어 있어, 패키지 설치 시점에 따라 다른 버전이 설치될 수 있습니다. 특히 주요 버전 업그레이드 시 예기치 않은 동작 변경이나 호환성 문제가 발생할 수 있습니다.
추천: 각 패키지에 정확한 버전을 고정하거나, 향후
constraints.txt를 도입하여 명시적으로 관리하세요.-pandas -psycopg2-binary -langchain-community +pandas==2.1.4 +psycopg2-binary==2.9.9 +langchain-community==0.2.5 tqdm selenium webdriver-manager numpy scikit-learn tensorflow yfinance groq requests beautifulsoup4
12-13: 선택사항: HTML 파싱 성능 개선용lxml추가 검토.이전 리뷰에서 제안한
lxml은 BeautifulSoup의 HTML 파서로 사용되어 파싱 속도 및 안정성을 향상시킵니다. 뉴스 크롤링 성능이 중요하다면 추가를 고려하세요.requests beautifulsoup4 +lxml
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
AI/requirements.txt(1 hunks)
🔇 Additional comments (1)
AI/requirements.txt (1)
12-13: ✅ 이전 리뷰의 필수 의존성 누락 문제 해결됨.이전 리뷰에서 지적된
requests와beautifulsoup4가 이제 추가되었습니다. 뉴스 수집 모듈의 런타임 ImportError 문제가 해결되었습니다.
|
고생했습니당 |
AI 모듈의 finder, transform, xai를 연결하는 통합 파이프라인을 구축하고 뉴스 수집 방식을 개선함.
테스트 방법
1 python libs/core/pipeline.py
향후 작업
Summary by CodeRabbit
새로운 기능
리팩토링
버그 수정
Chores