Files
ai-trade/modules/services/ollama.py
gahusb 4e77a1acf1 LSTM v3 멀티피처, KIS OHLCV 배치, 동적 전략 강화
- deep_learning.py: INPUT_SIZE=7 (close/open/high/low/volume/rsi/macd),
  feature_scaler/target_scaler 분리, ModelRegistry LRU 종목별 격리 (v3 체크포인트)
- kis.py: get_daily_ohlcv() OHLCV 전체 반환, KISAsyncClient 비동기 배치 조회 추가,
  order() 지정가/조건부 주문 지원
- strategy/process.py: ATR/ADX 기반 동적 손절익절, 트레일링 스탑, 포지션 사이징 강화
- config.py: OLLAMA_NUM_THREAD=8 (9800X3D 최적화), LSTM_COOLDOWN/FAST_EPOCHS 환경변수화
- macro.py: 거시경제 지표 계산 개선
- ollama.py: VRAM 여유량 기반 선택적 언로드
- monitor.py: CPU 서킷 브레이커 연속 횟수 조건 추가
- ipc.py: IPC_STALENESS 600초로 확대
- news.py: 비동기 뉴스 수집 개선
- telegram.py, runner.py: 안정성 개선

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 23:08:33 +09:00

137 lines
5.0 KiB
Python

import requests
import json
import psutil
try:
import pynvml
except ImportError:
pynvml = None
from modules.config import Config
class OllamaManager:
"""
Ollama API 세션 관리 및 메모리 누수 방지 래퍼
- GPU VRAM 사용량 모니터링
- keep_alive 파라미터를 통한 메모리 관리
"""
def __init__(self, model_name=None, base_url=None):
self.model_name = model_name or Config.OLLAMA_MODEL
self.base_url = base_url or Config.OLLAMA_API_URL
self.generate_url = f"{self.base_url}/api/generate"
self.gpu_available = False
try:
if pynvml:
pynvml.nvmlInit()
self.handle = pynvml.nvmlDeviceGetHandleByIndex(0) # 0번 GPU (5070 Ti)
self.gpu_available = True
print("✅ [OllamaManager] NVIDIA GPU Monitoring On")
else:
print("⚠️ [OllamaManager] 'nvidia-ml-py' not installed. GPU monitoring disabled.")
except Exception as e:
print(f"⚠️ [OllamaManager] GPU Init Failed: {e}")
def check_vram(self):
"""현재 GPU VRAM 사용량(GB) 반환"""
if not self.gpu_available:
return 0.0
try:
info = pynvml.nvmlDeviceGetMemoryInfo(self.handle)
used_gb = info.used / 1024**3
return used_gb
except Exception:
return 0.0
def get_gpu_status(self):
"""GPU 종합 상태 반환 (온도, 메모리, 사용률, 이름)"""
if not self.gpu_available:
return {"name": "N/A", "temp": 0, "vram_used": 0, "vram_total": 0, "load": 0}
try:
# GPU 이름
name = pynvml.nvmlDeviceGetName(self.handle)
if isinstance(name, bytes):
name = name.decode('utf-8')
# 온도
temp = pynvml.nvmlDeviceGetTemperature(self.handle, pynvml.NVML_TEMPERATURE_GPU)
# 메모리
mem_info = pynvml.nvmlDeviceGetMemoryInfo(self.handle)
vram_used = mem_info.used / 1024**3
vram_total = mem_info.total / 1024**3
# 사용률
util = pynvml.nvmlDeviceGetUtilizationRates(self.handle)
load = util.gpu
return {
"name": name,
"temp": temp,
"vram_used": round(vram_used, 1),
"vram_total": round(vram_total, 1),
"load": load
}
except Exception as e:
print(f"⚠️ GPU Status Check Failed: {e}")
return {"name": "N/A", "temp": 0, "vram_used": 0, "vram_total": 0, "load": 0}
def is_training_active(self):
"""LSTM 학습 중인지 확인 (GPU 메모리 충돌 방지)"""
try:
import torch
if torch.cuda.is_available():
# VRAM 사용량으로 학습 여부 추정
vram = self.check_vram()
return vram > Config.VRAM_WARNING_THRESHOLD
except Exception:
pass
return False
def request_inference(self, prompt, context_data=None):
"""
Ollama에 추론 요청
- LSTM 학습 중이면 대기 (GPU 메모리 충돌 방지)
"""
# LSTM 학습 중이면 최대 60초 대기
import time as _time
for _ in range(12):
if not self.is_training_active():
break
print("[Ollama] Waiting for LSTM training to finish...")
_time.sleep(5)
vram = self.check_vram()
if vram > Config.VRAM_WARNING_THRESHOLD:
print(f"[OllamaManager] High VRAM Usage ({vram:.1f}GB). Requesting unload.")
try:
# keep_alive=0으로 설정하여 모델 즉시 언로드
requests.post(self.generate_url,
json={"model": self.model_name, "keep_alive": 0}, timeout=5)
except Exception as e:
print(f"Warning: Failed to unload model: {e}")
payload = {
"model": self.model_name,
"prompt": prompt,
"stream": False,
"format": "json",
"options": {
"num_ctx": Config.OLLAMA_NUM_CTX, # 4096 (속도 2배)
"num_predict": Config.OLLAMA_NUM_PREDICT, # 응답 토큰 제한
"temperature": 0.1, # 더 결정론적 (JSON 파싱 안정성)
"num_gpu": 1,
"num_thread": Config.OLLAMA_NUM_THREAD # Config 설정값 (기본 8)
},
"keep_alive": "5m" # 5분 유지 (불필요한 VRAM 점유 줄임)
}
try:
response = requests.post(self.generate_url, json=payload, timeout=90) # 180→90초
response.raise_for_status()
return response.json().get('response')
except requests.exceptions.Timeout:
print(f"❌ Inference Timeout (90s): {self.model_name}")
return None
except Exception as e:
print(f"❌ Inference Error: {e}")
return None