hyerimmy 910bd902b1
Some checks failed
HealthSync Intelligence CI / build-and-push (push) Has been cancelled
feat : initial commit
2025-06-20 05:28:30 +00:00

98 lines
4.0 KiB
Python

"""
HealthSync AI Claude API 클라이언트 (공식 라이브러리 사용)
"""
import json
import logging
import asyncio
from typing import Dict, Any
import anthropic
from app.config.settings import settings
logger = logging.getLogger(__name__)
class ClaudeClient:
"""Claude API 호출 클라이언트 (공식 라이브러리 사용)"""
def __init__(self):
self.api_key = settings.claude_api_key
# API 키 검증
if not self.api_key or self.api_key == "" or self.api_key == "your_claude_api_key_here":
raise ValueError("Claude API 키가 설정되지 않았습니다. .env 파일에 CLAUDE_API_KEY를 설정해주세요.")
# 동기 클라이언트 초기화
self.client = anthropic.Anthropic(api_key=self.api_key)
logger.info(f"✅ Claude API 클라이언트 초기화 완료")
async def call_claude_api(self, prompt: str) -> str:
"""Claude API 호출 (비동기 래퍼)"""
try:
logger.info(f"🚀 Claude API 호출 시작 (모델: {settings.claude_model})")
# 동기 함수를 비동기로 실행
def sync_call():
return self.client.messages.create(
model=settings.claude_model,
max_tokens=settings.claude_max_tokens,
temperature=settings.claude_temperature,
messages=[
{
"role": "user",
"content": prompt
}
]
)
# 스레드 풀에서 동기 함수 실행
message = await asyncio.get_event_loop().run_in_executor(None, sync_call)
logger.info("✅ Claude API 호출 성공")
return message.content[0].text
except anthropic.AuthenticationError as e:
logger.error("❌ Claude API 인증 실패 - API 키 확인 필요")
raise Exception(f"Claude API 인증 실패: {str(e)}")
except anthropic.NotFoundError as e:
logger.error("❌ Claude API 엔드포인트 또는 모델을 찾을 수 없음")
raise Exception(f"Claude API 모델 또는 엔드포인트 오류: {str(e)}")
except anthropic.RateLimitError as e:
logger.error("❌ Claude API 요청 한도 초과")
raise Exception(f"Claude API 요청 한도 초과: {str(e)}")
except anthropic.APITimeoutError as e:
logger.error("⏰ Claude API 타임아웃")
raise Exception(f"Claude API 타임아웃: {str(e)}")
except Exception as e:
logger.error(f"❌ Claude API 호출 중 예상치 못한 오류: {str(e)}")
raise Exception(f"Claude API 호출 실패: {str(e)}")
def parse_json_response(self, response: str) -> Dict[str, Any]:
"""Claude 응답을 JSON으로 파싱"""
try:
# JSON 부분만 추출 (```json ... ``` 형태로 올 수 있음)
if "```json" in response:
start = response.find("```json") + 7
end = response.find("```", start)
json_str = response[start:end].strip()
elif "{" in response and "}" in response:
start = response.find("{")
end = response.rfind("}") + 1
json_str = response[start:end]
else:
json_str = response
parsed_json = json.loads(json_str)
logger.info(f"✅ Claude 응답 파싱 성공: {len(parsed_json.get('missions', []))}개 미션")
return parsed_json
except json.JSONDecodeError as e:
logger.error(f"❌ Claude 응답 JSON 파싱 실패: {str(e)}")
logger.error(f"파싱 대상 응답: {response}")
raise Exception(f"Claude 응답 파싱 실패: {str(e)}")
except Exception as e:
logger.error(f"❌ Claude 응답 처리 중 오류: {str(e)}")
raise Exception(f"Claude 응답 처리 실패: {str(e)}")